From 9686fe887a59c344271c4c21f79f364f475388f2 Mon Sep 17 00:00:00 2001 From: l3126596029 Date: Sat, 5 Sep 2026 04:22:41 +0800 Subject: [PATCH] =?UTF-8?q?FileCodeBox=20Go=20=E9=87=8D=E5=86=99=E7=89=88?= =?UTF-8?q?=20v2.5.6=EF=BC=88=E5=AE=89=E5=85=A8=E5=AE=A1=E8=AE=A1=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E7=89=88=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Go 1.27.1 (Gin+GORM) + Vue 3 文件快传服务: - 安全审计全部修复(docs/security-audit-2026-09-05.md): bcrypt 密码哈希与自动升级、presign 直传服务端大小/内容校验、 全局请求体上限、依赖升级(govulncheck 0 命中)、janitor 后台清理、 管理端审计动作落库、/admin CORS 收紧、通知内容白名单净化、 会话默认 7 天、限流缓存故障降级、robots.txt 端点等 - 前端:取件链接复制修复(不再重复拼接提取码)、markdown 净化器加固 - Redis 支持库号(FCB_REDIS_DB / redis://…/db URL) - 文档:docs/api/* 与 openapi.yaml 同步最新行为(robots.txt、 提码 5 位起、chunk 32MiB 上限、admin 审计动作等) 验证:gofmt/go vet/go test 全绿;二进制端到端冒烟通过 --- .dockerignore | 13 + .gitignore | 37 + README.md | 144 + deploy/.env.example | 60 + deploy/Dockerfile | 45 + deploy/README.md | 135 + deploy/docker-compose.yml | 144 + docs/api/00-overview.md | 100 + docs/api/01-auth.md | 105 + docs/api/02-text-share.md | 91 + docs/api/03-file-share.md | 103 + docs/api/04-pickup.md | 173 ++ docs/api/05-chunk-upload.md | 212 ++ docs/api/06-presign.md | 210 ++ docs/api/07-admin.md | 434 +++ docs/api/08-audit.md | 121 + docs/api/09-storage.md | 119 + docs/api/10-config.md | 190 ++ docs/api/11-errors.md | 62 + docs/api/12-logo.md | 61 + docs/openapi.yaml | 1920 +++++++++++++ docs/security-audit-2026-09-05.md | 186 ++ docs/verify-routes.js | 38 + server/README.md | 176 ++ server/cmd/server/main.go | 301 ++ server/go.mod | 79 + server/go.sum | 197 ++ server/internal/api/admin.go | 1523 ++++++++++ server/internal/api/chunk.go | 667 +++++ server/internal/api/helpers.go | 798 ++++++ server/internal/api/helpers_test.go | 264 ++ server/internal/api/policy.go | 45 + server/internal/api/policy_test.go | 571 ++++ server/internal/api/presign.go | 511 ++++ server/internal/api/router.go | 187 ++ server/internal/api/security_fixes_test.go | 193 ++ server/internal/api/setup.go | 327 +++ server/internal/api/share.go | 626 ++++ server/internal/api/v31_test.go | 170 ++ server/internal/api/web.go | 75 + server/internal/audit/audit.go | 212 ++ server/internal/audit/ua.go | 84 + server/internal/audit/ua_test.go | 45 + server/internal/cache/cache.go | 42 + server/internal/cache/cache_test.go | 81 + server/internal/cache/memory.go | 159 ++ server/internal/cache/redis.go | 117 + server/internal/cache/redis_options_test.go | 70 + server/internal/config/config.go | 417 +++ server/internal/config/config_test.go | 160 ++ server/internal/config/schema.go | 97 + server/internal/config/schema_test.go | 91 + server/internal/database/database.go | 139 + server/internal/database/database_test.go | 237 ++ server/internal/janitor/janitor.go | 124 + server/internal/middleware/audit.go | 261 ++ server/internal/middleware/audit_l5_test.go | 89 + server/internal/middleware/audit_test.go | 198 ++ server/internal/middleware/bodylimit.go | 30 + server/internal/middleware/cors.go | 74 + server/internal/middleware/jwt.go | 99 + server/internal/middleware/jwt_test.go | 83 + server/internal/middleware/ratelimit.go | 295 ++ .../middleware/ratelimit_fallback_test.go | 66 + server/internal/middleware/ratelimit_test.go | 126 + server/internal/model/model.go | 152 + server/internal/response/response.go | 25 + server/internal/settings/manager_test.go | 150 + server/internal/settings/password.go | 98 + server/internal/settings/password_test.go | 38 + server/internal/settings/sanitize.go | 258 ++ server/internal/settings/sanitize_test.go | 44 + server/internal/settings/schema.go | 87 + server/internal/settings/settings.go | 172 ++ server/internal/storage/errors.go | 10 + server/internal/storage/factory.go | 26 + server/internal/storage/interface.go | 111 + server/internal/storage/local.go | 449 +++ server/internal/storage/local_test.go | 346 +++ server/internal/storage/manager.go | 187 ++ server/internal/storage/manager_test.go | 197 ++ server/internal/storage/options.go | 70 + server/internal/storage/path.go | 103 + server/internal/storage/path_test.go | 74 + server/internal/storage/s3.go | 650 +++++ server/internal/storage/s3_test.go | 495 ++++ server/internal/storage/webdav.go | 889 ++++++ server/internal/storage/webdav_auth.go | 244 ++ server/internal/storage/webdav_test.go | 824 ++++++ .../web/dist/assets/AdminLayout-DJ2FR9f-.js | 1 + server/web/dist/assets/AuditView-BHN4e5xt.js | 1 + server/web/dist/assets/AuditView-f2PBwbQe.css | 1 + server/web/dist/assets/DocsView-Ds78oqBY.js | 1 + server/web/dist/assets/FilesView-ZJ5eqPp5.js | 1 + server/web/dist/assets/HomeView-D38U_AAJ.js | 1 + server/web/dist/assets/HomeView-DcN_X0wH.css | 1 + server/web/dist/assets/LoginView-BgHqRIwi.css | 1 + server/web/dist/assets/LoginView-BnVXNxFO.js | 1 + .../web/dist/assets/NotFoundView-CbfqSXyJ.js | 1 + .../web/dist/assets/OpenApiView-4SbIV1Bx.js | 267 ++ .../web/dist/assets/OpenApiView-BCH8BgeP.css | 1 + server/web/dist/assets/PageShell-D87JkPkG.js | 1 + server/web/dist/assets/PageShell-PYQzNiuf.css | 1 + ...ue_type_script_setup_true_lang-B3GeH0mY.js | 1 + server/web/dist/assets/PickupView-ClAAhXsM.js | 1 + .../web/dist/assets/SettingsView-CjSvbH8z.js | 129 + .../web/dist/assets/SettingsView-GpXVMuCV.css | 1 + ...ue_type_script_setup_true_lang-CwaEJKIZ.js | 1 + server/web/dist/assets/admin-DEOkRyTC.js | 1 + server/web/dist/assets/auth-B7MDTgxJ.js | 1 + server/web/dist/assets/docsSource-BXkkn0HE.js | 2517 +++++++++++++++++ server/web/dist/assets/favicon-Dl6ZLL7S.png | Bin 0 -> 61232 bytes server/web/dist/assets/index-CtyCxWf5.css | 1 + server/web/dist/assets/index-DYsKpclu.js | 28 + server/web/dist/assets/logo-CBe6oOaL.svg | 3 + server/web/dist/assets/markdown-B5D8JARp.js | 60 + server/web/dist/assets/share-CqDDLJWI.js | 1 + server/web/dist/assets/swagger-CqkleIqs.js | 1 + server/web/dist/index.html | 36 + server/web/web.go | 21 + web-embed/.gitkeep | 0 web/.gitignore | 4 + web/README.md | 79 + web/index.html | 35 + web/package-lock.json | 1960 +++++++++++++ web/package.json | 29 + web/src/App.vue | 145 + web/src/api/admin.ts | 157 + web/src/api/chunk.ts | 58 + web/src/api/http.ts | 209 ++ web/src/api/paths.ts | 89 + web/src/api/share.ts | 106 + web/src/api/types.ts | 135 + web/src/assets/brand/favicon.png | Bin 0 -> 61232 bytes web/src/assets/brand/logo.svg | 3 + web/src/components/AppModal.vue | 29 + web/src/components/ExpirePicker.vue | 122 + web/src/components/FileDrop.vue | 102 + web/src/components/NotifyPop.vue | 33 + web/src/components/PageShell.vue | 81 + web/src/components/Pager.vue | 36 + web/src/components/ResultCard.vue | 96 + web/src/components/SiteNav.vue | 74 + web/src/components/ToastHost.vue | 21 + web/src/docs/docsSource.ts | 88 + web/src/i18n/index.ts | 67 + web/src/locales/en-US.ts | 409 +++ web/src/locales/zh-CN.ts | 403 +++ web/src/main.ts | 16 + web/src/mock/index.ts | 256 ++ web/src/mock/types.ts | 13 + web/src/router/index.ts | 51 + web/src/stores/auth.ts | 61 + web/src/stores/config.ts | 161 ++ web/src/stores/toast.ts | 38 + web/src/styles/main.css | 1047 +++++++ web/src/theme/index.ts | 75 + web/src/utils/chunkUpload.ts | 122 + web/src/utils/format.ts | 141 + web/src/utils/markdown.ts | 98 + web/src/views/DocsView.vue | 147 + web/src/views/HomeView.vue | 279 ++ web/src/views/NotFoundView.vue | 17 + web/src/views/OpenApiView.vue | 92 + web/src/views/PickupView.vue | 176 ++ web/src/views/admin/AdminLayout.vue | 44 + web/src/views/admin/AuditView.vue | 218 ++ web/src/views/admin/FilesView.vue | 277 ++ web/src/views/admin/LoginView.vue | 83 + web/src/views/admin/SettingsView.vue | 784 +++++ web/src/vite-env.d.ts | 19 + web/tsconfig.json | 23 + web/vite.config.ts | 56 + 173 files changed, 32455 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 README.md create mode 100644 deploy/.env.example create mode 100644 deploy/Dockerfile create mode 100644 deploy/README.md create mode 100644 deploy/docker-compose.yml create mode 100644 docs/api/00-overview.md create mode 100644 docs/api/01-auth.md create mode 100644 docs/api/02-text-share.md create mode 100644 docs/api/03-file-share.md create mode 100644 docs/api/04-pickup.md create mode 100644 docs/api/05-chunk-upload.md create mode 100644 docs/api/06-presign.md create mode 100644 docs/api/07-admin.md create mode 100644 docs/api/08-audit.md create mode 100644 docs/api/09-storage.md create mode 100644 docs/api/10-config.md create mode 100644 docs/api/11-errors.md create mode 100644 docs/api/12-logo.md create mode 100644 docs/openapi.yaml create mode 100644 docs/security-audit-2026-09-05.md create mode 100644 docs/verify-routes.js create mode 100644 server/README.md create mode 100644 server/cmd/server/main.go create mode 100644 server/go.mod create mode 100644 server/go.sum create mode 100644 server/internal/api/admin.go create mode 100644 server/internal/api/chunk.go create mode 100644 server/internal/api/helpers.go create mode 100644 server/internal/api/helpers_test.go create mode 100644 server/internal/api/policy.go create mode 100644 server/internal/api/policy_test.go create mode 100644 server/internal/api/presign.go create mode 100644 server/internal/api/router.go create mode 100644 server/internal/api/security_fixes_test.go create mode 100644 server/internal/api/setup.go create mode 100644 server/internal/api/share.go create mode 100644 server/internal/api/v31_test.go create mode 100644 server/internal/api/web.go create mode 100644 server/internal/audit/audit.go create mode 100644 server/internal/audit/ua.go create mode 100644 server/internal/audit/ua_test.go create mode 100644 server/internal/cache/cache.go create mode 100644 server/internal/cache/cache_test.go create mode 100644 server/internal/cache/memory.go create mode 100644 server/internal/cache/redis.go create mode 100644 server/internal/cache/redis_options_test.go create mode 100644 server/internal/config/config.go create mode 100644 server/internal/config/config_test.go create mode 100644 server/internal/config/schema.go create mode 100644 server/internal/config/schema_test.go create mode 100644 server/internal/database/database.go create mode 100644 server/internal/database/database_test.go create mode 100644 server/internal/janitor/janitor.go create mode 100644 server/internal/middleware/audit.go create mode 100644 server/internal/middleware/audit_l5_test.go create mode 100644 server/internal/middleware/audit_test.go create mode 100644 server/internal/middleware/bodylimit.go create mode 100644 server/internal/middleware/cors.go create mode 100644 server/internal/middleware/jwt.go create mode 100644 server/internal/middleware/jwt_test.go create mode 100644 server/internal/middleware/ratelimit.go create mode 100644 server/internal/middleware/ratelimit_fallback_test.go create mode 100644 server/internal/middleware/ratelimit_test.go create mode 100644 server/internal/model/model.go create mode 100644 server/internal/response/response.go create mode 100644 server/internal/settings/manager_test.go create mode 100644 server/internal/settings/password.go create mode 100644 server/internal/settings/password_test.go create mode 100644 server/internal/settings/sanitize.go create mode 100644 server/internal/settings/sanitize_test.go create mode 100644 server/internal/settings/schema.go create mode 100644 server/internal/settings/settings.go create mode 100644 server/internal/storage/errors.go create mode 100644 server/internal/storage/factory.go create mode 100644 server/internal/storage/interface.go create mode 100644 server/internal/storage/local.go create mode 100644 server/internal/storage/local_test.go create mode 100644 server/internal/storage/manager.go create mode 100644 server/internal/storage/manager_test.go create mode 100644 server/internal/storage/options.go create mode 100644 server/internal/storage/path.go create mode 100644 server/internal/storage/path_test.go create mode 100644 server/internal/storage/s3.go create mode 100644 server/internal/storage/s3_test.go create mode 100644 server/internal/storage/webdav.go create mode 100644 server/internal/storage/webdav_auth.go create mode 100644 server/internal/storage/webdav_test.go create mode 100644 server/web/dist/assets/AdminLayout-DJ2FR9f-.js create mode 100644 server/web/dist/assets/AuditView-BHN4e5xt.js create mode 100644 server/web/dist/assets/AuditView-f2PBwbQe.css create mode 100644 server/web/dist/assets/DocsView-Ds78oqBY.js create mode 100644 server/web/dist/assets/FilesView-ZJ5eqPp5.js create mode 100644 server/web/dist/assets/HomeView-D38U_AAJ.js create mode 100644 server/web/dist/assets/HomeView-DcN_X0wH.css create mode 100644 server/web/dist/assets/LoginView-BgHqRIwi.css create mode 100644 server/web/dist/assets/LoginView-BnVXNxFO.js create mode 100644 server/web/dist/assets/NotFoundView-CbfqSXyJ.js create mode 100644 server/web/dist/assets/OpenApiView-4SbIV1Bx.js create mode 100644 server/web/dist/assets/OpenApiView-BCH8BgeP.css create mode 100644 server/web/dist/assets/PageShell-D87JkPkG.js create mode 100644 server/web/dist/assets/PageShell-PYQzNiuf.css create mode 100644 server/web/dist/assets/Pager.vue_vue_type_script_setup_true_lang-B3GeH0mY.js create mode 100644 server/web/dist/assets/PickupView-ClAAhXsM.js create mode 100644 server/web/dist/assets/SettingsView-CjSvbH8z.js create mode 100644 server/web/dist/assets/SettingsView-GpXVMuCV.css create mode 100644 server/web/dist/assets/SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js create mode 100644 server/web/dist/assets/admin-DEOkRyTC.js create mode 100644 server/web/dist/assets/auth-B7MDTgxJ.js create mode 100644 server/web/dist/assets/docsSource-BXkkn0HE.js create mode 100644 server/web/dist/assets/favicon-Dl6ZLL7S.png create mode 100644 server/web/dist/assets/index-CtyCxWf5.css create mode 100644 server/web/dist/assets/index-DYsKpclu.js create mode 100644 server/web/dist/assets/logo-CBe6oOaL.svg create mode 100644 server/web/dist/assets/markdown-B5D8JARp.js create mode 100644 server/web/dist/assets/share-CqDDLJWI.js create mode 100644 server/web/dist/assets/swagger-CqkleIqs.js create mode 100644 server/web/dist/index.html create mode 100644 server/web/web.go create mode 100644 web-embed/.gitkeep create mode 100644 web/.gitignore create mode 100644 web/README.md create mode 100644 web/index.html create mode 100644 web/package-lock.json create mode 100644 web/package.json create mode 100644 web/src/App.vue create mode 100644 web/src/api/admin.ts create mode 100644 web/src/api/chunk.ts create mode 100644 web/src/api/http.ts create mode 100644 web/src/api/paths.ts create mode 100644 web/src/api/share.ts create mode 100644 web/src/api/types.ts create mode 100644 web/src/assets/brand/favicon.png create mode 100644 web/src/assets/brand/logo.svg create mode 100644 web/src/components/AppModal.vue create mode 100644 web/src/components/ExpirePicker.vue create mode 100644 web/src/components/FileDrop.vue create mode 100644 web/src/components/NotifyPop.vue create mode 100644 web/src/components/PageShell.vue create mode 100644 web/src/components/Pager.vue create mode 100644 web/src/components/ResultCard.vue create mode 100644 web/src/components/SiteNav.vue create mode 100644 web/src/components/ToastHost.vue create mode 100644 web/src/docs/docsSource.ts create mode 100644 web/src/i18n/index.ts create mode 100644 web/src/locales/en-US.ts create mode 100644 web/src/locales/zh-CN.ts create mode 100644 web/src/main.ts create mode 100644 web/src/mock/index.ts create mode 100644 web/src/mock/types.ts create mode 100644 web/src/router/index.ts create mode 100644 web/src/stores/auth.ts create mode 100644 web/src/stores/config.ts create mode 100644 web/src/stores/toast.ts create mode 100644 web/src/styles/main.css create mode 100644 web/src/theme/index.ts create mode 100644 web/src/utils/chunkUpload.ts create mode 100644 web/src/utils/format.ts create mode 100644 web/src/utils/markdown.ts create mode 100644 web/src/views/DocsView.vue create mode 100644 web/src/views/HomeView.vue create mode 100644 web/src/views/NotFoundView.vue create mode 100644 web/src/views/OpenApiView.vue create mode 100644 web/src/views/PickupView.vue create mode 100644 web/src/views/admin/AdminLayout.vue create mode 100644 web/src/views/admin/AuditView.vue create mode 100644 web/src/views/admin/FilesView.vue create mode 100644 web/src/views/admin/LoginView.vue create mode 100644 web/src/views/admin/SettingsView.vue create mode 100644 web/src/vite-env.d.ts create mode 100644 web/tsconfig.json create mode 100644 web/vite.config.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..45f4269 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +# Docker 构建上下文排除(context=仓库根,防止 800MB+ 缓存与无关目录进 context) +.gocache/ +.gomodcache/ +.git/ +.agent-teams/ +reference/ +docs/ +web/node_modules/ +web/.npm-cache/ +node_modules/ +deploy/ +*.log +.DS_Store diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd0a39e --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# ===== 构建产物与本地缓存 ===== +# Go 构缓存 / 模块缓存(体积大,且 .gomodcache 含第三方源码副本) +.gocache/ +.gomodcache/ +.gotools/ +.npm-cache/ + +# 前端依赖与构建产物 +web/node_modules/ +web/dist/ +# web-embed/ 为宿主预构建产物(cp -a web/dist web-embed/),仅保留占位; +# Dockerfile 缺失 index.html 时自动回退为容器内构建前端 +web-embed/* +!web-embed/.gitkeep + +# go:embed 目标:直接提交构建产物,保证全新克隆 `go build`/`go run` 开箱可用 +# (前端源码变更后重新构建并同步:cd web && npm run build && rm -rf ../server/web/dist && cp -a dist ../server/web/dist) + +# 参考实现(第三方上游代码,非本项目源码,不入库) +reference/ + +# 本地运行数据(SQLite 数据库、本地存储文件、日志) +server/data/ +*.db +*.db-shm +*.db-wal +*.log +server.pid + +# 环境变量(保留 .env.example 模板;.env 属各部署环境自有配置) +deploy/.env + +# 系统杂物 +.DS_Store + +# 会话/协作目录(工具私有,非项目源码) +.agent-teams/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..eb496e2 --- /dev/null +++ b/README.md @@ -0,0 +1,144 @@ + +数据库**默认 SQLite 零依赖**(modernc.org/sqlite 纯 Go 驱动,数据文件 `./data/filecodebox.db`), +可选切换 Postgres(`FCB_DB_DRIVER=postgres` + DSN);Redis 为**可选**增强(未配置时自动降级为进程内存缓存)。 +存储引擎支持 **本地 / S3 / WebDAV**(运行时热切换,健康检查通过才生效;WebDAV 重点优化:流式、Range、重试、连接复用)。 + +| 目录 | 说明 | +|---|---| +| `server/` | Go 后端(API、模型迁移、缓存降级、认证限流、审计、存储引擎) | +| `web/` | Vue 3 + Vite + TS 前端(分享/取件/管理/审计/设置/`/docs` 文档页/`/openapi` Swagger) | +| `docs/` | 中文 API 操作文档(`docs/api/*.md`)与 OpenAPI 规范(`docs/openapi.yaml`) | +| `deploy/` | docker-compose 编排、三阶段 Dockerfile、`.env.example` | +| `reference/` | 参考原版仓库(只读克隆,目录名 `upstream`) | + +## 默认 Logo 与自定义 + +- 页面导航 Logo:本地资源 `web/src/assets/brand/logo.svg` +- favicon / 备用 Logo:本地资源 `web/src/assets/brand/favicon.png`(v2 起不再使用远程 URL 默认值) + +管理端自定义 Logo 三步: + +1. 登录后台 `POST /admin/login` 获取 Bearer 令牌; +2. `PATCH /admin/config/update` 提交 `{"logo_url":"…","favicon_url":"…"}`(或在管理界面「系统设置」页上传/填写 URL); +3. 保存即全站生效(前端读取 `GET /api/v1/config` 立即换新,无需重启)。 + +详见《[Logo 自定义](docs/api/12-logo.md)》。 + +## 快速开始(docker compose) + +```bash +cd deploy +cp .env.example .env +docker compose up -d --build +# 打开 http://localhost:8466 → 自动跳转 /setup 完成初始化 +``` + +- **数据库双路径**:默认 SQLite 零依赖——`docker compose up -d --build` 即可(无需 postgres profile, + 数据落 `serverdata` 卷 `/app/data/filecodebox.db`);Postgres 模式——`.env` 设 + `FCB_DB_DRIVER=postgres` 与 `FCB_DB_DSN` 后 `docker compose --profile postgres up -d --build`。 +- Redis 可选:`--profile redis` 并在 `.env` 设 `FCB_REDIS_ADDR=redis:6379`;未配置时自动降级为内存缓存。 +- 存储引擎切换:`.env` 改 `FCB_STORAGE_ENGINE=s3|webdav` 并带对应 profile 启动: + `docker compose --profile minio up -d --build`(含 mc 自动建桶)或 `docker compose --profile webdav up -d --build`。 +- 详见《[部署编排](deploy/README.md)》与《[存储引擎配置](docs/api/09-storage.md)》。 + +## 本地开发 + +前置:Go 1.27.1、Node 20(Postgres 16 仅 Postgres 模式需要)。 + +```bash +# 1) 后端(:8466)——默认 SQLite 零依赖,无需任何数据库 +cd server +go run ./cmd/server # 数据落 ./data/filecodebox.db;go test ./... 运行单测 + +# 2) 后端 Postgres 模式(可选) +docker run -d --name fcb-pg -p 5432:5432 \ + -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=filecodebox postgres:16 +export FCB_DB_DRIVER=postgres +export FCB_DB_DSN='postgres://postgres:postgres@localhost:5432/filecodebox?sslmode=disable' +go run ./cmd/server + +# 3) 前端 dev server(Vite 代理 /api → http://127.0.0.1:8466) +cd ../web +npm ci +npm run dev + +# 生产构建(go:embed 进二进制) +npm run build # 产出 web/dist/,构建时按 deploy/Dockerfile 拷入 server/web/dist +``` + +- 浏览器打开 `http://localhost:5173`(前端 dev)或 `http://localhost:8466`(后端 embed)。 +- 未初始化时除 `/setup` 与 `/api/v1/health` 外一律 428;首次访问按向导完成初始化。 + +## 文档 + +| 内容 | 入口 | +|---|---| +| API 操作文档(概述/认证/分享/分片/预签名/管理后台/审计/存储/配置/错误码/Logo) | 站内 `/docs`,源文件 `docs/api/*.md` | +| OpenAPI 3.0 规范 + Swagger UI | 站内 `/openapi`,源文件 `docs/openapi.yaml` | +| 后端设计契约 | [server/README.md](server/README.md) | +| 前端说明与渲染约定 | [web/README.md](web/README.md) | +| 部署编排 | [deploy/README.md](deploy/README.md) | + +## 全量配置 + +### 进程环境变量(`FCB_*`) + +| 变量 | 必需 | 默认 | 说明 | +|---|---|---|---| +| `FCB_DB_DRIVER` | ❌ | `sqlite` | 数据库驱动:`sqlite` \| `postgres`(v2 需求 ⑧) | +| `FCB_DB_DSN` | 视驱动 | - | postgres:连接串(**必需**);sqlite:文件路径(可空,默认 `./data/filecodebox.db`) | +| `FCB_REDIS_ADDR` | ❌ | 空 | 为空时缓存降级为内存实现;支持 `redis://[:password@]host:port[/db]` / `rediss://` URL 形式 | +| `FCB_REDIS_DB` | ❌ | `0` | Redis 逻辑库号 0-15(URL 显式 `/N` 时以 URL 为准) | +| `FCB_LISTEN` | ❌ | `:8466` | 监听地址 | +| `FCB_STORAGE_ENGINE` | ❌ | `local` | `local` / `s3` / `webdav` | +| `FCB_TRUSTED_PROXIES` | ❌ | 空 | 可信代理 CIDR(逗号分隔),生产必读(限流/审计 IP 依据) | +| `FCB_ADMIN_PASSWORD` | ❌ | 空 | 设置后首次启动自动初始化管理员(≥8 位),消除 `/setup` 被抢占窗口;初始化后建议移除 | + +### 引擎环境变量(种子注入,重启生效) + +| 变量 | 引擎 | +|---|---| +| `FCB_LOCAL_STORAGE_PATH`(容器内默认 `/app/data`) | local | +| `FCB_STORAGE_PATH` | 全部(存储相对路径前缀) | +| `FCB_S3_ACCESS_KEY_ID` / `FCB_S3_SECRET_ACCESS_KEY` / `FCB_AWS_SESSION_TOKEN` | s3 | +| `FCB_S3_BUCKET_NAME` / `FCB_S3_ENDPOINT_URL` / `FCB_S3_REGION_NAME` / `FCB_S3_ADDRESSING_STYLE` | s3 | +| `FCB_WEBDAV_URL` / `FCB_WEBDAV_USERNAME` / `FCB_WEBDAV_PASSWORD` / `FCB_WEBDAV_ROOT_PATH` | webdav | + +### 运行时配置(settings KV,管理端可改) + +站点信息(`site_name`、`logo_url`、`favicon_url`、`page_explain` 等)、v2 展示与通知 +(`background_url`、`footer_text`、`footer_beian`、`notify_enabled`、`notify_title/content`)、 +上传策略(`openUpload`、`enableChunk`、`uploadSize`、`allowed_file_types`、`expireStyle`、 +`code_generate_type`、`max_save_seconds`、`storageLimit`,及 v2 上限键 `max_save_count`、 +`max_file_size`)、限流(`uploadCount/uploadMinute`、`errorCount/errorMinute`、 +`loginCount/loginMinute`)、安全(`adminSessionExpire`;`admin_token`/`jwt_secret` 由系统管理)。 + +完整键表与默认值见《[环境变量与配置项](docs/api/10-config.md)》; +修改接口见《[管理后台 API](docs/api/07-admin.md)》(`PATCH /admin/config/update`,改密自动轮换 jwt_secret)。 + +## 审计日志 + +所有上传/下载端点自动落库:操作时间、IP、UA、设备解析(OS/浏览器/类型)、动作 +(`upload`/`download`)、结果(`success`/`denied`/`failed`)、字节数(文件总大小 + 实际传输, +Range 只计实际区间)、耗时、角色(`admin`/`guest`)。管理端查询: +`GET /admin/audit/list?page&size&action&result&ip&start_time&end_time`。 +详见《[审计日志查询](docs/api/08-audit.md)》。 + +## API 速览 + +| 模块 | 代表端点 | +|---|---| +| 公共 | `GET /api/v1/health` · `GET /api/v1/config` | +| 初始化 | `GET /setup` · `POST /setup` | +| 分享 | `POST /share/text` · `POST /share/file` · `GET /share/metadata?code=` · `GET/POST /share/select` · `GET /share/download` | +| 分片上传 | `POST /chunk/upload/init` → `POST /chunk/upload/{id}/{index}` → `POST /chunk/upload/complete/{id}` | +| 预签名直传 | `POST /presign/upload/init`(S3=direct,local/webdav=proxy) | +| 管理后台 | `POST /admin/login` · `/admin/file/*` · `PATCH /admin/config/update` | +| 审计 | `GET /admin/audit/list`(别名 `/admin/audit/logs`) | + +统一响应 `{"code":200,"msg":"ok","data":…}`;错误码语义见《[错误码](docs/api/11-errors.md)》。 + +## License + +GPL-3.0 + diff --git a/deploy/.env.example b/deploy/.env.example new file mode 100644 index 0000000..e8f14ce --- /dev/null +++ b/deploy/.env.example @@ -0,0 +1,60 @@ +# ===== FileCodeBox 部署环境变量示例 ===== +# 复制为 .env 后按需修改;所有变量均有默认值。 + +# ---- 站点 ---- +WEB_PORT=8466 + +# ---- 数据库(需求 ⑧)---- +# 默认 SQLite:零依赖,无需任何下方 Postgres 变量(数据落 serverdata 卷 /app/data/filecodebox.db) +FCB_DB_DRIVER=sqlite +FCB_DB_DSN= +# Postgres 模式(可选):先 `docker compose --profile postgres up -d --build`,再改为: +# FCB_DB_DRIVER=postgres +# FCB_DB_DSN=postgres://filecodebox:filecodebox@postgres:5432/filecodebox?sslmode=disable + +# ---- Postgres(仅 --profile postgres 时使用;生产务必修改默认口令并启用 TLS)---- +POSTGRES_USER=filecodebox +POSTGRES_PASSWORD=filecodebox +POSTGRES_DB=filecodebox + +# ---- 存储引擎切换:local | s3 | webdav ---- +FCB_STORAGE_ENGINE=local + +# ---- 可信代理(生产必读,安全审计 L10)---- +# 反代(nginx/caddy 等)后必须设为代理 IP/CIDR(逗号分隔), +# 否则限流按代理 IP 计数(全体用户共享一个限流桶)且审计 IP 失真。 +# 示例:FCB_TRUSTED_PROXIES=172.18.0.0/16,10.0.0.5 +FCB_TRUSTED_PROXIES= + +# ---- 管理员自动初始化(可选,安全审计 L1)---- +# 设置后服务首次启动即完成管理员初始化(≥8 位),消除 /setup 被抢占窗口; +# 初始化完成后建议移除该变量。留空则走 /setup 网页向导。 +# FCB_ADMIN_PASSWORD= + +# ---- 管理员会话有效期(秒,默认 7 天,可调 1~365 整天)---- +# FCB_ADMIN_SESSION_EXPIRE=604800 + +# ---- 本地引擎 ---- +# 容器内固定 /app/data;compose 已挂载 serverdata 卷 +# FCB_LOCAL_STORAGE_PATH=/app/data + +# ---- Redis(可选增强;留空=内存降级;启用服务用 --profile redis)---- +# FCB_REDIS_ADDR=redis:6379 +# Redis 逻辑库号 0-15(默认 0);地址为 redis://…/N URL 形式时以 URL 内库号优先 +# FCB_REDIS_DB=0 + +# ---- S3 引擎(FCB_STORAGE_ENGINE=s3 时必填 bucket/access/secret;服务用 --profile minio)---- +# 注意:minioadmin/minioadmin 仅为本机冒烟默认值,对外部署必须修改 +# (并在 compose 中删除 minio 的 ports 发布或仅绑定 127.0.0.1) +FCB_S3_ENDPOINT_URL=http://minio:9000 +FCB_S3_BUCKET_NAME=filecodebox +FCB_S3_ACCESS_KEY_ID=minioadmin +FCB_S3_SECRET_ACCESS_KEY=minioadmin +FCB_S3_REGION_NAME=us-east-1 +# FCB_S3_ADDRESSING_STYLE=auto + +# ---- WebDAV 引擎(FCB_STORAGE_ENGINE=webdav 时必填 url;服务用 --profile webdav)---- +FCB_WEBDAV_URL=http://webdav:5000 +FCB_WEBDAV_USERNAME=admin +FCB_WEBDAV_PASSWORD=admin123 +FCB_WEBDAV_ROOT_PATH=filebox_storage diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 0000000..9a47438 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,45 @@ +# 文件快传 Go 版多阶段构建: +# 1) node:20-alpine 构建 Vue 前端产物(web/dist) +# 2) golang:1.27.1-alpine 编译 server(go:embed 前端产物,缓存指向 /tmp 避免写权限问题) +# 3) 最小运行时镜像:server 二进制 + web/dist 静态产物 +FROM node:20-alpine AS frontend +WORKDIR /build +COPY web/package.json web/package-lock.json* ./ +RUN npm ci || npm install +COPY web/ ./ +RUN npm run build + +FROM golang:1.27.1-alpine AS backend +WORKDIR /build +# GOPROXY:默认走 goproxy.cn(proxy.golang.org 的 GCS 后端在本环境网络下 EOF/超时),direct 兜底 +ENV CGO_ENABLED=0 GOSUMDB=off GOPROXY=https://goproxy.cn,direct GOCACHE=/tmp/.gocache GOMODCACHE=/tmp/.gomodcache +# 先拷贝 go.mod/go.sum 利用层缓存 +COPY server/go.mod server/go.sum ./ +RUN go mod download +COPY server/ ./ +# 前端产物嵌入(go:embed 目标目录 server/web/dist): +# 优先用宿主预构建产物 web-embed/(构建前由 `cp -a web/dist web-embed/` 维护, +# 保证 embed 与前端最新修正一致);为空/缺失时回退跨阶段 COPY(CI 兜底) +COPY --from=frontend /build/dist ./frontend-dist +COPY web-embed/ ./web-embed/ +RUN if [ -f web-embed/index.html ]; then \ + rm -rf web/dist && cp -a web-embed/. web/dist/; \ + else \ + rm -rf web/dist && cp -a frontend-dist/. web/dist/; \ + fi \ + && rm -rf web-embed frontend-dist +RUN go build -trimpath -ldflags "-s -w" -o /out/server ./cmd/server + +FROM alpine:3.20 +# curl 供容器健康检查使用 +RUN apk add --no-cache ca-certificates tzdata curl && adduser -D -u 10001 fcb +WORKDIR /app +COPY --from=backend /out/server /app/server +# dist 随镜像分发(服务端已 go:embed,磁盘副本供运维检查/替换) +COPY --from=backend /build/web/dist /app/web/dist +RUN mkdir -p /app/data && chown -R fcb:fcb /app +USER fcb +ENV FCB_LISTEN=:8466 +EXPOSE 8466 +VOLUME ["/app/data"] +ENTRYPOINT ["/app/server"] diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..1384493 --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,135 @@ +# 文件快传 部署编排(deploy/) + +Go 1.27.1(Gin + GORM)+ Vue 3 重写版(v3.1)的容器化部署。数据库**默认 SQLite,零外部依赖** +(modernc.org/sqlite 纯 Go 驱动,无需 Postgres),可选切换 Postgres(`--profile postgres`); +Redis 为可选增强(`--profile redis`),未配置 `FCB_REDIS_ADDR` 时服务端自动降级为进程内存缓存。 + +> v3.1 功能提示:管理后台可设「站点对外域名」(内网部署生成公网分享链接)、 +> 分享时支持自定义提取码(4-8 位字母数字);这些均为运行时配置,无需改部署。 + +## compose 服务与 profile 一览 + +| 启动方式 | 服务 | 说明 | +|---|---|---| +| `docker compose up -d --build` | server | **默认:SQLite 零依赖**,仅启动一个容器 | +| `--profile postgres` | postgres | 可选:Postgres 数据库(数据落 `pgdata` 卷) | +| `--profile redis` | redis | 可选:缓存增强(未配置自动内存降级) | +| `--profile minio` | minio + minio-init | 可选:S3 引擎冒烟(minio-init 用 mc 自动建桶) | +| `--profile webdav` | webdav | 可选:WebDAV 引擎冒烟(dufs) | + +## 数据库双路径(需求 ⑧) + +### 路径 ①:默认 SQLite(零依赖,推荐) + +无需 postgres profile,也无需任何数据库环境变量: + +```bash +cd deploy +cp .env.example .env # 保持 FCB_DB_DRIVER=sqlite、FCB_DB_DSN 留空(默认值即可) +docker compose up -d --build +# 打开 http://localhost:8466(.env 可用 WEB_PORT 改端口)→ 自动跳转 /setup 完成初始化 +``` + +- 数据落 **`serverdata` 卷**:容器内 `/app/data/filecodebox.db`(WAL 模式,父目录自动创建)。 +- `.env` 中 Postgres 段变量(`POSTGRES_USER/PASSWORD/DB`)在此模式下不生效,无需修改。 + +### 路径 ②:Postgres 模式(可选) + +1. 编辑 `.env`: + + ```dotenv + FCB_DB_DRIVER=postgres + FCB_DB_DSN=postgres://filecodebox:filecodebox@postgres:5432/filecodebox?sslmode=disable + ``` + +2. 带 postgres profile 启动(server 会等 postgres 健康检查通过后再启动): + + ```bash + docker compose --profile postgres up -d --build + ``` + +- compose 将 `.env` 的 `FCB_DB_DRIVER/FCB_DB_DSN` 透传给 server 容器;DSN 主机必须为 compose + 服务名 `postgres`,凭据与 `POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB` 一致。 +- Postgres 数据落 `pgdata` 卷;首次启动自动执行迁移(AutoMigrate)。 + +> 注意:设置 `FCB_DB_DRIVER=postgres` 后务必带 `--profile postgres` 启动,否则 postgres +> 服务不会创建,server 将因连不上数据库而反复重启。 + +服务未初始化时(两种数据库模式行为一致)除 `/setup` 与 `/api/v1/health` 外一律返回 428, +浏览器打开站点按向导设置管理员密码。 + +## 存储引擎切换(本地 / S3 / WebDAV) + +修改 `.env` 中 `FCB_STORAGE_ENGINE` 并带对应 profile 启动: + +```bash +# S3 引擎(MinIO 冒烟) +docker compose --profile minio up -d --build # .env: FCB_STORAGE_ENGINE=s3 +# minio-init 一次性任务自动建桶(mc mb filecodebox) + +# WebDAV 引擎(dufs 冒烟,Basic 认证 admin/admin123@/:rw) +docker compose --profile webdav up -d --build # .env: FCB_STORAGE_ENGINE=webdav +``` + +> **v3 起支持运行时热切换**:也可不改 `.env`,直接在管理后台「系统设置 → 存储引擎」 +> 三选并保存引擎参数后点切换(健康检查通过才生效,失败保持原引擎),或调用 +> `POST /admin/storage/switch`——均无需重启容器。`.env` 的 `FCB_STORAGE_ENGINE` +> 仅作为首次启动(KV 为空时)的默认引擎。 + +## Redis 缓存增强(可选) + +Redis 不是必需服务:`--profile redis` 启动**且** `.env` 设 `FCB_REDIS_ADDR=redis:6379` 才接入; +未配置(或未启用该 profile)时服务端**自动降级为进程内存缓存**,单实例部署功能完全可用, +多实例部署或需要重启保活计数/会话场景时建议启用。 + +```bash +docker compose --profile redis up -d --build # .env: FCB_REDIS_ADDR=redis:6379 +``` + +## 文件说明 + +| 文件 | 说明 | +|---|---| +| `Dockerfile` | 三阶段构建:node 构建 web/dist → go 编译(go:embed 前端产物)→ alpine 运行镜像 | +| `docker-compose.yml` | server + 四个可选 profile:postgres / redis / minio(含 mc 建桶)/ webdav | +| `.env.example` | 全量环境变量:`FCB_DB_DRIVER` 双路径、三引擎切换、`WEB_PORT`、可信代理 | + +## 健康检查与验证 + +端口由 `.env` 的 `WEB_PORT`(默认 `8466`)映射到容器内 `8466`: + +```bash +curl http://localhost:8466/api/v1/health # {"code":200,"data":{"status":"ok",...}} +curl -I http://localhost:8466/ # 200,前端 SPA +curl -I http://localhost:8466/docs # 200(SPA 回退) +``` + +## 注意事项 + +- **数据持久化**:SQLite 数据库与本地引擎文件都在 `serverdata` 卷(容器内 `/app/data`); + Postgres 模式数据在 `pgdata` 卷;redis/minio/webdav 数据各在独立卷。 + 切换数据库驱动不会自动迁移既有数据(SQLite 文件与 Postgres 库互相独立)。 +- **非 root 运行**:容器以 uid 10001 运行。 +- **日志**:`docker compose logs -f server`。 +- **CORS**:同源部署无需额外配置;`/admin/*` 已收紧——仅同源或 `site_domain` 白名单可跨域调用, + 其余跨域由浏览器拦截;公开接口维持 `*`(Bearer 认证,无 Cookie CSRF 面)。 + +## 安全清单(生产部署必读) + +安全审计(`docs/security-audit-2026-09-05.md`)后的部署要求: + +1. **反向代理必须配置可信代理**:`.env` 设 `FCB_TRUSTED_PROXIES=<代理IP/CIDR>`(逗号分隔)。 + 否则限流按代理 IP 计数(全体用户共享一个限流桶、互相误伤),审计日志 IP 失真; + 也不可把不受信地址加进来——那会允许伪造 `X-Forwarded-For` 绕过限流。 +2. **部署后立即初始化管理员**:打开站点完成 `/setup`(设置 ≥8 位密码), + 或在 `.env` 设 `FCB_ADMIN_PASSWORD`(≥8 位)让服务启动即自动初始化, + 消除「公网上被抢先访问 /setup 接管」的窗口。初始化完成后建议从 `.env` 移除该变量。 +3. **修改组件默认凭据**:minio(`minioadmin/minioadmin`)、webdav(`admin/admin123`)、 + Postgres(`filecodebox/filecodebox`)默认凭据仅限本机冒烟;对外部署必须修改, + 且不建议把 9000/9001/5005 端口发布到公网(compose 中删除对应 `ports` 或仅绑 127.0.0.1)。 +4. **Postgres 建议启用 TLS**:默认示例 DSN 为 `sslmode=disable`,生产请改为 + `sslmode=require` 及以上。 +5. **管理员会话默认 7 天**(可在管理端调 1~365 天);修改管理员密码会使全部旧 token 失效。 +6. **上传/登录限流**依赖内存或 Redis 缓存;Redis 故障时自动降级为进程内限流(单实例语义)。 +7. **后台清理任务**:服务内置每 10 分钟的过期容量预留 / 超时上传会话 / 直传残留对象清理, + 无需额外 cron。 diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 0000000..de88b3f --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,144 @@ +# 文件快传 Go 版部署编排(v2,需求 ⑧): +# docker compose up -d --build # 默认:SQLite 零依赖(仅 server 容器) +# docker compose --profile postgres up -d --build # 可选:Postgres 模式(自动注入 FCB_DB_DSN) +# docker compose --profile redis up -d # 可选:Redis 缓存增强 +# docker compose --profile minio up -d # 可选:S3 引擎冒烟(FCB_STORAGE_ENGINE=s3) +# docker compose --profile webdav up -d # 可选:WebDAV 引擎冒烟(FCB_STORAGE_ENGINE=webdav) +# +# 数据库默认 SQLite(modernc.org/sqlite 纯 Go 驱动,数据落 serverdata 卷 /app/data/filecodebox.db); +# postgres 为可选 profile 服务,启用时 server 经 FCB_DB_DRIVER/FCB_DB_DSN 自动接入; +# Redis 同为可选,未配置时服务端内存降级。 +name: filecodebox + +services: + postgres: + image: postgres:16-alpine + restart: unless-stopped + # 可选服务(需求 ⑧):默认 SQLite 模式不会启动 postgres; + # 需要时 docker compose --profile postgres up -d --build + profiles: ["postgres"] + environment: + POSTGRES_USER: ${POSTGRES_USER:-filecodebox} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-filecodebox} + POSTGRES_DB: ${POSTGRES_DB:-filecodebox} + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-filecodebox}"] + interval: 5s + timeout: 3s + retries: 20 + + server: + build: + context: .. + dockerfile: deploy/Dockerfile + restart: unless-stopped + depends_on: + # postgres 为可选 profile 服务:--profile postgres 启用时等它健康, + # 默认 SQLite 模式不启用该 profile,不阻塞启动 + postgres: + condition: service_healthy + required: false + # redis 为可选 profile 服务:启用时等它健康,未启用不阻塞启动 + redis: + condition: service_healthy + required: false + healthcheck: + test: ["CMD", "curl", "-sf", "http://127.0.0.1:8466/api/v1/health"] + interval: 5s + timeout: 3s + retries: 30 + start_period: 5s + environment: + # 需求 ⑧:数据库驱动 sqlite(默认,零依赖)| postgres(需 --profile postgres) + # 默认 SQLite:DSN 留空 → 数据库文件落 /app/data/filecodebox.db(serverdata 卷) + # postgres 模式:在 .env 设 FCB_DB_DRIVER=postgres 与 FCB_DB_DSN(模板见 deploy/.env.example) + FCB_DB_DRIVER: ${FCB_DB_DRIVER:-sqlite} + FCB_DB_DSN: ${FCB_DB_DSN:-} + FCB_LISTEN: ":8466" + FCB_STORAGE_ENGINE: ${FCB_STORAGE_ENGINE:-local} + # Redis 可选增强:.env 设 FCB_REDIS_ADDR=redis:6379 且 --profile redis 启用时接入; + # 默认留空=内存缓存(需求 ⑧) + FCB_REDIS_ADDR: ${FCB_REDIS_ADDR:-} + FCB_REDIS_DB: ${FCB_REDIS_DB:-0} + FCB_TRUSTED_PROXIES: ${FCB_TRUSTED_PROXIES:-} + # 本地引擎根目录(容器内) + FCB_LOCAL_STORAGE_PATH: /app/data + # S3 引擎(MinIO profile) + FCB_S3_ENDPOINT_URL: ${FCB_S3_ENDPOINT_URL:-} + FCB_S3_BUCKET_NAME: ${FCB_S3_BUCKET_NAME:-filecodebox} + FCB_S3_ACCESS_KEY_ID: ${FCB_S3_ACCESS_KEY_ID:-minioadmin} + FCB_S3_SECRET_ACCESS_KEY: ${FCB_S3_SECRET_ACCESS_KEY:-minioadmin} + FCB_S3_REGION_NAME: ${FCB_S3_REGION_NAME:-us-east-1} + # WebDAV 引擎(dufs profile) + FCB_WEBDAV_URL: ${FCB_WEBDAV_URL:-} + FCB_WEBDAV_USERNAME: ${FCB_WEBDAV_USERNAME:-admin} + FCB_WEBDAV_PASSWORD: ${FCB_WEBDAV_PASSWORD:-admin123} + FCB_WEBDAV_ROOT_PATH: ${FCB_WEBDAV_ROOT_PATH:-filebox_storage} + ports: + - "${WEB_PORT:-8466}:8466" + volumes: + - serverdata:/app/data + + redis: + image: redis:7-alpine + restart: unless-stopped + profiles: ["redis"] + volumes: + - redisdata:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 20 + + minio: + image: minio/minio:latest + restart: unless-stopped + profiles: ["minio"] + command: server /data --console-address ":9001" + environment: + MINIO_ROOT_USER: ${FCB_S3_ACCESS_KEY_ID:-minioadmin} + MINIO_ROOT_PASSWORD: ${FCB_S3_SECRET_ACCESS_KEY:-minioadmin} + ports: + - "${MINIO_API_PORT:-9000}:9000" + - "${MINIO_CONSOLE_PORT:-9001}:9001" + volumes: + - miniodata:/data + healthcheck: + test: ["CMD", "mc", "ready", "local"] + interval: 5s + timeout: 3s + retries: 20 + + # MinIO 建桶初始化(mc;一次性任务) + minio-init: + image: minio/mc:latest + profiles: ["minio"] + depends_on: + minio: + condition: service_healthy + entrypoint: > + /bin/sh -c " + mc alias set local http://minio:9000 ${FCB_S3_ACCESS_KEY_ID:-minioadmin} ${FCB_S3_SECRET_ACCESS_KEY:-minioadmin} && + mc mb --ignore-existing local/${FCB_S3_BUCKET_NAME:-filecodebox} && + echo 'MinIO 桶已就绪'" + + # WebDAV 冒烟服务器(dufs,简单读写 + Basic 认证) + webdav: + image: sigoden/dufs:latest + restart: unless-stopped + profiles: ["webdav"] + command: /data -A -a admin:admin123@/:rw + ports: + - "${WEBDAV_PORT:-5005}:5000" + volumes: + - webdavdata:/data + +volumes: + pgdata: + redisdata: + miniodata: + webdavdata: + serverdata: diff --git a/docs/api/00-overview.md b/docs/api/00-overview.md new file mode 100644 index 0000000..de7d1be --- /dev/null +++ b/docs/api/00-overview.md @@ -0,0 +1,100 @@ +# API 概述 + +文件快传 Go 版(v2.5.6)对外提供一套 REST API,覆盖文本/文件分享、分片上传、 +预签名直传、管理后台与审计日志查询。本文档与 `server/internal/api/` 实际实现逐一对齐, +交互式规范见站内 `/openapi`(源文件 `docs/openapi.yaml`)。 + +## Base URL + +- 服务默认监听 `:8466`,Base URL 为 `http://:8466`(下文示例统一用 `http://localhost:8466`)。 +- **业务路由挂根路径**(与参考实现一致):`/share/*`、`/chunk/*`、`/presign/*`、`/admin/*`、`/setup`。 +- 仅两个公共接口带 `/api/v1` 前缀:`/api/v1/health`、`/api/v1/config`。 + +## 统一响应封装 + +所有 JSON 接口返回统一结构,HTTP 状态码与 `code` 一致;失败时 `data` 缺省: + +```json +{ "code": 200, "msg": "ok", "data": { } } +``` + +失败示例(404): + +```json +{ "code": 404, "msg": "文件不存在" } +``` + +个别端点直接返回原始内容而非 JSON 封装(文档中已单独标注): + +| 端点 | 响应形式 | +|---|---| +| `GET /share/select?code=`(文本分享) | `text/plain; charset=utf-8` 正文 | +| `GET /share/select?code=`(文件分享) | 文件二进制流(200/206,支持 Range) | +| `GET /share/download?key=&code=`(文件分享) | 文件二进制流(200/206,支持 Range) | +| `GET /admin/file/download?id=`(文件分享) | 文件二进制流 | +| `GET /setup` / `POST /setup`(表单) | HTML 向导/成功页 | + +## 字段命名约定 + +- 接口字段以 **snake_case** 为主(`file_code`、`size_bytes`)。 +- 文件列表与审计日志的行字段同时输出 **snake_case 与 camelCase 双份**(如 `expired_at` 与 `expiredAt`),文档以 snake_case 为准,camelCase 仅为前端兼容保留。 + +## 认证 + +- 游客接口无需认证;是否允许游客上传由配置 `openUpload` 控制(关闭时上传类接口要求管理员 `Authorization: Bearer `,否则 403)。 +- 管理接口(`/admin/login` 除外)一律要求 `Authorization: Bearer `,无效/缺失返回 401。 +- 详情见《认证与限流》。 + +## 限流 + +按 IP(可信代理场景解析 `X-Forwarded-For`)维度限流,超限返回 **423**: + +| 规则 | 计数时机 | 默认(次/窗口) | 相关配置 | +|---|---|---|---| +| `upload` | **上传成功**后计数 | 10 次 / 1 分钟 | `uploadCount` / `uploadMinute` | +| `error` | 取件失败(404/过期)时计数 | 10 次 / 1 分钟 | `errorCount` / `errorMinute` | +| `login` | 登录失败时计数 | 5 次 / 15 分钟 | `loginCount` / `loginMinute` | +| `metadata` | 每次访问即计数 | 同 `error` | `errorCount` / `errorMinute` | + +## 初始化守卫 + +系统未初始化(未设置管理员密码)时,除 `GET|POST /setup` 与 `GET /api/v1/health` 外, +**所有接口一律返回 428**: + +```json +{ "code": 428, "msg": "系统未初始化,请先完成初始化" } +``` + +首次部署请先访问 `GET /setup` 获取 HTML 向导,或直接 `POST /setup` 完成初始化(见《管理后台 API》初始化章节)。 + +## 审计 + +所有上传/下载端点经审计中间件自动落库(操作时间/IP/UA/设备解析/动作/结果/字节数/耗时/角色), +失败与被拒绝的请求同样记录;管理端经 `GET /admin/audit/list` 查询,详见《审计日志》。 + +## 端点总览 + +| 模块 | 端点 | +|---|---| +| 公共 | `GET /api/v1/health` · `GET /api/v1/config` · `GET /robots.txt`(输出 `robotsText` 配置) | +| 初始化 | `GET /setup` · `POST /setup` | +| 文本分享 | `POST /share/text` | +| 文件分享 | `POST /share/file` | +| 查询与取件 | `GET/POST /share/metadata` · `GET /share/select` · `POST /share/select` · `GET /share/download` | +| 分片上传 | `POST /chunk/upload/init` · `POST /chunk/upload/{uploadID}/{chunkIndex}` · `GET /chunk/upload/status/{uploadID}` · `POST /chunk/upload/complete/{uploadID}` · `DELETE /chunk/upload/{uploadID}` | +| 预签名直传 | `POST /presign/upload/init` · `PUT /presign/upload/proxy/{uploadID}` · `POST /presign/upload/confirm/{uploadID}` · `GET /presign/upload/status/{uploadID}` · `DELETE /presign/upload/{uploadID}` | +| 管理后台 | `POST /admin/login` · `GET /admin/verify` · `POST /admin/logout` · `GET /admin/dashboard` · 文件管理 `/admin/file/*` · 配置 `/admin/config/*` · 密码 `/admin/settings/password` | +| 审计日志 | `GET /admin/audit/list`(别名 `/admin/audit/logs`) | + +## 时间与编码 + +- 时间字段一律 RFC 3339(如 `2025-06-01T12:00:00+08:00`);管理员会话过期时间为 Unix 秒。 +- 请求体支持 `application/json` 与 `application/x-www-form-urlencoded`(上传类为 `multipart/form-data`),文档示例以 JSON/curl 为主。 +- CORS:公开接口放开(Bearer 认证,无 Cookie CSRF 面);**管理端 `/admin/*` 已收紧**——携带 Origin 且既不同源也不在 `site_domain` 白名单时不下发 CORS 头(浏览器拦截跨域读取)。 + +## 交互式文档 + +- 站内文档页:`/docs`(渲染本目录 markdown,构建时内嵌)。 +- Swagger UI:`/openapi`(渲染 `docs/openapi.yaml`,构建时内嵌)。 +- OpenAPI 规范源文件为仓库内 `docs/openapi.yaml`;如需经后端直接下载, + 需在部署时把它拷贝进前端静态产物 `web/dist/`(未拷贝时该路径按 SPA 回退返回页面)。 diff --git a/docs/api/01-auth.md b/docs/api/01-auth.md new file mode 100644 index 0000000..b0be0ba --- /dev/null +++ b/docs/api/01-auth.md @@ -0,0 +1,105 @@ +# 认证与限流 + +## 角色 + +| 角色 | 能力 | +|---|---| +| 游客(无 Authorization 头) | 取件、查询元信息;`openUpload=1` 时可上传 | +| 管理员(`Authorization: Bearer `) | 全部能力 + `/admin/*` 管理接口 | + +## 管理员令牌 + +- 由 `POST /admin/login` 用管理员密码换取,HS256 JWT,默认有效期 **7 天**(`adminSessionExpire`,1~365 整天,v2.5.6 起由 30 天缩短)。 +- 请求头格式:`Authorization: Bearer `。 +- **改密/重置管理员密码会轮换 `jwt_secret`,所有已签发令牌立即失效**(401)。 +- 密码存储为 bcrypt(cost 12);历史 `sha256$`/明文格式在登录成功后自动升级重哈希,无需手动迁移。 +- 游客上传关闭(`openUpload=0`)时,上传类接口也可用管理员 Bearer 令牌通过鉴权。 + +## 认证失败语义 + +| 场景 | 状态码 | +|---|---| +| `/admin/*` 缺失/无效令牌 | 401 | +| `POST /admin/login` 密码错误 | 401(并计入 login 限流) | +| 游客上传被关闭且未携带有效令牌 | 403 | +| 代理下载 `key` 校验失败 | 403 | + +## 未初始化(428) + +管理员密码未设置(`admin_token` 为空)时,除 `GET|POST /setup` 与 `GET /api/v1/health` 外全部接口返回 428。 +完成 `POST /setup` 初始化后自动解除。 + +## 限流规则 + +限流按 **客户端 IP** 维度(配置 `FCB_TRUSTED_PROXIES` 声明可信代理 CIDR,命中时解析 `X-Forwarded-For` 取真实 IP), +窗口计数原子化存储于缓存(未配置 Redis 时为进程内存)。**超限一律返回 423**: + +```json +{ "code": 423, "msg": "请求次数过多,请稍后再试" } +``` + +| 规则 | 生效端点 | 计数时机 | 默认 | 配置键 | +|---|---|---|---|---| +| `upload` | `/share/text`、`/share/file`、`/chunk/upload/*`、`/presign/upload/*` | **成功后**计数(进入时仅检查) | 10 次 / 1 分钟 | `uploadCount`、`uploadMinute` | +| `error` | `/share/select`、`/share/download` | 取件失败(不存在/过期/鉴权失败)时计数 | 10 次 / 1 分钟 | `errorCount`、`errorMinute` | +| `login` | `/admin/login` | 登录失败时计数 | 5 次 / 15 分钟 | `loginCount`、`loginMinute` | +| `metadata` | `/share/metadata` | **每次访问即计数**(含失败) | 同 `error` | `errorCount`、`errorMinute` | + +- 规则值可由管理端 `PATCH /admin/config/update` 运行时修改,立即生效(无需重启)。 +- 取件成功(`/share/select`、`/share/download`)不计入 `error` 限流。 + +## 代理下载令牌(key) + +`GET /share/download` 的 `key` 由服务端按窗口生成: +`sha256(code + timeFactor + "000" + jwt_secret)`,`timeFactor = unix秒 / 1000`(约 16.7 分钟一个窗口)。 +服务端**同时接受当前与上一窗口**的令牌,避免窗口边界竞态。令牌通过 `POST /share/select` 的响应 +`download_url` 下发,客户端不应自行构造。 + +## 示例 + +登录获取令牌: + +```bash +curl -s http://localhost:8466/admin/login \ + -H 'Content-Type: application/json' \ + -d '{"password":"your-admin-password"}' +``` + +```json +{ + "code": 200, "msg": "ok", + "data": { + "id": "admin", "username": "admin", + "token": "eyJhbGciOiJIUzI1NiIs...", + "token_type": "Bearer", + "expires_at": 1750000000, + "expires_in": 604800 + } +} +``` + +携带令牌调用管理接口: + +```bash +TOKEN="eyJhbGciOiJIUzI1NiIs..." +curl -s http://localhost:8466/admin/dashboard -H "Authorization: Bearer $TOKEN" +``` + +校验令牌是否有效: + +```bash +curl -s http://localhost:8466/admin/verify -H "Authorization: Bearer $TOKEN" +``` + +```json +{ + "code": 200, "msg": "ok", + "data": { "id": "admin", "username": "admin", "token": "eyJhbGciOiJIUzI1NiIs...", "token_type": "Bearer", "expires_at": 1750000000 } +} +``` + +令牌失效时: + +```json +{ "code": 401, "msg": "令牌无效或已过期" } +``` diff --git a/docs/api/02-text-share.md b/docs/api/02-text-share.md new file mode 100644 index 0000000..b7d3285 --- /dev/null +++ b/docs/api/02-text-share.md @@ -0,0 +1,91 @@ +# 文本分享 + +创建纯文本分享,返回取件码。文本大小上限 **222KB**(超限建议改用文件分享);请求体全局上限 1MiB,`Content-Length` >441KB 时读前直接 403。 +经审计中间件落库(action=upload)。 + +## POST /share/text + +**请求参数**(`application/x-www-form-urlencoded`,亦支持 multipart;`text` 为必需): + +| 参数 | 类型 | 必需 | 默认 | 说明 | +|---|---|---|---|---| +| `code` | string,可选;自定义提取码,5-8 位字母或数字(空=随机生成;占用 400「该提取码已被占用」) | +| `text` | string | ✅ | - | 文本内容(≤222KB,按 UTF-8 字节数) | +| `expire_value` | int | ❌ | `1` | 过期值(配合 `expire_style`) | +| `expire_style` | string | ❌ | `day` | `day`/`hour`/`minute`/`count`/`forever`(须在站点允许列表内) | + +过期语义: + +- `day`/`hour`/`minute`:按时间过期,`expired_count = -1`。 +- `count`:按次数过期,取件 `expire_value` 次后失效(`expired_count = expire_value`); + **v2 需求 ④**:`max_save_count>0` 时 `expire_value` 不得超出该上限,超限 403。 +- `forever`:永久(需站点允许;`max_save_seconds>0` 时其他方式受最长保存上限约束,超限 403)。 + +> 可选值与上限来自公开配置 `GET /api/v1/config`(`expireStyle`、`max_save_seconds`、 +> `max_save_count`),上传页动态读取并在范围内选择;管理端改策略后立即生效。 + +**curl 示例**: + +```bash +# 自定义提取码(可选):-d 'code=MYCODE1' +curl -s -X POST http://localhost:8466/share/text \ + -d 'text=你好,文件快传' \ + -d 'expire_value=1' \ + -d 'expire_style=day' +``` + +**成功响应**(200): + +```json +{ "code": 200, "msg": "ok", "data": { "code": "8XQ2M" } } +``` + +`data.code` 为 5 位取件码(数字或大写字母+数字,取决于 `code_generate_type`)。 + +**错误响应**: + +```json +{ "code": 400, "msg": "过期时间类型错误" } +``` + +```json +{ "code": 400, "msg": "过期时间值必须大于 0" } +``` + +```json +{ "code": 403, "msg": "内容过多,建议采用文件形式" } +``` + +```json +{ "code": 403, "msg": "限制最长时间为 7天,可换用其他方式" } +``` + +```json +{ "code": 403, "msg": "限制次数最多为 5 次" } +``` + +```json +{ "code": 423, "msg": "请求次数过多,请稍后再试" } +``` + +> 游客上传关闭(`openUpload=0`)时需携带管理员令牌,否则 403: +> `{"code":403,"msg":"本站未开启游客上传,如需上传请先登录后台"}` + +## 取回文本 + +文本分享的取回走统一的取件接口(消耗次数): + +- `GET /share/select?code=` → `text/plain` 正文即文本内容(响应头 `Content-Disposition` 带文件名,无扩展名时为 `.txt`)。 +- `POST /share/select`(`{"code":"8XQ2M"}`)→ JSON,`data.text` / `data.content` 为文本内容。 + +示例: + +```bash +curl -s "http://localhost:8466/share/select?code=8XQ2M" +``` + +```text +你好,文件快传 +``` + +**v3.1 变更**:① 支持 JSON 提交(`Content-Type: application/json`,字段同名);② 空文本 400「分享内容不能为空」;③ 可选 `code` 自定义提取码(5-8 位字母数字,占用 400)。 diff --git a/docs/api/03-file-share.md b/docs/api/03-file-share.md new file mode 100644 index 0000000..10a7e1a --- /dev/null +++ b/docs/api/03-file-share.md @@ -0,0 +1,103 @@ +# 文件分享 + +上传单个文件并创建分享。支持扩展名/MIME 白名单 + **magic bytes 防伪**(读文件前 64 字节校验, +伪造类型返回 403)。经审计中间件落库(action=upload,记录文件总大小与实际传输字节)。 + +## POST /share/file + +**请求参数**(`multipart/form-data`): + +| 参数 | 类型 | 必需 | 默认 | 说明 | +|---|---|---|---|---| +| `code` | string,可选;自定义提取码,5-8 位字母或数字(空=随机生成;占用 400) | +| `file` | file | ✅ | - | 上传的文件(大小 ≤ 生效上限:`max_file_size>0` 时为其,否则 `uploadSize`) | +| `expire_value` | int | ❌ | `1` | 过期值(配合 `expire_style`;`count` 型受 `max_save_count` 约束) | +| `expire_style` | string | ❌ | `day` | `day`/`hour`/`minute`/`count`/`forever`(须在 `expireStyle` 白名单内) | + +**curl 示例**: + +```bash +curl -s -X POST http://localhost:8466/share/file \ + -F 'file=@./report.pdf;type=application/pdf' \ + -F 'expire_value=7' \ + -F 'expire_style=day' +``` + +**成功响应**(200): + +```json +{ "code": 200, "msg": "ok", "data": { "code": "K3P9W", "name": "report.pdf" } } +``` + +**错误响应**: + +```json +{ "code": 400, "msg": "缺少上传文件 file 字段" } +``` + +```json +{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" } +``` + +> 大小上限为动态策略(v2 需求 ④⑩):管理端改 `max_file_size`(0=回落 `uploadSize`)后 +> **下一次上传立即按新上限执行**,无需重启;上限值可经 `GET /api/v1/config` 的 +> `max_file_size`/`maxFileSize` 字段读取。 + +```json +{ "code": 403, "msg": "不允许上传该类型文件" } +``` + +```json +{ "code": 403, "msg": "文件内容与扩展名不匹配,疑似伪造类型" } +``` + +```json +{ "code": 403, "msg": "限制最长时间为 7天,可换用其他方式" } +``` + +```json +{ "code": 403, "msg": "限制次数最多为 5 次" } +``` + +```json +{ "code": 403, "msg": "请求次数过多,请稍后再试" } +``` + +```json +{ "code": 507, "msg": "存储空间已达到管理员设置的容量上限" } +``` + +```json +{ "code": 503, "msg": "存储服务不可用,请稍后再试" } +``` + +## 文件类型白名单 + +由配置 `allowed_file_types` 控制(管理端可改): + +- `*`:不限制(默认)。 +- 扩展名规则:`.png`、`pdf`(自动补点)等,按文件名后缀匹配。 +- MIME 规则:`image/*`、`application/pdf` 等,按请求 `Content-Type` 通配匹配。 + +已知类型(png/jpg/gif/webp/bmp/pdf/zip/rar/7z/gz/mp3/mp4/exe/elf)会做 **magic bytes 交叉校验**: +扩展名或 Content-Type 声明了已知类型,但文件头不匹配时拒绝(403「疑似伪造类型」)。 + +## 下载取件 + +- `GET /share/select?code=`:消耗 1 次取件,返回文件流(`200` 全量 / `206` 区间, + 支持 `Range` 请求头;响应含 `Accept-Ranges: bytes`、`Content-Disposition: attachment; filename*=UTF-8''...`)。 +- `POST /share/select`:返回详情 JSON,`download_url` 为代理下载地址(见下)。 +- `GET /share/download?key=&code=`:代理下载,消耗 1 次,同样支持 Range。 + +Range 示例(取前 1024 字节): + +```bash +curl -s -H 'Range: bytes=0-1023' -o part.bin \ + "http://localhost:8466/share/select?code=K3P9W" +``` + +区间越界返回: + +```json +{ "code": 416, "msg": "请求范围超出文件大小" } +``` diff --git a/docs/api/04-pickup.md b/docs/api/04-pickup.md new file mode 100644 index 0000000..eb8576d --- /dev/null +++ b/docs/api/04-pickup.md @@ -0,0 +1,173 @@ +# 分享查询与取件 + +查询分享元信息(不消耗次数)与真正取件(消耗次数)的完整接口。 +除 `metadata` 每次 423 限流计数外,取件失败还会计入 `error` 限流。 + +## 元信息:GET /share/metadata + +按取件码查询元信息,**不消耗次数**。每次访问即计入 `metadata` 限流。 + +**参数**:`code`(query,必需)。 + +```bash +curl -s "http://localhost:8466/share/metadata?code=K3P9W" +``` + +**成功响应**(200): + +```json +{ + "code": 200, "msg": "ok", + "data": { + "code": "K3P9W", + "name": "report.pdf", + "size": 1048576, + "type": "file", + "is_text": false, + "created_at": "2025-06-01T12:00:00+08:00", + "expired_at": "2025-06-08T12:00:00+08:00", + "expires_at": "2025-06-08T12:00:00+08:00", + "expired_count": -1, + "used_count": 3, + "remaining_downloads": null + } +} +``` + +字段说明: + +| 字段 | 说明 | +|---|---| +| `type` | `text`(文本分享)/ `file`(文件分享) | +| `is_text` | 是否文本分享 | +| `size` | 字节数 | +| `expired_at` / `expires_at` | 过期时间(RFC 3339);永久分享为 `null` | +| `expired_count` | `-1` 按时间/永久;`>0` 剩余可取次数(原始限额) | +| `used_count` | 已取次数 | +| `remaining_downloads` | 剩余可取次数(仅次数型分享有值,否则 `null`) | + +> 不返回存储路径等敏感字段。 + +**错误响应**: + +```json +{ "code": 404, "msg": "文件不存在" } +``` + +```json +{ "code": 404, "msg": "文件已过期" } +``` + +## 元信息(POST):POST /share/metadata + +等价的 JSON 版本(`code` 放请求体): + +```bash +curl -s -X POST http://localhost:8466/share/metadata \ + -H 'Content-Type: application/json' \ + -d '{"code":"K3P9W"}' +``` + +响应与 GET 版本一致。 + +## 取件(消耗次数):GET /share/select + +**每调用一次消耗 1 次取件**(次数型分享扣减 `expired_count`;时间型扣减不计)。 + +- 文本分享:返回 `text/plain; charset=utf-8` 正文(非 JSON 封装),`Content-Disposition` 携带文件名。 +- 文件分享:返回文件流(`200` 全量 / `206` 区间),支持 `Range`。 + +```bash +curl -s -OJ "http://localhost:8466/share/select?code=K3P9W" +``` + +次数耗尽或已过期: + +```json +{ "code": 404, "msg": "文件已过期" } +``` + +超限(计入 error 限流): + +```json +{ "code": 423, "msg": "请求次数过多,请稍后再试" } +``` + +## 取件详情:POST /share/select + +返回元信息 + 文本内容/下载地址的 JSON 详情。**消耗语义**:次数型分享(`expired_count >= 0`) +返回代理地址 `download_url` 且本次**不消耗**(消耗发生在访问代理地址时);时间型/永久/文本分享在本次消耗。 + +```bash +curl -s -X POST http://localhost:8466/share/select \ + -H 'Content-Type: application/json' \ + -d '{"code":"K3P9W"}' +``` + +**文件分享响应**(200): + +```json +{ + "code": 200, "msg": "ok", + "data": { + "code": "K3P9W", + "name": "report.pdf", + "size": 1048576, + "type": "file", + "is_text": false, + "created_at": "2025-06-01T12:00:00+08:00", + "expired_at": "2025-06-08T12:00:00+08:00", + "expires_at": "2025-06-08T12:00:00+08:00", + "expired_count": -1, + "used_count": 4, + "remaining_downloads": null, + "text": "/share/download?key=9f2c…&code=K3P9W", + "download_url": "/share/download?key=9f2c…&code=K3P9W" + } +} +``` + +> S3 引擎下时间型/永久分享的 `download_url` 可能是预签名直链(1 小时有效)而非代理地址; +> 次数型分享恒为代理地址 `"/share/download?key=…&code=…"`。 + +**文本分享响应**(200): + +```json +{ + "code": 200, "msg": "ok", + "data": { + "code": "8XQ2M", "name": "Text.txt", "size": 24, "type": "text", "is_text": true, + "created_at": "2025-06-01T12:00:00+08:00", + "expired_at": "2025-06-02T12:00:00+08:00", "expires_at": "2025-06-02T12:00:00+08:00", + "expired_count": -1, "used_count": 1, "remaining_downloads": null, + "text": "你好,文件快传", + "content": "你好,文件快传", + "download_url": null + } +} +``` + +## 代理下载:GET /share/download + +`POST /share/select` 返回的代理地址,**每次访问消耗 1 次**,支持 Range。 + +| 参数 | 说明 | +|---|---| +| `key` | 窗口令牌(服务端下发,双窗口校验) | +| `code` | 取件码 | + +```bash +curl -s -OJ "http://localhost:8466/share/download?key=9f2c…&code=K3P9W" +``` + +**错误响应**: + +```json +{ "code": 403, "msg": "下载鉴权失败" } +``` + +```json +{ "code": 404, "msg": "文件已过期" } +``` + +> `key` 鉴权失败会计入 error 限流;文本分享经该接口返回 JSON 封装 `data` 为文本内容。 diff --git a/docs/api/05-chunk-upload.md b/docs/api/05-chunk-upload.md new file mode 100644 index 0000000..b7ae432 --- /dev/null +++ b/docs/api/05-chunk-upload.md @@ -0,0 +1,212 @@ +# 分片上传 + +大文件分片上传:客户端把文件切成固定大小的分片逐个上传,服务端按索引合并并做 SHA256 校验。 +支持**断点续传**(相同 `file_hash` + 大小 + 文件名的未完成会话自动续传)。 +需站点开启 `enableChunk`(公共配置 `enableChunk` 返回 `true`)。 +分片会话保留 24 小时。全部端点经审计中间件落库(action=upload)。 + +## 上传流程 + +```text +POST /chunk/upload/init → upload_id, total_chunks +POST /chunk/upload/{id}/{index} → 逐片上传(0 起,可并发) +GET /chunk/upload/status/{id} → 断点续传时查进度 +POST /chunk/upload/complete/{id} → 合并 + SHA256 → 取件码 +DELETE /chunk/upload/{id} → 取消(可选) +``` + +## 初始化:POST /chunk/upload/init + +**请求体**(JSON,亦兼容表单): + +| 参数 | 类型 | 必需 | 默认 | 说明 | +|---|---|---|---|---| +| `file_name` | string | ✅ | - | 文件名(会做清理与白名单校验) | +| `file_size` | int | ✅ | - | 文件总字节数(>0;服务端按分片数校验上限) | +| `chunk_size` | int | ❌ | `5242880`(5MB) | 每片大小(字节),硬上限 32MiB(超出 400「chunk_size 过大」) | +| `file_hash` | string | ❌ | - | 整文件 SHA256(断点续传的匹配键) | + +**curl 示例**: + +```bash +curl -s -X POST http://localhost:8466/chunk/upload/init \ + -H 'Content-Type: application/json' \ + -d '{"file_name":"movie.mp4","file_size":15728640,"chunk_size":5242880,"file_hash":""}' +``` + +**成功响应**(200,新建会话): + +```json +{ + "code": 200, "msg": "ok", + "data": { + "existed": false, + "upload_id": "3f6b8c2a4d5e6f708192a3b4c5d6e7f8", + "chunk_size": 5242880, + "total_chunks": 3, + "uploaded_chunks": [] + } +} +``` + +**断点续传响应**(200,命中未完成会话):返回既有会话,`uploaded_chunks` 为已传分片索引列表, +客户端只需补传缺失分片(注意:`existed` 字段恒为 `false`,是否续传以 `upload_id` 复用且 +`uploaded_chunks` 非空为准): + +```json +{ + "code": 200, "msg": "ok", + "data": { + "existed": false, + "upload_id": "3f6b8c2a4d5e6f708192a3b4c5d6e7f8", + "chunk_size": 5242880, + "total_chunks": 3, + "uploaded_chunks": [0, 1] + } +} +``` + +**错误响应**: + +```json +{ "code": 400, "msg": "file_size 必须大于 0" } +``` + +```json +{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" } +``` + +```json +{ "code": 403, "msg": "分片上传未启用" } +``` + +## 上传分片:POST /chunk/upload/{uploadID}/{chunkIndex} + +主路径(与参考实现语义一致)。`chunkIndex` 从 `0` 起。 + +**multipart 字段**:`chunk`(必需,该分片的二进制数据)。 + +**curl 示例**: + +```bash +split -b 5242880 movie.mp4 part- # 本地分片 +curl -s -X POST http://localhost:8466/chunk/upload/3f6b8c2a4d5e6f708192a3b4c5d6e7f8/0 \ + -F 'chunk=@./part-aa' +``` + +**成功响应**(200): + +```json +{ "code": 200, "msg": "ok", "data": { "chunk_hash": "9af1…", "chunk_index": 0 } } +``` + +重复上传已完成的分片(幂等跳过): + +```json +{ "code": 200, "msg": "ok", "data": { "chunk_hash": "9af1…", "chunk_index": 0, "skipped": true } } +``` + +**错误响应**: + +```json +{ "code": 404, "msg": "上传会话不存在" } +``` + +```json +{ "code": 400, "msg": "无效的分片索引" } +``` + +```json +{ "code": 400, "msg": "分片大小超过声明值: 最大 5242880, 实际 5300000" } +``` + +```json +{ "code": 400, "msg": "缺少分片文件字段 chunk" } +``` + +```json +{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" } +``` + +> 约束:单分片 ≤ `chunk_size`(init 声明值)且 ≤ **32MiB 硬上限**(init 时 `chunk_size>33554432` 直接 400「chunk_size 过大」); +> 总大小(init 按分片数上限、上传/合并按累计)受**动态策略上限**约束——`max_file_size>0` 时为其, +> 否则回落 `uploadSize`(v2 需求 ④⑩,管理端改后立即生效,超限清理会话);首个分片做 magic bytes +> 防伪校验(403「文件内容校验失败:…」)。分片哈希由服务端计算;合并时与分片记录交叉校验,不一致报 400。 +> +> **enableChunk 开关**:管理端关闭分片上传后,`/chunk/upload/*` 全部端点返回 403「分片上传未启用」(后端强制,前端仅隐藏入口)。 + +## 查询进度:GET /chunk/upload/status/{uploadID} + +```bash +curl -s http://localhost:8466/chunk/upload/status/3f6b8c2a4d5e6f708192a3b4c5d6e7f8 +``` + +**成功响应**(200): + +```json +{ + "code": 200, "msg": "ok", + "data": { + "upload_id": "3f6b8c2a4d5e6f708192a3b4c5d6e7f8", + "file_name": "movie.mp4", + "file_size": 15728640, + "chunk_size": 5242880, + "total_chunks": 3, + "uploaded_chunks": [0, 1], + "progress": 66.66666666666667 + } +} +``` + +## 完成合并:POST /chunk/upload/complete/{uploadID} + +全部分片到齐后调用。服务端按索引有序合并 + SHA256 校验,成功后创建分享并清理分片。 + +**请求体**(JSON,亦兼容表单): + +| 参数 | 类型 | 必需 | 默认 | 说明 | +|---|---|---|---|---| +| `expire_value` | int | ❌ | `1` | 过期值 | +| `expire_style` | string | ❌ | `day` | 过期方式(同文件分享) | + +```bash +curl -s -X POST http://localhost:8466/chunk/upload/complete/3f6b8c2a4d5e6f708192a3b4c5d6e7f8 \ + -H 'Content-Type: application/json' \ + -d '{"expire_value":1,"expire_style":"day"}' +``` + +**成功响应**(200): + +```json +{ "code": 200, "msg": "ok", "data": { "code": "R7T2K", "name": "movie.mp4" } } +``` + +**错误响应**: + +```json +{ "code": 400, "msg": "分片不完整" } +``` + +```json +{ "code": 400, "msg": "分片哈希校验失败,请重新上传" } +``` + +```json +{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" } +``` + +## 取消上传:DELETE /chunk/upload/{uploadID} + +清理分片文件与上传记录,释放容量预留。 + +```bash +curl -s -X DELETE http://localhost:8466/chunk/upload/3f6b8c2a4d5e6f708192a3b4c5d6e7f8 +``` + +```json +{ "code": 200, "msg": "ok", "data": { "message": "上传已取消" } } +``` + +```json +{ "code": 404, "msg": "上传会话不存在" } +``` diff --git a/docs/api/06-presign.md b/docs/api/06-presign.md new file mode 100644 index 0000000..5ab6eab --- /dev/null +++ b/docs/api/06-presign.md @@ -0,0 +1,210 @@ +# 预签名直传 + +服务端预生成上传地址,客户端直接向存储引擎(或服务端代理)上传文件,最后确认建分享。 +两种模式: + +| 模式 | 引擎 | 上传方式 | +|---|---|---| +| `direct` | S3(含 MinIO/R2 等 S3 兼容存储) | 客户端 `PUT` 到预签名 URL,直连对象存储 | +| `proxy` | 本地 / WebDAV | 客户端 `PUT` multipart 到服务端代理接口 | + +> 本地 / WebDAV 引擎不支持预签名直链,init 返回正常 `proxy` 模式(仅当引擎预签名调用本身异常时才报错)。 +> 会话有效期 **900 秒**(15 分钟)。proxy 模式响应另含 `legacy_proxy_upload_url` +> (`/api` 前缀的兼容别名,已废弃,与 `upload_url` 等价)。 +> 全部端点经审计中间件落库(action=upload)。 + +## 上传流程 + +```text +direct 模式: +POST /presign/upload/init → upload_url(S3 预签名 PUT) +PUT → 客户端直传 S3(无认证头) +POST /presign/upload/confirm/{id} → 确认 → 取件码 + +proxy 模式: +POST /presign/upload/init → upload_url = /presign/upload/proxy/{id} +PUT /presign/upload/proxy/{id} → multipart 上传,服务端转存并直接建分享 +``` + +## 初始化:POST /presign/upload/init + +**请求体**(JSON,亦兼容表单): + +| 参数 | 类型 | 必需 | 默认 | 说明 | +|---|---|---|---|---| +| `file_name` | string | ✅ | - | 文件名(清理 + 白名单校验) | +| `file_size` | int | ✅ | - | 文件字节数(≤ 生效上限:`max_file_size>0` 时为其,否则 `uploadSize`) | +| `expire_value` | int | ❌ | `1` | 过期值(`count` 型受 `max_save_count` 约束) | +| `expire_style` | string | ❌ | `day` | 过期方式(须在 `expireStyle` 白名单内) | + +**curl 示例**: + +```bash +curl -s -X POST http://localhost:8466/presign/upload/init \ + -H 'Content-Type: application/json' \ + -d '{"file_name":"backup.zip","file_size":20971520,"expire_value":7,"expire_style":"day"}' +``` + +**S3(direct)成功响应**(200): + +```json +{ + "code": 200, "msg": "ok", + "data": { + "upload_id": "6a1e…", + "upload_url": "https://minio:9000/filecodebox/share/data/2025/06/01/6a1e…/backup.zip?X-Amz-…", + "mode": "direct", + "expires_in": 900, + "file_path": "share/data/2025/06/01/6a1e…" + } +} +``` + +**local/WebDAV(proxy)成功响应**(200): + +```json +{ + "code": 200, "msg": "ok", + "data": { + "upload_id": "6a1e…", + "upload_url": "/presign/upload/proxy/6a1e…", + "proxy_upload_url": "/presign/upload/proxy/6a1e…", + "mode": "proxy", + "expires_in": 900, + "file_path": "share/data/2025/06/01/6a1e…" + } +} +``` + +**错误响应**: + +```json +{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" } +``` + +> 大小上限为动态策略(v2 需求 ④⑩):管理端改 `max_file_size`(0=回落 `uploadSize`)后 +> 立即按新上限校验 init 声明的 `file_size`。 + +```json +{ "code": 403, "msg": "不允许上传该类型文件" } +``` + +```json +{ "code": 507, "msg": "存储空间已达到管理员设置的容量上限" } +``` + +## 直传确认:POST /presign/upload/confirm/{uploadID} + +`direct` 模式专用:客户端向 S3 `PUT` 完成后调用。服务端会核对实际对象(多引擎一致): + +- **大小核对**:实际大小与声明差 >1KB → 400「文件大小与声明不符」;超过策略上限(`max_file_size`)→ **删除对象、释放容量预留**并 403; +- **内容校验**:取对象前 64 字节做 magic bytes 白名单校验(失败删除对象并报错); +- 全部通过后创建分享记录。 + +```bash +# 1) 直传 S3(注意:不要带 Authorization 头,预签名 URL 自带鉴权) +curl -X PUT "" --upload-file ./backup.zip +# 2) 确认 +curl -s -X POST http://localhost:8466/presign/upload/confirm/6a1e… +``` + +**成功响应**(200): + +```json +{ "code": 200, "msg": "ok", "data": { "code": "B4N8Q", "name": "backup.zip" } } +``` + +**错误响应**: + +```json +{ "code": 404, "msg": "文件未上传或上传失败" } +``` + +```json +{ "code": 400, "msg": "文件大小与声明不符" } +``` + +```json +{ "code": 403, "msg": "文件大小超过限制" } +``` + +```json +{ "code": 400, "msg": "此会话不支持direct模式" } +``` + +## 代理上传:PUT /presign/upload/proxy/{uploadID} + +`proxy` 模式专用:multipart 上传到服务端,服务端流式转存到存储引擎后立即创建分享记录。 + +**multipart 字段**:`file`(必需)。 + +**curl 示例**: + +```bash +curl -s -X PUT http://localhost:8466/presign/upload/proxy/6a1e… -F 'file=@./backup.zip' +``` + +**成功响应**(200): + +```json +{ "code": 200, "msg": "ok", "data": { "code": "B4N8Q", "name": "backup.zip" } } +``` + +**错误响应**: + +```json +{ "code": 400, "msg": "缺少上传文件 file 字段" } +``` + +```json +{ "code": 400, "msg": "文件大小与声明不符" } +``` + +```json +{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" } +``` + +> 文件实际大小须与 init 声明的 `file_size` 一致(±1KB 容差);成功后会话即删除,不可重复使用。 + +## 查询会话:GET /presign/upload/status/{uploadID} + +```bash +curl -s http://localhost:8466/presign/upload/status/6a1e… +``` + +**成功响应**(200): + +```json +{ + "code": 200, "msg": "ok", + "data": { + "upload_id": "6a1e…", + "file_name": "backup.zip", + "file_size": 20971520, + "mode": "proxy", + "created_at": "2025-06-01T12:00:00+08:00", + "expires_at": "2025-06-01T12:15:00+08:00", + "is_expired": false + } +} +``` + +## 取消会话:DELETE /presign/upload/{uploadID} + +删除会话并释放容量预留;`direct` 模式会尽力清理已直传到 S3 的对象。 + +```bash +curl -s -X DELETE http://localhost:8466/presign/upload/6a1e… +``` + +```json +{ "code": 200, "msg": "ok", "data": { "message": "上传会话已取消" } } +``` + +```json +{ "code": 404, "msg": "上传会话不存在" } +``` + +```json +{ "code": 404, "msg": "上传会话已过期" } +``` diff --git a/docs/api/07-admin.md b/docs/api/07-admin.md new file mode 100644 index 0000000..01b5d75 --- /dev/null +++ b/docs/api/07-admin.md @@ -0,0 +1,434 @@ +# 管理后台 API + +管理端接口:除 `POST /admin/login` 与初始化向导 `/setup` 外,一律要求 +`Authorization: Bearer `(见《认证与限流》),无效令牌 401。 + +## 初始化向导:GET /setup + +未初始化时返回 HTML 配置页(站点名称、管理员密码、上传/限流/保存策略); +已初始化时 `303` 重定向到 `/`。 + +```bash +curl -i http://localhost:8466/setup +``` + +## 初始化提交:POST /setup + +表单(浏览器向导)或 JSON 均可;成功后写入库配置 KV 并生成密码哈希与 `jwt_secret`。 +表单提交返回成功 HTML 页;JSON 提交返回 JSON。 + +**主要字段**: + +| 字段 | 必需 | 默认 | 说明 | +|---|---|---|---| +| `admin_password` | ✅ | - | 管理员密码(≥8 位) | +| `confirm_password` | ✅ | - | 确认密码(须一致) | +| `site_name` | ❌ | 文件快传 | 站点名称 | +| `upload_size_value` / `upload_size_unit` | ❌ | 10 / MB | 单文件大小限制(单位 KB/MB/GB) | +| `save_time_value` / `save_time_unit` | ❌ | 0 / day | 最长保存秒数(0=不限) | +| `expireStyle` | ❌ | day,hour,minute,forever,count | 过期方式(可多值/逗号分隔) | +| `code_generate_type` | ❌ | secret | 取件码类型 `number`/`secret` | +| `errorCount` / `errorMinute` | ❌ | 10 / 1 | 取件错误限流 | +| `loginCount` / `loginMinute` | ❌ | 5 / 15 | 登录失败限流 | +| `uploadCount` / `uploadMinute` | ❌ | 10 / 1 | 上传限流 | +| `allowed_file_types` | ❌ | `*` | 逗号分隔白名单 | +| `openUpload` / `enableChunk` | ❌ | 1 / 0 | 游客上传 / 分片开关(`1`/`true`/`on`/`yes`) | + +```bash +curl -s -X POST http://localhost:8466/setup \ + -H 'Content-Type: application/json' \ + -d '{"admin_password":"admin12345","confirm_password":"admin12345","site_name":"我的文件柜","upload_size_value":10,"upload_size_unit":"MB"}' +``` + +```json +{ "code": 200, "msg": "ok", "data": { "ok": true, "admin": "/#/admin" } } +``` + +**错误响应**(400,HTML 表单时内嵌错误提示): + +```json +{ "code": 400, "msg": "管理员密码至少 8 位" } +``` + +## 登录:POST /admin/login + +```bash +curl -s -X POST http://localhost:8466/admin/login \ + -H 'Content-Type: application/json' \ + -d '{"password":"admin12345"}' +``` + +**成功响应**(200): + +```json +{ + "code": 200, "msg": "ok", + "data": { + "id": "admin", "username": "admin", + "token": "eyJhbGciOiJIUzI1NiIs...", + "token_type": "Bearer", + "expires_at": 1750000000, + "expires_in": 604800 + } +} +``` + +**错误响应**: + +```json +{ "code": 401, "msg": "密码错误" } +``` + +```json +{ "code": 423, "msg": "请求次数过多,请稍后再试" } +``` + +## 校验会话:GET /admin/verify + +```bash +curl -s http://localhost:8466/admin/verify -H "Authorization: Bearer $TOKEN" +``` + +```json +{ "code": 200, "msg": "ok", "data": { "id": "admin", "username": "admin", "token": "eyJ…", "token_type": "Bearer", "expires_at": 1750000000 } } +``` + +## 登出:POST /admin/logout + +无状态 JWT,服务端仅返回确认(客户端应丢弃令牌): + +```json +{ "code": 200, "msg": "ok", "data": { "ok": true } } +``` + +## 仪表盘:GET /admin/dashboard + +```bash +curl -s http://localhost:8466/admin/dashboard -H "Authorization: Bearer $TOKEN" +``` + +```json +{ + "code": 200, "msg": "ok", + "data": { + "totalFiles": 42, + "storageUsed": "123456789", + "sysUptime": 1750000000000, + "yesterdayCount": 5, "yesterdaySize": "1048576", + "todayCount": 12, "todaySize": "5242880", + "activeCount": 40, "expiredCount": 2, + "textCount": 10, "fileCount": 32, "chunkedCount": 3, + "usedCount": 156, + "storageBackend": "local", + "uploadSizeLimit": 10485760, + "openUpload": 1, "enableChunk": 1, + "maxSaveSeconds": 0, + "topSuffixes": [ { "suffix": ".pdf", "count": 12 }, { "suffix": "Text", "count": 10 } ], + "recentFiles": [ { "id": 42, "code": "K3P9W", "name": "report.pdf", "size": 1048576, "created_at": "2025-06-01T12:00:00+08:00", "expired_at": "2025-06-08T12:00:00+08:00", "is_expired": false, "expired_count": -1, "used_count": 3, "is_text": false, "is_chunked": false, "is_permanent": false, "has_download_limit": false, "remaining_downloads": null, "file_hash": null, "prefix": "report", "suffix": ".pdf", "text": false, "createdAt": "2025-06-01T12:00:00+08:00", "expiredAt": "2025-06-08T12:00:00+08:00", "isExpired": false, "expiredCount": -1, "usedCount": 3, "isText": false, "isChunked": false, "isPermanent": false, "hasDownloadLimit": false, "remainingDownloads": null, "fileHash": null } ], + "recentActivities": [] + } +} +``` + +> `storageUsed`/`todaySize`/`yesterdaySize` 为字符串字节数;`sysUptime` 为服务启动时刻的 Unix 毫秒。 + +## 文件列表:GET /admin/file/list + +**参数**: + +| 参数 | 默认 | 说明 | +|---|---|---| +| `page` / `size` | 1 / 10 | 分页(size 1~100) | +| `keyword` | - | 模糊匹配取件码/文件名/哈希/文本内容 | +| `status` | - | `active` / `expired` | +| `type` | - | `text` / `file` / `chunked` | +| `sortBy` | `created_at` | `created_at`/`expired_at`/`name`/`size`/`used_count`/`code` | +| `sortOrder` | `desc` | `asc` / `desc` | + +```bash +curl -s "http://localhost:8466/admin/file/list?page=1&size=10&status=active&sortBy=size&sortOrder=desc" \ + -H "Authorization: Bearer $TOKEN" +``` + +```json +{ + "code": 200, "msg": "ok", + "data": { + "page": 1, "size": 10, "total": 40, + "summary": { "totalFiles": 42, "activeCount": 40, "expiredCount": 2, "textCount": 10, "fileCount": 32, "chunkedCount": 3, "storageUsed": 123456789, "usedCount": 156 }, + "data": [ + { "id": 42, "code": "K3P9W", "name": "report.pdf", "prefix": "report", "suffix": ".pdf", "size": 1048576, "is_text": false, "is_chunked": false, "is_expired": false, "expired_at": "2025-06-08T12:00:00+08:00", "expired_count": -1, "used_count": 3, "created_at": "2025-06-01T12:00:00+08:00", "has_download_limit": false, "is_permanent": false, "remaining_downloads": null, "file_hash": null, "text": false, "isText": false, "isChunked": false, "isExpired": false, "expiredAt": "2025-06-08T12:00:00+08:00", "expiredCount": -1, "usedCount": 3, "createdAt": "2025-06-01T12:00:00+08:00", "hasDownloadLimit": false, "isPermanent": false, "remainingDownloads": null, "fileHash": null } + ] + } +} +``` + +## 文件详情:GET /admin/file/detail + +`GET ?id=42` 或 `POST {"id":42}`。返回列表条目字段;文本分享额外含 `content`(全文)。 + +```json +{ "code": 200, "msg": "ok", "data": { "id": 42, "code": "K3P9W", "name": "report.pdf", "size": 1048576, "is_text": false, "expired_at": "2025-06-08T12:00:00+08:00", "expired_count": -1, "used_count": 3, "created_at": "2025-06-01T12:00:00+08:00", "file_hash": null } } +``` + +```json +{ "code": 404, "msg": "文件不存在" } +``` + +## 更新文件:PATCH /admin/file/update + +更新取件码/文件名(前后缀)/过期策略。**PATCH 为主名,POST 为兼容别名**。 + +**请求体**: + +| 字段 | 类型 | 说明 | +|---|---|---| +| `id` | int | 必需 | +| `code` | string | 新取件码(冲突 400「code已存在」) | +| `prefix` / `suffix` | string | 文件名前后缀 | +| `expired_at` | string | 过期时间(ISO 8601,如 `2025-07-01T00:00:00+08:00`) | +| `expired_count` | int | 取件次数上限(`-1` 按时间/永久) | + +```bash +curl -s -X PATCH http://localhost:8466/admin/file/update \ + -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ + -d '{"id":42,"expired_at":"2025-07-01T00:00:00+08:00","expired_count":10}' +``` + +```json +{ "code": 200, "msg": "ok", "data": "更新成功" } +``` + +```json +{ "code": 400, "msg": "code已存在" } +``` + +## 删除文件:DELETE /admin/file/delete + +删除分享记录并连带删除存储文件(文本分享无存储文件)。`DELETE` 或 `POST`,请求体 `{"id":42}`。 + +```bash +curl -s -X DELETE http://localhost:8466/admin/file/delete \ + -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"id":42}' +``` + +```json +{ "code": 200, "msg": "ok", "data": null } +``` + +```json +{ "code": 400, "msg": "请选择要删除的文件" } +``` + +## 批量删除:POST /admin/file/batch-delete + +`POST` 或 `DELETE`,请求体 `{"ids":[41,42,43]}`。返回逐条统计: + +```json +{ + "code": 200, "msg": "ok", + "data": { + "requestedCount": 3, "deletedCount": 2, "missingCount": 1, "failedCount": 0, + "deleted": [41, 42], "missing": [43], "failed": [], + "requested_count": 3, "deleted_count": 2, "missing_count": 1, "failed_count": 0 + } +} +``` + +## 批量更新:PATCH /admin/file/batch-update + +`PATCH` 或 `POST`。请求体:`ids[]` 必需;`expired_at`(ISO 8601)/`expired_count` 二选一; +`clearExpiredAt: true`(或 `clear_expired_at`)= 清空过期时间并置 `expired_count=-1`(永久)。 + +```bash +curl -s -X PATCH http://localhost:8466/admin/file/batch-update \ + -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ + -d '{"ids":[41,42],"clearExpiredAt":true}' +``` + +```json +{ + "code": 200, "msg": "ok", + "data": { "requestedCount": 2, "updatedCount": 2, "missingCount": 0, "failedCount": 0, "updated": 2, "missing": [], "failed": [], "requested_count": 2, "updated_count": 2, "missing_count": 0, "failed_count": 0 } +} +``` + +## 过期策略动作:PATCH /admin/file/policy-action + +对单个文件执行快捷策略。`PATCH` 或 `POST`。批量版为 `/admin/file/batch-policy-action`(`{"ids":[…]}`),响应结构同批量更新。 + +**请求体**: + +| 字段 | 说明 | +|---|---| +| `id` | 文件 ID | +| `action` | `extend_24h` / `extend_7d` / `make_permanent` / `reset_download_limit` | +| `downloadLimit` | 仅 `reset_download_limit` 用:新取件次数(默认 5,须 >0) | + +```bash +curl -s -X PATCH http://localhost:8466/admin/file/policy-action \ + -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ + -d '{"id":42,"action":"reset_download_limit","downloadLimit":3}' +``` + +```json +{ "code": 200, "msg": "ok", "data": { "id": 42, "action": "reset_download_limit" } } +``` + +```json +{ "code": 400, "msg": "不支持的策略动作" } +``` + +> `extend_24h`/`extend_7d` 在当前过期时间(未过期时)基础上顺延;`make_permanent` 清空过期时间并置次数 -1。 + +## 管理员下载:GET /admin/file/download?id= + +下载原文件(**不消耗取件次数**);文件返回二进制流(支持 Range),文本分享返回 JSON(`data` 为文本内容)。 + +```bash +curl -s -OJ "http://localhost:8466/admin/file/download?id=42" -H "Authorization: Bearer $TOKEN" +``` + +## 文本预览:GET /admin/file/preview + +仅文本分享可用(文件分享返回 400)。`maxChars` 截断长度(默认 4000,1~20000)。 + +```bash +curl -s "http://localhost:8466/admin/file/preview?id=41&maxChars=100" -H "Authorization: Bearer $TOKEN" +``` + +```json +{ + "code": 200, "msg": "ok", + "data": { + "id": 41, "code": "8XQ2M", "name": "Text.txt", "type": "text", + "content": "你好,文件快传", + "length": 24, "previewLength": 24, "truncated": false, + "maxChars": 100, "max_chars": 100, + "created_at": "2025-06-01T12:00:00+08:00", "createdAt": "2025-06-01T12:00:00+08:00" + } +} +``` + +```json +{ "code": 400, "msg": "仅文本分享支持预览" } +``` + +## 读取配置:GET /admin/config/get + +返回运行时配置 KV(含默认值与管理端修改)。`admin_token` 恒返回空串(屏蔽); +`jwt_secret` 不下发;存储引擎为进程级单例,`_engine_hint` 提示引擎配置修改需重启。 +v2 新增键(需求 ①②③④⑩)一并返回:`background_url`、`footer_text`、`footer_beian`、 +`notify_enabled`、`max_save_count`、`max_file_size` 等。 + +```json +{ + "code": 200, "msg": "ok", + "data": { + "site_name": "文件快传", + "name": "文件快传", + "description": "开箱即用的文件快传系统", + "page_explain": "…", "keywords": "…", + "notify_title": "系统通知", "notify_content": "…", "notify_enabled": 1, + "logo_url": "", + "favicon_url": "", + "background_url": "", "footer_text": "", "footer_beian": "", + "openUpload": 1, "uploadSize": 10485760, + "max_file_size": 0, "max_save_count": 0, + "allowed_file_types": ["*"], "expireStyle": ["day","hour","minute","forever","count"], + "code_generate_type": "secret", "enableChunk": 1, + "uploadMinute": 1, "uploadCount": 10, + "errorMinute": 1, "errorCount": 10, + "loginCount": 5, "loginMinute": 15, + "max_save_seconds": 0, "storageLimit": 0, + "opacity": 0.9, "background": "", "showAdminAddr": 0, "robotsText": "User-agent: *\nDisallow: /", + "adminSessionExpire": 604800, + "storage_path": "", "local_storage_path": "/app/data", + "file_storage": "local", + "admin_token": "", + "_engine_hint": { "storage_backend": "local", "note": "存储引擎为进程级单例,修改存储引擎相关配置后需重启服务生效" } + } +} +``` + +## 存储引擎热切换:POST /admin/storage/switch(v3) + +运行时切换存储引擎,**无需重启**: + +```bash +curl -s -X POST http://localhost:8466/admin/storage/switch \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"engine":"s3"}' +# 成功:{"code":200,"msg":"ok","data":{"ok":true,"engine":"s3"}} +# 失败:503 {"code":503,"msg":"存储引擎切换失败,已保持原引擎: …"} +``` + +- `engine` 仅接受 `local|s3|webdav`(400 中文错误)。 +- 切换流程:用最新 KV 参数构建新引擎 → 健康检查 → 通过才替换当前引擎并持久化 `storage_engine`。 +- 失败(构建/健康检查不过)返回 503,**原引擎与 KV 均保持不变**。 +- 切到当前引擎为幂等操作(直接返回 200)。 +- 旧文件按归属引擎(`file_codes.engine`)取回,切换后旧引擎文件仍可下载。 + +## 更新配置:PATCH /admin/config/update + +部分更新(JSON 对象,未提供的键不变;表单亦可)。**PATCH 为主名,POST 为兼容别名**。 + +- 仅接受管理端可见键(见上响应键集合),未知键忽略。 +- 数值型键自动转型:`openUpload`、`enableChunk`、`uploadSize`、`storageLimit`、限流四组、`max_save_seconds`、`adminSessionExpire`、`showAdminAddr`;v2 新增 `max_save_count`、`max_file_size`、`notify_enabled`;`opacity` 为浮点。 +- **v3.1**:`site_domain`(站点对外域名)可经本端点设置,非法格式 400(仅 http/https、主机+端口、不带路径)。 +- **v3 引擎键**:`storage_engine` 不经本端点修改(走 `POST /admin/storage/switch`);引擎参数键 + (`local_storage_path`、`webdav_url`、`webdav_root_path`、`webdav_username`、`webdav_password`、 + `s3_endpoint_url`、`s3_region_name`、`s3_bucket_name`、`s3_access_key_id`、`s3_secret_access_key`、 + `aws_session_token`、`s3_addressing_style`)可经本端点保存——保存后对应引擎实例缓存失效, + 下次切换/构建生效;敏感键空串或 `******` 表示不修改。 +- **v2 schema 校验**(`settings.KVSchema`,越界一律 400,中文错误信息): + - 整型边界:`max_file_size` ≤ 10GiB(10737418240)、`max_save_count` ≤ 100000、`max_save_seconds` ≤ 31536000(365 天)、`notify_enabled` ∈ {0,1}、`uploadCount` 1~10000、`uploadMinute` 1~1440 等; + - 字符串长度:`background_url` ≤ 2048、`footer_text` ≤ 2000、`footer_beian` ≤ 128、`notify_title` ≤ 128、`notify_content` ≤ 2000 字符; + - 列表键 `expireStyle` / `allowed_file_types`:须为字符串数组(或逗号分隔串)且至少保留一项; + - 错误示例:`{"code":400,"msg":"max_file_size 必须是整数"}`、`{"code":400,"msg":"max_file_size 不能大于 10737418240"}`、`{"code":400,"msg":"footer_beian 长度不能超过 128 字符"}`、`{"code":400,"msg":"notify_enabled 不能大于 1"}`。 +- `background_url` 协议白名单(需求 ①,防 `javascript:` 注入):仅 `http(s)://`、`data:image/*` 与站内相对路径(`/`开头);空串=清除背景。非法值 400: + `{"code":400,"msg":"background_url 仅支持 http(s) 地址、data:image 图片或站内相对路径"}` +- `admin_token`:明文密码自动哈希,**并轮换 `jwt_secret`(全部管理员令牌立即失效)**;空串忽略;已是哈希格式则原样保存。 +- `adminSessionExpire` 须为 1~365 的整天秒数(86400 的整数倍),否则 400。 +- `storageLimit` 不能小于 0。 +- 修改限流/策略配置**立即生效**(无需重启:限流规则运行时同步,策略由上传链路每次实时读取);引擎相关(`file_storage`/`s3_*`/`webdav_*`/`storage_path`/`local_storage_path`)需重启。 + +```bash +curl -s -X PATCH http://localhost:8466/admin/config/update \ + -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ + -d '{"site_name":"我的文件柜","uploadSize":52428800,"openUpload":1,"footer_beian":"京ICP备20240001号","max_file_size":10485760,"max_save_count":5}' +``` + +```json +{ "code": 200, "msg": "ok", "data": { "ok": true } } +``` + +```json +{ "code": 400, "msg": "adminSessionExpire 必须是 1 到 365 个整天" } +``` + +```json +{ "code": 400, "msg": "background_url 仅支持 http(s) 地址、data:image 图片或站内相对路径" } +``` + +## 修改管理员密码:PATCH /admin/settings/password + +`PATCH` 或 `POST`。新密码 ≥8 位;成功后哈希保存并**轮换 `jwt_secret`,所有旧令牌失效(401)**,需重新登录。 + +```bash +curl -s -X PATCH http://localhost:8466/admin/settings/password \ + -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ + -d '{"old_password":"admin12345","new_password":"new-pass-6789"}' +``` + +```json +{ "code": 200, "msg": "ok", "data": { "ok": true } } +``` + +```json +{ "code": 400, "msg": "新密码长度至少 8 位" } +``` + +```json +{ "code": 401, "msg": "旧密码错误" } +``` diff --git a/docs/api/08-audit.md b/docs/api/08-audit.md new file mode 100644 index 0000000..5f8a600 --- /dev/null +++ b/docs/api/08-audit.md @@ -0,0 +1,121 @@ +# 审计日志查询 + +所有上传/下载请求由审计中间件自动落库(需求 ③),管理端分页查询。 +认证:`Authorization: Bearer `。 + +## 审计记录内容 + +每条审计日志覆盖以下维度(需求 ③): + +| 维度 | 字段 | 说明 | +|---|---|---| +| 操作时间 | `created_at` | RFC 3339 | +| 客户端 | `ip` | 可信代理场景解析 XFF 后的真实 IP | +| 终端信息 | `user_agent` | 原始 UA | +| 设备解析 | `device_os` / `device_browser` / `device_type` | 由 UA 解析(如 Windows/Chrome/desktop) | +| 动作 | `action` | `upload`(上传类) / `download`(取件/下载类) / `admin`(管理端敏感操作,v2.5.6 新增) | +| 结果 | `result` | `success` / `denied`(拒绝:401/403/423/428)/ `failed`(失败:其余 4xx/5xx 或业务报错) | +| 字节数 | `size_bytes` | 文件总大小;`transferred_bytes` 实际传输(**Range 下载只计实际区间字节**;下载由中间件自动统计,上传由各 handler 填充) | +| 耗时 | `duration_ms` | 毫秒 | +| 角色 | `actor` | `admin`(有效管理员令牌)/ `guest` | +| 业务 | `file_code` / `file_name` | 取件码 / 文件名(分片上传时 `file_code` 为 `upload_id`) | +| 错误 | `error_msg` | 失败/拒绝原因 | + +**命中审计的端点**:上传类 `POST /share/text`、`POST /share/file`、`/chunk/upload*`、`/presign*`(POST/PUT); +下载类 `GET /share/download`、`GET /share/select`、`GET /share/metadata`; +管理类(`action=admin`)`POST /admin/login`、`POST /admin/logout`、`PATCH|POST /admin/config/update`、 +`PATCH|POST /admin/settings/password`、`POST /admin/storage/switch`、`PATCH|DELETE /admin/file/update|delete|batch-delete|batch-update|policy-action|batch-policy-action`。 +管理类动作未显式填结果时按 HTTP 状态兜底落库(401/403/423 → `denied`,5xx → `failed`,其余 → `success`)。 +失败与被拒绝的请求同样落库。 + +## 查询接口:GET /admin/audit/list + +**参数**: + +| 参数 | 默认 | 说明 | +|---|---|---| +| `page` | 1 | 页码(≥1) | +| `size` | 20 | 每页条数(1~200;兼容 `pageSize`) | +| `action` | - | `upload` / `download` / `admin` | +| `result` | - | `success` / `denied` / `failed` | +| `ip` | - | 按客户端 IP 过滤 | +| `start_time` / `end_time` | - | 时间范围,ISO 8601(如 `2025-06-01T00:00:00+08:00`;也接受 `2006-01-02 15:04:05` / 日期) | + +```bash +curl -s "http://localhost:8466/admin/audit/list?page=1&size=20&action=download&result=success&start_time=2025-06-01T00:00:00%2B08:00" \ + -H "Authorization: Bearer $TOKEN" +``` + +**成功响应**(200): + +```json +{ + "code": 200, "msg": "ok", + "data": { + "data": [ + { + "id": 318, + "action": "download", + "file_code": "K3P9W", + "file_name": "report.pdf", + "size_bytes": 1048576, + "transferred_bytes": 524288, + "ip": "203.0.113.7", + "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", + "device_os": "Windows", + "device_browser": "Chrome", + "device_type": "desktop", + "actor": "guest", + "result": "success", + "error_msg": "", + "duration_ms": 128, + "created_at": "2025-06-01T12:03:45+08:00", + "fileCode": "K3P9W", + "fileName": "report.pdf", + "sizeBytes": 1048576, + "transferredBytes": 524288, + "userAgent": "Mozilla/5.0 …", + "deviceOs": "Windows", + "deviceBrowser": "Chrome", + "deviceType": "desktop", + "errorMsg": "", + "durationMs": 128, + "createdAt": "2025-06-01T12:03:45+08:00" + } + ], + "total": 1180, + "page": 1, + "size": 20 + } +} +``` + +> 行字段以 snake_case 为准;camelCase 为兼容双份输出(文档不再重复列出)。 + +**错误响应**: + +```json +{ "code": 400, "msg": "start_time 时间格式错误" } +``` + +```json +{ "code": 401, "msg": "令牌无效或已过期" } +``` + +## 别名:GET /admin/audit/logs + +与 `/admin/audit/list` 完全相同(同一 handler 的兼容别名),参数与响应一致。 + +## 典型查询 + +```bash +# 最近的下载行为 +curl -s "http://localhost:8466/admin/audit/list?action=download&size=50" -H "Authorization: Bearer $TOKEN" + +# 某 IP 的全部被拒请求(限流/鉴权失败) +curl -s "http://localhost:8466/admin/audit/list?ip=203.0.113.7&result=denied" -H "Authorization: Bearer $TOKEN" + +# 今天 0 点以来的上传失败 +curl -s "http://localhost:8466/admin/audit/list?action=upload&result=failed&start_time=2025-06-01T00:00:00%2B08:00" \ + -H "Authorization: Bearer $TOKEN" +``` diff --git a/docs/api/09-storage.md b/docs/api/09-storage.md new file mode 100644 index 0000000..cd9c165 --- /dev/null +++ b/docs/api/09-storage.md @@ -0,0 +1,119 @@ +# 存储引擎配置 + +存储引擎只支持三种:**本地磁盘 / S3 / WebDAV**,由进程级环境变量 `FCB_STORAGE_ENGINE` 选择。 +引擎与引擎相关配置在启动时一次性读取(`storage.SetEngineOptions` → `NewEngine`), +**运行时修改引擎 KV 需重启服务**(管理端 `GET /admin/config/get` 的 `_engine_hint` 亦有提示)。 + +## 引擎选择 + +```bash +FCB_STORAGE_ENGINE=local # 启动默认(KV storage_engine 为空时生效) +FCB_STORAGE_ENGINE=s3 +FCB_STORAGE_ENGINE=webdav +``` + +非法值直接启动失败:`FCB_STORAGE_ENGINE 无效值 "xxx",仅支持 local|s3|webdav`。 + +**v3 运行时热切换**:管理端 `POST /admin/storage/switch`(或后台设置页「存储引擎」卡)可在不重启的情况下切换引擎—— +先构建新引擎并健康检查,通过才生效;失败 503 保持原引擎。当前引擎持久化在 settings KV `storage_engine`(空=回落启动值)。 +各引擎参数(存储目录/服务地址/存储桶/密钥)同样在后台设置页运行时可改;保存后对应引擎实例缓存失效,下次切换/构建生效。 + +## 文件归属引擎(v3) + +每条分享记录(`file_codes.engine`)与上传会话(`upload_chunks.engine` / `presign_upload_sessions.engine`) +在创建时戳记当时的引擎名。下载、分片合并、删除按**归属引擎**操作——切换引擎后,旧引擎里的文件仍可正常下载与删除 +(空戳为历史数据,回落当前引擎)。 + +## 按日期目录存储 + +文件落盘路径:`[storage_path/]share/data/YYYY/MM/DD//<文件名>`(如 `share/data/2026/09/04/…`)。 +按日嵌套目录自然排序、无日月歧义、避免单日海量文件挤在单目录;三种引擎一致适用;历史路径记录在 +`file_codes.file_path`,不受路径规则调整影响。 + +## 配置键与环境变量 + +引擎相关配置键(DB settings KV)可由环境变量种子注入(优先级:默认 < 环境变量 < DB KV): + +| KV 键 | 环境变量 | 引擎 | 说明 | +|---|---|---|---| +| `local_storage_path` | `FCB_LOCAL_STORAGE_PATH` | local | 本地存储根目录(容器内默认 `/app/data`) | +| `storage_path` | `FCB_STORAGE_PATH` | 全部 | 存储相对路径前缀(空 = `share/data/…`) | +| `s3_access_key_id` | `FCB_S3_ACCESS_KEY_ID` | s3 | 访问密钥 | +| `s3_secret_access_key` | `FCB_S3_SECRET_ACCESS_KEY` | s3 | 私有密钥 | +| `aws_session_token` | `FCB_AWS_SESSION_TOKEN` | s3 | 可选临时会话令牌 | +| `s3_bucket_name` | `FCB_S3_BUCKET_NAME` | s3 | 桶名 | +| `s3_endpoint_url` | `FCB_S3_ENDPOINT_URL` | s3 | S3 兼容端点(MinIO/R2 等;AWS 原生可空) | +| `s3_region_name` | `FCB_S3_REGION_NAME` | s3 | 区域(默认 `auto`) | +| `s3_addressing_style` | `FCB_S3_ADDRESSING_STYLE` | s3 | `auto`/`path`/`virtual` | +| `webdav_url` | `FCB_WEBDAV_URL` | webdav | WebDAV 服务地址(如 `http://webdav:5000`) | +| `webdav_username` | `FCB_WEBDAV_USERNAME` | webdav | 用户名 | +| `webdav_password` | `FCB_WEBDAV_PASSWORD` | webdav | 密码 | +| `webdav_root_path` | `FCB_WEBDAV_ROOT_PATH` | webdav | 根目录(默认 `filebox_storage`,不存在自动逐级创建) | + +`FCB_STORAGE_ENGINE` 本身不落库(`GET /admin/config/get` 中的 `file_storage` 为 KV 记忆键, +进程实际引擎以 `FCB_STORAGE_ENGINE` 与 `_engine_hint.storage_backend` 为准)。 + +## 各引擎要点 + +### 本地引擎(local) + +- 原子写:临时文件 + fsync + rename,避免半写文件。 +- 路径安全:`SanitizePath` + 符号链接逃逸双重防穿越。 +- Range 下载基于 `SectionReader`;分片按索引有序合并 + SHA256 校验,合并后清理分片目录。 + +### S3 引擎(s3) + +- 原生 multipart 流式合并(失败自动 Abort),分片落临时文件保证精确 Content-Length 与可重放。 +- 预签名 GET/PUT 直链(预签名直传唯一 `direct` 模式引擎)。 +- SDK 内置 5xx 指数退避重试;`when_required` 校验模式兼容 MinIO/R2 与纯流式转发。 + +### WebDAV 引擎(webdav,重点优化) + +- **连接复用**:池化 Transport,连接复用(实测 25 次请求仅 1 条 TCP 连接)。 +- **认证**:Basic + Digest(RFC 2617 `qop=auth`,MD5/SHA-256)自动协商,401 挑战驱动。 +- **Range**:`Range` 头透传 + `206` 解析,支持分块/断点下载。 +- **重试**:5xx/429/408 指数退避(封顶 2s ± 20% 抖动,尊重 `Retry-After`)。 +- **流式**:下载经 `io.Pipe` 流式转发不落盘;上下文取消挂到响应体读完之后,防止提前断连。 +- **目录**:按需逐级 `MKCOL` + 目录缓存,避免重复建目录。 +- **超时**:可配置(`webdav_url` 同级暂无独立超时键,引擎默认值内置)。 + +## 健康检查 + +三引擎均实现 `HealthCheck`:local 写探针、s3 `ListObjectsV2`、webdav `PROPFIND`(根目录不存在时自建)。 +服务启动时预检失败仅告警不阻断;运行状态可经 `GET /api/v1/health` 的 `data.storage` 查看当前引擎名。 + +## 预签名直传支持矩阵 + +| 引擎 | `PresignPutURL` / `PresignGetURL` | init 返回 mode | +|---|---|---| +| s3 | ✅ | `direct` | +| local / webdav | ❌(`ErrNotSupported`) | `proxy`(走服务端代理上传) | + +引擎不支持的操作经统一映射返回 501: + +```json +{ "code": 501, "msg": "当前存储引擎不支持该操作" } +``` + +## Docker Compose 冒烟编排 + +`deploy/docker-compose.yml` 提供可选 profile(详见 deploy/README.md): + +```bash +docker compose --profile minio up -d --build # MinIO(含 mc 自动建桶)+ FCB_STORAGE_ENGINE=s3 +docker compose --profile webdav up -d --build # dufs WebDAV 冒烟(admin/admin123)+ FCB_STORAGE_ENGINE=webdav +docker compose --profile redis up -d --build # Redis 缓存增强(非引擎) +``` + +## 存储哨兵错误 → HTTP 状态 + +| 哨兵错误 | HTTP | 文案 | +|---|---|---| +| `ErrNotFound` | 404 | 文件不存在 | +| `ErrInvalidPath` | 400 | 非法文件路径 | +| `ErrUnavailable` | 503 | 存储服务不可用,请稍后再试 | +| `ErrNotSupported` | 501 | 当前存储引擎不支持该操作 | +| `ErrRangeNotSatisfiable` | 416 | 请求范围超出文件大小 | +| `ErrHashMismatch` | 400 | 分片哈希校验失败,请重新上传 | + +容量超限(`storageLimit`,经容量预留判定)返回 507:`存储空间已达到管理员设置的容量上限`。 diff --git a/docs/api/10-config.md b/docs/api/10-config.md new file mode 100644 index 0000000..4806f9b --- /dev/null +++ b/docs/api/10-config.md @@ -0,0 +1,190 @@ +# 环境变量与配置项 + +配置分三层:**默认值 → `FCB_*` 环境变量 → 数据库 settings KV(管理端运行时修改)**。 +v2 起进程必需的环境变量为空集:数据库默认 **SQLite**(modernc.org/sqlite 纯 Go 驱动,零外部依赖, +DSN 缺省落 `./data/filecodebox.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DSN` 必需(需求 ⑧)。 + +## 环境变量(进程级) + +| 变量 | 必需 | 默认 | 说明 | +|---|---|---|---| +| `FCB_DB_DRIVER` | ❌ | `sqlite` | 数据库驱动:`sqlite` / `postgres`(需求 ⑧) | +| `FCB_DB_DSN` | 视驱动 | `./data/filecodebox.db` | postgres:连接串(**必需**,如 `postgres://user:pass@host:5432/filecodebox?sslmode=disable`);sqlite:数据库文件路径(可空,父目录自动创建) | +| `FCB_REDIS_ADDR` | ❌ | 空 | Redis 地址(如 `redis:6379`),也支持 `redis://[:password@]host:port[/db]` / `rediss://`(TLS)URL 形式;**空则缓存降级为进程内存实现**(缓存故障时自动降级为进程内限流计数) | +| `FCB_REDIS_DB` | ❌ | `0` | Redis 逻辑库号 0-15;URL 形式地址显式携带 `/N` 时以 URL 为准 | +| `FCB_ADMIN_PASSWORD` | ❌ | 空 | 设置后服务首次启动即自动初始化管理员(≥8 位,不足告警跳过),消除 `/setup` 被抢占窗口;初始化完成后建议移除 | +| `FCB_LISTEN` | ❌ | `:8466` | HTTP 监听地址 | +| `FCB_STORAGE_ENGINE` | ❌ | `local` | 存储引擎:`local` / `s3` / `webdav` | +| `FCB_TRUSTED_PROXIES` | ❌ | 空 | 可信代理 CIDR(逗号分隔),命中时从 `X-Forwarded-For` 解析真实客户端 IP | + +- SQLite 连接参数(驱动自动注入):`busy_timeout=10s` + `WAL` 日志模式 + `foreign_keys=1`;连接池 8/4。 +- Postgres 连接池沿用 v1 参数(32/8,1h 轮换);`FCB_DB_DRIVER=postgres` 且未设 `FCB_DB_DSN` 时**启动直接报错**。 + +引擎相关环境变量(种子注入 settings KV,见《存储引擎配置》):`FCB_LOCAL_STORAGE_PATH`、 +`FCB_STORAGE_PATH`、`FCB_S3_ACCESS_KEY_ID`、`FCB_S3_SECRET_ACCESS_KEY`、`FCB_AWS_SESSION_TOKEN`、 +`FCB_S3_BUCKET_NAME`、`FCB_S3_ENDPOINT_URL`、`FCB_S3_REGION_NAME`、`FCB_S3_ADDRESSING_STYLE`、 +`FCB_WEBDAV_URL`、`FCB_WEBDAV_USERNAME`、`FCB_WEBDAV_PASSWORD`、`FCB_WEBDAV_ROOT_PATH`。 + +部署用编排变量(`deploy/.env.example`):`WEB_PORT`(默认 8466)、 +`POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB`(默认 filecodebox,仅 `--profile postgres` 时使用)。 + +## 配置项(settings KV,默认值对齐参考实现) + +> 键名/类型/默认值/边界以 `server/internal/config/schema.go` 的 `KVSchema()` 为单一事实来源 +> (schema 同步测试保证与 defaults() 逐键一致);v2 新增键统一 snake_case。 + +### 站点信息与展示(需求 ①②③) + +| 键 | 类型/边界 | 默认 | 说明 | +|---|---|---|---| +| `site_name` / `name` | string | 文件快传 | 站点名称(`site_name` 优先) | +| `site_domain` | string,≤256 | 空 | **v3.1**:站点对外域名(`http(s)://host[:port]`,不带路径;裸主机自动补 `http://`)。配置后分享链接(结果卡/管理端复制)用该域名生成——内网部署也能把公网链接发出去;留空=用当前访问地址 | +| `description` | string | 开箱即用的文件快传系统 | 站点描述 | +| `page_explain` | string | (合规声明) | 页面说明文案 | +| `keywords` | string | 文件快传, 文件分享… | SEO 关键词 | +| `logo_url` | string | 空(前端回落本地打包 `/assets/logo-*.svg`,需求 ⑤) | 页面导航 Logo,管理端可设任意 URL | +| `favicon_url` | string | 空(前端回落本地打包 `/assets/favicon-*.png`,需求 ⑤) | favicon / 备用 Logo | +| `opacity` | float | 0.9 | 界面不透明度 | +| `background` | string | 空 | 背景图 URL(参考实现既有键,v1 兼容保留) | +| `background_url` | string,≤2048 字符 | 空 | **v2 需求 ①**:背景图 URL 或上传后地址(空=主题默认;取值时 legacy `background` 键兜底)。管理端保存时校验协议白名单:仅 `http(s)`、`data:image/*` 与站内相对路径(防 `javascript:` 注入,非法 400) | +| `footer_text` | string,≤2000 字符 | 空 | **v2 需求 ②**:页脚自定义内容(纯文本或受控 HTML 片段) | +| `footer_beian` | string,≤128 字符 | 空 | **v2 需求 ②**:备案号(如 `京ICP备2024xxxxxx号-1`),展示于页脚 | +| `notify_enabled` | int(0/1) | 1 | **v2 需求 ③**:通知开关(1=前台右上角悬浮窗展示 / 0=关闭) | +| `notify_title` | string,≤128 字符 | 系统通知 | 通知标题 | +| `notify_content` | string,≤2000 字符 | 欢迎使用… | 通知正文(**服务端白名单净化**:仅保留纯文本与 `` 为 http(s)/站内相对/`#` 锚点的链接,其余标签与事件属性剥离,保存与读取双侧生效) | +| `showAdminAddr` | int(0/1) | 0 | 是否展示后台入口 | +| `robotsText` | string | `User-agent: *\nDisallow: /` | robots.txt 内容(由公开端点 `GET /robots.txt` 输出) | + +### 保存策略(需求 ④,上传页动态读取并在范围内选择) + +| 键 | 类型/边界 | 默认 | 说明 | +|---|---|---|---| +| `max_save_seconds` | int64,0~31536000 | 0 | 最长保存秒数上限(0=仅默认 7 天兜底;>0 时按时间过期超限 403「限制最长时间为 X,可换用其他方式」)。**v3**:管理界面以「小时/天」下拉单位编辑(≥1 天自动显示天),提交时前端换算为秒——canonical 单位保持秒,接口语义不变 | +| `max_save_count` | int,0~100000 | 0 | **v2 新增**:单次分享最大可取(保存)次数上限(0=不限制;`expire_style=count` 且 `expire_value` 超上限时 403「限制次数最多为 N 次」) | +| `expireStyle` | []string | `["day","hour","minute","forever","count"]` | 允许的过期方式白名单(上传时不在白名单 400「过期时间类型错误」) | + +### 存储策略(需求 ④⑩) + +| 键 | 类型/边界 | 默认 | 说明 | +|---|---|---|---| +| `uploadSize` | int64,1024~10GiB | 10485760(10MB) | 单文件大小上限(字节),参考实现语义;`max_file_size=0` 时作为生效上限 | +| `max_file_size` | int64,0~10GiB | 0 | **v2 新增**:存储策略-单文件上限(字节),0=回落 `uploadSize`;超出 403(文案 humanSize 自适应 B/KB/MB/GB)。**v3**:管理界面以「MB/GB」下拉单位编辑(≥1 GiB 自动显示 GB),提交时前端换算为字节 | +| `allowed_file_types` | []string | `["*"]` | 允许类型白名单(扩展名/MIME 通配,`*` 不限制;非白名单 403「不允许上传该类型文件」) | +| `storageLimit` | int64,≥0 | 0 | 站点总容量(字节),0=不限制(超限 507) | +| `openUpload` | int(0/1) | 1 | 游客上传开关(0 时上传接口要求管理员令牌 403) | +| `enableChunk` | int(0/1) | 0 | 启用分片上传 | + +### 上传频率限制(需求 ④,既有键对齐参考 ip_limit["upload"]) + +| 键 | 类型/边界 | 默认 | 说明 | +|---|---|---|---| +| `uploadCount` / `uploadMinute` | int(1~10000 / 1~1440) | 10 / 1 | 窗口内允许上传次数 / 窗口分钟(上传成功才计数,超限 423;管理端修改后运行时同步限流规则,立即生效) | +| `errorCount` / `errorMinute` | int | 10 / 1 | 取件错误(失败计数)+ metadata 每次计数 | +| `loginCount` / `loginMinute` | int | 5 / 15 | 登录失败计数 | + +### 安全与会话 + +| 键 | 默认 | 说明 | +|---|---|---| +| `admin_token` | 空(未初始化) | 管理员密码哈希(`sha256$salt$hash`);GET 配置时屏蔽为空串 | +| `jwt_secret` | 空 | JWT 签名密钥(初始化/改密时自动生成轮换;不下发;`settings.SensitiveKeys` 双模式下一致屏蔽) | +| `adminSessionExpire` | 604800(7 天) | 管理员会话秒数(须 1~365 整天) | + +### 存储引擎(v3 运行时可配 + 热切换) + +**`storage_engine`**(v3 新增键):string,`local|s3|webdav`,默认空=回落启动值 `FCB_STORAGE_ENGINE`。 +运行时切换走 **`POST /admin/storage/switch`**(JWT 保护):构建新引擎 → 健康检查通过才生效; +失败返回 503「存储引擎切换失败,已保持原引擎: …」且不改 KV。成功后持久化 `storage_engine`,重启沿用。 +`GET /api/v1/config` 公开下发 `storage_engine` 当前名(仅名称,任何引擎参数/凭据不下发)。 + +引擎参数键(管理端可改;保存后对应引擎实例缓存失效,下次切换/构建生效): + +| 键 | 默认 | +|---|---| +| `file_storage` | `local` | +| `storage_path` | 空 | +| `local_storage_path` | 空(容器内由 `FCB_LOCAL_STORAGE_PATH=/app/data` 注入) | +| `s3_access_key_id` / `s3_secret_access_key` / `aws_session_token` | 空 | +| `s3_bucket_name` / `s3_endpoint_url` / `s3_hostname` | 空 | +| `s3_region_name` | `auto` | +| `s3_signature_version` | `s3v4` | +| `s3_addressing_style` | `auto` | +| `s3_proxy` | 0 | +| `webdav_url` / `webdav_username` / `webdav_password` | 空 | +| `webdav_root_path` | `filebox_storage` | +| `webdav_proxy` | 0 | + +> 敏感键 `webdav_password` / `s3_secret_access_key` / `aws_session_token`(v3 加入 `settings.SensitiveKeys`): +> 管理端 GET 返回掩码 `******`;PATCH 时空串或 `******` 表示不修改。直接写库 settings KV 后重启同样生效。 + +## 策略动态生效机制(v2 需求 ④⑩) + +上传页通过 `GET /api/v1/config` 的 `config` 字段读取**当前策略快照**并在范围内渲染选项; +上传链路(`/share/file`、`/chunk/*`、`/presign/*`)**每次请求实时读取** settings KV 同一组值校验: + +- 管理端改策略(`PATCH /admin/config/update`)→ 公开 config 即时反映 → 后续上传立即按新策略执行(含 403/400 拒绝与恢复放行)。 +- 生效上限:`max_file_size > 0` 时为 `max_file_size`,否则回落 `uploadSize`。 +- 校验点覆盖:单文件(`/share/file`)、分片 init 按分片数上限、分片上传累计、分片 complete 累计、预签名 init 声明大小,五处口径一致(`api.UploadPolicy.CheckSize`)。 + +## 公共配置接口 + +前端启动时经 `GET /api/v1/config` 获取站点公开配置(无需认证;v2 扩展需求 ①②③④⑩): + +```json +{ + "code": 200, "msg": "ok", + "data": { + "config": { + "name": "文件快传", + "description": "开箱即用的文件快传系统", + "explain": "请勿上传或分享违法内容…", + "logo_url": "", + "favicon_url": "", + "background_url": "", + "footer_text": "自定义页脚内容", + "footer_beian": "京ICP备2024xxxxxx号-1", + "notify_enabled": 1, + "notify_title": "系统通知", + "notify_content": "欢迎使用文件快传…", + "uploadSize": 10485760, + "max_file_size": 10485760, + "maxFileSize": 10485760, + "allowedFileTypes": ["*"], + "expireStyle": ["day", "hour", "minute", "forever", "count"], + "max_save_seconds": 0, + "maxSaveSeconds": 0, + "max_save_count": 0, + "maxSaveCount": 0, + "uploadCount": 10, + "uploadMinute": 1, + "enableChunk": false, + "openUpload": true + }, + "meta": { + "version": "2.5.6", + "features": { "chunkUpload": false, "guestUpload": true } + } + } +} +``` + +> 策略字段 snake_case 与 camelCase 双份下发(前端宽松解析);响应为白名单显式构造, +> 任何敏感键(`admin_token`/`jwt_secret`)均不会出现。 + +## 健康检查 + +```bash +curl -s http://localhost:8466/api/v1/health +``` + +```json +{ + "code": 200, "msg": "ok", + "data": { + "status": "ok", + "version": "2.5.6", + "storage": "local", + "time": "2025-06-01T12:00:00+08:00" + } +} +``` diff --git a/docs/api/11-errors.md b/docs/api/11-errors.md new file mode 100644 index 0000000..9281dc1 --- /dev/null +++ b/docs/api/11-errors.md @@ -0,0 +1,62 @@ +# 错误码 + +## 响应结构 + +```json +{ "code": 404, "msg": "文件不存在" } +``` + +- `code` 与 HTTP 状态码一致;失败时无 `data` 字段。 +- `msg` 为中文可读信息,可直接展示给用户。 + +## 业务状态码 + +| 状态码 | 语义 | 典型场景 | +|---|---|---| +| 200 | 成功 | 全部正常响应 | +| 400 | 参数/格式错误 | 缺字段、过期策略非法、时间格式错误、分片哈希不匹配、code 冲突、`chunk_size` 超 32MiB 上限、presign 实际大小与声明不符、请求体超过大小上限 | +| 401 | 未认证 | 管理端令牌缺失/无效;登录密码错误 | +| 403 | 拒绝 | 类型白名单拒绝、magic bytes 防伪、游客上传未开启、**分片上传未启用**(enableChunk=0)、presign 直传对象超限(服务端删除对象并释放预留)、下载 `key` 鉴权失败、超过大小/时长限制 | +| 404 | 不存在/已过期 | 取件码不存在、文件已过期、上传会话不存在、`/api/*` 未命中路由 | +| 409 | 冲突 | 上传容量预留信息不一致 | +| 416 | Range 越界 | `Range: bytes=…` 超出文件大小 | +| 423 | 限流 | upload/error/login/metadata 任一规则超限 | +| 428 | 未初始化 | 系统未初始化时访问除 `/setup`、`/api/v1/health` 外的接口 | +| 500 | 服务器错误 | 数据库/内部异常 | +| 501 | 引擎不支持 | 引擎不支持预签名等操作(local/webdav 的 `PresignGetURL/PutURL`) | +| 503 | 存储不可用 | 存储引擎连接失败/健康检查不通过时的操作 | +| 507 | 容量超限 | 达到 `storageLimit` 上限(含上传预留判定) | + +## 存储哨兵错误映射 + +存储层哨兵错误统一映射(支持错误包装链判定): + +| 哨兵错误 | HTTP | 响应 msg | +|---|---|---| +| `ErrNotFound` | 404 | 文件不存在 | +| `ErrInvalidPath` | 400 | 非法文件路径 | +| `ErrUnavailable` | 503 | 存储服务不可用,请稍后再试 | +| `ErrNotSupported` | 501 | 当前存储引擎不支持该操作 | +| `ErrRangeNotSatisfiable` | 416 | 请求范围超出文件大小 | +| `ErrHashMismatch` | 400 | 分片哈希校验失败,请重新上传 | + +未识别的存储错误归入 500(`存储操作失败: …`)。 + +## 错误结果的审计归类 + +错误响应同时写入审计日志(需求 ③): + +- `denied`:401 / 403 / 423 / 429 / 428(拒绝类)。 +- `failed`:其余 4xx / 5xx 及业务显式报错。 + +## 常见排障 + +| 现象 | 原因与处理 | +|---|---| +| 全部接口 428 | 未初始化:访问 `GET /setup` 或 `POST /setup` 完成向导 | +| 上传 403「本站未开启游客上传」 | `openUpload=0`,携带管理员 Bearer 令牌或后台开启 | +| 上传 423 | 触发 upload 限流,等待窗口或调大 `uploadCount/uploadMinute` | +| 取件 404「文件已过期」 | 分享过期/次数耗尽;管理员可 `PATCH /admin/file/update` 调整 | +| 下载 403「下载鉴权失败」 | `key` 窗口令牌过期/伪造:重新 `POST /share/select` 获取新地址 | +| 预签名 init 返回 proxy | local/webdav 引擎不支持直链,按 proxy 流程走服务端代理上传 | +| 503 存储服务不可用 | 检查引擎配置与远端服务(S3/WebDAV)连通性;`GET /api/v1/health` 的 `storage` 字段确认引擎 | diff --git a/docs/api/12-logo.md b/docs/api/12-logo.md new file mode 100644 index 0000000..fa7ff7f --- /dev/null +++ b/docs/api/12-logo.md @@ -0,0 +1,61 @@ +# Logo 自定义 + +## 默认 Logo(内置,v2 需求 ⑤) + +| 项 | 默认值 | 用途 | +|---|---|---| +| 页面导航 Logo | 前端打包本地资源 `/assets/logo-*.svg`(源:`web/src/assets/brand/logo.svg`) | 导航栏 ``;`config.logo_url` 为空时回落使用 | +| favicon / 备用 Logo | 前端打包本地资源 `/assets/favicon-*.png`(源:`web/src/assets/brand/favicon.png`) | `index.html` `` + 动态 favicon 回落 | + +v2 起默认不再引用远程 URL:`GET /api/v1/config` 中 `logo_url`/`favicon_url` 默认下发空串, +前端 `displayLogoUrl`/`displayFaviconUrl` 判空后回落到打包的本地资源。 +管理端仍可设置任意 URL 全站替换(三步如下)。 + +## 管理端自定义(三步) + +1. **登录后台**:`POST /admin/login` 获取 Bearer 令牌。 +2. **保存配置**:`PATCH /admin/config/update` 更新 `logo_url`(与可选 `favicon_url`),值为图片 URL 或经管理端上传后得到的地址。 +3. **全站生效**:保存即写入 settings KV 并热更新,前端读取公共配置立即换新 Logo,无需重启。 + +curl 示例: + +```bash +curl -s -X PATCH http://localhost:8466/admin/config/update \ + -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ + -d '{"logo_url":"https://cdn.example.com/logo.svg","favicon_url":"https://cdn.example.com/favicon.png"}' +``` + +```json +{ "code": 200, "msg": "ok", "data": { "ok": true } } +``` + +> 也可以在管理界面「系统设置」页操作(上传图片或填写 URL),效果相同。 + +## 校验生效 + +```bash +curl -s http://localhost:8466/api/v1/config +``` + +```json +{ + "code": 200, "msg": "ok", + "data": { "config": { "logo_url": "https://cdn.example.com/logo.svg", "favicon_url": "https://cdn.example.com/favicon.png" } } +} +``` + +## 恢复默认 + +把 `logo_url` / `favicon_url` 置回默认值(空串,前端回落本地打包资源)即可: + +```bash +curl -s -X PATCH http://localhost:8466/admin/config/update \ + -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ + -d '{"logo_url":"","favicon_url":""}' +``` + +## 相关行为 + +- 前端运行时优先读取配置值;空值回退前端打包的本地资源(`web/src/assets/brand/logo.svg` + `favicon.png`,经 `displayLogoUrl`/`displayFaviconUrl` 判空回落)。 +- `site_name` 同样支持运行时自定义(`PATCH /admin/config/update` 的 `site_name` 键)。 +- Logo/favicon 仅涉及展示层,修改不影响会话与令牌(不轮换 `jwt_secret`)。 diff --git a/docs/openapi.yaml b/docs/openapi.yaml new file mode 100644 index 0000000..26edc57 --- /dev/null +++ b/docs/openapi.yaml @@ -0,0 +1,1920 @@ +openapi: 3.0.3 +info: + title: 文件快传 Go 版 API + version: 2.5.6 + description: | + 文件快传 Go 重写版(Gin + GORM)完整 API 规范,与 `server/internal/api/` 实现逐一对齐。 + + 约定: + - 业务路由挂根路径(/share /chunk /presign /admin),公共接口保留 /api/v1 前缀。 + - 统一响应 `{"code":200,"msg":"ok","data":...}`,HTTP 状态码与 code 一致,失败时 data 缺省。 + - 管理接口(/admin/login 除外)需 `Authorization: Bearer `。 + - 未初始化时除 /setup 与 /api/v1/health 外一律 428;限流超限 423。 + contact: + name: 文件快传 Rewrite Team +servers: + - url: / + description: 同源部署(默认 :8466) +tags: + - name: 公共 + - name: 初始化 + - name: 分享 + - name: 分片上传 + - name: 预签名直传 + - name: 管理后台 + - name: 审计日志 + +paths: + /api/v1/health: + get: + tags: [公共] + summary: 健康检查 + operationId: health + responses: + '200': + description: 服务状态 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + status: { type: string, example: ok } + version: { type: string, example: 2.5.6 } + storage: { type: string, example: local } + time: { type: string, example: '2025-06-01T12:00:00+08:00' } + /robots.txt: + get: + tags: [公共] + summary: robots.txt + operationId: robotsText + description: | + 输出管理端可配置的 `robotsText` 内容(text/plain)。 + 内容可在管理后台「站点配置」中自定义,默认 `User-agent: *\nDisallow: /`。 + responses: + '200': + description: robots.txt 内容 + content: + text/plain: + schema: { type: string, example: 'User-agent: *\nDisallow: /' } + + /api/v1/config: + get: + tags: [公共] + summary: 公共站点配置 + operationId: publicConfig + responses: + '200': + description: 站点公开配置与功能开关 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + config: + type: object + description: 策略字段为上传页动态读取的当前快照(管理端改后即时反映);snake_case 与 camelCase 双份 + properties: + name: { type: string } + description: { type: string } + explain: { type: string } + logo_url: { type: string } + favicon_url: { type: string } + background_url: { type: string, description: '需求 ① 背景图 URL/上传地址,空=主题默认' } + footer_text: { type: string, description: '需求 ② 页脚自定义内容' } + footer_beian: { type: string, description: '需求 ② 备案号' } + storage_engine: { type: string, description: 当前存储引擎名(仅名称,凭据不下发) } + site_domain: { type: string, description: 'v3.1 站点对外域名(空=用当前访问地址)' } + notify_enabled: { type: integer, enum: [0, 1], description: '需求 ③ 通知开关(1=右上角悬浮窗)' } + notify_title: { type: string } + notify_content: { type: string } + uploadSize: { type: integer, format: int64, description: '回落上限(max_file_size=0 时生效)' } + max_file_size: { type: integer, format: int64, description: '需求 ⑩ 存储策略-单文件上限字节(0=回落 uploadSize)' } + maxFileSize: { type: integer, format: int64, description: 同 max_file_size } + allowedFileTypes: { type: array, items: { type: string } } + expireStyle: { type: array, items: { type: string } } + max_save_seconds: { type: integer, format: int64, description: '需求 ④ 最长保存秒数上限(0=默认 7 天兜底)' } + maxSaveSeconds: { type: integer, format: int64, description: 同 max_save_seconds } + max_save_count: { type: integer, description: '需求 ④ 单次分享最大可取次数上限(0=不限制)' } + maxSaveCount: { type: integer, description: 同 max_save_count } + uploadCount: { type: integer, description: 上传频率限制-窗口内次数 } + uploadMinute: { type: integer, description: 上传频率限制-窗口分钟 } + enableChunk: { type: boolean } + openUpload: { type: boolean } + meta: + type: object + properties: + version: { type: string } + features: + type: object + properties: + chunkUpload: { type: boolean } + guestUpload: { type: boolean } + + /setup: + get: + tags: [初始化] + summary: 初始化向导页面 + operationId: setupPage + description: 未初始化返回 HTML 向导;已初始化 303 重定向到 /。 + responses: + '200': + description: HTML 向导页 + content: + text/html: + schema: { type: string } + '303': + description: 已初始化,重定向到 / + post: + tags: [初始化] + summary: 提交初始化 + operationId: setupSubmit + description: | + 表单或 JSON。管理员密码 ≥8 位;`expireStyle` 支持多值(复选框或逗号分隔)。 + 成功写入站点配置、密码哈希并生成 jwt_secret。表单提交返回成功 HTML,JSON 提交返回 JSON。 + requestBody: + content: + application/json: + schema: + type: object + properties: + admin_password: { type: string, minLength: 8 } + confirm_password: { type: string } + site_name: { type: string } + upload_size_value: { type: integer, default: 10 } + upload_size_unit: { type: string, enum: [KB, MB, GB], default: MB } + save_time_value: { type: integer, default: 0 } + save_time_unit: { type: string, enum: [second, minute, hour, day], default: day } + expireStyle: + oneOf: + - type: string + - type: array + items: { type: string } + code_generate_type: { type: string, enum: [number, secret], default: secret } + errorCount: { type: integer, default: 10 } + errorMinute: { type: integer, default: 1 } + loginCount: { type: integer, default: 5 } + loginMinute: { type: integer, default: 15 } + uploadCount: { type: integer, default: 10 } + uploadMinute: { type: integer, default: 1 } + allowed_file_types: { type: string, default: '*' } + openUpload: { type: boolean, default: true } + enableChunk: { type: boolean, default: false } + application/x-www-form-urlencoded: + schema: + type: object + properties: + admin_password: { type: string } + confirm_password: { type: string } + site_name: { type: string } + responses: + '200': + description: 初始化成功 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + ok: { type: boolean, example: true } + admin: { type: string, example: '/#/admin' } + text/html: + schema: { type: string } + '400': + $ref: '#/components/responses/BadRequest' + + /share/text: + post: + tags: [分享] + summary: 创建文本分享 + operationId: shareText + description: | + 文本 ≤222KB(请求体全局上限 1MiB,Content-Length>441KB 直接 403);上传类接口(成功计数 upload 限流,423 超限),经审计中间件落库。 + 保存策略(需求 ④):expire_style 须在 expireStyle 白名单(400);count 型受 + max_save_count 约束(403「限制次数最多为 N 次」);时间型受 max_save_seconds 约束(403)。 + v3.1:支持 JSON 提交(字段同名);空文本 400「分享内容不能为空」; + 可选 code 自定义提取码(5-8 位字母或数字,占用 400「该提取码已被占用」)。 + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + required: [text] + properties: + text: { type: string, maxLength: 227328 } + expire_value: { type: integer, default: 1 } + expire_style: { type: string, enum: [day, hour, minute, count, forever], default: day } + code: { type: string, minLength: 5, maxLength: 8, pattern: '^[A-Za-z0-9]+$', description: 自定义提取码(5-8 位字母数字,可省略=随机) } + responses: + '200': + description: 取件码 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + code: { type: string, example: 8XQ2M } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /share/file: + post: + tags: [分享] + summary: 上传文件并创建分享 + operationId: shareFile + description: | + multipart 上传;扩展名/MIME 白名单 + magic bytes 防伪;大小受动态策略上限限制 + (max_file_size>0 时为其,否则回落 uploadSize;403「大小超过限制,最大为10.00 MB」, + 管理端改后立即生效);保存策略同 /share/text(403/400);容量超限 507。 + requestBody: + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + code: { type: string, minLength: 5, maxLength: 8, pattern: '^[A-Za-z0-9]+$', description: 自定义提取码(5-8 位字母数字,可省略=随机) } + file: + type: string + format: binary + expire_value: { type: integer, default: 1 } + expire_style: { type: string, enum: [day, hour, minute, count, forever], default: day } + responses: + '200': + description: 取件码与文件名 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + code: { type: string, example: K3P9W } + name: { type: string, example: report.pdf } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '423': { $ref: '#/components/responses/RateLimited' } + '503': { $ref: '#/components/responses/ServiceUnavailable' } + '507': { $ref: '#/components/responses/InsufficientStorage' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /share/metadata: + get: + tags: [分享] + summary: 查询分享元信息(GET) + operationId: shareMetadataGet + description: 不消耗次数;每次访问计入 metadata 限流。 + parameters: + - $ref: '#/components/parameters/CodeQuery' + responses: + '200': + description: 元信息 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ShareMetadata' } + '404': { $ref: '#/components/responses/NotFound' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [分享] + summary: 查询分享元信息(POST) + operationId: shareMetadataPost + requestBody: + content: + application/json: + schema: + type: object + required: [code] + properties: + code: { type: string } + responses: + '200': + description: 元信息(同 GET 版本) + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ShareMetadata' } + '400': { $ref: '#/components/responses/BadRequest' } + '404': { $ref: '#/components/responses/NotFound' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /share/select: + get: + tags: [分享] + summary: 取件(消耗次数) + operationId: shareSelectGet + description: | + 每调用一次消耗 1 次取件。文本分享返回 text/plain 正文;文件分享返回文件流 + (200 全量 / 206 区间,支持 Range,Accept-Ranges: bytes)。失败计入 error 限流(423)。 + parameters: + - $ref: '#/components/parameters/CodeQuery' + - name: Range + in: header + required: false + schema: { type: string, example: 'bytes=0-1023' } + description: 文件分享的区间请求(单区间) + responses: + '200': + description: 文本正文或文件流 + content: + text/plain: + schema: { type: string } + application/octet-stream: + schema: { type: string, format: binary } + '206': + description: Range 区间文件流(Content-Range) + content: + application/octet-stream: + schema: { type: string, format: binary } + '404': { $ref: '#/components/responses/NotFound' } + '416': { $ref: '#/components/responses/RangeNotSatisfiable' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [分享] + summary: 取件详情(JSON) + operationId: shareSelectPost + description: | + 返回元信息 + 内容/下载地址。文本分享含 text/content;文件分享含 download_url + (代理地址或 S3 预签名直链)。次数型分享返回代理地址且本次不消耗(消耗在代理下载时)。 + requestBody: + content: + application/json: + schema: + type: object + required: [code] + properties: + code: { type: string } + responses: + '200': + description: 分享详情 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ShareDetail' } + '404': { $ref: '#/components/responses/NotFound' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /share/download: + get: + tags: [分享] + summary: 代理下载(消耗次数) + operationId: shareDownload + description: key 为窗口令牌(双窗口校验,鉴权失败 403 且计入 error 限流);文件流 200/206 支持 Range。 + parameters: + - name: key + in: query + required: true + schema: { type: string } + - $ref: '#/components/parameters/CodeQuery' + responses: + '200': + description: 文本 JSON 或文件流 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + application/octet-stream: + schema: { type: string, format: binary } + '206': + description: Range 区间文件流 + content: + application/octet-stream: + schema: { type: string, format: binary } + '403': { $ref: '#/components/responses/Forbidden' } + '404': { $ref: '#/components/responses/NotFound' } + '416': { $ref: '#/components/responses/RangeNotSatisfiable' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /chunk/upload/init: + post: + tags: [分片上传] + summary: 初始化分片上传会话 + operationId: chunkInit + description: | + 相同 file_hash+file_size+file_name 的未完成会话自动续传(uploaded_chunks 非空即续传)。 + chunk_size 缺省 5MB、硬上限 32MiB(超出 400「chunk_size 过大」);需 enableChunk 开启(关闭时全部 /chunk 端点 403)。总大小按分片数上限受动态策略约束 + (max_file_size>0 时为其,否则 uploadSize;403);类型白名单 403。 + requestBody: + content: + application/json: + schema: + type: object + required: [file_name, file_size] + properties: + file_name: { type: string } + file_size: { type: integer, format: int64, minimum: 1 } + chunk_size: { type: integer, format: int64, default: 5242880, maximum: 33554432, description: 每片大小(字节),≤32MiB } + file_hash: { type: string, description: 整文件 SHA256,断点续传匹配键 } + responses: + '200': + description: 会话信息 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ChunkInitData' } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '423': { $ref: '#/components/responses/RateLimited' } + '507': { $ref: '#/components/responses/InsufficientStorage' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /chunk/upload/{uploadID}/{chunkIndex}: + post: + tags: [分片上传] + summary: 上传单个分片 + operationId: chunkUpload + description: multipart 字段 chunk;单分片 ≤ min(chunk_size, 32MiB);重复上传幂等(skipped:true);首分片做 magic bytes 防伪。 + parameters: + - $ref: '#/components/parameters/UploadID' + - name: chunkIndex + in: path + required: true + schema: { type: integer, minimum: 0 } + requestBody: + content: + multipart/form-data: + schema: + type: object + required: [chunk] + properties: + chunk: { type: string, format: binary } + responses: + '200': + description: 分片结果 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ChunkUploadData' } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /chunk/upload/status/{uploadID}: + get: + tags: [分片上传] + summary: 查询上传进度 + operationId: chunkStatus + parameters: + - $ref: '#/components/parameters/UploadID' + responses: + '200': + description: 进度 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ChunkStatusData' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /chunk/upload/complete/{uploadID}: + post: + tags: [分片上传] + summary: 合并分片并创建分享 + operationId: chunkComplete + description: 分片到齐后调用;按索引有序合并 + SHA256 校验(不完整 400 / 哈希不符 400);累计大小受动态策略上限(403,超限清理会话);保存策略同 /share/text(403/400)。 + parameters: + - $ref: '#/components/parameters/UploadID' + requestBody: + content: + application/json: + schema: + type: object + properties: + expire_value: { type: integer, default: 1 } + expire_style: { type: string, enum: [day, hour, minute, count, forever], default: day } + responses: + '200': + description: 取件码与文件名 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ShareFileData' } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '404': { $ref: '#/components/responses/NotFound' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /chunk/upload/{uploadID}: + delete: + tags: [分片上传] + summary: 取消上传 + operationId: chunkCancel + parameters: + - $ref: '#/components/parameters/UploadID' + responses: + '200': + description: 已取消 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + message: { type: string, example: 上传已取消 } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /presign/upload/init: + post: + tags: [预签名直传] + summary: 初始化预签名上传 + operationId: presignInit + description: | + S3 引擎返回 mode=direct(upload_url 为预签名 PUT URL);local/webdav 返回 mode=proxy + (upload_url=/presign/upload/proxy/{uploadID})。会话 900 秒有效。 + file_size 受动态策略上限(max_file_size>0 时为其,否则 uploadSize;403);保存策略同 /share/text。 + requestBody: + content: + application/json: + schema: + type: object + required: [file_name, file_size] + properties: + file_name: { type: string } + file_size: { type: integer, format: int64, minimum: 1 } + expire_value: { type: integer, default: 1 } + expire_style: { type: string, enum: [day, hour, minute, count, forever], default: day } + responses: + '200': + description: 上传会话 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/PresignInitData' } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '423': { $ref: '#/components/responses/RateLimited' } + '507': { $ref: '#/components/responses/InsufficientStorage' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /presign/upload/proxy/{uploadID}: + put: + tags: [预签名直传] + summary: 代理上传(proxy 模式) + operationId: presignProxy + description: multipart 字段 file;实际大小须与声明一致(±1KB);成功即建分享并删除会话。 + parameters: + - $ref: '#/components/parameters/UploadID' + requestBody: + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: { type: string, format: binary } + responses: + '200': + description: 取件码与文件名 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ShareFileData' } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '404': { $ref: '#/components/responses/NotFound' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /presign/upload/confirm/{uploadID}: + post: + tags: [预签名直传] + summary: 直传确认(direct 模式) + operationId: presignConfirm + description: | + 客户端完成 S3 PUT 后调用。服务端 HeadObject 核对实际对象: + 超过策略上限 → 删除对象、释放容量预留并 403;实际大小与声明差 >1KB → 400; + 前 64 字节做 magic bytes 校验(失败删除对象)。全部通过后创建分享记录。 + parameters: + - $ref: '#/components/parameters/UploadID' + responses: + '200': + description: 取件码与文件名 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ShareFileData' } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '404': { $ref: '#/components/responses/NotFound' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /presign/upload/status/{uploadID}: + get: + tags: [预签名直传] + summary: 查询预签名会话 + operationId: presignStatus + parameters: + - $ref: '#/components/parameters/UploadID' + responses: + '200': + description: 会话状态 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + upload_id: { type: string } + file_name: { type: string } + file_size: { type: integer, format: int64 } + mode: { type: string, enum: [direct, proxy] } + created_at: { type: string } + expires_at: { type: string } + is_expired: { type: boolean } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /presign/upload/{uploadID}: + delete: + tags: [预签名直传] + summary: 取消预签名会话 + operationId: presignCancel + description: direct 模式会尽力清理已直传的对象。 + parameters: + - $ref: '#/components/parameters/UploadID' + responses: + '200': + description: 已取消 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + message: { type: string, example: 上传会话已取消 } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/login: + post: + tags: [管理后台] + summary: 管理员登录 + operationId: adminLogin + description: 失败 401 并计入 login 限流(423 超限)。 + requestBody: + content: + application/json: + schema: + type: object + required: [password] + properties: + password: { type: string } + responses: + '200': + description: JWT 会话 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/LoginData' } + '401': { $ref: '#/components/responses/Unauthorized' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/verify: + get: + tags: [管理后台] + summary: 校验管理员会话 + operationId: adminVerify + security: [{ bearerAuth: [] }] + responses: + '200': + description: 会话信息 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/LoginData' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/logout: + post: + tags: [管理后台] + summary: 管理员登出 + operationId: adminLogout + description: 无状态 JWT,客户端丢弃令牌即可。 + security: [{ bearerAuth: [] }] + responses: + '200': + description: 确认 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/OkData' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/dashboard: + get: + tags: [管理后台] + summary: 管理端仪表盘统计 + operationId: adminDashboard + security: [{ bearerAuth: [] }] + responses: + '200': + description: 计数/存储用量/最近文件等 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + totalFiles: { type: integer } + storageUsed: { type: string, example: '123456789' } + sysUptime: { type: integer, nullable: true, description: 启动时刻 Unix 毫秒 } + yesterdayCount: { type: integer } + yesterdaySize: { type: string } + todayCount: { type: integer } + todaySize: { type: string } + activeCount: { type: integer } + expiredCount: { type: integer } + textCount: { type: integer } + fileCount: { type: integer } + chunkedCount: { type: integer } + usedCount: { type: integer } + storageBackend: { type: string, enum: [local, s3, webdav] } + uploadSizeLimit: { type: integer, format: int64 } + openUpload: { type: integer } + enableChunk: { type: integer } + maxSaveSeconds: { type: integer, format: int64 } + topSuffixes: + type: array + items: + type: object + properties: + suffix: { type: string } + count: { type: integer } + recentFiles: + type: array + items: { $ref: '#/components/schemas/AdminFileItem' } + recentActivities: { type: array, items: { type: object } } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/list: + get: + tags: [管理后台] + summary: 分页查询分享列表 + operationId: adminFileList + security: [{ bearerAuth: [] }] + parameters: + - { name: page, in: query, schema: { type: integer, default: 1, minimum: 1 } } + - { name: size, in: query, schema: { type: integer, default: 10, minimum: 1, maximum: 100 } } + - { name: keyword, in: query, schema: { type: string } } + - { name: status, in: query, schema: { type: string, enum: [active, expired] } } + - { name: type, in: query, schema: { type: string, enum: [text, file, chunked] } } + - { name: sortBy, in: query, schema: { type: string, enum: [created_at, expired_at, name, size, used_count, code] } } + - { name: sortOrder, in: query, schema: { type: string, enum: [asc, desc], default: desc } } + responses: + '200': + description: 列表与统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + page: { type: integer } + size: { type: integer } + total: { type: integer } + summary: + type: object + properties: + totalFiles: { type: integer } + activeCount: { type: integer } + expiredCount: { type: integer } + textCount: { type: integer } + fileCount: { type: integer } + chunkedCount: { type: integer } + storageUsed: { type: integer, format: int64 } + usedCount: { type: integer, format: int64 } + data: + type: array + items: { $ref: '#/components/schemas/AdminFileItem' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/detail: + get: + tags: [管理后台] + summary: 文件详情(GET) + operationId: adminFileDetailGet + security: [{ bearerAuth: [] }] + parameters: + - $ref: '#/components/parameters/FileIDQuery' + responses: + '200': + description: 文件条目(文本分享额外含 content 全文) + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/AdminFileItem' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 文件详情(POST) + operationId: adminFileDetailPost + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [id] + properties: + id: { type: integer, format: int64 } + responses: + '200': + description: 同 GET 版本 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/AdminFileItem' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/update: + patch: + tags: [管理后台] + summary: 更新分享字段(主名) + operationId: adminFileUpdate + description: code 冲突 400;POST /admin/file/update 为兼容别名。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: { $ref: '#/components/schemas/FileUpdateInput' } + responses: + '200': + description: 更新成功(data 为字符串“更新成功”) + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 更新分享字段(POST 别名) + operationId: adminFileUpdateAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: { $ref: '#/components/schemas/FileUpdateInput' } + responses: + '200': + description: 更新成功 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/delete: + delete: + tags: [管理后台] + summary: 删除单个分享(主名) + operationId: adminFileDelete + description: 连带删除存储文件;POST /admin/file/delete 为兼容别名。 + security: [{ bearerAuth: [] }] + parameters: + - { name: id, in: query, schema: { type: integer, format: int64 }, description: 亦可放请求体 } + requestBody: + content: + application/json: + schema: + type: object + properties: + id: { type: integer, format: int64 } + responses: + '200': + description: 删除成功(data 为 null) + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 删除单个分享(POST 别名) + operationId: adminFileDeleteAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + properties: + id: { type: integer, format: int64 } + responses: + '200': + description: 删除成功 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/batch-delete: + post: + tags: [管理后台] + summary: 批量删除 + operationId: adminFileBatchDelete + description: DELETE 方法亦可。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [ids] + properties: + ids: { type: array, items: { type: integer, format: int64 } } + responses: + '200': + description: 批量统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/BatchStats' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + delete: + tags: [管理后台] + summary: 批量删除(DELETE 别名) + operationId: adminFileBatchDeleteAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [ids] + properties: + ids: { type: array, items: { type: integer, format: int64 } } + responses: + '200': + description: 批量统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/BatchStats' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/batch-update: + patch: + tags: [管理后台] + summary: 批量更新过期策略 + operationId: adminFileBatchUpdate + description: expired_at / expired_count / clearExpiredAt 三选一;POST 亦可。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [ids] + properties: + ids: { type: array, items: { type: integer, format: int64 } } + expired_at: { type: string, description: ISO 8601 } + expired_count: { type: integer } + clearExpiredAt: { type: boolean, description: 清空过期时间并置 expired_count=-1(永久) } + clear_expired_at: { type: boolean, description: 同 clearExpiredAt } + responses: + '200': + description: 批量统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/BatchStats' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 批量更新(POST 别名) + operationId: adminFileBatchUpdateAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [ids] + properties: + ids: { type: array, items: { type: integer, format: int64 } } + expired_at: { type: string } + expired_count: { type: integer } + clearExpiredAt: { type: boolean } + responses: + '200': + description: 批量统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/BatchStats' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/policy-action: + patch: + tags: [管理后台] + summary: 单文件过期策略动作 + operationId: adminFilePolicyAction + description: 动作:extend_24h / extend_7d / make_permanent / reset_download_limit;POST 亦可。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: { $ref: '#/components/schemas/PolicyInput' } + responses: + '200': + description: 执行结果 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/PolicyResult' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 单文件策略动作(POST 别名) + operationId: adminFilePolicyActionAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: { $ref: '#/components/schemas/PolicyInput' } + responses: + '200': + description: 执行结果 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/PolicyResult' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/batch-policy-action: + patch: + tags: [管理后台] + summary: 批量过期策略动作 + operationId: adminFileBatchPolicyAction + description: 响应结构同批量更新;POST 亦可。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + allOf: + - type: object + required: [ids] + properties: + ids: { type: array, items: { type: integer, format: int64 } } + - $ref: '#/components/schemas/PolicyInput' + responses: + '200': + description: 批量统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/BatchStats' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 批量策略动作(POST 别名) + operationId: adminFileBatchPolicyActionAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + allOf: + - type: object + required: [ids] + properties: + ids: { type: array, items: { type: integer, format: int64 } } + - $ref: '#/components/schemas/PolicyInput' + responses: + '200': + description: 批量统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/BatchStats' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/download: + get: + tags: [管理后台] + summary: 管理员下载原文件 + operationId: adminFileDownload + description: 不消耗取件次数;文件返回二进制流(支持 Range),文本分享返回 JSON。 + security: [{ bearerAuth: [] }] + parameters: + - $ref: '#/components/parameters/FileIDQuery' + responses: + '200': + description: 文件流或文本 JSON + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + application/octet-stream: + schema: { type: string, format: binary } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/preview: + get: + tags: [管理后台] + summary: 文本预览 + operationId: adminFilePreview + description: 仅文本分享可用(文件分享 400);maxChars 默认 4000(1~20000)。 + security: [{ bearerAuth: [] }] + parameters: + - $ref: '#/components/parameters/FileIDQuery' + - { name: maxChars, in: query, schema: { type: integer, default: 4000, minimum: 1, maximum: 20000 } } + responses: + '200': + description: 预览内容 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + id: { type: integer, format: int64 } + code: { type: string } + name: { type: string } + type: { type: string, example: text } + content: { type: string } + length: { type: integer } + previewLength: { type: integer } + truncated: { type: boolean } + maxChars: { type: integer } + createdAt: { type: string } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/config/get: + get: + tags: [管理后台] + summary: 读取运行时配置 + operationId: adminConfigGet + description: admin_token 屏蔽(恒空串)、jwt_secret 不下发;_engine_hint 提示引擎配置需重启。 + security: [{ bearerAuth: [] }] + responses: + '200': + description: 配置 KV + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + additionalProperties: true + properties: + site_name: { type: string } + logo_url: { type: string } + favicon_url: { type: string } + background_url: { type: string } + footer_text: { type: string } + footer_beian: { type: string } + notify_enabled: { type: integer, enum: [0, 1] } + openUpload: { type: integer } + uploadSize: { type: integer, format: int64 } + max_file_size: { type: integer, format: int64, description: '0=回落 uploadSize' } + max_save_count: { type: integer, description: '0=不限制' } + max_save_seconds: { type: integer, format: int64 } + allowed_file_types: { type: array, items: { type: string } } + expireStyle: { type: array, items: { type: string } } + storageLimit: { type: integer, format: int64 } + uploadCount: { type: integer } + uploadMinute: { type: integer } + admin_token: { type: string, example: '' } + _engine_hint: + type: object + properties: + storage_backend: { type: string } + note: { type: string } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/config/update: + patch: + tags: [管理后台] + summary: 部分更新配置(主名) + operationId: adminConfigUpdate + description: | + JSON 对象部分更新,未知键忽略;数值键自动转型;v2 起新键按 settings.KVSchema 校验 + (整型边界/字符串长度/列表非空,越界 400 中文错误);background_url 协议白名单 + (http(s)/data:image/站内相对路径);admin_token 明文自动哈希并轮换 jwt_secret + (全部令牌立即失效);adminSessionExpire 须 1~365 整天(默认 604800 即 7 天);notify_content 服务端白名单净化(仅保留文本与 );策略与限流修改立即生效,引擎配置需重启。 + POST /admin/config/update 为兼容别名。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + additionalProperties: true + properties: + site_name: { type: string } + logo_url: { type: string } + favicon_url: { type: string } + background_url: + type: string + maxLength: 2048 + description: '需求 ① 背景图;仅 http(s)、data:image/* 与站内相对路径(/开头),空串清除;非法协议 400' + footer_text: { type: string, maxLength: 2000, description: '需求 ② 页脚自定义内容' } + footer_beian: { type: string, maxLength: 128, description: '需求 ② 备案号' } + notify_enabled: { type: integer, enum: [0, 1], description: '需求 ③ 通知开关' } + notify_title: { type: string, maxLength: 128 } + notify_content: { type: string, maxLength: 2000 } + openUpload: { type: integer, enum: [0, 1] } + uploadSize: { type: integer, format: int64, minimum: 1024, maximum: 10737418240 } + max_file_size: { type: integer, format: int64, minimum: 0, maximum: 10737418240, description: '需求 ⑩ 存储策略-单文件上限字节;0=回落 uploadSize' } + max_save_count: { type: integer, minimum: 0, maximum: 100000, description: '需求 ④ 单次分享最大可取次数上限;0=不限制' } + max_save_seconds: { type: integer, format: int64, minimum: 0, maximum: 31536000, description: '需求 ④ 最长保存秒数上限;0=默认 7 天兜底' } + allowed_file_types: + oneOf: + - type: array + items: { type: string } + minItems: 1 + - type: string + description: 逗号分隔亦可;"*" 不限制 + expireStyle: + oneOf: + - type: array + items: { type: string } + minItems: 1 + - type: string + description: 允许的过期方式白名单(day/hour/minute/forever/count 子集) + storageLimit: { type: integer, format: int64, minimum: 0 } + uploadCount: { type: integer, minimum: 1, maximum: 10000 } + uploadMinute: { type: integer, minimum: 1, maximum: 1440 } + errorCount: { type: integer } + errorMinute: { type: integer } + loginCount: { type: integer } + loginMinute: { type: integer } + admin_token: { type: string, description: 明文密码自动哈希并轮换 jwt_secret;空串忽略 } + adminSessionExpire: { type: integer, default: 604800, description: 秒,须 86400 的 1~365 整数倍(默认 7 天) } + responses: + '200': + description: 保存成功 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/OkData' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 部分更新配置(POST 别名) + operationId: adminConfigUpdateAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + additionalProperties: true + responses: + '200': + description: 保存成功 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/OkData' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/storage/switch: + post: + tags: [admin] + summary: 存储引擎热切换(v3) + description: | + 运行时切换存储引擎(local|s3|webdav)。构建新引擎并做健康检查, + 通过才生效;失败返回 503 且保持原引擎。成功后持久化 storage_engine KV, + 重启后沿用。 + operationId: adminStorageSwitch + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [engine] + properties: + engine: + type: string + enum: [local, s3, webdav] + responses: + "200": + description: 切换成功 + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Envelope" + - type: object + properties: + data: + type: object + properties: + ok: { type: boolean } + engine: { type: string } + "400": { $ref: "#/components/responses/BadRequest" } + "401": { $ref: "#/components/responses/Unauthorized" } + "503": { $ref: "#/components/responses/ServiceUnavailable" } + + /admin/settings/password: + patch: + tags: [管理后台] + summary: 修改管理员密码(主名) + operationId: adminChangePassword + description: 新密码 ≥8 位;成功后轮换 jwt_secret,全部旧令牌失效(401)。POST 亦可。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [old_password, new_password] + properties: + old_password: { type: string } + new_password: { type: string, minLength: 8 } + responses: + '200': + description: 修改成功 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/OkData' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 修改管理员密码(POST 别名) + operationId: adminChangePasswordAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [old_password, new_password] + properties: + old_password: { type: string } + new_password: { type: string, minLength: 8 } + responses: + '200': + description: 修改成功 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/OkData' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/audit/list: + get: + tags: [审计日志] + summary: 分页查询审计日志(主名) + operationId: adminAuditList + description: | + 需求 ③ 审计查询。行字段 snake_case 为主(camelCase 兼容双份)。 + action: upload|download;result: success|denied|failed;时间参数 ISO 8601。 + security: [{ bearerAuth: [] }] + parameters: + - { name: page, in: query, schema: { type: integer, default: 1, minimum: 1 } } + - { name: size, in: query, schema: { type: integer, default: 20, minimum: 1, maximum: 200 }, description: 兼容 pageSize } + - { name: action, in: query, schema: { type: string, enum: [upload, download] } } + - { name: result, in: query, schema: { type: string, enum: [success, denied, failed] } } + - { name: ip, in: query, schema: { type: string } } + - { name: start_time, in: query, schema: { type: string, example: '2025-06-01T00:00:00+08:00' } } + - { name: end_time, in: query, schema: { type: string, example: '2025-06-02T00:00:00+08:00' } } + responses: + '200': + description: 审计日志分页 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + data: + type: array + items: { $ref: '#/components/schemas/AuditItem' } + total: { type: integer } + page: { type: integer } + size: { type: integer } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/audit/logs: + get: + tags: [审计日志] + summary: 分页查询审计日志(别名) + operationId: adminAuditLogs + deprecated: true + description: 与 /admin/audit/list 完全相同(同一 handler 的兼容别名)。 + security: [{ bearerAuth: [] }] + parameters: + - { name: page, in: query, schema: { type: integer, default: 1 } } + - { name: size, in: query, schema: { type: integer, default: 20 } } + - { name: action, in: query, schema: { type: string, enum: [upload, download] } } + - { name: result, in: query, schema: { type: string, enum: [success, denied, failed] } } + - { name: ip, in: query, schema: { type: string } } + - { name: start_time, in: query, schema: { type: string } } + - { name: end_time, in: query, schema: { type: string } } + responses: + '200': + description: 审计日志分页 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + data: + type: array + items: { $ref: '#/components/schemas/AuditItem' } + total: { type: integer } + page: { type: integer } + size: { type: integer } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: POST /admin/login 获取 + parameters: + CodeQuery: + name: code + in: query + required: true + schema: { type: string } + description: 取件码 + UploadID: + name: uploadID + in: path + required: true + schema: { type: string } + description: 上传会话 ID + FileIDQuery: + name: id + in: query + required: true + schema: { type: integer, format: int64 } + description: 文件记录 ID + responses: + BadRequest: + description: 参数/格式错误 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 400, msg: '过期时间类型错误' } + Unauthorized: + description: 未认证 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 401, msg: '密码错误' } + Forbidden: + description: 拒绝(类型/鉴权/上限) + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 403, msg: '不允许上传该类型文件' } + NotFound: + description: 不存在/已过期 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 404, msg: '文件不存在' } + RangeNotSatisfiable: + description: Range 越界 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 416, msg: '请求范围超出文件大小' } + RateLimited: + description: 限流超限 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 423, msg: '请求次数过多,请稍后再试' } + NotInitialized: + description: 系统未初始化(除 /setup 与 /api/v1/health 外) + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 428, msg: '系统未初始化,请先完成初始化' } + NotImplemented: + description: 引擎不支持 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 501, msg: '当前存储引擎不支持该操作' } + ServiceUnavailable: + description: 存储不可用 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 503, msg: '存储服务不可用,请稍后再试' } + InsufficientStorage: + description: 容量超限 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 507, msg: '存储空间已达到管理员设置的容量上限' } + schemas: + Envelope: + type: object + description: 统一响应封装(HTTP 状态码 = code;失败时 data 缺省) + required: [code, msg] + properties: + code: { type: integer, example: 200 } + msg: { type: string, example: ok } + data: { description: 业务数据,失败时缺省 } + OkData: + type: object + properties: + ok: { type: boolean, example: true } + ShareMetadata: + type: object + properties: + code: { type: string } + name: { type: string } + size: { type: integer, format: int64 } + type: { type: string, enum: [text, file] } + is_text: { type: boolean } + created_at: { type: string } + expired_at: { type: string, nullable: true } + expires_at: { type: string, nullable: true } + expired_count: { type: integer, description: '-1 按时间/永久;>0 次数型限额' } + used_count: { type: integer } + remaining_downloads: { type: integer, nullable: true } + ShareDetail: + allOf: + - $ref: '#/components/schemas/ShareMetadata' + - type: object + properties: + text: { type: string, description: 文本内容或下载地址 } + content: { type: string, description: 文本内容(仅文本分享) } + download_url: { type: string, nullable: true, description: 代理地址或 S3 预签名直链 } + ShareFileData: + type: object + properties: + code: { type: string } + name: { type: string } + ChunkInitData: + type: object + properties: + existed: { type: boolean, example: false } + upload_id: { type: string } + chunk_size: { type: integer, format: int64 } + total_chunks: { type: integer } + uploaded_chunks: { type: array, items: { type: integer } } + ChunkUploadData: + type: object + properties: + chunk_hash: { type: string } + chunk_index: { type: integer } + skipped: { type: boolean, description: 幂等命中已传分片时为 true } + ChunkStatusData: + type: object + properties: + upload_id: { type: string } + file_name: { type: string } + file_size: { type: integer, format: int64 } + chunk_size: { type: integer, format: int64 } + total_chunks: { type: integer } + uploaded_chunks: { type: array, items: { type: integer } } + progress: { type: number } + PresignInitData: + type: object + properties: + upload_id: { type: string } + upload_url: { type: string, description: 'direct=S3 预签名 PUT URL;proxy=/presign/upload/proxy/{id}' } + mode: { type: string, enum: [direct, proxy] } + expires_in: { type: integer, example: 900 } + file_path: { type: string } + proxy_upload_url: { type: string, description: '仅 proxy 模式,与 upload_url 等价' } + legacy_proxy_upload_url: { type: string, description: '仅 proxy 模式;/api 前缀兼容别名,已废弃' } + LoginData: + type: object + properties: + id: { type: string, example: admin } + username: { type: string, example: admin } + token: { type: string } + token_type: { type: string, example: Bearer } + expires_at: { type: integer, format: int64, description: Unix 秒 } + expires_in: { type: integer, description: 秒 } + AdminFileItem: + type: object + description: snake_case 与 camelCase 双份输出(文档以 snake_case 为准) + properties: + id: { type: integer, format: int64 } + code: { type: string } + name: { type: string } + prefix: { type: string } + suffix: { type: string } + size: { type: integer, format: int64 } + is_text: { type: boolean } + is_chunked: { type: boolean } + is_expired: { type: boolean } + expired_at: { type: string, nullable: true } + expired_count: { type: integer } + used_count: { type: integer } + created_at: { type: string } + has_download_limit: { type: boolean } + is_permanent: { type: boolean } + remaining_downloads: { type: integer, nullable: true } + file_hash: { type: string, nullable: true } + text: { type: boolean, description: true 表示为文本分享 } + FileUpdateInput: + type: object + required: [id] + properties: + id: { type: integer, format: int64 } + code: { type: string } + prefix: { type: string } + suffix: { type: string } + expired_at: { type: string, description: ISO 8601 } + expired_count: { type: integer } + PolicyInput: + type: object + required: [action] + properties: + id: { type: integer, format: int64 } + action: { type: string, enum: [extend_24h, extend_7d, make_permanent, reset_download_limit] } + downloadLimit: { type: integer, minimum: 1, description: 仅 reset_download_limit;默认 5 } + PolicyResult: + type: object + properties: + id: { type: integer, format: int64 } + action: { type: string } + BatchStats: + type: object + description: 请求/成功/缺失/失败统计(snake_case 与 camelCase 双份) + properties: + requestedCount: { type: integer } + deletedCount: { type: integer, description: 仅批量删除 } + updatedCount: { type: integer, description: 仅批量更新 } + missingCount: { type: integer } + failedCount: { type: integer } + deleted: { type: array, items: { type: integer, format: int64 } } + missing: { type: array, items: { type: integer, format: int64 } } + failed: + type: array + items: + type: object + properties: + id: { type: integer, format: int64 } + reason: { type: string } + AuditItem: + type: object + description: 需求 ③ 审计行(snake_case 与 camelCase 双份) + properties: + id: { type: integer, format: int64 } + action: { type: string, enum: [upload, download] } + file_code: { type: string } + file_name: { type: string } + size_bytes: { type: integer, format: int64 } + transferred_bytes: { type: integer, format: int64 } + ip: { type: string } + user_agent: { type: string } + device_os: { type: string } + device_browser: { type: string } + device_type: { type: string } + actor: { type: string, enum: [admin, guest] } + result: { type: string, enum: [success, denied, failed] } + error_msg: { type: string } + duration_ms: { type: integer, format: int64 } + created_at: { type: string } + +security: [] diff --git a/docs/security-audit-2026-09-05.md b/docs/security-audit-2026-09-05.md new file mode 100644 index 0000000..b1e36d9 --- /dev/null +++ b/docs/security-audit-2026-09-05.md @@ -0,0 +1,186 @@ +# FileCodeBox Go 重写版 · 安全审计报告 + +- 审计日期:2026-09-05 +- 审计范围:`server/`(Go 1.27.1 Gin+GORM 后端,56 个文件约 13,900 行)、`web/`(Vue 3 前端)、`deploy/`(Dockerfile / docker-compose) +- 审计方式:人工代码审读(认证/会话、上传下载全链路、存储引擎、配置与注入面)+ 工具佐证(`go vet`、`govulncheck`、`npm audit`) +- 结论速览:**未发现可直接导致 RCE、SQL 注入、路径穿越或认证绕过的高危问题**;发现 5 项中危问题(密码哈希强度、S3 直传校验缺口、请求体无上限 DoS、依赖漏洞、上传会话资源滥用)与若干低危/加固建议。 +- **修复状态(2026-09-05 第二轮):M1–M5、L1–L10 及可行动 Info 项已全部修复**,逐项见各条目「✅ 修复」标记;验证:`gofmt`/`go vet`/`go test ./...` 全绿,`govulncheck` 0 命中,二进制端到端冒烟(初始化→登录→审计落库→限流锁定→XSS 净化→robots→分享取件)通过。前端 `markdown.ts` 净化器加固需重建前端产物(已重建 `web/dist` 与 `server/web/dist`)方可进入 go:embed 二进制。 + +--- + +## 一、中危(Medium) + +### M1 管理员密码哈希强度不足(单轮 SHA256+盐)✅ 已修复 + +- 位置:`server/internal/settings/password.go:15-21` +- 原状:`HashPassword` 生成 `sha256$salt$hash`,单轮 SHA256 + 16 字节随机盐。GPU 单卡对 SHA256 可达 10¹⁰ 次/秒,若数据库泄露(SQLite 文件/Postgres 备份),弱口令可被瞬间离线爆破。 +- ✅ 修复:`HashPassword` 改为 **bcrypt(cost 12)**,输出 `bcrypt$` + 原生 `$2a$12$…` 哈希串;`VerifyPassword` 兼容 bcrypt$/sha256$/明文三种历史格式实现平滑迁移;新增 `NeedsRehash`,管理员登录成功时自动将旧格式重哈希写回(`adminLogin` 内触发);密码长度 >72 字节按 bcrypt 语义截断(`bcryptBytes`);`GenerateJWTSecret` 的 rand 错误不再忽略(panic 显式失败)。测试:settings 包 + `TestAdminPasswordAutoUpgrade`。 + +### M2 S3 预签名直传(direct 模式)绕过大小与类型校验 ✅ 已修复 + +- 位置:`server/internal/api/presign.go:59-171`(init)、`presign.go:275-345`(confirm)、`server/internal/storage/s3.go:538-554`(PresignPutURL) +- 原状: + - init 只校验**声明**的 `file_size`;预签名 PUT URL 未签入 Content-Length 约束,客户端实际可 PUT 任意大小对象; + - confirm 仅 `FileExists`,不 `Stat` 校验实际对象大小,分享记录 `Size` 直接取声明值; + - magic bytes / 类型白名单校验在 direct 模式完全不生效(内容不经过服务器)。 +- 影响:开启游客上传 + S3 引擎的部署中,任意访客可绕过 `max_file_size` 与 `storageLimit`(配额按声明值记账),向桶内塞入任意大小/内容的对象(存储成本攻击、策略绕过)。 +- ✅ 修复(多引擎一致,local/S3/WebDAV 全覆盖): + 1. `Storage` 接口新增 `HeadMeta(ctx, savePath, headBytes)`;S3 用 Range GET `bytes=0-(n-1)`,WebDAV 用 Stat + Range,local 直接 Open; + 2. `presignConfirm` 现在:HeadMeta 取实际大小 → 超策略上限则 **DeleteFile + 释放配额 + 403**;实际大小与声明差 >1KB → **DeleteFile + 400**;前 64 字节补做 `validateFileMagic`; + 3. `presignInit` 拒绝 `file_size <= 0` 声明; + 4. 测试:`TestPresignConfirmRejectsOversizeObject`、`TestPresignConfirmRejectsSizeMismatch`。 + +### M3 请求体无全局大小上限,多处“先整读后校验”可被 DoS ✅ 已修复 + +- 位置: + - `server/internal/api/helpers.go:681-721`(`bindJSONOrForm` 对 text/plain 形态 `io.ReadAll` 全量进内存;JSON 绑定同样全量读入) + - `server/internal/api/chunk.go:314`(`io.ReadAll(LimitReader(f, ChunkSize+1))` 单分片全量进内存,`chunk_size` 上界=策略 `max_file_size`,schema 允许至 10GiB) + - `server/internal/api/share.go:100-128`(文本 222KB 限制在读完整个 body 之后才判定) +- 原状:全服务无 `http.MaxBytesReader`,也无上传前的 Content-Length 预检;multipart 大文件会先被完整解析(>32MB 落临时盘)后才被 `CheckSize` 拒绝。 +- 影响:默认 `openUpload=1` 的部署下,未认证攻击者可用大 body 消耗内存/磁盘/带宽。 +- ✅ 修复: + 1. 新增 `middleware.BodyLimit(limitFn)`,`main.go` 全局装配(在 GuardNotInitialized 之后、Audit 之前):`/setup`、`/admin/*`、`/share/text|metadata|select` 一律 1MiB,其余端点 `maxFileSize(+2MiB 开销)`(前端单请求单分片,已核实安全); + 2. `/share/text` 入口先查 `Content-Length > 441KB` 直接 403(读前预检); + 3. 单分片 `chunk_size` 硬上限 32MiB(超出 400),不再跟随 10GiB 的文件策略; + 4. 测试:`TestChunkSizeCap`。 + +### M4 依赖漏洞(govulncheck 实际命中 3 个 + 8 个 imported 级)✅ 已修复 + +- 工具输出(`govulncheck ./...`): + - `golang.org/x/text v0.30.0`:GO-2026-5970 非法输入死循环 DoS(经 gorm 归一化路径可达),修复于 **v0.39.0**; + - `github.com/quic-go/quic-go v0.54.0`:GO-2026-5676(修复 v0.59.1)、GO-2025-4233(修复 v0.57.0)QPACK 扩张 DoS(实际未启用 HTTP/3 监听,实践影响低); + - `golang.org/x/net v0.45.0`:8 个 imported 级漏洞(GO-2026-5030/5029/5028/5027/5026/5025/4918 等,含 HTTP/2 传输死循环),修复于 v0.53–v0.55。 +- ✅ 修复:`x/text v0.41.0`、`x/net v0.58.0`、`x/crypto v0.56.0`(转为直接依赖,供 bcrypt)、`quic-go v0.59.1`;`govulncheck ./...` 复扫 **0 个可达漏洞**(模块级仅剩 1 个未调用项)。前端 `npm audit` 仍报 2 个 moderate(`vue-i18n → @intlify/core-base`,上游暂无修复版,保持关注升级)。 + +### M5 上传会话与容量预留可被滥用(init 不计数、无过期清理)✅ 已修复 + +- 位置:`server/internal/api/chunk.go:39-157`(chunkInit 从不 `Limiter.Add`,上传限流仅在 complete/presign-init/shareFile 成功时计数)、`server/internal/api/helpers.go:380-436`(预留 TTL:chunk 24h / presign 15min) +- 原状: + - 游客可无限次 `POST /chunk/upload/init` 创建会话(每次写入 `upload_chunks` 行 + 24h 容量预留),无后台任务回收过期预留、未完成会话与孤儿分片对象(local `chunks/` 目录、S3 `*.part`); + - 若配置了 `storageLimit`,攻击者可用多次 init 把全部配额占用满 24 小时 → 全站上传 507(拒绝服务);默认 `storageLimit=0` 时则是磁盘/DB 垃圾持续累积。 +- ✅ 修复: + 1. `chunkInit` 在新会话保留成功后即 `Limiter.Add(c, LimitUpload)` 计数; + 2. chunk 预留 TTL 24h → **2h**(续传刷新); + 3. 新增 `internal/janitor` 后台清理循环(默认 10 分钟):过期 `storage_reservations`、超时(>24h 未完成)`upload_chunks` 会话(连带清理分片对象)、过期直传 presign 会话(连带删除残留对象);`main.go` 启动时随 ctx 拉起。 + +--- + +## 二、低危(Low) + +### L1 初始化向导(/setup)存在接管窗口 ✅ 已修复 + +- 位置:`server/internal/api/setup.go:47-63`、`middleware/audit.go:222-236` +- 原状:服务公开到公网后、管理员完成 /setup 前,任何人可抢先完成初始化并设置管理员密码(经典 setup race;双检查只防并发写坏,不防抢占)。 +- ✅ 修复:新增 `autoInitIfNeeded`(`main.go`,SystemStart 后执行)——设置 `FCB_ADMIN_PASSWORD`(≥8 位,否则告警跳过)即可在服务启动瞬间完成管理员初始化并生成 jwt_secret,消除 /setup 被抢占窗口;`deploy/.env.example` 与 README 安全清单已补充说明(初始化后建议移除该变量)。 + +### L2 下载令牌为非 HMAC 拼接哈希且非常量时间比较 ✅ 已修复 + +- 位置:`server/internal/api/helpers.go:249-253`(`sha256(code‖timeFactor‖"000"‖secret)`)、`share.go:458`(`key != GetSelectToken(...)`) +- 原状:secret 后置拼接,长度扩展不适用、256 位密钥不可爆破,当前**不可实际利用**;但拼接串存在理论歧义(code 与时间窗数字边界重叠),且字符串比较非常量时间。 +- ✅ 修复:`GetSelectToken` 改为 **HMAC-SHA256(secret, code‖timeFactor)**;新增 `VerifySelectToken` 用 `hmac.Equal` 常量时间比较(允许当前/上一两个时间窗,防临界失效);`shareDownload` 已切换到 `VerifySelectToken`。 + +### L3 数字取件码空间过小,防撞库完全依赖单 IP 限流 ✅ 已修复 + +- 位置:`server/internal/api/helpers.go:114-125`、`validatePickupCode`(4 位下限) +- 原状:`code_generate_type=number` 时仅 9 万空间(5 位数字),默认 `errorCount=10/分/IP` 下单 IP 需约 6 天扫完,分布式多 IP 可显著缩短;自定义码允许 4 位(36⁴≈168 万)。 +- ✅ 修复:自定义提码最小长度 4 → **5 位**(`pickupCodeMinLen=5`,36⁵≈6000 万空间);测试 `v31_test.go` 与 `TestPickupCodeMinLen` 同步更新。 + +### L4 `enableChunk` 开关后端不强制 ✅ 已修复 + +- 位置:`server/internal/api/router.go:56-67` +- 原状:`/chunk/*` 路由不检查 `cfg.EnableChunk()`,关闭开关后接口仍可用(仅前端隐藏入口)。若该开关被当作安全策略,需在 handler 层强制(403)。 +- ✅ 修复:新增 `requireChunkEnabled` 守卫(关闭时 403「分片上传未启用」),挂到 `chunkInit`(首检查)、`chunkUpload`、`chunkComplete` 三个端点;测试 `TestChunkToggleEnforced` 验证 0→403 / 1→200。 + +### L5 管理端安全事件未入审计日志 ✅ 已修复 + +- 位置:`server/internal/middleware/audit.go:55-78`(DefaultClassifier 仅覆盖 upload/download) +- 原状:管理员登录失败、配置修改、密码修改、引擎切换、文件删除等管理操作均不落审计。 +- ✅ 修复:`DefaultClassifier` 扩展 `adminAuditActions`(login/logout、config/update、settings/password、storage/switch、file/update/delete/batch-*、policy-action 等 POST/PATCH/DELETE 敏感操作 → `audit.ActionAdmin`);`Audit` 中间件跳过条件由「非 upload/download 即跳过」改为「分类未命中才跳过」,admin 动作同样建 `auditEntry` 并按 HTTP 状态兜底落库;新增 `audit_l5_test.go` 回归。端到端实测:登录失败 401 落 `admin/denied`、成功落 `admin/success`。 + +### L6 CORS 对所有接口(含 /admin/*)放开 `*` ✅ 已修复 + +- 位置:`server/internal/middleware/cors.go:10` +- 原状:Bearer 模式下无 CSRF 风险,但一旦 token 泄露(见 L8),任意网站均可跨域携带 token 调用管理 API。 +- ✅ 修复:`Cors` 重写——`/admin/*` 请求带 Origin 且既不同源也不在白名单(`site_domain` 配置注入)时**不回任何 CORS 头**(浏览器拦截跨域读取),预检直接 204;公开接口维持 `*`(Bearer 认证,无 Cookie CSRF 面);无 Origin 的非浏览器请求不受影响。 + +### L7 管理端通知内容 `v-html` 直出(存储型 XSS 面)✅ 已修复 + +- 位置:`web/src/components/NotifyPop.vue:23`(`notify_content` 来自 `GET /api/v1/config`,管理端可设) +- 原状:设计上"允许 `` 等受控 HTML",但服务端/前端均无净化。管理员账号被盗即可对全站访客注入脚本。 +- ✅ 修复(双侧): + 1. 服务端新增 `settings.SanitizeInlineHTML` 白名单净化器(`sanitize.go` + 18 个单测):仅保留文本与 ``(引号内 `>`、未闭合标签、`script/style/iframe/svg/math` 等危险标签连内容整体丢弃、事件属性不透传、`javascript:/data:` 拒绝);在 **adminConfigUpdate 写入侧** 与 **publicConfig 读取侧** 双重调用(覆盖历史存量与直改库数据); + 2. 前端 `web/src/utils/markdown.ts` 净化器加固:黑名单补 `svg/math/frame/applet/template/noscript` 等,新增 `srcdoc/sandbox/formaction/action/xlink:href/srcset` 等危险属性表,URL 校验改为协议白名单(http/mailto/相对/锚点,src 另许 `data:image/`)。端到端实测:`` 与 `javascript:` 链接被剔除、合法 `` 保留。 + - 注:前端净化器改动需重建前端产物(已重建 `web/dist` 并同步 `server/web/dist`)方进入 go:embed 二进制;未来渲染任何外部内容前建议仍替换为 DOMPurify。 + +### L8 管理员令牌存 localStorage、默认会话 30 天 ✅ 已修复 + +- 位置:`web/src/api/http.ts:14-33`、`server/internal/config/config.go:110` +- 原状:XSS 可窃取且有效期长(可配 1–365 天)。 +- ✅ 修复:`AdminSessionExpireDefault` 30 天 → **7 天**(仍可配 1–365 天,config 包测试通过);敏感操作改密已有旧密码校验 + jwt_secret 轮换(全部旧 token 失效)。`FCB_ADMIN_SESSION_EXPIRE` 环境变量种子同步支持(`main.go` + `.env.example` 注释)。 + +### L9 缓存故障时限流 fail-open ✅ 已修复 + +- 位置:`server/internal/middleware/ratelimit.go:148-164` +- 原状:Redis/缓存异常时 `Check` 一律放行(登录爆破防护随之失效)。 +- ✅ 修复:`Check`/`Add` 区分 `cache.ErrNotFound`(窗口内无计数,正常放行)与真实缓存故障;故障时降级为**进程内固定窗口计数**(带过期清理与容量上限 8192,单实例语义),缓存恢复自动回到共享缓存;测试 `TestRateLimiterFallbackOnCacheFailure` 证明故障降级下超限后 fail-close 拒绝。端到端实测:连续错误密码 3 次 401 后第 4 次起 423 锁定。 + +### L10 反向代理场景的限流与审计 IP 失真 ✅ 已修复(文档 + 配置面) + +- 位置:`server/internal/middleware/ratelimit.go:32-96`、`config.go:52` +- 原状:实现(仅信任 `FCB_TRUSTED_PROXIES` 命中的直连地址才采信 XFF)是正确的;但部署文档未强调反代后必须配置可信代理,否则全体用户共享代理 IP 的限流桶(互相误伤)且审计 IP 全是代理地址;反向配置错误则可伪造 XFF 绕过限流。 +- ✅ 修复:`deploy/README.md` 新增「安全清单(生产部署必读)」7 条(可信代理、立即初始化/FCB_ADMIN_PASSWORD、修改组件默认凭据与端口发布、Postgres TLS、会话有效期、限流降级语义、内置清理任务);`deploy/.env.example` 为 `FCB_TRUSTED_PROXIES`、`FCB_ADMIN_PASSWORD`、`FCB_ADMIN_SESSION_EXPIRE` 补充注释说明,弱凭据处(MinIO/WebDAV/Postgres)加「仅限本机冒烟,生产必须修改」警示。 + +--- + +## 三、提示 / 信息(Info)修复情况 + +1. `version`/`storage_engine` 公开暴露(小信息收集面):**保留**(前端展示与诊断需要,风险极低)。 +2. compose 弱凭据/端口发布:✅ 已在 `.env.example` 与 README 安全清单加警示(凭据为示例值,端口建议删除或绑 127.0.0.1)。 +3. `robotsText` 无路由:✅ 已新增 `GET /robots.txt`(`router.go`,输出配置内容,text/plain);端到端实测 200 且内容生效。 +4. `adminFileList` LIKE 未转义:✅ 已加 `escapeLike`(`\`→`\\`、`%`→`\%`、`_`→`\_`)+ `ESCAPE '\'`(admin-only,防御性修复)。 +5. Postgres 并发配额超额记账:✅ `reserveStorage` 包事务并对 Postgres 加 `pg_advisory_xact_lock`("FCBQ" 键)串行化配额判定;SQLite 写串行不受影响。 +6. `crypto/rand` 错误忽略:✅ `GenerateJWTSecret` 已改 panic 显式失败(见 M1);`generateCode`/`randomHex` 维持重试回退(非安全关键路径)。 + +--- + +## 四、确认到位的安全设计(无需修改) + +- **JWT**:HS256 + `WithValidMethods` 双重防算法混淆;密钥为 64 位 hex 随机;改密自动轮换(使全部旧会话失效);过期/签名校验完备。 +- **SQL**:全部 GORM/占位符参数化;排序字段白名单(`normalizeSortBy`);无字符串拼接 SQL。 +- **路径安全**:`SanitizePath`/`SanitizeFileName` + `withinRoot` + `EvalSymlinks` 符号链接逃逸双重校验;S3 key、WebDAV 路径同样清洗;未发现穿越。 +- **下载响应**:统一 `application/octet-stream` + `attachment` + RFC 5987 文件名编码,杜绝分享文件被当 HTML 渲染的存储型 XSS;取件文本以 `text/plain` 下发且前端 `
{{ }}
` 渲染。 +- **认证/授权**:`/admin/*` 全组 Bearer 鉴权;`/setup` 受 GuardNotInitialized 白名单约束;旧默认密码 `FileCodeBox2023` 视为未初始化;`verifyLegacyDefault`/密码比较用 `hmac.Equal`。 +- **上传策略**:大小上限五处口径一致(share/chunk-init/chunk-累计/presign/complete),magic bytes 防伪造,自定义码查重 + 唯一索引兜底并发。 +- **限流**:取件错误/登录失败/上传/metadata 四类固定窗口;clientIP 仅信任显式配置的代理。 +- **容量**:单条 INSERT..SELECT 原子预留判定,防超卖。 +- **部署**:多阶段构建、非 root(uid 10001)运行、`.dockerignore` 合理、`.env` 未含真实密钥(当前也非 git 仓库;初始化 git 后应将 `deploy/.env` 加入忽略清单)。 + +--- + +## 五、修复优先级建议(原计划,已全部落实) + +| 优先级 | 事项 | 状态 | +|---|---|---| +| 立即 | M4 依赖升级;M2 的 confirm 大小校验 | ✅ 已完成 | +| 短期 | M1 密码哈希迁移 bcrypt;M3 全局 MaxBytesReader;M5 init 计数 + 清理循环 | ✅ 已完成 | +| 计划 | L1–L10 按运营形态取舍 | ✅ 已全部修复 | + +--- + +## 六、修复验证记录(2026-09-05 第二轮) + +- **静态检查**:`gofmt -l .` 无输出、`go vet ./...` 通过、`go build ./...` 通过。 +- **单元/集成测试**:`go test ./...` 全绿(api/audit/cache/config/database/middleware/settings/storage 全部 ok),新增修复回归测试: + - `settings/sanitize_test.go`(L7 净化器 19 例) + - `middleware/audit_l5_test.go`(L5 admin 审计落库) + - `middleware/ratelimit_fallback_test.go`(L9 缓存故障降级 fail-close) + - `api/security_fixes_test.go`(L4 开关强制、M3 chunk 上限、L3 提码长度、M2 confirm 大小/超限、M1 密码迁移) +- **依赖扫描**:`govulncheck ./...` 0 可达漏洞;`npm audit --omit=dev` 剩 2 moderate(vue-i18n 上游未发布修复,跟踪中)。 +- **端到端冒烟**(编译二进制 + SQLite 实跑): + 1. `/setup` 初始化 200(M1 生效:DB 中 `admin_token = bcrypt$$2a$12$…`); + 2. 错误密码登录 401 → 审计落 `admin/denied`;成功登录落 `admin/success`(L5); + 3. 连续 3 次错误后第 4 次起 423 锁定(L9 限流); + 4. 配置写入 `` + `javascript:` 链接 → `/api/v1/config` 输出已剔除、合法 `
` 保留(L7); + 5. `/robots.txt` 200 且内容生效(Info3); + 6. 文本分享创建 + 取件下载全链路 200。 +- **遗留跟踪**:`vue-i18n` 上游修复版本发布后升级(当前 npm audit 的 2 个 moderate 均来源于此)。 diff --git a/docs/verify-routes.js b/docs/verify-routes.js new file mode 100644 index 0000000..27e5820 --- /dev/null +++ b/docs/verify-routes.js @@ -0,0 +1,38 @@ +// 验证 docs/openapi.yaml 与 Go 路由一致性(t4 验收辅助) +const fs = require('fs'); +const YAML = require('/opt/homebrew/lib/node_modules/@deepseek-ai/dsh/node_modules/yaml'); + +const doc = YAML.parse(fs.readFileSync('docs/openapi.yaml', 'utf8')); + +const goRoutes = new Set(); +function scan(src) { + let group = ''; + for (const raw of src.split('\n')) { + const line = raw.trim(); + const gm = line.match(/^(\w+)\s*:=\s*r\.Group\("([^"]+)"/); + if (gm) { group = gm[2]; continue; } + const am = line.match(/^admin\.POST\("([^"]+)"/); + if (am) { goRoutes.add('POST ' + group + am[1]); continue; } + const m = line.match(/^(r|share|chunk|presign|authed)\.(GET|POST|PUT|PATCH|DELETE)\("([^"]+)"/); + if (m) { + const base = (m[1] === 'r') ? '' : group; + goRoutes.add(m[2] + ' ' + base + m[3].replace(/:([A-Za-z]+)/g, '{$1}')); + } + } +} +scan(fs.readFileSync('server/internal/api/router.go', 'utf8')); +scan(fs.readFileSync('server/internal/api/admin.go', 'utf8')); +scan(fs.readFileSync('server/internal/api/setup.go', 'utf8')); + +const oaRoutes = new Set(); +for (const [p, item] of Object.entries(doc.paths)) { + for (const method of Object.keys(item)) { + if (['get', 'post', 'put', 'patch', 'delete'].includes(method)) oaRoutes.add(method.toUpperCase() + ' ' + p); + } +} +const goOnly = [...goRoutes].filter(r => !oaRoutes.has(r)); +const oaOnly = [...oaRoutes].filter(r => !goRoutes.has(r)); +console.log('Go 路由数:', goRoutes.size, ' OpenAPI 操作数:', oaRoutes.size); +console.log('Go 有而 OpenAPI 缺:', goOnly.length ? goOnly : '无'); +console.log('OpenAPI 有而 Go 缺:', oaOnly.length ? oaOnly : '无'); +console.log('(注:POST /chunk/upload 为扁平兼容端点,文档按 go-api 定稿仅写主路径 /chunk/upload/{uploadID}/{chunkIndex})'); diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..df0d775 --- /dev/null +++ b/server/README.md @@ -0,0 +1,176 @@ +# 文件快传 Go 后端(server/) + +Go 1.27.1 + Gin + GORM 重写的文件快传后端。数据库**默认 SQLite**(modernc.org/sqlite +纯 Go 驱动,CGO_ENABLED=0 交叉编译友好,零外部依赖),配置 `FCB_DB_DRIVER=postgres` 后 +切换 Postgres(需求 ⑧);Redis 为**可选**增强(未配置 `FCB_REDIS_ADDR` 时自动降级为进程内存缓存)。 + +## 目录结构 + +``` +server/ +├── cmd/server/main.go # 入口:装配 配置→DB→缓存→设置→审计→中间件→路由 +└── internal/ + ├── config/ # 配置:FCB_* 环境变量基线 + DB settings KV 运行时覆盖 + │ └── schema.go # v2 新配置键 schema(键名/类型/默认值/边界) + ├── model/ # GORM 模型 + 双方言 AutoMigrate + ├── cache/ # 缓存统一接口:redis.go / memory.go 双实现 + ├── database/ # 双方言连接与迁移(sqlite 默认 / postgres 可选) + ├── settings/ # settings KV 读写、密码哈希(sha256$salt$hash)、键 schema re-export + ├── audit/ # 审计服务(Sink 抽象、失败重试队列)+ UA 设备解析 + ├── middleware/ # JWT 认证、IP 限流、审计中间件、CORS、IP 解析 + ├── storage/ # 存储引擎契约(interface.go 为 go-storage 的实现契约) + └── response/ # 统一响应 {"code":200,"msg":"","data":...} +``` + +## 环境变量 + +| 变量 | 必需 | 默认 | 说明 | +|---|---|---|---| +| `FCB_DB_DRIVER` | ❌ | `sqlite` | 数据库驱动:`sqlite` \| `postgres`(需求 ⑧) | +| `FCB_DB_DSN` | 视驱动 | `./data/filecodebox.db` | postgres:连接串(**必需**),如 `postgres://user:pass@host:5432/filecodebox?sslmode=disable`;sqlite:数据库文件路径(可空,自动创建 `data/` 目录) | +| `FCB_REDIS_ADDR` | ❌ | 空 | 为空时缓存降级为内存实现 | +| `FCB_LISTEN` | ❌ | `:8466` | 监听地址 | +| `FCB_STORAGE_ENGINE` | ❌ | `local` | `local` \| `s3` \| `webdav` | +| `FCB_TRUSTED_PROXIES` | ❌ | 空 | 可信代理 CIDR(逗号分隔),用于解析真实客户端 IP | + +### 数据库模式(需求 ⑧) + +- **SQLite(默认)**:`FCB_DB_DRIVER=sqlite`(或缺省)。零 DSN 零依赖启动,数据库文件 + 默认 `./data/filecodebox.db`(`FCB_DB_DSN` 可覆盖路径;父目录自动创建)。 + 连接参数:`busy_timeout=10s` + `WAL` 日志模式 + `foreign_keys=1`(通过 DSN pragma 注入)。 +- **Postgres(可选)**:`FCB_DB_DRIVER=postgres` 且必须提供 `FCB_DB_DSN`,否则启动报错。 + 连接池沿用 v1 参数(32/8、1h 轮换)。 +- 双方言共用 GORM 抽象:AutoMigrate、settings KV、全部业务查询方言无关; + 唯一原生 DDL(migrates 台账表)在 `database.createMigratesTable` 内部分支处理。 + +## v2 新增配置键(internal/config/schema.go 为单一事实来源) + +| 键 | 类型 | 默认 | 说明 | +|---|---|---|---| +| `background_url` | string | `""` | 需求 ①:背景图 URL/上传地址(空=主题默认;legacy `background` 键兜底) | +| `footer_text` | string | `""` | 需求 ②:页脚自定义内容(≤2000 字符) | +| `footer_beian` | string | `""` | 需求 ②:备案号(≤128 字符) | +| `notify_enabled` | int | `1` | 需求 ③:通知开关(1 开 / 0 关) | +| `notify_title` / `notify_content` | string | 见 defaults | 需求 ③:通知标题/内容(沿用参考实现语义) | +| `max_save_seconds` | int64 | `0` | 需求 ④:最长保存秒数上限(0=仅默认 7 天兜底,≤365 天) | +| `max_save_count` | int | `0` | 需求 ④:单次分享最大可取次数上限(0=不限制,≤100000) | +| `max_file_size` | int64 | `0` | 需求 ⑩:存储策略-单文件上限字节(0=回落 `uploadSize`,≤10GiB) | +| `uploadSize` / `allowed_file_types` / `storageLimit` / `openUpload` | 既有 | - | 存储策略既有键(语义不变) | +| `uploadCount` / `uploadMinute` | 既有 | `10` / `1` | 上传频率限制(对齐参考 `ip_limit["upload"]`) | + +管理端与文档(t2/t4)以 `config.KVSchema()`(`settings.KVSchema()` re-export)为元数据源; +schema 同步测试保证 `KVSchema()` 与 `defaults()` 逐键一致。 + +## 数据模型 + +- `file_codes`:文件/文本分享(对齐参考 `apps/base/models.py::FileCodes`) +- `upload_chunks`:分片上传记录 +- `key_values`:运行时配置 KV(`settings` / `sys_start` 键) +- `presign_upload_sessions`:预签名直传会话 +- `storage_reservations`:上传容量预留 +- `audit_logs`:上传/下载审计(时间/IP/UA/设备/动作/结果/字节数/耗时),需求 ③ +- `migrates`:迁移台账表(双方言 DDL 分支,见 `database.createMigratesTable`) + +## 存储引擎契约(internal/storage/interface.go) + +`go-storage` 按此契约实现 local/s3/webdav 三引擎(**接口签名已冻结**): + +```go +type Storage interface { + SaveFile(ctx, r io.Reader, savePath) (int64, error) // 流式保存 + DeleteFile(ctx, savePath) error + Open(ctx, savePath, rng *Range) (*Download, error) // Range 下载 + Stat(ctx, savePath) (*FileMeta, error) + SaveChunk(ctx, uploadID, chunkIndex, r io.Reader, savePath) (int64, error) + MergeChunks(ctx, uploadID, total, verifyHash, savePath) (int64, string, error) + CleanChunks(ctx, uploadID, savePath) error + FileExists(ctx, savePath) (bool, error) + PresignGetURL(ctx, savePath, expires) (string, error) // 不支持→ErrNotSupported + PresignPutURL(ctx, savePath, expires) (string, error) + HealthCheck(ctx) error +} +``` + +错误映射约定:`ErrNotFound`→404、`ErrInvalidPath`→400、`ErrUnavailable`→503、`ErrNotSupported`→501、`ErrRangeNotSatisfiable`→416。 + +引擎通过 `storage.RegisterEngine("local"|"s3"|"webdav", factory)` 注册, +`storage.NewEngine(ctx, name)` 构造。分片路径约定:`<父目录>/chunks//.part`。 + +### 引擎构造选项(t3 API 层接入) + +构造引擎前必须先注入选项(否则 local 退到系统临时目录、s3/webdav 因缺配置失败): + +```go +storage.SetEngineOptions(storage.EngineOptions{ + Local: storage.LocalOptions{Root: cfg.GetString("local_storage_path")}, + S3: storage.S3Options{ + AccessKeyID: cfg.GetString("s3_access_key_id"), SecretAccessKey: cfg.GetString("s3_secret_access_key"), + Bucket: cfg.GetString("s3_bucket_name"), Endpoint: cfg.GetString("s3_endpoint_url"), + Region: cfg.GetString("s3_region_name"), AddressingStyle: cfg.GetString("s3_addressing_style"), + }, + WebDAV: storage.WebDAVOptions{ + BaseURL: cfg.GetString("webdav_url"), Username: cfg.GetString("webdav_username"), + Password: cfg.GetString("webdav_password"), RootPath: cfg.GetString("webdav_root_path"), + }, +}) +st, err := storage.NewEngine(ctx, cfg.Engine()) // 构造后调 st.HealthCheck(ctx) 完成启动自检 +``` + +引擎要点:local 原子写(临时文件+fsync+rename)、防穿越+符号链接逃逸校验; +s3 原生 multipart 流式合并、预签名直链、SDK 内置 5xx 重试;webdav 连接池复用、 +Basic/Digest 自动协商、Range 透传、按需逐级 MKCOL(带缓存)、5xx/429 指数退避重试、 +下载 io.Pipe 流式不落盘。三引擎均支持分片上传/合并(SHA256 校验)与 Range 下载。 + +## 审计埋点用法(API 层) + +```go +r.Use(middleware.Audit(auditSvc, nil)) // nil=默认按路由前缀分类 upload/download + +// handler 内填充业务字段并显式落库(推荐): +middleware.AuditSet(c, func(e *audit.Entry) { e.FileCode = code; e.FileName = name }) +middleware.AuditRecordRequest(c, auditSvc, model.AuditResultSuccess, "") +// 或不显式落库:中间件按 HTTP 状态兜底(4xx/5xx→failed,401/403/423/429/428→denied) +``` + +下载响应字节数由中间件自动统计;上传字节数由 handler 填 `TransferredBytes`。 + +## 限流语义(对齐参考实现) + +- `error`(取件错误)/`login`(登录失败):**仅在失败时计数**,handler 调用 `limiter.Add(c, kind)` +- `upload`:**成功上传才计数**(先 `Check` 放行,成功后 `Add`) +- `metadata`:每次访问即计数,可用 `RequireRateLimit` 中间件 +- 超限返回 HTTP 423;规则来自 settings KV(errorCount/errorMinute 等),可运行时调整 + +## 本地开发 + +```bash +# 默认 SQLite 模式:零依赖,数据库落 ./data/filecodebox.db +go run ./cmd/server # 启动于 :8466 +curl localhost:8466/api/v1/health + +# Postgres 模式(可选) +export FCB_DB_DRIVER=postgres +export FCB_DB_DSN='postgres://postgres:postgres@localhost:5432/filecodebox?sslmode=disable' +go run ./cmd/server + +# 双方言单测:sqlite 始终执行;postgres 需真实实例(FCB_TEST_PG_DSN 指向测试库) +FCB_TEST_PG_DSN='postgres://postgres:postgres@localhost:5432/fcb_test?sslmode=disable' go test ./internal/database/ ./internal/settings/ +go test ./... # 全量单测(未设置 FCB_TEST_PG_DSN 时自动跳过 PG 用例) +``` + +系统未初始化时除 `/setup` 与 `/api/v1/health` 外一律返回 428,初始化路由由 API 层任务接入。 + +## 开发注意 + +- **GOCACHE/GOMODCACHE 必须用 `export` 设置**(沙箱环境):本地与 CI 沙箱通常禁止写默认 Go 缓存 + 目录(`~/Library/Caches/go-build`)。注意一个隐蔽的坑——用**内联前缀变量**的方式 + (`GOCACHE=... GOMODCACHE=... go build ./... && go vet ./... && go test ./...`)时, + 只有第一条命令继承这些变量,`&&` 后续命令(vet/test)会丢失前缀、落回默认缓存路径, + 报错 `operation not permitted`(指向 `~/Library/Caches/...`)却极易误判为代码问题。 + 正确写法: + + ```bash + cd server + export GOCACHE=$(pwd)/../.gocache GOMODCACHE=$(pwd)/../.gomodcache GOSUMDB=off + go build ./... && go vet ./... && go test ./... # 后续命令也能继承 export 的变量 + ``` diff --git a/server/cmd/server/main.go b/server/cmd/server/main.go new file mode 100644 index 0000000..57f2a3f --- /dev/null +++ b/server/cmd/server/main.go @@ -0,0 +1,301 @@ +// 文件快递柜 Go 重写版服务入口: +// 装配 配置 → Postgres → 缓存 → 设置 → 审计 → 存储引擎 → 中间件 → API 路由。 +package main + +import ( + "context" + "log" + "net/http" + "os" + "os/signal" + "strconv" + "strings" + "syscall" + "time" + + "github.com/gin-gonic/gin" + + "filecodebox/internal/api" + "filecodebox/internal/audit" + "filecodebox/internal/cache" + "filecodebox/internal/config" + "filecodebox/internal/database" + "filecodebox/internal/janitor" + "filecodebox/internal/middleware" + "filecodebox/internal/settings" + "filecodebox/internal/storage" +) + +// APP_VERSION 版本号,对齐参考仓库 VERSION。 +const APP_VERSION = "2.5.6" + +func main() { + log.SetFlags(log.LstdFlags | log.Lshortfile) + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + + // 1. 配置(环境变量基线;引擎相关 env 种子在此注入,DB KV 仍可覆盖) + cfg, err := config.New() + if err != nil { + log.Fatalf("[boot] 配置加载失败: %v", err) + } + applyEnvEngineSeeds(cfg) + + // 2. 数据库(需求 ⑧:默认 SQLite 零依赖;配置 FCB_DB_DRIVER=postgres+DSN 后走 Postgres) + db, err := database.Open(ctx, database.Options{Driver: cfg.Env.DBDriver, DSN: cfg.Env.DBDSN}) + if err != nil { + log.Fatalf("[boot] 数据库初始化失败: %v", err) + } + defer func() { _ = database.Close(db) }() + if err := database.Migrate(ctx, db); err != nil { + log.Fatalf("[boot] 数据库迁移失败: %v", err) + } + if cfg.Env.DBDriver == config.DBDriverPostgres { + log.Printf("[boot] Postgres 数据库已启用") + } else { + log.Printf("[boot] SQLite 数据库: %s", cfg.SQLitePath()) + } + + // 3. 缓存:Redis 可选,未配置则内存降级(需求 ⑧:默认即内存,Redis 为增强项) + cacheImpl, err := cache.New(ctx, cache.RedisOptions{Addr: cfg.Env.RedisAddr, DB: cfg.Env.RedisDB}) + if err != nil { + log.Printf("[boot] Redis 连接失败,降级为内存缓存: %v", err) + cacheImpl, err = cache.NewMemory(), nil + if err != nil { + log.Fatalf("[boot] 内存缓存初始化失败: %v", err) + } + } + defer func() { _ = cacheImpl.Close() }() + if cfg.Env.RedisAddr != "" { + if cfg.Env.RedisDB != 0 { + log.Printf("[boot] Redis 缓存已启用: %s (db=%d)", cfg.Env.RedisAddr, cfg.Env.RedisDB) + } else { + log.Printf("[boot] Redis 缓存已启用: %s", cfg.Env.RedisAddr) + } + } else { + log.Println("[boot] 未配置 FCB_REDIS_ADDR,使用内存缓存") + } + + // 4. 设置管理器(env 基线 + DB settings KV 运行时覆盖) + mgr, err := settings.NewManager(ctx, db, cfg) + if err != nil { + log.Fatalf("[boot] 设置加载失败: %v", err) + } + mgr.SystemStart(ctx) + autoInitIfNeeded(mgr) + + // 5. 审计服务 + 重试循环(需求 ③) + auditSvc := audit.NewService(audit.NewDBSink(db)) + auditStop := make(chan struct{}) + defer close(auditStop) + auditSvc.StartRetryLoop(auditStop) + + // 6. 限流器(规则来自 settings KV,可被管理端动态调整) + limiter := middleware.NewRateLimiter(cacheImpl, map[string]middleware.LimitRule{ + middleware.LimitError: {Count: cfg.GetInt("errorCount"), Window: minutes(cfg.GetInt("errorMinute"))}, + middleware.LimitUpload: {Count: cfg.GetInt("uploadCount"), Window: minutes(cfg.GetInt("uploadMinute"))}, + middleware.LimitLogin: {Count: cfg.GetInt("loginCount"), Window: minutes(cfg.GetInt("loginMinute"))}, + middleware.LimitMeta: {Count: cfg.GetInt("errorCount"), Window: minutes(cfg.GetInt("errorMinute"))}, + }) + + // 7. 存储引擎:注入配置 → 构造 → 健康预检(需求 ④;v3 包装为可热切换 Manager) + storage.SetEngineOptions(buildEngineOptions(cfg)) + bootStore, err := storage.NewEngine(ctx, cfg.Engine()) + if err != nil { + log.Fatalf("[boot] 存储引擎 %s 初始化失败: %v", cfg.Engine(), err) + } + if err := bootStore.HealthCheck(ctx); err != nil { + // 预检失败仅告警不阻断启动(存储可能在运行中恢复) + log.Printf("[boot] 警告: 存储引擎 %s 健康检查未通过: %v", cfg.Engine(), err) + } else { + log.Printf("[boot] 存储引擎 %s 健康检查通过", cfg.Engine()) + } + // v3:Manager 包装——保存/读取委托当前引擎;管理端可热切换 + //(构建闭包在每次切换前用最新 KV 刷新 EngineOptions,参数改动即时生效) + store := storage.NewManager(cfg.Engine(), bootStore, func(name string) (storage.Storage, error) { + storage.SetEngineOptions(buildEngineOptions(cfg)) + return storage.NewEngine(ctx, name) + }) + + // 8. 路由装配 + gin.SetMode(gin.ReleaseMode) + r := gin.New() + r.Use(gin.Logger(), gin.Recovery()) + r.Use(middleware.ClientIP(middleware.ParseTrustedProxies(cfg.Env.TrustedProxies))) + // L6:管理端 CORS 收紧(允许站点对外域名 site_domain 跨域调用,其余拒绝) + r.Use(middleware.Cors(cfg.SiteDomain())) + // 未初始化守卫:除 /setup 与 /api/v1/health 外一律 428 + r.Use(middleware.GuardNotInitialized(mgr.IsInitialized)) + // M3:全局请求体大小上限(管理端 1MiB;上传类 = 单文件上限 + 表单开销) + r.Use(middleware.BodyLimit(bodyLimitFn(cfg))) + // 审计中间件:按 DefaultClassifier 路由模式自动分类 upload/download(需求 ③) + r.Use(middleware.Audit(auditSvc, nil)) + + api.Register(r, &api.Deps{ + DB: db, + Cfg: cfg, + Mgr: mgr, + AuditSvc: auditSvc, + Limiter: limiter, + Store: store, + Version: APP_VERSION, + }) + + // 9. HTTP 服务 + // M5:后台清理循环(过期预留/超时会话/直传残留对象),启动后 10 分钟首跑 + janitor.Start(ctx, db, store, 10*time.Minute) + srv := &http.Server{ + Addr: cfg.Env.Listen, + Handler: r, + ReadHeaderTimeout: 15 * time.Second, + } + go func() { + log.Printf("[boot] 文件快递柜 %s 启动于 %s(存储引擎 %s)", APP_VERSION, cfg.Env.Listen, cfg.Engine()) + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("[boot] HTTP 服务异常退出: %v", err) + } + }() + + <-ctx.Done() + log.Println("[boot] 收到退出信号,开始优雅关闭...") + shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + if err := srv.Shutdown(shutdownCtx); err != nil { + log.Printf("[boot] 优雅关闭超时: %v", err) + } + log.Println("[boot] 服务已退出") +} + +// autoInitIfNeeded 安全审计 L1:系统未初始化且配置了 FCB_ADMIN_PASSWORD 时, +// 启动即自动完成管理员初始化,消除「部署到公网后被抢先访问 /setup 接管」的窗口。 +// 密码不足 8 位时拒绝并保持 /setup 可用(不静默采用弱口令)。 +func autoInitIfNeeded(mgr *settings.Manager) { + if mgr.IsInitialized() { + return + } + pwd := strings.TrimSpace(os.Getenv("FCB_ADMIN_PASSWORD")) + if pwd == "" { + return + } + if len(pwd) < 8 { + log.Printf("[boot] 警告: FCB_ADMIN_PASSWORD 少于 8 位,已忽略;/setup 初始化向导保持可用") + return + } + ctx := context.Background() + if err := mgr.UpdateKV(ctx, map[string]any{ + "admin_token": settings.HashPassword(pwd), + "jwt_secret": settings.GenerateJWTSecret(), + }); err != nil { + log.Printf("[boot] 警告: FCB_ADMIN_PASSWORD 自动初始化写入失败: %v(/setup 仍可用)", err) + return + } + if err := mgr.Reload(ctx); err != nil { + log.Printf("[boot] 警告: 自动初始化后配置重载失败: %v", err) + return + } + log.Printf("[boot] 已通过 FCB_ADMIN_PASSWORD 自动完成管理员初始化") +} + +// envKVSeeds 容器编排常用的 FCB_* 环境变量 → settings KV 键映射。 +// config 包只解析进程必需的 5 个变量;引擎相关配置通过本桥接注入, +// 使 docker-compose 无需进库即可完成引擎配置。优先级:defaults < 环境变量种子 < DB settings KV。 +var envKVSeeds = []struct { + envKey string + kvKey string +}{ + {"FCB_LOCAL_STORAGE_PATH", "local_storage_path"}, + {"FCB_STORAGE_PATH", "storage_path"}, + {"FCB_S3_ACCESS_KEY_ID", "s3_access_key_id"}, + {"FCB_S3_SECRET_ACCESS_KEY", "s3_secret_access_key"}, + {"FCB_AWS_SESSION_TOKEN", "aws_session_token"}, + {"FCB_S3_BUCKET_NAME", "s3_bucket_name"}, + {"FCB_S3_ENDPOINT_URL", "s3_endpoint_url"}, + {"FCB_S3_REGION_NAME", "s3_region_name"}, + {"FCB_S3_ADDRESSING_STYLE", "s3_addressing_style"}, + {"FCB_WEBDAV_URL", "webdav_url"}, + {"FCB_WEBDAV_USERNAME", "webdav_username"}, + {"FCB_WEBDAV_PASSWORD", "webdav_password"}, + {"FCB_WEBDAV_ROOT_PATH", "webdav_root_path"}, +} + +// applyEnvEngineSeeds 把已设置的环境变量作为 KV 种子写入配置(仅种子,不落库)。 +func applyEnvEngineSeeds(cfg *config.Config) { + seeds := map[string]any{} + for _, m := range envKVSeeds { + if v := strings.TrimSpace(os.Getenv(m.envKey)); v != "" { + seeds[m.kvKey] = v + } + } + // L8:管理员会话有效期支持环境变量(秒;1~365 整天校验在 AdminSessionExpireSeconds) + if v := strings.TrimSpace(os.Getenv("FCB_ADMIN_SESSION_EXPIRE")); v != "" { + if n, err := strconv.Atoi(v); err == nil { + seeds["adminSessionExpire"] = n + } + } + if len(seeds) > 0 { + cfg.ApplyKV(seeds) + log.Printf("[boot] 已从环境变量注入 %d 项配置种子", len(seeds)) + } +} + +// buildEngineOptions 从配置构造引擎选项(对齐 go-storage 的注入约定: +// 必须在 NewEngine 之前调用;字段来源为 config KV,键名对齐参考实现)。 +func buildEngineOptions(cfg *config.Config) storage.EngineOptions { + return storage.EngineOptions{ + Local: storage.LocalOptions{ + Root: cfg.GetString("local_storage_path"), + }, + S3: storage.S3Options{ + AccessKeyID: cfg.GetString("s3_access_key_id"), + SecretAccessKey: cfg.GetString("s3_secret_access_key"), + SessionToken: cfg.GetString("aws_session_token"), + Bucket: cfg.GetString("s3_bucket_name"), + Endpoint: cfg.GetString("s3_endpoint_url"), + Region: cfg.GetString("s3_region_name"), + AddressingStyle: cfg.GetString("s3_addressing_style"), + }, + WebDAV: storage.WebDAVOptions{ + BaseURL: cfg.GetString("webdav_url"), + Username: cfg.GetString("webdav_username"), + Password: cfg.GetString("webdav_password"), + RootPath: cfg.GetString("webdav_root_path"), + }, + } +} + +// minutes 分钟数转 Duration。 +func minutes(n int) time.Duration { + if n <= 0 { + n = 1 + } + return time.Duration(n) * time.Minute +} + +// bodyLimitFn 构造按路径分类的请求体上限函数(M3): +// - /admin/*:1MiB(管理接口均为小 JSON/表单); +// - /share/text|metadata|select、/setup:1MiB(文本内容本身限 222KB); +// - 其余(上传类):max_file_size(0=回落 uploadSize,再 0=64MiB)+ 2MiB 表单开销。 +func bodyLimitFn(cfg *config.Config) func(c *gin.Context) int64 { + const adminLimit = int64(1) << 20 + const textLimit = int64(1) << 20 + const formOverhead = int64(2) << 20 + const fallbackUpload = int64(64) << 20 + return func(c *gin.Context) int64 { + p := c.Request.URL.Path + switch { + case p == "/setup" || strings.HasPrefix(p, "/admin/"): + return adminLimit + case p == "/share/text" || p == "/share/metadata" || p == "/share/select": + return textLimit + } + limit := cfg.MaxFileSize() + if limit <= 0 { + limit = cfg.UploadSize() + } + if limit <= 0 { + limit = fallbackUpload + } + // 分片/直传单请求体 ≤ 单片大小;share/file 的 multipart 有边界开销 + return limit + formOverhead + } +} diff --git a/server/go.mod b/server/go.mod new file mode 100644 index 0000000..f82463f --- /dev/null +++ b/server/go.mod @@ -0,0 +1,79 @@ +module filecodebox + +go 1.27.1 + +require ( + github.com/aws/aws-sdk-go-v2 v1.45.1 + github.com/aws/aws-sdk-go-v2/config v1.33.2 + github.com/aws/aws-sdk-go-v2/credentials v1.20.2 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.23.2 + github.com/aws/aws-sdk-go-v2/service/s3 v1.110.0 + github.com/aws/smithy-go v1.28.1 + github.com/gin-gonic/gin v1.11.0 + github.com/glebarez/sqlite v1.11.0 + github.com/golang-jwt/jwt/v5 v5.3.0 + github.com/redis/go-redis/v9 v9.17.0 + gorm.io/driver/postgres v1.6.2 + gorm.io/gorm v1.31.2 +) + +require ( + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.20 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.19.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.19 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.11.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.20.1 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.8.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.36.0 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.41.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.48.0 // indirect + github.com/bytedance/sonic v1.14.0 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/glebarez/go-sqlite v1.21.2 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.27.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/goccy/go-yaml v1.18.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.10.0 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.59.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.0 // indirect + golang.org/x/arch v0.20.0 // indirect + golang.org/x/crypto v0.56.0 // indirect + golang.org/x/net v0.58.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.41.0 // indirect + google.golang.org/protobuf v1.36.9 // indirect + modernc.org/libc v1.55.3 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/sqlite v1.34.5 // indirect +) diff --git a/server/go.sum b/server/go.sum new file mode 100644 index 0000000..47a3040 --- /dev/null +++ b/server/go.sum @@ -0,0 +1,197 @@ +github.com/aws/aws-sdk-go-v2 v1.45.1 h1:iIoG3NaLhV6UZpPXyPXlDj2I9oS8tV/nMcMnITCC6Ks= +github.com/aws/aws-sdk-go-v2 v1.45.1/go.mod h1:bttEH6JqnUL8LepvDVfdrds/fZ5bCIxzpe3abyUrhDU= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.20 h1:GPRlPwz40I2B2VrBEASOA3Bi77NyeqejNLkifosX0rs= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.20/go.mod h1:g7PNzKcsOKWb4fkSRBA7BZVAS6Y8IcxzN+nRohhQ1Q8= +github.com/aws/aws-sdk-go-v2/config v1.33.2 h1:Pj4+nF2kc4Z+1BJysVPnX9d5dMN7IYFXR4UJaWK2IpA= +github.com/aws/aws-sdk-go-v2/config v1.33.2/go.mod h1:Igw+HTwbR2tsTU/ydifAS9EHAFJ2s/FCgkwQWFnAdE4= +github.com/aws/aws-sdk-go-v2/credentials v1.20.2 h1:VQjZODPNfdikCX2ZZrltw4zNLkcwjyUFDUl2vT9yTwg= +github.com/aws/aws-sdk-go-v2/credentials v1.20.2/go.mod h1:OmeHCn28vZylsBvalLDf7t8fuJ2rHYQprJs+7WuxniI= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.19.1 h1:YIEBqcqRnpi4Pfv0YHImtgi6czGCwKHANC7SwmUAVD0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.19.1/go.mod h1:imEf0oufgAo8KAkCHhrOdqGEC0YWx1PPBQH82shSxGw= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.23.2 h1:yNAPkIRXwrXV3x4NMXi2oAveMy5WUaiBAY6X42K+vUs= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.23.2/go.mod h1:+/m7PPNzeC3wq8n5kgw39kAj7pIE3fkAKHrgCyVnMO0= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.1 h1:pc138gM1CW+XPc60rEwUlwwuwWFQK16CI1T7v1F9Oec= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.1/go.mod h1:1+koxpPIbfBdfzP6vojm5/zTpTQ/micYwlxIiNB3TxI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.1 h1:K0JsbZQj+1h208Ro1zHeA4l7bMp0NvRffHQ91q8Ol1s= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.1/go.mod h1:W3/vL6EtCIatICGy9ab29QhMuae+cOKPWcMxv02CO+Q= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.1 h1:yhw5KD1phVyP9vijxOUzDfEtJx+bt+L63k+VfuiYFAA= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.1/go.mod h1:ZW2e0d7DYlRxlS9hEiMXE47gTdX5KRN4byUiNbUpG+Q= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.19 h1:bAdDl/HkGCcGPoe25ToSHEw23VIxt6CT5fLcg111BKg= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.19/go.mod h1:KaUzbLxv4CeSxh6ZCl9B4m7CuFenS8kUEaDs+f/DQr4= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.11.1 h1:s67hBfG5t9rn1NCvDuB4E3QIep3UFhHPtaIqFDjV3N8= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.11.1/go.mod h1:FpvjBMXtSNMLPmDJsWwcY5cRnqJlpS2y1R6n4pvzs4k= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.1 h1:RmmWQPREQdk9U+PfqeHW3MqZaBaNK7TpV9W3RY+b+7g= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.1/go.mod h1:0A3W4F+68ZnNk5XcNL/e9HFMwnP8RlEicFfy6eOEDyw= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.20.1 h1:ZMbtPZZQRca+3+XYQne9PBvRiYpHZlNJJOZfE9WNfT0= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.20.1/go.mod h1:YAGWQdCYlVCoqrzvfv3RLxO6zKwti7gsAULOGWPLYv4= +github.com/aws/aws-sdk-go-v2/service/s3 v1.110.0 h1:He8vaTTqAAJrux/KdpjFXNWueLJZyKqE49QEXoqAu4I= +github.com/aws/aws-sdk-go-v2/service/s3 v1.110.0/go.mod h1:CUr46sCpGAg/rHaclRyhJX0LJAmH73uWSJPPSaMUrSk= +github.com/aws/aws-sdk-go-v2/service/signin v1.8.0 h1:bSvKIoLuRGFqGwASgeCQncCJDi9YKKBDEmCEZzOX1uU= +github.com/aws/aws-sdk-go-v2/service/signin v1.8.0/go.mod h1:9IqUlsJDbUPcg6cgx3WEzXdjrbWzLDQrak0aaSqlTcI= +github.com/aws/aws-sdk-go-v2/service/sso v1.36.0 h1:iivsh357VnfIc18IFWSuoyQEluf8frfWf4cL2Y0JUQw= +github.com/aws/aws-sdk-go-v2/service/sso v1.36.0/go.mod h1:tWuiVBUtPBr8/rgRiYS8Uf85sHcAN+G7XS3D3CEoUh8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.41.0 h1:wVxM3QzSKIK8tSN6OGgezp9OK91lCLH2zhmRInN9rFM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.41.0/go.mod h1:naFe83jSMuYkH+QjQPX8n1MLhBkeCFM5Lsnh5m5wz3c= +github.com/aws/aws-sdk-go-v2/service/sts v1.48.0 h1:RzZVCzYM19vhJCT5s6vO2wN8ie770Li/TmbAZ9B6N7E= +github.com/aws/aws-sdk-go-v2/service/sts v1.48.0/go.mod h1:mKo/CzaCz8qytGW70NG4vIIGAx1HXTlb5lHNkC5k3lk= +github.com/aws/smithy-go v1.28.1 h1:R/nXH00c8qcfCzQVELtRw+eLQWtzv+VAIEFJ1/xxXlQ= +github.com/aws/smithy-go v1.28.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= +github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= +github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= +github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= +github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= +github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= +github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= +github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= +github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= +github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0= +github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.59.1 h1:0Gmua0HW1Tv7ANR7hUYwRyD0MG5OJfgvYSZasGZzBic= +github.com/quic-go/quic-go v0.59.1/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/redis/go-redis/v9 v9.17.0 h1:K6E+ZlYN95KSMmZeEQPbU/c++wfmEvfFB17yEAq/VhM= +github.com/redis/go-redis/v9 v9.17.0/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= +github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= +golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= +golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= +golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= +golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.6.2 h1:BvXQ/cNUg63q5TFNg672DmDcowZSFrNLkkA3Xe6GXq4= +gorm.io/driver/postgres v1.6.2/go.mod h1:0c4fQA44XhOklXDkgtuKqysHCycTa5i9e3EIpDGCwXk= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= +gorm.io/gorm v1.31.2 h1:3o8FXNo9v9S858gil+3LlZA1LkCOzgb4g5BL64FgaCo= +gorm.io/gorm v1.31.2/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= +modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= +modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= +modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= +modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= +modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g= +modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/server/internal/api/admin.go b/server/internal/api/admin.go new file mode 100644 index 0000000..c428ae2 --- /dev/null +++ b/server/internal/api/admin.go @@ -0,0 +1,1523 @@ +package api + +import ( + "errors" + "fmt" + "log" + "net/http" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + + "filecodebox/internal/config" + "filecodebox/internal/middleware" + "filecodebox/internal/model" + "filecodebox/internal/response" + "filecodebox/internal/settings" + "filecodebox/internal/storage" +) + +// minutesDuration 分钟数转 Duration(0 回退 1 分钟,对齐 main.go 语义)。 +func minutesDuration(n int) time.Duration { + if n <= 0 { + n = 1 + } + return time.Duration(n) * time.Minute +} + +// syncRateRules 配置变更后同步限流规则(settings KV 驱动,运行时生效)。 +func (d *Deps) syncRateRules() { + cfg := d.Cfg + d.Limiter.SetRule(middleware.LimitError, middleware.LimitRule{ + Count: cfg.GetInt("errorCount"), Window: minutesDuration(cfg.GetInt("errorMinute"))}) + d.Limiter.SetRule(middleware.LimitUpload, middleware.LimitRule{ + Count: cfg.GetInt("uploadCount"), Window: minutesDuration(cfg.GetInt("uploadMinute"))}) + d.Limiter.SetRule(middleware.LimitLogin, middleware.LimitRule{ + Count: cfg.GetInt("loginCount"), Window: minutesDuration(cfg.GetInt("loginMinute"))}) + d.Limiter.SetRule(middleware.LimitMeta, middleware.LimitRule{ + Count: cfg.GetInt("errorCount"), Window: minutesDuration(cfg.GetInt("errorMinute"))}) +} + +// registerAdmin 注册管理端路由:login 公开,其余需管理员 JWT。 +func registerAdmin(r *gin.Engine, d *Deps) { + admin := r.Group("/admin") + admin.POST("/login", d.adminLogin) + + authed := admin.Group("", middleware.AdminAuth(d.Mgr.SecretProvider())) + { + authed.GET("/verify", d.adminVerify) + authed.POST("/logout", d.adminLogout) + authed.GET("/dashboard", d.adminDashboard) + + // 文件管理 + authed.GET("/file/list", d.adminFileList) + authed.GET("/file/detail", d.adminFileDetail) + authed.POST("/file/detail", d.adminFileDetail) + authed.PATCH("/file/update", d.adminFileUpdate) + authed.POST("/file/update", d.adminFileUpdate) + authed.DELETE("/file/delete", d.adminFileDelete) + authed.POST("/file/delete", d.adminFileDelete) + authed.DELETE("/file/batch-delete", d.adminFileBatchDelete) + authed.POST("/file/batch-delete", d.adminFileBatchDelete) + authed.PATCH("/file/batch-update", d.adminFileBatchUpdate) + authed.POST("/file/batch-update", d.adminFileBatchUpdate) + authed.PATCH("/file/policy-action", d.adminFilePolicyAction) + authed.POST("/file/policy-action", d.adminFilePolicyAction) + authed.PATCH("/file/batch-policy-action", d.adminFileBatchPolicyAction) + authed.POST("/file/batch-policy-action", d.adminFileBatchPolicyAction) + authed.GET("/file/download", d.adminFileDownload) + authed.GET("/file/preview", d.adminFilePreview) + + // 配置与安全 + authed.GET("/config/get", d.adminConfigGet) + authed.PATCH("/config/update", d.adminConfigUpdate) + authed.POST("/config/update", d.adminConfigUpdate) + authed.PATCH("/settings/password", d.adminChangePassword) + authed.POST("/settings/password", d.adminChangePassword) + + // v3 存储引擎:运行时热切换(健康检查通过才生效,失败保持原引擎) + authed.POST("/storage/switch", d.adminStorageSwitch) + + // 审计日志查询(需求 ③;logs 为 list 的别名) + authed.GET("/audit/list", d.adminAuditList) + authed.GET("/audit/logs", d.adminAuditList) + } +} + +// ============ 认证 ============ + +// adminLogin 管理员登录(对齐参考 login):失败计入 login 限流。 +func (d *Deps) adminLogin(c *gin.Context) { + // 进入即检查登录限流(对齐参考 Depends(ip_limit["login"])) + if allowed, _ := d.Limiter.Check(c, middleware.LimitLogin); !allowed { + response.Fail(c, http.StatusLocked, "请求次数过多,请稍后再试") + return + } + var body struct { + Password string `json:"password" form:"password"` + } + if err := bindJSONOrForm(c, &body); err != nil { + respondError(c, err) + return + } + stored := d.Cfg.GetString("admin_token") + if stored == "" || !settings.VerifyPassword(body.Password, stored) { + d.Limiter.Add(c, middleware.LimitLogin) // 登录失败计数 + response.Fail(c, http.StatusUnauthorized, "密码错误") + return + } + // M1 透明迁移:旧格式(sha256/明文)或低 cost 哈希在登录成功后升级为 bcrypt + if settings.NeedsRehash(stored) { + if err := d.Mgr.UpdateKV(c.Request.Context(), map[string]any{ + "admin_token": settings.HashPassword(body.Password), + }); err != nil { + log.Printf("[auth] 密码哈希升级失败(不影响本次登录): %v", err) + } else if err := d.Mgr.Reload(c.Request.Context()); err != nil { + log.Printf("[auth] 密码哈希升级后重载失败: %v", err) + } else { + log.Printf("[auth] 管理员密码哈希已升级为 bcrypt(旧格式兼容校验通过后自动迁移)") + } + } + expiresIn := d.Cfg.AdminSessionExpireSeconds() + token, expiresAt, err := middleware.SignAdminToken(d.jwtSecret(), time.Duration(expiresIn)*time.Second) + if err != nil { + respondError(c, errInternal("签发会话失败: "+err.Error())) + return + } + response.OK(c, gin.H{ + "id": "admin", + "username": "admin", + "token": token, + "token_type": "Bearer", + "expires_at": expiresAt.Unix(), + "expires_in": expiresIn, + }) +} + +// adminVerify 会话校验(对齐参考 verify_admin)。 +func (d *Deps) adminVerify(c *gin.Context) { + header := c.GetHeader("Authorization") + token := strings.TrimPrefix(header, "Bearer ") + expiresAt := int64(0) + if claims, err := middleware.VerifyAdminToken(d.jwtSecret(), token); err == nil && claims.ExpiresAt != nil { + expiresAt = claims.ExpiresAt.Unix() + } + response.OK(c, gin.H{ + "id": "admin", + "username": "admin", + "token": token, + "token_type": "Bearer", + "expires_at": expiresAt, + }) +} + +// adminLogout 登出(无状态 JWT,客户端丢弃 token 即可)。 +func (d *Deps) adminLogout(c *gin.Context) { + response.OK(c, gin.H{"ok": true}) +} + +// ============ 仪表盘 ============ + +// adminDashboard 管理端统计(对齐参考 dashboard 字段语义)。 +func (d *Deps) adminDashboard(c *gin.Context) { + ctx := c.Request.Context() + db := d.DB.WithContext(ctx) + var all []model.FileCodes + if err := db.Find(&all).Error; err != nil { + respondError(c, errInternal("查询统计失败: "+err.Error())) + return + } + now := time.Now() + todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) + yesterdayStart := todayStart.AddDate(0, 0, -1) + yesterdayEnd := todayStart.Add(-time.Microsecond) + + var ( + totalSize, usedCount int64 + todayCount, todaySize int64 + yesterdayCount, yesterdaySize int64 + expiredCount, textCount, chunkedCount int64 + ) + suffixCounter := map[string]int64{} + recent := make([]model.FileCodes, 0) + for _, fc := range all { + totalSize += fc.Size + usedCount += int64(fc.UsedCount) + if fc.CreatedAt.After(todayStart) { + todayCount++ + todaySize += fc.Size + } + if fc.CreatedAt.After(yesterdayStart) && (fc.CreatedAt.Before(yesterdayEnd) || fc.CreatedAt.Equal(yesterdayEnd)) { + yesterdayCount++ + yesterdaySize += fc.Size + } + if fc.Expired(now) { + expiredCount++ + } + if fc.Text != nil { + textCount++ + } else { + chunkedCount += boolToInt64(fc.IsChunked) + } + name := "Text" + if fc.Text == nil { + name = firstNonEmpty(fc.Suffix, "file") + } + suffixCounter[name]++ + recent = append(recent, fc) + } + fileCount := int64(len(all)) - textCount + + // 最近 8 条 + sortRecentDesc(recent) + if len(recent) > 8 { + recent = recent[:8] + } + recentFiles := make([]gin.H, 0, len(recent)) + for _, fc := range recent { + recentFiles = append(recentFiles, buildAdminFileItem(&fc, now)) + } + // 后缀 Top8 + type suffixCount struct { + Suffix string `json:"suffix"` + Count int64 `json:"count"` + } + top := make([]suffixCount, 0, 8) + for s, n := range suffixCounter { + top = append(top, suffixCount{s, n}) + } + for i := 0; i < len(top); i++ { + for j := i + 1; j < len(top); j++ { + if top[j].Count > top[i].Count { + top[i], top[j] = top[j], top[i] + } + } + } + if len(top) > 8 { + top = top[:8] + } + + // sys_start(启动时间戳毫秒) + var sysStartRow model.KeyValue + var sysUptime any + if err := db.Where("key = ?", "sys_start").First(&sysStartRow).Error; err == nil && sysStartRow.Value != nil { + var ms int64 + if err := parseJSONNumber(*sysStartRow.Value, &ms); err == nil { + sysUptime = ms + } + } + + response.OK(c, gin.H{ + "totalFiles": len(all), + "storageUsed": strconv.FormatInt(totalSize, 10), + "sysUptime": sysUptime, + "yesterdayCount": yesterdayCount, + "yesterdaySize": strconv.FormatInt(yesterdaySize, 10), + "todayCount": todayCount, + "todaySize": strconv.FormatInt(todaySize, 10), + "activeCount": int64(len(all)) - expiredCount, + "expiredCount": expiredCount, + "textCount": textCount, + "fileCount": fileCount, + "chunkedCount": chunkedCount, + "usedCount": usedCount, + "storageBackend": d.Cfg.Engine(), + "uploadSizeLimit": d.Cfg.UploadSize(), + "openUpload": boolToInt(d.Cfg.OpenUpload()), + "enableChunk": boolToInt(d.Cfg.EnableChunk()), + "maxSaveSeconds": d.Cfg.MaxSaveSeconds(), + "topSuffixes": top, + "recentFiles": recentFiles, + "recentActivities": []gin.H{}, + }) +} + +// ============ 文件管理 ============ + +// adminFileList 分页查询分享列表(对齐参考 file_list:过滤/排序/统计)。 +func (d *Deps) adminFileList(c *gin.Context) { + page := maxInt(queryInt(c, "page", 1), 1) + size := clampInt(queryInt(c, "size", 10), 1, 100) + keyword := strings.ToLower(strings.TrimSpace(c.Query("keyword"))) + status := strings.ToLower(strings.TrimSpace(c.Query("status"))) + fileType := strings.ToLower(strings.TrimSpace(c.Query("type"))) + sortBy := normalizeSortBy(c.Query("sortBy")) + sortOrder := strings.ToLower(c.Query("sortOrder")) + desc := sortOrder != "asc" + + ctx := c.Request.Context() + db := d.DB.WithContext(ctx) + q := db.Model(&model.FileCodes{}) + if keyword != "" { + // Info5:转义 LIKE 通配符(%/_/\),避免用户输入被当作通配模式 + escaped := escapeLike(keyword) + like := "%" + escaped + "%" + q = q.Where( + "LOWER(code) LIKE ? ESCAPE '\\' OR LOWER(prefix) LIKE ? ESCAPE '\\' OR LOWER(suffix) LIKE ? ESCAPE '\\' OR LOWER(COALESCE(file_hash,'')) LIKE ? ESCAPE '\\' OR LOWER(COALESCE(text,'')) LIKE ? ESCAPE '\\'", + like, like, like, like, like) + } + switch fileType { + case "text": + q = q.Where("text IS NOT NULL") + case "file": + q = q.Where("text IS NULL") + case "chunked": + q = q.Where("is_chunked = ?", true) + } + + // status 过滤在内存中判定(过期语义含 NULL 分支) + var all []model.FileCodes + if err := q.Find(&all).Error; err != nil { + respondError(c, errInternal("查询文件列表失败: "+err.Error())) + return + } + now := time.Now() + filtered := make([]model.FileCodes, 0, len(all)) + summary := gin.H{ + "totalFiles": len(all), + "activeCount": 0, + "expiredCount": 0, + "textCount": 0, + "fileCount": 0, + "chunkedCount": 0, + "storageUsed": int64(0), + "usedCount": int64(0), + } + for i := range all { + fc := &all[i] + isExpired := fc.Expired(now) + summary["storageUsed"] = summary["storageUsed"].(int64) + fc.Size + summary["usedCount"] = summary["usedCount"].(int64) + int64(fc.UsedCount) + if isExpired { + summary["expiredCount"] = summary["expiredCount"].(int) + 1 + } else { + summary["activeCount"] = summary["activeCount"].(int) + 1 + } + if fc.Text != nil { + summary["textCount"] = summary["textCount"].(int) + 1 + } else { + summary["fileCount"] = summary["fileCount"].(int) + 1 + if fc.IsChunked { + summary["chunkedCount"] = summary["chunkedCount"].(int) + 1 + } + } + switch status { + case "active": + if isExpired { + continue + } + case "expired": + if !isExpired { + continue + } + } + filtered = append(filtered, *fc) + } + + // 排序(白名单字段,防注入) + sortFileCodes(filtered, sortBy, desc) + total := len(filtered) + offset := (page - 1) * size + end := offset + size + if offset > total { + offset = total + } + if end > total { + end = total + } + items := make([]gin.H, 0, end-offset) + for i := offset; i < end; i++ { + items = append(items, buildAdminFileItem(&filtered[i], now)) + } + response.OK(c, gin.H{ + "page": page, "size": size, "data": items, "total": total, "summary": summary, + }) +} + +// buildAdminFileItem 构造管理端文件条目(snake_case 与 camelCase 双份,前端宽松解析)。 +func buildAdminFileItem(fc *model.FileCodes, now time.Time) gin.H { + isText := fc.Text != nil + isExpired := fc.Expired(now) + name := fc.Prefix + fc.Suffix + var remaining any + if fc.ExpiredCount >= 0 { + remaining = maxInt(fc.ExpiredCount, 0) + } + var expiredAt any + if fc.ExpiredAt != nil { + expiredAt = fc.ExpiredAt.Format(time.RFC3339) + } + item := gin.H{ + "id": fc.ID, "code": fc.Code, "name": name, + "prefix": fc.Prefix, "suffix": fc.Suffix, + "size": fc.Size, + "isText": isText, "is_text": isText, + "isChunked": fc.IsChunked, "is_chunked": fc.IsChunked, + "isExpired": isExpired, "is_expired": isExpired, + "expiredAt": expiredAt, "expired_at": expiredAt, + "expiredCount": fc.ExpiredCount, "expired_count": fc.ExpiredCount, + "usedCount": fc.UsedCount, "used_count": fc.UsedCount, + "createdAt": fc.CreatedAt.Format(time.RFC3339), "created_at": fc.CreatedAt.Format(time.RFC3339), + "hasDownloadLimit": fc.ExpiredCount >= 0, "has_download_limit": fc.ExpiredCount >= 0, + "isPermanent": fc.ExpiredAt == nil && fc.ExpiredCount < 0, "is_permanent": fc.ExpiredAt == nil && fc.ExpiredCount < 0, + "remainingDownloads": remaining, "remaining_downloads": remaining, + "engine": fc.Engine, // v3:归属引擎(管理端展示/排查用) + } + if fc.FileHash != nil { + item["fileHash"] = *fc.FileHash + item["file_hash"] = *fc.FileHash + } else { + item["fileHash"] = nil + item["file_hash"] = nil + } + if isText { + item["text"] = true + } else { + item["text"] = false + } + return item +} + +// adminFileDetail 文件详情(GET ?id= 或 POST {id})。 +func (d *Deps) adminFileDetail(c *gin.Context) { + id, err := requestID(c) + if err != nil { + respondError(c, err) + return + } + fc, err := d.fileByID(c, id) + if err != nil { + respondError(c, err) + return + } + item := buildAdminFileItem(fc, time.Now()) + if fc.Text != nil { + item["content"] = *fc.Text + } + response.OK(c, item) +} + +// adminFileUpdate 更新分享字段(对齐参考 update_file:code 冲突 400)。 +func (d *Deps) adminFileUpdate(c *gin.Context) { + var body struct { + ID int64 `json:"id"` + Code *string `json:"code"` + Prefix *string `json:"prefix"` + Suffix *string `json:"suffix"` + ExpiredAt *string `json:"expired_at"` + ExpiredCount *int `json:"expired_count"` + } + if err := bindJSONOrForm(c, &body); err != nil { + respondError(c, err) + return + } + if body.ID <= 0 { + response.Fail(c, http.StatusBadRequest, "请选择要更新的文件") + return + } + ctx := c.Request.Context() + fc, err := d.fileByID(c, body.ID) + if err != nil { + respondError(c, err) + return + } + updates := map[string]any{} + if body.Code != nil && *body.Code != fc.Code { + var cnt int64 + if err := d.DB.WithContext(ctx).Model(&model.FileCodes{}). + Where("code = ? AND id <> ?", *body.Code, fc.ID).Count(&cnt).Error; err != nil { + respondError(c, errInternal("查询取件码失败: "+err.Error())) + return + } + if cnt > 0 { + response.Fail(c, http.StatusBadRequest, "code已存在") + return + } + updates["code"] = *body.Code + } + if body.Prefix != nil && *body.Prefix != fc.Prefix { + updates["prefix"] = *body.Prefix + } + if body.Suffix != nil && *body.Suffix != fc.Suffix { + updates["suffix"] = *body.Suffix + } + if body.ExpiredAt != nil && *body.ExpiredAt != "" { + t, err := parseISOTime(*body.ExpiredAt) + if err != nil { + response.Fail(c, http.StatusBadRequest, "expired_at 时间格式错误") + return + } + updates["expired_at"] = t + } + if body.ExpiredCount != nil && *body.ExpiredCount != fc.ExpiredCount { + updates["expired_count"] = *body.ExpiredCount + } + if len(updates) > 0 { + if err := d.DB.WithContext(ctx).Model(&model.FileCodes{}). + Where("id = ?", fc.ID).Updates(updates).Error; err != nil { + respondError(c, errInternal("更新失败: "+err.Error())) + return + } + } + response.OK(c, "更新成功") +} + +// adminFileDelete 删除单个分享(连带删除存储文件,DELETE {id} 或 POST {id})。 +func (d *Deps) adminFileDelete(c *gin.Context) { + var body struct { + ID int64 `json:"id"` + } + if err := bindJSONOrForm(c, &body); err != nil { + respondError(c, err) + return + } + if body.ID <= 0 { + if v := c.Query("id"); v != "" { + body.ID, _ = strconv.ParseInt(v, 10, 64) + } + } + if body.ID <= 0 { + response.Fail(c, http.StatusBadRequest, "请选择要删除的文件") + return + } + fc, err := d.fileByID(c, body.ID) + if err != nil { + respondError(c, err) + return + } + if err := d.deleteFileCode(c, fc); err != nil { + respondError(c, err) + return + } + response.OK(c, nil) +} + +// adminFileBatchDelete 批量删除(对齐参考 delete_files 的统计响应)。 +func (d *Deps) adminFileBatchDelete(c *gin.Context) { + var body struct { + IDs []int64 `json:"ids"` + } + if err := bindJSONOrForm(c, &body); err != nil { + respondError(c, err) + return + } + if len(body.IDs) == 0 { + response.Fail(c, http.StatusBadRequest, "请选择要删除的文件") + return + } + deleted, missing, failed := d.deleteMany(c, body.IDs) + response.OK(c, gin.H{ + "requestedCount": len(body.IDs), "requested_count": len(body.IDs), + "deletedCount": len(deleted), "deleted_count": len(deleted), + "missingCount": len(missing), "missing_count": len(missing), + "failedCount": len(failed), "failed_count": len(failed), + "deleted": deleted, "missing": missing, "failed": failed, + }) +} + +// adminFileBatchUpdate 批量更新(对齐参考 batch_update_files)。 +func (d *Deps) adminFileBatchUpdate(c *gin.Context) { + var body struct { + IDs []int64 `json:"ids"` + ExpiredAt *string `json:"expired_at"` + ExpiredCount *int `json:"expired_count"` + ClearExpiredAt bool `json:"clearExpiredAt"` + ClearExpiredAlt bool `json:"clear_expired_at"` + } + if err := bindJSONOrForm(c, &body); err != nil { + respondError(c, err) + return + } + if len(body.IDs) == 0 { + response.Fail(c, http.StatusBadRequest, "请选择要更新的文件") + return + } + updates := map[string]any{} + shouldClear := body.ClearExpiredAt || body.ClearExpiredAlt + switch { + case shouldClear: + updates["expired_at"] = nil + updates["expired_count"] = -1 + case body.ExpiredAt != nil && *body.ExpiredAt != "": + t, err := parseISOTime(*body.ExpiredAt) + if err != nil { + response.Fail(c, http.StatusBadRequest, "expired_at 时间格式错误") + return + } + updates["expired_at"] = t + } + if !shouldClear && body.ExpiredCount != nil { + updates["expired_count"] = *body.ExpiredCount + } + if len(updates) == 0 { + response.Fail(c, http.StatusBadRequest, "请选择要更新的字段") + return + } + ctx := c.Request.Context() + updated, missing, failed := 0, []int64{}, []gin.H{} + for _, id := range body.IDs { + res := d.DB.WithContext(ctx).Model(&model.FileCodes{}). + Where("id = ?", id).Updates(updates) + switch { + case res.Error != nil: + failed = append(failed, gin.H{"id": id, "reason": res.Error.Error()}) + case res.RowsAffected == 0: + missing = append(missing, id) + default: + updated++ + } + } + response.OK(c, gin.H{ + "requestedCount": len(body.IDs), "requested_count": len(body.IDs), + "updatedCount": updated, "updated_count": updated, + "missingCount": len(missing), "missing_count": len(missing), + "failedCount": len(failed), "failed_count": len(failed), + "updated": updated, "missing": missing, "failed": failed, + }) +} + +// adminFilePolicyAction 单文件策略动作(对齐参考 apply_file_policy_action)。 +func (d *Deps) adminFilePolicyAction(c *gin.Context) { + var body struct { + ID int64 `json:"id"` + Action string `json:"action"` + DownloadLimit *int `json:"downloadLimit"` + } + if err := bindJSONOrForm(c, &body); err != nil { + respondError(c, err) + return + } + fc, err := d.fileByID(c, body.ID) + if err != nil { + respondError(c, err) + return + } + updates, err := buildPolicyUpdate(fc, body.Action, body.DownloadLimit) + if err != nil { + respondError(c, err) + return + } + if err := d.DB.WithContext(c.Request.Context()).Model(&model.FileCodes{}). + Where("id = ?", fc.ID).Updates(updates).Error; err != nil { + respondError(c, errInternal("策略执行失败: "+err.Error())) + return + } + response.OK(c, gin.H{"id": fc.ID, "action": body.Action}) +} + +// adminFileBatchPolicyAction 批量策略动作。 +func (d *Deps) adminFileBatchPolicyAction(c *gin.Context) { + var body struct { + IDs []int64 `json:"ids"` + Action string `json:"action"` + DownloadLimit *int `json:"downloadLimit"` + } + if err := bindJSONOrForm(c, &body); err != nil { + respondError(c, err) + return + } + if len(body.IDs) == 0 { + response.Fail(c, http.StatusBadRequest, "请选择要更新的文件") + return + } + ctx := c.Request.Context() + updated, missing, failed := 0, []int64{}, []gin.H{} + for _, id := range body.IDs { + fc, err := d.fileByID(c, id) + if err != nil { + if fc == nil { + missing = append(missing, id) + } else { + failed = append(failed, gin.H{"id": id, "reason": err.Error()}) + } + continue + } + updates, err := buildPolicyUpdate(fc, body.Action, body.DownloadLimit) + if err != nil { + failed = append(failed, gin.H{"id": id, "reason": err.Error()}) + continue + } + if err := d.DB.WithContext(ctx).Model(&model.FileCodes{}). + Where("id = ?", id).Updates(updates).Error; err != nil { + failed = append(failed, gin.H{"id": id, "reason": err.Error()}) + continue + } + updated++ + } + response.OK(c, gin.H{ + "requestedCount": len(body.IDs), "requested_count": len(body.IDs), + "updatedCount": updated, "updated_count": updated, + "missingCount": len(missing), "missing_count": len(missing), + "failedCount": len(failed), "failed_count": len(failed), + "updated": updated, "missing": missing, "failed": failed, + }) +} + +// buildPolicyUpdate 构造策略动作更新字段(对齐参考 _build_policy_action_update)。 +func buildPolicyUpdate(fc *model.FileCodes, action string, downloadLimit *int) (map[string]any, error) { + action = strings.ToLower(strings.TrimSpace(action)) + now := time.Now() + switch action { + case "extend_24h": + return map[string]any{"expired_at": extendExpiration(fc, now, 24*time.Hour)}, nil + case "extend_7d": + return map[string]any{"expired_at": extendExpiration(fc, now, 7*24*time.Hour)}, nil + case "make_permanent": + return map[string]any{"expired_at": nil, "expired_count": -1}, nil + case "reset_download_limit": + limit := 5 + if downloadLimit != nil { + limit = *downloadLimit + } + if limit < 1 { + return nil, errBadRequest("取件次数必须大于 0") + } + return map[string]any{"expired_count": limit}, nil + } + return nil, errBadRequest("不支持的策略动作") +} + +// extendExpiration 在现有过期时间(未过期时)或当前时间基础上延长。 +func extendExpiration(fc *model.FileCodes, now time.Time, d time.Duration) time.Time { + base := now + if fc.ExpiredAt != nil && fc.ExpiredAt.After(now) { + base = *fc.ExpiredAt + } + return base.Add(d) +} + +// adminFileDownload 管理员下载原文件(不消耗次数;文本返回 JSON)。 +func (d *Deps) adminFileDownload(c *gin.Context) { + id, err := requestID(c) + if err != nil { + respondError(c, err) + return + } + fc, err := d.fileByID(c, id) + if err != nil { + respondError(c, err) + return + } + if fc.Text != nil { + response.OK(c, *fc.Text) + return + } + if fc.FilePath == nil || fc.UUIDFileName == nil { + response.Fail(c, http.StatusNotFound, "文件不存在") + return + } + d.serveFile(c, fc) +} + +// adminFilePreview 文本预览(对齐参考 preview_file:仅文本分享)。 +func (d *Deps) adminFilePreview(c *gin.Context) { + id, err := requestID(c) + if err != nil { + respondError(c, err) + return + } + maxChars := clampInt(queryInt(c, "maxChars", 4000), 1, 20000) + fc, err := d.fileByID(c, id) + if err != nil { + respondError(c, err) + return + } + if fc.Text == nil { + response.Fail(c, http.StatusBadRequest, "仅文本分享支持预览") + return + } + content := []rune(*fc.Text) + truncated := len(content) > maxChars + preview := string(content[:minInt(len(content), maxChars)]) + response.OK(c, gin.H{ + "id": fc.ID, "code": fc.Code, "name": fc.Prefix + fc.Suffix, + "type": "text", "content": preview, "length": len(content), + "previewLength": len([]rune(preview)), "preview_length": len([]rune(preview)), + "truncated": truncated, "maxChars": maxChars, "max_chars": maxChars, + "createdAt": fc.CreatedAt.Format(time.RFC3339), "created_at": fc.CreatedAt.Format(time.RFC3339), + }) +} + +// ============ 配置 ============ + +// configKeys 管理端可见/可改的配置键(不含 jwt_secret;admin_token 屏蔽展示)。 +// v2 新增键(需求 ①②③④⑩):背景图、页脚、通知开关、保存/存储策略、频率限制。 +var configKeys = []string{ + "site_name", "name", "description", "page_explain", "keywords", + "notify_title", "notify_content", "notify_enabled", "logo_url", "favicon_url", + "footer_text", "footer_beian", "background_url", + "openUpload", "uploadSize", "max_file_size", "allowed_file_types", "expireStyle", + "max_save_count", "max_save_seconds", "storageLimit", + "code_generate_type", "enableChunk", + "uploadMinute", "uploadCount", "errorMinute", "errorCount", + "loginCount", "loginMinute", + "opacity", "background", "showAdminAddr", "robotsText", "site_domain", // v3.1:站点对外域名 + "adminSessionExpire", "storage_path", "local_storage_path", + "file_storage", + // v3 存储引擎与引擎参数(热切换;凭据为敏感键,get 掩码/update 空跳过) + "storage_engine", + "local_storage_path", + "webdav_url", "webdav_root_path", "webdav_username", "webdav_password", + "s3_endpoint_url", "s3_region_name", "s3_bucket_name", + "s3_access_key_id", "s3_secret_access_key", "aws_session_token", "s3_addressing_style", +} + +// intConfigKeys 需按 schema 边界校验的整型键(adminConfigUpdate 归一化用)。 +// v1 既有键保留原语义;v2 新增键(max_file_size/max_save_count/notify_enabled) +// 的边界来自 settings.KVSchema(单一事实来源在 config/schema.go)。 +var intConfigKeys = []string{ + "openUpload", "enableChunk", "showAdminAddr", "storageLimit", + "uploadMinute", "uploadCount", "errorMinute", "errorCount", + "loginCount", "loginMinute", "max_save_seconds", "uploadSize", + "adminSessionExpire", + "max_save_count", "max_file_size", "notify_enabled", +} + +// validateConfigValue 按 settings.KVSchema 校验单个配置值: +// - 整型键:Min/Max 边界(如 max_file_size ≤ 10GiB、notify_enabled ∈ {0,1}); +// - 字符串键:长度上限; +// - 列表键(expireStyle/allowed_file_types):必须可解析为字符串数组且非空。 +// +// 校验不通过返回中文 400 错误;未知键不做拦截(与 v1 行为一致,交由 configKeys 过滤)。 +func validateConfigValue(key string, v any) error { + entry := settings.KVSchemaByKey(key) + if entry == nil { + return nil + } + switch entry.Type { + case "int", "int64": + n, ok := toInt64(v) + if !ok { + return errBadRequest(fmt.Sprintf("%s 必须是整数", key)) + } + if n < entry.Min { + return errBadRequest(fmt.Sprintf("%s 不能小于 %d", key, entry.Min)) + } + if entry.Max >= 0 && n > entry.Max { + return errBadRequest(fmt.Sprintf("%s 不能大于 %d", key, entry.Max)) + } + case "string": + s, ok := v.(string) + if !ok { + return errBadRequest(fmt.Sprintf("%s 必须是字符串", key)) + } + if entry.Max >= 0 && len([]rune(s)) > int(entry.Max) { + return errBadRequest(fmt.Sprintf("%s 长度不能超过 %d 字符", key, entry.Max)) + } + case "[]string": + list := toStrSlice(v) + if list == nil { + return errBadRequest(fmt.Sprintf("%s 必须是字符串数组", key)) + } + if len(list) == 0 { + return errBadRequest(fmt.Sprintf("%s 至少保留一项", key)) + } + } + return nil +} + +// toInt64 宽松整型转换(JSON 数字 float64、字符串、int/int64)。 +func toInt64(v any) (int64, bool) { + switch n := v.(type) { + case int: + return int64(n), true + case int64: + return n, true + case float64: + return int64(n), true + case string: + if i, err := strconv.ParseInt(strings.TrimSpace(n), 10, 64); err == nil { + return i, true + } + } + return 0, false +} + +// toStrSlice 宽松字符串数组转换:JSON 数组 / 逗号分隔字符串。 +func toStrSlice(v any) []string { + switch s := v.(type) { + case []string: + return s + case []any: + out := make([]string, 0, len(s)) + for _, item := range s { + if item == nil { + continue + } + out = append(out, fmt.Sprintf("%v", item)) + } + return out + case string: + var out []string + for _, item := range strings.Split(s, ",") { + if item = strings.TrimSpace(item); item != "" { + out = append(out, item) + } + } + return out + } + return nil +} + +// adminConfigGet 读取配置(对齐参考 get_config:admin_token 屏蔽、jwt_secret 不下发)。 +// v3:引擎凭据类敏感键返回掩码占位(前端表单"留空=不修改");storage_engine 为当前热切换后的引擎。 +func (d *Deps) adminConfigGet(c *gin.Context) { + cfg := d.Cfg + out := gin.H{} + for _, key := range configKeys { + if v, ok := cfg.Get(key); ok { + out[key] = v + } + } + for _, key := range settings.SensitiveKeys { + if _, present := out[key]; present { + if key == "admin_token" { + out[key] = "" // 屏蔽(既有语义) + } else { + out[key] = settings.SensitiveMaskValue // 掩码占位 + } + } + } + // jwt_secret 永不下发 + delete(out, "jwt_secret") + // 引擎运行时状态(v3:热切换即时生效,无需重启) + out["_engine_hint"] = gin.H{ + "storage_backend": d.Store.CurrentName(), + "engines": gin.H{"local": true, "s3": true, "webdav": true}, + "note": "存储引擎支持运行时热切换(POST /admin/storage/switch);修改引擎参数保存后下次构建生效", + } + response.OK(c, out) +} + +// adminConfigUpdate 部分更新配置(对齐参考 update_config:改密自动轮换 jwt_secret)。 +func (d *Deps) adminConfigUpdate(c *gin.Context) { + var patch map[string]any + ct := c.GetHeader("Content-Type") + var err error + if strings.Contains(ct, "application/json") { + err = c.ShouldBindJSON(&patch) + } else { + patch = map[string]any{} + err = c.Request.ParseForm() + if err == nil { + for k, v := range c.Request.PostForm { + if len(v) > 0 { + patch[k] = v[0] + } + } + } + } + if err != nil { + response.Fail(c, http.StatusBadRequest, "请求体格式错误") + return + } + if len(patch) == 0 { + response.Fail(c, http.StatusBadRequest, "没有需要更新的配置") + return + } + + dbPatch := map[string]any{} + for k, v := range patch { + known := false + for _, key := range configKeys { + if key == k { + known = true + break + } + } + if !known { + continue + } + // 类型归一 + schema 边界校验(v2:数值/字符串长度/列表键统一走 KVSchema) + isInt := false + for _, key := range intConfigKeys { + if key == k { + isInt = true + break + } + } + switch { + case isInt: + if n, ok := toInt(v); ok { + if err := validateConfigValue(k, n); err != nil { + response.Fail(c, http.StatusBadRequest, err.Error()) + return + } + dbPatch[k] = n + } else { + response.Fail(c, http.StatusBadRequest, fmt.Sprintf("%s 必须是整数", k)) + return + } + case k == "opacity": + if f, ok := toFloat(v); ok { + dbPatch[k] = f + } + case k == "expireStyle" || k == "allowed_file_types": + list := toStrSlice(v) + if list == nil { + response.Fail(c, http.StatusBadRequest, fmt.Sprintf("%s 必须是字符串数组", k)) + return + } + if err := validateConfigValue(k, v); err != nil { + response.Fail(c, http.StatusBadRequest, err.Error()) + return + } + dbPatch[k] = list + default: + // 字符串键按 schema 长度上限校验(背景图/页脚/备案号/通知等) + if err := validateConfigValue(k, v); err != nil { + response.Fail(c, http.StatusBadRequest, err.Error()) + return + } + dbPatch[k] = v + } + } + + // 管理员密码:空串忽略;明文则哈希并轮换 jwt_secret + passwordChanged := false + if raw, ok := patch["admin_token"]; ok { + if s, isStr := raw.(string); isStr && strings.TrimSpace(s) != "" { + if !settings.IsPasswordHashed(s) { + dbPatch["admin_token"] = settings.HashPassword(s) + } else { + dbPatch["admin_token"] = s + } + passwordChanged = true + } + } + // adminSessionExpire 校验(1~365 整天) + if v, ok := dbPatch["adminSessionExpire"]; ok { + sec, _ := toInt(v) + if sec < 86400 || sec > 365*86400 || sec%86400 != 0 { + response.Fail(c, http.StatusBadRequest, "adminSessionExpire 必须是 1 到 365 个整天") + return + } + } + if v, ok := dbPatch["storageLimit"]; ok { + n, _ := toInt(v) + if n < 0 { + response.Fail(c, http.StatusBadRequest, "storageLimit 不能小于 0") + return + } + } + // 背景图 URL 白名单协议(需求 ①):http(s)、data: 与站内相对路径,防 javascript: 注入 + if v, ok := dbPatch["background_url"]; ok { + if s, isStr := v.(string); isStr { + s = strings.TrimSpace(s) + if s != "" && !strings.HasPrefix(s, "http://") && !strings.HasPrefix(s, "https://") && + !strings.HasPrefix(s, "data:image/") && !strings.HasPrefix(s, "/") { + response.Fail(c, http.StatusBadRequest, "background_url 仅支持 http(s) 地址、data:image 图片或站内相对路径") + return + } + dbPatch["background_url"] = s + } + } + // L7:notify_content 白名单净化(仅保留文本与 ),防存储型 XSS + if v, ok := dbPatch["notify_content"]; ok { + if s, isStr := v.(string); isStr { + dbPatch["notify_content"] = settings.SanitizeInlineHTML(s) + } + } + if passwordChanged { + dbPatch["jwt_secret"] = settings.GenerateJWTSecret() + } + + // v3.1:site_domain 规范化(http(s)://host[:port];空=用当前地址) + if raw, ok := dbPatch["site_domain"]; ok { + sv, _ := raw.(string) + normalized, err := normalizeSiteDomain(sv) + if err != nil { + response.Fail(c, http.StatusBadRequest, err.Error()) + return + } + dbPatch["site_domain"] = normalized + } + + // v3 引擎键处理:参数键与 storage_engine 分离。 + // 1) storage_engine 只接受合法枚举; + // 2) 敏感凭据键空串/掩码=不修改(避免管理端表单回显把密钥抹掉); + // 3) 先持久化普通键+参数键 → Invalidate 对应引擎缓存 → 再尝试 Switch 新引擎; + // 4) Switch 失败回滚 storage_engine 的 KV 值并返回 503(参数与普通键保留)。 + newEngine := "" + if raw, ok := dbPatch["storage_engine"]; ok { + s, isStr := raw.(string) + if !isStr || !storage.ValidEngine(strings.TrimSpace(s)) { + response.Fail(c, http.StatusBadRequest, "storage_engine 仅支持 local|s3|webdav") + return + } + newEngine = strings.TrimSpace(s) + delete(dbPatch, "storage_engine") + } + changedParamEngine := engineOfParamKeys(dbPatch) + for _, sk := range settings.SensitiveKeys { + if v, ok := dbPatch[sk]; ok { + if s, isStr := v.(string); isStr && (strings.TrimSpace(s) == "" || s == settings.SensitiveMaskValue) { + delete(dbPatch, sk) // 空/掩码=不修改 + } + } + } + + ctx := c.Request.Context() + if err := d.Mgr.UpdateKV(ctx, dbPatch); err != nil { + respondError(c, errInternal("保存配置失败: "+err.Error())) + return + } + // 引擎参数已变:使对应引擎实例缓存失效(下次构建用新参数) + for _, eng := range changedParamEngine { + d.Store.Invalidate(eng) + } + if newEngine != "" && newEngine != d.Store.CurrentName() { + if _, err := d.Store.Switch(newEngine); err != nil { + // 切换失败:不持久化新引擎名(保持旧引擎 KV),参数/普通键已保存 + respondError(c, &apiError{Status: http.StatusServiceUnavailable, Msg: "存储引擎切换失败,已保持原引擎: " + err.Error()}) + return + } + if err := d.Mgr.UpdateKV(ctx, map[string]any{config.KeyStorageEngine: newEngine}); err != nil { + respondError(c, errInternal("保存存储引擎设置失败: "+err.Error())) + return + } + log.Printf("[storage] 存储引擎已热切换: %s -> %s", d.Cfg.Engine(), newEngine) + } + if err := d.Mgr.Reload(ctx); err != nil { + respondError(c, errInternal("配置重载失败: "+err.Error())) + return + } + d.syncRateRules() + response.OK(c, gin.H{"ok": true, "engine": d.Store.CurrentName()}) +} + +// engineOfParamKeys 判断 patch 涉及哪些引擎的参数(返回需 Invalidate 的引擎列表)。 +func engineOfParamKeys(patch map[string]any) []string { + affect := map[string]bool{} + for k := range patch { + switch k { + case "local_storage_path", "storage_path": + affect["local"] = true + case "webdav_url", "webdav_root_path", "webdav_username", "webdav_password": + affect["webdav"] = true + case "s3_endpoint_url", "s3_region_name", "s3_bucket_name", + "s3_access_key_id", "s3_secret_access_key", "aws_session_token", "s3_addressing_style": + affect["s3"] = true + } + } + out := make([]string, 0, len(affect)) + for eng := range affect { + out = append(out, eng) + } + return out +} + +// adminStorageSwitch v3 存储引擎热切换:{engine:"local"|"s3"|"webdav"}。 +// 成功:持久化 storage_engine KV 并返回当前引擎;失败:503 且原引擎不变。 +func (d *Deps) adminStorageSwitch(c *gin.Context) { + var body struct { + Engine string `json:"engine" form:"engine"` + } + if err := bindJSONOrForm(c, &body); err != nil { + respondError(c, err) + return + } + engine := strings.TrimSpace(body.Engine) + if !storage.ValidEngine(engine) { + response.Fail(c, http.StatusBadRequest, "engine 仅支持 local|s3|webdav") + return + } + if engine != d.Store.CurrentName() { + if _, err := d.Store.Switch(engine); err != nil { + respondError(c, &apiError{Status: http.StatusServiceUnavailable, Msg: "存储引擎切换失败,已保持原引擎: " + err.Error()}) + return + } + } + if err := d.Mgr.UpdateKV(c.Request.Context(), map[string]any{config.KeyStorageEngine: engine}); err != nil { + respondError(c, errInternal("保存存储引擎设置失败: "+err.Error())) + return + } + if err := d.Mgr.Reload(c.Request.Context()); err != nil { + respondError(c, errInternal("配置重载失败: "+err.Error())) + return + } + log.Printf("[storage] 存储引擎已热切换 -> %s", engine) + response.OK(c, gin.H{"ok": true, "engine": engine}) +} + +// adminChangePassword 修改管理员密码(校验旧密码;新密码哈希 + 轮换 jwt_secret)。 +func (d *Deps) adminChangePassword(c *gin.Context) { + var body struct { + OldPassword string `json:"old_password" form:"old_password"` + NewPassword string `json:"new_password" form:"new_password"` + } + if err := bindJSONOrForm(c, &body); err != nil { + respondError(c, err) + return + } + if len(body.NewPassword) < 8 { + response.Fail(c, http.StatusBadRequest, "新密码长度至少 8 位") + return + } + stored := d.Cfg.GetString("admin_token") + if stored == "" || !settings.VerifyPassword(body.OldPassword, stored) { + response.Fail(c, http.StatusUnauthorized, "旧密码错误") + return + } + ctx := c.Request.Context() + patch := map[string]any{ + "admin_token": settings.HashPassword(body.NewPassword), + "jwt_secret": settings.GenerateJWTSecret(), + } + if err := d.Mgr.UpdateKV(ctx, patch); err != nil { + respondError(c, errInternal("保存密码失败: "+err.Error())) + return + } + if err := d.Mgr.Reload(ctx); err != nil { + respondError(c, errInternal("配置重载失败: "+err.Error())) + return + } + response.OK(c, gin.H{"ok": true}) +} + +// ============ 审计日志查询(需求 ③)============ + +// adminAuditList 分页查询审计日志: +// 参数 page/size/action/result/ip/start_time/end_time(ISO 8601)。 +func (d *Deps) adminAuditList(c *gin.Context) { + page := maxInt(queryInt(c, "page", 1), 1) + size := clampInt(queryInt(c, "size", queryInt(c, "pageSize", 20)), 1, 200) + action := strings.TrimSpace(c.Query("action")) + result := strings.TrimSpace(c.Query("result")) + ip := strings.TrimSpace(c.Query("ip")) + var begin, end *time.Time + if raw := strings.TrimSpace(c.Query("start_time")); raw != "" { + if t, err := parseISOTime(raw); err == nil { + begin = &t + } else { + response.Fail(c, http.StatusBadRequest, "start_time 时间格式错误") + return + } + } + if raw := strings.TrimSpace(c.Query("end_time")); raw != "" { + if t, err := parseISOTime(raw); err == nil { + end = &t + } else { + response.Fail(c, http.StatusBadRequest, "end_time 时间格式错误") + return + } + } + logs, total, err := d.AuditSvc.Query(page, size, action, ip, result, begin, end) + if err != nil { + respondError(c, errInternal("查询审计日志失败: "+err.Error())) + return + } + items := make([]gin.H, 0, len(logs)) + for i := range logs { + items = append(items, buildAuditItem(&logs[i])) + } + response.OK(c, gin.H{"data": items, "total": total, "page": page, "size": size}) +} + +// buildAuditItem 审计行(snake_case 原生 + camelCase 双份)。 +func buildAuditItem(a *model.AuditLog) gin.H { + return gin.H{ + "id": a.ID, + "action": a.Action, + "file_code": a.FileCode, "fileCode": a.FileCode, + "file_name": a.FileName, "fileName": a.FileName, + "size_bytes": a.SizeBytes, "sizeBytes": a.SizeBytes, + "transferred_bytes": a.TransferredBytes, "transferredBytes": a.TransferredBytes, + "ip": a.IP, + "user_agent": a.UserAgent, "userAgent": a.UserAgent, + "device_os": a.DeviceOS, "deviceOs": a.DeviceOS, + "device_browser": a.DeviceBrowser, "deviceBrowser": a.DeviceBrowser, + "device_type": a.DeviceType, "deviceType": a.DeviceType, + "actor": a.Actor, + "result": a.Result, + "error_msg": a.ErrorMsg, "errorMsg": a.ErrorMsg, + "duration_ms": a.DurationMs, "durationMs": a.DurationMs, + "created_at": a.CreatedAt.Format(time.RFC3339), "createdAt": a.CreatedAt.Format(time.RFC3339), + } +} + +// ============ 内部辅助 ============ + +// fileByID 按 ID 查询分享记录。 +func (d *Deps) fileByID(c *gin.Context, id int64) (*model.FileCodes, error) { + if id <= 0 { + return nil, errBadRequest("无效的文件 ID") + } + var fc model.FileCodes + if err := d.DB.WithContext(c.Request.Context()). + Where("id = ?", id).First(&fc).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errNotFound("文件不存在") + } + return nil, errInternal("查询失败: " + err.Error()) + } + return &fc, nil +} + +// deleteFileCode 删除分享记录与存储文件(文本分享无存储文件)。 +func (d *Deps) deleteFileCode(c *gin.Context, fc *model.FileCodes) error { + if fc.Text == nil && fc.FilePath != nil && fc.UUIDFileName != nil { + // v3:删除走文件归属引擎(旧引擎里的文件也要能删掉) + if delStore, dErr := d.storeFor(fc.Engine); dErr == nil { + if err := delStore.DeleteFile(c.Request.Context(), fileSavePath(fc)); err != nil && !errors.Is(err, storage.ErrNotFound) { + return errInternal("存储文件删除失败: " + err.Error()) + } + } + } + if err := d.DB.WithContext(c.Request.Context()). + Where("id = ?", fc.ID).Delete(&model.FileCodes{}).Error; err != nil { + return errInternal("删除记录失败: " + err.Error()) + } + return nil +} + +// deleteMany 批量删除:返回 (已删除, 不存在, 失败)。 +func (d *Deps) deleteMany(c *gin.Context, ids []int64) (deleted []int64, missing []int64, failed []gin.H) { + deleted, missing = []int64{}, []int64{} + failed = []gin.H{} + for _, id := range ids { + fc, err := d.fileByID(c, id) + if err != nil { + missing = append(missing, id) + continue + } + if err := d.deleteFileCode(c, fc); err != nil { + failed = append(failed, gin.H{"id": id, "reason": err.Error()}) + continue + } + deleted = append(deleted, id) + } + return +} + +// requestID 从 query 或 JSON body 取 id。 +func requestID(c *gin.Context) (int64, error) { + if v := c.Query("id"); v != "" { + n, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return 0, errBadRequest("无效的文件 ID") + } + return n, nil + } + var body struct { + ID int64 `json:"id"` + } + if err := bindJSONOrForm(c, &body); err != nil { + return 0, err + } + return body.ID, nil +} + +// queryInt 读取整数 query 参数。 +func queryInt(c *gin.Context, key string, def int) int { + raw := c.Query(key) + if raw == "" { + return def + } + n, err := strconv.Atoi(raw) + if err != nil { + return def + } + return n +} + +// maxInt / minInt / clampInt 整数辅助。 +func maxInt(a, b int) int { + if a > b { + return a + } + return b +} + +func minInt(a, b int) int { + if a < b { + return a + } + return b +} + +func clampInt(v, lo, hi int) int { return maxInt(lo, minInt(hi, v)) } + +func boolToInt(b bool) int { + if b { + return 1 + } + return 0 +} + +func boolToInt64(b bool) int64 { + if b { + return 1 + } + return 0 +} + +func firstNonEmpty(items ...string) string { + for _, s := range items { + if s != "" { + return s + } + } + return "" +} + +// toInt / toFloat 宽松类型转换(JSON 数字 float64、字符串、int)。 +func toInt(v any) (int, bool) { + switch n := v.(type) { + case int: + return n, true + case int64: + return int(n), true + case float64: + return int(n), true + case string: + if i, err := strconv.Atoi(strings.TrimSpace(n)); err == nil { + return i, true + } + } + return 0, false +} + +func toFloat(v any) (float64, bool) { + switch n := v.(type) { + case float64: + return n, true + case int: + return float64(n), true + case string: + if f, err := strconv.ParseFloat(strings.TrimSpace(n), 64); err == nil { + return f, true + } + } + return 0, false +} + +// parseJSONNumber 解析 JSON 标量数字。 +func parseJSONNumber(raw string, out *int64) error { + raw = strings.TrimSpace(raw) + n, err := strconv.ParseInt(raw, 10, 64) + if err != nil { + return fmt.Errorf("非法数字: %s", raw) + } + *out = n + return nil +} + +// escapeLike 转义 LIKE 通配符(\ % _),配合 `LIKE ? ESCAPE '\'` 使用。 +func escapeLike(s string) string { + r := strings.NewReplacer(`\`, `\\`, `%`, `\%`, `_`, `\_`) + return r.Replace(s) +} + +// normalizeSortBy 归一化排序字段(白名单外回退 created_at)。 +func normalizeSortBy(s string) string { + s = strings.ToLower(strings.ReplaceAll(strings.TrimSpace(s), "-", "_")) + switch s { + case "created_at", "createdat", "expired_at", "expiredat", "name", "size", "used_count", "usedcount", "code": + if s == "createdat" { + return "created_at" + } + if s == "expiredat" { + return "expired_at" + } + if s == "usedcount" { + return "used_count" + } + return s + } + return "created_at" +} + +// sortRecentDesc 按创建时间倒序(插入排序,数据量小)。 +func sortRecentDesc(items []model.FileCodes) { + for i := 1; i < len(items); i++ { + for j := i; j > 0 && items[j].CreatedAt.After(items[j-1].CreatedAt); j-- { + items[j], items[j-1] = items[j-1], items[j] + } + } +} + +// sortFileCodes 按白名单字段排序。 +func sortFileCodes(items []model.FileCodes, sortBy string, desc bool) { + less := func(a, b *model.FileCodes) bool { + switch sortBy { + case "expired_at": + var ta, tb time.Time + if a.ExpiredAt != nil { + ta = *a.ExpiredAt + } + if b.ExpiredAt != nil { + tb = *b.ExpiredAt + } + return ta.Before(tb) + case "name": + return a.Prefix+a.Suffix < b.Prefix+b.Suffix + case "size": + return a.Size < b.Size + case "used_count": + return a.UsedCount < b.UsedCount + case "code": + return a.Code < b.Code + default: // created_at + return a.CreatedAt.Before(b.CreatedAt) + } + } + // 插入排序(分页前数据量有限;列表页通常数百条内) + for i := 1; i < len(items); i++ { + for j := i; j > 0; j-- { + if desc { + if less(&items[j-1], &items[j]) { + items[j-1], items[j] = items[j], items[j-1] + continue + } + } else if less(&items[j], &items[j-1]) { + items[j-1], items[j] = items[j], items[j-1] + continue + } + break + } + } +} diff --git a/server/internal/api/chunk.go b/server/internal/api/chunk.go new file mode 100644 index 0000000..806a9a9 --- /dev/null +++ b/server/internal/api/chunk.go @@ -0,0 +1,667 @@ +package api + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "io" + "mime/multipart" + "net/http" + "strconv" + "time" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + + "filecodebox/internal/middleware" + "filecodebox/internal/model" + "filecodebox/internal/response" + "filecodebox/internal/storage" +) + +// chunkExpireTTL 分片会话保留时长(M5:预留窗口由 24h 缩短为 2h; +// 会话本身保留 24h 支持断点续传,见 janitor 的清理周期)。 +const chunkExpireTTL = 2 * time.Hour + +// maxChunkSizeBytes 单分片大小上限 32MB(M3:限制 io.ReadAll 内存占用)。 +const maxChunkSizeBytes = 32 * 1024 * 1024 + +// ============ POST /chunk/upload/init 初始化分片会话 ============ + +// requireChunkEnabled L4:enableChunk 开关后端强制(此前仅前端隐藏入口, +// 开关关闭后 /chunk/* 接口仍可直接调用)。 +func (d *Deps) requireChunkEnabled(c *gin.Context) bool { + if d.Cfg.EnableChunk() { + return true + } + auditRecordFailed(c, d.AuditSvc, "分片上传未启用") + response.Fail(c, http.StatusForbidden, "分片上传未启用") + return false +} + +// chunkInitRequest init 请求体(JSON 或表单)。 +type chunkInitRequest struct { + FileName string `json:"file_name" form:"file_name"` + ChunkSize int64 `json:"chunk_size" form:"chunk_size"` + FileSize int64 `json:"file_size" form:"file_size"` + FileHash string `json:"file_hash" form:"file_hash"` +} + +// chunkInit 创建分片上传会话(对齐参考 init_chunk_upload): +// 支持断点续传(相同 hash/大小/文件名的未完成会话直接续传)。 +func (d *Deps) chunkInit(c *gin.Context) { + if !d.requireChunkEnabled(c) { + return + } + if !d.requireShareLogin(c) { + return + } + if !requireUploadLimit(c, d.Limiter) { + return + } + var req chunkInitRequest + if err := bindJSONOrForm(c, &req); err != nil { + respondError(c, err) + return + } + safeName := storage.SanitizeFileName(req.FileName) + if safeName == "" { + auditRecordFailed(c, d.AuditSvc, "文件名非法") + response.Fail(c, http.StatusBadRequest, "文件名非法") + return + } + // 文件类型白名单(无内容可校验,仅名称) + if err := validateFileMagic(d.Cfg, safeName, "", nil); err != nil { + auditUploadEntry(c, "", safeName, req.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "文件类型被拒绝") + respondError(c, err) + return + } + chunkSize := req.ChunkSize + if chunkSize <= 0 { + chunkSize = 5 * 1024 * 1024 // 默认 5MB(对齐参考 InitChunkUploadModel) + } + // M3:单片全部读入内存后再落存储,必须限制单片大小(客户端声明的 + // chunk_size 上界受策略约束,但策略允许至 10GiB → 显式封顶 32MB)。 + if chunkSize > maxChunkSizeBytes { + auditRecordFailed(c, d.AuditSvc, "chunk_size 超过上限") + response.Fail(c, http.StatusBadRequest, fmt.Sprintf("chunk_size 过大,最大为 %d MB", maxChunkSizeBytes>>20)) + return + } + if req.FileSize <= 0 { + auditRecordFailed(c, d.AuditSvc, "file_size 非法") + response.Fail(c, http.StatusBadRequest, "file_size 必须大于 0") + return + } + // 服务端按分片数上限校验总大小(防分片声明绕过) + totalChunks := (req.FileSize + chunkSize - 1) / chunkSize + maxPossible := totalChunks * chunkSize + // v2 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize) + if err := d.CurrentUploadPolicy().CheckSize(maxPossible); err != nil { + auditUploadEntry(c, "", safeName, req.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "文件大小超过限制") + respondError(c, err) + return + } + + ctx := c.Request.Context() + // 断点续传:查找相同 hash+大小+文件名的未完成会话(chunk_index=-1 为会话头) + var existing model.UploadChunk + err := d.DB.WithContext(ctx). + Where("chunk_hash = ? AND chunk_index = -1 AND file_size = ? AND file_name = ?", + req.FileHash, req.FileSize, safeName). + First(&existing).Error + if err == nil { + if existing.SavePath == "" { + // 脏会话:清理后按新建处理 + _ = d.DB.WithContext(ctx). + Where("upload_id = ?", existing.UploadID). + Delete(&model.UploadChunk{}).Error + releaseStorage(ctx, d.DB, "chunk:"+existing.UploadID) + } else { + if err := reserveStorage(ctx, d.DB, d.Cfg, "chunk:"+existing.UploadID, existing.FileSize, chunkExpireTTL); err != nil { + respondError(c, err) + return + } + uploaded := d.uploadedChunkIndexes(ctx, existing.UploadID) + auditUploadEntry(c, existing.UploadID, safeName, req.FileSize, 0) + auditRecordSuccess(c, d.AuditSvc) + response.OK(c, gin.H{ + "existed": false, + "upload_id": existing.UploadID, + "chunk_size": existing.ChunkSize, + "total_chunks": existing.TotalChunks, + "uploaded_chunks": uploaded, + }) + return + } + } else if !errors.Is(err, gorm.ErrRecordNotFound) { + respondError(c, errInternal("查询上传会话失败: "+err.Error())) + return + } + + // 新建会话 + uploadID := uuidHex() + resToken := "chunk:" + uploadID + if err := reserveStorage(ctx, d.DB, d.Cfg, resToken, req.FileSize, chunkExpireTTL); err != nil { + auditUploadEntry(c, "", safeName, req.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "容量预留失败") + respondError(c, err) + return + } + // M5:init 即计入上传限流(此前仅 complete 成功时计数, + // 恶意客户端可无限创建会话占用容量预留) + d.Limiter.Add(c, middleware.LimitUpload) + _, _, _, _, savePath := buildSavePath(d.Cfg, safeName, uploadID) + session := model.UploadChunk{ + UploadID: uploadID, + ChunkIndex: -1, + TotalChunks: int(totalChunks), + FileSize: req.FileSize, + ChunkSize: int(chunkSize), + ChunkHash: req.FileHash, + FileName: safeName, + SavePath: savePath, + Engine: d.Store.CurrentName(), // v3:会话归属引擎(分片/合并全程走同一引擎) + } + if err := d.DB.WithContext(ctx).Create(&session).Error; err != nil { + releaseStorage(ctx, d.DB, resToken) + auditUploadEntry(c, "", safeName, req.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "会话创建失败") + respondError(c, errInternal("创建上传会话失败: "+err.Error())) + return + } + auditUploadEntry(c, uploadID, safeName, req.FileSize, 0) + auditRecordSuccess(c, d.AuditSvc) + response.OK(c, gin.H{ + "existed": false, + "upload_id": uploadID, + "chunk_size": chunkSize, + "total_chunks": totalChunks, + "uploaded_chunks": []int{}, + }) +} + +// uploadedChunkIndexes 查询会话中已完成分片的索引列表。 +func (d *Deps) uploadedChunkIndexes(ctx context.Context, uploadID string) []int { + var rows []model.UploadChunk + if err := d.DB.WithContext(ctx). + Where("upload_id = ? AND completed = ?", uploadID, true). + Order("chunk_index ASC").Find(&rows).Error; err != nil { + return []int{} + } + out := make([]int, 0, len(rows)) + for _, r := range rows { + out = append(out, r.ChunkIndex) + } + return out +} + +// ============ POST /chunk/upload/{uploadID}/{index}(及扁平兼容)============ + +// chunkUploadFlat 扁平模式:POST /chunk/upload,upload_id/chunk_index 走表单或 query。 +// 多文件字段(chunk/chunks)时按 base_chunk_index 顺序批量接收。 +func (d *Deps) chunkUploadFlat(c *gin.Context) { + c.Params = append(c.Params, gin.Param{Key: "uploadID", Value: resolveUploadID(c)}) + c.Params = append(c.Params, gin.Param{Key: "chunkIndex", Value: resolveChunkIndex(c)}) + d.chunkUpload(c) +} + +// resolveUploadID 解析 upload_id:路径参数 → multipart 表单 → query。 +func resolveUploadID(c *gin.Context) string { + if v := c.Param("uploadID"); v != "" { + return v + } + if v := c.PostForm("upload_id"); v != "" { + return v + } + return c.Query("upload_id") +} + +// resolveChunkIndex 解析 chunk_index:路径参数 → multipart 表单 → query。 +func resolveChunkIndex(c *gin.Context) string { + if v := c.Param("chunkIndex"); v != "" { + return v + } + if v := c.PostForm("chunk_index"); v != "" { + return v + } + return c.Query("chunk_index") +} + +// chunkUpload 上传单个(或批量)分片(对齐参考 upload_chunk)。 +// multipart 文件字段:chunk(主)或 file(回退);批量用 chunk[]/chunks 数组 + chunk_index 为起始索引。 +func (d *Deps) chunkUpload(c *gin.Context) { + if !d.requireChunkEnabled(c) { + return + } + if !d.requireShareLogin(c) { + return + } + uploadID := resolveUploadID(c) + ctx := c.Request.Context() + + var session model.UploadChunk + if err := d.DB.WithContext(ctx). + Where("upload_id = ? AND chunk_index = -1", uploadID). + First(&session).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + auditUploadEntry(c, uploadID, "", 0, 0) + auditRecordFailed(c, d.AuditSvc, "上传会话不存在") + response.Fail(c, http.StatusNotFound, "上传会话不存在") + return + } + respondError(c, errInternal("查询上传会话失败: "+err.Error())) + return + } + if err := reserveStorage(ctx, d.DB, d.Cfg, "chunk:"+uploadID, session.FileSize, chunkExpireTTL); err != nil { + respondError(c, err) + return + } + + // 收集分片文件:chunk(单)→ file(回退)→ chunk[]/chunks(批量) + form, err := c.MultipartForm() + if err != nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "multipart 解析失败") + response.Fail(c, http.StatusBadRequest, "multipart 表单解析失败") + return + } + files := form.File["chunk"] + single := len(files) == 0 + if single { + files = form.File["file"] + } + if len(files) == 0 { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "缺少 chunk 分片字段") + response.Fail(c, http.StatusBadRequest, "缺少分片文件字段 chunk") + return + } + + baseIndex, err := strconv.Atoi(resolveChunkIndex(c)) + if err != nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "无效的分片索引") + response.Fail(c, http.StatusBadRequest, "无效的分片索引") + return + } + + results := make([]gin.H, 0, len(files)) + for i, fh := range files { + // 单分片模式严格使用请求索引;批量模式从 base 递增 + idx := baseIndex + if !single && len(files) > 1 { + idx = baseIndex + i + } + res, status, msg := d.saveOneChunk(c, ctx, &session, idx, fh) + if status != 0 { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, msg) + response.Fail(c, status, msg) + return + } + results = append(results, res) + } + // 审计:传输字节数为本次请求分片总和 + var transferred int64 + for _, fh := range files { + transferred += fh.Size + } + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, transferred) + auditRecordSuccess(c, d.AuditSvc) + if len(results) == 1 { + response.OK(c, results[0]) + return + } + response.OK(c, gin.H{"chunks": results}) +} + +// saveOneChunk 保存一个分片:查重→读数据→校验→存储→记录。 +// 返回 (响应体, HTTP错误状态码, 错误信息);成功时状态码为 0。 +func (d *Deps) saveOneChunk(c *gin.Context, ctx context.Context, session *model.UploadChunk, idx int, fh *multipart.FileHeader) (gin.H, int, string) { + if idx < 0 || idx >= session.TotalChunks { + return nil, http.StatusBadRequest, "无效的分片索引" + } + // 已上传分片:断点续传直接跳过 + var existing model.UploadChunk + err := d.DB.WithContext(ctx). + Where("upload_id = ? AND chunk_index = ? AND completed = ?", session.UploadID, idx, true). + First(&existing).Error + if err == nil { + return gin.H{"chunk_hash": existing.ChunkHash, "skipped": true, "chunk_index": idx}, 0, "" + } + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, http.StatusInternalServerError, "查询分片记录失败" + } + + f, err := fh.Open() + if err != nil { + return nil, http.StatusBadRequest, "分片数据读取失败" + } + defer func() { _ = f.Close() }() + data, err := io.ReadAll(io.LimitReader(f, int64(session.ChunkSize)+1)) + if err != nil { + return nil, http.StatusBadRequest, "分片数据读取失败" + } + // 校验分片大小不超过声明值 + if int64(len(data)) > int64(session.ChunkSize) { + return nil, http.StatusBadRequest, + "分片大小超过声明值: 最大 " + strconv.Itoa(session.ChunkSize) + ", 实际 " + strconv.Itoa(len(data)) + } + // 累计大小校验(已传分片数×chunk_size + 当前分片;动态策略上限) + var uploadedCount int64 + _ = d.DB.WithContext(ctx).Model(&model.UploadChunk{}). + Where("upload_id = ? AND completed = ?", session.UploadID, true). + Count(&uploadedCount).Error + if err := d.CurrentUploadPolicy().CheckSize(uploadedCount*int64(session.ChunkSize) + int64(len(data))); err != nil { + return nil, http.StatusForbidden, err.Error() + } + // 首分片做 magic bytes 防伪造 + if idx == 0 { + head := data + if len(head) > 64 { + head = head[:64] + } + if err := validateFileMagic(d.Cfg, session.FileName, "", head); err != nil { + return nil, http.StatusForbidden, "文件内容校验失败:" + err.Error() + } + } + + sum := sha256.Sum256(data) + chunkHash := hex.EncodeToString(sum[:]) + if _, err := d.Store.SaveChunk(ctx, session.UploadID, idx, bytes.NewReader(data), session.SavePath); err != nil { + return nil, http.StatusInternalServerError, "分片保存失败: " + err.Error() + } + // 保存成功后再记录(对齐参考:先存储后落库)。 + // 注意:不能用结构体 Where 条件(GORM 会忽略零值字段,chunk_index=0 会被 + // 丢弃从而误匹配 -1 会话行),必须用字符串条件 + 完整目标结构体。 + rec := model.UploadChunk{ + UploadID: session.UploadID, + ChunkIndex: idx, + ChunkHash: chunkHash, + Completed: true, + FileSize: session.FileSize, + TotalChunks: session.TotalChunks, + ChunkSize: session.ChunkSize, + FileName: session.FileName, + SavePath: session.SavePath, + Engine: session.Engine, // v3:继承会话引擎 + } + if err := d.DB.WithContext(ctx). + Where("upload_id = ? AND chunk_index = ?", session.UploadID, idx). + FirstOrCreate(&rec).Error; err != nil { + return nil, http.StatusInternalServerError, "分片记录写入失败" + } + return gin.H{"chunk_hash": chunkHash, "chunk_index": idx}, 0, "" +} + +// ============ GET /chunk/upload/status/{uploadID} ============ + +// chunkStatus 查询上传进度(对齐参考 get_upload_status)。 +func (d *Deps) chunkStatus(c *gin.Context) { + if !d.requireShareLogin(c) { + return + } + uploadID := c.Param("uploadID") + if uploadID == "" { + uploadID = c.Query("upload_id") + } + ctx := c.Request.Context() + var session model.UploadChunk + if err := d.DB.WithContext(ctx). + Where("upload_id = ? AND chunk_index = -1", uploadID). + First(&session).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Fail(c, http.StatusNotFound, "上传会话不存在") + return + } + respondError(c, errInternal("查询上传会话失败: "+err.Error())) + return + } + uploaded := d.uploadedChunkIndexes(ctx, uploadID) + var progress float64 + if session.TotalChunks > 0 { + progress = float64(len(uploaded)) / float64(session.TotalChunks) * 100 + } + response.OK(c, gin.H{ + "upload_id": uploadID, + "file_name": session.FileName, + "file_size": session.FileSize, + "chunk_size": session.ChunkSize, + "total_chunks": session.TotalChunks, + "uploaded_chunks": uploaded, + "progress": progress, + }) +} + +// ============ POST /chunk/upload/complete/{uploadID} ============ + +// chunkCompleteRequest complete 请求体。 +type chunkCompleteRequest struct { + ExpireValue int `json:"expire_value" form:"expire_value"` + ExpireStyle string `json:"expire_style" form:"expire_style"` + Code string `json:"code" form:"code"` // v3.1:自定义提取码(4-8 位字母数字,空=随机) +} + +// chunkComplete 合并分片并创建分享(对齐参考 complete_upload)。 +func (d *Deps) chunkComplete(c *gin.Context) { + if !d.requireChunkEnabled(c) { + return + } + if !d.requireShareLogin(c) { + return + } + if !requireUploadLimit(c, d.Limiter) { + return + } + uploadID := c.Param("uploadID") + if uploadID == "" { + uploadID = resolveUploadID(c) + } + ctx := c.Request.Context() + + var session model.UploadChunk + if err := d.DB.WithContext(ctx). + Where("upload_id = ? AND chunk_index = -1", uploadID). + First(&session).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + auditUploadEntry(c, uploadID, "", 0, 0) + auditRecordFailed(c, d.AuditSvc, "上传会话不存在") + response.Fail(c, http.StatusNotFound, "上传会话不存在") + return + } + respondError(c, errInternal("查询上传会话失败: "+err.Error())) + return + } + var req chunkCompleteRequest + if err := bindJSONOrForm(c, &req); err != nil { + respondError(c, err) + return + } + exp, err := resolveExpire(d.Cfg, req.ExpireValue, req.ExpireStyle) + if err != nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "过期策略非法") + respondError(c, err) + return + } + // v3.1:自定义提取码(合并前校验,失败快速返回) + if err := validatePickupCode(req.Code); err != nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "提取码非法") + respondError(c, err) + return + } + if err := reserveStorage(ctx, d.DB, d.Cfg, "chunk:"+uploadID, session.FileSize, chunkExpireTTL); err != nil { + respondError(c, err) + return + } + + // 分片完整性校验(chunk_index >= 0;-1 为会话头,completed 恒为 false) + var completed []model.UploadChunk + if err := d.DB.WithContext(ctx). + Where("upload_id = ? AND completed = ? AND chunk_index >= 0", uploadID, true). + Find(&completed).Error; err != nil { + respondError(c, errInternal("查询分片记录失败: "+err.Error())) + return + } + if len(completed) != session.TotalChunks { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "分片不完整") + response.Fail(c, http.StatusBadRequest, "分片不完整") + return + } + // 累计大小上限校验(超限清理会话,对齐参考;动态策略上限) + if err := d.CurrentUploadPolicy().CheckSize(int64(len(completed)) * int64(session.ChunkSize)); err != nil { + if cs, ce := d.storeFor(session.Engine); ce == nil { + _ = cs.CleanChunks(ctx, uploadID, session.SavePath) + } + _ = d.DB.WithContext(ctx).Where("upload_id = ?", uploadID).Delete(&model.UploadChunk{}).Error + releaseStorage(ctx, d.DB, "chunk:"+uploadID) + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "实际上传大小超过限制") + respondError(c, err) + return + } + + // 合并(引擎负责按索引有序合并+SHA256 校验) + verifyHash := func(index int) (string, error) { + var rec model.UploadChunk + err := d.DB.WithContext(ctx). + Where("upload_id = ? AND chunk_index = ?", uploadID, index). + First(&rec).Error + if err != nil { + return "", err + } + return rec.ChunkHash, nil + } + // v3:合并走会话归属引擎(会话创建时的引擎,即使中途热切换也不受影响) + mergeStore, sErr := d.storeFor(session.Engine) + if sErr != nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, session.FileSize) + auditRecordFailed(c, d.AuditSvc, "存储引擎不可用: "+sErr.Error()) + respondError(c, mapStorageError(sErr)) + return + } + size, fileHash, err := mergeStore.MergeChunks(ctx, uploadID, session.TotalChunks, verifyHash, session.SavePath) + if err != nil { + _ = mergeStore.CleanChunks(ctx, uploadID, session.SavePath) + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, session.FileSize) + auditRecordFailed(c, d.AuditSvc, "文件合并失败") + respondError(c, mapStorageError(err)) + return + } + + // 创建分享记录(v3.1:支持自定义提取码) + code, err := pickCustomCode(ctx, d.DB, d.Cfg, req.Code) + if err == nil { + fc := model.FileCodes{ + Code: code, + FileHash: &fileHash, + IsChunked: true, + UploadID: &uploadID, + Size: session.FileSize, + ExpiredAt: exp.ExpiredAt, + ExpiredCount: exp.ExpiredCount, + UsedCount: exp.UsedCount, + Engine: session.Engine, // v3:归属引擎戳 + } + // 拆分路径与文件名(对齐参考:path=dirname(save_path), uuid=basename) + dir, name := splitDirBase(session.SavePath) + ext := baseExt(name) + fc.FilePath = &dir + fc.UUIDFileName = &name + fc.Prefix = trimExt(name) + fc.Suffix = ext + err = d.DB.WithContext(ctx).Create(&fc).Error + err = mapCodeConflict(err) // v3.1 + } + if err == nil { + // 成功:清理分片与记录(走归属引擎) + _ = mergeStore.CleanChunks(ctx, uploadID, session.SavePath) + _ = d.DB.WithContext(ctx).Where("upload_id = ?", uploadID).Delete(&model.UploadChunk{}).Error + } + releaseStorage(ctx, d.DB, "chunk:"+uploadID) + if err != nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, size) + auditRecordFailed(c, d.AuditSvc, "创建分享失败") + respondError(c, errInternal("创建分享失败: "+err.Error())) + return + } + + d.Limiter.Add(c, middleware.LimitUpload) + auditUploadEntry(c, code, session.FileName, session.FileSize, size) + auditRecordSuccess(c, d.AuditSvc) + response.OK(c, gin.H{"code": code, "name": session.FileName}) +} + +// splitDirBase 拆分相对路径为目录与文件名。 +func splitDirBase(p string) (dir, base string) { + for i := len(p) - 1; i >= 0; i-- { + if p[i] == '/' { + return p[:i], p[i+1:] + } + } + return "", p +} + +// baseExt 提取扩展名(含点)。 +func baseExt(name string) string { + for i := len(name) - 1; i >= 0; i-- { + if name[i] == '.' { + return name[i:] + } + if name[i] == '/' { + break + } + } + return "" +} + +// trimExt 去除扩展名。 +func trimExt(name string) string { + ext := baseExt(name) + return name[:len(name)-len(ext)] +} + +// ============ DELETE /chunk/upload/{uploadID} 取消上传 ============ + +// chunkCancel 取消上传并清理临时文件(对齐参考 cancel_upload)。 +func (d *Deps) chunkCancel(c *gin.Context) { + if !d.requireShareLogin(c) { + return + } + uploadID := c.Param("uploadID") + if uploadID == "" { + uploadID = c.Query("upload_id") + } + ctx := c.Request.Context() + var session model.UploadChunk + if err := d.DB.WithContext(ctx). + Where("upload_id = ? AND chunk_index = -1", uploadID). + First(&session).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Fail(c, http.StatusNotFound, "上传会话不存在") + return + } + respondError(c, errInternal("查询上传会话失败: "+err.Error())) + return + } + if session.SavePath != "" { + if cs, ce := d.storeFor(session.Engine); ce == nil { + _ = cs.CleanChunks(ctx, uploadID, session.SavePath) + } + } + if err := d.DB.WithContext(ctx). + Where("upload_id = ?", uploadID). + Delete(&model.UploadChunk{}).Error; err != nil { + respondError(c, errInternal("取消上传失败: "+err.Error())) + return + } + releaseStorage(ctx, d.DB, "chunk:"+uploadID) + response.OK(c, gin.H{"message": "上传已取消"}) +} diff --git a/server/internal/api/helpers.go b/server/internal/api/helpers.go new file mode 100644 index 0000000..350e535 --- /dev/null +++ b/server/internal/api/helpers.go @@ -0,0 +1,798 @@ +// Package api 提供文件快传的 HTTP API 层: +// 分享(share)、分片上传(chunk)、预签名直传(presign)、管理端(admin)、 +// 初始化向导(setup)与前端静态资源(web)。 +// +// 接口语义对齐参考实现 apps/base/views.py 与 apps/admin/views.py; +// 响应统一 {"code":200,"msg":"","data":...}(internal/response)。 +// 所有上传/下载端点经 middleware.Audit 落审计日志(需求 ③)。 +package api + +import ( + "bytes" + "context" + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "io" + "math/big" + "mime/multipart" + "net/http" + "net/url" + "path" + "regexp" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + + "filecodebox/internal/audit" + "filecodebox/internal/config" + "filecodebox/internal/middleware" + "filecodebox/internal/model" + "filecodebox/internal/response" + "filecodebox/internal/storage" +) + +// apiError 统一的业务错误:handler 返回该错误并由 respondError 映射 HTTP 状态。 +type apiError struct { + Status int // HTTP 状态码(同时写入响应体 code) + Msg string // 错误描述(中文) +} + +func (e *apiError) Error() string { return e.Msg } + +func errBadRequest(msg string) error { return &apiError{Status: http.StatusBadRequest, Msg: msg} } +func errForbidden(msg string) error { return &apiError{Status: http.StatusForbidden, Msg: msg} } +func errNotFound(msg string) error { return &apiError{Status: http.StatusNotFound, Msg: msg} } +func errConflict(msg string) error { return &apiError{Status: http.StatusConflict, Msg: msg} } +func errLocked(msg string) error { return &apiError{Status: http.StatusLocked, Msg: msg} } +func errInternal(msg string) error { + return &apiError{Status: http.StatusInternalServerError, Msg: msg} +} +func errInsufficient(msg string) error { + return &apiError{Status: http.StatusInsufficientStorage, Msg: msg} +} +func errNotImpl(msg string) error { return &apiError{Status: http.StatusNotImplemented, Msg: msg} } + +// mapStorageError 把存储层哨兵错误映射为 apiError(对齐 README 约定): +// ErrNotFound→404、ErrInvalidPath→400、ErrUnavailable→503、ErrNotSupported→501、 +// ErrRangeNotSatisfiable→416、ErrHashMismatch→400(支持 %w 包装判定)。 +func mapStorageError(err error) error { + if err == nil { + return nil + } + switch { + case errors.Is(err, storage.ErrNotFound): + return errNotFound("文件不存在") + case errors.Is(err, storage.ErrInvalidPath): + return errBadRequest("非法文件路径") + case errors.Is(err, storage.ErrUnavailable): + return &apiError{Status: http.StatusServiceUnavailable, Msg: "存储服务不可用,请稍后再试"} + case errors.Is(err, storage.ErrNotSupported): + return errNotImpl("当前存储引擎不支持该操作") + case errors.Is(err, storage.ErrRangeNotSatisfiable): + return &apiError{Status: http.StatusRequestedRangeNotSatisfiable, Msg: "请求范围超出文件大小"} + case errors.Is(err, storage.ErrHashMismatch): + return errBadRequest("分片哈希校验失败,请重新上传") + } + return errInternal("存储操作失败: " + err.Error()) +} + +// respondError 统一错误出口:apiError 按其状态码响应,其余 500。 +func respondError(c *gin.Context, err error) { + var ae *apiError + if !errors.As(err, &ae) { + ae = &apiError{Status: http.StatusInternalServerError, Msg: err.Error()} + } + // 非 2xx 且未标记审计跳过时,兜底给出 result/errorMsg(中间件还会按状态兜底) + middleware.AuditSet(c, func(e *audit.Entry) { + if e.Result == "" { + if ae.Status == 401 || ae.Status == 403 || ae.Status == 423 || ae.Status == 429 || ae.Status == 428 { + e.Result = model.AuditResultDenied + } else { + e.Result = model.AuditResultFailed + } + } + if e.ErrorMsg == "" { + e.ErrorMsg = ae.Msg + } + }) + response.Fail(c, ae.Status, ae.Msg) +} + +// ============ 取件码 / 下载令牌 ============ + +// codeChars 随机字符码字符集(对齐参考 string.ascii_uppercase + digits)。 +const codeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +// generateCode 生成取件码:secret→5 位大写字母+数字;number→5 位数字(10000~99999)。 +// 与参考 get_random_code 一致:冲突时重试(由调用方控制上限)。 +func generateCode(style string) string { + if style == "number" { + n, _ := rand.Int(rand.Reader, big.NewInt(90000)) + return strconv.FormatInt(10000+n.Int64(), 10) + } + b := make([]byte, 5) + for i := range b { + n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(codeChars)))) + b[i] = codeChars[n.Int64()] + } + return string(b) +} + +// ============ 自定义提取码(v3.1,防撞库)============ + +const ( + // pickupCodeMinLen 最小长度:L3 由 4 提升至 5(4 位码空间仅 168 万, + // 多 IP 分布式撞库可在限流窗口内覆盖;5 位起 6000 万空间显著提高成本) + pickupCodeMinLen = 5 + pickupCodeMaxLen = 8 +) + +// validatePickupCode 校验自定义提取码: +// - 长度 5-8 字符(下限保证码空间 36^5≈6000 万起,配合取件错误限流防撞库); +// - 仅字母/数字(排除空格与符号,规避中文场景误输与 URL 转义问题); +// - 空串合法(空 = 使用系统随机码)。 +// - 兼容:仅约束新建码,历史 4 位码仍可正常取件。 +func validatePickupCode(code string) error { + code = strings.TrimSpace(code) + if code == "" { + return nil + } + if l := len(code); l < pickupCodeMinLen || l > pickupCodeMaxLen { + return errBadRequest(fmt.Sprintf("提取码长度须为 %d-%d 位", pickupCodeMinLen, pickupCodeMaxLen)) + } + for _, r := range code { + if !(r >= '0' && r <= '9') && !(r >= 'a' && r <= 'z') && !(r >= 'A' && r <= 'Z') { + return errBadRequest("提取码仅支持字母和数字") + } + } + return nil +} + +// pickCustomCode 解析提取码:自定义非空→查重返回;空→随机码。 +// 占用时 400(不静默改码——用户可能已把链接发出去,静默改码会导致取不到件)。 +func pickCustomCode(ctx context.Context, db *gorm.DB, cfg *config.Config, custom string) (string, error) { + custom = strings.ToUpper(strings.TrimSpace(custom)) + if custom == "" { + return randomCode(ctx, db, cfg) + } + var count int64 + if err := db.WithContext(ctx).Model(&model.FileCodes{}).Where("code = ?", custom).Count(&count).Error; err != nil { + return "", errInternal("取件码查重失败: " + err.Error()) + } + if count > 0 { + return "", errBadRequest("该提取码已被占用,请换一个") + } + return custom, nil +} + +// mapCodeConflict v3.1:分享记录创建失败时,若是自定义码唯一索引冲突(并发兜底, +// pickCustomCode 的预查重未覆盖竞态),转为友好 400;其余错误原样返回。 +func mapCodeConflict(err error) error { + if err == nil { + return nil + } + msg := err.Error() + if strings.Contains(msg, "已被占用") || + strings.Contains(msg, "UNIQUE constraint") || + strings.Contains(msg, "duplicate key") || + strings.Contains(msg, "Duplicate entry") { + return errBadRequest("该提取码已被占用,请换一个") + } + return err +} + +// ============ 站点对外域名(v3.1)============ + +// SiteDomain 站点对外域名规范化(v3.1):空串合法(分享链接用当前访问地址)。 +// 接受 http(s)://host[:port] 或裸 host[:port](自动补 http://,内网场景)。 +// 拒绝路径/查询/片段/用户信息/非 http(s) 协议/非法主机字符(防 javascript: 注入分享链接)。 +var siteDomainHostRe = regexp.MustCompile(`^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*$`) + +func normalizeSiteDomain(raw string) (string, error) { + s := strings.TrimSpace(raw) + if s == "" { + return "", nil + } + s = strings.TrimRight(s, "/") + if !strings.Contains(s, "://") { + s = "http://" + s + } + u, err := url.Parse(s) + if err != nil || u.Host == "" || (u.Scheme != "http" && u.Scheme != "https") { + return "", errBadRequest("站点域名格式:http(s)://主机[:端口],如 https://share.example.com") + } + if (u.Path != "" && u.Path != "/") || u.RawQuery != "" || u.Fragment != "" || u.User != nil { + return "", errBadRequest("站点域名只填主机与端口,不带路径,如 https://share.example.com") + } + if !siteDomainHostRe.MatchString(u.Hostname()) { + return "", errBadRequest("站点域名主机名仅支持字母、数字、点与连字符") + } + if p := u.Port(); p != "" { + if n, perr := strconv.Atoi(p); perr != nil || n < 1 || n > 65535 { + return "", errBadRequest("站点域名端口须为 1-65535") + } + } + return s, nil +} + +// randomCode 生成唯一取件码(查库去重,最多尝试 20 次防止极端碰撞)。 +// style 为空时取配置 code_generate_type(secret/string→secret,其余→number)。 +func randomCode(ctx context.Context, db *gorm.DB, cfg *config.Config) (string, error) { + style := strings.TrimSpace(cfg.GetString("code_generate_type")) + if style == "string" { + style = "secret" + } + if style != "secret" && style != "number" { + style = "number" + } + for i := 0; i < 20; i++ { + code := generateCode(style) + var count int64 + if err := db.WithContext(ctx).Model(&model.FileCodes{}). + Where("code = ?", code).Count(&count).Error; err != nil { + return "", errInternal("取件码生成失败: " + err.Error()) + } + if count == 0 { + return code, nil + } + } + return "", errInternal("取件码生成失败,请重试") +} + +// GetSelectToken 生成下载令牌(L2:HMAC-SHA256 替换拼接哈希,消除拼接歧义; +// 密钥前置为 HMAC key,窗口语义不变): +// HMAC-SHA256(key=secret, msg=code|time_factor),time_factor = unix秒/1000 - offset。 +// offset=0 当前窗口、offset=1 上一窗口——下载端点同时接受两个窗口, +// 避免 ~16.7 分钟窗口边界竞态导致偶发 403。 +func GetSelectToken(code, secret string, offset int) string { + timeFactor := time.Now().Unix()/1000 - int64(offset) + mac := hmac.New(sha256.New, []byte(secret)) + fmt.Fprintf(mac, "%s|%d", code, timeFactor) + return hex.EncodeToString(mac.Sum(nil)) +} + +// VerifySelectToken 常量时间校验下载令牌(当前与上一窗口任一匹配即通过)。 +func VerifySelectToken(code, secret, key string) bool { + return hmac.Equal([]byte(key), []byte(GetSelectToken(code, secret, 0))) || + hmac.Equal([]byte(key), []byte(GetSelectToken(code, secret, 1))) +} + +// ============ 过期策略 ============ + +// expireResult 过期策略解析结果(对齐参考 get_expire_info)。 +type expireResult struct { + ExpiredAt *time.Time // nil 表示永久 + ExpiredCount int // <0 按时间过期;>0 按次数 + UsedCount int +} + +// resolveExpire 校验 expire_style 白名单并计算过期信息。 +// 对齐参考:max_save_seconds>0 时为最长保存上限(超限 403),否则默认 7 天上限; +// v2 需求 ④:style=count 时 expire_value 不得超出 max_save_count(0=不限制,超限 403)。 +func resolveExpire(cfg *config.Config, expireValue int, expireStyle string) (*expireResult, error) { + allowed := cfg.ExpireStyle() + okStyle := false + for _, s := range allowed { + if s == expireStyle { + okStyle = true + break + } + } + if !okStyle { + return nil, errBadRequest("过期时间类型错误") + } + if expireValue <= 0 { + return nil, errBadRequest("过期时间值必须大于 0") + } + + now := time.Now() + res := &expireResult{ExpiredCount: -1, UsedCount: 0} + var expiredAt time.Time + switch expireStyle { + case "day": + expiredAt = now.AddDate(0, 0, expireValue) + case "hour": + expiredAt = now.Add(time.Duration(expireValue) * time.Hour) + case "minute": + expiredAt = now.Add(time.Duration(expireValue) * time.Minute) + case "count": + // 保存次数策略(需求 ④):max_save_count>0 时为可取次数上限,超限 403 + if maxCount := cfg.MaxSaveCount(); maxCount > 0 && expireValue > maxCount { + return nil, errForbidden(fmt.Sprintf("限制次数最多为 %d 次", maxCount)) + } + // 按次数过期:固定保留 1 天时间兜底(对齐参考) + expiredAt = now.AddDate(0, 0, 1) + res.ExpiredCount = expireValue + case "forever": + res.ExpiredAt = nil + res.ExpiredCount = -1 + return res, nil + default: + expiredAt = now.AddDate(0, 0, 1) + } + // 最长保存时间限制 + maxSeconds := cfg.MaxSaveSeconds() + maxDelta := 7 * 24 * time.Hour + if maxSeconds > 0 { + maxDelta = time.Duration(maxSeconds) * time.Second + } + if expiredAt.Sub(now) > maxDelta { + return nil, errForbidden(fmt.Sprintf("限制最长时间为 %s,可换用其他方式", formatDurationCN(maxDelta))) + } + res.ExpiredAt = &expiredAt + return res, nil +} + +// formatDurationCN 把时长格式化为中文描述(对齐参考 max_save_times_desc)。 +func formatDurationCN(d time.Duration) string { + sec := int64(d.Seconds()) + days := sec / 86400 + hours := sec % 86400 / 3600 + minutes := sec % 3600 / 60 + seconds := sec % 60 + var parts []string + if days > 0 { + parts = append(parts, fmt.Sprintf("%d天", days)) + } + if hours > 0 { + parts = append(parts, fmt.Sprintf("%d小时", hours)) + } + if minutes > 0 { + parts = append(parts, fmt.Sprintf("%d分钟", minutes)) + } + if seconds > 0 { + parts = append(parts, fmt.Sprintf("%d秒", seconds)) + } + if len(parts) == 0 { + return "0秒" + } + return strings.Join(parts, "") +} + +// ============ 存储路径 / 容量预留 ============ + +// storeFor v3:按归属引擎取存储实例(空戳/未知名回落当前引擎,兼容历史数据)。 +func (d *Deps) storeFor(engine string) (storage.Storage, error) { + if engine == "" || !storage.ValidEngine(engine) { + return d.Store, nil + } + if engine == d.Store.CurrentName() { + return d.Store, nil + } + return d.Store.EngineOf(engine) +} + +// buildSavePath 生成上传文件的存储相对路径(对齐参考 get_file_path_name): +// [storage_path/]share/data/YYYY/MM/DD//<清理后文件名>。 +func buildSavePath(cfg *config.Config, rawName string, fileUUID string) (dirPath, prefix, suffix, cleanName, savePath string) { + today := time.Now().Format("2006/01/02") + cleanName = storage.SanitizeFileName(rawName) + ext := path.Ext(cleanName) + prefix = strings.TrimSuffix(cleanName, ext) + suffix = ext + base := "share/data/" + today + "/" + fileUUID + if sp := strings.Trim(cfg.GetString("storage_path"), "/"); sp != "" { + base = sp + "/" + base + } + dirPath = base + savePath = base + "/" + cleanName + return +} + +// reserveStorage 原子预留上传容量(对齐参考 quota.reserve_storage): +// storageLimit<=0 或 size=0 时不限制直接返回; +// 通过单条 INSERT...SELECT 条件写入保证 (已用+已预留+本次) <= limit,超限返回 507。 +func reserveStorage(ctx context.Context, db *gorm.DB, cfg *config.Config, token string, size int64, ttl time.Duration) error { + limit := cfg.GetInt64("storageLimit") + if limit <= 0 || size <= 0 { + return nil + } + now := time.Now() + expiresAt := now.Add(ttl) + // 清理同 token 的过期预留 + if err := db.WithContext(ctx). + Where("token = ? AND expires_at <= ?", token, now). + Delete(&model.StorageReservation{}).Error; err != nil { + return errInternal("容量预留失败: " + err.Error()) + } + // 同 token 已有生效预留:大小一致则幂等返回,不一致报冲突(对齐参考 409) + var existing model.StorageReservation + err := db.WithContext(ctx). + Where("token = ? AND expires_at > ?", token, now).First(&existing).Error + if err == nil { + if existing.Size == size { + return nil + } + return errConflict("上传容量预留信息不一致") + } + if !errors.Is(err, gorm.ErrRecordNotFound) { + return errInternal("容量预留失败: " + err.Error()) + } + // 原子条件插入:已用 + 生效预留 + 本次 <= 上限。 + // Info5:Postgres READ COMMITTED 下并发 INSERT..SELECT 可能同时读到相同快照 + // 而轻微超额记账,故包事务并用事务级 advisory lock 串行化配额判定 + // (SQLite 写本身串行,无需加锁)。 + lockFn := func(tx *gorm.DB) error { + if tx.Dialector.Name() == config.DBDriverPostgres { + return tx.Exec(`SELECT pg_advisory_xact_lock(?)`, quotaLockKey).Error + } + return nil + } + var insertErr error + txErr := db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := lockFn(tx); err != nil { + return err + } + res := tx.Exec(` + INSERT INTO storage_reservations (token, size, expires_at) + SELECT ?, ?, ? + WHERE ( + COALESCE((SELECT COALESCE(SUM(size),0) FROM file_codes), 0) + + COALESCE((SELECT COALESCE(SUM(size),0) FROM storage_reservations WHERE expires_at > ?), 0) + + ? + ) <= ?`, + token, size, expiresAt, now, size, limit) + insertErr = res.Error + if res.Error != nil { + return res.Error // 触发回滚(同 token 冲突分支在外层处理) + } + if res.RowsAffected == 0 { + return errInsufficient("存储空间已达到管理员设置的容量上限") + } + return nil + }) + if txErr != nil { + // 并发冲突回退:检查是否已有同 token 同大小的生效预留(对齐参考并发分支) + var cnt int64 + _ = db.WithContext(ctx).Model(&model.StorageReservation{}). + Where("token = ? AND size = ? AND expires_at > ?", token, size, now). + Count(&cnt).Error + if cnt > 0 { + return nil + } + var ins *apiError + if errors.As(txErr, &ins) && ins.Status == http.StatusInsufficientStorage { + return txErr // 507:真实容量不足 + } + if insertErr != nil && errors.Is(insertErr, txErr) { + return errInternal("容量预留失败: " + insertErr.Error()) + } + return errInternal("容量预留失败: " + txErr.Error()) + } + return nil +} + +// quotaLockKey Postgres advisory lock 键(配额判定的事务级串行化)。 +const quotaLockKey int64 = 0x46434251 // "FCBQ" + +// releaseStorage 释放容量预留(幂等)。 +func releaseStorage(ctx context.Context, db *gorm.DB, token string) { + _ = db.WithContext(ctx).Where("token = ?", token).Delete(&model.StorageReservation{}).Error +} + +// ============ 文件类型校验(对齐 apps/base/file_validation.py)============ + +// fileKind 已知文件类型:扩展名 / MIME / magic bytes。 +type fileKind struct { + name string + extensions []string + mimes []string + signatures [][]byte +} + +var fileKinds = []fileKind{ + {"png", []string{".png"}, []string{"image/png"}, [][]byte{{0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n'}}}, + {"jpg", []string{".jpg", ".jpeg"}, []string{"image/jpeg"}, [][]byte{{0xff, 0xd8, 0xff}}}, + {"gif", []string{".gif"}, []string{"image/gif"}, [][]byte{[]byte("GIF87a"), []byte("GIF89a")}}, + {"webp", []string{".webp"}, []string{"image/webp"}, nil}, + {"bmp", []string{".bmp"}, []string{"image/bmp", "image/x-ms-bmp"}, [][]byte{[]byte("BM")}}, + {"pdf", []string{".pdf"}, []string{"application/pdf"}, [][]byte{[]byte("%PDF")}}, + {"zip", []string{".zip", ".docx", ".xlsx", ".pptx", ".apk", ".jar"}, + []string{"application/zip", "application/x-zip-compressed", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/java-archive", "application/vnd.android.package-archive"}, + [][]byte{[]byte("PK\x03\x04"), []byte("PK\x05\x06"), []byte("PK\x07\x08")}}, + {"rar", []string{".rar"}, []string{"application/x-rar-compressed", "application/vnd.rar"}, + [][]byte{[]byte("Rar!\x1a\x07\x00"), []byte("Rar!\x1a\x07\x01\x00")}}, + {"7z", []string{".7z"}, []string{"application/x-7z-compressed"}, [][]byte{{0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c}}}, + {"gz", []string{".gz", ".tgz"}, []string{"application/gzip", "application/x-gzip"}, [][]byte{{0x1f, 0x8b}}}, + {"mp3", []string{".mp3"}, []string{"audio/mpeg"}, [][]byte{[]byte("ID3"), {0xff, 0xfb}, {0xff, 0xf3}, {0xff, 0xf2}}}, + {"mp4", []string{".mp4", ".m4a", ".mov"}, []string{"video/mp4", "audio/mp4", "video/quicktime"}, nil}, + {"exe", []string{".exe", ".dll", ".sys"}, []string{"application/x-msdownload", "application/x-dosexec"}, [][]byte{[]byte("MZ")}}, + {"elf", []string{".elf", ".so", ".o"}, []string{"application/x-executable"}, [][]byte{{0x7f, 'E', 'L', 'F'}}}, +} + +// knownExtensions 全部已知扩展名集合。 +var knownExtensions = func() map[string]bool { + m := map[string]bool{} + for _, k := range fileKinds { + for _, ext := range k.extensions { + m[ext] = true + } + } + return m +}() + +// isTypeAllowed 判断文件是否在 allowed_file_types 白名单内("*"/*/* 放行全部)。 +func isTypeAllowed(cfg *config.Config, fileName, contentType string) bool { + allowed := cfg.AllowedFileTypes() + if len(allowed) == 0 { + return true + } + name := strings.ToLower(strings.TrimSpace(fileName)) + ct := strings.ToLower(strings.TrimSpace(contentType)) + for _, rule := range allowed { + rule = strings.ToLower(strings.TrimSpace(rule)) + switch { + case rule == "*" || rule == "*/*": + return true + case strings.Contains(rule, "/"): + if ok, _ := path.Match(rule, ct); ok { + return true + } + default: + if !strings.HasPrefix(rule, ".") { + rule = "." + rule + } + if strings.HasSuffix(name, rule) { + return true + } + } + } + return false +} + +// detectFileKind 按文件头识别类型(对齐参考:RIFF/WEBP、ftyp/mp4 与前缀签名表)。 +func detectFileKind(header []byte) *fileKind { + if len(header) == 0 { + return nil + } + if len(header) >= 12 && string(header[:4]) == "RIFF" && string(header[8:12]) == "WEBP" { + for i := range fileKinds { + if fileKinds[i].name == "webp" { + return &fileKinds[i] + } + } + } + if len(header) >= 12 && string(header[4:8]) == "ftyp" { + for i := range fileKinds { + if fileKinds[i].name == "mp4" { + return &fileKinds[i] + } + } + } + var best *fileKind + bestLen := 0 + for i := range fileKinds { + for _, sig := range fileKinds[i].signatures { + if len(sig) > 0 && len(header) >= len(sig) && string(header[:len(sig)]) == string(sig) { + if len(sig) > bestLen { + bestLen = len(sig) + best = &fileKinds[i] + } + } + } + } + return best +} + +// validateFileMagic 白名单 + magic bytes 防伪造(对齐参考 validate_file_magic)。 +// header 为文件前 64 字节,可为空(空则只校验白名单)。 +func validateFileMagic(cfg *config.Config, fileName, contentType string, header []byte) error { + if !isTypeAllowed(cfg, fileName, contentType) { + return errForbidden("不允许上传该类型文件") + } + if len(header) == 0 { + return nil + } + ext := strings.ToLower(path.Ext(fileName)) + ct := strings.ToLower(strings.TrimSpace(contentType)) + detected := detectFileKind(header) + + if knownExtensions[ext] { + if detected == nil { + return errForbidden("文件内容与扩展名不匹配,疑似伪造类型") + } + matched := false + for _, e := range detected.extensions { + if e == ext { + matched = true + break + } + } + if !matched { + return errForbidden("文件内容与扩展名不匹配,疑似伪造类型") + } + } + if ct != "" { + for _, k := range fileKinds { + for _, m := range k.mimes { + if m == ct { + // 声明了已知 MIME:内容必须匹配 + if detected == nil { + return errForbidden("文件内容与 Content-Type 不匹配,疑似伪造类型") + } + matched := false + for _, m2 := range detected.mimes { + if m2 == ct { + matched = true + break + } + } + if !matched { + return errForbidden("文件内容与 Content-Type 不匹配,疑似伪造类型") + } + break + } + } + } + } + return nil +} + +// readMultipartHeader 读取上传文件前 n 字节并 seek 回起点(用于 magic 校验)。 +func readMultipartHeader(f multipart.File, n int64) []byte { + if f == nil { + return nil + } + buf := make([]byte, n) + nread, _ := f.Read(buf) + _, _ = f.Seek(0, 0) + if nread <= 0 { + return nil + } + return buf[:nread] +} + +// ============ 杂项 ============ + +// humanSize 把字节数转成人类可读描述(B/KB/MB/GB 自适应; +// v2 需求④:max_file_size 支持子 MB 上限,固定 MB 格式会显示 0.00 MB)。 +func humanSize(n int64) string { + const kb, mb, gb = int64(1024), int64(1024 * 1024), int64(1024 * 1024 * 1024) + switch { + case n >= gb: + return fmt.Sprintf("%.2f GB", float64(n)/float64(gb)) + case n >= mb: + return fmt.Sprintf("%.2f MB", float64(n)/float64(mb)) + case n >= kb: + return fmt.Sprintf("%.2f KB", float64(n)/float64(kb)) + default: + return fmt.Sprintf("%d B", n) + } +} + +// contentDisposition 生成 RFC 5987 附件头(对齐参考 filename*=UTF-8” 格式)。 +func contentDisposition(name string) string { + quoted := urlPathEscape(name) + return "attachment; filename*=UTF-8''" + quoted +} + +// urlPathEscape RFC 5987 风格百分号编码(等价 urllib.parse.quote(safe=”))。 +func urlPathEscape(s string) string { + var b strings.Builder + for _, r := range []byte(s) { + if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || + r == '-' || r == '_' || r == '.' || r == '~' { + b.WriteByte(r) + } else { + fmt.Fprintf(&b, "%%%02X", r) + } + } + return b.String() +} + +// parseISOTime 解析 ISO 8601 / RFC3339 / 日期时间字符串,失败返回错误。 +func parseISOTime(s string) (time.Time, error) { + s = strings.TrimSpace(s) + if s == "" { + return time.Time{}, errors.New("空时间") + } + layouts := []string{ + time.RFC3339Nano, time.RFC3339, + "2006-01-02T15:04:05", "2006-01-02 15:04:05", "2006-01-02", + } + for _, layout := range layouts { + if t, err := time.Parse(layout, s); err == nil { + return t, nil + } + } + return time.Time{}, fmt.Errorf("时间格式错误: %s", s) +} + +// requireUploadLimit 上传限流入口检查(进入即校验,超限 423;成功后由 handler 显式 Add)。 +// 对齐参考:FastAPI Depends(ip_limit["upload"]) 在进入时 check。 +func requireUploadLimit(c *gin.Context, limiter *middleware.RateLimiter) bool { + if allowed, _ := limiter.Check(c, middleware.LimitUpload); !allowed { + response.Fail(c, http.StatusLocked, "请求次数过多,请稍后再试") + return false + } + return true +} + +// bindJSONOrForm 兼容 JSON 与表单请求体绑定到结构体。 +func bindJSONOrForm(c *gin.Context, obj any) error { + ct := c.GetHeader("Content-Type") + if strings.Contains(ct, "application/json") { + if err := c.ShouldBindJSON(obj); err != nil { + return errBadRequest("请求体格式错误: " + err.Error()) + } + return nil + } + // v3.1.1 兼容归一化:旧前端 bundle(fetch 字符串 body 默认 text/plain)发的 + // 是 text/plain + urlencoded 格式。此类请求改写 Content-Type 后走表单绑定, + // 否则 ShouldBind 对 text/plain 不解析,非空字段全部丢失。 + base := ct + if i := strings.IndexByte(ct, ';'); i >= 0 { + base = ct[:i] + } + if strings.EqualFold(strings.TrimSpace(base), "text/plain") && + c.Request != nil && c.Request.Body != nil { + if raw, err := io.ReadAll(c.Request.Body); err == nil { + trimmed := bytes.TrimSpace(raw) + // JSON 形态(无头/误标 text/plain):改写后按 JSON 绑定(须先于 ParseQuery 判断, + // 否则形如 {"a":1} 的 JSON 会被 ParseQuery 误判为单键 urlencoded) + if len(trimmed) > 0 && trimmed[0] == '{' { + c.Request.Header.Set("Content-Type", "application/json") + c.Request.Body = io.NopCloser(bytes.NewReader(raw)) + return c.ShouldBindJSON(obj) + } + if vals, perr := url.ParseQuery(string(raw)); perr == nil && len(vals) > 0 { + // urlencoded 形态:改写 Content-Type 走表单绑定 + c.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded") + c.Request.Body = io.NopCloser(bytes.NewReader(raw)) + return c.ShouldBind(obj) + } + // 其他形态:还原 body 让 ShouldBind 按原样处理 + c.Request.Body = io.NopCloser(bytes.NewReader(raw)) + } + } + if err := c.ShouldBind(obj); err != nil { + return errBadRequest("请求体格式错误: " + err.Error()) + } + return nil +} + +// auditUploadEntry 填充上传类审计业务字段的便捷函数。 +func auditUploadEntry(c *gin.Context, code, name string, size, transferred int64) { + middleware.AuditSet(c, func(e *audit.Entry) { + e.FileCode = code + e.FileName = name + e.SizeBytes = size + e.TransferredBytes = transferred + }) +} + +// auditRecordSuccess / auditRecordFailed 显式落库便捷函数。 +func auditRecordSuccess(c *gin.Context, svc *audit.Service) { + middleware.AuditRecordRequest(c, svc, model.AuditResultSuccess, "") +} + +func auditRecordFailed(c *gin.Context, svc *audit.Service, msg string) { + middleware.AuditRecordRequest(c, svc, model.AuditResultFailed, msg) +} + +// uuidHex 生成 32 位十六进制随机串(对齐参考 uuid4().hex)。 +func uuidHex() string { + b := make([]byte, 16) + _, _ = rand.Read(b) + // 设置版本号与变体位以保持 uuid4 兼容格式 + b[6] = (b[6] & 0x0f) | 0x40 + b[8] = (b[8] & 0x3f) | 0x80 + return hex.EncodeToString(b) +} + +// uuidCanonical 生成带连字符的 UUID 字符串(upload_id 用)。 +func uuidCanonical() string { + h := uuidHex() + return h[0:8] + "-" + h[8:12] + "-" + h[12:16] + "-" + h[16:20] + "-" + h[20:32] +} diff --git a/server/internal/api/helpers_test.go b/server/internal/api/helpers_test.go new file mode 100644 index 0000000..dd835c3 --- /dev/null +++ b/server/internal/api/helpers_test.go @@ -0,0 +1,264 @@ +package api + +import ( + "errors" + "fmt" + "testing" + "time" + + "filecodebox/internal/config" + "filecodebox/internal/storage" +) + +// newTestConfig 构造测试配置(defaults 基线,无 KV 覆盖;需求 ⑧ 默认 sqlite,无需真实数据库)。 +func newTestConfig(t *testing.T) *config.Config { + t.Helper() + t.Setenv("FCB_DB_DRIVER", "sqlite") + t.Setenv("FCB_DB_DSN", "") + cfg, err := config.New() + if err != nil { + t.Fatalf("config.New 失败: %v", err) + } + return cfg +} + +// TestGetSelectTokenWindow 验证下载令牌的窗口号逻辑(对齐参考 get_select_token)。 +func TestGetSelectTokenWindow(t *testing.T) { + code := "AB12C" + secret := "test-secret" + + tok0 := GetSelectToken(code, secret, 0) + tok1 := GetSelectToken(code, secret, 1) + + if tok0 == "" || tok1 == "" { + t.Fatal("令牌不应为空") + } + // 同一窗口内 offset=0 的两次生成必须一致(确定性问题) + if tok0 != GetSelectToken(code, secret, 0) { + t.Fatal("同窗口令牌应确定一致") + } + // 不同 offset 的令牌必然不同(窗口号不同) + if tok0 == tok1 { + t.Fatal("offset=0 与 offset=1 的令牌应不同") + } + // 不同 code / secret 的令牌不同 + if tok0 == GetSelectToken("ZZ999", secret, 0) { + t.Fatal("不同取件码的令牌应不同") + } + if tok0 == GetSelectToken(code, "other-secret", 0) { + t.Fatal("不同密钥的令牌应不同") + } + // 令牌为 64 位十六进制(sha256 hex) + if len(tok0) != 64 { + t.Fatalf("令牌长度应为 64,实际 %d", len(tok0)) + } + // 窗口号公式:unix/1000 - offset(秒级窗口约 16.7 分钟) + now := time.Now().Unix() + if now/1000 == (now+1100)/1000 { + // 仅当测试跨越窗口边界才跳过该断言(罕见,容忍) + t.Log("测试跨越窗口边界,跳过窗口公式断言") + } +} + +// TestMapStorageError 验证存储哨兵错误→HTTP 状态映射表。 +func TestMapStorageError(t *testing.T) { + cases := []struct { + name string + err error + expect int + }{ + {"NotFound 直返", storage.ErrNotFound, 404}, + {"NotFound 包装", fmt.Errorf("引擎内层: %w", storage.ErrNotFound), 404}, + {"InvalidPath", storage.ErrInvalidPath, 400}, + {"InvalidPath 包装", fmt.Errorf("webdav: %w", storage.ErrInvalidPath), 400}, + {"Unavailable", storage.ErrUnavailable, 503}, + {"Unavailable 包装", fmt.Errorf("s3: %w", storage.ErrUnavailable), 503}, + {"NotSupported", storage.ErrNotSupported, 501}, + {"NotSupported 包装", fmt.Errorf("local: %w", storage.ErrNotSupported), 501}, + {"RangeNotSatisfiable", storage.ErrRangeNotSatisfiable, 416}, + {"HashMismatch", storage.ErrHashMismatch, 400}, + {"未知错误", errors.New("其他错误"), 500}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + mapped := mapStorageError(tc.err) + var ae *apiError + if !errors.As(mapped, &ae) { + t.Fatalf("应映射为 apiError,得到 %T", mapped) + } + if ae.Status != tc.expect { + t.Fatalf("状态码应为 %d,实际 %d", tc.expect, ae.Status) + } + }) + } +} + +// TestResolveExpire 验证过期策略解析(白名单/上限/各 style)。 +func TestResolveExpire(t *testing.T) { + cfg := newTestConfig(t) + // 非法 style + if _, err := resolveExpire(cfg, 1, "year"); err == nil { + t.Fatal("非白名单 style 应报错") + } + // 非法 value + if _, err := resolveExpire(cfg, 0, "day"); err == nil { + t.Fatal("expire_value<=0 应报错") + } + // day:7 天内合法 + res, err := resolveExpire(cfg, 3, "day") + if err != nil { + t.Fatalf("3 天应合法: %v", err) + } + if res.ExpiredAt == nil || res.ExpiredCount != -1 { + t.Fatal("day 类型应有 expired_at 且 expired_count=-1") + } + // 超过 7 天上限 + if _, err := resolveExpire(cfg, 30, "day"); err == nil { + t.Fatal("超过 7 天上限应报错") + } + // count:按次数 + res, err = resolveExpire(cfg, 5, "count") + if err != nil { + t.Fatalf("count 应合法: %v", err) + } + if res.ExpiredCount != 5 { + t.Fatalf("count 类型 expired_count 应为 5,实际 %d", res.ExpiredCount) + } + // forever:永久 + res, err = resolveExpire(cfg, 1, "forever") + if err != nil { + t.Fatalf("forever 应合法: %v", err) + } + if res.ExpiredAt != nil || res.ExpiredCount != -1 { + t.Fatal("forever 应为 expired_at=nil 且 expired_count=-1") + } +} + +// TestGenerateCode 验证取件码格式。 +func TestGenerateCode(t *testing.T) { + for i := 0; i < 50; i++ { + num := generateCode("number") + if len(num) != 5 { + t.Fatalf("数字码应为 5 位,实际 %q", num) + } + for _, ch := range num { + if ch < '0' || ch > '9' { + t.Fatalf("数字码含非数字字符: %q", num) + } + } + secret := generateCode("secret") + if len(secret) != 5 { + t.Fatalf("字符码应为 5 位,实际 %q", secret) + } + } +} + +// TestParseRangeHeader 验证 Range 头解析(对齐 HTTP 语义)。 +func TestParseRangeHeader(t *testing.T) { + // 全量(无 Range) + if parseRangeHeader("", 1000) != nil { + t.Fatal("无 Range 头应返回 nil") + } + // 标准区间 + r := parseRangeHeader("bytes=0-99", 1000) + if r == nil || r.Start != 0 || r.End != 99 { + t.Fatalf("bytes=0-99 解析错误: %+v", r) + } + // 开区间到末尾 + r = parseRangeHeader("bytes=500-", 1000) + if r == nil || r.Start != 500 || r.End != -1 { + t.Fatalf("bytes=500- 解析错误: %+v", r) + } + // 后缀区间(最后 100 字节) + r = parseRangeHeader("bytes=-100", 1000) + if r == nil || r.Start != 900 || r.End != -1 { + t.Fatalf("bytes=-100 解析错误: %+v", r) + } + // 后缀超长:截断到全文件 + r = parseRangeHeader("bytes=-5000", 1000) + if r == nil || r.Start != 0 { + t.Fatalf("bytes=-5000 应从头开始: %+v", r) + } + // 多区间不支持→回退全量 + if parseRangeHeader("bytes=0-1,5-6", 1000) != nil { + t.Fatal("多区间应返回 nil(回退全量)") + } + // 非法格式 + if parseRangeHeader("items=0-1", 1000) != nil { + t.Fatal("非 bytes 单位应返回 nil") + } + if parseRangeHeader("bytes=abc-", 1000) != nil { + t.Fatal("非法数字应返回 nil") + } +} + +// TestParseISOTime 验证时间解析的多格式兼容。 +func TestParseISOTime(t *testing.T) { + valid := []string{ + "2025-01-01T00:00:00Z", + "2025-01-01T08:00:00+08:00", + "2025-01-01 08:00:00", + "2025-01-01", + } + for _, s := range valid { + if _, err := parseISOTime(s); err != nil { + t.Fatalf("%q 应解析成功: %v", s, err) + } + } + if _, err := parseISOTime("not-a-time"); err == nil { + t.Fatal("非法时间应报错") + } + if _, err := parseISOTime(""); err == nil { + t.Fatal("空串应报错") + } +} + +// TestFormatDurationCN 验证中文时长描述。 +func TestFormatDurationCN(t *testing.T) { + cases := []struct { + d time.Duration + expect string + }{ + {7 * 24 * time.Hour, "7天"}, + {90 * time.Minute, "1小时30分钟"}, + {45 * time.Second, "45秒"}, + } + for _, tc := range cases { + if got := formatDurationCN(tc.d); got != tc.expect { + t.Fatalf("formatDurationCN(%v)=%q,期望 %q", tc.d, got, tc.expect) + } + } +} + +// TestFileMagicValidation 验证 magic bytes 防伪造。 +func TestFileMagicValidation(t *testing.T) { + cfg := newTestConfig(t) + // 白名单 * 全放行 + if err := validateFileMagic(cfg, "a.txt", "", nil); err != nil { + t.Fatalf("白名单 * 应放行: %v", err) + } + // PNG 内容 + .exe 扩展名 → 拒绝(伪造) + if err := validateFileMagic(cfg, "evil.exe", "", []byte{0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n'}); err == nil { + t.Fatal("PNG 内容伪装 exe 应拒绝") + } + // PNG 内容 + .png 扩展名 → 通过 + if err := validateFileMagic(cfg, "ok.png", "image/png", []byte{0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n'}); err != nil { + t.Fatalf("真 PNG 应通过: %v", err) + } + // 文本内容 + .png 扩展名 → 拒绝 + if err := validateFileMagic(cfg, "fake.png", "", []byte("hello world, this is text")); err == nil { + t.Fatal("文本伪装 png 应拒绝") + } +} + +// TestSanitizePathBuild 验证存储路径构造不含穿越。 +func TestSanitizePathBuild(t *testing.T) { + cfg := newTestConfig(t) + _, _, _, clean, savePath := buildSavePath(cfg, "../../etc/passwd", "uuid-123") + if clean != "etc_passwd" && clean != "passwd" { + t.Logf("清理后的文件名: %q", clean) + } + if _, ok := storage.SanitizePath(savePath); !ok { + t.Fatalf("构造的 savePath 应通过安全校验: %q", savePath) + } +} diff --git a/server/internal/api/policy.go b/server/internal/api/policy.go new file mode 100644 index 0000000..5fbb215 --- /dev/null +++ b/server/internal/api/policy.go @@ -0,0 +1,45 @@ +// policy.go — v2 上传策略统一读取与校验(需求 ④⑩)。 +// +// 管理端在后台设置页修改策略(settings KV,t1 schema)后,上传链路 +// (share/file、chunk、presign)每次请求实时读取当前策略并动态校验: +// - 大小上限:max_file_size(0=回落 uploadSize,语义见 config.MaxFileSize); +// - 类型白名单:allowed_file_types("*" 不限制),由 validateFileMagic 统一执行; +// - 保存策略:expire_style 白名单 + max_save_seconds 时间上限 + max_save_count +// 次数上限,统一在 resolveExpire(helpers.go)执行。 +// +// 超限返回 403(超出策略限制)/400(参数非法),错误信息为中文。 +package api + +import ( + "fmt" +) + +// UploadPolicy 当前生效的上传策略快照(每次上传请求实时读取,管理端改动立即生效)。 +type UploadPolicy struct { + MaxFileSize int64 // 单文件大小上限(字节),0=不限制 + AllowedTypes []string // 类型白名单,"*" 不限制 + ExpireStyles []string // 允许的过期方式白名单 + MaxSaveSeconds int64 // 最长保存秒数,0=不限制(默认 7 天兜底) + MaxSaveCount int // 单次分享最大可取次数上限,0=不限制 +} + +// CurrentUploadPolicy 读取当前上传策略快照。 +// 上传页亦通过 GET /api/v1/config 的 policy 字段读取同一组值做动态渲染。 +func (d *Deps) CurrentUploadPolicy() UploadPolicy { + cfg := d.Cfg + return UploadPolicy{ + MaxFileSize: cfg.MaxFileSize(), + AllowedTypes: cfg.AllowedFileTypes(), + ExpireStyles: cfg.ExpireStyle(), + MaxSaveSeconds: cfg.MaxSaveSeconds(), + MaxSaveCount: cfg.MaxSaveCount(), + } +} + +// CheckSize 校验单文件大小是否超出策略上限(超出返回 403,文案对齐参考实现)。 +func (p UploadPolicy) CheckSize(size int64) error { + if p.MaxFileSize > 0 && size > p.MaxFileSize { + return errForbidden(fmt.Sprintf("大小超过限制,最大为%s", humanSize(p.MaxFileSize))) + } + return nil +} diff --git a/server/internal/api/policy_test.go b/server/internal/api/policy_test.go new file mode 100644 index 0000000..332ebbe --- /dev/null +++ b/server/internal/api/policy_test.go @@ -0,0 +1,571 @@ +package api + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "mime/multipart" + "net/http" + "net/http/httptest" + "path/filepath" + "strings" + "testing" + + "github.com/gin-gonic/gin" + + "filecodebox/internal/audit" + "filecodebox/internal/cache" + "filecodebox/internal/config" + "filecodebox/internal/database" + "filecodebox/internal/middleware" + "filecodebox/internal/settings" + "filecodebox/internal/storage" +) + +// ============ 测试环境装配(真实 sqlite + 内存缓存 + 本地存储)============ + +// newPolicyTestDeps 构造带真实依赖的 Deps:sqlite 文件库(t.TempDir)、 +// 本地存储引擎、内存缓存限流器与审计服务(需求 ⑧ 默认形态)。 +func newPolicyTestDeps(t *testing.T) *Deps { + t.Helper() + gin.SetMode(gin.TestMode) + dir := t.TempDir() + t.Setenv("FCB_DB_DRIVER", "sqlite") + t.Setenv("FCB_DB_DSN", filepath.Join(dir, "test.db")) + cfg, err := config.New() + if err != nil { + t.Fatalf("config.New: %v", err) + } + ctx := context.Background() + db, err := database.Open(ctx, database.Options{Driver: config.DBDriverSQLite, DSN: filepath.Join(dir, "test.db")}) + if err != nil { + t.Fatalf("database.Open: %v", err) + } + t.Cleanup(func() { _ = database.Close(db) }) + if err := database.Migrate(ctx, db); err != nil { + t.Fatalf("database.Migrate: %v", err) + } + mgr, err := settings.NewManager(ctx, db, cfg) + if err != nil { + t.Fatalf("settings.NewManager: %v", err) + } + store, err := storage.NewLocalStorage(filepath.Join(dir, "storage")) + if err != nil { + t.Fatalf("storage.NewLocalStorage: %v", err) + } + // v3:包装为 Manager(build 直接返回 local 实例,测试无需真实多引擎) + storeMgr := storage.NewManager("local", store, func(string) (storage.Storage, error) { + return storage.NewLocalStorage(filepath.Join(dir, "storage")) + }) + return &Deps{ + DB: db, + Cfg: cfg, + Mgr: mgr, + AuditSvc: audit.NewService(audit.NewDBSink(db)), + Limiter: middleware.NewRateLimiter(cache.NewMemory(), nil), + Store: storeMgr, + Version: "test", + } +} + +// ============ 请求构造辅助 ============ + +// invoke 以给定请求调用 handler 并返回响应。 +func invoke(handler gin.HandlerFunc, req *http.Request) *httptest.ResponseRecorder { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = req + handler(c) + return w +} + +// patchConfig 以 JSON 调用 PATCH /admin/config/update。 +func patchConfig(d *Deps, patch map[string]any) *httptest.ResponseRecorder { + raw, _ := json.Marshal(patch) + req := httptest.NewRequest(http.MethodPatch, "/admin/config/update", bytes.NewReader(raw)) + req.Header.Set("Content-Type", "application/json") + return invoke(d.adminConfigUpdate, req) +} + +// getConfig 调用 GET /admin/config/get。 +func getConfig(d *Deps) *httptest.ResponseRecorder { + return invoke(d.adminConfigGet, httptest.NewRequest(http.MethodGet, "/admin/config/get", nil)) +} + +// getPublicConfig 调用 GET /api/v1/config。 +func getPublicConfig(d *Deps) *httptest.ResponseRecorder { + return invoke(d.publicConfig, httptest.NewRequest(http.MethodGet, "/api/v1/config", nil)) +} + +// uploadFile 以 multipart 表单调用 POST /share/file。 +func uploadFile(d *Deps, name string, content []byte, fields map[string]string) *httptest.ResponseRecorder { + body := &bytes.Buffer{} + mw := multipart.NewWriter(body) + fw, err := mw.CreateFormFile("file", name) + if err != nil { + panic(err) + } + _, _ = fw.Write(content) + for k, v := range fields { + _ = mw.WriteField(k, v) + } + _ = mw.Close() + req := httptest.NewRequest(http.MethodPost, "/share/file", body) + req.Header.Set("Content-Type", mw.FormDataContentType()) + return invoke(d.shareFile, req) +} + +// chunkInitJSON 以 JSON 调用 POST /chunk/upload/init。 +func chunkInitJSON(d *Deps, payload string) *httptest.ResponseRecorder { + req := httptest.NewRequest(http.MethodPost, "/chunk/upload/init", strings.NewReader(payload)) + req.Header.Set("Content-Type", "application/json") + return invoke(d.chunkInit, req) +} + +// respBody 解析统一响应体。 +func respBody(t *testing.T, w *httptest.ResponseRecorder) (code int, data map[string]any) { + t.Helper() + var body struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data map[string]any `json:"data"` + } + if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil { + t.Fatalf("响应解析失败: %v; body=%s", err, w.Body.String()) + } + return body.Code, body.Data +} + +// pngMagic 最小合法 PNG 头(magic 校验可识别)。 +var pngMagic = []byte{0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n'} + +// ============ ① 公开 config:v2 展示与策略字段下发 ============ + +// TestPublicConfigV2Fields 验证 /api/v1/config 下发背景/页脚/备案/通知与策略范围, +// 且响应不包含任何敏感键(admin_token/jwt_secret)。 +func TestPublicConfigV2Fields(t *testing.T) { + d := newPolicyTestDeps(t) + // 管理端先设置 v2 展示字段 + if w := patchConfig(d, map[string]any{ + "background_url": "https://cdn.example.com/bg.png", + "footer_text": "自定义页脚内容", + "footer_beian": "京ICP备2024xxxxxx号-1", + "notify_enabled": 0, + "max_save_count": 5, + }); w.Code != 200 { + t.Fatalf("patchConfig 失败: %d %s", w.Code, w.Body.String()) + } + + w := getPublicConfig(d) + code, data := respBody(t, w) + if code != 200 { + t.Fatalf("publicConfig code=%d", code) + } + cfgMap, _ := data["config"].(map[string]any) + if cfgMap == nil { + t.Fatal("响应缺少 config 对象") + } + for key, want := range map[string]any{ + "background_url": "https://cdn.example.com/bg.png", + "footer_text": "自定义页脚内容", + "footer_beian": "京ICP备2024xxxxxx号-1", + "notify_enabled": float64(0), + "notify_title": "系统通知", + } { + if got := cfgMap[key]; got != want { + t.Fatalf("config.%s = %v, 期望 %v", key, got, want) + } + } + // 策略范围 + if _, ok := cfgMap["max_file_size"]; !ok { + t.Fatal("config 缺少 max_file_size(存储策略)") + } + if _, ok := cfgMap["max_save_seconds"]; !ok { + t.Fatal("config 缺少 max_save_seconds(保存时间策略)") + } + if got := cfgMap["max_save_count"]; got != float64(5) { + t.Fatalf("config.max_save_count = %v, 期望 5", got) + } + if _, ok := cfgMap["allowedFileTypes"]; !ok { + t.Fatal("config 缺少 allowedFileTypes") + } + if _, ok := cfgMap["expireStyle"]; !ok { + t.Fatal("config 缺少 expireStyle") + } + if _, ok := cfgMap["uploadSize"]; !ok { + t.Fatal("config 缺少 uploadSize") + } + // 敏感键绝不下发 + raw := w.Body.String() + if strings.Contains(raw, "admin_token") || strings.Contains(raw, "jwt_secret") { + t.Fatal("公开 config 响应包含敏感键") + } +} + +// ============ ② 管理端 get/update:v2 键全链路 + 类型范围校验 ============ + +// TestAdminConfigV2RoundTrip 验证 v2 新键 update → get → public 的往返, +// 且 admin_token 屏蔽、jwt_secret 不下发。 +func TestAdminConfigV2RoundTrip(t *testing.T) { + d := newPolicyTestDeps(t) + patch := map[string]any{ + "background_url": "https://cdn.example.com/bg.png", + "footer_text": "页脚 HTML 片段", + "footer_beian": "京ICP备20240001号", + "notify_enabled": 0, + "max_save_count": 20, + "max_file_size": 5242880, + "max_save_seconds": 86400, + } + w := patchConfig(d, patch) + if w.Code != 200 { + t.Fatalf("update 失败: %d %s", w.Code, w.Body.String()) + } + + // admin config get:新键可见 + 敏感键屏蔽 + w = getConfig(d) + code, data := respBody(t, w) + if code != 200 { + t.Fatalf("get code=%d", code) + } + for key, want := range map[string]any{ + "background_url": "https://cdn.example.com/bg.png", + "footer_text": "页脚 HTML 片段", + "footer_beian": "京ICP备20240001号", + "notify_enabled": float64(0), + "max_save_count": float64(20), + "max_file_size": float64(5242880), + "max_save_seconds": float64(86400), + } { + if got := data[key]; got != want { + t.Fatalf("admin get %s = %v, 期望 %v", key, got, want) + } + } + // v1 既有设计:admin_token 不在 configKeys 白名单(响应中不存在即屏蔽); + // 兼容两种形态:键缺失或空串均算通过 + if got, present := data["admin_token"]; present && got != "" { + t.Fatalf("admin_token 应屏蔽(缺失或空串),实际 %v", got) + } + rawGet := w.Body.String() + if strings.Contains(rawGet, `"jwt_secret"`) { + t.Fatal("admin get 不应下发 jwt_secret") + } + + // public config 立即反映(改策略 → 公开 config 即时更新) + w = getPublicConfig(d) + _, data = respBody(t, w) + cfgMap := data["config"].(map[string]any) + if got := cfgMap["max_file_size"]; got != float64(5242880) { + t.Fatalf("public max_file_size = %v, 期望 5242880", got) + } + if got := cfgMap["footer_beian"]; got != "京ICP备20240001号" { + t.Fatalf("public footer_beian = %v", got) + } +} + +// TestAdminConfigV2Validation 验证新键的类型与范围校验(400 + 中文错误)。 +func TestAdminConfigV2Validation(t *testing.T) { + d := newPolicyTestDeps(t) + cases := []struct { + name string + patch map[string]any + }{ + {"max_file_size 负数", map[string]any{"max_file_size": -1}}, + {"max_file_size 超上限", map[string]any{"max_file_size": config.MaxFileSizeMax + 1}}, + {"max_save_count 超上限", map[string]any{"max_save_count": config.MaxSaveCountMax + 1}}, + {"notify_enabled 越界", map[string]any{"notify_enabled": 2}}, + {"footer_beian 超长", map[string]any{"footer_beian": strings.Repeat("备", config.FooterBeianMaxLen+1)}}, + {"footer_text 超长", map[string]any{"footer_text": strings.Repeat("页", config.FooterTextMaxLen+1)}}, + {"background_url 非法协议", map[string]any{"background_url": "javascript:alert(1)"}}, + {"max_save_seconds 超上限", map[string]any{"max_save_seconds": config.MaxSaveSecondsMax + 1}}, + {"allowed_file_types 类型错误", map[string]any{"allowed_file_types": 123}}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + w := patchConfig(d, tc.patch) + if w.Code != http.StatusBadRequest { + t.Fatalf("应 400,实际 %d %s", w.Code, w.Body.String()) + } + code, _ := respBody(t, w) + if code != http.StatusBadRequest { + t.Fatalf("响应 code 应为 400,实际 %d", code) + } + }) + } + // 合法值不受影响 + if w := patchConfig(d, map[string]any{ + "max_save_count": 0, // 0 = 不限制 + "notify_enabled": 1, + "background_url": "data:image/png;base64,AAA", + "max_file_size": 1024, + "max_save_seconds": 0, + }); w.Code != 200 { + t.Fatalf("合法 patch 应 200: %d %s", w.Code, w.Body.String()) + } +} + +// ============ ③ 上传动态校验:admin 改策略 → 上传行为即时变化 ============ + +// TestUploadPolicyDynamicEnforcement 全链路:默认可传 → 改 max_file_size/白名单/ +// 保存策略后 → 公开 config 反映 → 上传被新策略拒绝(403/400)。 +func TestUploadPolicyDynamicEnforcement(t *testing.T) { + d := newPolicyTestDeps(t) + // 默认策略:小 PNG 上传成功 + w := uploadFile(d, "ok.png", pngMagic, map[string]string{"expire_value": "1", "expire_style": "day"}) + if code, _ := respBody(t, w); code != 200 { + t.Fatalf("默认策略上传应成功: %d %s", w.Code, w.Body.String()) + } + + // —— 大小上限:max_file_size=100 → 200B 文件 403 —— + if w = patchConfig(d, map[string]any{"max_file_size": 100}); w.Code != 200 { + t.Fatalf("patch max_file_size: %d %s", w.Code, w.Body.String()) + } + w = getPublicConfig(d) + _, data := respBody(t, w) + if got := data["config"].(map[string]any)["max_file_size"]; got != float64(100) { + t.Fatalf("公开 config 未即时反映 max_file_size=100: %v", got) + } + w = uploadFile(d, "big.png", append(pngMagic, bytes.Repeat([]byte{0}, 200)...), + map[string]string{"expire_value": "1", "expire_style": "day"}) + code, _ := respBody(t, w) + if code != http.StatusForbidden { + t.Fatalf("超限上传应 403: %d %s", w.Code, w.Body.String()) + } + + // —— 类型白名单:allowed_file_types=[.png] → .txt 403 —— + if w = patchConfig(d, map[string]any{"allowed_file_types": []string{".png"}}); w.Code != 200 { + t.Fatalf("patch allowed_file_types: %d %s", w.Code, w.Body.String()) + } + w = uploadFile(d, "note.txt", []byte("hello"), map[string]string{"expire_value": "1", "expire_style": "day"}) + if code, _ := respBody(t, w); code != http.StatusForbidden { + t.Fatalf("非白名单类型应 403: %d %s", w.Code, w.Body.String()) + } + + // —— 保存时间:max_save_seconds=3600 → expire 1 天 403 —— + if w = patchConfig(d, map[string]any{"max_save_seconds": 3600}); w.Code != 200 { + t.Fatalf("patch max_save_seconds: %d %s", w.Code, w.Body.String()) + } + w = uploadFile(d, "timed.png", pngMagic, map[string]string{"expire_value": "1", "expire_style": "day"}) + if code, _ := respBody(t, w); code != http.StatusForbidden { + t.Fatalf("保存时间超范围应 403: %d %s", w.Code, w.Body.String()) + } + + // —— 保存次数:max_save_count=5 → count=10 403(重置时间策略避免交叉影响)—— + if w = patchConfig(d, map[string]any{"max_save_count": 5, "max_save_seconds": 0}); w.Code != 200 { + t.Fatalf("patch max_save_count: %d %s", w.Code, w.Body.String()) + } + w = uploadFile(d, "counted.png", pngMagic, map[string]string{"expire_value": "10", "expire_style": "count"}) + if code, _ := respBody(t, w); code != http.StatusForbidden { + t.Fatalf("保存次数超上限应 403: %d %s", w.Code, w.Body.String()) + } + // 次数在上限内合法 + w = uploadFile(d, "counted.png", pngMagic, map[string]string{"expire_value": "3", "expire_style": "count"}) + if code, _ := respBody(t, w); code != 200 { + t.Fatalf("次数在上限内应成功: %d %s", w.Code, w.Body.String()) + } + + // —— 过期方式白名单收窄:expireStyle=[day] → hour 400 —— + if w = patchConfig(d, map[string]any{"expireStyle": []string{"day"}}); w.Code != 200 { + t.Fatalf("patch expireStyle: %d %s", w.Code, w.Body.String()) + } + w = uploadFile(d, "hour.png", pngMagic, map[string]string{"expire_value": "2", "expire_style": "hour"}) + if code, _ := respBody(t, w); code != http.StatusBadRequest { + t.Fatalf("非白名单 expire_style 应 400: %d %s", w.Code, w.Body.String()) + } + // 恢复后可用(说明策略动态读取) + if w = patchConfig(d, map[string]any{"expireStyle": []string{"day", "hour", "minute", "forever", "count"}}); w.Code != 200 { + t.Fatal("恢复 expireStyle 失败") + } + w = uploadFile(d, "hour.png", pngMagic, map[string]string{"expire_value": "2", "expire_style": "hour"}) + if code, _ := respBody(t, w); code != 200 { + t.Fatalf("白名单恢复后应成功: %d %s", w.Code, w.Body.String()) + } +} + +// TestChunkUploadPolicyEnforcement 验证分片上传链路接入动态策略。 +func TestChunkUploadPolicyEnforcement(t *testing.T) { + d := newPolicyTestDeps(t) + // L4:后端强制 enableChunk 开关,本测试前置开启 + if w := patchConfig(d, map[string]any{"enableChunk": 1}); w.Code != 200 { + t.Fatalf("patch enableChunk: %d %s", w.Code, w.Body.String()) + } + // 大小:max_file_size=1000 → file_size 5000 拒绝 + if w := patchConfig(d, map[string]any{"max_file_size": 1000}); w.Code != 200 { + t.Fatalf("patch max_file_size: %d %s", w.Code, w.Body.String()) + } + w := chunkInitJSON(d, `{"file_name":"a.png","file_size":5000,"chunk_size":1024,"file_hash":"h"}`) + if code, _ := respBody(t, w); code != http.StatusForbidden { + t.Fatalf("分片总大小超限应 403: %d %s", w.Code, w.Body.String()) + } + // 类型:allowed_file_types=[.png] → b.txt 拒绝(max_file_size 重置为回落,隔离类型断言) + if w := patchConfig(d, map[string]any{"allowed_file_types": []string{".png"}, "max_file_size": 0}); w.Code != 200 { + t.Fatalf("patch allowed_file_types: %d %s", w.Code, w.Body.String()) + } + w = chunkInitJSON(d, `{"file_name":"b.txt","file_size":500,"chunk_size":1024,"file_hash":"h"}`) + if code, _ := respBody(t, w); code != http.StatusForbidden { + t.Fatalf("分片文件类型非白名单应 403: %d %s", w.Code, w.Body.String()) + } + // 白名单内 + 大小内 → 会话创建成功 + w = chunkInitJSON(d, `{"file_name":"c.png","file_size":500,"chunk_size":1024,"file_hash":"h"}`) + if code, _ := respBody(t, w); code != 200 { + t.Fatalf("合法分片初始化应 200: %d %s", w.Code, w.Body.String()) + } +} + +// TestPresignPolicyEnforcement 验证预签名直传链路接入动态大小策略。 +func TestPresignPolicyEnforcement(t *testing.T) { + d := newPolicyTestDeps(t) + if w := patchConfig(d, map[string]any{"max_file_size": 1000}); w.Code != 200 { + t.Fatalf("patch max_file_size: %d %s", w.Code, w.Body.String()) + } + payload := `{"file_name":"a.png","file_size":5000,"expire_value":1,"expire_style":"day"}` + req := httptest.NewRequest(http.MethodPost, "/presign/upload/init", strings.NewReader(payload)) + req.Header.Set("Content-Type", "application/json") + w := invoke(d.presignInit, req) + if code, _ := respBody(t, w); code != http.StatusForbidden { + t.Fatalf("预签名直传超限应 403: %d %s", w.Code, w.Body.String()) + } +} + +// TestSensitiveKeysNeverInPublicConfig 额外兜底:公开 config 任意策略下都无敏感键。 +func TestSensitiveKeysNeverInPublicConfig(t *testing.T) { + d := newPolicyTestDeps(t) + // 写入敏感 KV(模拟已初始化实例),公开端点依旧不能带出 + ctx := context.Background() + if err := d.Mgr.UpdateKV(ctx, map[string]any{ + "jwt_secret": "super-secret-value", + "admin_token": settings.HashPassword("password-123456"), + }); err != nil { + t.Fatalf("UpdateKV: %v", err) + } + if err := d.Mgr.Reload(ctx); err != nil { + t.Fatalf("Reload: %v", err) + } + raw := getPublicConfig(d).Body.String() + if strings.Contains(raw, "super-secret-value") || strings.Contains(raw, "jwt_secret") { + t.Fatal("公开 config 泄露 jwt_secret") + } + if strings.Contains(raw, "admin_token") { + t.Fatal("公开 config 泄露 admin_token") + } +} + +// TestPolicySnapshotMatchesConfig 验证策略快照与 config 一致(单一读取口径)。 +func TestPolicySnapshotMatchesConfig(t *testing.T) { + d := newPolicyTestDeps(t) + if w := patchConfig(d, map[string]any{"max_file_size": 2048, "max_save_count": 9, "max_save_seconds": 7200}); w.Code != 200 { + t.Fatal("patch 失败") + } + pol := d.CurrentUploadPolicy() + if pol.MaxFileSize != 2048 || pol.MaxSaveCount != 9 || pol.MaxSaveSeconds != 7200 { + t.Fatalf("策略快照不一致: %+v", pol) + } + if err := pol.CheckSize(2048); err != nil { + t.Fatalf("边界值应放行: %v", err) + } + if err := pol.CheckSize(2049); err == nil { + t.Fatal("超限应拒绝") + } + // 0=回落 uploadSize + if w := patchConfig(d, map[string]any{"max_file_size": 0}); w.Code != 200 { + t.Fatal("patch 失败") + } + if got := d.CurrentUploadPolicy().MaxFileSize; got != d.Cfg.UploadSize() { + t.Fatalf("max_file_size=0 应回落 uploadSize: %d vs %d", got, d.Cfg.UploadSize()) + } +} + +// 编译期保证 fmt 被使用(测试辅助函数中错误路径占位)。 +var _ = fmt.Sprintf + +// ============ v3 存储引擎热切换 ============ + +// switchEngine 调用 POST /admin/storage/switch。 +func switchEngine(d *Deps, engine string) *httptest.ResponseRecorder { + raw, _ := json.Marshal(map[string]any{"engine": engine}) + req := httptest.NewRequest(http.MethodPost, "/admin/storage/switch", bytes.NewReader(raw)) + req.Header.Set("Content-Type", "application/json") + return invoke(d.adminStorageSwitch, req) +} + +// TestAdminStorageSwitchLocal 本地引擎切换(测试 build 只产 local,切 local 恒成功)。 +func TestAdminStorageSwitchLocal(t *testing.T) { + d := newPolicyTestDeps(t) + w := switchEngine(d, "local") + if w.Code != 200 { + t.Fatalf("switch local 失败: %d %s", w.Code, w.Body.String()) + } + // KV 持久化:admin get 可见 + if got := getConfig(d); got.Code != 200 { + t.Fatal("get 失败") + } + // 非法引擎名 400 + if w := switchEngine(d, "ftp"); w.Code != http.StatusBadRequest { + t.Fatalf("非法引擎应 400,实际 %d", w.Code) + } +} + +// TestAdminConfigEngineSwitchFailure 测试环境下切换到不可用引擎保持原引擎(503)。 +// 测试 Manager 的 build 返回 local;这里通过直接操作 Manager 验证 503 路径的响应格式。 +func TestAdminConfigEngineSwitchFailure(t *testing.T) { + d := newPolicyTestDeps(t) + // 用一个恒失败的 Manager 替换(模拟 s3/webdav 健康检查不过) + d.Store = storage.NewManager("local", mustLocal(t), func(string) (storage.Storage, error) { + return nil, errors.New("连接失败") + }) + w := switchEngine(d, "s3") + if w.Code != http.StatusServiceUnavailable { + t.Fatalf("不可用引擎应 503,实际 %d %s", w.Code, w.Body.String()) + } + if !strings.Contains(w.Body.String(), "已保持原引擎") { + t.Fatal("错误信息应包含「已保持原引擎」") + } + // 失败后当前引擎不变 + if d.Store.CurrentName() != "local" { + t.Fatalf("失败后应保持 local,实际 %s", d.Store.CurrentName()) + } +} + +// TestAdminConfigMaskedSecrets 敏感引擎凭据:get 掩码、update 空/掩码不落库。 +func TestAdminConfigMaskedSecrets(t *testing.T) { + d := newPolicyTestDeps(t) + // 先写入真实凭据 + if w := patchConfig(d, map[string]any{"webdav_password": "real-secret", "s3_secret_access_key": "sk-real"}); w.Code != 200 { + t.Fatalf("写凭据失败: %s", w.Body.String()) + } + // get 应为掩码 + _, data := respBody(t, getConfig(d)) + if got := data["webdav_password"]; got != settings.SensitiveMaskValue { + t.Fatalf("webdav_password 应掩码,实际 %v", got) + } + if got := data["s3_secret_access_key"]; got != settings.SensitiveMaskValue { + t.Fatalf("s3_secret_access_key 应掩码,实际 %v", got) + } + // 提交掩码(模拟前端回显原样提交)→ 不应覆盖为掩码串 + if w := patchConfig(d, map[string]any{"webdav_password": settings.SensitiveMaskValue}); w.Code != 200 { + t.Fatalf("掩码提交应 200: %s", w.Body.String()) + } + // 提交空串 → 不修改 + if w := patchConfig(d, map[string]any{"s3_secret_access_key": ""}); w.Code != 200 { + t.Fatalf("空串提交应 200: %s", w.Body.String()) + } + // 公开 config 绝不含引擎凭据 + _, pub := respBody(t, getPublicConfig(d)) + rawPub, _ := json.Marshal(pub) + for _, sk := range []string{"webdav_password", "s3_secret_access_key", "aws_session_token", "jwt_secret"} { + if strings.Contains(string(rawPub), sk) { + t.Fatalf("公开 config 不应包含 %s", sk) + } + } +} + +func mustLocal(t *testing.T) storage.Storage { + t.Helper() + s, err := storage.NewLocalStorage(t.TempDir()) + if err != nil { + t.Fatal(err) + } + return s +} diff --git a/server/internal/api/presign.go b/server/internal/api/presign.go new file mode 100644 index 0000000..a9fb4a2 --- /dev/null +++ b/server/internal/api/presign.go @@ -0,0 +1,511 @@ +package api + +import ( + "errors" + "fmt" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + + "filecodebox/internal/middleware" + "filecodebox/internal/model" + "filecodebox/internal/response" + "filecodebox/internal/storage" +) + +// presignSessionExpires 预签名会话有效期(对齐参考 PRESIGN_SESSION_EXPIRES=900 秒)。 +const presignSessionExpires = 900 + +// getValidSession 校验并返回预签名会话(对齐参考 _get_valid_session): +// 不存在 404、已过期删除后 404、mode 不符 400。 +func (d *Deps) getValidSession(c *gin.Context, uploadID, expectedMode string) (*model.PresignUploadSession, error) { + ctx := c.Request.Context() + var session model.PresignUploadSession + err := d.DB.WithContext(ctx). + Where("upload_id = ?", uploadID).First(&session).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errNotFound("上传会话不存在") + } + if err != nil { + return nil, errInternal("查询上传会话失败: " + err.Error()) + } + if session.IsExpired(time.Now()) { + _ = d.DB.WithContext(ctx).Where("upload_id = ?", uploadID). + Delete(&model.PresignUploadSession{}).Error + releaseStorage(ctx, d.DB, "presign:"+uploadID) + return nil, errNotFound("上传会话已过期") + } + if expectedMode != "" && session.Mode != expectedMode { + return nil, errBadRequest("此会话不支持" + expectedMode + "模式") + } + return &session, nil +} + +// ============ POST /presign/upload/init 初始化预签名上传 ============ + +// presignInitRequest init 请求体(对齐参考 PresignUploadInitRequest)。 +type presignInitRequest struct { + FileName string `json:"file_name" form:"file_name"` + FileSize int64 `json:"file_size" form:"file_size"` + ExpireValue int `json:"expire_value" form:"expire_value"` + ExpireStyle string `json:"expire_style" form:"expire_style"` + Code string `json:"code" form:"code"` // v3.1:自定义提取码(init 时校验,完成时落库) +} + +// presignInit 初始化预签名上传(对齐参考 presign_upload_init): +// 引擎支持直链(S3)返回 direct + 预签名 PUT URL;否则返回 proxy + 代理地址。 +func (d *Deps) presignInit(c *gin.Context) { + if !d.requireShareLogin(c) { + return + } + if !requireUploadLimit(c, d.Limiter) { + return + } + var req presignInitRequest + if err := bindJSONOrForm(c, &req); err != nil { + respondError(c, err) + return + } + safeName := storage.SanitizeFileName(req.FileName) + if safeName == "" { + auditRecordFailed(c, d.AuditSvc, "文件名非法") + response.Fail(c, http.StatusBadRequest, "文件名非法") + return + } + // v3.1:自定义提取码提前校验(init 时快速失败;完成请求须再次携带) + if err := validatePickupCode(req.Code); err != nil { + auditRecordFailed(c, d.AuditSvc, "提取码非法") + respondError(c, err) + return + } + if err := validateFileMagic(d.Cfg, safeName, "", nil); err != nil { + auditUploadEntry(c, "", safeName, req.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "文件类型被拒绝") + respondError(c, err) + return + } + // v2 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize) + if err := d.CurrentUploadPolicy().CheckSize(req.FileSize); err != nil { + auditUploadEntry(c, "", safeName, req.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "文件大小超过限制") + respondError(c, err) + return + } + // M2:直传 confirm 的实际大小校验依赖真实对象,0/负值声明直接拒绝 + if req.FileSize <= 0 { + auditRecordFailed(c, d.AuditSvc, "file_size 非法") + response.Fail(c, http.StatusBadRequest, "file_size 必须大于 0") + return + } + if req.ExpireValue <= 0 { + req.ExpireValue = 1 + } + if req.ExpireStyle == "" { + req.ExpireStyle = "day" + } + if _, err := resolveExpire(d.Cfg, req.ExpireValue, req.ExpireStyle); err != nil { + auditUploadEntry(c, "", safeName, req.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "过期策略非法") + respondError(c, err) + return + } + + ctx := c.Request.Context() + uploadID := uuidHex() + resToken := "presign:" + uploadID + if err := reserveStorage(ctx, d.DB, d.Cfg, resToken, req.FileSize, presignSessionExpires*time.Second); err != nil { + auditUploadEntry(c, "", safeName, req.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "容量预留失败") + respondError(c, err) + return + } + + dirPath, _, _, _, savePath := buildSavePath(d.Cfg, safeName, uploadID) + mode := "proxy" + uploadURL := "/presign/upload/proxy/" + uploadID + putURL, err := d.Store.PresignPutURL(ctx, savePath, presignSessionExpires) + switch { + case err == nil: + mode = "direct" + uploadURL = putURL + case errors.Is(err, storage.ErrNotSupported): + // 引擎不支持直传:代理模式 + default: + releaseStorage(ctx, d.DB, resToken) + auditUploadEntry(c, "", safeName, req.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "生成预签名失败") + respondError(c, mapStorageError(err)) + return + } + + session := model.PresignUploadSession{ + UploadID: uploadID, + FileName: safeName, + FileSize: req.FileSize, + SavePath: savePath, + Mode: mode, + Engine: d.Store.CurrentName(), // v3:会话归属引擎 + ExpireValue: req.ExpireValue, + ExpireStyle: req.ExpireStyle, + ExpiresAt: time.Now().Add(presignSessionExpires * time.Second), + } + if err := d.DB.WithContext(ctx).Create(&session).Error; err != nil { + releaseStorage(ctx, d.DB, resToken) + auditUploadEntry(c, "", safeName, req.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "会话创建失败") + respondError(c, errInternal("创建上传会话失败: "+err.Error())) + return + } + + d.Limiter.Add(c, middleware.LimitUpload) + auditUploadEntry(c, uploadID, safeName, req.FileSize, 0) + auditRecordSuccess(c, d.AuditSvc) + detail := gin.H{ + "upload_id": uploadID, + "upload_url": uploadURL, + "mode": mode, + "expires_in": presignSessionExpires, + "file_path": dirPath, + } + if mode == "proxy" { + detail["proxy_upload_url"] = uploadURL + detail["legacy_proxy_upload_url"] = "/api" + uploadURL + } + response.OK(c, detail) +} + +// ============ PUT /presign/upload/proxy/{uploadID} 代理上传 ============ + +// presignProxy 代理模式上传(对齐参考 presign_upload_proxy): +// 服务器接收文件并转存到存储引擎,随后立即创建分享记录。 +func (d *Deps) presignProxy(c *gin.Context) { + if !d.requireShareLogin(c) { + return + } + if !requireUploadLimit(c, d.Limiter) { + return + } + uploadID := c.Param("uploadID") + session, err := d.getValidSession(c, uploadID, "proxy") + if err != nil { + auditUploadEntry(c, uploadID, "", 0, 0) + auditRecordFailed(c, d.AuditSvc, err.Error()) + respondError(c, err) + return + } + // v3.1:自定义提取码随代理上传表单携带(init 时已预校验) + if err := validatePickupCode(c.PostForm("code")); err != nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "提取码非法") + respondError(c, err) + return + } + ctx := c.Request.Context() + if err := reserveStorage(ctx, d.DB, d.Cfg, "presign:"+uploadID, session.FileSize, presignSessionExpires*time.Second); err != nil { + respondError(c, err) + return + } + + fh, err := c.FormFile("file") + if err != nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "缺少 file 字段") + response.Fail(c, http.StatusBadRequest, "缺少上传文件 file 字段") + return + } + // 动态策略快照(与 share/chunk 上传路径一致,消除会话窗口内的策略滞后) + maxSize := d.CurrentUploadPolicy().MaxFileSize + if maxSize > 0 && fh.Size > maxSize { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "大小超过限制") + response.Fail(c, http.StatusForbidden, fmt.Sprintf("大小超过限制,最大为%s", humanSize(maxSize))) + return + } + // 文件大小与声明不符(±1KB 容差,对齐参考) + if abs64(fh.Size-session.FileSize) > 1024 { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "文件大小与声明不符") + response.Fail(c, http.StatusBadRequest, "文件大小与声明不符") + return + } + f, err := fh.Open() + if err == nil { + defer func() { _ = f.Close() }() + if err = validateFileMagic(d.Cfg, session.FileName, fh.Header.Get("Content-Type"), readMultipartHeader(f, 64)); err == nil { + // v3:落盘走会话归属引擎 + var ps storage.Storage + ps, sErr := d.storeFor(session.Engine) + if sErr != nil { + err = sErr + } else if _, err = ps.SaveFile(ctx, f, session.SavePath); err != nil { + // 落盘失败,err 交给统一错误处理 + } + } + } + if err != nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "文件保存失败") + if isStorageErr(err) { + respondError(c, mapStorageError(err)) + } else { + respondError(c, errInternal("文件保存失败: "+err.Error())) + } + return + } + + code, err := d.createRecordFromSession(c, session, c.PostForm("code")) + releaseStorage(ctx, d.DB, "presign:"+uploadID) + if err != nil { + if ps, sErr := d.storeFor(session.Engine); sErr == nil { + _ = ps.DeleteFile(ctx, session.SavePath) // v3:清理走归属引擎 + } + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "创建分享失败") + respondError(c, err) + return + } + _ = d.DB.WithContext(ctx).Where("upload_id = ?", uploadID). + Delete(&model.PresignUploadSession{}).Error + d.Limiter.Add(c, middleware.LimitUpload) + auditUploadEntry(c, code, session.FileName, session.FileSize, fh.Size) + auditRecordSuccess(c, d.AuditSvc) + response.OK(c, gin.H{"code": code, "name": session.FileName}) +} + +// ============ POST /presign/upload/confirm/{uploadID} 直传确认 ============ + +// presignConfirm 直传确认(对齐参考 presign_upload_confirm): +// 客户端完成 S3 直传后调用,校验文件已存在并创建分享记录。 +func (d *Deps) presignConfirm(c *gin.Context) { + if !d.requireShareLogin(c) { + return + } + if !requireUploadLimit(c, d.Limiter) { + return + } + uploadID := c.Param("uploadID") + session, err := d.getValidSession(c, uploadID, "direct") + if err != nil { + auditUploadEntry(c, uploadID, "", 0, 0) + auditRecordFailed(c, d.AuditSvc, err.Error()) + respondError(c, err) + return + } + ctx := c.Request.Context() + if err := reserveStorage(ctx, d.DB, d.Cfg, "presign:"+uploadID, session.FileSize, presignSessionExpires*time.Second); err != nil { + respondError(c, err) + return + } + + // v3:直传文件存在性按会话归属引擎检查(直传可能落在旧引擎) + psCheck, sErr := d.storeFor(session.Engine) + if sErr != nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "存储引擎不可用: "+sErr.Error()) + respondError(c, mapStorageError(sErr)) + return + } + // v3.1:自定义提取码随确认请求携带(query 或 JSON/form body,均可选) + customCode := c.Query("code") + if customCode == "" && c.Request.Body != nil && c.Request.ContentLength != 0 { + var fin struct { + Code string `json:"code" form:"code"` + } + if err := bindJSONOrForm(c, &fin); err != nil { + respondError(c, err) + return + } + customCode = fin.Code + } + + exists, err := psCheck.FileExists(ctx, session.SavePath) + if err == nil && !exists { + err = errNotFound("文件未上传或上传失败") + } + if err != nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "文件未上传或上传失败") + respondError(c, err) + return + } + + // M2 修复:直传内容不经过服务器,confirm 必须核实实际大小与内容类型。 + meta, head, hErr := psCheck.HeadMeta(ctx, session.SavePath, 64) + if hErr != nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "文件信息读取失败") + respondError(c, mapStorageError(hErr)) + return + } + if meta == nil { + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "文件未上传或上传失败") + respondError(c, errNotFound("文件未上传或上传失败")) + return + } + // 大小上限:实际大小超过策略上限 → 删除对象并 403(防绕过 max_file_size / storageLimit) + maxSize := d.CurrentUploadPolicy().MaxFileSize + if maxSize > 0 && meta.Size > maxSize { + _ = psCheck.DeleteFile(ctx, session.SavePath) + releaseStorage(ctx, d.DB, "presign:"+uploadID) + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "实际文件大小超过限制") + respondError(c, errForbidden(fmt.Sprintf("大小超过限制,最大为%s", humanSize(maxSize)))) + return + } + // 大小与声明不符(±1KB 容差,对齐 proxy 模式):超差删除对象并 400 + if abs64(meta.Size-session.FileSize) > 1024 { + _ = psCheck.DeleteFile(ctx, session.SavePath) + releaseStorage(ctx, d.DB, "presign:"+uploadID) + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "文件大小与声明不符") + respondError(c, errBadRequest("文件大小与声明不符")) + return + } + // 内容类型防伪造(对齐 proxy 模式 magic bytes 校验) + if err := validateFileMagic(d.Cfg, session.FileName, "", head); err != nil { + _ = psCheck.DeleteFile(ctx, session.SavePath) + releaseStorage(ctx, d.DB, "presign:"+uploadID) + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "文件内容校验失败") + respondError(c, err) + return + } + + code, err := d.createRecordFromSession(c, session, customCode) + releaseStorage(ctx, d.DB, "presign:"+uploadID) + if err != nil { + if ps, sErr := d.storeFor(session.Engine); sErr == nil { + _ = ps.DeleteFile(ctx, session.SavePath) // v3:清理走归属引擎 + } + auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0) + auditRecordFailed(c, d.AuditSvc, "创建分享失败") + respondError(c, err) + return + } + _ = d.DB.WithContext(ctx).Where("upload_id = ?", uploadID). + Delete(&model.PresignUploadSession{}).Error + d.Limiter.Add(c, middleware.LimitUpload) + auditUploadEntry(c, code, session.FileName, session.FileSize, session.FileSize) + auditRecordSuccess(c, d.AuditSvc) + response.OK(c, gin.H{"code": code, "name": session.FileName}) +} + +// createRecordFromSession 依据预签名会话创建分享记录(对齐参考 create_file_record)。 +func (d *Deps) createRecordFromSession(c *gin.Context, session *model.PresignUploadSession, customCode string) (string, error) { + exp, err := resolveExpire(d.Cfg, session.ExpireValue, session.ExpireStyle) + if err != nil { + return "", err + } + // v3.1:完成请求的自定义提取码兜底校验(init 已验,防只发完成请求绕过) + if err := validatePickupCode(customCode); err != nil { + return "", err + } + ctx := c.Request.Context() + code, err := pickCustomCode(ctx, d.DB, d.Cfg, customCode) + if err != nil { + return "", err + } + dir, name := splitDirBase(session.SavePath) + ext := baseExt(name) + fc := model.FileCodes{ + Code: code, + Prefix: trimExt(name), + Suffix: ext, + UUIDFileName: &name, + FilePath: &dir, + Size: session.FileSize, + ExpiredAt: exp.ExpiredAt, + ExpiredCount: exp.ExpiredCount, + UsedCount: exp.UsedCount, + Engine: session.Engine, // v3:归属引擎戳 + } + if err := d.DB.WithContext(ctx).Create(&fc).Error; err != nil { + return "", mapCodeConflict(err) // v3.1:并发占用自定义码 → 友好 400 + } + return code, nil +} + +// ============ GET /presign/upload/status/{uploadID} ============ + +// presignStatus 查询预签名会话状态(对齐参考 presign_upload_status)。 +func (d *Deps) presignStatus(c *gin.Context) { + if !d.requireShareLogin(c) { + return + } + uploadID := c.Param("uploadID") + ctx := c.Request.Context() + var session model.PresignUploadSession + if err := d.DB.WithContext(ctx). + Where("upload_id = ?", uploadID).First(&session).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Fail(c, http.StatusNotFound, "上传会话不存在") + return + } + respondError(c, errInternal("查询上传会话失败: "+err.Error())) + return + } + response.OK(c, gin.H{ + "upload_id": session.UploadID, + "file_name": session.FileName, + "file_size": session.FileSize, + "mode": session.Mode, + "created_at": session.CreatedAt.Format(time.RFC3339), + "expires_at": session.ExpiresAt.Format(time.RFC3339), + "is_expired": session.IsExpired(time.Now()), + }) +} + +// ============ DELETE /presign/upload/{uploadID} 取消会话 ============ + +// presignCancel 取消预签名上传会话(对齐参考 presign_upload_cancel): +// 直传模式尽力清理已直传的文件。 +func (d *Deps) presignCancel(c *gin.Context) { + if !d.requireShareLogin(c) { + return + } + uploadID := c.Param("uploadID") + ctx := c.Request.Context() + session, err := d.getValidSession(c, uploadID, "") + if err != nil { + respondError(c, err) + return + } + if session.Mode == "direct" { + // v3:清理走会话归属引擎 + if ps, sErr := d.storeFor(session.Engine); sErr == nil { + if exists, eErr := ps.FileExists(ctx, session.SavePath); eErr == nil && exists { + _ = ps.DeleteFile(ctx, session.SavePath) + } + } + } + if err := d.DB.WithContext(ctx).Where("upload_id = ?", uploadID). + Delete(&model.PresignUploadSession{}).Error; err != nil { + respondError(c, errInternal("取消上传会话失败: "+err.Error())) + return + } + releaseStorage(ctx, d.DB, "presign:"+uploadID) + response.OK(c, gin.H{"message": "上传会话已取消"}) +} + +// ============ 杂项 ============ + +// abs64 绝对值。 +func abs64(n int64) int64 { + if n < 0 { + return -n + } + return n +} + +// isStorageErr 判断是否为存储层哨兵错误(含 %w 包装)。 +func isStorageErr(err error) bool { + return err != nil && (errors.Is(err, storage.ErrNotFound) || + errors.Is(err, storage.ErrInvalidPath) || + errors.Is(err, storage.ErrUnavailable) || + errors.Is(err, storage.ErrNotSupported) || + errors.Is(err, storage.ErrRangeNotSatisfiable) || + errors.Is(err, storage.ErrHashMismatch)) +} diff --git a/server/internal/api/router.go b/server/internal/api/router.go new file mode 100644 index 0000000..a03916c --- /dev/null +++ b/server/internal/api/router.go @@ -0,0 +1,187 @@ +package api + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + + "filecodebox/internal/audit" + "filecodebox/internal/config" + "filecodebox/internal/middleware" + "filecodebox/internal/response" + "filecodebox/internal/settings" + "filecodebox/internal/storage" +) + +// Deps API 层共享依赖(main.go 装配后注入)。 +type Deps struct { + DB *gorm.DB + Cfg *config.Config + Mgr *settings.Manager + AuditSvc *audit.Service + Limiter *middleware.RateLimiter + Store *storage.Manager // v3:可热切换引擎管理器(实现 Storage 接口) + Version string +} + +// jwtSecret 当前 JWT 签名密钥(settings KV 运行时可变)。 +func (d *Deps) jwtSecret() string { return d.Mgr.SecretProvider()() } + +// Register 注册全部 API 路由与前端静态资源回退。 +// 业务路由挂根路径(/share /chunk /presign /admin),与审计中间件 +// DefaultClassifier 的路由模式一致(t1 冻结契约);公共接口保留 +// /api/v1/health 与 /api/v1/config(对齐 t1 骨架)。 +func Register(r *gin.Engine, d *Deps) { + // —— 公共接口 —— + r.GET("/api/v1/health", d.health) + r.GET("/api/v1/config", d.publicConfig) + // Info3:robotsText 配置键此前无路由承接,补上(内容可在管理端自定义) + r.GET("/robots.txt", d.robotsText) + + // —— 初始化向导(未初始化时唯一可用入口,GuardNotInitialized 白名单)—— + registerSetup(r, d) + + // —— 分享 —— + share := r.Group("/share") + { + share.POST("/text", d.shareText) + share.POST("/file", d.shareFile) + // metadata:每次访问即计数(RequireRateLimit=进入检查+完成计数) + share.GET("/metadata", d.Limiter.RequireRateLimit(middleware.LimitMeta), d.shareMetadata) + share.POST("/metadata", d.Limiter.RequireRateLimit(middleware.LimitMeta), d.shareMetadataPost) + share.GET("/select", d.shareSelect) + share.POST("/select", d.shareSelectPost) + share.GET("/download", d.shareDownload) + } + + // —— 分片上传 —— + chunk := r.Group("/chunk") + { + chunk.POST("/upload/init", d.chunkInit) + // 主路径(参考语义):/chunk/upload/{uploadID}/{index}; + // 扁平兼容:/chunk/upload + 表单/query 传 upload_id/chunk_index + chunk.POST("/upload/:uploadID/:chunkIndex", d.chunkUpload) + chunk.POST("/upload", d.chunkUploadFlat) + chunk.GET("/upload/status/:uploadID", d.chunkStatus) + chunk.POST("/upload/complete/:uploadID", d.chunkComplete) + chunk.DELETE("/upload/:uploadID", d.chunkCancel) + } + + // —— 预签名直传 —— + presign := r.Group("/presign") + { + presign.POST("/upload/init", d.presignInit) + presign.PUT("/upload/proxy/:uploadID", d.presignProxy) + presign.POST("/upload/confirm/:uploadID", d.presignConfirm) + presign.GET("/upload/status/:uploadID", d.presignStatus) + presign.DELETE("/upload/:uploadID", d.presignCancel) + } + + // —— 管理端(login 公开,其余需管理员 JWT)—— + registerAdmin(r, d) + + // —— 前端静态资源 + SPA 回退(须最后注册)—— + registerWeb(r, d) +} + +// health 健康检查(对齐 t1 骨架,保持 /api/v1/health 语义不变)。 +func (d *Deps) health(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "code": 200, + "msg": "ok", + "data": gin.H{ + "status": "ok", + "version": d.Version, + "storage": d.Cfg.Engine(), + "time": nowRFC3339(), + }, + }) +} + +// robotsText 输出管理端可配置的 robots.txt 内容。 +func (d *Deps) robotsText(c *gin.Context) { + c.Data(http.StatusOK, "text/plain; charset=utf-8", []byte(d.Cfg.GetString("robotsText"))) +} + +// publicConfig 公共配置(前端首页/上传页所需;v2 需求 ①②③④⑩ 扩展): +// - 展示字段:站点信息、Logo/favicon、背景图、页脚文案/备案号、通知; +// - 策略范围(上传页动态渲染):大小上限、类型白名单、过期方式、保存 +// 时间/次数上限、上传频率(仅范围,不含内部实现键)。 +// +// 敏感键(admin_token/jwt_secret,settings.SensitiveKeys)与本端点无关: +// 下发字段为白名单显式构造,任何敏感键均不会出现在响应中。 +func (d *Deps) publicConfig(c *gin.Context) { + cfg := d.Cfg + policy := d.CurrentUploadPolicy() + uploadCount := cfg.GetInt("uploadCount") + uploadMinute := cfg.GetInt("uploadMinute") + // uploadSize 为参考语义的回落上限,单独下发供管理端联动展示 + c.JSON(http.StatusOK, gin.H{ + "code": 200, + "msg": "ok", + "data": gin.H{ + "config": gin.H{ + "name": cfg.SiteName(), + "description": cfg.GetString("description"), + "explain": cfg.GetString("page_explain"), + // 需求 ①:Logo/favicon/背景图 + "logo_url": cfg.LogoURL(), + "favicon_url": cfg.FaviconURL(), + "background_url": cfg.BackgroundURL(), + // 需求 ②:页脚自定义内容与备案号 + "footer_text": cfg.FooterText(), + "footer_beian": cfg.FooterBeian(), + // v3:当前存储引擎名(仅名称,任何引擎参数/凭据不下发) + "storage_engine": d.Store.CurrentName(), + "site_domain": d.Cfg.SiteDomain(), + // 需求 ③:系统通知(开关 + 内容,前台右上角悬浮窗) + // L7:读取侧再做一次白名单净化,覆盖历史存量与直改库的数据 + "notify_enabled": boolToInt(cfg.NotifyEnabled()), + "notify_title": cfg.GetString("notify_title"), + "notify_content": settings.SanitizeInlineHTML(cfg.GetString("notify_content")), + // 策略范围(需求 ④⑩):上传页动态读取并在范围内选择 + "uploadSize": cfg.UploadSize(), + "max_file_size": policy.MaxFileSize, + "maxFileSize": policy.MaxFileSize, + "allowedFileTypes": policy.AllowedTypes, + "expireStyle": policy.ExpireStyles, + "max_save_seconds": policy.MaxSaveSeconds, + "maxSaveSeconds": policy.MaxSaveSeconds, + "max_save_count": policy.MaxSaveCount, + "maxSaveCount": policy.MaxSaveCount, + "uploadCount": uploadCount, + "uploadMinute": uploadMinute, + "enableChunk": cfg.EnableChunk(), + "openUpload": cfg.OpenUpload(), + }, + "meta": gin.H{ + "version": d.Version, + "features": gin.H{ + "chunkUpload": cfg.EnableChunk(), + "guestUpload": cfg.OpenUpload(), + }, + }, + }, + }) +} + +// requireShareLogin 分享上传权限(对齐参考 share_required_login): +// openUpload 开启时游客可传;关闭时要求管理员 Bearer token(403)。 +func (d *Deps) requireShareLogin(c *gin.Context) bool { + if d.Cfg.OpenUpload() { + return true + } + header := c.GetHeader("Authorization") + const prefix = "Bearer " + if len(header) <= len(prefix) || header[:len(prefix)] != prefix { + response.Fail(c, http.StatusForbidden, "本站未开启游客上传,如需上传请先登录后台") + return false + } + token := header[len(prefix):] + if _, err := middleware.VerifyAdminToken(d.jwtSecret(), token); err != nil { + response.Fail(c, http.StatusForbidden, "本站未开启游客上传,如需上传请先登录后台") + return false + } + return true +} diff --git a/server/internal/api/security_fixes_test.go b/server/internal/api/security_fixes_test.go new file mode 100644 index 0000000..6b25f6a --- /dev/null +++ b/server/internal/api/security_fixes_test.go @@ -0,0 +1,193 @@ +// security_fixes_test.go — 安全审计修复项行为测试: +// L4 enableChunk 强制、M2 presign 大小/类型校验、L3 提码长度、M3 chunk_size 上限。 +package api + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/gin-gonic/gin" + + "filecodebox/internal/model" + "filecodebox/internal/settings" +) + +// postJSON 以 JSON body 调用 POST 端点。 +func postJSON(d *Deps, path string, body any) *httptest.ResponseRecorder { + var reader *bytes.Reader + if body == nil { + reader = bytes.NewReader(nil) + } else { + raw, _ := json.Marshal(body) + reader = bytes.NewReader(raw) + } + req := httptest.NewRequest(http.MethodPost, path, reader) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = req + c.Params = append(c.Params, gin.Param{Key: "uploadID", Value: req.URL.Path[len("/presign/upload/confirm/"):]}) + d.presignConfirm(c) + return w +} + +// sha256LegacyHash 构造旧版 sha256$salt$hash 格式(M1 迁移测试用)。 +func sha256LegacyHash(password string) string { + salt := make([]byte, 16) + for i := range salt { + salt[i] = byte(i) + } + saltHex := hex.EncodeToString(salt) + sum := sha256.Sum256([]byte(saltHex + password)) + return "sha256$" + saltHex + "$" + hex.EncodeToString(sum[:]) +} + +// TestChunkToggleEnforced L4:enableChunk=0 时 /chunk 相关端点一律 403。 +func TestChunkToggleEnforced(t *testing.T) { + d := newPolicyTestDeps(t) + // 默认 enableChunk=0 + w := chunkInitJSON(d, `{"file_name":"a.png","file_size":500,"chunk_size":1024,"file_hash":"h"}`) + if code, _ := respBody(t, w); code != http.StatusForbidden { + t.Fatalf("enableChunk=0 时 init 应 403: %d %s", code, w.Body.String()) + } + // 开启后放行 + if w := patchConfig(d, map[string]any{"enableChunk": 1}); w.Code != 200 { + t.Fatalf("patch enableChunk: %d", w.Code) + } + w = chunkInitJSON(d, `{"file_name":"a.png","file_size":500,"chunk_size":1024,"file_hash":"h"}`) + if code, _ := respBody(t, w); code != 200 { + t.Fatalf("enableChunk=1 时 init 应 200: %d %s", code, w.Body.String()) + } +} + +// TestChunkSizeCap M3:chunk_size 超过 32MB 上限时 400。 +func TestChunkSizeCap(t *testing.T) { + d := newPolicyTestDeps(t) + if w := patchConfig(d, map[string]any{"enableChunk": 1}); w.Code != 200 { + t.Fatalf("patch enableChunk: %d", w.Code) + } + w := chunkInitJSON(d, `{"file_name":"a.bin","file_size":70000000000,"chunk_size":34000000,"file_hash":"h"}`) + if code, _ := respBody(t, w); code != http.StatusBadRequest { + t.Fatalf("chunk_size 超上限应 400: %d %s", code, w.Body.String()) + } +} + +// TestPickupCodeMinLen L3:4 位自定义码拒绝、5 位通过。 +func TestPickupCodeMinLen(t *testing.T) { + if err := validatePickupCode("abcd"); err == nil { + t.Fatal("4 位码应被拒绝") + } + if err := validatePickupCode("abcde"); err != nil { + t.Fatalf("5 位码应通过: %v", err) + } +} + +// TestPresignConfirmRejectsOversizeObject M2: +// 直传会话 confirm 时,若对象实际大小超过策略上限,应删除对象并 403。 +func TestPresignConfirmRejectsOversizeObject(t *testing.T) { + d := newPolicyTestDeps(t) + ctx := context.Background() + + // 声明 10 字节、策略上限 100 → 实际 PUT 500 字节对象 + if err := d.Mgr.UpdateKV(ctx, map[string]any{"max_file_size": 100}); err != nil { + t.Fatalf("UpdateKV: %v", err) + } + if err := d.Mgr.Reload(ctx); err != nil { + t.Fatalf("Reload: %v", err) + } + + uploadID := "test-oversize-confirm" + savePath := "share/data/presign_test.bin" + if _, err := d.Store.SaveFile(ctx, bytes.NewReader(make([]byte, 500)), savePath); err != nil { + t.Fatalf("SaveFile: %v", err) + } + sess := model.PresignUploadSession{ + UploadID: uploadID, FileName: "presign_test.bin", FileSize: 10, + SavePath: savePath, Mode: "direct", + ExpireValue: 1, ExpireStyle: "day", + CreatedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour), Engine: "local", + } + if err := d.DB.WithContext(ctx).Create(&sess).Error; err != nil { + t.Fatalf("create session: %v", err) + } + res := model.StorageReservation{Token: "presign:" + uploadID, Size: 10, ExpiresAt: time.Now().Add(time.Hour)} + if err := d.DB.WithContext(ctx).Create(&res).Error; err != nil { + t.Fatalf("create reservation: %v", err) + } + + w := postJSON(d, "/presign/upload/confirm/"+uploadID, nil) + if w.Code != http.StatusForbidden { + t.Fatalf("超限对象 confirm 应 403: %d %s", w.Code, w.Body.String()) + } + // 对象应被删除、预留应释放 + if ok, _ := d.Store.FileExists(ctx, savePath); ok { + t.Fatal("超限对象应被服务端删除") + } + var cnt int64 + _ = d.DB.WithContext(ctx).Model(&model.StorageReservation{}).Where("token = ?", res.Token).Count(&cnt).Error + if cnt != 0 { + t.Fatal("预留应被释放") + } +} + +// TestPresignConfirmRejectsSizeMismatch M2:实际大小与声明差超过 ±1KB 时 400。 +func TestPresignConfirmRejectsSizeMismatch(t *testing.T) { + d := newPolicyTestDeps(t) + ctx := context.Background() + + uploadID := "test-mismatch-confirm" + savePath := "share/data/presign_mismatch.bin" + if _, err := d.Store.SaveFile(ctx, bytes.NewReader(make([]byte, 2048)), savePath); err != nil { + t.Fatalf("SaveFile: %v", err) + } + sess := model.PresignUploadSession{ + UploadID: uploadID, FileName: "presign_mismatch.bin", FileSize: 10, + SavePath: savePath, Mode: "proxy", // proxy 模式同样走大小核对(多引擎一致) + ExpireValue: 1, ExpireStyle: "day", + CreatedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour), Engine: "local", + } + if err := d.DB.WithContext(ctx).Create(&sess).Error; err != nil { + t.Fatalf("create session: %v", err) + } + res := model.StorageReservation{Token: "presign:" + uploadID, Size: 10, ExpiresAt: time.Now().Add(time.Hour)} + if err := d.DB.WithContext(ctx).Create(&res).Error; err != nil { + t.Fatalf("create reservation: %v", err) + } + + w := postJSON(d, "/presign/upload/confirm/"+uploadID, nil) + if w.Code != http.StatusBadRequest { + t.Fatalf("大小不符 confirm 应 400: %d %s", w.Code, w.Body.String()) + } +} + +// TestAdminPasswordAutoUpgrade M1:明文/旧哈希经 VerifyPassword 后 NeedsRehash 为真, +// bcrypt 哈希不再需要升级。 +func TestAdminPasswordAutoUpgrade(t *testing.T) { + if !settings.NeedsRehash("FileCodeBox2023") { + t.Fatal("明文哈希需要升级") + } + legacy := sha256LegacyHash("pwd12345") + if !settings.NeedsRehash(legacy) { + t.Fatal("sha256 哈希需要升级") + } + if !settings.VerifyPassword("pwd12345", legacy) { + t.Fatal("旧 sha256 哈希兼容校验失败") + } + b := settings.HashPassword("pwd12345") + if settings.NeedsRehash(b) { + t.Fatal("bcrypt 哈希不需要升级") + } + if !settings.VerifyPassword("pwd12345", b) { + t.Fatal("bcrypt 校验失败") + } + if settings.VerifyPassword("wrong", b) { + t.Fatal("错误密码不应通过") + } +} diff --git a/server/internal/api/setup.go b/server/internal/api/setup.go new file mode 100644 index 0000000..8b5f2ac --- /dev/null +++ b/server/internal/api/setup.go @@ -0,0 +1,327 @@ +package api + +import ( + "net/http" + "strconv" + "strings" + + "github.com/gin-gonic/gin" + + "filecodebox/internal/response" + "filecodebox/internal/settings" +) + +// fileSizeUnits 文件大小单位(对齐参考 FILE_SIZE_UNITS)。 +var fileSizeUnits = map[string]int64{"KB": 1024, "MB": 1024 * 1024, "GB": 1024 * 1024 * 1024} + +// saveTimeUnits 保存时间单位(秒)。 +var saveTimeUnits = map[string]int64{"second": 1, "minute": 60, "hour": 3600, "day": 86400} + +// expireStyleOptions 可用过期方式(用于 setup 表单校验)。 +var expireStyleOptions = []string{"day", "hour", "minute", "forever", "count"} + +// setupFormValue 取表单/JSON 字符串值。 +func setupFormValue(data map[string]any, key, def string) string { + v, ok := data[key] + if !ok || v == nil { + return def + } + if s, ok := v.(string); ok { + return s + } + return strings.TrimSpace(strconv.FormatFloat(toAnyFloat(v), 'f', -1, 64)) +} + +func toAnyFloat(v any) float64 { + switch n := v.(type) { + case float64: + return n + case int: + return float64(n) + case int64: + return float64(n) + } + return 0 +} + +// registerSetup 注册初始化向导(未初始化时唯一可用入口,白名单 /setup)。 +func registerSetup(r *gin.Engine, d *Deps) { + r.GET("/setup", func(c *gin.Context) { + if d.Mgr.IsInitialized() { + c.Redirect(http.StatusSeeOther, "/") + return + } + c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(buildSetupPage(""))) + }) + r.POST("/setup", func(c *gin.Context) { + if d.Mgr.IsInitialized() { + c.Redirect(http.StatusSeeOther, "/") + return + } + d.setupSubmit(c) + }) +} + +// setupSubmit 处理初始化提交(对齐参考 setup_submit + parse_setup_options)。 +func (d *Deps) setupSubmit(c *gin.Context) { + // 兼容 JSON 与表单 + data := map[string]any{} + if strings.Contains(c.GetHeader("Content-Type"), "application/json") { + if err := c.ShouldBindJSON(&data); err != nil { + c.Data(http.StatusBadRequest, "text/html; charset=utf-8", []byte(buildSetupPage("请求体格式错误"))) + return + } + } else if err := c.Request.ParseForm(); err != nil { + c.Data(http.StatusBadRequest, "text/html; charset=utf-8", []byte(buildSetupPage("请求体格式错误"))) + return + } else { + // 多值字段(如多个 expireStyle 复选框)保留完整列表,单值取首项 + for k, v := range c.Request.PostForm { + switch { + case len(v) == 1: + data[k] = v[0] + case len(v) > 1: + data[k] = v + } + } + } + + adminPassword := setupFormValue(data, "admin_password", "") + confirmPassword := setupFormValue(data, "confirm_password", "") + siteName := setupFormValue(data, "site_name", "") + + if adminPassword == "" || len(adminPassword) < 8 { + c.Data(http.StatusBadRequest, "text/html; charset=utf-8", []byte(buildSetupPage("管理员密码至少 8 位"))) + return + } + if adminPassword != confirmPassword { + c.Data(http.StatusBadRequest, "text/html; charset=utf-8", []byte(buildSetupPage("两次输入的管理员密码不一致"))) + return + } + patch, errMsg := parseSetupOptions(data) + if errMsg != "" { + c.Data(http.StatusBadRequest, "text/html; charset=utf-8", []byte(buildSetupPage(errMsg))) + return + } + patch["site_name"] = firstNonEmpty(siteName, "文件快传") + patch["admin_token"] = settings.HashPassword(adminPassword) + patch["jwt_secret"] = settings.GenerateJWTSecret() + + ctx := c.Request.Context() + if err := d.Mgr.UpdateKV(ctx, patch); err != nil { + c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte(buildSetupPage("初始化失败: "+err.Error()))) + return + } + if err := d.Mgr.Reload(ctx); err != nil { + c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte(buildSetupPage("配置重载失败: "+err.Error()))) + return + } + d.syncRateRules() + // JSON 请求返回 JSON;表单返回成功页 + if strings.Contains(c.GetHeader("Accept"), "application/json") || + strings.Contains(c.GetHeader("Content-Type"), "application/json") { + response.OK(c, gin.H{"ok": true, "admin": "/#/admin"}) + return + } + c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(buildSetupSuccessPage())) +} + +// parseSetupOptions 解析并校验初始化选项(对齐参考 parse_setup_options)。 +func parseSetupOptions(data map[string]any) (map[string]any, string) { + out := map[string]any{} + + // 文件大小限制 + unit := strings.ToUpper(setupFormValue(data, "upload_size_unit", "MB")) + if _, ok := fileSizeUnits[unit]; !ok { + return nil, "文件大小单位不正确" + } + sizeVal, err := strconv.Atoi(setupFormValue(data, "upload_size_value", "10")) + if err != nil || sizeVal < 1 { + return nil, "文件大小限制必须是正整数" + } + out["uploadSize"] = int64(sizeVal) * fileSizeUnits[unit] + + // 最长保存时间 + saveUnit := strings.ToLower(setupFormValue(data, "save_time_unit", "day")) + if _, ok := saveTimeUnits[saveUnit]; !ok { + return nil, "最长保存时间单位不正确" + } + saveVal, err := strconv.Atoi(setupFormValue(data, "save_time_value", "0")) + if err != nil || saveVal < 0 { + return nil, "最长保存时间必须是非负整数" + } + out["max_save_seconds"] = int64(saveVal) * saveTimeUnits[saveUnit] + + // 过期方式白名单 + var styles []string + if raw, ok := data["expireStyle"]; ok { + switch v := raw.(type) { + case []any: + for _, item := range v { + if s, ok := item.(string); ok { + styles = append(styles, s) + } + } + case []string: + styles = v + case string: + for _, s := range strings.Split(v, ",") { + styles = append(styles, strings.TrimSpace(s)) + } + } + } + valid := map[string]bool{} + var finalStyles []string + for _, s := range styles { + s = strings.TrimSpace(s) + if s == "" || valid[s] { + continue + } + for _, opt := range expireStyleOptions { + if opt == s { + valid[s] = true + finalStyles = append(finalStyles, s) + break + } + } + } + if len(finalStyles) == 0 { + return nil, "至少需要选择一种过期方式" + } + out["expireStyle"] = finalStyles + + // 取件码类型 + codeType := setupFormValue(data, "code_generate_type", "secret") + if codeType != "number" && codeType != "secret" { + return nil, "提取码类型不正确" + } + out["code_generate_type"] = codeType + + // 频率限制 + for _, item := range []struct{ key, def string }{ + {"errorCount", "10"}, {"errorMinute", "1"}, + {"loginCount", "5"}, {"loginMinute", "15"}, + {"uploadCount", "10"}, {"uploadMinute", "1"}, + } { + n, err := strconv.Atoi(setupFormValue(data, item.key, item.def)) + if err != nil || n < 1 { + return nil, item.key + " 必须是正整数" + } + out[item.key] = n + } + + // 布尔开关 + out["openUpload"] = boolToInt(parseSetupBool(data, "openUpload", true)) + out["enableChunk"] = boolToInt(parseSetupBool(data, "enableChunk", false)) + + // 允许文件类型 + allowed := setupFormValue(data, "allowed_file_types", "*") + var types []string + for _, item := range strings.Split(allowed, ",") { + if item = strings.TrimSpace(item); item != "" { + types = append(types, item) + } + } + if len(types) == 0 { + types = []string{"*"} + } + out["allowed_file_types"] = types + return out, "" +} + +// parseSetupBool 解析表单布尔(缺省 default;"1"/"true"/"on"/"yes" 为真)。 +func parseSetupBool(data map[string]any, key string, def bool) bool { + v, ok := data[key] + if !ok { + return def + } + switch s := v.(type) { + case string: + switch strings.ToLower(strings.TrimSpace(s)) { + case "1", "true", "on", "yes": + return true + case "0", "false", "off", "no", "": + return false + } + case float64: + return s != 0 + case bool: + return s + } + return def +} + +// buildSetupPage 初始化向导页面(简洁中文表单)。 +func buildSetupPage(errMsg string) string { + errBlock := "" + if errMsg != "" { + errBlock = `
` + htmlEscape(errMsg) + `
` + } + return ` + + + + + 初始化 文件快传 + + +
+

初始化 文件快传

+

首次配置管理员密码、上传限制和取件策略,后续可在后台调整。

+ ` + errBlock + ` +
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + +
+
` +} + +// buildSetupSuccessPage 初始化完成页。 +func buildSetupSuccessPage() string { + return ` +初始化完成 + +
+

初始化完成

管理员密码已设置,请使用刚才的密码登录后台。

进入后台 +
` +} + +// htmlEscape HTML 转义(错误信息拼接用)。 +func htmlEscape(s string) string { + r := strings.NewReplacer("&", "&", "<", "<", ">", ">", `"`, "'") + return r.Replace(s) +} diff --git a/server/internal/api/share.go b/server/internal/api/share.go new file mode 100644 index 0000000..c9f304c --- /dev/null +++ b/server/internal/api/share.go @@ -0,0 +1,626 @@ +package api + +import ( + "errors" + "fmt" + "io" + "net/http" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + + "filecodebox/internal/audit" + "filecodebox/internal/middleware" + "filecodebox/internal/model" + "filecodebox/internal/response" + "filecodebox/internal/storage" +) + +// nowRFC3339 当前时间的 RFC3339 表示。 +func nowRFC3339() string { return time.Now().Format(time.RFC3339) } + +// lookupByCode 按取件码查询分享记录(对齐参考 get_code_file_by_code): +// 不存在返回 "文件不存在";expired=true 时过期返回 "文件已过期"。 +// 服务端兜底:历史「链接+提取码」复制格式会把「CODE CODE」整串当码传入 +// (空格经 URL 编码进 query/path),取第一段有效码避免误报不存在。 +func (d *Deps) lookupByCode(c *gin.Context, code string, checkExpired bool) (*model.FileCodes, error) { + code = strings.TrimSpace(code) + if fields := strings.Fields(code); len(fields) > 1 { + code = fields[0] + } + if code == "" { + return nil, errNotFound("文件不存在") + } + var fc model.FileCodes + err := d.DB.WithContext(c.Request.Context()). + Where("code = ?", code).First(&fc).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errNotFound("文件不存在") + } + if err != nil { + return nil, errInternal("查询失败: " + err.Error()) + } + if checkExpired && fc.Expired(time.Now()) { + return nil, errNotFound("文件已过期") + } + return &fc, nil +} + +// consumeUsage 原子校验分享状态并记录一次实际领取(对齐参考 consume_file_usage): +// 仅当 expired_count>0(次数剩余)或 expired_count<0 且未到过期时间时扣减成功。 +func (d *Deps) consumeUsage(c *gin.Context, fc *model.FileCodes) bool { + now := time.Now() + res := d.DB.WithContext(c.Request.Context()). + Model(&model.FileCodes{}). + Where("id = ?", fc.ID). + Where("expired_count > 0 OR (expired_count < 0 AND (expired_at IS NULL OR expired_at > ?))", now). + Updates(map[string]any{ + "expired_count": gorm.Expr("CASE WHEN expired_count > 0 THEN expired_count - 1 ELSE expired_count END"), + "used_count": gorm.Expr("used_count + 1"), + }) + return res.Error == nil && res.RowsAffected > 0 +} + +// fileSavePath 拼接分享记录的存储相对路径(file_path/uuid_file_name)。 +func fileSavePath(fc *model.FileCodes) string { + dir := "" + if fc.FilePath != nil { + dir = strings.Trim(*fc.FilePath, "/") + } + name := "" + if fc.UUIDFileName != nil { + name = *fc.UUIDFileName + } + if dir == "" { + return name + } + return dir + "/" + name +} + +// ============ POST /share/text 文本分享 ============ + +// shareText 创建文本分享(对齐参考 share_text)。 +func (d *Deps) shareText(c *gin.Context) { + if !d.requireShareLogin(c) { + return + } + if !requireUploadLimit(c, d.Limiter) { + return + } + // v3.1 修复:JSON/表单/ultipart 统一绑定(form+json 双标签——此前仅 PostForm 时, + // JSON 提交会静默存成空文本并 200,取件页空白)。 + var body struct { + Text string `json:"text" form:"text"` + ExpireValue int `json:"expire_value" form:"expire_value"` + ExpireStyle string `json:"expire_style" form:"expire_style"` + Code string `json:"code" form:"code"` + } + if err := bindJSONOrForm(c, &body); err != nil { + respondError(c, err) + return + } + text := body.Text + if strings.TrimSpace(text) == "" { + response.Fail(c, http.StatusBadRequest, "分享内容不能为空") + return + } + // M3:前置拒绝超大 body(配合全局 BodyLimit;441KB 为 222KB 内容 + 表单/JSON 编码余量) + if c.Request.ContentLength > 441*1024 { + response.Fail(c, http.StatusForbidden, "内容过多,建议采用文件形式") + return + } + expireValue := body.ExpireValue + if expireValue == 0 { + expireValue = 1 + } + expireStyle := body.ExpireStyle + if expireStyle == "" { + expireStyle = "day" + } + // v3.1:自定义提取码格式校验(4-8 位字母数字,空=随机) + if err := validatePickupCode(body.Code); err != nil { + respondError(c, err) + return + } + + exp, err := resolveExpire(d.Cfg, expireValue, expireStyle) + if err != nil { + respondError(c, err) + return + } + textSize := int64(len([]byte(text))) + if textSize > 222*1024 { + response.Fail(c, http.StatusForbidden, "内容过多,建议采用文件形式") + return + } + + ctx := c.Request.Context() + token := "text:" + uuidHex() + if err := reserveStorage(ctx, d.DB, d.Cfg, token, textSize, 300*time.Second); err != nil { + respondError(c, err) + return + } + code, err := pickCustomCode(ctx, d.DB, d.Cfg, body.Code) + if err == nil { + fc := model.FileCodes{ + Code: code, + Text: &text, + Size: textSize, + Prefix: "Text", + ExpiredAt: exp.ExpiredAt, + ExpiredCount: exp.ExpiredCount, + UsedCount: exp.UsedCount, + Engine: d.Store.CurrentName(), // v3:归属引擎戳(文本也记录,保持一致性) + } + err = d.DB.WithContext(ctx).Create(&fc).Error + } + err = mapCodeConflict(err) // v3.1:自定义码唯一索引冲突 → 友好 400 + releaseStorage(ctx, d.DB, token) + if err != nil { + auditRecordFailed(c, d.AuditSvc, "文本分享创建失败") + respondError(c, err) + return + } + + d.Limiter.Add(c, middleware.LimitUpload) // 上传成功才计数 + auditUploadEntry(c, code, "Text", textSize, textSize) + auditRecordSuccess(c, d.AuditSvc) + response.OK(c, gin.H{"code": code}) +} + +// ============ POST /share/file 文件分享 ============ + +// shareFile 上传文件并创建分享(对齐参考 share_file)。 +func (d *Deps) shareFile(c *gin.Context) { + if !d.requireShareLogin(c) { + return + } + if !requireUploadLimit(c, d.Limiter) { + return + } + fh, err := c.FormFile("file") + if err != nil { + auditRecordFailed(c, d.AuditSvc, "缺少 file 字段") + response.Fail(c, http.StatusBadRequest, "缺少上传文件 file 字段") + return + } + origName := fh.Filename + // v2 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize) + if err := d.CurrentUploadPolicy().CheckSize(fh.Size); err != nil { + auditUploadEntry(c, "", origName, fh.Size, 0) + auditRecordFailed(c, d.AuditSvc, "大小超过限制") + respondError(c, err) + return + } + expireValue := formInt(c, "expire_value", 1) + expireStyle := c.DefaultPostForm("expire_style", "day") + exp, err := resolveExpire(d.Cfg, expireValue, expireStyle) + if err != nil { + auditUploadEntry(c, "", origName, fh.Size, 0) + auditRecordFailed(c, d.AuditSvc, "过期策略非法") + respondError(c, err) + return + } + + // v3.1:自定义提取码(落盘前校验,失败快速返回不占容量预留) + if err := validatePickupCode(c.PostForm("code")); err != nil { + auditUploadEntry(c, "", origName, fh.Size, 0) + auditRecordFailed(c, d.AuditSvc, "提取码非法") + respondError(c, err) + return + } + + // magic bytes 防伪造(读前 64 字节) + f, err := fh.Open() + if err != nil { + auditUploadEntry(c, "", origName, fh.Size, 0) + auditRecordFailed(c, d.AuditSvc, "文件读取失败") + response.Fail(c, http.StatusBadRequest, "文件读取失败") + return + } + defer func() { _ = f.Close() }() + if err := validateFileMagic(d.Cfg, origName, fh.Header.Get("Content-Type"), readMultipartHeader(f, 64)); err != nil { + auditUploadEntry(c, "", origName, fh.Size, 0) + auditRecordFailed(c, d.AuditSvc, "文件类型被拒绝") + respondError(c, err) + return + } + + dirPath, prefix, suffix, cleanName, savePath := buildSavePath(d.Cfg, origName, uuidHex()) + ctx := c.Request.Context() + resToken := "file:" + uuidHex() + if err := reserveStorage(ctx, d.DB, d.Cfg, resToken, fh.Size, time.Hour); err != nil { + auditUploadEntry(c, "", origName, fh.Size, 0) + auditRecordFailed(c, d.AuditSvc, "容量预留失败") + respondError(c, err) + return + } + + code, err := pickCustomCode(ctx, d.DB, d.Cfg, c.PostForm("code")) + if err == nil { + if _, err = d.Store.SaveFile(ctx, f, savePath); err != nil { + // 保存失败:清理半写文件 + _ = d.Store.DeleteFile(ctx, savePath) + } + } + if err == nil { + fc := model.FileCodes{ + Code: code, + Prefix: prefix, + Suffix: suffix, + UUIDFileName: &cleanName, + FilePath: &dirPath, + Size: fh.Size, + ExpiredAt: exp.ExpiredAt, + ExpiredCount: exp.ExpiredCount, + UsedCount: exp.UsedCount, + Engine: d.Store.CurrentName(), // v3:归属引擎戳(下载按此取回) + } + if err = d.DB.WithContext(ctx).Create(&fc).Error; err != nil { + err = mapCodeConflict(err) // v3.1 + // 记录创建失败:清理已落盘文件 + _ = d.Store.DeleteFile(ctx, savePath) + } + } else { + // 保存失败:尽力清理半写文件 + _ = d.Store.DeleteFile(ctx, savePath) + } + releaseStorage(ctx, d.DB, resToken) + if err != nil { + auditUploadEntry(c, "", origName, fh.Size, 0) + auditRecordFailed(c, d.AuditSvc, "文件保存失败") + if be, ok := err.(*apiError); ok && be.Status == http.StatusBadRequest { + respondError(c, err) // v3.1:提取码冲突等业务 400 原样透出 + } else { + respondError(c, mapStorageError(err)) + } + return + } + + d.Limiter.Add(c, middleware.LimitUpload) + auditUploadEntry(c, code, origName, fh.Size, fh.Size) + auditRecordSuccess(c, d.AuditSvc) + response.OK(c, gin.H{"code": code, "name": origName}) +} + +// ============ GET/POST /share/metadata 分享元信息(不消耗次数)============ + +// shareMetadataGET 查询分享元信息(对齐参考 get_file_metadata / build_file_metadata)。 +func (d *Deps) shareMetadata(c *gin.Context) { + d.metadataCommon(c, c.Query("code")) +} + +// shareMetadataPost JSON 体查询分享元信息(对齐参考 post_file_metadata)。 +func (d *Deps) shareMetadataPost(c *gin.Context) { + var body struct { + Code string `json:"code"` + } + if err := bindJSONOrForm(c, &body); err != nil { + auditRecordFailed(c, d.AuditSvc, "请求体格式错误") + respondError(c, err) + return + } + d.metadataCommon(c, body.Code) +} + +// metadataCommon 元信息查询公共实现。 +func (d *Deps) metadataCommon(c *gin.Context, code string) { + fc, err := d.lookupByCode(c, code, true) + if err != nil { + auditUploadEntry(c, strings.TrimSpace(code), "", 0, 0) + auditRecordFailed(c, d.AuditSvc, err.Error()) + respondError(c, err) + return + } + auditUploadEntry(c, fc.Code, fc.Prefix+fc.Suffix, fc.Size, 0) + auditRecordSuccess(c, d.AuditSvc) + response.OK(c, buildFileMetadata(fc)) +} + +// buildFileMetadata 构造分享元信息(对齐参考 build_file_metadata,不暴露存储路径)。 +func buildFileMetadata(fc *model.FileCodes) gin.H { + isText := fc.Text != nil + var remaining any + if fc.ExpiredCount > 0 { + remaining = fc.ExpiredCount + } + var expiredAt any + if fc.ExpiredAt != nil { + expiredAt = fc.ExpiredAt.Format(time.RFC3339) + } + return gin.H{ + "code": fc.Code, + "name": fc.Prefix + fc.Suffix, + "size": fc.Size, + "type": map[bool]string{true: "text", false: "file"}[isText], + "is_text": isText, + "created_at": fc.CreatedAt.Format(time.RFC3339), + "expired_at": expiredAt, + "expires_at": expiredAt, + "expired_count": fc.ExpiredCount, + "used_count": fc.UsedCount, + "remaining_downloads": remaining, + } +} + +// ============ GET /share/select 取件(消耗次数,流式下载)============ + +// shareSelect 取件:文本直接返回纯文本;文件流式返回(支持 Range,对齐参考 get_code_file)。 +func (d *Deps) shareSelect(c *gin.Context) { + // error 类限流:进入即检查(对齐参考 Depends(ip_limit["error"])) + if allowed, _ := d.Limiter.Check(c, middleware.LimitError); !allowed { + response.Fail(c, http.StatusLocked, "请求次数过多,请稍后再试") + return + } + code := strings.TrimSpace(c.Query("code")) + fc, err := d.lookupByCode(c, code, true) + if err != nil { + d.Limiter.Add(c, middleware.LimitError) // 取件失败计数 + auditUploadEntry(c, code, "", 0, 0) + auditRecordFailed(c, d.AuditSvc, err.Error()) + respondError(c, err) + return + } + if !d.consumeUsage(c, fc) { + auditUploadEntry(c, fc.Code, fc.Prefix+fc.Suffix, fc.Size, 0) + auditRecordFailed(c, d.AuditSvc, "文件已过期") + response.Fail(c, http.StatusNotFound, "文件已过期") + return + } + if fc.Text != nil { + // 文本分享:text/plain 响应(对齐参考 Response(content=text, media_type=text/plain)) + name := fc.Prefix + suffixOrTxt(fc) + auditUploadEntry(c, fc.Code, name, fc.Size, int64(len(*fc.Text))) + auditRecordSuccess(c, d.AuditSvc) + c.Header("Content-Disposition", contentDisposition(name)) + c.Data(http.StatusOK, "text/plain; charset=utf-8", []byte(*fc.Text)) + return + } + d.serveFile(c, fc) +} + +func suffixOrTxt(fc *model.FileCodes) string { + if fc.Suffix != "" { + return fc.Suffix + } + return ".txt" +} + +// ============ POST /share/select 取件详情(JSON,对齐参考 select_file)============ + +// shareSelectPost 返回分享详情 JSON(元信息+内容/下载地址,对齐参考 select_file)。 +// 有次数限制的文件返回代理下载地址(消耗发生在 download 时),其余在本次消耗。 +func (d *Deps) shareSelectPost(c *gin.Context) { + if allowed, _ := d.Limiter.Check(c, middleware.LimitError); !allowed { + response.Fail(c, http.StatusLocked, "请求次数过多,请稍后再试") + return + } + var body struct { + Code string `json:"code"` + } + if err := bindJSONOrForm(c, &body); err != nil { + respondError(c, err) + return + } + fc, err := d.lookupByCode(c, body.Code, true) + if err != nil { + d.Limiter.Add(c, middleware.LimitError) + respondError(c, err) + return + } + + detail := buildFileMetadata(fc) + var downloadURL string + if fc.Text != nil { + detail["text"] = *fc.Text + detail["content"] = *fc.Text + } else if fc.ExpiredCount >= 0 { + // 有次数限制:必须经代理下载接口扣次数 + downloadURL = d.proxyDownloadURL(fc.Code) + detail["text"] = downloadURL + } else { + // 时间型/永久:优先引擎直链(如 S3 预签名),不支持则代理 + if url, err := d.Store.PresignGetURL(c.Request.Context(), fileSavePath(fc), 3600); err == nil { + downloadURL = url + } else { + downloadURL = d.proxyDownloadURL(fc.Code) + } + detail["text"] = downloadURL + } + detail["download_url"] = nil + if downloadURL != "" { + detail["download_url"] = downloadURL + } + + // 仅当下载地址不是代理地址时在本次消耗次数(对齐参考 consumes_on_download 判定) + consumesOnDownload := strings.HasPrefix(downloadURL, "/share/download?") + if !consumesOnDownload { + if !d.consumeUsage(c, fc) { + response.Fail(c, http.StatusNotFound, "文件已过期") + return + } + for k, v := range buildFileMetadata(fc) { + detail[k] = v + } + } + response.OK(c, detail) +} + +// proxyDownloadURL 生成代理下载地址(对齐参考 get_file_url)。 +func (d *Deps) proxyDownloadURL(code string) string { + secret := d.jwtSecret() + return "/share/download?key=" + GetSelectToken(code, secret, 0) + "&code=" + code +} + +// ============ GET /share/download 代理下载(token 鉴权,消耗次数)============ + +// shareDownload 代理下载(对齐参考 download_file): +// key 为 GetSelectToken 生成的窗口令牌,同时接受当前与上一窗口。 +func (d *Deps) shareDownload(c *gin.Context) { + key := c.Query("key") + code := strings.TrimSpace(c.Query("code")) + + secret := d.jwtSecret() + // L2:HMAC 令牌 + 常量时间比较 + if key == "" || secret == "" || !VerifySelectToken(code, secret, key) { + d.Limiter.Add(c, middleware.LimitError) + auditUploadEntry(c, code, "", 0, 0) + auditRecordFailed(c, d.AuditSvc, "下载鉴权失败") + response.Fail(c, http.StatusForbidden, "下载鉴权失败") + return + } + fc, err := d.lookupByCode(c, code, true) + if err != nil { + auditUploadEntry(c, code, "", 0, 0) + auditRecordFailed(c, d.AuditSvc, err.Error()) + respondError(c, err) + return + } + if !d.consumeUsage(c, fc) { + auditUploadEntry(c, fc.Code, fc.Prefix+fc.Suffix, fc.Size, 0) + auditRecordFailed(c, d.AuditSvc, "文件已过期") + response.Fail(c, http.StatusNotFound, "文件已过期") + return + } + if fc.Text != nil { + auditUploadEntry(c, fc.Code, fc.Prefix+fc.Suffix, fc.Size, int64(len(*fc.Text))) + auditRecordSuccess(c, d.AuditSvc) + response.OK(c, *fc.Text) + return + } + d.serveFile(c, fc) +} + +// ============ 文件流式下载(含 Range)============ + +// parseRangeHeader 解析 Range 头(仅 bytes 单区间,多区间/单位错误返回 nil 交由全量处理; +// 合法但越界由引擎返回 ErrRangeNotSatisfiable→416)。 +func parseRangeHeader(header string, totalSize int64) *storage.Range { + header = strings.TrimSpace(header) + if header == "" || !strings.HasPrefix(header, "bytes=") { + return nil + } + spec := strings.TrimPrefix(header, "bytes=") + if strings.Contains(spec, ",") { // 多区间不支持,回退全量 + return nil + } + dash := strings.Index(spec, "-") + if dash < 0 { + return nil + } + startStr, endStr := strings.TrimSpace(spec[:dash]), strings.TrimSpace(spec[dash+1:]) + if startStr == "" { + // bytes=-N:后缀区间 + n, err := strconv.ParseInt(endStr, 10, 64) + if err != nil || n <= 0 { + return nil + } + if totalSize > 0 && n >= totalSize { + n = totalSize + } + return &storage.Range{Start: totalSize - n, End: -1} + } + start, err := strconv.ParseInt(startStr, 10, 64) + if err != nil || start < 0 { + return nil + } + if endStr == "" { + return &storage.Range{Start: start, End: -1} + } + end, err := strconv.ParseInt(endStr, 10, 64) + if err != nil || end < start { + return nil + } + return &storage.Range{Start: start, End: end} +} + +// serveFile 打开存储流并写响应(200 全量 / 206 区间,自动 Accept-Ranges/Content-Range)。 +// 响应字节数由审计中间件包装的 Writer 自动统计。 +func (d *Deps) serveFile(c *gin.Context, fc *model.FileCodes) { + ctx := c.Request.Context() + savePath := fileSavePath(fc) + name := fc.Prefix + fc.Suffix + + // v3:按文件归属引擎取回(切换引擎后旧文件仍可下载);空戳=历史数据回落当前引擎 + store, err := d.storeFor(fc.Engine) + if err != nil { + auditUploadEntry(c, fc.Code, name, fc.Size, 0) + auditRecordFailed(c, d.AuditSvc, "存储引擎不可用: "+err.Error()) + respondError(c, mapStorageError(err)) + return + } + + // 先 Stat 拿总大小(用于审计与 Range 后缀解析) + var total int64 = -1 + if meta, err := store.Stat(ctx, savePath); err == nil && meta != nil { + total = meta.Size + } + + rng := parseRangeHeader(c.GetHeader("Range"), total) + dl, err := store.Open(ctx, savePath, rng) + if err != nil { + auditUploadEntry(c, fc.Code, name, fc.Size, 0) + auditRecordFailed(c, d.AuditSvc, "文件读取失败") + respondError(c, mapStorageError(err)) + return + } + defer func() { _ = dl.Close() }() + + if dl.Total >= 0 { + total = dl.Total + } + c.Header("Accept-Ranges", "bytes") + c.Header("Content-Disposition", contentDisposition(name)) + c.Header("Content-Type", "application/octet-stream") + + status := http.StatusOK + if rng != nil { + end := dl.End + if end < 0 && total >= 0 { + end = total - 1 + } + if end < dl.Start { + auditUploadEntry(c, fc.Code, name, total, 0) + auditRecordFailed(c, d.AuditSvc, "请求范围超出文件大小") + c.Header("Content-Range", fmt.Sprintf("bytes */%d", total)) + response.Fail(c, http.StatusRequestedRangeNotSatisfiable, "请求范围超出文件大小") + return + } + status = http.StatusPartialContent + c.Header("Content-Range", fmt.Sprintf("bytes %d-%d/%d", dl.Start, end, total)) + c.Header("Content-Length", strconv.FormatInt(end-dl.Start+1, 10)) + } else if total >= 0 { + c.Header("Content-Length", strconv.FormatInt(total, 10)) + } + auditUploadEntry(c, fc.Code, name, total, 0) + c.Status(status) + n, _ := io.Copy(c.Writer, dl) + middleware.AuditSet(c, func(e *audit.Entry) { + if e.TransferredBytes == 0 { + e.TransferredBytes = n + } + if e.SizeBytes == 0 { + e.SizeBytes = total + } + }) + auditRecordSuccess(c, d.AuditSvc) +} + +// formInt 读取表单整数(缺省 default 值,非法值亦回退 default)。 +func formInt(c *gin.Context, key string, def int) int { + raw := c.PostForm(key) + if raw == "" { + raw = c.Query(key) + } + if raw == "" { + return def + } + n, err := strconv.Atoi(raw) + if err != nil { + return def + } + return n +} diff --git a/server/internal/api/v31_test.go b/server/internal/api/v31_test.go new file mode 100644 index 0000000..2f2d3c5 --- /dev/null +++ b/server/internal/api/v31_test.go @@ -0,0 +1,170 @@ +package api + +// v3.1:自定义提取码与站点域名单元测试。 + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + + "github.com/gin-gonic/gin" +) + +// postForm 以 urlencoded 表单调用 handler(v3.1 测试辅助)。 +func postForm(d *Deps, path string, fields map[string]string) *httptest.ResponseRecorder { + form := url.Values{} + for k, v := range fields { + form.Set(k, v) + } + req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(form.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + var handler gin.HandlerFunc + switch path { + case "/share/text": + handler = d.shareText + default: + handler = func(c *gin.Context) { c.AbortWithStatus(http.StatusNotFound) } + } + return invoke(handler, req) +} + +func TestValidatePickupCode(t *testing.T) { + // 合法:空(用随机码) + if err := validatePickupCode(""); err != nil { + t.Fatalf("空码应合法: %v", err) + } + // 合法:5-8 位字母数字(L3:最小长度由 4 提升至 5) + for _, c := range []string{"abcde", "AB123", "12345678", "a1B2c"} { + if err := validatePickupCode(c); err != nil { + t.Fatalf("合法码 %s 不应报错: %v", c, err) + } + } + // 非法:长度(4 位及以下不再允许) + for _, c := range []string{"abcd", "a1B2", "abc", "123456789"} { + if err := validatePickupCode(c); err == nil { + t.Fatalf("非法长度 %s 应报错", c) + } + } + // 非法:字符 + for _, c := range []string{"ab c1", "提码", "ab-cd", "ab.cd", "ab+cd"} { + if err := validatePickupCode(c); err == nil { + t.Fatalf("非法字符 %s 应报错", c) + } + } +} + +func TestNormalizeSiteDomain(t *testing.T) { + // 空 = 当前地址 + d, err := normalizeSiteDomain("") + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if d != "" { + t.Fatalf("want empty, got %q", d) + } + // 完整 URL + d, err = normalizeSiteDomain("https://share.example.com") + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if d != "https://share.example.com" { + t.Fatalf("want https://share.example.com, got %q", d) + } + // 带端口 + 去尾斜杠 + d, err = normalizeSiteDomain("http://192.168.1.5:8466/") + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if d != "http://192.168.1.5:8466" { + t.Fatalf("want http://192.168.1.5:8466, got %q", d) + } + // 裸主机自动补 http + d, err = normalizeSiteDomain("share.example.com") + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if d != "http://share.example.com" { + t.Fatalf("want http://share.example.com, got %q", d) + } + // 非法:路径 / 协议 + for _, bad := range []string{"https://a.com/path", "ftp://a.com", "javascript:alert(1)"} { + if _, err := normalizeSiteDomain(bad); err == nil { + t.Fatalf("非法域名 %s 应报错", bad) + } + } +} + +// TestShareTextTextPlainCompat 复刻真实浏览器请求形态: +// 旧前端 bundle 发 text/plain Content-Type + urlencoded body(fetch 字符串 body 默认头)。 +// 修复前:该形态被静默存成空文本(bug 1)或 400「分享内容不能为空」(bug 2)。 +func TestShareTextTextPlainCompat(t *testing.T) { + d := newPolicyTestDeps(t) + + body := strings.NewReader("text=111&expire_value=1&expire_style=day&code=") + req := httptest.NewRequest(http.MethodPost, "/share/text", body) + req.Header.Set("Content-Type", "text/plain;charset=UTF-8") + w := invoke(d.shareText, req) + if w.Code != http.StatusOK { + t.Fatalf("text/plain+urlencoded 应 200: %d %s", w.Code, w.Body.String()) + } + + // JSON 体但 Content-Type 缺失/为 text/plain 也应可解析 + req2 := httptest.NewRequest(http.MethodPost, "/share/text", + strings.NewReader(`{"text":"无头JSON","expire_value":1,"expire_style":"day"}`)) + req2.Header.Set("Content-Type", "text/plain;charset=UTF-8") + w2 := invoke(d.shareText, req2) + if w2.Code != http.StatusOK { + t.Fatalf("text/plain+JSON体 应 200: %d %s", w2.Code, w2.Body.String()) + } + + // 取件确认内容真实落库 + req3 := httptest.NewRequest(http.MethodPost, "/share/select", + strings.NewReader(`{"code":"`+codeOf(w)+`"}`)) + req3.Header.Set("Content-Type", "application/json") + w3 := invoke(d.shareSelectPost, req3) + if !strings.Contains(w3.Body.String(), "111") { + t.Fatalf("落库内容应为 111: %s", w3.Body.String()) + } +} + +// codeOf 从创建响应提取取件码。 +func codeOf(w *httptest.ResponseRecorder) string { + var env struct { + Data struct { + Code string `json:"code"` + } `json:"data"` + } + _ = json.Unmarshal(w.Body.Bytes(), &env) + return env.Data.Code +} + +func TestShareTextCustomCode(t *testing.T) { + d := newPolicyTestDeps(t) + + // 自定义码成功创建 + w := postForm(d, "/share/text", map[string]string{"text": "自定义码测试", "code": "MYCODE1"}) + if w.Code != http.StatusOK { + t.Fatalf("自定义码创建失败: %d %s", w.Code, w.Body.String()) + } + + // 重复占用 → 400 + w = postForm(d, "/share/text", map[string]string{"text": "第二条", "code": "MYCODE1"}) + if w.Code != http.StatusBadRequest { + t.Fatalf("占用码应 400: %d %s", w.Code, w.Body.String()) + } + + // 非法码 → 400 + w = postForm(d, "/share/text", map[string]string{"text": "第三条", "code": "abc"}) + if w.Code != http.StatusBadRequest { + t.Fatalf("过短码应 400: %d %s", w.Code, w.Body.String()) + } + + // 空码 → 随机码仍正常 + w = postForm(d, "/share/text", map[string]string{"text": "第四条", "code": ""}) + if w.Code != http.StatusOK { + t.Fatalf("空码应回退随机: %d %s", w.Code, w.Body.String()) + } +} diff --git a/server/internal/api/web.go b/server/internal/api/web.go new file mode 100644 index 0000000..31b9098 --- /dev/null +++ b/server/internal/api/web.go @@ -0,0 +1,75 @@ +package api + +import ( + "io/fs" + "net/http" + "path" + "strings" + + "github.com/gin-gonic/gin" + + "filecodebox/internal/response" + web "filecodebox/web" +) + +// registerWeb 注册前端静态资源与 SPA 回退(必须最后注册): +// - 静态资源命中 web/dist 内文件则直接服务(带 Immutable 缓存,html 不缓存); +// - 未命中且为 GET/HEAD 且非 /api 前缀:回退 index.html(前端 history 路由 +// /s/:code、/admin/*、/docs、/openapi 由 SPA 接管); +// - /api/* 未命中路由:JSON 404(避免调试时拿到 HTML 掩盖真实错误)。 +func registerWeb(r *gin.Engine, d *Deps) { + dist, err := web.Dist() + if err != nil { + return // 嵌入异常时跳过(API 仍可用) + } + fileServer := http.StripPrefix("/", http.FileServer(http.FS(dist))) + indexHTML := readIndexHTML(dist) + + r.NoRoute(func(c *gin.Context) { + p := c.Request.URL.Path + // 1. API 未命中:JSON 404 + if strings.HasPrefix(p, "/api/") || p == "/api" { + response.Fail(c, http.StatusNotFound, "接口不存在") + return + } + // 2. 静态资源命中:直接服务 + if p != "/" { + clean := strings.TrimPrefix(path.Clean(p), "/") + if clean != "" { + if f, err := dist.Open(clean); err == nil { + _ = f.Close() + fileServer.ServeHTTP(c.Writer, c.Request) + return + } + } + } + // 3. SPA 回退:仅 GET/HEAD 且接受 HTML + if c.Request.Method == http.MethodGet || c.Request.Method == http.MethodHead { + accept := c.GetHeader("Accept") + if accept == "" || strings.Contains(accept, "text/html") || strings.Contains(accept, "*/*") { + if indexHTML != nil { + c.Data(http.StatusOK, "text/html; charset=utf-8", indexHTML) + return + } + } + // 非 HTML 请求未命中:普通 404 + c.Status(http.StatusNotFound) + return + } + c.Status(http.StatusNotFound) + }) +} + +// readIndexHTML 读取嵌入的 index.html(SPA 回退用)。 +func readIndexHTML(dist fs.FS) []byte { + f, err := dist.Open("index.html") + if err != nil { + return nil + } + defer func() { _ = f.Close() }() + data, err := fs.ReadFile(dist, "index.html") + if err != nil { + return nil + } + return data +} diff --git a/server/internal/audit/audit.go b/server/internal/audit/audit.go new file mode 100644 index 0000000..8ba9176 --- /dev/null +++ b/server/internal/audit/audit.go @@ -0,0 +1,212 @@ +// Package audit 提供上传/下载审计日志服务(需求 ③): +// 记录操作时间/IP/UA/设备解析/动作/结果/字节数/耗时,落库 Postgres。 +package audit + +import ( + "context" + "errors" + "log" + "strings" + "sync" + "time" + + "gorm.io/gorm" + + "filecodebox/internal/model" +) + +// Service 审计日志服务。 +type Service struct { + sink Sink +} + +// Sink 审计落库抽象(生产为 Postgres,测试为内存实现)。 +type Sink interface { + // Save 批量落库。 + Save(ctx context.Context, logs []model.AuditLog) error +} + +// DBSink 基于 GORM 的落库实现。 +type DBSink struct{ db *gorm.DB } + +// NewDBSink 构造数据库落库实现。 +func NewDBSink(db *gorm.DB) *DBSink { return &DBSink{db: db} } + +// Save 批量插入审计记录。 +func (s *DBSink) Save(ctx context.Context, logs []model.AuditLog) error { + if len(logs) == 0 { + return nil + } + return s.db.WithContext(ctx).CreateInBatches(&logs, 200).Error +} + +// NewService 构造审计服务。 +func NewService(sink Sink) *Service { + return &Service{sink: sink} +} + +// Entry 一次待落库的审计事件。 +type Entry struct { + Action string // upload | download + FileCode string // 取件码 + FileName string // 原始文件名 + SizeBytes int64 // 文件总字节数 + TransferredBytes int64 // 实际传输字节数 + IP string // 客户端 IP + UserAgent string // User-Agent + DeviceOS string // 操作系统 + DeviceBrowser string // 浏览器 + DeviceType string // desktop/mobile/tablet/bot/other + Actor string // admin | guest + Result string // success | denied | failed + ErrorMsg string // 失败原因 + Duration time.Duration // 耗时 +} + +// Record 异步写入一条审计日志:先尝试同步落库,失败时进入内存缓冲等待重试, +// 避免审计失败影响主请求,也避免高峰期阻塞。 +func (s *Service) Record(entry Entry) { + record := model.AuditLog{ + Action: entry.Action, + FileCode: truncate(entry.FileCode, 64), + FileName: truncate(entry.FileName, 255), + SizeBytes: entry.SizeBytes, + TransferredBytes: entry.TransferredBytes, + IP: truncate(entry.IP, 64), + UserAgent: truncate(entry.UserAgent, 512), + DeviceOS: truncate(entry.DeviceOS, 64), + DeviceBrowser: truncate(entry.DeviceBrowser, 64), + DeviceType: truncate(entry.DeviceType, 32), + Actor: truncate(entry.Actor, 64), + Result: normalizeResult(entry.Result), + ErrorMsg: truncate(entry.ErrorMsg, 512), + DurationMs: entry.Duration.Milliseconds(), + } + go func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := s.sink.Save(ctx, []model.AuditLog{record}); err != nil { + log.Printf("[audit] 审计日志落库失败,进入重试队列: %v", err) + s.enqueue(record) + } + }() +} + +// retryBuf 落库失败时的内存重试缓冲。 +var retryBuf struct { + sync.Mutex + items []model.AuditLog +} + +const maxRetryBuffer = 10000 + +// enqueue 入队;超出上限时丢弃最旧的,防止内存无限增长。 +func (s *Service) enqueue(record model.AuditLog) { + retryBuf.Lock() + if len(retryBuf.items) >= maxRetryBuffer { + retryBuf.items = retryBuf.items[1:] + } + retryBuf.items = append(retryBuf.items, record) + retryBuf.Unlock() +} + +// FlushRetry 将缓冲中的审计日志重新落库;由后台定时任务调用。 +func (s *Service) FlushRetry() { + retryBuf.Lock() + if len(retryBuf.items) == 0 { + retryBuf.Unlock() + return + } + items := retryBuf.items + retryBuf.items = nil + retryBuf.Unlock() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + if err := s.sink.Save(ctx, items); err != nil { + log.Printf("[audit] 重试队列落库失败: %v", err) + // 失败则放回队首 + retryBuf.Lock() + retryBuf.items = append(items, retryBuf.items...) + if len(retryBuf.items) > maxRetryBuffer { + retryBuf.items = retryBuf.items[:maxRetryBuffer] + } + retryBuf.Unlock() + } +} + +// StartRetryLoop 启动后台重试循环。 +func (s *Service) StartRetryLoop(stop <-chan struct{}) { + go func() { + ticker := time.NewTicker(30 * time.Second) + defer ticker.Stop() + for { + select { + case <-stop: + s.FlushRetry() + return + case <-ticker.C: + s.FlushRetry() + } + } + }() +} + +// Query 按条件分页查询审计日志(管理端使用)。 +// action/ip/result 为可选过滤;begin/end 为创建时间范围(可选)。 +func (s *Service) Query(page, pageSize int, action, ip, result string, begin, end *time.Time) ([]model.AuditLog, int64, error) { + dbSink, ok := s.sink.(*DBSink) + if !ok { + return nil, 0, errors.New("audit: 当前 sink 不支持查询") + } + if page < 1 { + page = 1 + } + if pageSize < 1 || pageSize > 200 { + pageSize = 20 + } + q := dbSink.db.Model(&model.AuditLog{}) + if action != "" { + q = q.Where("action = ?", action) + } + if ip != "" { + q = q.Where("ip = ?", ip) + } + if result != "" { + q = q.Where("result = ?", result) + } + if begin != nil { + q = q.Where("created_at >= ?", *begin) + } + if end != nil { + q = q.Where("created_at <= ?", *end) + } + var total int64 + if err := q.Count(&total).Error; err != nil { + return nil, 0, err + } + var logs []model.AuditLog + err := q.Order("id DESC"). + Offset((page - 1) * pageSize). + Limit(pageSize). + Find(&logs).Error + return logs, total, err +} + +func truncate(s string, n int) string { + if len(s) <= n { + return s + } + return s[:n] +} + +func normalizeResult(r string) string { + switch strings.TrimSpace(r) { + case model.AuditResultSuccess, model.AuditResultDenied, model.AuditResultFailed: + return strings.TrimSpace(r) + case "": + return model.AuditResultFailed + default: + return model.AuditResultFailed + } +} diff --git a/server/internal/audit/ua.go b/server/internal/audit/ua.go new file mode 100644 index 0000000..4f53200 --- /dev/null +++ b/server/internal/audit/ua.go @@ -0,0 +1,84 @@ +package audit + +import ( + "strings" +) + +// DeviceInfo 从 User-Agent 解析出的设备信息。 +type DeviceInfo struct { + OS string // Windows/macOS/Android/iOS/Linux/Unknown + Browser string // Chrome/Firefox/Safari/Edge/Other + Type string // desktop/mobile/tablet/bot/other +} + +// 动作常量。 +const ( + ActionUpload = "upload" + ActionDownload = "download" + // ActionAdmin 管理端敏感操作(登录/登出/配置/密码/引擎切换/文件删除等), + // L5:纳入审计以便追溯登录失败与配置变更。 + ActionAdmin = "admin" +) + +// 角色常量。 +const ( + ActorAdmin = "admin" + ActorGuest = "guest" +) + +// botKeywords 常见爬虫/机器人标识。 +var botKeywords = []string{"bot", "spider", "crawl", "slurp", "curl/", "wget", "python-requests", "go-http-client"} + +// ParseUserAgent 解析 User-Agent 为设备信息(轻量规则,避免引入重依赖)。 +func ParseUserAgent(ua string) DeviceInfo { + ua = strings.TrimSpace(ua) + lower := strings.ToLower(ua) + if ua == "" { + return DeviceInfo{OS: "Unknown", Browser: "Other", Type: "other"} + } + for _, kw := range botKeywords { + if strings.Contains(lower, kw) { + return DeviceInfo{OS: "Unknown", Browser: "Other", Type: "bot"} + } + } + + info := DeviceInfo{OS: "Unknown", Browser: "Other", Type: "desktop"} + + // 操作系统 + switch { + case strings.Contains(lower, "windows"): + info.OS = "Windows" + case strings.Contains(lower, "iphone"), strings.Contains(lower, "ipod"): + info.OS = "iOS" + info.Type = "mobile" + case strings.Contains(lower, "ipad"): + info.OS = "iOS" + info.Type = "tablet" + case strings.Contains(lower, "mac os x"), strings.Contains(lower, "macintosh"): + info.OS = "macOS" + case strings.Contains(lower, "android"): + info.OS = "Android" + info.Type = "mobile" + if strings.Contains(lower, "tablet") || !strings.Contains(lower, "mobile") { + info.Type = "tablet" + } + case strings.Contains(lower, "linux"), strings.Contains(lower, "ubuntu"), strings.Contains(lower, "fedora"): + info.OS = "Linux" + } + + // 浏览器(顺序重要:Edge/OPR 必须在 Chrome 之前判断) + switch { + case strings.Contains(lower, "edg/"), strings.Contains(lower, "edge/"): + info.Browser = "Edge" + case strings.Contains(lower, "opr/"), strings.Contains(lower, "opera"): + info.Browser = "Opera" + case strings.Contains(lower, "chrome/"), strings.Contains(lower, "crios/"): + info.Browser = "Chrome" + case strings.Contains(lower, "firefox/"), strings.Contains(lower, "fxios/"): + info.Browser = "Firefox" + case strings.Contains(lower, "safari/"): + info.Browser = "Safari" + } + + return info +} diff --git a/server/internal/audit/ua_test.go b/server/internal/audit/ua_test.go new file mode 100644 index 0000000..08e17f2 --- /dev/null +++ b/server/internal/audit/ua_test.go @@ -0,0 +1,45 @@ +package audit + +import "testing" + +func TestParseUserAgent(t *testing.T) { + cases := []struct { + ua string + os string + browser string + typ string + }{ + { + ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", + os: "Windows", browser: "Chrome", typ: "desktop", + }, + { + ua: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1", + os: "iOS", browser: "Safari", typ: "mobile", + }, + { + ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0", + os: "macOS", browser: "Edge", typ: "desktop", + }, + { + ua: "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", + os: "Android", browser: "Chrome", typ: "mobile", + }, + { + ua: "curl/8.4.0", + os: "Unknown", browser: "Other", typ: "bot", + }, + { + ua: "Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0", + os: "Linux", browser: "Firefox", typ: "desktop", + }, + {ua: "", os: "Unknown", browser: "Other", typ: "other"}, + } + for i, tc := range cases { + got := ParseUserAgent(tc.ua) + if got.OS != tc.os || got.Browser != tc.browser || got.Type != tc.typ { + t.Errorf("case %d: ParseUserAgent(%q) = %+v, want os=%s browser=%s type=%s", + i, tc.ua, got, tc.os, tc.browser, tc.typ) + } + } +} diff --git a/server/internal/cache/cache.go b/server/internal/cache/cache.go new file mode 100644 index 0000000..fb485a6 --- /dev/null +++ b/server/internal/cache/cache.go @@ -0,0 +1,42 @@ +// Package cache 提供统一缓存接口:FCB_REDIS_ADDR 未配置时自动降级为进程内存实现, +// 用于 IP 限流计数与热点配置缓存(需求 ② 的可选 Redis 增强)。 +package cache + +import ( + "context" + "errors" + "time" +) + +// ErrNotFound 表示键不存在。 +var ErrNotFound = errors.New("cache: key 不存在") + +// Cache 缓存统一接口。 +type Cache interface { + // Get 读取字符串值;键不存在返回 ErrNotFound。 + Get(ctx context.Context, key string) (string, error) + // Set 写入字符串值,ttl<=0 表示不过期。 + Set(ctx context.Context, key, value string, ttl time.Duration) error + // Delete 删除键。 + Delete(ctx context.Context, keys ...string) error + // Exists 判断键是否存在。 + Exists(ctx context.Context, key string) (bool, error) + // Incr 原子自增;键不存在时从 0 开始并设置 ttl 窗口(限流固定窗口用)。 + Incr(ctx context.Context, key string, ttl time.Duration) (int64, error) + // Close 释放底层资源(Redis 连接;内存实现为空操作)。 + Close() error +} + +// RedisOptions Redis 连接参数(addr 为空 → 内存实现;db 为 FCB_REDIS_DB 库号)。 +type RedisOptions struct { + Addr string + DB int // 逻辑库号 0-15(cluster 模式忽略) +} + +// New 按配置构造缓存实现:redisAddr 为空 → 内存实现。 +func New(ctx context.Context, opt RedisOptions) (Cache, error) { + if opt.Addr == "" { + return NewMemory(), nil + } + return NewRedis(ctx, opt.Addr, opt.DB) +} diff --git a/server/internal/cache/cache_test.go b/server/internal/cache/cache_test.go new file mode 100644 index 0000000..6db80e1 --- /dev/null +++ b/server/internal/cache/cache_test.go @@ -0,0 +1,81 @@ +package cache + +import ( + "context" + "sync" + "testing" + "time" +) + +func TestMemoryCacheSetGet(t *testing.T) { + c := NewMemory() + defer c.Close() + ctx := context.Background() + + if err := c.Set(ctx, "k1", "v1", 0); err != nil { + t.Fatalf("Set 失败: %v", err) + } + v, err := c.Get(ctx, "k1") + if err != nil || v != "v1" { + t.Fatalf("Get = (%q, %v)", v, err) + } + if _, err := c.Get(ctx, "missing"); err != ErrNotFound { + t.Fatalf("缺失键应返回 ErrNotFound: %v", err) + } + _ = c.Delete(ctx, "k1") + if _, err := c.Get(ctx, "k1"); err != ErrNotFound { + t.Fatal("删除后应不存在") + } +} + +func TestMemoryCacheTTL(t *testing.T) { + c := NewMemory() + defer c.Close() + ctx := context.Background() + _ = c.Set(ctx, "ttl", "x", 50*time.Millisecond) + if ok, _ := c.Exists(ctx, "ttl"); !ok { + t.Fatal("TTL 内应存在") + } + time.Sleep(80 * time.Millisecond) + if _, err := c.Get(ctx, "ttl"); err != ErrNotFound { + t.Fatal("过期后应不存在") + } +} + +func TestMemoryCacheIncrWindow(t *testing.T) { + c := NewMemory() + defer c.Close() + ctx := context.Background() + for i := int64(1); i <= 3; i++ { + n, err := c.Incr(ctx, "rl", time.Minute) + if err != nil || n != i { + t.Fatalf("Incr = (%d, %v), want (%d, nil)", n, err, i) + } + } + // 窗口过期后重新计数 + _ = c.Set(ctx, "short", "seed", time.Millisecond) + time.Sleep(5 * time.Millisecond) + n, err := c.Incr(ctx, "short", time.Millisecond) + if err != nil || n != 1 { + t.Fatalf("过期窗口重置失败: (%d, %v)", n, err) + } +} + +func TestMemoryCacheConcurrentIncr(t *testing.T) { + c := NewMemory() + defer c.Close() + ctx := context.Background() + var wg sync.WaitGroup + for i := 0; i < 50; i++ { + wg.Add(1) + go func() { + defer wg.Done() + _, _ = c.Incr(ctx, "cnt", time.Minute) + }() + } + wg.Wait() + n, _ := c.Incr(ctx, "cnt", time.Minute) + if n != 51 { + t.Fatalf("并发计数丢失: %d != 51", n) + } +} diff --git a/server/internal/cache/memory.go b/server/internal/cache/memory.go new file mode 100644 index 0000000..3556585 --- /dev/null +++ b/server/internal/cache/memory.go @@ -0,0 +1,159 @@ +package cache + +import ( + "context" + "sync" + "time" +) + +// memoryItem 内存缓存条目。 +type memoryItem struct { + value string + expiresAt time.Time // 零值表示不过期 +} + +// MemoryCache 进程内存缓存实现(单机、无持久化)。 +type MemoryCache struct { + mu sync.RWMutex + items map[string]memoryItem + done chan struct{} +} + +// NewMemory 构造内存缓存,并启动后台过期清理。 +func NewMemory() *MemoryCache { + m := &MemoryCache{ + items: make(map[string]memoryItem), + done: make(chan struct{}), + } + go m.gcLoop() + return m +} + +// gcLoop 每分钟清理一次过期键,避免长期运行内存膨胀。 +func (m *MemoryCache) gcLoop() { + ticker := time.NewTicker(time.Minute) + defer ticker.Stop() + for { + select { + case <-m.done: + return + case now := <-ticker.C: + m.mu.Lock() + for k, item := range m.items { + if !item.expiresAt.IsZero() && now.After(item.expiresAt) { + delete(m.items, k) + } + } + m.mu.Unlock() + } + } +} + +// Get 读取键值。 +func (m *MemoryCache) Get(_ context.Context, key string) (string, error) { + m.mu.RLock() + item, ok := m.items[key] + m.mu.RUnlock() + if !ok { + return "", ErrNotFound + } + if !item.expiresAt.IsZero() && time.Now().After(item.expiresAt) { + return "", ErrNotFound + } + return item.value, nil +} + +// Set 写入键值。 +func (m *MemoryCache) Set(_ context.Context, key, value string, ttl time.Duration) error { + item := memoryItem{value: value} + if ttl > 0 { + item.expiresAt = time.Now().Add(ttl) + } + m.mu.Lock() + m.items[key] = item + m.mu.Unlock() + return nil +} + +// Delete 删除键。 +func (m *MemoryCache) Delete(_ context.Context, keys ...string) error { + m.mu.Lock() + for _, k := range keys { + delete(m.items, k) + } + m.mu.Unlock() + return nil +} + +// Exists 判断键是否存在。 +func (m *MemoryCache) Exists(_ context.Context, key string) (bool, error) { + _, err := m.Get(context.Background(), key) + return err == nil, nil +} + +// Incr 原子自增;首次创建时记录窗口起点(以过期时间体现)。 +func (m *MemoryCache) Incr(_ context.Context, key string, ttl time.Duration) (int64, error) { + m.mu.Lock() + defer m.mu.Unlock() + now := time.Now() + item, ok := m.items[key] + if ok && !item.expiresAt.IsZero() && now.After(item.expiresAt) { + // 窗口已过期,重新计数 + ok = false + } + var n int64 + if !ok { + n = 1 + newItem := memoryItem{value: "1"} + if ttl > 0 { + newItem.expiresAt = now.Add(ttl) + } + m.items[key] = newItem + return n, nil + } + // 解析现有值 + for _, c := range item.value { + if c < '0' || c > '9' { + n = 0 + break + } + n = n*10 + int64(c-'0') + } + n++ + newItem := memoryItem{value: itoa(n), expiresAt: item.expiresAt} + m.items[key] = newItem + return n, nil +} + +// Close 停止清理协程。 +func (m *MemoryCache) Close() error { + select { + case <-m.done: + default: + close(m.done) + } + return nil +} + +// itoa 简单整数转字符串,避免在锁内依赖 strconv 的额外开销(数值都很小)。 +func itoa(n int64) string { + if n == 0 { + return "0" + } + var buf [20]byte + i := len(buf) + neg := n < 0 + if neg { + n = -n + } + for n > 0 { + i-- + buf[i] = byte('0' + n%10) + n /= 10 + } + if neg { + i-- + buf[i] = '-' + } + return string(buf[i:]) +} diff --git a/server/internal/cache/redis.go b/server/internal/cache/redis.go new file mode 100644 index 0000000..9bfab22 --- /dev/null +++ b/server/internal/cache/redis.go @@ -0,0 +1,117 @@ +package cache + +import ( + "context" + "fmt" + "net/url" + "strings" + "time" + + "github.com/redis/go-redis/v9" +) + +// RedisCache 基于 Redis 的缓存实现(可选增强)。 +type RedisCache struct { + client *redis.Client +} + +// NewRedis 连接 Redis 并校验可用性。addr 支持两种形式: +// - host:port(纯地址,库号由 db 参数指定) +// - redis://[:password@]host:port[/db](URL 形式,URL 中的库号优先于 db 参数) +func NewRedis(ctx context.Context, addr string, db int) (*RedisCache, error) { + opts, err := buildRedisOptions(addr, db) + if err != nil { + return nil, err + } + client := redis.NewClient(opts) + if err := client.Ping(ctx).Err(); err != nil { + _ = client.Close() + return nil, fmt.Errorf("cache: Redis 连接失败 %s: %w", addr, err) + } + return &RedisCache{client: client}, nil +} + +// buildRedisOptions 构造 go-redis 连接选项(纯地址 / URL 形式统一入口)。 +func buildRedisOptions(addr string, db int) (*redis.Options, error) { + opts := &redis.Options{ + Addr: addr, + DB: db, + DialTimeout: 5 * time.Second, + ReadTimeout: 3 * time.Second, + WriteTimeout: 3 * time.Second, + PoolSize: 32, + } + if strings.HasPrefix(addr, "redis://") || strings.HasPrefix(addr, "rediss://") { + u, err := redis.ParseURL(addr) + if err != nil { + return nil, fmt.Errorf("cache: Redis 地址解析失败 %s: %w", addr, err) + } + // URL 未显式携带库号(路径为空或 /)时用 db 参数;显式 /N 优先 + if u.DB == 0 && !urlHasDBPath(addr) { + u.DB = db + } + u.DialTimeout = opts.DialTimeout + u.ReadTimeout = opts.ReadTimeout + u.WriteTimeout = opts.WriteTimeout + u.PoolSize = opts.PoolSize + opts = u + } + return opts, nil +} + +// urlHasDBPath 判断 redis:// URL 是否显式携带了库号路径(如 /5)。 +func urlHasDBPath(raw string) bool { + u, err := url.Parse(raw) + if err != nil { + return false + } + return strings.Trim(u.Path, "/") != "" +} + +// Get 读取键值。 +func (r *RedisCache) Get(ctx context.Context, key string) (string, error) { + val, err := r.client.Get(ctx, key).Result() + if err == redis.Nil { + return "", ErrNotFound + } + return val, err +} + +// Set 写入键值。 +func (r *RedisCache) Set(ctx context.Context, key, value string, ttl time.Duration) error { + return r.client.Set(ctx, key, value, ttl).Err() +} + +// Delete 删除键。 +func (r *RedisCache) Delete(ctx context.Context, keys ...string) error { + if len(keys) == 0 { + return nil + } + return r.client.Del(ctx, keys...).Err() +} + +// Exists 判断键是否存在。 +func (r *RedisCache) Exists(ctx context.Context, key string) (bool, error) { + n, err := r.client.Exists(ctx, key).Result() + return n > 0, err +} + +// Incr 原子自增;首次创建时设置窗口 TTL。 +// 使用 Lua 脚本保证 INCR+EXPIRE 原子性,避免多实例下窗口被反复重置。 +func (r *RedisCache) Incr(ctx context.Context, key string, ttl time.Duration) (int64, error) { + var incrScript = redis.NewScript(` +local n = redis.call('INCR', KEYS[1]) +if n == 1 and ARGV[1] ~= '0' then + redis.call('PEXPIRE', KEYS[1], ARGV[1]) +end +return n +`) + ttlMs := int64(0) + if ttl > 0 { + ttlMs = ttl.Milliseconds() + } + return incrScript.Run(ctx, r.client, []string{key}, ttlMs).Int64() +} + +// Close 关闭 Redis 连接。 +func (r *RedisCache) Close() error { return r.client.Close() } diff --git a/server/internal/cache/redis_options_test.go b/server/internal/cache/redis_options_test.go new file mode 100644 index 0000000..77d6cf1 --- /dev/null +++ b/server/internal/cache/redis_options_test.go @@ -0,0 +1,70 @@ +// redis_options_test.go — FCB_REDIS_DB / URL 库号解析单测。 +package cache + +import "testing" + +func TestBuildRedisOptionsPlainAddr(t *testing.T) { + opts, err := buildRedisOptions("127.0.0.1:6379", 0) + if err != nil { + t.Fatalf("plain addr: %v", err) + } + if opts.DB != 0 { + t.Fatalf("默认库号应为 0, got %d", opts.DB) + } + + opts, err = buildRedisOptions("127.0.0.1:6379", 5) + if err != nil { + t.Fatalf("plain addr db=5: %v", err) + } + if opts.Addr != "127.0.0.1:6379" || opts.DB != 5 { + t.Fatalf("host:port + db: got addr=%s db=%d", opts.Addr, opts.DB) + } +} + +func TestBuildRedisOptionsURL(t *testing.T) { + cases := []struct { + name string + url string + dbParam int + wantDB int + wantPw string + }{ + {"URL 无库号用参数", "redis://127.0.0.1:6379", 3, 3, ""}, + {"URL 显式库号优先", "redis://127.0.0.1:6379/7", 3, 7, ""}, + {"URL 带密码", "redis://:secretpw@127.0.0.1:6379/2", 0, 2, "secretpw"}, + {"rediss 无库号用参数", "rediss://127.0.0.1:6379", 9, 9, ""}, + {"URL 根路径视为无库号", "redis://127.0.0.1:6379/", 4, 4, ""}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + opts, err := buildRedisOptions(tc.url, tc.dbParam) + if err != nil { + t.Fatalf("buildRedisOptions(%q): %v", tc.url, err) + } + if opts.DB != tc.wantDB { + t.Fatalf("db = %d, want %d", opts.DB, tc.wantDB) + } + if opts.Password != tc.wantPw { + t.Fatalf("password = %q, want %q", opts.Password, tc.wantPw) + } + if opts.Addr != "127.0.0.1:6379" { + t.Fatalf("addr = %q", opts.Addr) + } + }) + } +} + +func TestBuildRedisOptionsInvalidURL(t *testing.T) { + if _, err := buildRedisOptions("redis://[bad", 0); err == nil { + t.Fatal("非法 URL 应报错") + } +} + +func TestURLHasDBPath(t *testing.T) { + if urlHasDBPath("redis://h:6379") || urlHasDBPath("redis://h:6379/") { + t.Fatal("无路径或根路径应视为 false") + } + if !urlHasDBPath("redis://h:6379/5") { + t.Fatal("/5 应视为 true") + } +} diff --git a/server/internal/config/config.go b/server/internal/config/config.go new file mode 100644 index 0000000..213c1f2 --- /dev/null +++ b/server/internal/config/config.go @@ -0,0 +1,417 @@ +// Package config 提供全局配置:默认值对齐参考实现 core/settings.py, +// 支持 FCB_* 环境变量覆盖默认值,再由数据库 settings KV 做运行时覆盖。 +package config + +import ( + "fmt" + "os" + "strconv" + "strings" +) + +// 会话有效期边界(与参考实现保持一致:天级、可配 1~365 天)。 +const ( + // AdminSessionExpireDefault 默认 7 天(L8:由 30 天缩短,降低 localStorage + // token 泄露后的暴露窗口;管理员可在 1~365 天内自行调整) + AdminSessionExpireDefault = 7 * 24 * 60 * 60 + AdminSessionExpireMin = 24 * 60 * 60 // 最小 1 天 + AdminSessionExpireMax = 365 * 24 * 60 * 60 // 最大 365 天 + + // DefaultSQLitePath SQLite 模式默认数据库文件路径(相对运行目录,自动创建 data/)。 + DefaultSQLitePath = "./data/filecodebox.db" +) + +// 数据库驱动常量(需求 ⑧:SQLite 默认、Postgres 可选)。 +const ( + DBDriverSQLite = "sqlite" + DBDriverPostgres = "postgres" +) + +// DefaultLogoURL / DefaultFaviconURL 默认 Logo 与 favicon(需求 ⑤): +// v2 起默认改用前端打包的本地资源(web/src/assets/brand/logo.svg + favicon.png, +// 经 Vite 产出 /assets/logo-*.svg 与 /assets/favicon-*.png)。此处留空, +// GET /api/v1/config 下发空值时前端 displayLogoUrl/displayFaviconUrl 回落到本地打包资源; +// 管理端仍可设置任意 URL 全站替换。 +const DefaultLogoURL = "" + +// DefaultFaviconURL favicon/备用 Logo 默认空串(语义见 DefaultLogoURL 注释)。 +const DefaultFaviconURL = "" + +// Config 运行时配置。Env 为 FCB_* 环境变量解析结果(进程级), +// KV 为数据库 settings 键值覆盖(可被管理端动态修改)。 +type Config struct { + Env *EnvConfig + KV map[string]any +} + +// EnvConfig 进程级环境变量配置,仅能通过环境变量修改。 +type EnvConfig struct { + DBDriver string // FCB_DB_DRIVER,sqlite|postgres,默认 sqlite(需求 ⑧) + DBDSN string // FCB_DB_DSN;postgres 必需;sqlite 为空时用 DefaultSQLitePath + RedisAddr string // FCB_REDIS_ADDR,可选;为空时缓存降级为内存实现 + RedisDB int // FCB_REDIS_DB,Redis 逻辑库号 0-15,默认 0(URL 形式地址以 URL 内库号优先) + Listen string // FCB_LISTEN,监听地址,默认 :8466 + StorageEngine string // FCB_STORAGE_ENGINE,local|s3|webdav,默认 local + TrustedProxies []string // FCB_TRUSTED_PROXIES,逗号分隔的可信代理 CIDR +} + +// defaults 返回与参考实现 core/settings.py DEFAULT_CONFIG 对齐的默认配置。 +func defaults() map[string]any { + return map[string]any{ + // 存储引擎与路径 + "file_storage": "local", + "storage_path": "", + "storageLimit": 0, + // v3:存储引擎运行时可配(热切换);空=沿用 Env.StorageEngine 启动值 + "storage_engine": "", + "site_domain": "", + // 站点信息 + "name": "文件快传", + "site_name": "文件快传", // 新增:管理端可自定义 + "description": "开箱即用的文件快传系统", + "notify_title": "系统通知", + "notify_content": "欢迎使用文件快传,拖拽或粘贴即可分享文本与文件。", + "page_explain": "请勿上传或分享违法内容。根据《中华人民共和国网络安全法》、《中华人民共和国刑法》、《中华人民共和国治安管理处罚法》等相关规定。 传播或存储违法、违规内容,会受到相关处罚,严重者将承担刑事责任。本站坚决配合相关部门,确保网络内容的安全,和谐,打造绿色网络环境。", + "keywords": "文件快传, 文件分享, 匿名口令分享文本, 文件", + // 需求 ⑤:默认 Logo 与 favicon(空 = 前端使用打包的本地资源) + "logo_url": DefaultLogoURL, + "favicon_url": DefaultFaviconURL, + // 需求 ①:背景图(v2 新增 background_url;background 为参考实现既有键,保留兼容) + "background": "", + "background_url": "", + // 需求 ②:页脚自定义内容与备案号 + "footer_text": "", + "footer_beian": "", + // 需求 ③:系统通知(notify_enabled 新增开关,title/content 沿用参考语义) + "notify_enabled": 1, + // 需求 ④:保存策略(次数上限新增;时间上限沿用 max_save_seconds) + "max_save_count": 0, + // 需求 ⑩:存储策略-单文件上限(0=回落 uploadSize,避免与参考键冲突) + "max_file_size": 0, + // 本地存储 + "local_storage_path": "", + // S3 引擎 + "s3_access_key_id": "", + "s3_secret_access_key": "", + "s3_bucket_name": "", + "s3_endpoint_url": "", + "s3_region_name": "auto", + "s3_signature_version": "s3v4", + "s3_hostname": "", + "s3_addressing_style": "auto", + "s3_proxy": 0, + "aws_session_token": "", + // WebDAV 引擎 + "webdav_url": "", + "webdav_username": "", + "webdav_password": "", + "webdav_root_path": "filebox_storage", + "webdav_proxy": 0, + // 安全 + "admin_token": "", // 管理员密码哈希;为空表示未初始化 + "jwt_secret": "", + "adminSessionExpire": AdminSessionExpireDefault, + // 上传与分享策略 + "openUpload": 1, + "uploadSize": 1024 * 1024 * 10, + "allowed_file_types": []string{"*"}, + "expireStyle": []string{"day", "hour", "minute", "forever", "count"}, + "code_generate_type": "secret", + "uploadMinute": 1, + "uploadCount": 10, + "errorMinute": 1, + "errorCount": 10, + "loginCount": 5, + "loginMinute": 15, + "max_save_seconds": 0, + "enableChunk": 0, + // 界面 + "opacity": 0.9, + "showAdminAddr": 0, + "robotsText": "User-agent: *\nDisallow: /", + "serverWorkers": 1, + "serverHost": "0.0.0.0", + "serverPort": 8466, + } +} + +// loadEnv 解析 FCB_* 环境变量;返回 nil 表示未设置任何必需项。 +func loadEnv() (*EnvConfig, error) { + env := &EnvConfig{ + DBDriver: strings.ToLower(strings.TrimSpace(os.Getenv("FCB_DB_DRIVER"))), + DBDSN: strings.TrimSpace(os.Getenv("FCB_DB_DSN")), + RedisAddr: strings.TrimSpace(os.Getenv("FCB_REDIS_ADDR")), + Listen: strings.TrimSpace(os.Getenv("FCB_LISTEN")), + StorageEngine: strings.TrimSpace(os.Getenv("FCB_STORAGE_ENGINE")), + } + // Redis 库号(FCB_REDIS_DB,0-15;非法值忽略用默认 0) + if v := strings.TrimSpace(os.Getenv("FCB_REDIS_DB")); v != "" { + if n, err := strconv.Atoi(v); err == nil && n >= 0 && n <= 15 { + env.RedisDB = n + } + } + if env.Listen == "" { + env.Listen = ":8466" + } + if env.StorageEngine == "" { + env.StorageEngine = "local" + } + switch env.StorageEngine { + case "local", "s3", "webdav": + default: + return nil, fmt.Errorf("FCB_STORAGE_ENGINE 无效值 %q,仅支持 local|s3|webdav", env.StorageEngine) + } + if raw := strings.TrimSpace(os.Getenv("FCB_TRUSTED_PROXIES")); raw != "" { + for _, item := range strings.Split(raw, ",") { + if item = strings.TrimSpace(item); item != "" { + env.TrustedProxies = append(env.TrustedProxies, item) + } + } + } + return env, nil +} + +// New 从环境变量构造配置;KV 覆盖先为空。 +// 需求 ⑧:FCB_DB_DRIVER 默认 sqlite(零依赖);postgres 必须提供 FCB_DB_DSN。 +func New() (*Config, error) { + env, err := loadEnv() + if err != nil { + return nil, err + } + switch env.DBDriver { + case "", DBDriverSQLite: + env.DBDriver = DBDriverSQLite + // sqlite 模式 DSN 可为空:数据库层回退到 DefaultSQLitePath + case DBDriverPostgres: + if env.DBDSN == "" { + return nil, fmt.Errorf("FCB_DB_DRIVER=postgres 时必须提供 FCB_DB_DSN(Postgres 连接串)") + } + default: + return nil, fmt.Errorf("FCB_DB_DRIVER 无效值 %q,仅支持 sqlite|postgres", env.DBDriver) + } + return &Config{Env: env, KV: map[string]any{}}, nil +} + +// ApplyKV 用数据库 settings KV 覆盖运行时配置(内部键以 _ 开头的不允许覆盖)。 +func (c *Config) ApplyKV(kv map[string]any) { + for k, v := range kv { + if strings.HasPrefix(k, "_") { + continue + } + c.KV[k] = v + } +} + +// Get 按 键读取:KV 覆盖 > 默认值;找不到返回零值与 false。 +func (c *Config) Get(key string) (any, bool) { + if v, ok := c.KV[key]; ok { + return v, true + } + v, ok := defaults()[key] + return v, ok +} + +// GetString 取字符串配置。 +func (c *Config) GetString(key string) string { + v, ok := c.Get(key) + if !ok || v == nil { + return "" + } + if s, ok := v.(string); ok { + return s + } + return fmt.Sprintf("%v", v) +} + +// GetInt 取整型配置,兼容 JSON 数字(float64)与字符串。 +func (c *Config) GetInt(key string) int { + n, _ := c.getInt64(key) + return int(n) +} + +// GetInt64 取长整型配置。 +func (c *Config) GetInt64(key string) int64 { + n, _ := c.getInt64(key) + return n +} + +func (c *Config) getInt64(key string) (int64, bool) { + v, ok := c.Get(key) + if !ok || v == nil { + return 0, false + } + switch n := v.(type) { + case int: + return int64(n), true + case int64: + return n, true + case float64: + return int64(n), true + case string: + if n, err := strconv.ParseInt(strings.TrimSpace(n), 10, 64); err == nil { + return n, true + } + } + return 0, false +} + +// GetBool 取布尔配置,兼容 1/0、"true"/"false"/"on"/"yes"。 +func (c *Config) GetBool(key string) bool { + v, ok := c.Get(key) + if !ok || v == nil { + return false + } + switch b := v.(type) { + case bool: + return b + case int: + return b != 0 + case float64: + return b != 0 + case string: + switch strings.ToLower(strings.TrimSpace(b)) { + case "1", "true", "on", "yes": + return true + } + } + return false +} + +// GetStringSlice 取字符串切片配置。 +// SiteDomain 站点对外域名(v3.1):空=分享链接用当前访问地址。 +func (c *Config) SiteDomain() string { + return strings.TrimRight(strings.TrimSpace(c.GetString("site_domain")), "/") +} + +func (c *Config) GetStringSlice(key string) []string { + v, ok := c.Get(key) + if !ok || v == nil { + return nil + } + switch s := v.(type) { + case []string: + return s + case []any: + out := make([]string, 0, len(s)) + for _, item := range s { + if item == nil { + continue + } + out = append(out, fmt.Sprintf("%v", item)) + } + return out + case string: + var out []string + for _, item := range strings.Split(s, ",") { + if item = strings.TrimSpace(item); item != "" { + out = append(out, item) + } + } + return out + } + return nil +} + +// —— 常用字段的便捷访问(与参考 settings.xxx 对齐)—— + +// SiteName 站点名称。 +func (c *Config) SiteName() string { + if v := c.GetString("site_name"); v != "" { + return v + } + return c.GetString("name") +} + +// LogoURL 页面 Logo。 +func (c *Config) LogoURL() string { return c.GetString("logo_url") } + +// FaviconURL favicon 地址。 +func (c *Config) FaviconURL() string { return c.GetString("favicon_url") } + +// OpenUpload 是否允许游客上传。 +func (c *Config) OpenUpload() bool { return c.GetBool("openUpload") } + +// UploadSize 单文件大小上限(字节)。 +func (c *Config) UploadSize() int64 { return c.GetInt64("uploadSize") } + +// AllowedFileTypes 允许的文件类型列表("*" 表示不限制)。 +func (c *Config) AllowedFileTypes() []string { return c.GetStringSlice("allowed_file_types") } + +// ExpireStyle 允许的过期方式。 +func (c *Config) ExpireStyle() []string { return c.GetStringSlice("expireStyle") } + +// EnableChunk 是否启用分片上传。 +func (c *Config) EnableChunk() bool { return c.GetBool("enableChunk") } + +// MaxSaveSeconds 最长保存秒数,0 表示不限制。 +func (c *Config) MaxSaveSeconds() int64 { return c.GetInt64("max_save_seconds") } + +// MaxSaveCount 单次分享最大可取次数上限(需求 ④),0 表示不限制。 +func (c *Config) MaxSaveCount() int { return c.GetInt("max_save_count") } + +// MaxFileSize 存储策略-单文件上限(需求 ⑩);0 表示回落 uploadSize。 +func (c *Config) MaxFileSize() int64 { + if n := c.GetInt64("max_file_size"); n > 0 { + return n + } + return c.UploadSize() +} + +// FooterText 页脚自定义内容(需求 ②)。 +func (c *Config) FooterText() string { return c.GetString("footer_text") } + +// FooterBeian 备案号(需求 ②)。 +func (c *Config) FooterBeian() string { return c.GetString("footer_beian") } + +// BackgroundURL 背景图地址(需求 ①);空表示使用主题默认。 +func (c *Config) BackgroundURL() string { + if v := c.GetString("background_url"); v != "" { + return v + } + return c.GetString("background") +} + +// NotifyEnabled 系统通知开关(需求 ③):默认开启。 +func (c *Config) NotifyEnabled() bool { + if v, ok := c.Get("notify_enabled"); ok && v != nil { + return c.GetBool("notify_enabled") + } + return true +} + +// SQLitePath 数据库文件路径:sqlite 模式下 DSN 为空时回退默认路径(需求 ⑧)。 +func (c *Config) SQLitePath() string { + if c.Env.DBDriver != DBDriverSQLite { + return "" + } + if c.Env.DBDSN != "" { + return c.Env.DBDSN + } + return DefaultSQLitePath +} + +// AdminSessionExpireSeconds 管理员会话有效期(秒), +// 参考 apps/admin/dependencies.py 的 get_admin_session_expire_seconds。 +func (c *Config) AdminSessionExpireSeconds() int { + n := c.GetInt("adminSessionExpire") + if n < AdminSessionExpireMin || n > AdminSessionExpireMax || n%AdminSessionExpireMin != 0 { + return AdminSessionExpireDefault + } + return n +} + +// Engine 当前存储引擎。 +// Engine 返回当前存储引擎名:KV storage_engine 优先(v3 运行时可改), +// 空(未设置/历史数据)回落启动值 Env.StorageEngine(env 校验过的 local|s3|webdav)。 +// 枚举校验内联(避免 config→storage 反向依赖)。 +func (c *Config) Engine() string { + if v, ok := c.Get(KeyStorageEngine); ok { + if s, isStr := v.(string); isStr { + switch s { + case "local", "s3", "webdav": + return s + } + } + } + return c.Env.StorageEngine +} diff --git a/server/internal/config/config_test.go b/server/internal/config/config_test.go new file mode 100644 index 0000000..d0f6c14 --- /dev/null +++ b/server/internal/config/config_test.go @@ -0,0 +1,160 @@ +package config + +import ( + "testing" +) + +func TestNewDefaultsToSQLite(t *testing.T) { + t.Setenv("FCB_DB_DRIVER", "") + t.Setenv("FCB_DB_DSN", "") + t.Setenv("FCB_REDIS_ADDR", "") + t.Setenv("FCB_LISTEN", "") + t.Setenv("FCB_STORAGE_ENGINE", "") + c, err := New() + if err != nil { + t.Fatalf("默认(无 DSN)应可构造: %v", err) + } + if c.Env.DBDriver != DBDriverSQLite { + t.Errorf("默认驱动应为 sqlite,实际 %s", c.Env.DBDriver) + } + if c.SQLitePath() != DefaultSQLitePath { + t.Errorf("SQLite 默认路径 = %s", c.SQLitePath()) + } + if c.Env.Listen != ":8466" { + t.Errorf("默认监听地址错误: %s", c.Env.Listen) + } + if c.Env.StorageEngine != "local" { + t.Errorf("默认存储引擎错误: %s", c.Env.StorageEngine) + } + if c.Env.RedisAddr != "" { + t.Errorf("RedisAddr 应为空: %s", c.Env.RedisAddr) + } +} + +func TestNewPostgresRequiresDSN(t *testing.T) { + t.Setenv("FCB_DB_DRIVER", "postgres") + t.Setenv("FCB_DB_DSN", "") + if _, err := New(); err == nil { + t.Fatal("postgres 模式缺少 FCB_DB_DSN 应报错") + } + t.Setenv("FCB_DB_DSN", "postgres://user:pass@localhost:5432/fcb") + c, err := New() + if err != nil { + t.Fatalf("postgres + DSN 应可构造: %v", err) + } + if c.Env.DBDriver != DBDriverPostgres { + t.Errorf("驱动应为 postgres,实际 %s", c.Env.DBDriver) + } + if c.SQLitePath() != "" { + t.Errorf("postgres 模式 SQLitePath 应为空: %s", c.SQLitePath()) + } +} + +func TestNewInvalidDriver(t *testing.T) { + t.Setenv("FCB_DB_DRIVER", "mysql") + t.Setenv("FCB_DB_DSN", "x") + if _, err := New(); err == nil { + t.Fatal("非法驱动应报错") + } +} + +func TestNewInvalidEngine(t *testing.T) { + t.Setenv("FCB_DB_DSN", "postgres://x") + t.Setenv("FCB_STORAGE_ENGINE", "onedrive") + if _, err := New(); err == nil { + t.Fatal("非法引擎应报错") + } +} + +func TestEnvOverridesAndDefaults(t *testing.T) { + t.Setenv("FCB_DB_DSN", "postgres://x") + t.Setenv("FCB_LISTEN", ":9999") + t.Setenv("FCB_STORAGE_ENGINE", "webdav") + c, err := New() + if err != nil { + t.Fatalf("New 失败: %v", err) + } + if c.Env.Listen != ":9999" || c.Env.StorageEngine != "webdav" { + t.Fatalf("env 覆盖失败: %+v", c.Env) + } + // 默认值对齐参考 DEFAULT_CONFIG + if got := c.GetInt("uploadSize"); got != 1024*1024*10 { + t.Errorf("uploadSize 默认值 = %d", got) + } + if got := c.GetInt("errorCount"); got != 10 { + t.Errorf("errorCount 默认值 = %d", got) + } + if got := c.GetInt("loginCount"); got != 5 { + t.Errorf("loginCount 默认值 = %d", got) + } + if got := c.GetInt("loginMinute"); got != 15 { + t.Errorf("loginMinute 默认值 = %d", got) + } + if got := c.GetBool("openUpload"); !got { + t.Error("openUpload 默认应为开启") + } + if c.EnableChunk() { + t.Error("enableChunk 默认应关闭") + } + // 新增字段(需求 ①) + if c.LogoURL() != DefaultLogoURL { + t.Errorf("logo_url 默认值 = %s", c.LogoURL()) + } + if c.FaviconURL() != DefaultFaviconURL { + t.Errorf("favicon_url 默认值 = %s", c.FaviconURL()) + } + if c.SiteName() == "" { + t.Error("site_name 默认值不应为空") + } + // 过期方式与文件类型 + if len(c.ExpireStyle()) != 5 { + t.Errorf("expireStyle 默认值 = %v", c.ExpireStyle()) + } + if len(c.AllowedFileTypes()) != 1 || c.AllowedFileTypes()[0] != "*" { + t.Errorf("allowed_file_types 默认值 = %v", c.AllowedFileTypes()) + } +} + +func TestKVOverridesEnvAndDefaults(t *testing.T) { + t.Setenv("FCB_DB_DSN", "postgres://x") + c, _ := New() + c.ApplyKV(map[string]any{ + "uploadSize": 1024, + "openUpload": 0, + "site_name": "我的快递柜", + "logo_url": "https://example.com/logo.svg", + "internalKey": "x", // 非下划线开头允许;下划线开头被拒 + "_secret": "no", + }) + if got := c.GetInt("uploadSize"); got != 1024 { + t.Errorf("KV 覆盖 uploadSize 失败: %d", got) + } + if c.OpenUpload() { + t.Error("KV 覆盖 openUpload 失败") + } + if c.SiteName() != "我的快递柜" { + t.Errorf("site_name KV 覆盖失败: %s", c.SiteName()) + } + if c.LogoURL() != "https://example.com/logo.svg" { + t.Errorf("logo_url KV 覆盖失败: %s", c.LogoURL()) + } + if _, ok := c.Get("_secret"); ok { + t.Error("下划线内部键不应可通过 ApplyKV 覆盖") + } +} + +func TestAdminSessionExpireClamp(t *testing.T) { + t.Setenv("FCB_DB_DSN", "postgres://x") + c, _ := New() + if got := c.AdminSessionExpireSeconds(); got != AdminSessionExpireDefault { + t.Errorf("默认会话有效期 = %d", got) + } + c.ApplyKV(map[string]any{"adminSessionExpire": 7 * 24 * 60 * 60}) + if got := c.AdminSessionExpireSeconds(); got != 7*24*60*60 { + t.Errorf("7 天会话有效期 = %d", got) + } + c.ApplyKV(map[string]any{"adminSessionExpire": 3600}) // 非整天,回落默认 + if got := c.AdminSessionExpireSeconds(); got != AdminSessionExpireDefault { + t.Errorf("非法值应回落默认 = %d", got) + } +} diff --git a/server/internal/config/schema.go b/server/internal/config/schema.go new file mode 100644 index 0000000..6822cde --- /dev/null +++ b/server/internal/config/schema.go @@ -0,0 +1,97 @@ +// Package config — schema.go 定义 v2 新增配置键(KV)schema: +// 键名常量、类型、默认值与取值边界。管理与 API 层(t2)按下表读写与校验, +// 文档(t4)按本表生成说明。键名除参考实现既有 camelCase 键外, +// v2 新增键统一 snake_case。 +package config + +// —— v2 新增/沿用键名常量(单一事实来源;settings 包会 re-export)—— +// 命名规则:v2 新增键 snake_case;与参考实现对齐的既有键保持原拼写。 +const ( + // 需求 ①:背景图 + KeyBackground = "background" // 参考实现既有键(v1 兼容保留) + KeyBackgroundURL = "background_url" // v2 新增:背景图 URL 或上传后的访问地址(空=默认主题) + // 需求 ②:页脚 + KeyFooterText = "footer_text" // v2 新增:页脚自定义内容(纯文本或受控 HTML 片段) + KeyFooterBeian = "footer_beian" // v2 新增:备案号(如 京ICP备2024xxxxxx号-1) + // 需求 ③:系统通知 + KeyNotifyEnabled = "notify_enabled" // v2 新增:通知开关,1 开启 / 0 关闭 + KeyNotifyTitle = "notify_title" // 既有键:通知标题 + KeyNotifyContent = "notify_content" // 既有键:通知内容(允许 等受控 HTML) + // 需求 ④:保存策略(上传页动态读取并在范围内选择) + KeyMaxSaveSeconds = "max_save_seconds" // 既有键:最长保存秒数,0=不限制(仅受默认 7 天兜底) + KeyMaxSaveCount = "max_save_count" // v2 新增:单次分享最大可取(保存)次数上限,0=不限制 + KeyExpireStyle = "expireStyle" // 既有键:允许的过期方式白名单 + // 需求 ④:上传频率限制(既有键,对齐参考 ip_limit["upload"]) + KeyUploadCount = "uploadCount" // 窗口内允许上传次数 + KeyUploadMinute = "uploadMinute" // 频率窗口(分钟) + // 需求 ④⑩:存储策略(最大文件大小/允许类型/总容量) + KeyUploadSize = "uploadSize" // 既有键:单文件上限(字节),参考实现语义 + KeyMaxFileSize = "max_file_size" // v2 新增:存储策略-单文件上限(字节),0=回落 uploadSize + KeyAllowedTypes = "allowed_file_types" // 既有键:允许类型白名单("*" 不限制) + KeyStorageLimit = "storageLimit" // 既有键:站点总容量(字节),0=不限制 + KeyOpenUpload = "openUpload" // 既有键:游客上传开关 + // v3:存储引擎运行时可配(热切换;file_storage 为参考既有键保留兼容) + KeyStorageEngine = "storage_engine" // 当前存储引擎:local|s3|webdav + KeySiteDomain = "site_domain" // 站点对外域名(空=分享链接用当前地址) +) + +// —— 取值边界(管理端保存与 API 校验用)—— +const ( + // 保存时间上限:最长 365 天,0 表示不限制。 + MaxSaveSecondsMax = 365 * 24 * 60 * 60 + // 保存次数上限:最长 100000 次,0 表示不限制。 + MaxSaveCountMax = 100000 + // 单文件大小上限:最长 10 GiB,0 表示回落 uploadSize。 + MaxFileSizeMax = 10 * 1024 * 1024 * 1024 + // 背景图 URL 最大长度(含 data: 之外的普通 http(s) URL)。 + BackgroundURLMaxLen = 2048 + // 页脚自定义内容最大长度。 + FooterTextMaxLen = 2000 + // 备案号最大长度。 + FooterBeianMaxLen = 128 + // 通知标题/内容最大长度。 + NotifyTitleMaxLen = 128 + NotifyContentMaxLen = 2000 +) + +// KVSchemaEntry 配置键元数据:类型 / 默认值 / 说明,供管理端 UI 与文档生成。 +type KVSchemaEntry struct { + Key string // KV 键名 + Type string // string | int | int64 | bool | []string + Default any // 默认值(与 defaults() 保持一致,测试保证同步) + Min int64 // 数值键最小值(字符串键为长度下界) + Max int64 // 数值键最大值(字符串键为长度上界;-1 不限制) + Description string // 中文说明 +} + +// KVSchema v2 全量配置键 schema 表(含既有策略键,供管理端/文档/AI 校验)。 +// 注意:Default 与 config defaults() 逐一对应(schema_test 保证)。 +func KVSchema() []KVSchemaEntry { + return []KVSchemaEntry{ + // —— 需求 ① 背景图 —— + {KeyBackgroundURL, "string", "", 0, BackgroundURLMaxLen, "背景图 URL 或上传后地址(空=主题默认)"}, + // —— 需求 ② 页脚 —— + {KeyFooterText, "string", "", 0, FooterTextMaxLen, "页脚自定义内容(纯文本或受控 HTML 片段)"}, + {KeyFooterBeian, "string", "", 0, FooterBeianMaxLen, "备案号,展示于页脚"}, + // —— 需求 ③ 系统通知 —— + {KeyNotifyEnabled, "int", 1, 0, 1, "系统通知开关:1 右上角悬浮窗展示 / 0 关闭"}, + {KeyNotifyTitle, "string", "系统通知", 0, NotifyTitleMaxLen, "通知标题"}, + {KeyNotifyContent, "string", "欢迎使用文件快传,拖拽或粘贴即可分享文本与文件。", 0, NotifyContentMaxLen, "通知内容(允许 等受控 HTML)"}, + // —— 需求 ④ 保存策略 —— + {KeyMaxSaveSeconds, "int64", int64(0), 0, MaxSaveSecondsMax, "最长保存秒数上限,0=不限制(默认 7 天兜底)"}, + {KeyMaxSaveCount, "int", 0, 0, MaxSaveCountMax, "单次分享最大可取次数上限,0=不限制"}, + {KeyExpireStyle, "[]string", []string{"day", "hour", "minute", "forever", "count"}, -1, -1, "上传页可选过期方式白名单"}, + // —— 需求 ④ 上传频率限制(既有键对齐参考)—— + {KeyUploadCount, "int", 10, 1, 10000, "频率窗口内允许的上传次数"}, + {KeyUploadMinute, "int", 1, 1, 1440, "上传频率窗口(分钟)"}, + // —— 需求 ④⑩ 存储策略 —— + {KeyMaxFileSize, "int64", int64(0), 0, MaxFileSizeMax, "存储策略-单文件上限(字节),0=回落 uploadSize"}, + {KeyUploadSize, "int64", int64(1024 * 1024 * 10), 1024, MaxFileSizeMax, "单文件上限(字节),参考实现语义"}, + {KeyAllowedTypes, "[]string", []string{"*"}, -1, -1, "允许上传类型白名单(\"*\" 不限制)"}, + {KeyStorageLimit, "int64", int64(0), 0, -1, "站点总容量(字节),0=不限制"}, + {KeyOpenUpload, "int", 1, 0, 1, "游客上传开关:1 开 / 0 需管理员登录"}, + // —— v3 存储引擎(热切换;引擎参数键沿用 defaults() 既有键,管理端经 config get/update 读写)—— + {KeyStorageEngine, "string", "", 0, 16, "当前存储引擎:local|s3|webdav(热切换,健康检查通过才生效;空=回落启动值 FCB_STORAGE_ENGINE)"}, + {KeySiteDomain, "string", "", 0, 256, "站点对外域名(http(s)://host[:port],不带路径;空=分享链接用当前访问地址)"}, + } +} diff --git a/server/internal/config/schema_test.go b/server/internal/config/schema_test.go new file mode 100644 index 0000000..ee09498 --- /dev/null +++ b/server/internal/config/schema_test.go @@ -0,0 +1,91 @@ +// schema 同步测试:保证 config.KVSchema() 的默认值/键集合与 defaults() 完全一致, +// 与 settings 包 re-export 的键名常量同源。新增键时任何一处漏改都会在此失败。 +package config + +import ( + "encoding/json" + "testing" +) + +// TestKVSchemaDefaultsMatchDefaults KVSchema 的 Default 必须 === defaults() 中同名键。 +func TestKVSchemaDefaultsMatchDefaults(t *testing.T) { + def := defaults() + for _, e := range KVSchema() { + want, ok := def[e.Key] + if !ok { + t.Fatalf("schema 键 %q 缺少 defaults() 默认值", e.Key) + } + // 类型规范化比较(JSON 序列化可比较 []string / int / float) + a, _ := json.Marshal(e.Default) + b, _ := json.Marshal(want) + if string(a) != string(b) { + t.Fatalf("键 %q 默认值不一致: schema=%s defaults=%s", e.Key, a, b) + } + } +} + +// TestKVSchemaNoDuplicates 键名不得重复。 +func TestKVSchemaNoDuplicates(t *testing.T) { + seen := map[string]bool{} + for _, e := range KVSchema() { + if seen[e.Key] { + t.Fatalf("schema 键 %q 重复定义", e.Key) + } + seen[e.Key] = true + } +} + +// TestV2NewKeysPresent v2 新增键必须在 schema 与 defaults 中同时存在。 +func TestV2NewKeysPresent(t *testing.T) { + def := defaults() + newKeys := []string{ + KeyBackgroundURL, KeyFooterText, KeyFooterBeian, + KeyNotifyEnabled, KeyMaxSaveCount, KeyMaxFileSize, + } + for _, k := range newKeys { + if _, ok := def[k]; !ok { + t.Fatalf("v2 新键 %q 缺少默认值", k) + } + } +} + +// TestV2AccessorDefaults v2 便捷访问器默认语义。 +func TestV2AccessorDefaults(t *testing.T) { + t.Setenv("FCB_DB_DRIVER", "sqlite") + t.Setenv("FCB_DB_DSN", "") + c, err := New() + if err != nil { + t.Fatalf("New: %v", err) + } + // 背景图:background_url 与 background 均空 → 空 + if c.BackgroundURL() != "" { + t.Fatalf("背景图默认应为空: %q", c.BackgroundURL()) + } + // legacy background 键兜底 + c.ApplyKV(map[string]any{KeyBackground: "/legacy/bg.jpg"}) + if c.BackgroundURL() != "/legacy/bg.jpg" { + t.Fatalf("legacy background 应回落生效: %q", c.BackgroundURL()) + } + // max_file_size > 0 时优先于 uploadSize + c.ApplyKV(map[string]any{KeyMaxFileSize: int64(1024), KeyUploadSize: int64(2048)}) + if c.MaxFileSize() != 1024 { + t.Fatalf("max_file_size 应优先: %d", c.MaxFileSize()) + } + // max_file_size = 0 回落 uploadSize + c.ApplyKV(map[string]any{KeyMaxFileSize: int64(0)}) + if c.MaxFileSize() != 2048 { + t.Fatalf("max_file_size=0 应回落 uploadSize: %d", c.MaxFileSize()) + } + // 通知默认开启 + if !c.NotifyEnabled() { + t.Fatal("notify_enabled 默认应开启") + } + // 保存次数上限默认不限制 + if c.MaxSaveCount() != 0 { + t.Fatalf("max_save_count 默认应 0: %d", c.MaxSaveCount()) + } + // 页脚默认空 + if c.FooterText() != "" || c.FooterBeian() != "" { + t.Fatal("页脚默认应为空") + } +} diff --git a/server/internal/database/database.go b/server/internal/database/database.go new file mode 100644 index 0000000..6d1a96d --- /dev/null +++ b/server/internal/database/database.go @@ -0,0 +1,139 @@ +// Package database 负责数据库连接与迁移(需求 ⑧:双方言): +// - sqlite(默认):modernc.org/sqlite 纯 Go 驱动(GORM 封装 glebarez/sqlite),零 CGO、零外部依赖; +// - postgres:可选,配置 FCB_DB_DRIVER=postgres + FCB_DB_DSN 后启用。 +// +// 两方言共用 GORM 抽象层,AutoMigrate 与全部业务查询保持方言无关; +// 唯一的原生 SQL(migrates 建表)已改为双方言分支。 +package database + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/glebarez/sqlite" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm.io/gorm/logger" + + "filecodebox/internal/config" + "filecodebox/internal/model" +) + +// Options 连接选项(main.go 从 config.Env 装配)。 +type Options struct { + Driver string // sqlite | postgres(空按 sqlite 处理) + DSN string // postgres 连接串;sqlite 为文件路径(空回退 config.DefaultSQLitePath) +} + +// Open 按驱动连接数据库并执行连接池设置与探活。 +func Open(ctx context.Context, opts Options) (*gorm.DB, error) { + driver := strings.ToLower(strings.TrimSpace(opts.Driver)) + if driver == "" { + driver = config.DBDriverSQLite + } + var dialector gorm.Dialector + switch driver { + case config.DBDriverSQLite: + path := strings.TrimSpace(opts.DSN) + if path == "" { + path = config.DefaultSQLitePath + } + // 自动创建父目录(如 ./data),对齐参考实现 data_root 语义 + if dir := filepath.Dir(path); dir != "" && dir != "." { + if err := os.MkdirAll(dir, 0o755); err != nil { + return nil, fmt.Errorf("database: 创建 SQLite 目录 %s 失败: %w", dir, err) + } + } + // DSN 参数:busy_timeout 防写锁竞态;WAL 提升并发读写(Query 参数形式,驱动原生支持) + dsn := path + "?_pragma=busy_timeout(10000)&_pragma=journal_mode(WAL)&_pragma=foreign_keys(1)" + dialector = sqlite.Open(dsn) + case config.DBDriverPostgres: + if strings.TrimSpace(opts.DSN) == "" { + return nil, fmt.Errorf("database: FCB_DB_DRIVER=postgres 需要提供 FCB_DB_DSN") + } + dialector = postgres.Open(opts.DSN) + default: + return nil, fmt.Errorf("database: 不支持的数据库驱动 %q(仅支持 sqlite|postgres)", driver) + } + + db, err := gorm.Open(dialector, &gorm.Config{ + Logger: logger.Default.LogMode(logger.Warn), + // 避免 GORM 生成方言特有子句;时间语义由应用层统一(容器本地时区) + NowFunc: time.Now, + }) + if err != nil { + return nil, fmt.Errorf("database: 连接 %s 失败: %w", driver, err) + } + + sqlDB, err := db.DB() + if err != nil { + return nil, err + } + // 连接池:SQLite 单文件场景保守设置;Postgres 沿用 v1 参数 + switch driver { + case config.DBDriverSQLite: + sqlDB.SetMaxOpenConns(8) + sqlDB.SetMaxIdleConns(4) + sqlDB.SetConnMaxLifetime(0) // 长连接文件句柄,无需轮换 + case config.DBDriverPostgres: + sqlDB.SetMaxOpenConns(32) + sqlDB.SetMaxIdleConns(8) + sqlDB.SetConnMaxLifetime(time.Hour) + } + + // 连接探活(带超时) + pingCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + if err := sqlDB.PingContext(pingCtx); err != nil { + return nil, fmt.Errorf("database: %s 探活失败: %w", driver, err) + } + return db, nil +} + +// Migrate 执行迁移:先建迁移台账表(双方言分支),再 AutoMigrate 全部模型。 +func Migrate(ctx context.Context, db *gorm.DB) error { + if err := createMigratesTable(ctx, db); err != nil { + return err + } + if err := model.AutoMigrate(db); err != nil { + return fmt.Errorf("database: AutoMigrate 失败: %w", err) + } + return nil +} + +// createMigratesTable 创建迁移台账表。 +// 双方言差异:自增主键 postgres 用 BIGSERIAL、sqlite 用 INTEGER PRIMARY KEY AUTOINCREMENT; +// 时间戳默认值 postgres 用 CURRENT_TIMESTAMP、sqlite 用 CURRENT_TIMESTAMP(等价)。 +func createMigratesTable(ctx context.Context, db *gorm.DB) error { + ddl := ` + CREATE TABLE IF NOT EXISTS migrates ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + migration_file VARCHAR(255) NOT NULL UNIQUE, + executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + )` + if db.Dialector.Name() == config.DBDriverPostgres { + ddl = ` + CREATE TABLE IF NOT EXISTS migrates ( + id BIGSERIAL PRIMARY KEY, + migration_file VARCHAR(255) NOT NULL UNIQUE, + executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + )` + } + if err := db.WithContext(ctx).Exec(ddl).Error; err != nil { + return fmt.Errorf("database: 创建 migrates 表失败: %w", err) + } + return nil +} + +// Close 关闭底层连接。 +func Close(db *gorm.DB) error { + sqlDB, err := db.DB() + if err != nil { + return err + } + return sqlDB.Close() +} diff --git a/server/internal/database/database_test.go b/server/internal/database/database_test.go new file mode 100644 index 0000000..4d0d122 --- /dev/null +++ b/server/internal/database/database_test.go @@ -0,0 +1,237 @@ +// 数据库双方言测试(需求 ⑧): +// - sqlite:始终执行(纯 Go,临时目录建库); +// - postgres:设置 FCB_TEST_PG_DSN(真实连接串)后执行,未设置时跳过。 +// +// 覆盖:Open/Migrate 全表建立、settings KV 读写、JSON 字段往返、 +// 分页查询(LIMIT/OFFSET 语义)、布尔/时间字段往返 —— 双方言逐项比对。 +package database_test + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "gorm.io/gorm" + + "filecodebox/internal/database" + "filecodebox/internal/model" +) + +// pgTestDSN 返回 Postgres 测试连接串;未设置 FCB_TEST_PG_DSN 时返回空。 +func pgTestDSN(t *testing.T) string { + t.Helper() + dsn := os.Getenv("FCB_TEST_PG_DSN") + if dsn == "" { + t.Skip("未设置 FCB_TEST_PG_DSN,跳过 Postgres 双方言用例(sqlite 用例仍执行)") + } + return dsn +} + +// openTestDB 按方言打开数据库并执行迁移;返回 gorm 实例与关闭函数。 +func openTestDB(t *testing.T, driver, dsn string) (*gorm.DB, func()) { + t.Helper() + if dsn == "" { + // sqlite:临时文件库 + dir := t.TempDir() + dsn = filepath.Join(dir, "test.db") + } + ctx := context.Background() + db, err := database.Open(ctx, database.Options{Driver: driver, DSN: dsn}) + if err != nil { + t.Fatalf("[%s] Open 失败: %v", driver, err) + } + if err := database.Migrate(ctx, db); err != nil { + _ = database.Close(db) + t.Fatalf("[%s] Migrate 失败: %v", driver, err) + } + return db, func() { _ = database.Close(db) } +} + +// runDialectSuite 双方言共用的行为断言集。 +func runDialectSuite(t *testing.T, db *gorm.DB) { + t.Helper() + ctx := context.Background() + + // —— 1. 全表建立 —— + for _, m := range model.AllModels() { + if !db.Migrator().HasTable(m) { + t.Fatalf("表 %T 未创建", m) + } + } + + // —— 2. settings KV 读写 + JSON 字段往返 —— + // GORM 软特性:KeyValue.Value 为 *string(JSON 文本),双方言 text 类型 + // 可重跑:先清掉同键旧行(共享测试库场景) + if err := db.WithContext(ctx).Where(model.KeyValue{Key: "settings"}).Delete(&model.KeyValue{}).Error; err != nil { + t.Fatalf("KV 旧数据清理失败: %v", err) + } + kv := map[string]any{"background_url": "https://example.com/bg.jpg", "footer_beian": "京ICP备2024000001号-1", "max_save_seconds": 3600} + raw, err := json.Marshal(kv) + if err != nil { + t.Fatalf("marshal KV: %v", err) + } + row := model.KeyValue{Key: "settings", Value: strPtr(string(raw))} + if err := db.WithContext(ctx).Create(&row).Error; err != nil { + t.Fatalf("KV 写入失败: %v", err) + } + var got model.KeyValue + if err := db.WithContext(ctx).Where(model.KeyValue{Key: "settings"}).First(&got).Error; err != nil { + t.Fatalf("KV 读取失败: %v", err) + } + parsed := map[string]any{} + if err := json.Unmarshal([]byte(*got.Value), &parsed); err != nil { + t.Fatalf("KV JSON 解析失败: %v", err) + } + if parsed["background_url"] != "https://example.com/bg.jpg" { + t.Fatalf("KV JSON 字段往返不一致: %v", parsed) + } + // 更新(先查后改,方言无关) + if err := db.WithContext(ctx).Model(&got).Update("value", strPtr(`{"notify_enabled":0}`)).Error; err != nil { + t.Fatalf("KV 更新失败: %v", err) + } + var got2 model.KeyValue + _ = db.WithContext(ctx).Where(model.KeyValue{Key: "settings"}).First(&got2) + if *got2.Value != `{"notify_enabled":0}` { + t.Fatalf("KV 更新未生效: %s", *got2.Value) + } + + // —— 3. 分页查询(LIMIT/OFFSET)—— + // 每次运行用随机前缀避免脏数据互相影响 + prefix := fmt.Sprintf("pg%d_", time.Now().UnixNano()) + for i := 0; i < 25; i++ { + fc := model.FileCodes{ + Code: fmt.Sprintf("%s%03d", prefix, i), + ExpiredCount: -1, + IsChunked: i%2 == 0, // 布尔字段往返 + } + if err := db.WithContext(ctx).Create(&fc).Error; err != nil { + t.Fatalf("FileCodes 写入失败: %v", err) + } + } + var page []model.FileCodes + if err := db.WithContext(ctx). + Where("code LIKE ?", prefix+"%"). + Order("id ASC"). + Limit(10).Offset(20). + Find(&page).Error; err != nil { + t.Fatalf("分页查询失败: %v", err) + } + if len(page) != 5 { + t.Fatalf("第二页应剩 5 条,实际 %d", len(page)) + } + if page[0].Code != prefix+"020" { + t.Fatalf("分页偏移错误: %s", page[0].Code) + } + var total int64 + if err := db.WithContext(ctx).Model(&model.FileCodes{}). + Where("code LIKE ?", prefix+"%").Count(&total).Error; err != nil { + t.Fatalf("计数查询失败: %v", err) + } + if total != 25 { + t.Fatalf("总数应 25,实际 %d", total) + } + + // —— 4. 布尔/时间/可空字段往返 —— + now := time.Now().Truncate(time.Second) // sqlite 秒级精度 + fc := model.FileCodes{ + Code: prefix + "special", + ExpiredAt: &now, + ExpiredCount: 5, + Text: strPtr("你好 FileCodeBox"), + FileHash: strPtr("abc123"), + IsChunked: true, + } + if err := db.WithContext(ctx).Create(&fc).Error; err != nil { + t.Fatalf("完整字段写入失败: %v", err) + } + var back model.FileCodes + if err := db.WithContext(ctx).Where(model.FileCodes{Code: fc.Code}).First(&back).Error; err != nil { + t.Fatalf("完整字段读取失败: %v", err) + } + if back.Text == nil || *back.Text != "你好 FileCodeBox" { + t.Fatalf("text 字段往返不一致: %v", back.Text) + } + if !back.IsChunked { + t.Fatal("布尔字段往返不一致") + } + if back.ExpiredAt == nil { + t.Fatal("时间字段往返丢失") + } + if diff := back.ExpiredAt.Sub(now); diff > time.Second || diff < -time.Second { + t.Fatalf("时间字段偏差过大: %v", diff) + } + if back.FileHash == nil || *back.FileHash != "abc123" { + t.Fatalf("可空字段往返不一致: %v", back.FileHash) + } + // LOWER + LIKE(admin 列表检索路径:真实代码先对关键词小写化再拼 LIKE 模式, + // 对齐 admin.go 的 "LOWER(code) LIKE ?" 用法,双方言均支持) + var hits int64 + lowerPattern := "%" + strings.ToLower(prefix+"SPECIAL") + "%" + if err := db.WithContext(ctx).Model(&model.FileCodes{}). + Where("LOWER(code) LIKE ?", lowerPattern).Count(&hits).Error; err != nil { + t.Fatalf("LOWER/LIKE 查询失败: %v", err) + } + if hits != 1 { + t.Fatalf("LOWER/LIKE 命中数应 1,实际 %d", hits) + } + // 可重跑:清理本前缀数据(共享测试库场景) + if err := db.WithContext(ctx).Where("code LIKE ?", prefix+"%").Delete(&model.FileCodes{}).Error; err != nil { + t.Fatalf("清理测试数据失败: %v", err) + } +} + +func strPtr(s string) *string { return &s } + +// TestSQLiteDialect sqlite(默认模式):临时文件库全流程。 +func TestSQLiteDialect(t *testing.T) { + db, closeFn := openTestDB(t, "sqlite", "") + defer closeFn() + runDialectSuite(t, db) +} + +// TestSQLiteInMemoryDialect sqlite 内存库(DSN 为 :memory: 等价路径场景)。 +func TestSQLiteInMemoryDialect(t *testing.T) { + dir := t.TempDir() + db, closeFn := openTestDB(t, "sqlite", filepath.Join(dir, "mem.db")) + defer closeFn() + runDialectSuite(t, db) +} + +// TestPostgresDialect postgres(可选模式):FCB_TEST_PG_DSN 指向真实实例。 +func TestPostgresDialect(t *testing.T) { + dsn := pgTestDSN(t) + db, closeFn := openTestDB(t, "postgres", dsn) + defer closeFn() + runDialectSuite(t, db) +} + +// TestOpenRejectsUnknownDriver 非法驱动应报错。 +func TestOpenRejectsUnknownDriver(t *testing.T) { + if _, err := database.Open(context.Background(), database.Options{Driver: "mysql", DSN: "x"}); err == nil { + t.Fatal("非法驱动应报错") + } +} + +// TestOpenPostgresRequiresDSN postgres 模式缺 DSN 应报错。 +func TestOpenPostgresRequiresDSN(t *testing.T) { + if _, err := database.Open(context.Background(), database.Options{Driver: "postgres", DSN: ""}); err == nil { + t.Fatal("postgres 缺 DSN 应报错") + } +} + +// TestSQLiteAutoCreatesDataDir sqlite 默认相对路径下自动创建父目录。 +func TestSQLiteAutoCreatesDataDir(t *testing.T) { + dir := t.TempDir() + nested := filepath.Join(dir, "deep", "data", "fcb.db") + db, closeFn := openTestDB(t, "sqlite", nested) + defer closeFn() + if _, err := os.Stat(nested); err != nil { + t.Fatalf("数据库文件应已创建: %v", err) + } + runDialectSuite(t, db) +} diff --git a/server/internal/janitor/janitor.go b/server/internal/janitor/janitor.go new file mode 100644 index 0000000..cacff18 --- /dev/null +++ b/server/internal/janitor/janitor.go @@ -0,0 +1,124 @@ +// Package janitor 后台清理循环(安全审计 M5): +// 回收过期容量预留、超时未完成的上传会话(含其分片对象)与过期预签名会话 +// (direct 模式残留对象一并删除)。此前这些资源仅在同 token 复用/显式取消时 +// 释放,恶意 init 可长期占用容量预留或累积垃圾数据。 +package janitor + +import ( + "context" + "errors" + "log" + "time" + + "gorm.io/gorm" + + "filecodebox/internal/model" + "filecodebox/internal/storage" +) + +// chunkSessionMaxAge 未完成分片会话的最大保留时长(预留 TTL 为 2h, +// 会话保留 24h 以支持断点续传;超时后由本循环清理)。 +const chunkSessionMaxAge = 24 * time.Hour + +// presignGrace 过期预签名会话的宽限时长(到点即删,避免与在途 confirm 竞争)。 +const presignGrace = time.Hour + +// Start 启动周期清理循环;ctx 取消时退出。 +func Start(ctx context.Context, db *gorm.DB, store *storage.Manager, interval time.Duration) { + go func() { + ticker := time.NewTicker(interval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + Run(ctx, db, store) + } + } + }() +} + +// Run 执行一轮清理;单项失败仅记日志,不影响其他项。 +func Run(ctx context.Context, db *gorm.DB, store *storage.Manager) { + now := time.Now() + cleanExpiredReservations(ctx, db, now) + cleanExpiredChunkSessions(ctx, db, store, now) + cleanExpiredPresignSessions(ctx, db, store, now) +} + +// cleanExpiredReservations 删除全部过期容量预留。 +func cleanExpiredReservations(ctx context.Context, db *gorm.DB, now time.Time) { + if err := db.WithContext(ctx). + Where("expires_at <= ?", now). + Delete(&model.StorageReservation{}).Error; err != nil { + log.Printf("[janitor] 清理过期容量预留失败: %v", err) + } +} + +// engineFor 按归属引擎取回实例;空/未知引擎回落当前引擎(对齐 API 层 storeFor 语义)。 +func engineFor(store *storage.Manager, name string) (storage.Storage, error) { + if name != "" && storage.ValidEngine(name) { + if s, err := store.EngineOf(name); err == nil { + return s, nil + } + } + return store.Current(), nil +} + +// cleanExpiredChunkSessions 清理超时未完成的分片会话及其分片对象。 +func cleanExpiredChunkSessions(ctx context.Context, db *gorm.DB, store *storage.Manager, now time.Time) { + var sessions []model.UploadChunk + if err := db.WithContext(ctx). + Where("chunk_index = -1 AND created_at < ?", now.Add(-chunkSessionMaxAge)). + Limit(200). + Find(&sessions).Error; err != nil { + log.Printf("[janitor] 查询过期分片会话失败: %v", err) + return + } + for _, s := range sessions { + engine, err := engineFor(store, s.Engine) + if err == nil && s.SavePath != "" { + if err := engine.CleanChunks(ctx, s.UploadID, s.SavePath); err != nil && + !errors.Is(err, storage.ErrNotFound) && !errors.Is(err, storage.ErrInvalidPath) { + log.Printf("[janitor] 清理分片对象失败 upload_id=%s: %v", s.UploadID, err) + } + } + if err := db.WithContext(ctx). + Where("upload_id = ?", s.UploadID). + Delete(&model.UploadChunk{}).Error; err != nil { + log.Printf("[janitor] 删除过期分片会话失败 upload_id=%s: %v", s.UploadID, err) + continue + } + log.Printf("[janitor] 已清理超时分片会话 upload_id=%s file=%s", s.UploadID, s.FileName) + } +} + +// cleanExpiredPresignSessions 清理过期预签名会话;direct 模式残留对象一并删除。 +func cleanExpiredPresignSessions(ctx context.Context, db *gorm.DB, store *storage.Manager, now time.Time) { + var sessions []model.PresignUploadSession + if err := db.WithContext(ctx). + Where("expires_at < ?", now.Add(-presignGrace)). + Limit(200). + Find(&sessions).Error; err != nil { + log.Printf("[janitor] 查询过期预签名会话失败: %v", err) + return + } + for _, s := range sessions { + if s.Mode == "direct" && s.SavePath != "" { + if engine, err := engineFor(store, s.Engine); err == nil { + if err := engine.DeleteFile(ctx, s.SavePath); err != nil && + !errors.Is(err, storage.ErrNotFound) && !errors.Is(err, storage.ErrInvalidPath) { + log.Printf("[janitor] 删除直传残留对象失败 upload_id=%s: %v", s.UploadID, err) + } + } + } + if err := db.WithContext(ctx). + Where("upload_id = ?", s.UploadID). + Delete(&model.PresignUploadSession{}).Error; err != nil { + log.Printf("[janitor] 删除过期预签名会话失败 upload_id=%s: %v", s.UploadID, err) + continue + } + log.Printf("[janitor] 已清理过期预签名会话 upload_id=%s mode=%s", s.UploadID, s.Mode) + } +} diff --git a/server/internal/middleware/audit.go b/server/internal/middleware/audit.go new file mode 100644 index 0000000..fb6168f --- /dev/null +++ b/server/internal/middleware/audit.go @@ -0,0 +1,261 @@ +package middleware + +import ( + "net/http" + "strings" + "time" + + "github.com/gin-gonic/gin" + + "filecodebox/internal/audit" + "filecodebox/internal/model" + "filecodebox/internal/response" +) + +// auditHooks 审计钩子:由 API 层在响应前后填充与落库。 +// 中间件负责计时与公共字段(IP/UA/设备/耗时),业务上下文通过 auditEntry 传递。 +type auditEntry struct { + Entry audit.Entry + // start 请求进入审计中间件的时刻,用于计算耗时。 + start time.Time + // writer 下载动作时包装的响应计数器。 + writer *bytesCountWriter + // skip 为 true 表示业务 handler 显式跳过审计(AuditSkip)。 + skip bool + // recorded 防止重复落库。 + recorded bool +} + +// bytesCountWriter 统计响应体写出字节数(用于下载审计)。 +type bytesCountWriter struct { + gin.ResponseWriter + count int64 +} + +func (w *bytesCountWriter) Write(b []byte) (int, error) { + n, err := w.ResponseWriter.Write(b) + w.count += int64(n) + return n, err +} + +func (w *bytesCountWriter) WriteString(s string) (int, error) { + n, err := w.ResponseWriter.WriteString(s) + w.count += int64(n) + return n, err +} + +// Classifier 判定请求是否属于需审计的动作;返回动作名与是否命中。 +type Classifier func(c *gin.Context) (action string, ok bool) + +// DefaultClassifier 按任务合同的默认路由语义分类: +// - 上传:POST /share/file、/share/text、/chunk/upload*、/presign* +// - 下载:GET /share/download、/share/select、/share/metadata +// - 管理(L5):POST/PATCH/DELETE 的敏感管理操作——登录/登出、配置与密码 +// 修改、存储引擎切换、文件更新/删除/策略动作 +// +// API 层可传入自定义分类器覆盖。 +func DefaultClassifier(c *gin.Context) (string, bool) { + path := c.FullPath() + if path == "" { + path = c.Request.URL.Path + } + p := strings.TrimRight(path, "/") + switch c.Request.Method { + case http.MethodPost, http.MethodPut: + switch { + case p == "/share/file" || p == "/share/text": + return audit.ActionUpload, true + case strings.HasPrefix(p, "/chunk/upload"): + return audit.ActionUpload, true + case strings.HasPrefix(p, "/presign"): + return audit.ActionUpload, true + } + if adminAuditActions[p] { + return audit.ActionAdmin, true + } + case http.MethodPatch, http.MethodDelete: + if adminAuditActions[p] { + return audit.ActionAdmin, true + } + case http.MethodGet: + switch p { + case "/share/download", "/share/select", "/share/metadata": + return audit.ActionDownload, true + } + } + return "", false +} + +// adminAuditActions 需要审计的管理端敏感操作路由(L5)。 +var adminAuditActions = map[string]bool{ + "/admin/login": true, + "/admin/logout": true, + "/admin/config/update": true, + "/admin/settings/password": true, + "/admin/storage/switch": true, + "/admin/file/update": true, + "/admin/file/delete": true, + "/admin/file/batch-delete": true, + "/admin/file/batch-update": true, + "/admin/file/policy-action": true, + "/admin/file/batch-policy-action": true, +} + +// Audit 审计中间件:对分类器命中的 upload/download/admin 动作写审计日志。 +// handler 通过 AuditSet 填充取件码/文件名/字节数等业务字段; +// handler 未显式 AuditRecordRequest 时按 HTTP 状态兜底落库。 +func Audit(service *audit.Service, classify Classifier) gin.HandlerFunc { + if classify == nil { + classify = DefaultClassifier + } + return func(c *gin.Context) { + start := time.Now() + + action, ok := classify(c) + // 未命中审计动作的请求直接放行,不产生审计记录。 + // L5:admin 类动作同样需要建 auditEntry 并落库(登录失败/配置变更等)。 + if !ok { + c.Next() + return + } + + entry := audit.Entry{ + Action: action, + IP: GetClientIP(c), + UserAgent: c.Request.UserAgent(), + } + info := audit.ParseUserAgent(entry.UserAgent) + entry.DeviceOS = info.OS + entry.DeviceBrowser = info.Browser + entry.DeviceType = info.Type + + // 交给后续 handler 填充 + state := &auditEntry{Entry: entry, start: start} + c.Set("auditEntry", state) + + // 下载动作:包装 Writer 以捕获实际写出字节数(必须在 c.Next() 前替换) + if action == audit.ActionDownload { + state.writer = &bytesCountWriter{ResponseWriter: c.Writer} + c.Writer = state.writer + } + + c.Next() + + // 下载兜底统计:handler 未填 TransferredBytes 时取响应写出字节 + if action == audit.ActionDownload && state.Entry.TransferredBytes == 0 && + !state.recorded && !state.skip && state.writer != nil { + state.Entry.TransferredBytes = state.writer.count + } + + // handler 未显式落库时兜底记录 + ae, exists := c.Get("auditEntry") + if !exists { + return + } + state, isState := ae.(*auditEntry) + if !isState || state.recorded || state.skip { + return + } + state.Entry.Duration = time.Since(start) + state.Entry.Actor = resolveActor(c) + status := c.Writer.Status() + switch { + case state.Entry.Result != "": + // handler 已给出结论 + case status >= 500: + state.Entry.Result = model.AuditResultFailed + case status == 401 || status == 403 || status == 423 || status == 429 || status == 428: + state.Entry.Result = model.AuditResultDenied + case status >= 400: + state.Entry.Result = model.AuditResultFailed + default: + state.Entry.Result = model.AuditResultSuccess + } + switch { + case state.Entry.ErrorMsg != "": + // handler 已给出错误信息 + case c.Errors.String() != "": + state.Entry.ErrorMsg = c.Errors.String() + case status >= 400: + // 兜底:记录 HTTP 状态 + state.Entry.ErrorMsg = "HTTP " + itoa64(int64(status)) + } + service.Record(state.Entry) + state.recorded = true + } +} + +// AuditEntry 获取当前请求的审计状态(由 Audit 中间件创建)。 +func AuditEntry(c *gin.Context) *auditEntry { + if v, ok := c.Get("auditEntry"); ok { + if ae, ok := v.(*auditEntry); ok { + return ae + } + } + return nil +} + +// AuditSet 填充当前请求的审计字段;仅对已启用审计的请求生效。 +func AuditSet(c *gin.Context, fn func(e *audit.Entry)) { + if ae := AuditEntry(c); ae != nil && fn != nil { + fn(&ae.Entry) + } +} + +// AuditRecordRequest 显式触发落库(含耗时);由 handler 在响应前调用。 +func AuditRecordRequest(c *gin.Context, service *audit.Service, result, errMsg string) { + ae := AuditEntry(c) + if ae == nil || ae.recorded || ae.skip { + return + } + ae.Entry.Duration = time.Since(ae.start) + ae.Entry.Result = result + ae.Entry.ErrorMsg = errMsg + ae.Entry.Actor = resolveActor(c) + service.Record(ae.Entry) + ae.recorded = true +} + +// AuditSkip 标记当前请求不写审计。 +func AuditSkip(c *gin.Context) { + if ae := AuditEntry(c); ae != nil { + ae.skip = true + } +} + +// resolveActor 判断请求者角色:管理员 JWT 有效 → admin,否则 guest。 +func resolveActor(c *gin.Context) string { + header := c.GetHeader("Authorization") + if len(header) > 7 && header[:7] == "Bearer " { + // 仅检查声明是否有效,不重复校验签名逻辑(AdminAuth 已处理受保护路由) + if _, ok := c.Get("claims"); ok { + return audit.ActorAdmin + } + } + return audit.ActorGuest +} + +// AuditRecord 显式按结果落库;duration 由中间件按起始时间计算。 +func AuditRecord(c *gin.Context, service *audit.Service, result, errMsg string) { + ae := AuditEntry(c) + if ae == nil || ae.recorded || ae.skip { + return + } + AuditRecordRequest(c, service, result, errMsg) +} + +// GuardNotInitialized 系统未初始化守卫:除 setup/health 外返回 428。 +func GuardNotInitialized(isInit func() bool) gin.HandlerFunc { + return func(c *gin.Context) { + if isInit() { + c.Next() + return + } + path := c.Request.URL.Path + if path == "/setup" || path == "/api/v1/health" { + c.Next() + return + } + response.Fail(c, 428, "系统未初始化,请先完成初始化") + } +} diff --git a/server/internal/middleware/audit_l5_test.go b/server/internal/middleware/audit_l5_test.go new file mode 100644 index 0000000..a0149c8 --- /dev/null +++ b/server/internal/middleware/audit_l5_test.go @@ -0,0 +1,89 @@ +// audit_l5_test.go — L5 回归:admin 类动作(如登录失败)必须落审计。 +package middleware + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/gin-gonic/gin" + + "filecodebox/internal/audit" + "filecodebox/internal/model" +) + +type captureSink struct { + logs []model.AuditLog +} + +func (s *captureSink) Save(_ context.Context, logs []model.AuditLog) error { + s.logs = append(s.logs, logs...) + return nil +} + +// TestAuditRecordsAdminActions L5:/admin/login 失败(401)后应产生一条 +// result=denied 的 admin 审计记录(此前 skip 条件把 admin 动作整体跳过)。 +func TestAuditRecordsAdminActions(t *testing.T) { + gin.SetMode(gin.TestMode) + sink := &captureSink{} + svc := audit.NewService(sink) + + r := gin.New() + r.Use(Audit(svc, nil)) // DefaultClassifier + r.POST("/admin/login", func(c *gin.Context) { + c.JSON(http.StatusUnauthorized, gin.H{"code": 401}) + }) + r.POST("/share/text", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"code": 200}) + }) + r.GET("/healthz", func(c *gin.Context) { + c.Status(http.StatusOK) // 未分类动作:不应产生审计 + }) + + // 管理端:401 → denied + w := httptest.NewRecorder() + r.ServeHTTP(w, httptest.NewRequest("POST", "/admin/login", nil)) + if w.Code != http.StatusUnauthorized { + t.Fatalf("login should 401, got %d", w.Code) + } + // 上传类:200 → success + w2 := httptest.NewRecorder() + r.ServeHTTP(w2, httptest.NewRequest("POST", "/share/text", nil)) + // 未分类:不落库 + w3 := httptest.NewRecorder() + r.ServeHTTP(w3, httptest.NewRequest("GET", "/healthz", nil)) + + // audit.Service 异步落库,轮询等待 + var actions []string + for i := 0; i < 50; i++ { + if len(sink.logs) >= 2 { + break + } + waitMillis(20) + } + if len(sink.logs) != 2 { + t.Fatalf("应恰好 2 条审计记录, got %d", len(sink.logs)) + } + for _, l := range sink.logs { + actions = append(actions, l.Action) + switch l.Action { + case audit.ActionAdmin: + if l.Result != model.AuditResultDenied { + t.Fatalf("admin 401 应记 denied, got %q", l.Result) + } + case audit.ActionUpload: + if l.Result != model.AuditResultSuccess { + t.Fatalf("upload 200 应记 success, got %q", l.Result) + } + default: + t.Fatalf("意外动作 %q", l.Action) + } + } + _ = actions +} + +func waitMillis(ms int) { + time.Sleep(time.Duration(ms) * time.Millisecond) +} diff --git a/server/internal/middleware/audit_test.go b/server/internal/middleware/audit_test.go new file mode 100644 index 0000000..8c84011 --- /dev/null +++ b/server/internal/middleware/audit_test.go @@ -0,0 +1,198 @@ +package middleware + +import ( + "context" + "net/http/httptest" + "sync" + "testing" + "time" + + "github.com/gin-gonic/gin" + + "filecodebox/internal/audit" + "filecodebox/internal/model" +) + +// memSink 测试用内存落库实现。 +type memSink struct { + mu sync.Mutex + logs []model.AuditLog + notif chan struct{} +} + +func newMemSink() *memSink { return &memSink{notif: make(chan struct{}, 16)} } + +func (m *memSink) Save(_ context.Context, logs []model.AuditLog) error { + m.mu.Lock() + m.logs = append(m.logs, logs...) + m.mu.Unlock() + m.notif <- struct{}{} + return nil +} + +func (m *memSink) snapshot() []model.AuditLog { + m.mu.Lock() + defer m.mu.Unlock() + out := make([]model.AuditLog, len(m.logs)) + copy(out, m.logs) + return out +} + +// waitFor 等待 sink 收到 n 条记录(带超时)。 +func (m *memSink) waitFor(t *testing.T, n int) []model.AuditLog { + t.Helper() + deadline := time.After(2 * time.Second) + for { + logs := m.snapshot() + if len(logs) >= n { + return logs + } + select { + case <-m.notif: + case <-deadline: + t.Fatalf("等待审计记录超时: 已收到 %d 条", len(logs)) + } + } +} + +func auditRouter(svc *audit.Service) *gin.Engine { + r := gin.New() + r.Use(ClientIP(nil)) + r.Use(Audit(svc, nil)) // 默认分类器 + // 上传路由:命中默认分类器(POST /share/file) + r.POST("/share/file", func(c *gin.Context) { + // 模拟 handler 填充业务字段并显式落库 + AuditSet(c, func(e *audit.Entry) { + e.FileCode = "Ab3xY" + e.FileName = "hello.zip" + e.SizeBytes = 1024 + }) + AuditRecordRequest(c, svc, model.AuditResultSuccess, "") + c.JSON(200, gin.H{"ok": true}) + }) + // 下载路由:命中默认分类器(GET /share/download) + r.GET("/share/download", func(c *gin.Context) { + AuditSet(c, func(e *audit.Entry) { e.FileCode = "Xy12Z" }) + c.JSON(404, gin.H{"msg": "文件已过期删除"}) // 未显式落库 → 状态码兜底 + }) + // 普通路由:不命中,不应产生审计 + r.GET("/plain", func(c *gin.Context) { c.JSON(200, gin.H{"ok": true}) }) + return r +} + +func TestAuditMiddlewareRecordsUpload(t *testing.T) { + sink := newMemSink() + svc := audit.NewService(sink) + r := auditRouter(svc) + + w := httptest.NewRecorder() + req := httptest.NewRequest("POST", "/share/file", nil) + req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36") + r.ServeHTTP(w, req) + if w.Code != 200 { + t.Fatalf("上传应成功: %d", w.Code) + } + logs := sink.waitFor(t, 1) + e := logs[0] + if e.Action != audit.ActionUpload { + t.Errorf("action = %s", e.Action) + } + if e.FileCode != "Ab3xY" || e.FileName != "hello.zip" { + t.Errorf("file fields = %s/%s", e.FileCode, e.FileName) + } + if e.SizeBytes != 1024 { + t.Errorf("size = %d", e.SizeBytes) + } + if e.Result != model.AuditResultSuccess { + t.Errorf("result = %s", e.Result) + } + if e.DeviceOS != "Windows" || e.DeviceBrowser != "Chrome" || e.DeviceType != "desktop" { + t.Errorf("device = %s/%s/%s", e.DeviceOS, e.DeviceBrowser, e.DeviceType) + } + if e.DurationMs < 0 { + t.Errorf("duration = %d", e.DurationMs) + } + if e.Actor != audit.ActorGuest { + t.Errorf("actor = %s", e.Actor) + } +} + +func TestAuditMiddlewareSkipsPlainRoutes(t *testing.T) { + sink := newMemSink() + svc := audit.NewService(sink) + r := auditRouter(svc) + + w := httptest.NewRecorder() + r.ServeHTTP(w, httptest.NewRequest("GET", "/plain", nil)) + if w.Code != 200 { + t.Fatalf("plain 路由应成功: %d", w.Code) + } + time.Sleep(150 * time.Millisecond) + if logs := sink.snapshot(); len(logs) != 0 { + t.Fatalf("普通路由不应产生审计记录: %v", logs) + } +} + +func TestAuditFailedDownloadFallback(t *testing.T) { + sink := newMemSink() + svc := audit.NewService(sink) + r := auditRouter(svc) + + w := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/share/download?code=xyz", nil) + req.Header.Set("User-Agent", "curl/8.4.0") + r.ServeHTTP(w, req) + logs := sink.waitFor(t, 1) + e := logs[0] + if e.Action != audit.ActionDownload { + t.Errorf("action = %s", e.Action) + } + if e.Result != model.AuditResultFailed { + t.Errorf("4xx 兜底 result = %s", e.Result) + } + if e.DeviceType != "bot" { + t.Errorf("curl 应识别为 bot: %s", e.DeviceType) + } + if e.ErrorMsg == "" { + t.Error("失败记录应包含错误信息") + } +} + +func TestAuditDeniedStatusMapping(t *testing.T) { + sink := newMemSink() + svc := audit.NewService(sink) + r := gin.New() + r.Use(ClientIP(nil)) + r.Use(Audit(svc, nil)) + r.GET("/share/select", func(c *gin.Context) { c.AbortWithStatus(429) }) + + w := httptest.NewRecorder() + r.ServeHTTP(w, httptest.NewRequest("GET", "/share/select?code=abc", nil)) + logs := sink.waitFor(t, 1) + if logs[0].Result != model.AuditResultDenied { + t.Errorf("429 应映射为 denied: %s", logs[0].Result) + } +} + +func TestAuditDownloadBytesCounted(t *testing.T) { + sink := newMemSink() + svc := audit.NewService(sink) + r := gin.New() + r.Use(ClientIP(nil)) + r.Use(Audit(svc, nil)) + r.GET("/share/download", func(c *gin.Context) { + payload := []byte("0123456789abcdef") // 16 字节 + c.Data(200, "application/octet-stream", payload) + // 未显式落库 → 中间件兜底;TransferredBytes 应等于写出字节 + }) + + w := httptest.NewRecorder() + r.ServeHTTP(w, httptest.NewRequest("GET", "/share/download?code=bytes", nil)) + logs := sink.waitFor(t, 1) + if logs[0].TransferredBytes != 16 { + t.Errorf("下载字节数 = %d, want 16", logs[0].TransferredBytes) + } + if logs[0].Result != model.AuditResultSuccess { + t.Errorf("result = %s", logs[0].Result) + } +} diff --git a/server/internal/middleware/bodylimit.go b/server/internal/middleware/bodylimit.go new file mode 100644 index 0000000..5149caf --- /dev/null +++ b/server/internal/middleware/bodylimit.go @@ -0,0 +1,30 @@ +// Package middleware — bodylimit.go:全局请求体大小限制。 +// +// 安全审计 M3:此前服务对请求体无任何上限,text/plain 形态的绑定会先 +// io.ReadAll 全量进内存、multipart 大文件先完整落盘后才被策略拒绝, +// 未认证攻击者可借大 body 消耗内存/磁盘/带宽。 +// 本中间件用 http.MaxBytesReader 按「路径类别」给出上限: +// - 管理端(/admin/*):1MiB; +// - 文本/元信息/取件类(/share/text|metadata|select):1MiB; +// - 其余(含上传):maxFileSize(0=回落 uploadSize,仍为 0 时 64MiB 兜底)+ 2MiB 表单开销。 +// +// 超限时后续读取返回错误,统一被 handler 的 bind 错误路径映射为 400。 +package middleware + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +// BodyLimit 按请求路径动态限制请求体大小(limit<=0 表示不限制)。 +func BodyLimit(limitFn func(c *gin.Context) int64) gin.HandlerFunc { + return func(c *gin.Context) { + if c.Request.Body != nil && limitFn != nil { + if limit := limitFn(c); limit > 0 { + c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, limit) + } + } + c.Next() + } +} diff --git a/server/internal/middleware/cors.go b/server/internal/middleware/cors.go new file mode 100644 index 0000000..bd42ccb --- /dev/null +++ b/server/internal/middleware/cors.go @@ -0,0 +1,74 @@ +package middleware + +import ( + "net/url" + "strings" + + "github.com/gin-gonic/gin" +) + +// Cors 跨域中间件(L6 收紧): +// - 公开接口:维持 allow_origins=*(Bearer Token 认证,无 Cookie CSRF 面); +// - 管理端(/admin/*):当请求携带 Origin 且既不同源也不在允许域名列表时, +// 不回 CORS 头(浏览器将拦截跨域读取)。防止管理端 token 泄露后 +// 被任意第三方页面直接跨域调用。无 Origin 的非浏览器请求不受影响。 +// +// extraAllowedOrigins:管理端额外允许的来源(如 site_domain 配置的对外域名)。 +func Cors(extraAllowedOrigins ...string) gin.HandlerFunc { + allowedHosts := map[string]bool{} + for _, o := range extraAllowedOrigins { + if o == "" { + continue + } + raw := strings.TrimSpace(o) + if !strings.Contains(raw, "://") { + raw = "https://" + raw + } + if u, err := url.Parse(raw); err == nil && u.Host != "" { + allowedHosts[u.Host] = true + } + } + + // adminCrossOriginBlocked 判断 /admin 请求是否应拒绝跨域: + // 仅在「带 Origin 且 Origin 既不同源也不在白名单」时为 true。 + adminBlocked := func(c *gin.Context) bool { + p := c.Request.URL.Path + if p != "/admin" && !strings.HasPrefix(p, "/admin/") { + return false + } + origin := c.GetHeader("Origin") + if origin == "" { + return false + } + o, err := url.Parse(origin) + if err != nil || o.Host == "" { + return true // Origin 非法:按跨域拒绝处理 + } + if o.Host == c.Request.Host || allowedHosts[o.Host] { + return false + } + return true + } + + return func(c *gin.Context) { + if adminBlocked(c) { + // 不回 ACAO;预检直接 204(浏览器会因无 CORS 头拦截后续请求) + if c.Request.Method == "OPTIONS" { + c.AbortWithStatus(204) + return + } + c.Next() + return + } + c.Header("Access-Control-Allow-Origin", "*") + c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD") + c.Header("Access-Control-Allow-Headers", "Authorization, Content-Type, Content-Disposition, X-Requested-With") + c.Header("Access-Control-Expose-Headers", "Content-Disposition, Content-Length") + c.Header("Access-Control-Max-Age", "86400") + if c.Request.Method == "OPTIONS" { + c.AbortWithStatus(204) + return + } + c.Next() + } +} diff --git a/server/internal/middleware/jwt.go b/server/internal/middleware/jwt.go new file mode 100644 index 0000000..ea976b1 --- /dev/null +++ b/server/internal/middleware/jwt.go @@ -0,0 +1,99 @@ +package middleware + +import ( + "errors" + "strings" + "time" + + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" + + "filecodebox/internal/response" +) + +// jwtClaims 自定义声明:对齐参考实现(payload 含 is_admin 与 exp)。 +type jwtClaims struct { + IsAdmin bool `json:"is_admin"` + jwt.RegisteredClaims +} + +// 签发/校验相关错误。 +var ( + ErrTokenExpired = errors.New("token已过期") + ErrTokenInvalid = errors.New("无效的签名") + ErrNotAdmin = errors.New("未授权或授权校验失败") +) + +// SignAdminToken 用 HS256 签发管理员 JWT。 +// secret 为数据库 settings 中的 jwt_secret;expires 为会话有效期。 +func SignAdminToken(secret string, expires time.Duration) (string, time.Time, error) { + if strings.TrimSpace(secret) == "" { + return "", time.Time{}, errors.New("JWT签名密钥未初始化") + } + expiresAt := time.Now().Add(expires) + claims := jwtClaims{ + IsAdmin: true, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(expiresAt), + IssuedAt: jwt.NewNumericDate(time.Now()), + Issuer: "filecodebox", + }, + } + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + signed, err := token.SignedString([]byte(secret)) + return signed, expiresAt, err +} + +// VerifyAdminToken 校验管理员 JWT:签名、过期时间与 is_admin 声明。 +func VerifyAdminToken(secret, token string) (*jwtClaims, error) { + if strings.TrimSpace(secret) == "" { + return nil, errors.New("JWT签名密钥未初始化") + } + parsed, err := jwt.ParseWithClaims(token, &jwtClaims{}, func(t *jwt.Token) (any, error) { + if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, ErrTokenInvalid + } + return []byte(secret), nil + }, jwt.WithValidMethods([]string{"HS256"})) + if err != nil { + if errors.Is(err, jwt.ErrTokenExpired) { + return nil, ErrTokenExpired + } + return nil, ErrTokenInvalid + } + claims, ok := parsed.Claims.(*jwtClaims) + if !ok || !parsed.Valid { + return nil, ErrTokenInvalid + } + if !claims.IsAdmin { + return nil, ErrNotAdmin + } + return claims, nil +} + +// SecretProvider 动态提供当前 jwt_secret(settings KV 运行时可变)。 +type SecretProvider func() string + +// AdminAuth 管理员鉴权中间件:校验 Authorization: Bearer 。 +// 成功后把声明写入 gin 上下文(ctxClaims)。 +func AdminAuth(secret SecretProvider) gin.HandlerFunc { + return func(c *gin.Context) { + header := c.GetHeader("Authorization") + if !strings.HasPrefix(header, "Bearer ") { + response.Fail(c, 401, "未授权或授权校验失败") + return + } + token := strings.TrimSpace(strings.TrimPrefix(header, "Bearer ")) + if token == "" { + response.Fail(c, 401, "未授权或授权校验失败") + return + } + claims, err := VerifyAdminToken(secret(), token) + if err != nil { + response.Fail(c, 401, err.Error()) + return + } + c.Set("claims", claims) + c.Next() + } +} diff --git a/server/internal/middleware/jwt_test.go b/server/internal/middleware/jwt_test.go new file mode 100644 index 0000000..f2b4df8 --- /dev/null +++ b/server/internal/middleware/jwt_test.go @@ -0,0 +1,83 @@ +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/gin-gonic/gin" + + "filecodebox/internal/response" +) + +const testSecret = "unit-test-secret-0123456789abcdef" + +func init() { gin.SetMode(gin.TestMode) } + +func TestSignAndVerifyAdminToken(t *testing.T) { + token, expiresAt, err := SignAdminToken(testSecret, time.Hour) + if err != nil { + t.Fatalf("签发失败: %v", err) + } + if expiresAt.Before(time.Now()) { + t.Fatal("过期时间不合理") + } + claims, err := VerifyAdminToken(testSecret, token) + if err != nil { + t.Fatalf("校验失败: %v", err) + } + if !claims.IsAdmin { + t.Fatal("is_admin 应为 true") + } +} + +func TestVerifyTamperedToken(t *testing.T) { + token, _, _ := SignAdminToken(testSecret, time.Hour) + claims, err := VerifyAdminToken(testSecret+"-wrong", token) + if err == nil || claims != nil { + t.Fatal("密钥不匹配应校验失败") + } + // 篡改 payload + tampered := token[:len(token)-3] + "abc" + if _, err := VerifyAdminToken(testSecret, tampered); err == nil { + t.Fatal("篡改的 token 应校验失败") + } + // 非 HMAC 算法拒绝 + algNone := "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc19hZG1pbiI6dHJ1ZX0." + if _, err := VerifyAdminToken(testSecret, algNone); err == nil { + t.Fatal("none 算法应被拒绝") + } +} + +func TestAdminAuthMiddleware(t *testing.T) { + r := gin.New() + r.GET("/protected", AdminAuth(func() string { return testSecret }), func(c *gin.Context) { + response.OK(c, gin.H{"ok": true}) + }) + + // 无 token → 401 + w := httptest.NewRecorder() + r.ServeHTTP(w, httptest.NewRequest("GET", "/protected", nil)) + if w.Code != http.StatusUnauthorized { + t.Fatalf("无 token 应 401: %d", w.Code) + } + // 有效 token → 200 + token, _, _ := SignAdminToken(testSecret, time.Hour) + w = httptest.NewRecorder() + req := httptest.NewRequest("GET", "/protected", nil) + req.Header.Set("Authorization", "Bearer "+token) + r.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("有效 token 应 200: %d %s", w.Code, w.Body.String()) + } + // 过期 token → 401 + expired, _, _ := SignAdminToken(testSecret, -time.Minute) + w = httptest.NewRecorder() + req = httptest.NewRequest("GET", "/protected", nil) + req.Header.Set("Authorization", "Bearer "+expired) + r.ServeHTTP(w, req) + if w.Code != http.StatusUnauthorized { + t.Fatalf("过期 token 应 401: %d", w.Code) + } +} diff --git a/server/internal/middleware/ratelimit.go b/server/internal/middleware/ratelimit.go new file mode 100644 index 0000000..e15f451 --- /dev/null +++ b/server/internal/middleware/ratelimit.go @@ -0,0 +1,295 @@ +package middleware + +import ( + "context" + "errors" + "net" + "net/http" + "strings" + "sync" + "time" + + "github.com/gin-gonic/gin" + + "filecodebox/internal/cache" + "filecodebox/internal/response" +) + +// 限流类别(对齐参考 apps/base/utils.py 的 ip_limit)。 +const ( + LimitError = "error" // 取件错误(密码错误、取件失败) + LimitUpload = "upload" // 上传次数 + LimitLogin = "login" // 管理员登录失败 + LimitMeta = "metadata" // 分享元信息查询 +) + +// LimitRule 限流规则:window 内最多 count 次。 +type LimitRule struct { + Count int // 允许次数 + Window time.Duration // 时间窗口 +} + +// clientIP 解析客户端真实 IP:仅当直连地址属于可信代理时才采信 X-Forwarded-For / X-Real-IP。 +// 语义对齐参考 apps/base/dependencies.py 的 get_client_ip。 +func clientIP(c *gin.Context, trustedProxies []*net.IPNet) string { + remote := net.ParseIP(c.RemoteIP()) + parse := func(s string) net.IP { + ip := net.ParseIP(strings.TrimSpace(s)) + return ip + } + isTrusted := func(ip net.IP) bool { + if ip == nil { + return false + } + for _, n := range trustedProxies { + if n.Contains(ip) { + return true + } + } + return false + } + + if !isTrusted(remote) { + return remote.String() + } + // X-Forwarded-For:从右往左找第一个非可信代理地址 + if xff := c.GetHeader("X-Forwarded-For"); xff != "" { + parts := strings.Split(xff, ",") + for i := len(parts) - 1; i >= 0; i-- { + candidate := parse(parts[i]) + if candidate == nil { + return remote.String() + } + if !isTrusted(candidate) { + return candidate.String() + } + } + return strings.TrimSpace(parts[0]) + } + if xr := c.GetHeader("X-Real-IP"); xr != "" { + if ip := parse(xr); ip != nil { + return ip.String() + } + } + return remote.String() +} + +// ParseTrustedProxies 把 CIDR/单 IP 字符串解析为网络列表。 +func ParseTrustedProxies(items []string) []*net.IPNet { + var out []*net.IPNet + for _, item := range items { + item = strings.TrimSpace(item) + if item == "" { + continue + } + if !strings.Contains(item, "/") { + item += "/32" + if strings.Contains(item, ":") { // IPv6 + item = item[:len(item)-3] + "/128" + } + } + _, network, err := net.ParseCIDR(item) + if err != nil { + continue + } + out = append(out, network) + } + return out +} + +// ClientIP 中间件:解析真实 IP 并写入上下文(ctxClientIP)。 +func ClientIP(trustedProxies []*net.IPNet) gin.HandlerFunc { + return func(c *gin.Context) { + c.Set("ctxClientIP", clientIP(c, trustedProxies)) + c.Next() + } +} + +// GetClientIP 从 gin 上下文取解析后的客户端 IP。 +func GetClientIP(c *gin.Context) string { + if v, ok := c.Get("ctxClientIP"); ok { + if s, ok := v.(string); ok { + return s + } + } + if ip := net.ParseIP(c.RemoteIP()); ip != nil { + return ip.String() + } + return c.RemoteIP() +} + +// RateLimiter 基于 cache.Cache 的固定窗口 IP 限流器。 +// 计数语义对齐参考实现:check 通过时放行,业务方在发生"计数事件"(如失败/成功上传)后调用 Add。 +// +// L9:缓存故障降级——此前 cache.Get/Incr 失败(如 Redis 宕机)时一律放行, +// 登录爆破防护随之失效。现降级为进程内固定窗口计数(单实例语义), +// 缓存恢复后自动回到共享缓存计数。降级期间计数独立于缓存,不叠加。 +type RateLimiter struct { + cache cache.Cache + limits map[string]LimitRule + prefix string + + fbMu sync.Mutex + fallback map[string]*fallbackEntry // 进程内降级计数 + lastPrune time.Time +} + +type fallbackEntry struct { + count int64 + expires time.Time +} + +// fallbackMaxEntries 降级计数表上限(超出即整体重置,防内存增长)。 +const fallbackMaxEntries = 8192 + +// NewRateLimiter 构造限流器;limits 为各类别规则(来自 settings 的 errorCount/errorMinute 等)。 +func NewRateLimiter(cache cache.Cache, limits map[string]LimitRule) *RateLimiter { + if limits == nil { + limits = map[string]LimitRule{} + } + return &RateLimiter{cache: cache, limits: limits, prefix: "fcb:rl", fallback: map[string]*fallbackEntry{}} +} + +// SetRule 运行时更新规则(settings KV 变更后调用)。 +func (r *RateLimiter) SetRule(kind string, rule LimitRule) { + r.limits[kind] = rule +} + +func (r *RateLimiter) windowKey(kind, ip string, now time.Time) string { + // 固定窗口:按窗口起点分桶 + bucket := now.Unix() / int64(r.limits[kind].Window/time.Second) + return r.prefix + ":" + kind + ":" + ip + ":" + itoa64(bucket) +} + +// Check 只读检查该 IP 在当前窗口内是否仍被允许(不计数)。 +// 对齐参考 check_ip:已用次数 >= 上限即拒绝。 +// 缓存键不存在(ErrNotFound)视为 0 次;缓存故障时降级为进程内计数。 +func (r *RateLimiter) Check(c *gin.Context, kind string) (bool, int64) { + rule, ok := r.limits[kind] + if !ok || rule.Count <= 0 || rule.Window <= 0 { + return true, 0 + } + ip := GetClientIP(c) + ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second) + defer cancel() + now := time.Now() + raw, err := r.cache.Get(ctx, r.windowKey(kind, ip, now)) + if err != nil { + if errors.Is(err, cache.ErrNotFound) { + return true, 0 // 键不存在:窗口内尚无计数 + } + // 缓存故障:降级进程内计数判定 + return r.fallbackCount(kind, ip, rule, now) < int64(rule.Count), 0 + } + n := parseInt64(raw) + return n < int64(rule.Count), n +} + +// Add 记录一次计数事件(对齐参考 add_ip:调用即计数,如上传成功/登录失败/取件错误)。 +// 缓存故障时降级为进程内计数。 +func (r *RateLimiter) Add(c *gin.Context, kind string) { + rule, ok := r.limits[kind] + if !ok || rule.Count <= 0 || rule.Window <= 0 { + return + } + ip := GetClientIP(c) + ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second) + defer cancel() + if _, err := r.cache.Incr(ctx, r.windowKey(kind, ip, time.Now()), rule.Window); err != nil && + !errors.Is(err, cache.ErrNotFound) { + // Incr 正常情况下不会因键不存在失败(缺键即从 0 起); + // 其余错误视为缓存故障 → 进程内计数 + r.fallbackIncr(kind, ip, rule, time.Now()) + } +} + +// —— 进程内降级计数(L9)—— + +func (r *RateLimiter) fallbackIncr(kind, ip string, rule LimitRule, now time.Time) { + key := r.windowKey(kind, ip, now) + expires := now.Add(rule.Window) + r.fbMu.Lock() + defer r.fbMu.Unlock() + r.pruneFallbackLocked(now) + if len(r.fallback) >= fallbackMaxEntries { + r.fallback = map[string]*fallbackEntry{} // 极端情况整体重置,防内存无限增长 + } + e, ok := r.fallback[key] + if !ok || now.After(e.expires) { + r.fallback[key] = &fallbackEntry{count: 1, expires: expires} + return + } + e.count++ +} + +func (r *RateLimiter) fallbackCount(kind, ip string, rule LimitRule, now time.Time) int64 { + key := r.windowKey(kind, ip, now) + r.fbMu.Lock() + defer r.fbMu.Unlock() + e, ok := r.fallback[key] + if !ok || now.After(e.expires) { + return 0 + } + return e.count +} + +// pruneFallbackLocked 清理已过窗口的降级计数(低频触发:每 1000 条或 10 分钟一次)。 +func (r *RateLimiter) pruneFallbackLocked(now time.Time) { + if r.lastPrune.IsZero() || len(r.fallback) >= 1024 || now.Sub(r.lastPrune) >= 10*time.Minute { + for k, e := range r.fallback { + if now.After(e.expires) { + delete(r.fallback, k) + } + } + r.lastPrune = now + } +} + +// RequireRateLimit 中间件:请求进入即检查,请求完成即计数。 +// 适用于"每次访问都计数"的类别(如 metadata 查询); +// 上传/登录等"仅成功/失败才计数"的场景由 handler 显式调用 Check/Add。 +func (r *RateLimiter) RequireRateLimit(kind string) gin.HandlerFunc { + return func(c *gin.Context) { + allowed, _ := r.Check(c, kind) + if !allowed { + response.Fail(c, http.StatusLocked, "请求次数过多,请稍后再试") + return + } + c.Next() + r.Add(c, kind) + } +} + +// parseInt64 解析十进制整数字符串,非法输入返回 0。 +func parseInt64(s string) int64 { + var n int64 + for _, ch := range s { + if ch < '0' || ch > '9' { + return 0 + } + n = n*10 + int64(ch-'0') + } + return n +} + +func itoa64(n int64) string { + if n == 0 { + return "0" + } + neg := n < 0 + if neg { + n = -n + } + var buf [21]byte + i := len(buf) + for n > 0 { + i-- + buf[i] = byte('0' + n%10) + n /= 10 + } + if neg { + i-- + buf[i] = '-' + } + return string(buf[i:]) +} diff --git a/server/internal/middleware/ratelimit_fallback_test.go b/server/internal/middleware/ratelimit_fallback_test.go new file mode 100644 index 0000000..611d3f4 --- /dev/null +++ b/server/internal/middleware/ratelimit_fallback_test.go @@ -0,0 +1,66 @@ +package middleware + +import ( + "context" + "net/http/httptest" + "testing" + "time" + + "github.com/gin-gonic/gin" + + "filecodebox/internal/cache" +) + +// failingCache 模拟缓存故障(Get/Incr 均返回非 ErrNotFound 错误)。 +type failingCache struct{ cache.Cache } + +func (f *failingCache) Get(_ context.Context, _ string) (string, error) { + return "", context.DeadlineExceeded +} +func (f *failingCache) Set(_ context.Context, _, _ string, _ time.Duration) error { + return context.DeadlineExceeded +} +func (f *failingCache) Incr(_ context.Context, _ string, _ time.Duration) (int64, error) { + return 0, context.DeadlineExceeded +} + +func newLimiterTest(c *gin.Context, cacheImpl cache.Cache, count int) *RateLimiter { + return NewRateLimiter(cacheImpl, map[string]LimitRule{ + LimitLogin: {Count: count, Window: time.Minute}, + }) +} + +func ginTestContext() *gin.Context { + gin.SetMode(gin.TestMode) + c, _ := gin.CreateTestContext(httptest.NewRecorder()) + c.Request = httptest.NewRequest("POST", "/admin/login", nil) + return c +} + +// TestRateLimiterFallbackOnCacheFailure L9:缓存故障时限流降级为进程内计数, +// 超过上限后 Check 拒绝(此前 fail-open 会一直放行)。 +func TestRateLimiterFallbackOnCacheFailure(t *testing.T) { + c := ginTestContext() + rl := newLimiterTest(c, &failingCache{cache.NewMemory()}, 3) + for i := 0; i < 3; i++ { + if ok, _ := rl.Check(c, LimitLogin); !ok { + t.Fatalf("第 %d 次检查不应拒绝", i+1) + } + rl.Add(c, LimitLogin) + } + if ok, _ := rl.Check(c, LimitLogin); ok { + t.Fatal("缓存故障降级下,超过上限后 Check 应拒绝(fail-close)") + } +} + +// TestRateLimiterNormalCacheCounting 正常缓存路径行为不变。 +func TestRateLimiterNormalCacheCounting(t *testing.T) { + c := ginTestContext() + rl := newLimiterTest(c, cache.NewMemory(), 2) + for i := 0; i < 2; i++ { + rl.Add(c, LimitLogin) + } + if ok, _ := rl.Check(c, LimitLogin); ok { + t.Fatal("达到上限后 Check 应拒绝") + } +} diff --git a/server/internal/middleware/ratelimit_test.go b/server/internal/middleware/ratelimit_test.go new file mode 100644 index 0000000..0e53f1a --- /dev/null +++ b/server/internal/middleware/ratelimit_test.go @@ -0,0 +1,126 @@ +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/gin-gonic/gin" + + "filecodebox/internal/cache" +) + +func rateLimitRouter(rl *RateLimiter, kind string) *gin.Engine { + r := gin.New() + r.Use(ClientIP(nil)) + r.GET("/limited", rl.RequireRateLimit(kind), func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"ok": true}) + }) + return r +} + +func TestRateLimiterBlocksAfterCount(t *testing.T) { + mem := cache.NewMemory() + defer mem.Close() + rl := NewRateLimiter(mem, map[string]LimitRule{ + LimitMeta: {Count: 3, Window: time.Minute}, + }) + r := rateLimitRouter(rl, LimitMeta) + + // 前 3 次通过 + for i := 0; i < 3; i++ { + w := httptest.NewRecorder() + r.ServeHTTP(w, httptest.NewRequest("GET", "/limited", nil)) + if w.Code != http.StatusOK { + t.Fatalf("第 %d 次应通过: %d", i+1, w.Code) + } + } + // 第 4 次 423 + w := httptest.NewRecorder() + r.ServeHTTP(w, httptest.NewRequest("GET", "/limited", nil)) + if w.Code != http.StatusLocked { + t.Fatalf("超限应 423: %d", w.Code) + } +} + +func TestRateLimiterAddAfterSuccess(t *testing.T) { + // 模拟 upload 语义:Check 放行 + handler 成功后 Add + mem := cache.NewMemory() + defer mem.Close() + rl := NewRateLimiter(mem, map[string]LimitRule{ + LimitUpload: {Count: 2, Window: time.Minute}, + }) + r := gin.New() + r.Use(ClientIP(nil)) + r.POST("/upload", func(c *gin.Context) { + if allowed, _ := rl.Check(c, LimitUpload); !allowed { + c.JSON(http.StatusLocked, gin.H{"err": "too many"}) + return + } + rl.Add(c, LimitUpload) // 成功上传计数 + c.JSON(http.StatusOK, gin.H{"ok": true}) + }) + + for i := 0; i < 2; i++ { + w := httptest.NewRecorder() + r.ServeHTTP(w, httptest.NewRequest("POST", "/upload", nil)) + if w.Code != http.StatusOK { + t.Fatalf("第 %d 次上传应通过", i+1) + } + } + w := httptest.NewRecorder() + r.ServeHTTP(w, httptest.NewRequest("POST", "/upload", nil)) + if w.Code != http.StatusLocked { + t.Fatalf("第 3 次上传应被拒绝: %d", w.Code) + } +} + +func TestParseTrustedProxies(t *testing.T) { + nets := ParseTrustedProxies([]string{"10.0.0.0/8", "192.168.1.1", "", "bad-input"}) + if len(nets) != 2 { + t.Fatalf("应解析出 2 个可信网段: %d", len(nets)) + } +} + +func TestClientIPFromTrustedProxy(t *testing.T) { + // 对齐参考语义:仅当直连地址可信时才解析 XFF, + // 且从右往左返回第一个"非可信代理"地址(该地址即最近可信代理看到的客户端)。 + nets := ParseTrustedProxies([]string{"127.0.0.0/8", "10.0.0.0/8"}) + r := gin.New() + r.Use(ClientIP(nets)) + var seen string + r.GET("/ip", func(c *gin.Context) { seen = GetClientIP(c) }) + + req := httptest.NewRequest("GET", "/ip", nil) + req.RemoteAddr = "127.0.0.1:5000" + req.Header.Set("X-Forwarded-For", "203.0.113.9, 10.0.0.1") + r.ServeHTTP(httptest.NewRecorder(), req) + if seen != "203.0.113.9" { + t.Fatalf("多级可信代理下应取最左非可信地址: %s", seen) + } + + // 仅直连可信:XFF 右起第一个(10.0.0.1)非可信 → 取它 + nets2 := ParseTrustedProxies([]string{"127.0.0.0/8"}) + r2 := gin.New() + r2.Use(ClientIP(nets2)) + var seen2 string + r2.GET("/ip", func(c *gin.Context) { seen2 = GetClientIP(c) }) + req2 := httptest.NewRequest("GET", "/ip", nil) + req2.RemoteAddr = "127.0.0.1:5000" + req2.Header.Set("X-Forwarded-For", "203.0.113.9, 10.0.0.1") + r2.ServeHTTP(httptest.NewRecorder(), req2) + if seen2 != "10.0.0.1" { + t.Fatalf("右起第一个非可信地址应为 10.0.0.1: %s", seen2) + } + + // 非可信直连:忽略伪造头 + seen = "" + req = httptest.NewRequest("GET", "/ip", nil) + req.RemoteAddr = "8.8.8.8:1234" + req.Header.Set("X-Forwarded-For", "1.2.3.4") + r.ServeHTTP(httptest.NewRecorder(), req) + if seen != "8.8.8.8" { + t.Fatalf("非可信直连应忽略 XFF: %s", seen) + } +} diff --git a/server/internal/model/model.go b/server/internal/model/model.go new file mode 100644 index 0000000..708f6ce --- /dev/null +++ b/server/internal/model/model.go @@ -0,0 +1,152 @@ +// Package model 定义 GORM 数据模型与 Postgres 自动迁移。 +// 字段对齐参考实现 apps/base/models.py,并新增审计日志表。 +package model + +import ( + "time" + + "gorm.io/gorm" +) + +// FileCodes 文件/文本分享记录(对齐参考 FileCodes)。 +type FileCodes struct { + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` + Code string `gorm:"column:code;size:255;uniqueIndex;not null" json:"code"` // 取件码 + Prefix string `gorm:"size:255;default:''" json:"prefix"` // 文件名前缀/文本分享标记 + Suffix string `gorm:"size:255;default:''" json:"suffix"` // 文件名后缀(含扩展名) + UUIDFileName *string `gorm:"size:255" json:"uuid_file_name"` // 存储侧 UUID 文件名 + FilePath *string `gorm:"size:255" json:"file_path"` // 存储侧相对路径 + Size int64 `gorm:"default:0" json:"size"` // 字节数;文本为字符数 + Text *string `gorm:"type:text" json:"text"` // 文本分享内容 + ExpiredAt *time.Time `json:"expired_at"` // 过期时间;永久分享为 NULL + ExpiredCount int `gorm:"default:0" json:"expired_count"` // 剩余可取次数;<0 表示按时间过期 + UsedCount int `gorm:"default:0" json:"used_count"` // 已取次数 + CreatedAt time.Time `json:"created_at"` + FileHash *string `gorm:"size:64" json:"file_hash"` // SHA256 + IsChunked bool `gorm:"default:false" json:"is_chunked"` + UploadID *string `gorm:"size:36" json:"upload_id"` // 分片上传会话 ID + Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(v3:local|s3|webdav;空=历史数据按当前引擎取) +} + +// TableName 表名。 +func (FileCodes) TableName() string { return "file_codes" } + +// Expired 判断是否已过期(对齐参考语义:expired_count<0 按时间,否则按次数)。 +func (f *FileCodes) Expired(now time.Time) bool { + if f.ExpiredAt == nil { + return false + } + if f.ExpiredCount < 0 { + return f.ExpiredAt.Before(now) + } + return f.ExpiredCount <= 0 +} + +// UploadChunk 分片上传记录(对齐参考 UploadChunk)。 +type UploadChunk struct { + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` + UploadID string `gorm:"size:36;index:idx_upload_chunk,unique,priority:1;not null" json:"upload_id"` + ChunkIndex int `gorm:"index:idx_upload_chunk,unique,priority:2;not null" json:"chunk_index"` + ChunkHash string `gorm:"size:64;not null" json:"chunk_hash"` // 分片 SHA256 + TotalChunks int `json:"total_chunks"` + FileSize int64 `json:"file_size"` + ChunkSize int `json:"chunk_size"` + FileName string `gorm:"size:255" json:"file_name"` + SavePath string `gorm:"size:512" json:"save_path"` + CreatedAt time.Time `json:"created_at"` + Completed bool `gorm:"default:false" json:"completed"` + Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(v3:分片与会话记录当时引擎,合并走同一引擎) +} + +// TableName 表名。 +func (UploadChunk) TableName() string { return "upload_chunks" } + +// KeyValue 运行时配置键值(对齐参考 KeyValue)。value 存 JSON。 +type KeyValue struct { + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` + Key string `gorm:"size:255;uniqueIndex;not null" json:"key"` + Value *string `gorm:"type:text" json:"value"` // JSON 字符串 + CreatedAt time.Time `json:"created_at"` +} + +// TableName 表名。 +func (KeyValue) TableName() string { return "key_values" } + +// PresignUploadSession 预签名直传会话(对齐参考 PresignUploadSession)。 +type PresignUploadSession struct { + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` + UploadID string `gorm:"size:36;uniqueIndex;not null" json:"upload_id"` + FileName string `gorm:"size:255" json:"file_name"` + FileSize int64 `json:"file_size"` + SavePath string `gorm:"size:512" json:"save_path"` + Mode string `gorm:"size:10" json:"mode"` // direct=客户端直传 | proxy=服务器代理 + ExpireValue int `json:"expire_value"` + ExpireStyle string `gorm:"size:20;default:day" json:"expire_style"` + CreatedAt time.Time `json:"created_at"` + ExpiresAt time.Time `json:"expires_at"` + Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(v3:直传/代理完成走同一引擎取回) +} + +// TableName 表名。 +func (PresignUploadSession) TableName() string { return "presign_upload_sessions" } + +// IsExpired 会话是否已过期。 +func (p *PresignUploadSession) IsExpired(now time.Time) bool { return p.ExpiresAt.Before(now) } + +// StorageReservation 上传容量预留(尚未写入 file_codes 的占位)。 +type StorageReservation struct { + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` + Token string `gorm:"size:64;uniqueIndex;not null" json:"token"` + Size int64 `json:"size"` + ExpiresAt time.Time `gorm:"index" json:"expires_at"` +} + +// TableName 表名。 +func (StorageReservation) TableName() string { return "storage_reservations" } + +// 审计结果常量。 +const ( + AuditResultSuccess = "success" // 操作成功 + AuditResultDenied = "denied" // 被拒绝(限流/鉴权/策略) + AuditResultFailed = "failed" // 执行失败(服务端/客户端错误) +) + +// AuditLog 上传/下载审计日志(需求 ③)。 +type AuditLog struct { + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` + Action string `gorm:"size:32;index" json:"action"` // upload | download + FileCode string `gorm:"size:64;index" json:"file_code"` // 取件码(上传时为生成的码) + FileName string `gorm:"size:255" json:"file_name"` // 原始文件名/文本标记 + SizeBytes int64 `json:"size_bytes"` // 文件总字节数 + TransferredBytes int64 `json:"transferred_bytes"` // 本次实际传输字节数 + IP string `gorm:"size:64;index" json:"ip"` + UserAgent string `gorm:"size:512" json:"user_agent"` + DeviceOS string `gorm:"size:64" json:"device_os"` // Windows/macOS/Android/iOS/Linux/Unknown + DeviceBrowser string `gorm:"size:64" json:"device_browser"` // Chrome/Firefox/Safari/Edge/... + DeviceType string `gorm:"size:32" json:"device_type"` // desktop/mobile/tablet/bot/other + Actor string `gorm:"size:64" json:"actor"` // admin | guest + Result string `gorm:"size:16;index" json:"result"` // success | denied | failed + ErrorMsg string `gorm:"size:512" json:"error_msg"` + DurationMs int64 `json:"duration_ms"` + CreatedAt time.Time `gorm:"index" json:"created_at"` // 操作时间 +} + +// TableName 表名。 +func (AuditLog) TableName() string { return "audit_logs" } + +// AllModels 全部需要迁移的模型。 +func AllModels() []any { + return []any{ + &FileCodes{}, + &UploadChunk{}, + &KeyValue{}, + &PresignUploadSession{}, + &StorageReservation{}, + &AuditLog{}, + } +} + +// AutoMigrate 在 Postgres 上建表/补列;服务启动时调用。 +func AutoMigrate(db *gorm.DB) error { + return db.AutoMigrate(AllModels()...) +} diff --git a/server/internal/response/response.go b/server/internal/response/response.go new file mode 100644 index 0000000..7760f8b --- /dev/null +++ b/server/internal/response/response.go @@ -0,0 +1,25 @@ +// Package response 提供统一响应封装:{"code":200,"msg":"...","data":...}。 +package response + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +// Body 统一响应体。 +type Body struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data any `json:"data,omitempty"` +} + +// OK 成功响应(code=200)。 +func OK(c *gin.Context, data any) { + c.JSON(http.StatusOK, Body{Code: 200, Msg: "ok", Data: data}) +} + +// Fail 失败响应,httpStatus 与 code 语义一致(404 过期/不存在、403 拒绝、429 限流、500 服务端错误)。 +func Fail(c *gin.Context, httpStatus int, msg string) { + c.AbortWithStatusJSON(httpStatus, Body{Code: httpStatus, Msg: msg}) +} diff --git a/server/internal/settings/manager_test.go b/server/internal/settings/manager_test.go new file mode 100644 index 0000000..77071a4 --- /dev/null +++ b/server/internal/settings/manager_test.go @@ -0,0 +1,150 @@ +// settings 包双方言测试:Manager 全流程(ensure 行、KV 读写合并、Reload、 +// UpdateKV 屏蔽内部键、SystemStart)分别在 sqlite(默认)与 postgres(FCB_TEST_PG_DSN)上执行。 +package settings_test + +import ( + "context" + "os" + "path/filepath" + "testing" + + "gorm.io/gorm" + + "filecodebox/internal/config" + "filecodebox/internal/database" + "filecodebox/internal/settings" +) + +// pgTestDSN 返回 Postgres 测试连接串;未设置 FCB_TEST_PG_DSN 时跳过。 +func pgTestDSN(t *testing.T) string { + t.Helper() + dsn := os.Getenv("FCB_TEST_PG_DSN") + if dsn == "" { + t.Skip("未设置 FCB_TEST_PG_DSN,跳过 Postgres 双方言用例") + } + return dsn +} + +// newTestManager 按方言构造库 + Manager(已完成 Migrate)。 +func newTestManager(t *testing.T, driver, dsn string) (*settings.Manager, *gorm.DB, func()) { + t.Helper() + if dsn == "" { + dsn = filepath.Join(t.TempDir(), "settings-test.db") + } + ctx := context.Background() + db, err := database.Open(ctx, database.Options{Driver: driver, DSN: dsn}) + if err != nil { + t.Fatalf("[%s] Open: %v", driver, err) + } + if err := database.Migrate(ctx, db); err != nil { + _ = database.Close(db) + t.Fatalf("[%s] Migrate: %v", driver, err) + } + // Config 仅作内存载体(驱动不回连数据库),postgres 模式给占位 DSN 以通过校验 + t.Setenv("FCB_DB_DRIVER", driver) + if driver == "postgres" { + t.Setenv("FCB_DB_DSN", dsn) + } else { + t.Setenv("FCB_DB_DSN", "") + } + cfg, err := config.New() + if err != nil { + _ = database.Close(db) + t.Fatalf("[%s] config.New: %v", driver, err) + } + mgr, err := settings.NewManager(ctx, db, cfg) + if err != nil { + _ = database.Close(db) + t.Fatalf("[%s] NewManager: %v", driver, err) + } + return mgr, db, func() { _ = database.Close(db) } +} + +// runManagerSuite 双方言共用的 Manager 行为断言。 +func runManagerSuite(t *testing.T, mgr *settings.Manager) { + t.Helper() + ctx := context.Background() + + // 1. 初始:未初始化(admin_token 空) + if mgr.IsInitialized() { + t.Fatal("初始 admin_token 为空应视为未初始化") + } + + // 2. UpdateKV 写入策略键 → Reload 后读取生效 + patch := map[string]any{ + settings.KeyBackgroundURL: "https://example.com/bg.png", + settings.KeyFooterText: "自建部署,仅供内部演示", + settings.KeyFooterBeian: "京ICP备2024000001号-1", + settings.KeyNotifyEnabled: 0, + settings.KeyMaxSaveSeconds: 86400, + "_internal_secret": "must-drop", // 下划线内部键必须被拒 + } + if err := mgr.UpdateKV(ctx, patch); err != nil { + t.Fatalf("UpdateKV 失败: %v", err) + } + if err := mgr.Reload(ctx); err != nil { + t.Fatalf("Reload 失败: %v", err) + } + cfg := mgr.Get() + if got := cfg.GetString(settings.KeyBackgroundURL); got != "https://example.com/bg.png" { + t.Fatalf("background_url 未生效: %q", got) + } + if got := cfg.GetString(settings.KeyFooterBeian); got != "京ICP备2024000001号-1" { + t.Fatalf("footer_beian 未生效: %q", got) + } + if cfg.GetBool(settings.KeyNotifyEnabled) { + t.Fatal("notify_enabled=0 应生效") + } + if got := cfg.MaxSaveSeconds(); got != 86400 { + t.Fatalf("max_save_seconds 未生效: %d", got) + } + if _, ok := cfg.Get("_internal_secret"); ok { + t.Fatal("下划线内部键不应进入运行时配置") + } + + // 3. KV 合并语义:二次 UpdateKV 不覆盖未提及键 + if err := mgr.UpdateKV(ctx, map[string]any{settings.KeyNotifyEnabled: 1}); err != nil { + t.Fatalf("二次 UpdateKV: %v", err) + } + if err := mgr.Reload(ctx); err != nil { + t.Fatalf("二次 Reload: %v", err) + } + cfg = mgr.Get() + if !cfg.GetBool(settings.KeyNotifyEnabled) { + t.Fatal("notify_enabled 二次写入应生效") + } + if got := cfg.GetString(settings.KeyFooterText); got == "" { + t.Fatal("二次写入不应清空 footer_text") + } + + // 4. SystemStart:sys_start 键写入且为毫秒时间戳 + mgr.SystemStart(ctx) + + // 5. 敏感键判定(双模式一致) + if !settings.IsSensitiveKey("admin_token") || !settings.IsSensitiveKey("jwt_secret") { + t.Fatal("admin_token/jwt_secret 应为敏感键") + } + if settings.IsSensitiveKey("footer_text") { + t.Fatal("footer_text 不应为敏感键") + } + + // 6. KV schema 表完整性:全部键可从默认值读取 + for _, e := range settings.KVSchema() { + if _, ok := cfg.Get(e.Key); !ok { + t.Fatalf("schema 键 %q 在默认配置中不存在", e.Key) + } + } +} + +func TestManagerSQLite(t *testing.T) { + mgr, _, closeFn := newTestManager(t, "sqlite", "") + defer closeFn() + runManagerSuite(t, mgr) +} + +func TestManagerPostgres(t *testing.T) { + dsn := pgTestDSN(t) + mgr, _, closeFn := newTestManager(t, "postgres", dsn) + defer closeFn() + runManagerSuite(t, mgr) +} diff --git a/server/internal/settings/password.go b/server/internal/settings/password.go new file mode 100644 index 0000000..9a1b61d --- /dev/null +++ b/server/internal/settings/password.go @@ -0,0 +1,98 @@ +// Package settings 密码哈希与校验: +// 新密码使用 bcrypt(格式 bcrypt$);同时兼容两代旧格式—— +// sha256$salt$hash(上一版)与旧版明文(迁移校验)。 +// 安全审计 M1:单轮 SHA256+盐抗 GPU 爆破不足,新哈希统一升级 bcrypt。 +// 兼容策略:VerifyPassword 支持全部三代格式;调用方可用 NeedsRehash 判定 +// 登录成功后是否需要用新算法重哈希写回(登录升级路径见 api.adminLogin)。 +package settings + +import ( + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "strconv" + "strings" + + "golang.org/x/crypto/bcrypt" +) + +// bcryptCost bcrypt 工作因子:12(2026 年桌面 CPU 单次校验约 100-250ms, +// 离线爆破成本相比单轮 SHA256 提升数个数量级)。 +const bcryptCost = 12 + +// bcryptMaxLen bcrypt 算法只取前 72 字节;超长输入统一截断, +// 避免 GenerateFromPassword/CompareHashAndPassword 对 >72 字节返回错误。 +const bcryptMaxLen = 72 + +func bcryptBytes(password string) []byte { + b := []byte(password) + if len(b) > bcryptMaxLen { + b = b[:bcryptMaxLen] + } + return b +} + +// HashPassword 生成 bcrypt$ 格式密码哈希( 为 bcrypt 原生串 +// `$2a$$`,cost 内嵌于哈希串中)。 +func HashPassword(password string) string { + sum, err := bcrypt.GenerateFromPassword(bcryptBytes(password), bcryptCost) + if err != nil { + // 截断后仅剩非法 cost 等实现级错误:确定性失败优于弱哈希回落 + panic("settings: bcrypt 哈希失败: " + err.Error()) + } + return "bcrypt$" + string(sum) +} + +// VerifyPassword 校验密码:支持 bcrypt$、sha256$salt$hash 与旧版明文三种格式。 +func VerifyPassword(password, hashed string) bool { + if hashed == "" { + return false + } + switch { + case strings.HasPrefix(hashed, "bcrypt$"): + return bcrypt.CompareHashAndPassword([]byte(hashed[len("bcrypt$"):]), bcryptBytes(password)) == nil + case strings.HasPrefix(hashed, "sha256$"): + parts := strings.Split(hashed, "$") + if len(parts) != 3 { + return false + } + salt, stored := parts[1], parts[2] + sum := sha256.Sum256([]byte(salt + password)) + return hmac.Equal([]byte(hex.EncodeToString(sum[:])), []byte(stored)) + } + // 旧版明文比较(兼容迁移) + return hmac.Equal([]byte(password), []byte(hashed)) +} + +// NeedsRehash 判断哈希是否需要升级为当前算法/成本(登录成功后判定,透明迁移)。 +// sha256 与明文一律 true;bcrypt 成本低于当前 bcryptCost 时 true。 +func NeedsRehash(hashed string) bool { + if !strings.HasPrefix(hashed, "bcrypt$") { + return true + } + // bcrypt 原生串格式:$2a$$ + parts := strings.Split(hashed[len("bcrypt$"):], "$") + if len(parts) < 4 { + return true + } + cost, err := strconv.Atoi(parts[2]) + if err != nil { + return true + } + return cost < bcryptCost +} + +// IsPasswordHashed 判断是否为受支持的哈希格式(bcrypt / sha256)。 +func IsPasswordHashed(s string) bool { + return strings.HasPrefix(s, "bcrypt$") || strings.HasPrefix(s, "sha256$") +} + +// GenerateJWTSecret 生成 64 字符十六进制随机密钥。 +func GenerateJWTSecret() string { + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + panic("settings: crypto/rand 不可用: " + err.Error()) + } + return hex.EncodeToString(b) +} diff --git a/server/internal/settings/password_test.go b/server/internal/settings/password_test.go new file mode 100644 index 0000000..2a7b122 --- /dev/null +++ b/server/internal/settings/password_test.go @@ -0,0 +1,38 @@ +package settings + +import "testing" + +func TestHashPasswordRoundTrip(t *testing.T) { + h := HashPassword("s3cret-密码") + if !IsPasswordHashed(h) { + t.Fatalf("哈希格式不对: %s", h) + } + if !VerifyPassword("s3cret-密码", h) { + t.Fatal("正确密码校验失败") + } + if VerifyPassword("wrong", h) { + t.Fatal("错误密码竟通过校验") + } + if h == HashPassword("s3cret-密码") { + t.Fatal("盐值未随机化") + } +} + +func TestVerifyLegacyPlaintext(t *testing.T) { + if !VerifyPassword("FileCodeBox2023", "FileCodeBox2023") { + t.Fatal("旧版明文兼容校验失败") + } + if VerifyPassword("nope", "FileCodeBox2023") { + t.Fatal("明文比较不应放行其他密码") + } +} + +func TestGenerateJWTSecretLength(t *testing.T) { + s := GenerateJWTSecret() + if len(s) < 32 { + t.Fatalf("密钥太短: %d", len(s)) + } + if s == GenerateJWTSecret() { + t.Fatal("密钥未随机化") + } +} diff --git a/server/internal/settings/sanitize.go b/server/internal/settings/sanitize.go new file mode 100644 index 0000000..33128ba --- /dev/null +++ b/server/internal/settings/sanitize.go @@ -0,0 +1,258 @@ +// Package settings — sanitize.go:受控 HTML 白名单净化(安全审计 L7)。 +// +// notify_content 设计上「允许 等受控 HTML」,此前由管理端任意写入并经 +// 前端 v-html 直出——管理员账号一旦被盗即可对全站访客注入脚本。 +// 本净化器只保留纯文本与 ,其余标签连同其内层内容 +// 一并丢弃(不做 HTML 转义输出,避免脚本字面量进入页面 DOM), +// 在公开配置读取与保存两处调用(双保险,覆盖历史存量数据)。 +package settings + +import ( + "strings" +) + +// dropContentTags 标签内部内容也一并丢弃的危险标签(script/style 等)。 +var dropContentTags = map[string]bool{ + "script": true, "style": true, "iframe": true, "object": true, "embed": true, + "title": true, "textarea": true, "noscript": true, "template": true, + "svg": true, "math": true, "xmp": true, "noembed": true, "noframes": true, +} + +// SanitizeInlineHTML 白名单净化内联 HTML: +// - world`, "helloworld"}, + {"img 丢弃保留文本", `ab`, "ab"}, + {"javascript href 拒绝", `x`, "x"}, + {"data href 拒绝", `x`, "x"}, + {"事件属性不透传", `y`, `y`}, + {"注释丢弃", `ab`, "ab"}, + {"未闭合标签丢弃剩余", `okfine`) + if got != "fine" { + t.Fatalf("script 内容应整体丢弃, got %q", got) + } +} diff --git a/server/internal/settings/schema.go b/server/internal/settings/schema.go new file mode 100644 index 0000000..883ffc7 --- /dev/null +++ b/server/internal/settings/schema.go @@ -0,0 +1,87 @@ +// Package settings — schema.go:v2 配置键 schema 常量与元数据表。 +// +// 键名常量的单一事实来源在 internal/config/schema.go(defaults() 需引用); +// 本文件 re-export 供 API/管理层使用,并提供「键名/类型/默认值」全量表, +// 供管理端设置页与文档生成(t4)对齐。新增键必须同步: +// 1. config/schema.go 键名与边界常量 +// 2. config/config.go defaults() 默认值 +// 3. 本文件 KVSchema() 元数据行 +// 4. schema 同步测试(config schema_test / settings schema_test) +package settings + +import "filecodebox/internal/config" + +// —— 键名 re-export(与 config 包保持同一字符串,避免魔法值散落)—— +const ( + // 需求 ① 背景图 + KeyBackground = config.KeyBackground + KeyBackgroundURL = config.KeyBackgroundURL + // 需求 ② 页脚 + KeyFooterText = config.KeyFooterText + KeyFooterBeian = config.KeyFooterBeian + // 需求 ③ 系统通知 + KeyNotifyEnabled = config.KeyNotifyEnabled + KeyNotifyTitle = config.KeyNotifyTitle + KeyNotifyContent = config.KeyNotifyContent + // 需求 ④ 保存策略与上传频率限制 + KeyMaxSaveSeconds = config.KeyMaxSaveSeconds + KeyMaxSaveCount = config.KeyMaxSaveCount + KeyExpireStyle = config.KeyExpireStyle + KeyUploadCount = config.KeyUploadCount + KeyUploadMinute = config.KeyUploadMinute + // 需求 ④⑩ 存储策略 + KeyUploadSize = config.KeyUploadSize + KeyMaxFileSize = config.KeyMaxFileSize + KeyAllowedTypes = config.KeyAllowedTypes + KeyStorageLimit = config.KeyStorageLimit + KeyOpenUpload = config.KeyOpenUpload + // v3 存储引擎 + KeyStorageEngine = config.KeyStorageEngine +) + +// —— 取值边界 re-export —— +const ( + MaxSaveSecondsMax = config.MaxSaveSecondsMax // 最长保存秒数上限(365 天) + MaxSaveCountMax = config.MaxSaveCountMax // 保存次数上限 + MaxFileSizeMax = config.MaxFileSizeMax // 单文件大小上限(10 GiB) + BackgroundURLMaxLen = config.BackgroundURLMaxLen // 背景图 URL 长度上限 + FooterTextMaxLen = config.FooterTextMaxLen // 页脚内容长度上限 + FooterBeianMaxLen = config.FooterBeianMaxLen // 备案号长度上限 + NotifyTitleMaxLen = config.NotifyTitleMaxLen // 通知标题长度上限 + NotifyContentMaxLen = config.NotifyContentMaxLen // 通知内容长度上限 +) + +// 敏感键:不允许出现在管理端 config get 下发/前端可见集合中(双模式下一致生效)。 +// v3:引擎凭据(webdav_password/s3_secret_access_key/aws_session_token)加入敏感集—— +// 管理端 get 返回掩码占位,update 时空串/掩码=不修改;公开 config 永不下发。 +var SensitiveKeys = []string{ + "admin_token", "jwt_secret", + "webdav_password", "s3_secret_access_key", "aws_session_token", +} + +// SensitiveMaskValue 敏感键掩码占位(管理端 get 展示用)。 +const SensitiveMaskValue = "******" + +// IsSensitiveKey 判断键是否为敏感键(config get 必须屏蔽)。 +func IsSensitiveKey(key string) bool { + for _, k := range SensitiveKeys { + if k == key { + return true + } + } + return false +} + +// KVSchema 返回 v2 全量配置键元数据(键名/类型/默认值/边界/说明)。 +// 默认值必须与 config defaults() 一致(schema 同步测试保证)。 +func KVSchema() []config.KVSchemaEntry { return config.KVSchema() } + +// KVSchemaByKey 以键名为索引查看 schema;未知键返回 nil。 +func KVSchemaByKey(key string) *config.KVSchemaEntry { + for i := range config.KVSchema() { + if config.KVSchema()[i].Key == key { + return &config.KVSchema()[i] + } + } + return nil +} diff --git a/server/internal/settings/settings.go b/server/internal/settings/settings.go new file mode 100644 index 0000000..58d8541 --- /dev/null +++ b/server/internal/settings/settings.go @@ -0,0 +1,172 @@ +// Package settings 提供数据库 settings KV 的运行时读写: +// env(FCB_*)提供基线,DB KV 覆盖可变项;管理端修改后立即生效。 +package settings + +import ( + "context" + "encoding/json" + "errors" + "log" + "sync" + "time" + + "gorm.io/gorm" + + "filecodebox/internal/config" + "filecodebox/internal/model" +) + +// settingsKey 数据库中的配置键(对齐参考实现)。 +const settingsKey = "settings" + +// Manager 设置管理器:线程安全,缓存 KV 覆盖到内存。 +type Manager struct { + db *gorm.DB + cfg *config.Config + + mu sync.RWMutex + secret string // jwt_secret(频繁使用,单独缓存) + initPwd string // admin_token 哈希(频繁使用,单独缓存) +} + +// NewManager 构造设置管理器并加载 DB KV。 +// ensure 默认配置行(首次启动时写入 settings 键)。 +func NewManager(ctx context.Context, db *gorm.DB, cfg *config.Config) (*Manager, error) { + m := &Manager{db: db, cfg: cfg} + if err := m.ensureSettingsRow(ctx); err != nil { + return nil, err + } + if err := m.Reload(ctx); err != nil { + return nil, err + } + return m, nil +} + +// ensureSettingsRow 首次启动时把默认安全配置写入 KV(对齐 ensure_settings_row)。 +func (m *Manager) ensureSettingsRow(ctx context.Context) error { + var row model.KeyValue + err := m.db.WithContext(ctx).Where(model.KeyValue{Key: settingsKey}).First(&row).Error + if err == nil { + return nil + } + // 双方言:必须用 errors.Is 判定(sqlite 驱动错误链与字符串消息与 postgres 不同) + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + // 不存在:写入初始配置(不含 admin_token/jwt_secret,保持未初始化状态) + initial := map[string]any{} + raw, _ := json.Marshal(initial) + row = model.KeyValue{Key: settingsKey, Value: strPtr(string(raw))} + if err := m.db.WithContext(ctx).Create(&row).Error; err != nil { + return err + } + log.Println("[settings] 系统尚未初始化,请在浏览器中打开站点并完成管理员密码设置") + return nil +} + +// Reload 从数据库加载 settings KV 并覆盖到运行时配置。 +func (m *Manager) Reload(ctx context.Context) error { + var row model.KeyValue + err := m.db.WithContext(ctx).Where(model.KeyValue{Key: settingsKey}).First(&row).Error + if err != nil { + // 行不存在时保持现有覆盖(双方言:errors.Is 判定) + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } + return err + } + kv := map[string]any{} + if row.Value != nil && *row.Value != "" { + if err := json.Unmarshal([]byte(*row.Value), &kv); err != nil { + log.Printf("[settings] settings KV 解析失败: %v", err) + } + } + // 内部键不允许通过 KV 覆盖(_ 开头) + safe := map[string]any{} + for k, v := range kv { + if len(k) > 0 && k[0] == '_' { + continue + } + safe[k] = v + } + m.mu.Lock() + m.cfg.ApplyKV(safe) + m.secret, _ = safe["jwt_secret"].(string) + m.initPwd, _ = safe["admin_token"].(string) + m.mu.Unlock() + return nil +} + +// Get 返回当前配置(只读使用;不要修改返回值)。 +func (m *Manager) Get() *config.Config { return m.cfg } + +// SecretProvider 返回 jwt_secret 读取函数(JWT 中间件用)。 +func (m *Manager) SecretProvider() func() string { + return func() string { + m.mu.RLock() + defer m.mu.RUnlock() + return m.secret + } +} + +// IsInitialized 系统是否已完成初始化(管理员密码已设置且非默认密码)。 +func (m *Manager) IsInitialized() bool { + m.mu.RLock() + defer m.mu.RUnlock() + if m.initPwd == "" { + return false + } + // 旧版默认密码视为未初始化(对齐 LEGACY_DEFAULT_ADMIN_TOKEN 检查) + return !verifyLegacyDefault(m.initPwd) +} + +// legacyDefaultToken 参考实现的旧默认管理员密码。 +const legacyDefaultToken = "FileCodeBox2023" + +// verifyLegacyDefault 检查哈希是否对应旧默认密码。 +func verifyLegacyDefault(hashed string) bool { + if hashed == "" { + return false + } + return VerifyPassword(legacyDefaultToken, hashed) +} + +// UpdateKV 合并更新 settings KV(管理端保存配置)。 +func (m *Manager) UpdateKV(ctx context.Context, patch map[string]any) error { + m.mu.Lock() + defer m.mu.Unlock() + // 读现有值 + var row model.KeyValue + err := m.db.WithContext(ctx).Where(model.KeyValue{Key: settingsKey}).First(&row).Error + kv := map[string]any{} + if err == nil && row.Value != nil { + _ = json.Unmarshal([]byte(*row.Value), &kv) + } + for k, v := range patch { + if len(k) > 0 && k[0] == '_' { + continue + } + kv[k] = v + } + raw, err := json.Marshal(kv) + if err != nil { + return err + } + if err == nil && row.ID > 0 { + row.Value = strPtr(string(raw)) + return m.db.WithContext(ctx).Model(&row).Update("value", row.Value).Error + } + row = model.KeyValue{Key: settingsKey, Value: strPtr(string(raw))} + return m.db.WithContext(ctx).Create(&row).Error +} + +// SystemStart 记录系统启动时间(对齐 sys_start 键)。 +func (m *Manager) SystemStart(ctx context.Context) { + now := time.Now().UnixMilli() + raw, _ := json.Marshal(now) + _ = m.db.WithContext(ctx).Where(model.KeyValue{Key: "sys_start"}). + Assign(model.KeyValue{Value: strPtr(string(raw))}). + FirstOrCreate(&model.KeyValue{Key: "sys_start"}).Error +} + +func strPtr(s string) *string { return &s } diff --git a/server/internal/storage/errors.go b/server/internal/storage/errors.go new file mode 100644 index 0000000..d1bf9aa --- /dev/null +++ b/server/internal/storage/errors.go @@ -0,0 +1,10 @@ +package storage + +import "errors" + +// ErrRangeNotSatisfiable 请求的字节范围超出文件大小(对应 HTTP 416)。 +// 属于增量错误定义,不改动 interface.go 的既有签名。 +var ErrRangeNotSatisfiable = errors.New("storage: 请求范围超出文件大小") + +// ErrHashMismatch 分片哈希校验失败(合并分片时检测到数据损坏)。 +var ErrHashMismatch = errors.New("storage: 分片哈希校验失败") diff --git a/server/internal/storage/factory.go b/server/internal/storage/factory.go new file mode 100644 index 0000000..8310eb9 --- /dev/null +++ b/server/internal/storage/factory.go @@ -0,0 +1,26 @@ +package storage + +import ( + "context" + "fmt" +) + +// Factory 按配置构造存储引擎。由 go-storage 提供 New* 实现后接入。 +// 这里提供注册表模式:各引擎实现注册自己的构造函数,main.go 按名称选择。 +type Factory func(ctx context.Context) (Storage, error) + +var registry = map[string]Factory{} + +// RegisterEngine 注册引擎构造函数(init 时调用,名称:local|s3|webdav)。 +func RegisterEngine(name string, f Factory) { + registry[name] = f +} + +// NewEngine 按名称构造引擎。 +func NewEngine(ctx context.Context, name string) (Storage, error) { + f, ok := registry[name] + if !ok { + return nil, fmt.Errorf("storage: 未知存储引擎 %q(仅支持 local|s3|webdav)", name) + } + return f(ctx) +} diff --git a/server/internal/storage/interface.go b/server/internal/storage/interface.go new file mode 100644 index 0000000..2939ea6 --- /dev/null +++ b/server/internal/storage/interface.go @@ -0,0 +1,111 @@ +// Package storage 定义存储引擎统一契约。 +// +// 本文件是 go-storage 并行开发的接口契约:签名一经定义不再改动。 +// 三种引擎(local/s3/webdav)都要实现该接口;工厂按 FCB_STORAGE_ENGINE 选择。 +package storage + +import ( + "context" + "errors" + "io" +) + +// 错误定义:实现方应返回这些哨兵错误(可用 %w 包装),便于 API 层映射 HTTP 状态码。 +var ( + // ErrNotFound 文件不存在(HTTP 404)。 + ErrNotFound = errors.New("storage: 文件不存在") + // ErrInvalidPath 非法路径(路径穿越等,HTTP 400)。 + ErrInvalidPath = errors.New("storage: 非法文件路径") + // ErrUnavailable 存储服务不可用(连接失败等,HTTP 503)。 + ErrUnavailable = errors.New("storage: 存储服务不可用") +) + +// FileMeta 文件元信息(大小等)。 +type FileMeta struct { + Size int64 // 字节数 + ContentType string // MIME 类型,可为空 + AcceptRanges bool // 是否支持 Range 请求 +} + +// Download 流式下载句柄。调用方负责 Close。 +type Download struct { + // ReadCloser 文件内容流(已按 Range 重定位)。 + io.ReadCloser + // Meta 文件元信息。 + Meta FileMeta + // Start 当前流的起始字节偏移(Range 请求时为 rangeStart)。 + Start int64 + // End 流的结束字节偏移(含);未知为 -1。 + End int64 + // Total 文件总大小(字节);未知为 -1。 + Total int64 +} + +// Range 字节范围(对齐 HTTP Range 语义)。 +// nil 指针表示完整文件。 +type Range struct { + Start int64 // 起始字节(含) + End int64 // 结束字节(含);-1 表示到文件末尾 +} + +// Storage 存储引擎统一接口。 +// +// 约定: +// - savePath 为存储侧相对路径(引擎内部负责安全解析,拒绝 .. 穿越); +// - 所有方法必须是并发安全的; +// - 实现方遇到不可恢复错误时返回本包哨兵错误(或用 %w 包装)。 +type Storage interface { + // SaveFile 流式保存文件:r 读取到 EOF 即完成,返回实际写入字节数。 + // 引擎必须按 256KB 级别分块读取,不得将整个文件读入内存。 + SaveFile(ctx context.Context, r io.Reader, savePath string) (int64, error) + + // DeleteFile 删除文件;文件不存在时返回 ErrNotFound 或 nil 均可接受。 + DeleteFile(ctx context.Context, savePath string) error + + // Open 以下载模式打开文件,支持 HTTP Range 请求语义: + // - rng 为 nil:返回完整文件流(Start=0,End=Total-1); + // - rng 非 nil:返回 [Start, End] 区间流。 + // 引擎应尽量透传 Range(WebDAV/S3)或按块 seek(local)。 + Open(ctx context.Context, savePath string, rng *Range) (*Download, error) + + // Stat 获取文件元信息;不存在返回 ErrNotFound。 + Stat(ctx context.Context, savePath string) (*FileMeta, error) + + // SaveChunk 保存一个分片到临时区(upload_id 隔离),返回分片字节数。 + SaveChunk(ctx context.Context, uploadID string, chunkIndex int, r io.Reader, savePath string) (int64, error) + + // MergeChunks 按索引 0..total-1 有序合并分片并落为正式文件。 + // verifyHash 为nil 时不校验;否则为分片 SHA256 校验函数(输入索引,输出期望哈希,空串表示跳过)。 + // 返回 (最终文件大小, 整个文件 SHA256)。 + MergeChunks(ctx context.Context, uploadID string, total int, verifyHash func(index int) (string, error), savePath string) (int64, string, error) + + // CleanChunks 清理分片临时区;不存在时静默成功。 + CleanChunks(ctx context.Context, uploadID string, savePath string) error + + // FileExists 检查文件是否存在。 + FileExists(ctx context.Context, savePath string) (bool, error) + + // HeadMeta 读取对象元信息与头部字节(可选能力,供直传 confirm 校验实际 + // 大小与内容;不支持时返回 ErrNotSupported)。 + // meta 允许为 nil(仅取头部);head 为对象前 headBytes 字节(不足时取实际长度)。 + HeadMeta(ctx context.Context, savePath string, headBytes int64) (*FileMeta, []byte, error) + + // PresignGetURL 生成限时直链(下载);不支持直链的引擎返回 ErrNotSupported。 + PresignGetURL(ctx context.Context, savePath string, expires int64) (string, error) + + // PresignPutURL 生成限时直传(上传)URL;不支持直传的引擎返回 ErrNotSupported。 + PresignPutURL(ctx context.Context, savePath string, expires int64) (string, error) + + // HealthCheck 引擎健康检查(启动时与 /health 使用)。 + HealthCheck(ctx context.Context) error +} + +// ErrNotSupported 当前引擎不支持该能力(如本地引擎不支持预签名)。 +var ErrNotSupported = errors.New("storage: 当前引擎不支持该操作") + +// ChunkPath 返回分片临时路径(约定统一为
/chunks//.part)。 +// 引擎可使用 ChunkDir 拼接自身路径。 +type PathBuilder interface { + // ChunkDir 分片临时目录(相对 savePath 所在目录)。 + ChunkDir(savePath, uploadID string) string +} diff --git a/server/internal/storage/local.go b/server/internal/storage/local.go new file mode 100644 index 0000000..d6f3a85 --- /dev/null +++ b/server/internal/storage/local.go @@ -0,0 +1,449 @@ +package storage + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "io" + "mime" + "os" + "path/filepath" + "strings" + "sync/atomic" +) + +// 每次读写使用的缓冲大小:256KB,对齐参考实现 SystemFileStorage.chunk_size。 +const localChunkSize = 256 * 1024 + +// LocalStorage 本地文件系统引擎。 +// +// 相比参考实现(SystemFileStorage)的改进: +// - 双重路径防护:清洗相对路径 + 根目录前缀校验 + 符号链接逃逸校验; +// - 全部落盘走「临时文件 + fsync + 原子重命名」,断电/中断不产生半截文件; +// - 下载使用 io.NewSectionReader 支持任意 Range,无需整文件读入内存。 +type LocalStorage struct { + // root 存储根目录(绝对路径)。 + root string + // rootReal 经符号链接解析后的真实根目录,用于逃逸校验。 + rootReal string +} + +// NewLocalStorage 构造本地引擎。root 为空时使用系统临时目录下的 filecodebox_storage。 +func NewLocalStorage(root string) (*LocalStorage, error) { + if strings.TrimSpace(root) == "" { + root = filepath.Join(os.TempDir(), "filecodebox_storage") + } + abs, err := filepath.Abs(root) + if err != nil { + return nil, fmt.Errorf("storage/local: 解析根目录失败: %w", err) + } + if err := os.MkdirAll(abs, 0o755); err != nil { + return nil, fmt.Errorf("storage/local: 创建根目录失败: %w", err) + } + real := abs + if resolved, err := filepath.EvalSymlinks(abs); err == nil { + real = resolved + } + return &LocalStorage{root: abs, rootReal: real}, nil +} + +func init() { + RegisterEngine("local", func(ctx context.Context) (Storage, error) { + return NewLocalStorage(engineOptions.Local.Root) + }) +} + +// withinRoot 判断路径 p 是否位于 root 内(含 root 本身)。 +func withinRoot(p, root string) bool { + p = filepath.Clean(p) + root = filepath.Clean(root) + if p == root { + return true + } + return strings.HasPrefix(p, root+string(os.PathSeparator)) +} + +// absPath 将存储侧相对路径解析为根目录内的绝对路径。 +// 任何路径穿越或符号链接逃逸都会返回 ErrInvalidPath。 +func (l *LocalStorage) absPath(savePath string) (string, error) { + cleaned, ok := SanitizePath(savePath) + if !ok { + return "", fmt.Errorf("%w: %q", ErrInvalidPath, savePath) + } + full := filepath.Join(l.root, filepath.FromSlash(cleaned)) + if !withinRoot(full, l.root) { + return "", fmt.Errorf("%w: %q", ErrInvalidPath, savePath) + } + // 符号链接逃逸校验:文件已存在时解析真实路径;不存在时校验最深已存在的父目录。 + if real, err := filepath.EvalSymlinks(full); err == nil { + if !withinRoot(real, l.rootReal) { + return "", fmt.Errorf("%w: 符号链接逃逸 %q", ErrInvalidPath, savePath) + } + } else { + dir := filepath.Dir(full) + if realDir, err := filepath.EvalSymlinks(dir); err == nil && !withinRoot(realDir, l.rootReal) { + return "", fmt.Errorf("%w: 符号链接逃逸 %q", ErrInvalidPath, savePath) + } + } + return full, nil +} + +// SaveFile 流式保存:256KB 分块读取写入临时文件,fsync 后原子重命名。 +func (l *LocalStorage) SaveFile(ctx context.Context, r io.Reader, savePath string) (int64, error) { + full, err := l.absPath(savePath) + if err != nil { + return 0, err + } + src := &countingReader{r: r} + if err := writeFileAtomic(full, src); err != nil { + return src.count(), err + } + return src.count(), nil +} + +// DeleteFile 删除文件;文件不存在时静默成功(对齐契约)。 +func (l *LocalStorage) DeleteFile(ctx context.Context, savePath string) error { + full, err := l.absPath(savePath) + if err != nil { + return err + } + if err := os.Remove(full); err != nil && !errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("storage/local: 删除失败: %w", err) + } + return nil +} + +// Open 打开文件下载流;rng 非 nil 时用 SectionReader 实现 Range 语义。 +func (l *LocalStorage) Open(ctx context.Context, savePath string, rng *Range) (*Download, error) { + full, err := l.absPath(savePath) + if err != nil { + return nil, err + } + f, err := os.Open(full) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil, ErrNotFound + } + return nil, fmt.Errorf("storage/local: 打开文件失败: %w", err) + } + info, err := f.Stat() + if err != nil { + _ = f.Close() + return nil, fmt.Errorf("storage/local: 获取文件信息失败: %w", err) + } + if info.IsDir() { + _ = f.Close() + return nil, ErrNotFound + } + size := info.Size() + start, end := int64(0), size-1 + if rng != nil { + if rng.Start < 0 || (rng.End != -1 && rng.End < rng.Start) { + _ = f.Close() + return nil, fmt.Errorf("%w: 非法 Range", ErrRangeNotSatisfiable) + } + if rng.Start >= size { + _ = f.Close() + return nil, ErrRangeNotSatisfiable + } + start = rng.Start + end = size - 1 + if rng.End != -1 && rng.End < end { + end = rng.End + } + } + section := io.NewSectionReader(f, start, end-start+1) + dl := &Download{ + ReadCloser: &fileSection{Reader: section, closer: f}, + Start: start, + End: end, + Total: size, + Meta: FileMeta{ + Size: size, + ContentType: mime.TypeByExtension(strings.ToLower(filepath.Ext(full))), + AcceptRanges: true, + }, + } + if end < 0 { // 空文件:End 语义上等于 -1(未知),Total=0 已表达大小 + dl.End = -1 + } + return dl, nil +} + +// fileSection 组合 SectionReader 与文件关闭器。 +type fileSection struct { + io.Reader + closer io.Closer +} + +func (f *fileSection) Close() error { return f.closer.Close() } + +// Stat 获取文件元信息。 +func (l *LocalStorage) Stat(ctx context.Context, savePath string) (*FileMeta, error) { + full, err := l.absPath(savePath) + if err != nil { + return nil, err + } + info, err := os.Stat(full) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil, ErrNotFound + } + return nil, fmt.Errorf("storage/local: Stat 失败: %w", err) + } + if info.IsDir() { + return nil, ErrNotFound + } + return &FileMeta{ + Size: info.Size(), + ContentType: mime.TypeByExtension(strings.ToLower(filepath.Ext(full))), + AcceptRanges: true, + }, nil +} + +// SaveChunk 保存分片到 <父目录>/chunks//.part,原子写入。 +func (l *LocalStorage) SaveChunk(ctx context.Context, uploadID string, chunkIndex int, r io.Reader, savePath string) (int64, error) { + // 先校验目标路径合法性,分片目录随合法路径派生。 + if _, err := l.absPath(savePath); err != nil { + return 0, err + } + chunkRel, ok := SanitizePath(ChunkPartPath(savePath, uploadID, chunkIndex)) + if !ok { + return 0, fmt.Errorf("%w: 分片路径非法 %q", ErrInvalidPath, savePath) + } + full := filepath.Join(l.root, filepath.FromSlash(chunkRel)) + if !withinRoot(full, l.root) { + return 0, fmt.Errorf("%w: 分片路径非法 %q", ErrInvalidPath, chunkRel) + } + src := &countingReader{r: r} + if err := writeFileAtomic(full, src); err != nil { + return src.count(), err + } + return src.count(), nil +} + +// MergeChunks 按索引 0..total-1 有序合并分片: +// - 逐分片流式拷贝到临时输出(边拷贝边计算整文件与分片 SHA256); +// - verifyHash 非 nil 时校验分片哈希(空串跳过); +// - 全部通过后 fsync + 原子重命名,并清理分片临时目录。 +func (l *LocalStorage) MergeChunks(ctx context.Context, uploadID string, total int, verifyHash func(index int) (string, error), savePath string) (int64, string, error) { + if total <= 0 { + return 0, "", fmt.Errorf("storage/local: 非法分片总数 %d", total) + } + full, err := l.absPath(savePath) + if err != nil { + return 0, "", err + } + if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil { + return 0, "", fmt.Errorf("storage/local: 创建目标目录失败: %w", err) + } + tmp, err := os.CreateTemp(filepath.Dir(full), "."+filepath.Base(full)+".merging-*") + if err != nil { + return 0, "", fmt.Errorf("storage/local: 创建临时文件失败: %w", err) + } + tmpName := tmp.Name() + defer func() { + _ = tmp.Close() + _ = os.Remove(tmpName) // 成功时已被重命名,删除静默失败 + }() + + totalHash := sha256.New() + buf := make([]byte, localChunkSize) + var size int64 + for i := 0; i < total; i++ { + if err := ctx.Err(); err != nil { + return 0, "", err + } + partRel, ok := SanitizePath(ChunkPartPath(savePath, uploadID, i)) + if !ok { + return 0, "", fmt.Errorf("%w: 分片 %d 路径非法", ErrInvalidPath, i) + } + partPath := filepath.Join(l.root, filepath.FromSlash(partRel)) + in, err := os.Open(partPath) + if err != nil { + return 0, "", fmt.Errorf("storage/local: 分片 %d 不存在: %w", i, err) + } + chunkHash := sha256.New() + n, err := io.CopyBuffer(io.MultiWriter(tmp, totalHash, chunkHash), in, buf) + _ = in.Close() + if err != nil { + return 0, "", fmt.Errorf("storage/local: 读取分片 %d 失败: %w", i, err) + } + if verifyHash != nil { + expected, err := verifyHash(i) + if err != nil { + return 0, "", err + } + if expected != "" && expected != hex.EncodeToString(chunkHash.Sum(nil)) { + return 0, "", fmt.Errorf("%w: 分片 %d 期望 %s", ErrHashMismatch, i, expected) + } + } + size += n + } + if err := tmp.Sync(); err != nil { + return 0, "", fmt.Errorf("storage/local: 落盘失败: %w", err) + } + if err := tmp.Close(); err != nil { + return 0, "", fmt.Errorf("storage/local: 关闭临时文件失败: %w", err) + } + if err := os.Rename(tmpName, full); err != nil { + return 0, "", fmt.Errorf("storage/local: 原子重命名失败: %w", err) + } + // 合并成功后清理分片临时目录(静默容错,不掩盖成功结果)。 + _ = l.CleanChunks(ctx, uploadID, savePath) + return size, hex.EncodeToString(totalHash.Sum(nil)), nil +} + +// CleanChunks 清理分片临时目录;不存在时静默成功,并尝试移除空 chunks 父目录。 +func (l *LocalStorage) CleanChunks(ctx context.Context, uploadID string, savePath string) error { + dirRel, ok := SanitizePath(chunkDirOf(savePath, uploadID)) + if !ok { + return fmt.Errorf("%w: 分片目录非法 %q", ErrInvalidPath, savePath) + } + dir := filepath.Join(l.root, filepath.FromSlash(dirRel)) + if !withinRoot(dir, l.root) { + return fmt.Errorf("%w: 分片目录非法 %q", ErrInvalidPath, dirRel) + } + if err := os.RemoveAll(dir); err != nil { + return fmt.Errorf("storage/local: 清理分片目录失败: %w", err) + } + // 父级 chunks 目录为空则一并清理(对齐参考实现)。 + chunksParent := filepath.Dir(dir) + if entries, err := os.ReadDir(chunksParent); err == nil && len(entries) == 0 { + _ = os.Remove(chunksParent) + } + return nil +} + +// chunkDirOf 返回分片目录(去掉文件名部分):<父目录>/chunks/。 +func chunkDirOf(savePath, uploadID string) string { + cd := ChunkDir(savePath, uploadID) + // ChunkDir 返回 "/chunks//",去掉末段文件名即目录。 + if idx := strings.LastIndex(cd, "/"); idx > 0 { + return cd[:idx] + } + return cd +} + +// FileExists 检查文件是否存在;非法路径按不存在处理(对齐参考实现)。 +func (l *LocalStorage) FileExists(ctx context.Context, savePath string) (bool, error) { + full, err := l.absPath(savePath) + if err != nil { + return false, nil + } + info, err := os.Stat(full) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return false, nil + } + return false, fmt.Errorf("storage/local: Stat 失败: %w", err) + } + return !info.IsDir(), nil +} + +// HeadMeta 读取文件元信息与前 n 字节(本地引擎实现)。 +func (l *LocalStorage) HeadMeta(ctx context.Context, savePath string, headBytes int64) (*FileMeta, []byte, error) { + full, err := l.absPath(savePath) + if err != nil { + return nil, nil, err + } + f, err := os.Open(full) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil, nil, ErrNotFound + } + return nil, nil, fmt.Errorf("storage/local: 打开文件失败: %w", err) + } + defer func() { _ = f.Close() }() + info, err := f.Stat() + if err != nil { + return nil, nil, fmt.Errorf("storage/local: Stat 失败: %w", err) + } + if info.IsDir() { + return nil, nil, ErrNotFound + } + head := make([]byte, headBytes) + n, _ := io.ReadFull(f, head) + return &FileMeta{ + Size: info.Size(), + ContentType: mime.TypeByExtension(strings.ToLower(filepath.Ext(full))), + AcceptRanges: true, + }, head[:n], nil +} + +// PresignGetURL 本地引擎不支持直链。 +func (l *LocalStorage) PresignGetURL(ctx context.Context, savePath string, expires int64) (string, error) { + return "", ErrNotSupported +} + +// PresignPutURL 本地引擎不支持直传。 +func (l *LocalStorage) PresignPutURL(ctx context.Context, savePath string, expires int64) (string, error) { + return "", ErrNotSupported +} + +// HealthCheck 健康检查:根目录可写(写入并删除探针文件)。 +func (l *LocalStorage) HealthCheck(ctx context.Context) error { + if err := os.MkdirAll(l.root, 0o755); err != nil { + return fmt.Errorf("%w: 本地存储根目录不可创建: %v", ErrUnavailable, err) + } + probe := filepath.Join(l.root, ".health-probe") + if err := os.WriteFile(probe, []byte("ok"), 0o644); err != nil { + return fmt.Errorf("%w: 本地存储不可写: %v", ErrUnavailable, err) + } + _ = os.Remove(probe) + return nil +} + +// writeFileAtomic 临时文件 + fsync + rename 的原子落盘。 +func writeFileAtomic(dst string, src io.Reader) error { + dir := filepath.Dir(dst) + if err := os.MkdirAll(dir, 0o755); err != nil { + return fmt.Errorf("storage/local: 创建目录失败: %w", err) + } + tmp, err := os.CreateTemp(dir, "."+filepath.Base(dst)+".tmp-*") + if err != nil { + return fmt.Errorf("storage/local: 创建临时文件失败: %w", err) + } + tmpName := tmp.Name() + cleanup := func() { _ = tmp.Close(); _ = os.Remove(tmpName) } + if _, err := io.CopyBuffer(tmp, src, make([]byte, localChunkSize)); err != nil { + cleanup() + return fmt.Errorf("storage/local: 写入失败: %w", err) + } + if err := tmp.Sync(); err != nil { + cleanup() + return fmt.Errorf("storage/local: fsync 失败: %w", err) + } + if err := tmp.Close(); err != nil { + _ = os.Remove(tmpName) + return fmt.Errorf("storage/local: 关闭临时文件失败: %w", err) + } + if err := os.Rename(tmpName, dst); err != nil { + _ = os.Remove(tmpName) + return fmt.Errorf("storage/local: 原子重命名失败: %w", err) + } + return nil +} + +// countingReader 统计累计读取字节数(并发安全)。 +type countingReader struct { + r io.Reader + n atomic.Int64 +} + +func (c *countingReader) Read(p []byte) (int, error) { + n, err := c.r.Read(p) + c.n.Add(int64(n)) + return n, err +} + +// count 返回累计字节数。 +func (c *countingReader) count() int64 { return c.n.Load() } + +// reset 归零计数(请求体重放时使用)。 +func (c *countingReader) reset() { c.n.Store(0) } + +// 接口编译期断言。 +var _ Storage = (*LocalStorage)(nil) diff --git a/server/internal/storage/local_test.go b/server/internal/storage/local_test.go new file mode 100644 index 0000000..19a22e1 --- /dev/null +++ b/server/internal/storage/local_test.go @@ -0,0 +1,346 @@ +package storage + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "io" + "os" + "path/filepath" + "strings" + "sync" + "testing" +) + +// newTestLocal 构造以临时目录为根的本地引擎。 +func newTestLocal(t *testing.T) *LocalStorage { + t.Helper() + st, err := NewLocalStorage(t.TempDir()) + if err != nil { + t.Fatalf("NewLocalStorage: %v", err) + } + return st +} + +func sha256Hex(b []byte) string { + sum := sha256.Sum256(b) + return hex.EncodeToString(sum[:]) +} + +// TestLocalSaveOpenRange 保存/Stat/完整与 Range 下载。 +func TestLocalSaveOpenRange(t *testing.T) { + st := newTestLocal(t) + ctx := context.Background() + data := []byte("hello filecodebox 本地引擎 0123456789") + + n, err := st.SaveFile(ctx, bytes.NewReader(data), "2025/08/测试文件.bin") + if err != nil { + t.Fatalf("SaveFile: %v", err) + } + if n != int64(len(data)) { + t.Fatalf("SaveFile n = %d, want %d", n, len(data)) + } + + // Stat + meta, err := st.Stat(ctx, "2025/08/测试文件.bin") + if err != nil { + t.Fatalf("Stat: %v", err) + } + if meta.Size != int64(len(data)) || !meta.AcceptRanges { + t.Fatalf("Stat = %+v", meta) + } + + // 完整下载:对齐 go-api 约定 Start=0、End=Total-1 + dl, err := st.Open(ctx, "2025/08/测试文件.bin", nil) + if err != nil { + t.Fatalf("Open: %v", err) + } + got, err := io.ReadAll(dl) + _ = dl.Close() + if err != nil { + t.Fatalf("read full: %v", err) + } + if !bytes.Equal(got, data) { + t.Fatalf("full content mismatch") + } + if dl.Start != 0 || dl.End != int64(len(data))-1 || dl.Total != int64(len(data)) { + t.Fatalf("full download offsets = %d,%d,%d", dl.Start, dl.End, dl.Total) + } + + // Range 下载 [2, 7] + dl, err = st.Open(ctx, "2025/08/测试文件.bin", &Range{Start: 2, End: 7}) + if err != nil { + t.Fatalf("Open range: %v", err) + } + got, err = io.ReadAll(dl) + _ = dl.Close() + if err != nil { + t.Fatalf("read range: %v", err) + } + if !bytes.Equal(got, data[2:8]) { + t.Fatalf("range content = %q, want %q", got, data[2:8]) + } + + // Range end 越界自动钳制 + dl, err = st.Open(ctx, "2025/08/测试文件.bin", &Range{Start: 5, End: 99999}) + if err != nil { + t.Fatalf("Open clamp range: %v", err) + } + got, _ = io.ReadAll(dl) + _ = dl.Close() + if !bytes.Equal(got, data[5:]) { + t.Fatalf("clamp range mismatch") + } + + // 起点越界 → 416 + if _, err := st.Open(ctx, "2025/08/测试文件.bin", &Range{Start: int64(len(data)) + 1, End: -1}); err == nil { + t.Fatalf("out-of-range start should fail") + } else if !strings.Contains(err.Error(), ErrRangeNotSatisfiable.Error()) { + t.Fatalf("want ErrRangeNotSatisfiable, got %v", err) + } + + // 不存在 → 404 + if _, err := st.Open(ctx, "no/such/file.bin", nil); err == nil || !strings.Contains(err.Error(), ErrNotFound.Error()) { + t.Fatalf("want ErrNotFound, got %v", err) + } +} + +// TestLocalSaveFileAtomic 原子写:目录自动创建,无临时残留。 +func TestLocalSaveFileAtomic(t *testing.T) { + ctx := context.Background() + dir := t.TempDir() + st2, _ := NewLocalStorage(dir) + + big := bytes.Repeat([]byte("abc123"), 128*1024) // 768KB,跨多个 256KB 缓冲 + if _, err := st2.SaveFile(ctx, bytes.NewReader(big), "a/b/c/big.bin"); err != nil { + t.Fatalf("SaveFile: %v", err) + } + // 无临时残留 + entries, _ := os.ReadDir(filepath.Join(dir, "a", "b", "c")) + for _, e := range entries { + if strings.HasPrefix(e.Name(), ".") { + t.Fatalf("临时文件残留: %s", e.Name()) + } + } + got, _ := os.ReadFile(filepath.Join(dir, "a", "b", "c", "big.bin")) + if !bytes.Equal(got, big) { + t.Fatalf("content mismatch") + } +} + +// TestLocalTraversal 防路径穿越(含符号链接逃逸)。 +func TestLocalTraversal(t *testing.T) { + st := newTestLocal(t) + ctx := context.Background() + + for _, p := range []string{"../escape.txt", "a/../../escape", "..", "/../x"} { + if _, err := st.SaveFile(ctx, strings.NewReader("x"), p); err == nil || + !strings.Contains(err.Error(), ErrInvalidPath.Error()) { + t.Fatalf("SaveFile(%q) 应拒绝: %v", p, err) + } + if _, err := st.Open(ctx, p, nil); err == nil || !strings.Contains(err.Error(), ErrInvalidPath.Error()) { + t.Fatalf("Open(%q) 应拒绝: %v", p, err) + } + } + + // 符号链接逃逸 + outside := filepath.Join(t.TempDir(), "outside.txt") + if err := os.WriteFile(outside, []byte("secret"), 0o644); err != nil { + t.Fatal(err) + } + link := filepath.Join(st.root, "link.txt") + if err := os.Symlink(outside, link); err != nil { + t.Skipf("symlink 不可用: %v", err) + } + if _, err := st.Open(ctx, "link.txt", nil); err == nil || + !strings.Contains(err.Error(), ErrInvalidPath.Error()) { + t.Fatalf("symlink escape 应拒绝: %v", err) + } +} + +// TestLocalChunkLifecycle 分片保存/合并(索引有序 + SHA256 校验 + 清理临时目录)。 +func TestLocalChunkLifecycle(t *testing.T) { + st := newTestLocal(t) + ctx := context.Background() + savePath := "2025/09/chunked.bin" + uploadID := "upload-abc" + + chunks := [][]byte{[]byte("AAAA"), []byte("BB"), []byte("CCCCCC")} + hashes := make([]string, len(chunks)) + var total int64 + for i, c := range chunks { + n, err := st.SaveChunk(ctx, uploadID, i, bytes.NewReader(c), savePath) + if err != nil { + t.Fatalf("SaveChunk %d: %v", i, err) + } + if n != int64(len(c)) { + t.Fatalf("SaveChunk %d n = %d", i, n) + } + hashes[i] = sha256Hex(c) + total += int64(len(c)) + } + + // 分片文件确实存在于临时目录 + for i := range chunks { + exists, err := st.FileExists(ctx, ChunkPartPath(savePath, uploadID, i)) + if err != nil || !exists { + t.Fatalf("分片 %d 应存在: %v %v", i, exists, err) + } + } + + size, fileHash, err := st.MergeChunks(ctx, uploadID, len(chunks), func(i int) (string, error) { + return hashes[i], nil + }, savePath) + if err != nil { + t.Fatalf("MergeChunks: %v", err) + } + if size != total { + t.Fatalf("merged size = %d, want %d", size, total) + } + want := sha256Hex(bytes.Join(chunks, nil)) + if fileHash != want { + t.Fatalf("file hash = %s, want %s", fileHash, want) + } + // 合并内容 = 按索引有序拼接 + got, err := os.ReadFile(filepath.Join(st.root, filepath.FromSlash(savePath))) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(got, bytes.Join(chunks, nil)) { + t.Fatalf("merged content mismatch") + } + // 合并后分片目录已清理 + exists, _ := st.FileExists(ctx, ChunkPartPath(savePath, uploadID, 0)) + if exists { + t.Fatalf("合并后分片应已清理") + } + if _, err := os.Stat(filepath.Join(st.root, "2025/09/chunks")); !os.IsNotExist(err) { + t.Fatalf("chunks 父目录应已清理: %v", err) + } +} + +// TestLocalChunkHashMismatch 哈希不匹配 → 合并失败且不留输出文件。 +func TestLocalChunkHashMismatch(t *testing.T) { + st := newTestLocal(t) + ctx := context.Background() + savePath := "mismatch.bin" + if _, err := st.SaveChunk(ctx, "uid", 0, strings.NewReader("data"), savePath); err != nil { + t.Fatal(err) + } + + _, _, err := st.MergeChunks(ctx, "uid", 1, func(i int) (string, error) { + return sha256Hex([]byte("WRONG")), nil + }, savePath) + if err == nil || !strings.Contains(err.Error(), ErrHashMismatch.Error()) { + t.Fatalf("want ErrHashMismatch, got %v", err) + } + exists, _ := st.FileExists(ctx, savePath) + if exists { + t.Fatalf("校验失败不应产出正式文件") + } +} + +// TestLocalVerifyHashAbort verifyHash 返回 error → 合并中止。 +func TestLocalVerifyHashAbort(t *testing.T) { + st := newTestLocal(t) + ctx := context.Background() + if _, err := st.SaveChunk(ctx, "uid2", 0, strings.NewReader("data"), "v.bin"); err != nil { + t.Fatal(err) + } + boom := context.Canceled + if _, _, err := st.MergeChunks(ctx, "uid2", 1, func(i int) (string, error) { + return "", boom + }, "v.bin"); err == nil { + t.Fatalf("verifyHash 错误应向上传播") + } +} + +// TestLocalDeleteExistsClean 删除/存在性/清理。 +func TestLocalDeleteExistsClean(t *testing.T) { + st := newTestLocal(t) + ctx := context.Background() + if _, err := st.SaveFile(ctx, strings.NewReader("x"), "d/f.bin"); err != nil { + t.Fatal(err) + } + if ok, _ := st.FileExists(ctx, "d/f.bin"); !ok { + t.Fatalf("文件应存在") + } + if err := st.DeleteFile(ctx, "d/f.bin"); err != nil { + t.Fatalf("DeleteFile: %v", err) + } + if ok, _ := st.FileExists(ctx, "d/f.bin"); ok { + t.Fatalf("文件应已删除") + } + // 删除不存在 → nil + if err := st.DeleteFile(ctx, "d/f.bin"); err != nil { + t.Fatalf("删除不存在应静默: %v", err) + } + // CleanChunks 幂等 + if err := st.CleanChunks(ctx, "uid-x", "y.bin"); err != nil { + t.Fatalf("CleanChunks: %v", err) + } +} + +// TestLocalPresignNotSupported 预签名 → ErrNotSupported。 +func TestLocalPresignNotSupported(t *testing.T) { + st := newTestLocal(t) + ctx := context.Background() + if _, err := st.PresignGetURL(ctx, "a.bin", 60); err == nil || !strings.Contains(err.Error(), ErrNotSupported.Error()) { + t.Fatalf("PresignGetURL want ErrNotSupported, got %v", err) + } + if _, err := st.PresignPutURL(ctx, "a.bin", 60); err == nil || !strings.Contains(err.Error(), ErrNotSupported.Error()) { + t.Fatalf("PresignPutURL want ErrNotSupported, got %v", err) + } +} + +// TestLocalHealthCheck 健康检查。 +func TestLocalHealthCheck(t *testing.T) { + st := newTestLocal(t) + if err := st.HealthCheck(context.Background()); err != nil { + t.Fatalf("HealthCheck: %v", err) + } +} + +// TestLocalConcurrent 并发安全冒烟。 +func TestLocalConcurrent(t *testing.T) { + st := newTestLocal(t) + ctx := context.Background() + var wg sync.WaitGroup + for i := 0; i < 16; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + p := "conc/" + itoa(i) + ".bin" + if _, err := st.SaveFile(ctx, strings.NewReader(strings.Repeat("x", i+1)), p); err != nil { + t.Errorf("save %d: %v", i, err) + return + } + dl, err := st.Open(ctx, p, nil) + if err != nil { + t.Errorf("open %d: %v", i, err) + return + } + _, _ = io.ReadAll(dl) + _ = dl.Close() + }(i) + } + wg.Wait() +} + +// TestLocalFactoryRegistry 工厂注册与构造。 +func TestLocalFactoryRegistry(t *testing.T) { + prev := engineOptions.Local.Root + engineOptions.Local.Root = t.TempDir() + defer func() { engineOptions.Local.Root = prev }() + st, err := NewEngine(context.Background(), "local") + if err != nil { + t.Fatalf("NewEngine(local): %v", err) + } + if err := st.HealthCheck(context.Background()); err != nil { + t.Fatalf("HealthCheck: %v", err) + } + if _, err := NewEngine(context.Background(), "unknown"); err == nil { + t.Fatalf("未知引擎应报错") + } +} diff --git a/server/internal/storage/manager.go b/server/internal/storage/manager.go new file mode 100644 index 0000000..134c42c --- /dev/null +++ b/server/internal/storage/manager.go @@ -0,0 +1,187 @@ +package storage + +import ( + "context" + "fmt" + "io" + "sync" +) + +// Manager 存储引擎管理器:实现 Storage 全接口并支持运行时热切换。 +// +// v3 需求:管理后台可设置存储类型(local|s3|webdav)与各引擎参数, +// 保存后无需重启即生效。设计要点: +// - 读写/保存类操作全部委托到"当前引擎"(原子指针,无锁热路径); +// - Switch 先构建并健康检查新引擎,成功才替换指针,失败保持原引擎; +// - EngineOf 按名字取引擎实例(带缓存),供"按文件归属引擎取回旧文件"使用; +// - 管理端修改引擎参数后调用 Invalidate 使对应实例缓存失效,下次构建生效。 +type Manager struct { + // build 构建指定引擎实例(由装配方注入:内部刷新全局 EngineOptions 后走工厂)。 + build func(name string) (Storage, error) + + mu sync.RWMutex + current Storage + curName string + cache map[string]Storage +} + +// validEngines 合法引擎名(与 FCB_STORAGE_ENGINE 枚举一致)。 +var validEngines = map[string]bool{"local": true, "s3": true, "webdav": true} + +// ValidEngine 校验引擎名是否合法。 +func ValidEngine(name string) bool { return validEngines[name] } + +// NewManager 创建管理器:current 为启动时已构建的引擎(主装配流已做过健康检查)。 +// build 注入构建函数(管理端切换/参数变更时使用,内部须串行——Manager 已加锁)。 +func NewManager(name string, current Storage, build func(name string) (Storage, error)) *Manager { + return &Manager{ + build: build, + current: current, + curName: name, + cache: map[string]Storage{name: current}, + } +} + +// —— Storage 接口委托(全部走当前引擎)—— + +// SaveFile 流式保存文件(委托当前引擎)。 +func (m *Manager) SaveFile(ctx context.Context, r io.Reader, savePath string) (int64, error) { + return m.current.SaveFile(ctx, r, savePath) +} + +// DeleteFile 删除文件(委托当前引擎)。 +func (m *Manager) DeleteFile(ctx context.Context, savePath string) error { + return m.current.DeleteFile(ctx, savePath) +} + +// Open 打开文件流(委托当前引擎;旧文件由 API 层先经 EngineOf 按归属引擎取)。 +func (m *Manager) Open(ctx context.Context, savePath string, rng *Range) (*Download, error) { + return m.current.Open(ctx, savePath, rng) +} + +// Stat 文件元信息(委托当前引擎)。 +func (m *Manager) Stat(ctx context.Context, savePath string) (*FileMeta, error) { + return m.current.Stat(ctx, savePath) +} + +// SaveChunk 保存分片(委托当前引擎)。 +func (m *Manager) SaveChunk(ctx context.Context, uploadID string, chunkIndex int, r io.Reader, savePath string) (int64, error) { + return m.current.SaveChunk(ctx, uploadID, chunkIndex, r, savePath) +} + +// MergeChunks 合并分片(委托当前引擎)。 +func (m *Manager) MergeChunks(ctx context.Context, uploadID string, total int, verifyHash func(index int) (string, error), savePath string) (int64, string, error) { + return m.current.MergeChunks(ctx, uploadID, total, verifyHash, savePath) +} + +// CleanChunks 清理分片临时区(委托当前引擎)。 +func (m *Manager) CleanChunks(ctx context.Context, uploadID string, savePath string) error { + return m.current.CleanChunks(ctx, uploadID, savePath) +} + +// FileExists 文件存在性(委托当前引擎)。 +func (m *Manager) FileExists(ctx context.Context, savePath string) (bool, error) { + return m.current.FileExists(ctx, savePath) +} + +// HeadMeta 元信息与头部字节(委托当前引擎;供直传 confirm 校验)。 +func (m *Manager) HeadMeta(ctx context.Context, savePath string, headBytes int64) (*FileMeta, []byte, error) { + return m.current.HeadMeta(ctx, savePath, headBytes) +} + +// PresignGetURL 限时直链下载(委托当前引擎)。 +func (m *Manager) PresignGetURL(ctx context.Context, savePath string, expires int64) (string, error) { + return m.current.PresignGetURL(ctx, savePath, expires) +} + +// PresignPutURL 限时直传(委托当前引擎)。 +func (m *Manager) PresignPutURL(ctx context.Context, savePath string, expires int64) (string, error) { + return m.current.PresignPutURL(ctx, savePath, expires) +} + +// HealthCheck 健康检查(委托当前引擎)。 +func (m *Manager) HealthCheck(ctx context.Context) error { + return m.current.HealthCheck(ctx) +} + +// —— 管理面:当前引擎名 / 按名取实例 / 热切换 / 缓存失效 —— + +// CurrentName 当前引擎名(管理端展示与文件归属戳用;并发安全)。 +func (m *Manager) CurrentName() string { + m.mu.RLock() + defer m.mu.RUnlock() + return m.curName +} + +// Current 当前引擎实例。 +func (m *Manager) Current() Storage { + m.mu.RLock() + defer m.mu.RUnlock() + return m.current +} + +// EngineOf 按名字取引擎实例(带缓存;用于按文件归属引擎取回旧文件)。 +// 实例不存在时现场构建(不健康检查——读旧文件尽力而为,构建失败即报错)。 +func (m *Manager) EngineOf(name string) (Storage, error) { + if !validEngines[name] { + return nil, fmt.Errorf("storage: 未知存储引擎 %q(仅支持 local|s3|webdav)", name) + } + m.mu.RLock() + if s, ok := m.cache[name]; ok { + m.mu.RUnlock() + return s, nil + } + m.mu.RUnlock() + + m.mu.Lock() + defer m.mu.Unlock() + // 双检:拿写锁期间可能已被并发构建 + if s, ok := m.cache[name]; ok { + return s, nil + } + s, err := m.build(name) + if err != nil { + return nil, err + } + m.cache[name] = s + return s, nil +} + +// Switch 热切换当前引擎:构建新实例 → 健康检查 → 成功才替换指针。 +// 任一步失败返回错误且当前引擎保持不变(管理端 503 上报)。 +func (m *Manager) Switch(name string) (Storage, error) { + if !validEngines[name] { + return nil, fmt.Errorf("storage: 未知存储引擎 %q(仅支持 local|s3|webdav)", name) + } + m.mu.Lock() + defer m.mu.Unlock() + if m.curName == name { + return m.current, nil + } + s, ok := m.cache[name] + if !ok { + var err error + s, err = m.build(name) + if err != nil { + return nil, fmt.Errorf("storage: 构建 %s 引擎失败: %w", name, err) + } + } + if err := s.HealthCheck(context.Background()); err != nil { + return nil, fmt.Errorf("storage: %s 引擎健康检查未通过: %w", name, err) + } + m.current = s + m.curName = name + m.cache[name] = s + return s, nil +} + +// Invalidate 引擎参数变更后使对应实例缓存失效(下次 EngineOf/Switch 重建生效)。 +// 当前引擎不受影响(运行中实例继续服务,直到显式 Switch)。 +func (m *Manager) Invalidate(name string) { + m.mu.Lock() + defer m.mu.Unlock() + if name == m.curName { + return // 当前引擎实例仍被热路径使用,不重建;参数生效由下一次 Switch 完成 + } + delete(m.cache, name) +} diff --git a/server/internal/storage/manager_test.go b/server/internal/storage/manager_test.go new file mode 100644 index 0000000..4a01e9f --- /dev/null +++ b/server/internal/storage/manager_test.go @@ -0,0 +1,197 @@ +package storage + +import ( + "context" + "errors" + "io" + "strings" + "sync/atomic" + "testing" +) + +// fakeEngine 可配置健康检查结果的桩引擎。 +type fakeEngine struct{ failHealth bool } + +func (f *fakeEngine) SaveFile(ctx context.Context, r io.Reader, savePath string) (int64, error) { + return 0, nil +} +func (f *fakeEngine) DeleteFile(ctx context.Context, savePath string) error { return nil } +func (f *fakeEngine) Open(ctx context.Context, savePath string, rng *Range) (*Download, error) { + return nil, ErrNotFound +} +func (f *fakeEngine) Stat(ctx context.Context, savePath string) (*FileMeta, error) { + return nil, ErrNotFound +} +func (f *fakeEngine) SaveChunk(ctx context.Context, uploadID string, chunkIndex int, r io.Reader, savePath string) (int64, error) { + return 0, nil +} +func (f *fakeEngine) MergeChunks(ctx context.Context, uploadID string, total int, verifyHash func(index int) (string, error), savePath string) (int64, string, error) { + return 0, "", nil +} +func (f *fakeEngine) CleanChunks(ctx context.Context, uploadID string, savePath string) error { + return nil +} +func (f *fakeEngine) FileExists(ctx context.Context, savePath string) (bool, error) { + return false, nil +} +func (f *fakeEngine) HeadMeta(ctx context.Context, savePath string, headBytes int64) (*FileMeta, []byte, error) { + return nil, nil, ErrNotSupported +} +func (f *fakeEngine) PresignGetURL(ctx context.Context, savePath string, expires int64) (string, error) { + return "", ErrNotSupported +} +func (f *fakeEngine) PresignPutURL(ctx context.Context, savePath string, expires int64) (string, error) { + return "", ErrNotSupported +} +func (f *fakeEngine) HealthCheck(ctx context.Context) error { + if f.failHealth { + return ErrUnavailable + } + return nil +} + +// newTestManager 构造测试用 Manager:local 健康引擎起步;s3/webdav 由计数器控制健康。 +func newTestManager(s3Fail *atomic.Bool) *Manager { + build := func(name string) (Storage, error) { + switch name { + case "local": + return &fakeEngine{}, nil + case "s3": + return &fakeEngine{failHealth: s3Fail.Load()}, nil + case "webdav": + return &fakeEngine{}, nil + } + return nil, errors.New("unknown") + } + return NewManager("local", &fakeEngine{}, build) +} + +// TestSwitchSuccessAndCurrentName 切换成功后当前引擎名与实例更新。 +func TestSwitchSuccessAndCurrentName(t *testing.T) { + var s3Fail atomic.Bool + m := newTestManager(&s3Fail) + if m.CurrentName() != "local" { + t.Fatalf("初始引擎应为 local,得到 %s", m.CurrentName()) + } + if _, err := m.Switch("s3"); err != nil { + t.Fatalf("Switch(s3) 失败: %v", err) + } + if m.CurrentName() != "s3" { + t.Fatalf("切换后引擎应为 s3,得到 %s", m.CurrentName()) + } + if _, err := m.Switch("webdav"); err != nil { + t.Fatalf("Switch(webdav) 失败: %v", err) + } + if m.CurrentName() != "webdav" { + t.Fatalf("切换后引擎应为 webdav,得到 %s", m.CurrentName()) + } +} + +// TestSwitchFailureKeepsCurrent 健康检查失败时保持原引擎(v3 核心语义)。 +func TestSwitchFailureKeepsCurrent(t *testing.T) { + var s3Fail atomic.Bool + s3Fail.Store(true) // s3 不健康 + m := newTestManager(&s3Fail) + if _, err := m.Switch("s3"); err == nil { + t.Fatal("s3 不健康时 Switch 应失败") + } + if m.CurrentName() != "local" { + t.Fatalf("切换失败后应保持 local,得到 %s", m.CurrentName()) + } + // 恢复健康后可切换成功 + s3Fail.Store(false) + if _, err := m.Switch("s3"); err != nil { + t.Fatalf("恢复健康后 Switch(s3) 应成功: %v", err) + } + if m.CurrentName() != "s3" { + t.Fatalf("恢复后引擎应为 s3,得到 %s", m.CurrentName()) + } +} + +// TestSwitchInvalidName 非法引擎名拒绝。 +func TestSwitchInvalidName(t *testing.T) { + m := newTestManager(&atomic.Bool{}) + if _, err := m.Switch("ftp"); err == nil || !strings.Contains(err.Error(), "未知存储引擎") { + t.Fatalf("非法引擎名应报未知存储引擎,得到 %v", err) + } + if !ValidEngine("local") || ValidEngine("ftp") { + t.Fatal("ValidEngine 判定错误") + } +} + +// TestEngineOfCacheAndInvalidate EngineOf 缓存命中 + Invalidate 后重建(参数生效路径)。 +func TestEngineOfCacheAndInvalidate(t *testing.T) { + var builds atomic.Int64 + build := func(name string) (Storage, error) { + builds.Add(1) + return &fakeEngine{failHealth: false}, nil + } + m := NewManager("local", &fakeEngine{}, build) + + s1, err := m.EngineOf("s3") + if err != nil { + t.Fatalf("EngineOf(s3): %v", err) + } + s2, err := m.EngineOf("s3") + if err != nil { + t.Fatalf("EngineOf(s3) second: %v", err) + } + if s1 != s2 { + t.Fatal("EngineOf 应命中缓存返回同一实例") + } + if n := builds.Load(); n != 1 { + t.Fatalf("应只构建 1 次,实际 %d", n) + } + + // Invalidate 后下次取重建新实例 + m.Invalidate("s3") + s3, err := m.EngineOf("s3") + if err != nil { + t.Fatalf("EngineOf(s3) after invalidate: %v", err) + } + if s3 == s1 { + t.Fatal("Invalidate 后应返回重建的新实例") + } + if n := builds.Load(); n != 2 { + t.Fatalf("Invalidate 后应再构建 1 次,实际累计 %d", n) + } +} + +// TestInvalidateCurrentNoop Invalidate 当前引擎不生效(热路径实例保持)。 +func TestInvalidateCurrentNoop(t *testing.T) { + m := newTestManager(&atomic.Bool{}) + cur := m.Current() + m.Invalidate("local") // 当前引擎:应为 no-op + if m.Current() != cur { + t.Fatal("Invalidate 当前引擎不应替换实例") + } +} + +// TestSwitchSameNameNoop 同名 Switch 幂等。 +func TestSwitchSameNameNoop(t *testing.T) { + m := newTestManager(&atomic.Bool{}) + s, err := m.Switch("local") + if err != nil { + t.Fatalf("Switch(local) 同名应成功: %v", err) + } + if s != m.Current() { + t.Fatal("同名 Switch 应返回当前实例") + } +} + +// TestDelegateToCurrent 保存/读取类操作委托当前引擎(切换后指向新引擎)。 +func TestDelegateToCurrent(t *testing.T) { + var s3Fail atomic.Bool + m := newTestManager(&s3Fail) + ctx := context.Background() + // local 引擎 HealthCheck 健康 + if err := m.HealthCheck(ctx); err != nil { + t.Fatalf("委托 HealthCheck(local): %v", err) + } + if _, err := m.Switch("s3"); err != nil { + t.Fatalf("Switch(s3): %v", err) + } + if err := m.HealthCheck(ctx); err != nil { + t.Fatalf("委托 HealthCheck(s3): %v", err) + } +} diff --git a/server/internal/storage/options.go b/server/internal/storage/options.go new file mode 100644 index 0000000..999e281 --- /dev/null +++ b/server/internal/storage/options.go @@ -0,0 +1,70 @@ +package storage + +// EngineOptions 引擎构造选项:由 main.go(API 层任务)从 config KV 填充。 +// 各引擎的 RegisterEngine 工厂读取本结构;零值即安全默认。 +type EngineOptions struct { + // Local 本地引擎选项。 + Local LocalOptions + // S3 S3 引擎选项。 + S3 S3Options + // WebDAV WebDAV 引擎选项。 + WebDAV WebDAVOptions +} + +// LocalOptions 本地引擎配置(对齐 local_storage_path)。 +type LocalOptions struct { + // Root 存储根目录;空则使用系统临时目录。 + Root string +} + +// S3Options S3 引擎配置(对齐 s3_* 配置键)。 +type S3Options struct { + AccessKeyID string // s3_access_key_id + SecretAccessKey string // s3_secret_access_key + SessionToken string // aws_session_token + Bucket string // s3_bucket_name + Endpoint string // s3_endpoint_url(MinIO 等;空则 AWS 默认端点) + Region string // s3_region_name,默认 auto + AddressingStyle string // s3_addressing_style: auto|path|virtual +} + +// WebDAVOptions WebDAV 引擎配置(对齐 webdav_* 配置键 + 本次优化项)。 +type WebDAVOptions struct { + // BaseURL 服务地址,如 https://dav.example.com/dav/。 + BaseURL string + // Username/Password 凭据(Basic 与 Digest 共用)。 + Username string + Password string + // RootPath 远端根目录(webdav_root_path),会自动逐级创建。 + RootPath string + // MaxRetries 5xx/网络错误最大重试次数(指数退避),0 取默认 3。 + MaxRetries int + // BaseBackoff 重试基础退避时长,0 取默认 200ms。 + BaseBackoff int64 + // Timeout 单请求超时秒数,0 取默认 30s。 + Timeout int64 + // MaxIdleConnsPerHost 连接池每主机最大空闲连接,0 取默认 16(连接复用优化)。 + MaxIdleConnsPerHost int +} + +// engineOptions 全局引擎选项(由 main.go 注入;默认零值)。 +var engineOptions EngineOptions + +// SetEngineOptions 注入引擎构造选项(在 RegisterEngine 工厂执行前调用)。 +func SetEngineOptions(opts EngineOptions) { engineOptions = opts } + +// applyDefaults 填充零值默认项。 +func (o *WebDAVOptions) applyDefaults() { + if o.MaxRetries <= 0 { + o.MaxRetries = 3 + } + if o.BaseBackoff <= 0 { + o.BaseBackoff = 200 + } + if o.Timeout <= 0 { + o.Timeout = 30 + } + if o.MaxIdleConnsPerHost <= 0 { + o.MaxIdleConnsPerHost = 16 + } +} diff --git a/server/internal/storage/path.go b/server/internal/storage/path.go new file mode 100644 index 0000000..4b683e1 --- /dev/null +++ b/server/internal/storage/path.go @@ -0,0 +1,103 @@ +package storage + +import ( + "path" + "strings" +) + +// ChunkDir 实现默认分片目录约定:<父目录>/chunks/。 +// local/s3/webdav 三引擎共用,保持分片路径一致。 +func ChunkDir(savePath, uploadID string) string { + dir := path.Dir(savePath) + name := path.Base(savePath) + // 防御:savePath 非法时仍返回明确结构,具体引擎再做安全校验 + if name == "." || name == "/" { + name = "file" + } + return path.Join(dir, "chunks", uploadID) + "/" + name +} + +// ChunkPartPath 分片对象完整路径(相对存储根)。 +func ChunkPartPath(savePath, uploadID string, index int) string { + dir := path.Dir(savePath) + return path.Join(dir, "chunks", uploadID, itoa(index)+".part") +} + +// SanitizePath 清理相对路径:统一斜杠、去首尾斜杠、拒绝 .. 穿越。 +// 返回清理后的相对路径与是否合法。 +func SanitizePath(p string) (string, bool) { + raw := strings.ReplaceAll(strings.TrimSpace(p), "\\", "/") + raw = strings.TrimPrefix(raw, "/") + if raw == "" { + return "", false + } + cleaned := path.Clean(raw) + if cleaned == ".." || strings.HasPrefix(cleaned, "../") || path.IsAbs(cleaned) { + return "", false + } + // 拒绝任何单独的 .. 段 + for _, seg := range strings.Split(cleaned, "/") { + if seg == ".." { + return "", false + } + } + return cleaned, true +} + +// SanitizeFileName 清理文件名:剥离路径、替换非法字符、限制长度。 +// 对齐参考 core/utils.py 的 sanitize_filename。 +func SanitizeFileName(name string) string { + // 剥离路径 + if idx := strings.LastIndexAny(name, "/\\"); idx >= 0 { + name = name[idx+1:] + } + var b strings.Builder + for _, r := range name { + switch { + case r < 0x20 || r == 0x7f: + b.WriteByte('_') + case strings.ContainsRune(`\*?:"<>|`, r): + b.WriteByte('_') + case r == ' ': + b.WriteByte('_') + default: + b.WriteRune(r) + } + } + cleaned := b.String() + // 压缩连续下划线 + for strings.Contains(cleaned, "__") { + cleaned = strings.ReplaceAll(cleaned, "__", "_") + } + cleaned = strings.Trim(cleaned, "._") + if cleaned == "" { + return "unnamed_file" + } + if len(cleaned) > 255 { + cleaned = cleaned[:255] + } + return cleaned +} + +// itoa 小整数转字符串。 +func itoa(n int) string { + if n == 0 { + return "0" + } + neg := n < 0 + if neg { + n = -n + } + var buf [21]byte + i := len(buf) + for n > 0 { + i-- + buf[i] = byte('0' + n%10) + n /= 10 + } + if neg { + i-- + buf[i] = '-' + } + return string(buf[i:]) +} diff --git a/server/internal/storage/path_test.go b/server/internal/storage/path_test.go new file mode 100644 index 0000000..2eb6c66 --- /dev/null +++ b/server/internal/storage/path_test.go @@ -0,0 +1,74 @@ +package storage + +import ( + "crypto/sha256" + "encoding/hex" + "io" + "testing" +) + +// TestSanitizePath 校验路径穿越防护。 +func TestSanitizePath(t *testing.T) { + cases := []struct { + in string + ok bool + out string + }{ + {"2025/08/uuid.zip", true, "2025/08/uuid.zip"}, + {"/2025/08/uuid.zip", true, "2025/08/uuid.zip"}, + {"a\\b\\c.txt", true, "a/b/c.txt"}, + {"../etc/passwd", false, ""}, + {"a/../../b", false, ""}, + {"..", false, ""}, + {"", false, ""}, + } + for _, tc := range cases { + got, ok := SanitizePath(tc.in) + if ok != tc.ok || (ok && got != tc.out) { + t.Errorf("SanitizePath(%q) = (%q, %v), want (%q, %v)", tc.in, got, ok, tc.out, tc.ok) + } + } +} + +// TestSanitizeFileName 校验文件名清理。 +func TestSanitizeFileName(t *testing.T) { + cases := []struct{ in, want string }{ + {"hello world.zip", "hello_world.zip"}, + {"/path/to/file.txt", "file.txt"}, + {"a:c?.mp4", "a_b_c_.mp4"}, // 连续下划线压缩,对齐参考 re.sub(r"_+", "_") + {"", "unnamed_file"}, + {"__..__", "unnamed_file"}, + } + for _, tc := range cases { + if got := SanitizeFileName(tc.in); got != tc.want { + t.Errorf("SanitizeFileName(%q) = %q, want %q", tc.in, got, tc.want) + } + } +} + +// TestChunkPartPath 校验分片路径约定。 +func TestChunkPartPath(t *testing.T) { + got := ChunkPartPath("2025/08/uuid.zip", "upload-1", 3) + want := "2025/08/chunks/upload-1/3.part" + if got != want { + t.Errorf("ChunkPartPath = %q, want %q", got, want) + } +} + +// TestChunkDir 校验分片目录约定。 +func TestChunkDir(t *testing.T) { + got := ChunkDir("2025/08/uuid.zip", "upload-1") + want := "2025/08/chunks/upload-1/uuid.zip" + if got != want { + t.Errorf("ChunkDir = %q, want %q", got, want) + } +} + +// TestSHA256Helper 辅助:确认 sha256 用法一致(合并校验依赖)。 +func TestSHA256Helper(t *testing.T) { + h := sha256.Sum256([]byte("abc")) + if got := hex.EncodeToString(h[:]); got != "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" { + t.Errorf("sha256(abc) = %s", got) + } + _ = io.EOF +} diff --git a/server/internal/storage/s3.go b/server/internal/storage/s3.go new file mode 100644 index 0000000..90fef61 --- /dev/null +++ b/server/internal/storage/s3.go @@ -0,0 +1,650 @@ +package storage + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "io" + "net" + "net/http" + "os" + "strconv" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/feature/s3/manager" + "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/aws/aws-sdk-go-v2/service/s3/types" + "github.com/aws/smithy-go" +) + +// S3Storage 基于 aws-sdk-go-v2 的 S3 兼容对象存储引擎(AWS / MinIO / R2 / OSS 等)。 +// +// 相比参考实现(S3FileStorage,aioboto3)的改进: +// - 单例客户端 + 自定义连接池 Transport(参考实现每次操作新建 session); +// - SaveFile 走 manager.Uploader 分片并发上传(未知长度也可流式,内存占用 ≤ partSize); +// - SaveChunk 落本地临时文件获取精确 Content-Length(参考实现整块读入内存); +// - MergeChunks 用 S3 原生 multipart 流式合并,边读边校验哈希(不落盘、不整块进内存); +// - 5xx/网络错误由 SDK 内置指数退避重试器处理(可配次数)。 +type S3Storage struct { + client *s3.Client + presigner *s3.PresignClient + uploader *manager.Uploader + bucket string +} + +// NewS3Storage 构造 S3 引擎。 +func NewS3Storage(opts S3Options) (*S3Storage, error) { + if strings.TrimSpace(opts.Bucket) == "" { + return nil, fmt.Errorf("storage/s3: 缺少 bucket 配置(s3_bucket_name)") + } + region := strings.TrimSpace(opts.Region) + if region == "" { + region = "us-east-1" + } + loadOpts := []func(*config.LoadOptions) error{ + config.WithRegion(region), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider( + opts.AccessKeyID, opts.SecretAccessKey, opts.SessionToken, + )), + // SDK 内置重试器:标准模式,指数退避 + 抖动,覆盖 5xx 与网络错误。 + config.WithRetryMaxAttempts(3), + // 兼容性:仅协议要求时才计算校验和。默认的 trailing CRC32 需要可重放流 + // 或 TLS,MinIO/R2 等自建端点通常不需要,关闭后 MergeChunks 的 + // GET→UploadPart 纯流式转发才能工作。 + config.WithRequestChecksumCalculation(aws.RequestChecksumCalculationWhenRequired), + config.WithResponseChecksumValidation(aws.ResponseChecksumValidationWhenRequired), + } + if ep := strings.TrimSpace(opts.Endpoint); ep != "" { + loadOpts = append(loadOpts, config.WithBaseEndpoint(ep)) + } + awsCfg, err := config.LoadDefaultConfig(context.Background(), loadOpts...) + if err != nil { + return nil, fmt.Errorf("storage/s3: 初始化 SDK 配置失败: %w", err) + } + client := s3.NewFromConfig(awsCfg, func(o *s3.Options) { + // 寻址风格:path 显式启用;auto 时自定义端点(自建 MinIO 等)默认 path-style。 + switch strings.ToLower(strings.TrimSpace(opts.AddressingStyle)) { + case "path": + o.UsePathStyle = true + case "virtual": + o.UsePathStyle = false + default: // auto + o.UsePathStyle = strings.TrimSpace(opts.Endpoint) != "" + } + // 连接复用:自定义 Transport 连接池。 + o.HTTPClient = newPooledHTTPClient() + }) + st := &S3Storage{ + client: client, + presigner: s3.NewPresignClient(client), + bucket: opts.Bucket, + } + st.uploader = manager.NewUploader(client, func(u *manager.Uploader) { + u.PartSize = 5 * 1024 * 1024 // 5MB,S3 multipart 最小分片 + u.Concurrency = 4 + u.LeavePartsOnError = false + }) + return st, nil +} + +func init() { + RegisterEngine("s3", func(ctx context.Context) (Storage, error) { + return NewS3Storage(engineOptions.S3) + }) +} + +// newPooledHTTPClient 供 SDK 使用的连接池化 HTTP 客户端。 +func newPooledHTTPClient() *http.Client { + return &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 10 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + MaxIdleConnsPerHost: 16, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: time.Second, + ResponseHeaderTimeout: 60 * time.Second, + }, + } +} + +// key 校验并规范化对象键(拒绝穿越,统一斜杠)。 +func (s *S3Storage) key(savePath string) (string, error) { + cleaned, ok := SanitizePath(savePath) + if !ok { + return "", fmt.Errorf("%w: %q", ErrInvalidPath, savePath) + } + return cleaned, nil +} + +// SaveFile 流式保存:manager.Uploader 按需分片并发上传,内存占用恒定。 +func (s *S3Storage) SaveFile(ctx context.Context, r io.Reader, savePath string) (int64, error) { + key, err := s.key(savePath) + if err != nil { + return 0, err + } + src := &countingReader{r: r} + _, err = s.uploader.Upload(ctx, &s3.PutObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + Body: src, + ContentType: aws.String("application/octet-stream"), + }) + if err != nil { + return src.count(), mapS3Error(err, "PutObject") + } + return src.count(), nil +} + +// DeleteFile 删除对象;S3 对不存在的键也返回成功。 +func (s *S3Storage) DeleteFile(ctx context.Context, savePath string) error { + key, err := s.key(savePath) + if err != nil { + return err + } + _, err = s.client.DeleteObject(ctx, &s3.DeleteObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + }) + return mapS3Error(err, "DeleteObject") +} + +// Open 获取下载流:Range 直接透传为 GetObject Range 头(流式,不落盘)。 +func (s *S3Storage) Open(ctx context.Context, savePath string, rng *Range) (*Download, error) { + key, err := s.key(savePath) + if err != nil { + return nil, err + } + input := &s3.GetObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + } + if rng != nil { + input.Range = aws.String(rangeHeaderValue(rng)) + } + out, err := s.client.GetObject(ctx, input) + if err != nil { + return nil, mapS3Error(err, "GetObject") + } + size := aws.ToInt64(out.ContentLength) + start, end := int64(0), size-1 + if cr := aws.ToString(out.ContentRange); cr != "" { // 服务端按 206 返回了区间 + if sr, e, total, ok := parseContentRange(cr); ok { + start, end = sr, e + if total >= 0 { + size = total + } + } + } + return &Download{ + ReadCloser: out.Body, + Start: start, + End: end, + Total: size, + Meta: FileMeta{ + Size: size, + ContentType: aws.ToString(out.ContentType), + AcceptRanges: true, + }, + }, nil +} + +// rangeHeaderValue 将 Range 结构转为 HTTP Range 头值。 +func rangeHeaderValue(rng *Range) string { + if rng.End < 0 { + return fmt.Sprintf("bytes=%d-", rng.Start) + } + return fmt.Sprintf("bytes=%d-%d", rng.Start, rng.End) +} + +// parseContentRange 解析 "bytes 0-99/1000"(total 可能为 "*")。 +func parseContentRange(v string) (start, end, total int64, ok bool) { + v = strings.TrimSpace(v) + if !strings.HasPrefix(v, "bytes ") { + return 0, 0, -1, false + } + parts := strings.SplitN(strings.TrimPrefix(v, "bytes "), "/", 2) + if len(parts) != 2 { + return 0, 0, -1, false + } + total = -1 + if parts[1] != "*" { + t, err := strconv.ParseInt(parts[1], 10, 64) + if err != nil { + return 0, 0, -1, false + } + total = t + } + se := strings.SplitN(parts[0], "-", 2) + if len(se) != 2 { + return 0, 0, -1, false + } + s0, err1 := strconv.ParseInt(se[0], 10, 64) + e0, err2 := strconv.ParseInt(se[1], 10, 64) + if err1 != nil || err2 != nil { + return 0, 0, -1, false + } + return s0, e0, total, true +} + +// Stat 获取对象元信息(HeadObject)。 +func (s *S3Storage) Stat(ctx context.Context, savePath string) (*FileMeta, error) { + key, err := s.key(savePath) + if err != nil { + return nil, err + } + out, err := s.client.HeadObject(ctx, &s3.HeadObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + }) + if err != nil { + return nil, mapS3Error(err, "HeadObject") + } + return &FileMeta{ + Size: aws.ToInt64(out.ContentLength), + ContentType: aws.ToString(out.ContentType), + AcceptRanges: true, + }, nil +} + +// SaveChunk 保存分片对象:落临时文件获取精确长度后 PutObject(可重试)。 +func (s *S3Storage) SaveChunk(ctx context.Context, uploadID string, chunkIndex int, r io.Reader, savePath string) (int64, error) { + if _, err := s.key(savePath); err != nil { + return 0, err + } + key, err := s.key(ChunkPartPath(savePath, uploadID, chunkIndex)) + if err != nil { + return 0, err + } + // 落临时文件:获得精确 Content-Length 与可重放 Body(网络失败可安全重试)。 + tmp, err := os.CreateTemp("", "fcb-s3-chunk-*") + if err != nil { + return 0, fmt.Errorf("storage/s3: 创建临时文件失败: %w", err) + } + tmpName := tmp.Name() + defer func() { _ = tmp.Close(); _ = os.Remove(tmpName) }() + size, err := io.Copy(tmp, r) + if err != nil { + return 0, fmt.Errorf("storage/s3: 缓存分片失败: %w", err) + } + if _, err := tmp.Seek(0, io.SeekStart); err != nil { + return 0, fmt.Errorf("storage/s3: 回卷分片失败: %w", err) + } + _, err = s.client.PutObject(ctx, &s3.PutObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + Body: tmp, + ContentLength: aws.Int64(size), + ContentType: aws.String("application/octet-stream"), + }) + if err != nil { + return size, mapS3Error(err, "PutObject(分片)") + } + return size, nil +} + +// S3 multipart 最小分片限制:除最后一片外每片 ≥5MB,否则 Complete 返回 EntityTooSmall。 +// 分片上传的分块大小由服务端配置保证(建议 ≥5MB)。 +const s3MinPartSize = 5 * 1024 * 1024 + +// MergeChunks 用 S3 原生 multipart 流式合并: +// 逐分片 GET → 边流边算哈希 → UploadPart(带精确 Content-Length)→ Complete。 +// 任一步失败即 Abort 并返回错误;成功后清理分片对象。 +func (s *S3Storage) MergeChunks(ctx context.Context, uploadID string, total int, verifyHash func(index int) (string, error), savePath string) (int64, string, error) { + if total <= 0 { + return 0, "", fmt.Errorf("storage/s3: 非法分片总数 %d", total) + } + key, err := s.key(savePath) + if err != nil { + return 0, "", err + } + chunkPrefix, err := s.key(chunkDirOf(savePath, uploadID)) + if err != nil { + return 0, "", err + } + + // 单分片快速路径:直接流式 PutObject,绕过 multipart 的 5MB 限制。 + if total == 1 { + return s.mergeSingle(ctx, chunkPrefix+"/0.part", key, verifyHash, 0) + } + + mpu, err := s.client.CreateMultipartUpload(ctx, &s3.CreateMultipartUploadInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + ContentType: aws.String("application/octet-stream"), + }) + if err != nil { + return 0, "", mapS3Error(err, "CreateMultipartUpload") + } + _ = aws.ToString(mpu.UploadId) // S3 侧 multipart 会话 ID(Abort 时复用 mpu.UploadId) + + size := int64(0) + totalHash := sha256.New() + parts := make([]types.CompletedPart, 0, total) + defer func() { + // 出错时取消 multipart(避免残留分片产生存储费用)。 + if len(parts) < total { + _, _ = s.client.AbortMultipartUpload(ctx, &s3.AbortMultipartUploadInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + UploadId: mpu.UploadId, + }) + } + }() + for i := 0; i < total; i++ { + if err := ctx.Err(); err != nil { + return 0, "", err + } + var expected string + if verifyHash != nil { + expected, err = verifyHash(i) + if err != nil { + return 0, "", err + } + } + getOut, err := s.client.GetObject(ctx, &s3.GetObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(fmt.Sprintf("%s/%d.part", chunkPrefix, i)), + }) + if err != nil { + return 0, "", mapS3Error(err, fmt.Sprintf("GetObject(分片 %d)", i)) + } + // 分片先流式落临时文件:计算哈希 + 获得可回卷 body(SDK 签名哈希需要 seekable 流, + // 同时为 UploadPart 失败重试保留数据)。 + chunkHash := sha256.New() + tmp, err := os.CreateTemp("", "fcb-s3-part-*") + if err != nil { + _ = getOut.Body.Close() + return 0, "", fmt.Errorf("storage/s3: 创建分片临时文件失败: %w", err) + } + partLen, err := io.Copy(io.MultiWriter(tmp, totalHash, chunkHash), getOut.Body) + _ = getOut.Body.Close() + if err != nil { + _ = tmp.Close() + _ = os.Remove(tmp.Name()) + return 0, "", fmt.Errorf("storage/s3: 读取分片 %d 失败: %w", i, err) + } + if expected != "" && expected != hex.EncodeToString(chunkHash.Sum(nil)) { + _ = tmp.Close() + _ = os.Remove(tmp.Name()) + return 0, "", fmt.Errorf("%w: 分片 %d 期望 %s", ErrHashMismatch, i, expected) + } + if _, err := tmp.Seek(0, io.SeekStart); err != nil { + _ = tmp.Close() + _ = os.Remove(tmp.Name()) + return 0, "", fmt.Errorf("storage/s3: 回卷分片 %d 失败: %w", i, err) + } + up, err := s.client.UploadPart(ctx, &s3.UploadPartInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + UploadId: mpu.UploadId, + PartNumber: aws.Int32(int32(i + 1)), + Body: tmp, + ContentLength: aws.Int64(partLen), + }) + _ = tmp.Close() + _ = os.Remove(tmp.Name()) + if err != nil { + return 0, "", mapS3Error(err, fmt.Sprintf("UploadPart(分片 %d)", i)) + } + parts = append(parts, types.CompletedPart{ + PartNumber: aws.Int32(int32(i + 1)), + ETag: up.ETag, + }) + size += partLen + } + if _, err := s.client.CompleteMultipartUpload(ctx, &s3.CompleteMultipartUploadInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + UploadId: mpu.UploadId, + MultipartUpload: &types.CompletedMultipartUpload{Parts: parts}, + }); err != nil { + return 0, "", mapS3Error(err, "CompleteMultipartUpload") + } + // 合并成功后清理分片对象(静默容错)。 + _ = s.CleanChunks(ctx, uploadID, savePath) + return size, hex.EncodeToString(totalHash.Sum(nil)), nil +} + +// mergeSingle 单分片合并快速路径:GET 分片 → 落临时文件校验 → PutObject 正式键。 +func (s *S3Storage) mergeSingle(ctx context.Context, chunkKey, dstKey string, verifyHash func(index int) (string, error), index int) (int64, string, error) { + getOut, err := s.client.GetObject(ctx, &s3.GetObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(chunkKey), + }) + if err != nil { + return 0, "", mapS3Error(err, "GetObject(分片)") + } + defer func() { _ = getOut.Body.Close() }() + tmp, err := os.CreateTemp("", "fcb-s3-merge-*") + if err != nil { + return 0, "", fmt.Errorf("storage/s3: 创建临时文件失败: %w", err) + } + tmpName := tmp.Name() + defer func() { _ = tmp.Close(); _ = os.Remove(tmpName) }() + fileHash := sha256.New() + size, err := io.Copy(io.MultiWriter(tmp, fileHash), getOut.Body) + if err != nil { + return 0, "", fmt.Errorf("storage/s3: 读取分片失败: %w", err) + } + if verifyHash != nil { + expected, err := verifyHash(index) + if err != nil { + return 0, "", err + } + if expected != "" && expected != hex.EncodeToString(fileHash.Sum(nil)) { + return 0, "", fmt.Errorf("%w: 分片 %d 期望 %s", ErrHashMismatch, index, expected) + } + } + if _, err := tmp.Seek(0, io.SeekStart); err != nil { + return 0, "", fmt.Errorf("storage/s3: 回卷临时文件失败: %w", err) + } + if _, err := s.client.PutObject(ctx, &s3.PutObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(dstKey), + Body: tmp, + ContentLength: aws.Int64(size), + ContentType: aws.String("application/octet-stream"), + }); err != nil { + return 0, "", mapS3Error(err, "PutObject(合并)") + } + return size, hex.EncodeToString(fileHash.Sum(nil)), nil +} + +// CleanChunks 列举并批量删除分片对象;前缀不存在时静默成功。 +func (s *S3Storage) CleanChunks(ctx context.Context, uploadID string, savePath string) error { + prefix, err := s.key(chunkDirOf(savePath, uploadID)) + if err != nil { + return err + } + prefix += "/" + paginator := s3.NewListObjectsV2Paginator(s.client, &s3.ListObjectsV2Input{ + Bucket: aws.String(s.bucket), + Prefix: aws.String(prefix), + }) + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return mapS3Error(err, "ListObjectsV2(分片)") + } + if len(page.Contents) == 0 { + return nil + } + objs := make([]types.ObjectIdentifier, 0, len(page.Contents)) + for _, obj := range page.Contents { + objs = append(objs, types.ObjectIdentifier{Key: obj.Key}) + } + if _, err := s.client.DeleteObjects(ctx, &s3.DeleteObjectsInput{ + Bucket: aws.String(s.bucket), + Delete: &types.Delete{Objects: objs, Quiet: aws.Bool(true)}, + }); err != nil { + return mapS3Error(err, "DeleteObjects(分片)") + } + } + return nil +} + +// FileExists HeadObject 探测存在性。 +func (s *S3Storage) FileExists(ctx context.Context, savePath string) (bool, error) { + key, err := s.key(savePath) + if err != nil { + return false, err + } + _, err = s.client.HeadObject(ctx, &s3.HeadObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + }) + if err != nil { + if isS3NotFound(err) { + return false, nil + } + return false, mapS3Error(err, "HeadObject") + } + return true, nil +} + +// PresignGetURL 生成限时下载直链。 +func (s *S3Storage) PresignGetURL(ctx context.Context, savePath string, expires int64) (string, error) { + key, err := s.key(savePath) + if err != nil { + return "", err + } + if expires <= 0 { + expires = 3600 + } + out, err := s.presigner.PresignGetObject(ctx, &s3.GetObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + }, s3.WithPresignExpires(time.Duration(expires)*time.Second)) + if err != nil { + return "", mapS3Error(err, "PresignGetObject") + } + return out.URL, nil +} + +// PresignPutURL 生成限时直传 URL。 +func (s *S3Storage) PresignPutURL(ctx context.Context, savePath string, expires int64) (string, error) { + key, err := s.key(savePath) + if err != nil { + return "", err + } + if expires <= 0 { + expires = 900 + } + out, err := s.presigner.PresignPutObject(ctx, &s3.PutObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + }, s3.WithPresignExpires(time.Duration(expires)*time.Second)) + if err != nil { + return "", mapS3Error(err, "PresignPutObject") + } + return out.URL, nil +} + +// HealthCheck 健康检查:列举 bucket(MaxKeys=1),同时校验连通性、凭据与 bucket 存在。 +func (s *S3Storage) HealthCheck(ctx context.Context) error { + maxKeys := int32(1) + _, err := s.client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{ + Bucket: aws.String(s.bucket), + MaxKeys: aws.Int32(maxKeys), + Prefix: aws.String(""), + }) + if err != nil { + return fmt.Errorf("%w: S3 健康检查失败: %v", ErrUnavailable, err) + } + return nil +} + +// ReadHead 读取对象前 n 字节(保留的便捷封装:HeadMeta 的仅头部形态)。 +func (s *S3Storage) ReadHead(ctx context.Context, savePath string, n int64) ([]byte, error) { + _, head, err := s.HeadMeta(ctx, savePath, n) + return head, err +} + +// HeadMeta 读取对象元信息与头部字节(S3 引擎实现:HeadObject + Range GET)。 +func (s *S3Storage) HeadMeta(ctx context.Context, savePath string, headBytes int64) (*FileMeta, []byte, error) { + key, err := s.key(savePath) + if err != nil { + return nil, nil, err + } + head, err := s.headBytes(ctx, s.bucket, key, headBytes) + if err != nil { + return nil, nil, err + } + meta, err := s.Stat(ctx, savePath) + if err != nil { + return nil, nil, err + } + return meta, head, nil +} + +// headBytes 通过 Range GET 读取对象前 n 字节。 +func (s *S3Storage) headBytes(ctx context.Context, bucket, key string, n int64) ([]byte, error) { + if n <= 0 { + return nil, nil + } + out, err := s.client.GetObject(ctx, &s3.GetObjectInput{ + Bucket: aws.String(bucket), + Key: aws.String(key), + Range: aws.String(fmt.Sprintf("bytes=0-%d", n-1)), + }) + if err != nil { + return nil, mapS3Error(err, "GetObject(head)") + } + defer func() { _ = out.Body.Close() }() + return io.ReadAll(io.LimitReader(out.Body, n)) +} + +// isS3NotFound 判断错误是否为对象不存在。 +func isS3NotFound(err error) bool { + var nf *types.NotFound + if errors.As(err, &nf) { + return true + } + var ae smithy.APIError + if errors.As(err, &ae) { + switch ae.ErrorCode() { + case "NotFound", "NoSuchKey": + return true + } + } + var re *awshttp.ResponseError + if errors.As(err, &re) && re.HTTPStatusCode() == http.StatusNotFound { + return true + } + return false +} + +// mapS3Error 将 SDK 错误映射为包内哨兵错误。 +func mapS3Error(err error, op string) error { + if err == nil { + return nil + } + if isS3NotFound(err) { + return fmt.Errorf("%w(%s)", ErrNotFound, op) + } + var re *awshttp.ResponseError + if errors.As(err, &re) && re.HTTPStatusCode() == http.StatusRequestedRangeNotSatisfiable { + return fmt.Errorf("%w(%s)", ErrRangeNotSatisfiable, op) + } + var ae smithy.APIError + if errors.As(err, &ae) && ae.ErrorCode() == "InvalidRange" { + return fmt.Errorf("%w(%s)", ErrRangeNotSatisfiable, op) + } + return fmt.Errorf("storage/s3: %s 失败: %w", op, err) +} + +// 接口编译期断言。 +var _ Storage = (*S3Storage)(nil) diff --git a/server/internal/storage/s3_test.go b/server/internal/storage/s3_test.go new file mode 100644 index 0000000..3686704 --- /dev/null +++ b/server/internal/storage/s3_test.go @@ -0,0 +1,495 @@ +package storage + +import ( + "bytes" + "context" + "encoding/xml" + "fmt" + "io" + "net/http" + "net/http/httptest" + "sort" + "strings" + "sync" + "testing" + "time" +) + +// ---- 最小 S3 兼容假服务(仅覆盖本引擎用到的 API)---- + +type fakeS3Upload struct { + key string + parts map[int][]byte +} + +type fakeS3 struct { + mu sync.Mutex + objects map[string][]byte + uploads map[string]*fakeS3Upload + nextID int + + putCount int + getCount int + headCount int + deleteCount int + listCount int + completeN int + + // failNextGet:让接下来 N 次 GET 返回 503(重试测试用)。 + failNextGet int +} + +func newFakeS3() *fakeS3 { + return &fakeS3{objects: map[string][]byte{}, uploads: map[string]*fakeS3Upload{}} +} + +// s3Key 从 path-style 路径剥离 bucket 前缀得到对象键。 +func s3Key(r *http.Request) (bucket, key string) { + p := strings.TrimPrefix(r.URL.Path, "/") + if i := strings.Index(p, "/"); i >= 0 { + return p[:i], p[i+1:] + } + return p, "" +} + +func s3ErrorXML(w http.ResponseWriter, status int, code, msg string) { + w.Header().Set("Content-Type", "application/xml") + w.WriteHeader(status) + _, _ = w.Write([]byte(fmt.Sprintf( + `%s%s`, code, msg))) +} + +func (f *fakeS3) ServeHTTP(w http.ResponseWriter, r *http.Request) { + f.mu.Lock() + defer f.mu.Unlock() + q := r.URL.Query() + bucket, key := s3Key(r) + + switch { + // UploadPart + case r.Method == http.MethodPut && q.Get("partNumber") != "" && q.Get("uploadId") != "": + up, ok := f.uploads[q.Get("uploadId")] + if !ok { + s3ErrorXML(w, 404, "NoSuchUpload", "upload not found") + return + } + body, _ := io.ReadAll(r.Body) + var n int + _, _ = fmt.Sscanf(q.Get("partNumber"), "%d", &n) + up.parts[n] = body + w.Header().Set("ETag", fmt.Sprintf(`"part-%d"`, n)) + w.WriteHeader(http.StatusOK) + // CreateMultipartUpload + case r.Method == http.MethodPost && q.Has("uploads"): + f.nextID++ + id := fmt.Sprintf("mpu-%d", f.nextID) + f.uploads[id] = &fakeS3Upload{key: key, parts: map[int][]byte{}} + w.Header().Set("Content-Type", "application/xml") + _, _ = w.Write([]byte(fmt.Sprintf( + `%s%s%s`, + bucket, key, id))) + // CompleteMultipartUpload + case r.Method == http.MethodPost && q.Get("uploadId") != "": + up, ok := f.uploads[q.Get("uploadId")] + if !ok { + s3ErrorXML(w, 404, "NoSuchUpload", "upload not found") + return + } + // 按 partNumber 有序拼接 + nums := make([]int, 0, len(up.parts)) + for n := range up.parts { + nums = append(nums, n) + } + sort.Ints(nums) + var merged bytes.Buffer + for _, n := range nums { + merged.Write(up.parts[n]) + } + f.objects[up.key] = merged.Bytes() + delete(f.uploads, q.Get("uploadId")) + f.completeN++ + w.Header().Set("Content-Type", "application/xml") + _, _ = w.Write([]byte(fmt.Sprintf( + `http://%s/%s/%s%s%s"merged"`, + r.Host, bucket, up.key, bucket, up.key))) + // AbortMultipartUpload + case r.Method == http.MethodDelete && q.Get("uploadId") != "": + delete(f.uploads, q.Get("uploadId")) + w.WriteHeader(http.StatusNoContent) + // DeleteObjects(批量) + case r.Method == http.MethodPost && q.Has("delete"): + var req struct { + Objects []struct { + Key string `xml:"Key"` + } `xml:"Object"` + } + _ = xml.NewDecoder(r.Body).Decode(&req) + for _, o := range req.Objects { + delete(f.objects, o.Key) + } + w.Header().Set("Content-Type", "application/xml") + _, _ = w.Write([]byte(``)) + // ListObjectsV2 + case r.Method == http.MethodGet && q.Get("list-type") == "2": + f.listCount++ + prefix := q.Get("prefix") + var body strings.Builder + body.WriteString(`` + bucket + `` + prefix + `false`) + keys := make([]string, 0, len(f.objects)) + for k := range f.objects { + if strings.HasPrefix(k, prefix) { + keys = append(keys, k) + } + } + sort.Strings(keys) + for _, k := range keys { + body.WriteString(fmt.Sprintf("%s%d", k, len(f.objects[k]))) + } + body.WriteString("") + w.Header().Set("Content-Type", "application/xml") + _, _ = w.Write([]byte(body.String())) + // PutObject + case r.Method == http.MethodPut: + f.putCount++ + body, _ := io.ReadAll(r.Body) + f.objects[key] = body + w.Header().Set("ETag", `"put"`) + w.WriteHeader(http.StatusOK) + // GetObject + case r.Method == http.MethodGet: + if f.failNextGet > 0 { + f.failNextGet-- + s3ErrorXML(w, 503, "ServiceUnavailable", "flaky") + return + } + f.getCount++ + data, ok := f.objects[key] + if !ok { + s3ErrorXML(w, 404, "NoSuchKey", "not found") + return + } + w.Header().Set("Content-Type", "application/octet-stream") + if rng := r.Header.Get("Range"); rng != "" { + start, end := int64(0), int64(len(data))-1 + if _, err := fmt.Sscanf(rng, "bytes=%d-%d", &start, &end); err != nil { + var s int64 + if _, err := fmt.Sscanf(rng, "bytes=%d-", &s); err == nil { + start, end = s, int64(len(data))-1 + } + } + if start < 0 || start >= int64(len(data)) { + s3ErrorXML(w, 416, "InvalidRange", "range not satisfiable") + return + } + if end >= int64(len(data)) { + end = int64(len(data)) - 1 + } + slice := data[start : end+1] + w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, len(data))) + w.WriteHeader(http.StatusPartialContent) + _, _ = w.Write(slice) + return + } + w.WriteHeader(http.StatusOK) + _, _ = w.Write(data) + // HeadObject + case r.Method == http.MethodHead: + f.headCount++ + data, ok := f.objects[key] + if !ok { + w.WriteHeader(http.StatusNotFound) + return + } + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(data))) + w.WriteHeader(http.StatusOK) + // DeleteObject + case r.Method == http.MethodDelete: + f.deleteCount++ + delete(f.objects, key) + w.WriteHeader(http.StatusNoContent) + default: + s3ErrorXML(w, 400, "NotImplemented", "unsupported") + } +} + +// newTestS3 构造对接假服务的 S3 引擎。 +func newTestS3(t *testing.T) (*S3Storage, *fakeS3) { + t.Helper() + f := newFakeS3() + srv := httptest.NewServer(f) + t.Cleanup(srv.Close) + st, err := NewS3Storage(S3Options{ + AccessKeyID: "test-ak", + SecretAccessKey: "test-sk", + Bucket: "test-bucket", + Endpoint: srv.URL, + Region: "us-east-1", + AddressingStyle: "path", + }) + if err != nil { + t.Fatalf("NewS3Storage: %v", err) + } + return st, f +} + +// TestS3SaveStatOpenRange 保存/元信息/完整与 Range 下载。 +func TestS3SaveStatOpenRange(t *testing.T) { + st, f := newTestS3(t) + ctx := context.Background() + data := []byte("0123456789abcdef S3 引擎测试数据") + + n, err := st.SaveFile(ctx, bytes.NewReader(data), "2025/08/s3.bin") + if err != nil { + t.Fatalf("SaveFile: %v", err) + } + if n != int64(len(data)) { + t.Fatalf("n = %d", n) + } + if got := f.objects["2025/08/s3.bin"]; !bytes.Equal(got, data) { + t.Fatalf("stored mismatch") + } + + meta, err := st.Stat(ctx, "2025/08/s3.bin") + if err != nil { + t.Fatalf("Stat: %v", err) + } + if meta.Size != int64(len(data)) || !meta.AcceptRanges { + t.Fatalf("Stat = %+v", meta) + } + + dl, err := st.Open(ctx, "2025/08/s3.bin", nil) + if err != nil { + t.Fatalf("Open: %v", err) + } + got, _ := io.ReadAll(dl) + _ = dl.Close() + if !bytes.Equal(got, data) { + t.Fatalf("full content mismatch") + } + if dl.Start != 0 || dl.End != int64(len(data))-1 || dl.Total != int64(len(data)) { + t.Fatalf("full offsets = %d,%d,%d", dl.Start, dl.End, dl.Total) + } + + dl, err = st.Open(ctx, "2025/08/s3.bin", &Range{Start: 4, End: 9}) + if err != nil { + t.Fatalf("Open range: %v", err) + } + got, _ = io.ReadAll(dl) + _ = dl.Close() + if !bytes.Equal(got, data[4:10]) { + t.Fatalf("range mismatch: %q", got) + } + if dl.Start != 4 || dl.End != 9 || dl.Total != int64(len(data)) { + t.Fatalf("range offsets = %d,%d,%d", dl.Start, dl.End, dl.Total) + } + + // 404 / 416 + if _, err := st.Open(ctx, "missing.bin", nil); err == nil || !strings.Contains(err.Error(), ErrNotFound.Error()) { + t.Fatalf("want ErrNotFound, got %v", err) + } + if _, err := st.Open(ctx, "2025/08/s3.bin", &Range{Start: int64(len(data)) + 5, End: -1}); err == nil || + !strings.Contains(err.Error(), ErrRangeNotSatisfiable.Error()) { + t.Fatalf("want ErrRangeNotSatisfiable, got %v", err) + } +} + +// TestS3DeleteExists 删除与存在性。 +func TestS3DeleteExists(t *testing.T) { + st, _ := newTestS3(t) + ctx := context.Background() + if _, err := st.SaveFile(ctx, strings.NewReader("x"), "del.bin"); err != nil { + t.Fatal(err) + } + ok, err := st.FileExists(ctx, "del.bin") + if err != nil || !ok { + t.Fatalf("exists = %v %v", ok, err) + } + if err := st.DeleteFile(ctx, "del.bin"); err != nil { + t.Fatalf("DeleteFile: %v", err) + } + ok, err = st.FileExists(ctx, "del.bin") + if err != nil || ok { + t.Fatalf("after delete exists = %v %v", ok, err) + } +} + +// TestS3ChunkMergeMulti 多分片合并:原生 multipart + 哈希校验 + 分片清理。 +func TestS3ChunkMergeMulti(t *testing.T) { + st, f := newTestS3(t) + ctx := context.Background() + savePath := "2025/09/s3-chunked.bin" + uploadID := "uid-s3" + + chunks := [][]byte{bytes.Repeat([]byte("A"), 6*1024*1024/3), []byte("BBBB"), []byte("CC")} + // 注意:multipart 除最后一片需 ≥5MB;此处只验证代码路径,真实约束由部署配置保证。 + // 为避免 EntityTooSmall,将第一片放大: + chunks[0] = bytes.Repeat([]byte("A"), 5*1024*1024) + hashes := make([]string, len(chunks)) + var total int64 + for i, c := range chunks { + n, err := st.SaveChunk(ctx, uploadID, i, bytes.NewReader(c), savePath) + if err != nil { + t.Fatalf("SaveChunk %d: %v", i, err) + } + hashes[i] = sha256Hex(c) + total += int64(len(c)) + _ = n + } + + size, fileHash, err := st.MergeChunks(ctx, uploadID, len(chunks), func(i int) (string, error) { + return hashes[i], nil + }, savePath) + if err != nil { + t.Fatalf("MergeChunks: %v", err) + } + if size != total { + t.Fatalf("size = %d want %d", size, total) + } + if fileHash != sha256Hex(bytes.Join(chunks, nil)) { + t.Fatalf("file hash mismatch") + } + merged := f.objects[savePath] + if !bytes.Equal(merged, bytes.Join(chunks, nil)) { + t.Fatalf("merged object mismatch (len=%d)", len(merged)) + } + if f.completeN != 1 { + t.Fatalf("CompleteMultipartUpload 次数 = %d", f.completeN) + } + // 分片对象已清理 + for k := range f.objects { + if strings.Contains(k, "chunks/"+uploadID) { + t.Fatalf("分片对象残留: %s", k) + } + } +} + +// TestS3ChunkMergeSingle 单分片快速路径。 +func TestS3ChunkMergeSingle(t *testing.T) { + st, f := newTestS3(t) + ctx := context.Background() + data := []byte("single-chunk") + if _, err := st.SaveChunk(ctx, "uid1", 0, bytes.NewReader(data), "one.bin"); err != nil { + t.Fatal(err) + } + size, fileHash, err := st.MergeChunks(ctx, "uid1", 1, func(i int) (string, error) { + return sha256Hex(data), nil + }, "one.bin") + if err != nil { + t.Fatalf("MergeChunks: %v", err) + } + if size != int64(len(data)) || fileHash != sha256Hex(data) { + t.Fatalf("size/hash mismatch") + } + if !bytes.Equal(f.objects["one.bin"], data) { + t.Fatalf("object mismatch") + } +} + +// TestS3CleanChunks 清理残留分片。 +func TestS3CleanChunks(t *testing.T) { + st, f := newTestS3(t) + ctx := context.Background() + savePath := "clean.bin" + for i := 0; i < 3; i++ { + if _, err := st.SaveChunk(ctx, "uidc", i, strings.NewReader("zz"), savePath); err != nil { + t.Fatal(err) + } + } + prefix := chunkDirOf(savePath, "uidc") + "/" + count := 0 + for k := range f.objects { + if strings.HasPrefix(k, prefix) { + count++ + } + } + if count != 3 { + t.Fatalf("期望 3 个分片对象,实际 %d", count) + } + if err := st.CleanChunks(ctx, "uidc", savePath); err != nil { + t.Fatalf("CleanChunks: %v", err) + } + for k := range f.objects { + if strings.HasPrefix(k, prefix) { + t.Fatalf("分片未清理: %s", k) + } + } + // 幂等:再清理一次不报错 + if err := st.CleanChunks(ctx, "uidc", savePath); err != nil { + t.Fatalf("CleanChunks idempotent: %v", err) + } +} + +// TestS3Presign 预签名 URL 生成。 +func TestS3Presign(t *testing.T) { + st, _ := newTestS3(t) + ctx := context.Background() + getURL, err := st.PresignGetURL(ctx, "presign.bin", 600) + if err != nil { + t.Fatalf("PresignGetURL: %v", err) + } + if !strings.Contains(getURL, "X-Amz-Signature") || !strings.Contains(getURL, "X-Amz-Expires=600") { + t.Fatalf("GET 直链缺少签名参数: %s", getURL) + } + putURL, err := st.PresignPutURL(ctx, "presign.bin", 300) + if err != nil { + t.Fatalf("PresignPutURL: %v", err) + } + if !strings.Contains(putURL, "X-Amz-Signature") { + t.Fatalf("PUT 直链缺少签名参数: %s", putURL) + } +} + +// TestS3HealthCheck 健康检查(ListObjectsV2)。 +func TestS3HealthCheck(t *testing.T) { + st, _ := newTestS3(t) + if err := st.HealthCheck(context.Background()); err != nil { + t.Fatalf("HealthCheck: %v", err) + } +} + +// TestS3RetryOn503 SDK 内置重试器:503 后成功。 +func TestS3RetryOn503(t *testing.T) { + st, f := newTestS3(t) + ctx := context.Background() + data := []byte("retry-me") + if _, err := st.SaveFile(ctx, bytes.NewReader(data), "retry.bin"); err != nil { + t.Fatal(err) + } + f.mu.Lock() + f.failNextGet = 1 + f.mu.Unlock() + dl, err := st.Open(ctx, "retry.bin", nil) + if err != nil { + t.Fatalf("503 后应重试成功: %v", err) + } + got, _ := io.ReadAll(dl) + _ = dl.Close() + if !bytes.Equal(got, data) { + t.Fatalf("content mismatch") + } +} + +// TestS3FactoryRegistry 工厂构造。 +func TestS3FactoryRegistry(t *testing.T) { + f := newFakeS3() + srv := httptest.NewServer(f) + defer srv.Close() + prev := engineOptions.S3 + engineOptions.S3 = S3Options{ + AccessKeyID: "ak", SecretAccessKey: "sk", Bucket: "b", + Endpoint: srv.URL, Region: "us-east-1", AddressingStyle: "path", + } + defer func() { engineOptions.S3 = prev }() + st, err := NewEngine(context.Background(), "s3") + if err != nil { + t.Fatalf("NewEngine(s3): %v", err) + } + if err := st.HealthCheck(context.Background()); err != nil { + t.Fatalf("HealthCheck: %v", err) + } + if _, err := st.PresignGetURL(context.Background(), "x.bin", 60); err != nil { + t.Fatalf("Presign: %v", err) + } + _ = time.Now +} diff --git a/server/internal/storage/webdav.go b/server/internal/storage/webdav.go new file mode 100644 index 0000000..7ff8dc9 --- /dev/null +++ b/server/internal/storage/webdav.go @@ -0,0 +1,889 @@ +package storage + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/xml" + "errors" + "fmt" + "io" + "math/rand" + "net" + "net/http" + "net/url" + "os" + "path" + "strconv" + "strings" + "sync" + "time" +) + +// WebDAVStorage 基于 net/http 的 WebDAV 引擎(本次重写的重点优化对象)。 +// +// 相比参考实现(WebDAVFileStorage,aiohttp)的改进: +// - 单例 http.Client + 连接池化 Transport(参考实现每个操作新建 ClientSession,无复用); +// - Basic 与 Digest(RFC 2617,qop=auth,MD5/SHA-256)双认证自动协商(参考实现仅 Basic); +// - GET 下载透传 Range 头(参考实现全量 GET,无法断点/分段); +// - 5xx/429/网络错误指数退避重试,可配次数(参考实现无重试); +// - 下载经 io.Pipe 流式转发,全程不落盘; +// - 目录存在性内存缓存,按需逐级 MKCOL,避免每次保存都发 PROPFIND; +// - 非流式操作带可配超时;流式传输由调用方 ctx 管控(可取消)。 +type WebDAVStorage struct { + base *url.URL // 服务基址(含可能的路径前缀),以 / 结尾 + root string // 远端根目录(webdav_root_path) + username string + password string + client *http.Client + transport *http.Transport + auth *authState + + maxRetries int // 5xx/网络错误最大重试次数 + baseBackoff time.Duration // 退避基数 + opTimeout time.Duration // 非流式操作超时 + + dirMu sync.RWMutex + knownDirs map[string]struct{} // 已确认存在的远端目录(含根前缀) + spacesPool sync.Pool // 256KB 复用缓冲 +} + +// NewWebDAVStorage 构造 WebDAV 引擎。 +func NewWebDAVStorage(opts WebDAVOptions) (*WebDAVStorage, error) { + opts.applyDefaults() + raw := strings.TrimSpace(opts.BaseURL) + if raw == "" { + return nil, fmt.Errorf("storage/webdav: 缺少 webdav_url 配置") + } + if !strings.Contains(raw, "://") { + raw = "http://" + raw + } + u, err := url.Parse(raw) + if err != nil { + return nil, fmt.Errorf("storage/webdav: webdav_url 非法: %w", err) + } + if u.Scheme != "http" && u.Scheme != "https" { + return nil, fmt.Errorf("storage/webdav: webdav_url 仅支持 http/https,收到 %q", u.Scheme) + } + if !strings.HasSuffix(u.Path, "/") { + u.Path += "/" + } + root := strings.Trim(opts.RootPath, "/") + if root == "" { + root = "filebox_storage" + } + root = strings.ReplaceAll(root, "\\", "/") + transport := newPooledTransport(opts.MaxIdleConnsPerHost) + return &WebDAVStorage{ + base: u, + root: root, + username: opts.Username, + password: opts.Password, + client: &http.Client{Transport: transport}, + transport: transport, + auth: newAuthState(opts.Username, opts.Password), + maxRetries: opts.MaxRetries, + baseBackoff: time.Duration(opts.BaseBackoff) * time.Millisecond, + opTimeout: time.Duration(opts.Timeout) * time.Second, + knownDirs: map[string]struct{}{}, + spacesPool: sync.Pool{New: func() any { + b := make([]byte, localChunkSize) + return &b + }}, + }, nil +} + +func init() { + RegisterEngine("webdav", func(ctx context.Context) (Storage, error) { + return NewWebDAVStorage(engineOptions.WebDAV) + }) +} + +// newPooledTransport 连接池化 Transport:Keep-Alive 连接复用是 WebDAV 优化的核心。 +func newPooledTransport(maxIdlePerHost int) *http.Transport { + return &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 10 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + MaxIdleConnsPerHost: maxIdlePerHost, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: time.Second, + ResponseHeaderTimeout: 60 * time.Second, + } +} + +// requestOpts 单次 WebDAV 请求参数。 +type requestOpts struct { + // body 请求体工厂:每次尝试调用一次(重试时重新获取,可重放)。 + body func() (io.Reader, int64, error) + // retryBody 请求体是否可重放(seekable);false 时 PUT 类请求失败不重试。 + retryBody bool + // headers 附加请求头。 + headers map[string]string + // streaming 流式传输(GET/PUT 大 body):不套 opTimeout,由调用方 ctx 管控。 + streaming bool +} + +// do 执行一次 WebDAV 请求:认证自动协商 + 指数退避重试。 +// 返回的响应由调用方负责关闭(drainClose / readErrorBody)。 +// +// 重要:非流式操作的可配超时通过 ctx 实现,cancel 不随 do() 返回而调用, +// 而是挂在 davResponse 上、待响应体读完后再触发——否则取消会提前杀掉 +// Keep-Alive 连接,破坏连接复用。 +func (w *WebDAVStorage) do(ctx context.Context, method, rawURL string, opts requestOpts) (*davResponse, error) { + // 非流式操作套可配超时(流式由调用方 ctx 管控)。 + var cancel context.CancelFunc + if !opts.streaming { + if _, hasDeadline := ctx.Deadline(); !hasDeadline { + ctx, cancel = context.WithTimeout(ctx, w.opTimeout) + } + } + fail := func(err error) (*davResponse, error) { + if cancel != nil { + cancel() + } + return nil, err + } + // 幂等方法或可重放 body 才允许整体重试。 + idempotent := method == http.MethodGet || method == http.MethodHead || + method == "PROPFIND" || method == "MKCOL" || method == http.MethodDelete || + method == http.MethodOptions + retryable := idempotent || opts.retryBody + + const maxAuthRetries = 2 + budget := w.maxRetries + maxAuthRetries // 认证挑战重试不消耗退避预算 + authRetries := 0 + for attempt := 0; attempt < budget; attempt++ { + var body io.Reader + var length int64 = -1 + if opts.body != nil { + var err error + body, length, err = opts.body() + if err != nil { + return fail(fmt.Errorf("%w: 构造请求体失败: %v", ErrUnavailable, err)) + } + } + req, err := http.NewRequestWithContext(ctx, method, rawURL, body) + if err != nil { + return fail(fmt.Errorf("%w: 构造请求失败: %v", ErrInvalidPath, err)) + } + if length >= 0 { + req.ContentLength = length + } + for k, v := range opts.headers { + req.Header.Set(k, v) + } + w.auth.apply(req) + resp, err := w.client.Do(req) + if err != nil { + if ctx.Err() != nil { // 调用方取消/超时优先 + return fail(ctx.Err()) + } + if retryable && attempt+1 < budget { + if sleepErr := w.backoff(ctx, attempt, 0); sleepErr != nil { + return fail(sleepErr) + } + continue + } + return fail(fmt.Errorf("%w: %s %s: %v", ErrUnavailable, method, rawURL, err)) + } + // 401 认证挑战:切换 Basic/Digest 后立即重试(不退避、不额外计数)。 + if resp.StatusCode == http.StatusUnauthorized && authRetries < maxAuthRetries { + challenge := resp.Header.Get("WWW-Authenticate") + drainClose(&davResponse{Response: resp}) + if challenge != "" && w.auth.challenge(challenge) { + authRetries++ + continue + } + return fail(fmt.Errorf("%w: WebDAV 认证失败(401,%s)", ErrUnavailable, rawURL)) + } + // 5xx/429/408:幂等或可重放 body 时指数退避重试。 + if retryable && isRetryStatus(resp.StatusCode) && attempt+1 < budget { + retryAfter := retryAfterSeconds(resp.Header.Get("Retry-After")) + drainClose(&davResponse{Response: resp}) + if sleepErr := w.backoff(ctx, attempt, retryAfter); sleepErr != nil { + return fail(sleepErr) + } + continue + } + return &davResponse{Response: resp, cancel: cancel}, nil + } + return fail(fmt.Errorf("%w: WebDAV 重试耗尽(%s %s)", ErrUnavailable, method, rawURL)) +} + +// davResponse WebDAV 响应 + 关联的超时取消函数。 +// 非流式操作读完响应体后必须经 drainClose/readErrorBody 释放(触发 cancel)。 +type davResponse struct { + *http.Response + cancel context.CancelFunc +} + +// isRetryStatus 判断状态码是否值得重试。 +func isRetryStatus(code int) bool { + switch code { + case http.StatusRequestTimeout, http.StatusTooManyRequests, + http.StatusInternalServerError, http.StatusBadGateway, + http.StatusServiceUnavailable, http.StatusGatewayTimeout: + return true + } + return false +} + +// retryAfterSeconds 解析 Retry-After(秒);非法或负值返回 0。 +func retryAfterSeconds(v string) time.Duration { + if v == "" { + return 0 + } + n, err := strconv.Atoi(strings.TrimSpace(v)) + if err != nil || n <= 0 { + return 0 + } + if n > 5 { + n = 5 // 上限 5s,避免异常服务端拖死请求 + } + return time.Duration(n) * time.Second +} + +// backoff 指数退避:base * 2^attempt,封顶 2s,带 ±20% 抖动;retryAfter 优先。 +func (w *WebDAVStorage) backoff(ctx context.Context, attempt int, retryAfter time.Duration) error { + d := retryAfter + if d <= 0 { + d = w.baseBackoff << attempt + if d > 2*time.Second { + d = 2 * time.Second + } + // ±20% 抖动 + jitter := time.Duration(int64(d) / 5) + if jitter > 0 { + d -= time.Duration(rand.Int63n(int64(jitter))) + } + } + if d <= 0 { + d = time.Millisecond + } + timer := time.NewTimer(d) + defer timer.Stop() + select { + case <-ctx.Done(): + return ctx.Err() + case <-timer.C: + return nil + } +} + +// drainClose 读取少量残余并关闭响应体,保证连接可复用;随后触发超时清理。 +func drainClose(resp *davResponse) { + if resp == nil || resp.Body == nil { + return + } + _, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 8<<10)) + _ = resp.Body.Close() + if resp.cancel != nil { + resp.cancel() + } +} + +// joinRemote 校验 savePath 并拼接远端完整路径(含根目录前缀)。 +func (w *WebDAVStorage) joinRemote(savePath string) (string, error) { + cleaned, ok := SanitizePath(savePath) + if !ok { + return "", fmt.Errorf("%w: %q", ErrInvalidPath, savePath) + } + return path.Join(w.root, cleaned), nil +} + +// urlFor 将远端路径转为完整 URL(URL.String 自动按段转义)。 +func (w *WebDAVStorage) urlFor(remotePath string) string { + u := *w.base + p := strings.TrimSuffix(u.Path, "/") + remotePath = strings.Trim(remotePath, "/") + if remotePath != "" && remotePath != "." { + p += "/" + remotePath + } + u.Path = p + return u.String() +} + +// propfindBody PROPFIND 请求体:只取需要的属性。 +const propfindBody = `` + + `` + + `` + + `` + +// davMultistatus 207 Multi-Status XML 解析结构(标签名与命名空间无关匹配)。 +type davMultistatus struct { + Responses []struct { + Href string `xml:"href"` + Propstat []struct { + Status string `xml:"status"` + Prop struct { + ContentLength int64 `xml:"getcontentlength"` + ContentType string `xml:"getcontenttype"` + ResourceType struct { + Collection *struct{} `xml:"collection"` + } `xml:"resourcetype"` + } `xml:"prop"` + } `xml:"propstat"` + } `xml:"response"` +} + +// firstProp 取第一个 HTTP 2xx 状态的属性块。 +func (m *davMultistatus) firstProp() (length int64, ctype string, isDir bool, ok bool) { + for _, r := range m.Responses { + for _, ps := range r.Propstat { + if !strings.Contains(ps.Status, " 200 ") { + continue + } + return ps.Prop.ContentLength, ps.Prop.ContentType, ps.Prop.ResourceType.Collection != nil, true + } + } + return 0, "", false, false +} + +// propfind 执行 PROPFIND 并解析 207 响应;404 时返回 (nil, nil)。 +func (w *WebDAVStorage) propfind(ctx context.Context, rawURL string, depth string) (*davMultistatus, error) { + resp, err := w.do(ctx, "PROPFIND", rawURL, requestOpts{ + body: func() (io.Reader, int64, error) { + return strings.NewReader(propfindBody), int64(len(propfindBody)), nil + }, + headers: map[string]string{"Depth": depth, "Content-Type": "application/xml"}, + }) + if err != nil { + return nil, err + } + defer drainClose(resp) + switch resp.StatusCode { + case http.StatusMultiStatus, http.StatusOK: + body, err := io.ReadAll(io.LimitReader(resp.Body, 4<<20)) + if err != nil { + return nil, fmt.Errorf("%w: PROPFIND 读取失败: %v", ErrUnavailable, err) + } + var ms davMultistatus + if err := xml.Unmarshal(body, &ms); err != nil { + return nil, fmt.Errorf("%w: PROPFIND XML 解析失败: %v", ErrUnavailable, err) + } + return &ms, nil + case http.StatusNotFound: + return nil, nil + default: + return nil, fmt.Errorf("%w: PROPFIND %s → %d", ErrUnavailable, rawURL, resp.StatusCode) + } +} + +// remoteExists PROPFIND 探测远端路径存在性。 +func (w *WebDAVStorage) remoteExists(ctx context.Context, remotePath string) (bool, error) { + ms, err := w.propfind(ctx, w.urlFor(remotePath), "0") + if err != nil { + return false, err + } + return ms != nil, nil +} + +// markDir 记录已确认存在的目录(避免重复 PROPFIND/MKCOL 往返)。 +func (w *WebDAVStorage) markDir(remotePath string) { + w.dirMu.Lock() + defer w.dirMu.Unlock() + w.knownDirs[remotePath] = struct{}{} +} + +// unmarkDir 目录被删除时移除缓存。 +func (w *WebDAVStorage) unmarkDir(remotePath string) { + w.dirMu.Lock() + defer w.dirMu.Unlock() + delete(w.knownDirs, remotePath) +} + +// isMarkedDir 查询目录缓存。 +func (w *WebDAVStorage) isMarkedDir(remotePath string) bool { + w.dirMu.RLock() + defer w.dirMu.RUnlock() + _, ok := w.knownDirs[remotePath] + return ok +} + +// ensureDirs 按需逐级创建远端目录(含根前缀;MKCOL 级联,成功后写缓存)。 +func (w *WebDAVStorage) ensureDirs(ctx context.Context, remotePath string) error { + segments := splitRemoteSegments(remotePath) + cur := "" + for _, seg := range segments { + cur = path.Join(cur, seg) + if w.isMarkedDir(cur) { + continue + } + exists, err := w.remoteExists(ctx, cur) + if err == nil && exists { + w.markDir(cur) + continue + } + if err != nil && !errors.Is(err, ErrNotFound) { + return err + } + resp, err := w.do(ctx, "MKCOL", w.urlFor(cur), requestOpts{}) + if err != nil { + return err + } + status := resp.StatusCode + drainClose(resp) + // 201 创建成功;405 已存在;其余视为失败(409 通常因父目录缺失,理论上不会出现)。 + if status == http.StatusCreated || status == http.StatusOK || + status == http.StatusNoContent || status == http.StatusMethodNotAllowed { + w.markDir(cur) + continue + } + return fmt.Errorf("%w: MKCOL %s → %d", ErrUnavailable, cur, status) + } + return nil +} + +// splitRemoteSegments 拆分远端路径段。 +func splitRemoteSegments(p string) []string { + p = strings.Trim(strings.ReplaceAll(p, "\\", "/"), "/") + if p == "" { + return nil + } + return strings.Split(p, "/") +} + +// deleteEmptyParents 删除空父目录(含根前缀,但不删根目录本身);尽力而为。 +func (w *WebDAVStorage) deleteEmptyParents(ctx context.Context, remotePath string) { + dir := path.Dir(remotePath) + for dir != "" && dir != "." && dir != w.root && strings.HasPrefix(dir+"/", w.root+"/") { + ms, err := w.propfind(ctx, w.urlFor(dir), "1") + if err != nil || ms == nil { + return + } + if len(ms.Responses) > 1 { // 非空(自身 + 子项) + return + } + resp, err := w.do(ctx, http.MethodDelete, w.urlFor(dir), requestOpts{}) + if err != nil { + return + } + ok := resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNoContent + drainClose(resp) + if !ok { + return + } + w.unmarkDir(dir) + dir = path.Dir(dir) + } +} + +// putFile PUT 上传:body 工厂每次尝试返回可重放的读取器。 +func (w *WebDAVStorage) putFile(ctx context.Context, rawURL string, body func() (io.Reader, int64, error), retryBody bool) (*davResponse, error) { + return w.do(ctx, http.MethodPut, rawURL, requestOpts{ + body: body, + retryBody: retryBody, + headers: map[string]string{"Content-Type": "application/octet-stream"}, + streaming: true, + }) +} + +// checkPutStatus 校验 PUT 响应状态。 +func checkPutStatus(resp *davResponse, op string) error { + switch resp.StatusCode { + case http.StatusOK, http.StatusCreated, http.StatusNoContent: + drainClose(resp) + return nil + default: + msg := readErrorBody(resp) + return fmt.Errorf("%w: %s → %d %s", ErrUnavailable, op, resp.StatusCode, msg) + } +} + +// readErrorBody 读取错误响应前 200 字节并释放连接。 +func readErrorBody(resp *davResponse) string { + if resp == nil || resp.Body == nil { + return "" + } + b, _ := io.ReadAll(io.LimitReader(resp.Body, 200)) + _ = resp.Body.Close() + if resp.cancel != nil { + resp.cancel() + } + return strings.TrimSpace(string(b)) +} + +// SaveFile 流式保存(PUT):按需建目录,seekable 源可安全重试。 +func (w *WebDAVStorage) SaveFile(ctx context.Context, r io.Reader, savePath string) (int64, error) { + remote, err := w.joinRemote(savePath) + if err != nil { + return 0, err + } + if err := w.ensureDirs(ctx, path.Dir(remote)); err != nil { + return 0, err + } + // 可重放判定:seekable 源失败后可从头重传(PUT 覆盖语义保证最终一致)。 + seeker, seekable := r.(io.Seeker) + var knownLen int64 = -1 + if seekable { + if cur, err := seeker.Seek(0, io.SeekCurrent); err == nil { + if end, err := seeker.Seek(0, io.SeekEnd); err == nil { + knownLen = end - cur + _, _ = seeker.Seek(cur, io.SeekStart) + } + } + } + src := &countingReader{r: r} + body := func() (io.Reader, int64, error) { + if seekable { + if _, err := seeker.Seek(0, io.SeekStart); err != nil { + return nil, -1, err + } + src.reset() + } + return src, knownLen, nil + } + resp, err := w.putFile(ctx, w.urlFor(remote), body, seekable) + if err != nil { + return src.count(), err + } + if err := checkPutStatus(resp, fmt.Sprintf("PUT %s", remote)); err != nil { + return src.count(), err + } + return src.count(), nil +} + +// DeleteFile DELETE 文件 + 尽力清理空父目录。 +func (w *WebDAVStorage) DeleteFile(ctx context.Context, savePath string) error { + remote, err := w.joinRemote(savePath) + if err != nil { + return err + } + resp, err := w.do(ctx, http.MethodDelete, w.urlFor(remote), requestOpts{}) + if err != nil { + return err + } + switch resp.StatusCode { + case http.StatusOK, http.StatusNoContent, http.StatusNotFound: + drainClose(resp) + default: + msg := readErrorBody(resp) + return fmt.Errorf("%w: DELETE %s → %d %s", ErrUnavailable, remote, resp.StatusCode, msg) + } + w.deleteEmptyParents(ctx, remote) + return nil +} + +// Open 打开下载流:Range 透传,io.Pipe 流式转发不落盘,ctx 可取消。 +func (w *WebDAVStorage) Open(ctx context.Context, savePath string, rng *Range) (*Download, error) { + remote, err := w.joinRemote(savePath) + if err != nil { + return nil, err + } + opts := requestOpts{streaming: true} + if rng != nil { + opts.headers = map[string]string{"Range": rangeHeaderValue(rng)} + } + resp, err := w.do(ctx, http.MethodGet, w.urlFor(remote), opts) + if err != nil { + return nil, err + } + switch resp.StatusCode { + case http.StatusOK, http.StatusPartialContent: + // 正常,继续 + case http.StatusNotFound: + drainClose(resp) + return nil, ErrNotFound + case http.StatusRequestedRangeNotSatisfiable: + drainClose(resp) + return nil, ErrRangeNotSatisfiable + default: + msg := readErrorBody(resp) + return nil, fmt.Errorf("%w: GET %s → %d %s", ErrUnavailable, remote, resp.StatusCode, msg) + } + + total := resp.ContentLength + start, end := int64(0), total-1 + if resp.StatusCode == http.StatusPartialContent { + if cr := resp.Header.Get("Content-Range"); cr != "" { + if s0, e0, t0, ok := parseContentRange(cr); ok { + start, end = s0, e0 + if t0 >= 0 { + total = t0 + } + } + } + } + if total < 0 { // 服务端未给出长度(chunked):按未知大小处理 + start, end, total = 0, -1, -1 + } + if rng == nil { // 对齐契约:完整文件 Start=0、End=Total-1 + start, end = 0, total-1 + } + if end < 0 { // 空文件或未知大小:End 未知语义 + end = -1 + } + + // io.Pipe 流式桥接:HTTP 响应体 → 管道 → 调用方,全程不落盘; + // 调用方提前 Close 或 ctx 取消都会终止拷贝并释放连接。 + body := resp.Body + pr, pw := io.Pipe() + go func() { + bufp, _ := w.spacesPool.Get().(*[]byte) + _, copyErr := io.CopyBuffer(pw, body, *bufp) + w.spacesPool.Put(bufp) + _ = body.Close() + pw.CloseWithError(copyErr) // copyErr 为 nil 时写入 EOF + }() + context.AfterFunc(ctx, func() { + _ = pw.CloseWithError(ctx.Err()) + }) + + contentType := resp.Header.Get("Content-Type") + return &Download{ + ReadCloser: pr, + Start: start, + End: end, + Total: total, + Meta: FileMeta{ + Size: total, + ContentType: contentType, + AcceptRanges: true, + }, + }, nil +} + +// Stat PROPFIND Depth 0 获取元信息。 +func (w *WebDAVStorage) Stat(ctx context.Context, savePath string) (*FileMeta, error) { + remote, err := w.joinRemote(savePath) + if err != nil { + return nil, err + } + ms, err := w.propfind(ctx, w.urlFor(remote), "0") + if err != nil { + if errors.Is(err, ErrNotFound) { + return nil, ErrNotFound + } + return nil, err + } + if ms == nil { + return nil, ErrNotFound + } + length, ctype, _, ok := ms.firstProp() + if !ok { + return nil, ErrNotFound + } + return &FileMeta{Size: length, ContentType: ctype, AcceptRanges: true}, nil +} + +// HeadMeta 读取文件元信息与前 n 字节(WebDAV 实现:PROPFIND + Range GET)。 +func (w *WebDAVStorage) HeadMeta(ctx context.Context, savePath string, headBytes int64) (*FileMeta, []byte, error) { + meta, err := w.Stat(ctx, savePath) + if err != nil { + return nil, nil, err + } + if headBytes <= 0 { + return meta, nil, nil + } + dl, err := w.Open(ctx, savePath, &Range{Start: 0, End: headBytes - 1}) + if err != nil { + if errors.Is(err, ErrRangeNotSatisfiable) { // 空文件等边界:返回空头 + return meta, nil, nil + } + return nil, nil, err + } + defer func() { _ = dl.Close() }() + head := make([]byte, headBytes) + n, _ := io.ReadFull(dl.ReadCloser, head) + return meta, head[:n], nil +} + +// SaveChunk 保存分片:落临时文件获得精确长度与可重放 body,PUT 到分片路径。 +func (w *WebDAVStorage) SaveChunk(ctx context.Context, uploadID string, chunkIndex int, r io.Reader, savePath string) (int64, error) { + if _, err := w.joinRemote(savePath); err != nil { + return 0, err + } + chunkRel, ok := SanitizePath(ChunkPartPath(savePath, uploadID, chunkIndex)) + if !ok { + return 0, fmt.Errorf("%w: 分片路径非法 %q", ErrInvalidPath, savePath) + } + remote := path.Join(w.root, chunkRel) + if err := w.ensureDirs(ctx, path.Dir(remote)); err != nil { + return 0, err + } + // 分片体积有限(默认 ≤8MB):落临时文件换取精确 Content-Length 与可重试性。 + tmp, err := os.CreateTemp("", "fcb-webdav-chunk-*") + if err != nil { + return 0, fmt.Errorf("storage/webdav: 创建临时文件失败: %w", err) + } + tmpName := tmp.Name() + defer func() { _ = tmp.Close(); _ = os.Remove(tmpName) }() + size, err := io.CopyBuffer(tmp, r, make([]byte, localChunkSize)) + if err != nil { + return 0, fmt.Errorf("storage/webdav: 缓存分片失败: %w", err) + } + if _, err := tmp.Seek(0, io.SeekStart); err != nil { + return 0, fmt.Errorf("storage/webdav: 回卷分片失败: %w", err) + } + body := func() (io.Reader, int64, error) { + if _, err := tmp.Seek(0, io.SeekStart); err != nil { + return nil, -1, err + } + return tmp, size, nil + } + resp, err := w.putFile(ctx, w.urlFor(remote), body, true) + if err != nil { + return size, err + } + if err := checkPutStatus(resp, fmt.Sprintf("PUT 分片 %s", remote)); err != nil { + return size, err + } + return size, nil +} + +// MergeChunks 合并 WebDAV 分片: +// 逐分片 GET 流式拼入本地临时文件(边拷贝边校验哈希)→ PUT 上传目标 → 清理远端分片与本地临时文件。 +// 说明:WebDAV 无服务端聚合能力,合并必须经服务端中转;临时文件仅用于拼接与重试,最终 PUT 可重放。 +func (w *WebDAVStorage) MergeChunks(ctx context.Context, uploadID string, total int, verifyHash func(index int) (string, error), savePath string) (int64, string, error) { + if total <= 0 { + return 0, "", fmt.Errorf("storage/webdav: 非法分片总数 %d", total) + } + remote, err := w.joinRemote(savePath) + if err != nil { + return 0, "", err + } + if err := w.ensureDirs(ctx, path.Dir(remote)); err != nil { + return 0, "", err + } + tmp, err := os.CreateTemp("", "fcb-webdav-merge-*") + if err != nil { + return 0, "", fmt.Errorf("storage/webdav: 创建合并临时文件失败: %w", err) + } + tmpName := tmp.Name() + defer func() { _ = tmp.Close(); _ = os.Remove(tmpName) }() + + totalHash := sha256.New() + var size int64 + for i := 0; i < total; i++ { + if err := ctx.Err(); err != nil { + return 0, "", err + } + chunkRel, ok := SanitizePath(ChunkPartPath(savePath, uploadID, i)) + if !ok { + return 0, "", fmt.Errorf("%w: 分片 %d 路径非法", ErrInvalidPath, i) + } + resp, err := w.do(ctx, http.MethodGet, w.urlFor(path.Join(w.root, chunkRel)), requestOpts{streaming: true}) + if err != nil { + return 0, "", fmt.Errorf("storage/webdav: 读取分片 %d 失败: %w", i, err) + } + if resp.StatusCode == http.StatusNotFound { + drainClose(resp) + return 0, "", fmt.Errorf("storage/webdav: 分片 %d 不存在", i) + } + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent { + msg := readErrorBody(resp) + return 0, "", fmt.Errorf("storage/webdav: 读取分片 %d → %d %s", i, resp.StatusCode, msg) + } + chunkHash := sha256.New() + n, err := io.CopyBuffer(io.MultiWriter(tmp, totalHash, chunkHash), resp.Body, make([]byte, localChunkSize)) + _ = resp.Body.Close() + if err != nil { + return 0, "", fmt.Errorf("storage/webdav: 拼接分片 %d 失败: %w", i, err) + } + if verifyHash != nil { + expected, err := verifyHash(i) + if err != nil { + return 0, "", err + } + if expected != "" && expected != hex.EncodeToString(chunkHash.Sum(nil)) { + return 0, "", fmt.Errorf("%w: 分片 %d 期望 %s", ErrHashMismatch, i, expected) + } + } + size += n + } + if _, err := tmp.Seek(0, io.SeekStart); err != nil { + return 0, "", fmt.Errorf("storage/webdav: 回卷合并文件失败: %w", err) + } + body := func() (io.Reader, int64, error) { + if _, err := tmp.Seek(0, io.SeekStart); err != nil { + return nil, -1, err + } + return tmp, size, nil + } + resp, err := w.putFile(ctx, w.urlFor(remote), body, true) + if err != nil { + return size, "", err + } + if err := checkPutStatus(resp, fmt.Sprintf("PUT 合并 %s", remote)); err != nil { + return size, "", err + } + // 合并成功后清理远端分片目录与本地临时文件(defer 兜底删除本地文件)。 + _ = w.CleanChunks(ctx, uploadID, savePath) + return size, hex.EncodeToString(totalHash.Sum(nil)), nil +} + +// CleanChunks 递归删除远端分片目录(RFC 4918 DELETE 对 collection 递归)。 +func (w *WebDAVStorage) CleanChunks(ctx context.Context, uploadID string, savePath string) error { + dirRel, ok := SanitizePath(chunkDirOf(savePath, uploadID)) + if !ok { + return fmt.Errorf("%w: 分片目录非法 %q", ErrInvalidPath, savePath) + } + remote := path.Join(w.root, dirRel) + resp, err := w.do(ctx, http.MethodDelete, w.urlFor(remote), requestOpts{}) + if err != nil { + return err + } + switch resp.StatusCode { + case http.StatusOK, http.StatusNoContent, http.StatusNotFound: + drainClose(resp) + w.unmarkDir(remote) + default: + msg := readErrorBody(resp) + return fmt.Errorf("%w: 清理分片目录 %s → %d %s", ErrUnavailable, remote, resp.StatusCode, msg) + } + return nil +} + +// FileExists PROPFIND 探测存在性;非法路径按不存在处理。 +func (w *WebDAVStorage) FileExists(ctx context.Context, savePath string) (bool, error) { + remote, err := w.joinRemote(savePath) + if err != nil { + return false, nil + } + return w.remoteExists(ctx, remote) +} + +// PresignGetURL WebDAV 无预签名直链能力。 +func (w *WebDAVStorage) PresignGetURL(ctx context.Context, savePath string, expires int64) (string, error) { + return "", ErrNotSupported +} + +// PresignPutURL WebDAV 无预签名直传能力。 +func (w *WebDAVStorage) PresignPutURL(ctx context.Context, savePath string, expires int64) (string, error) { + return "", ErrNotSupported +} + +// HealthCheck 健康检查:PROPFIND 根目录;不存在时 MKCOL 创建(启动自愈)。 +// 同时完成凭据与连通性验证(do 内 401 协商)。 +func (w *WebDAVStorage) HealthCheck(ctx context.Context) error { + exists, err := w.remoteExists(ctx, w.root) + if err == nil && exists { + w.markDir(w.root) + return nil + } + if err != nil && !errors.Is(err, ErrNotFound) { + return fmt.Errorf("%w: WebDAV 健康检查失败: %v", ErrUnavailable, err) + } + resp, err := w.do(ctx, "MKCOL", w.urlFor(w.root), requestOpts{}) + if err != nil { + return err + } + switch resp.StatusCode { + case http.StatusCreated, http.StatusOK, http.StatusNoContent, http.StatusMethodNotAllowed: + drainClose(resp) + w.markDir(w.root) + return nil + default: + msg := readErrorBody(resp) + return fmt.Errorf("%w: WebDAV 根目录创建失败 → %d %s", ErrUnavailable, resp.StatusCode, msg) + } +} + +// 接口编译期断言。 +var _ Storage = (*WebDAVStorage)(nil) diff --git a/server/internal/storage/webdav_auth.go b/server/internal/storage/webdav_auth.go new file mode 100644 index 0000000..de1784c --- /dev/null +++ b/server/internal/storage/webdav_auth.go @@ -0,0 +1,244 @@ +package storage + +import ( + "crypto/md5" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "fmt" + "net/http" + "strings" + "sync" +) + +// authMode 认证模式(WebDAV 服务端挑战后自动协商)。 +type authMode int + +const ( + authModeUnknown authMode = iota // 未定:先发 Basic 探测 + authModeBasic + authModeDigest +) + +// authState WebDAV Basic/Digest 认证状态。 +// +// 策略: +// - 首个请求预置 Basic;若服务端 401 且挑战为 Digest,则解析挑战参数切换为 Digest; +// - Digest 按 RFC 2617/7616 实现 qop=auth(MD5 / SHA-256,含 -sess 变体); +// qop 缺失时回退 RFC 2069 旧式响应; +// - nonce 变更时重置 nc 计数;nc/cnonce 在互斥锁内生成保证并发唯一。 +type authState struct { + mu sync.Mutex + username string + password string + mode authMode + realm string + nonce string + qop string // 选定的 qop("auth" 或空 = RFC2069) + opaque string + algorithm string // MD5 | MD5-sess | SHA-256 | SHA-256-sess + nc uint32 + knownBasicOK bool // 已确认 Basic 可用 +} + +// newAuthState 构造认证状态(默认以 Basic 起步)。 +func newAuthState(username, password string) *authState { + return &authState{username: username, password: password} +} + +// apply 为请求设置 Authorization 头(每次请求调用,Digest 时消耗一个 nc)。 +func (a *authState) apply(req *http.Request) { + a.mu.Lock() + defer a.mu.Unlock() + switch { + case a.mode == authModeDigest && a.nonce != "": + req.Header.Set("Authorization", a.digestHeader(req)) + default: + req.SetBasicAuth(a.username, a.password) + } +} + +// digestHeader 依据缓存的挑战参数计算 Digest Authorization 头(调用方需持锁)。 +func (a *authState) digestHeader(req *http.Request) string { + uri := req.URL.RequestURI() + method := strings.ToUpper(req.Method) + ncStr := fmt.Sprintf("%08x", a.nc+1) + a.nc++ + cnonce := randomHex(8) + + var ha1 string + switch strings.ToLower(a.algorithm) { + case "md5-sess": + ha1 = hashHex("md5", hashHex("md5", a.username+":"+a.realm+":"+a.password)+":"+a.nonce+":"+cnonce) + case "sha-256-sess": + ha1 = hashHex("sha256", hashHex("sha256", a.username+":"+a.realm+":"+a.password)+":"+a.nonce+":"+cnonce) + case "sha-256": + ha1 = hashHex("sha256", a.username+":"+a.realm+":"+a.password) + default: // md5 + ha1 = hashHex("md5", a.username+":"+a.realm+":"+a.password) + } + ha2 := hashHex(algoName(a.algorithm), method+":"+uri) + + var response string + var fields []string + esc := escapeDigestValue(a.username) + if a.qop == "" { // RFC 2069 + response = hashHex(algoName(a.algorithm), ha1+":"+a.nonce+":"+ha2) + fields = append(fields, + `Digest username="`+esc+`"`, + `realm="`+escapeDigestValue(a.realm)+`"`, + `nonce="`+escapeDigestValue(a.nonce)+`"`, + `uri="`+escapeDigestValue(uri)+`"`, + `response="`+response+`"`) + } else { + response = hashHex(algoName(a.algorithm), ha1+":"+a.nonce+":"+ncStr+":"+cnonce+":"+a.qop+":"+ha2) + fields = append(fields, + `Digest username="`+esc+`"`, + `realm="`+escapeDigestValue(a.realm)+`"`, + `nonce="`+escapeDigestValue(a.nonce)+`"`, + `uri="`+escapeDigestValue(uri)+`"`, + `cnonce="`+cnonce+`"`, + `nc=`+ncStr, + `qop=`+a.qop, + `response="`+response+`"`, + `algorithm=`+a.algorithm) + } + if a.opaque != "" { + fields = append(fields, `opaque="`+escapeDigestValue(a.opaque)+`"`) + } + return strings.Join(fields, ", ") +} + +// challenge 处理 401 的 WWW-Authenticate 挑战;返回是否已切换认证方式可重试。 +// 返回 false 表示凭据错误或算法不受支持,调用方应直接报错。 +func (a *authState) challenge(header string) bool { + a.mu.Lock() + defer a.mu.Unlock() + h := strings.TrimSpace(header) + lower := strings.ToLower(h) + switch { + case strings.HasPrefix(lower, "digest"): + params := parseChallengeParams(strings.TrimPrefix(h[len("Digest"):], " ")) + algo := strings.ToUpper(strings.TrimSpace(params["algorithm"])) + if algo == "" { + algo = "MD5" + } + switch algo { + case "MD5", "MD5-SESS", "SHA-256", "SHA-256-SESS": + default: + return false // 不支持的摘要算法 + } + if params["nonce"] == "" || params["realm"] == "" { + return false + } + qop := "" + if raw := strings.TrimSpace(params["qop"]); raw != "" { + for _, candidate := range strings.Split(raw, ",") { + if strings.EqualFold(strings.TrimSpace(candidate), "auth") { + qop = "auth" + break + } + } + if qop == "" { + return false // 仅支持 auth-int 等需要 body 哈希的模式 + } + } + if a.nonce != params["nonce"] { + a.nc = 0 + } + a.realm, a.nonce, a.qop = params["realm"], params["nonce"], qop + a.opaque, a.algorithm = params["opaque"], strings.ToLower(algo) + a.mode = authModeDigest + a.knownBasicOK = false + return true + case strings.HasPrefix(lower, "basic"): + if a.knownBasicOK || a.mode == authModeBasic { + return false // 已用 Basic 仍 401:凭据错误 + } + a.mode = authModeBasic + a.knownBasicOK = true + return true + } + return false +} + +// parseChallengeParams 解析 "realm=\"x\", nonce=\"y\"" 形式的挑战参数(引号内逗号不切分)。 +func parseChallengeParams(s string) map[string]string { + out := map[string]string{} + for _, item := range splitAuthParams(s) { + kv := strings.SplitN(item, "=", 2) + if len(kv) != 2 { + continue + } + k := strings.ToLower(strings.TrimSpace(kv[0])) + v := strings.TrimSpace(kv[1]) + if len(v) >= 2 && strings.HasPrefix(v, `"`) && strings.HasSuffix(v, `"`) { + v = v[1 : len(v)-1] + } + out[k] = v + } + return out +} + +// splitAuthParams 逗号切分但忽略引号内的逗号。 +func splitAuthParams(s string) []string { + var parts []string + var b strings.Builder + inQuote := false + for i := 0; i < len(s); i++ { + c := s[i] + switch { + case c == '"': + inQuote = !inQuote + b.WriteByte(c) + case c == ',' && !inQuote: + if t := strings.TrimSpace(b.String()); t != "" { + parts = append(parts, t) + } + b.Reset() + default: + b.WriteByte(c) + } + } + if t := strings.TrimSpace(b.String()); t != "" { + parts = append(parts, t) + } + return parts +} + +// algoName 映射哈希函数名。 +func algoName(algorithm string) string { + switch strings.ToLower(algorithm) { + case "sha-256", "sha-256-sess": + return "sha256" + default: + return "md5" + } +} + +// hashHex 通用哈希摘要(algo: md5|sha256)。 +func hashHex(algo, s string) string { + if algo == "sha256" { + sum := sha256.Sum256([]byte(s)) + return hex.EncodeToString(sum[:]) + } + sum := md5.Sum([]byte(s)) + return hex.EncodeToString(sum[:]) +} + +// escapeDigestValue 转义引号。 +func escapeDigestValue(s string) string { + return strings.ReplaceAll(s, `"`, `\"`) +} + +// randomHex 生成 n 字节随机 hex。 +func randomHex(n int) string { + b := make([]byte, n) + if _, err := rand.Read(b); err != nil { + // crypto/rand 失败极其罕见;退化为全零仍保持协议可用。 + for i := range b { + b[i] = 0 + } + } + return hex.EncodeToString(b) +} diff --git a/server/internal/storage/webdav_test.go b/server/internal/storage/webdav_test.go new file mode 100644 index 0000000..97d7d28 --- /dev/null +++ b/server/internal/storage/webdav_test.go @@ -0,0 +1,824 @@ +package storage + +import ( + "bytes" + "context" + "encoding/base64" + "fmt" + "io" + "net" + "net/http" + "net/http/httptest" + "strconv" + "strings" + "sync" + "sync/atomic" + "testing" + "time" +) + +// ---- 最小 WebDAV 假服务:PUT/GET/HEAD/PROPFIND/MKCOL/DELETE + Basic/Digest 认证 ---- + +type davLog struct { + Method string + Path string + Status int +} + +type fakeDav struct { + mu sync.Mutex + dirs map[string]bool + files map[string][]byte + + // 认证配置:mode = none|basic|digest;digest 配合 algo = MD5|SHA-256。 + mode string + username string + password string + realm string + nonce string + opaque string + algo string + + failNext map[string]int // method → 剩余 503 次数 + logs []davLog +} + +func newFakeDav(mode string) *fakeDav { + return &fakeDav{ + dirs: map[string]bool{}, + files: map[string][]byte{}, + mode: mode, + username: "fcb", + password: "fcb-pass", + realm: "test-realm", + nonce: "dcd98b7102dd2f0e8b11d0f600bfb0c0", + opaque: "5ccc069c403ebaf9f0171e9517f40e41", + algo: "MD5", + failNext: map[string]int{}, + } +} + +// auth 校验请求凭据;失败时写出 401 与对应挑战。 +func (f *fakeDav) auth(w http.ResponseWriter, r *http.Request) bool { + if f.mode == "none" { + return true + } + h := r.Header.Get("Authorization") + ok := false + switch f.mode { + case "basic": + ok = h == "Basic "+basicAuth(f.username, f.password) + case "digest": + ok = f.checkDigest(r) + } + if ok { + return true + } + switch f.mode { + case "basic": + w.Header().Set("WWW-Authenticate", `Basic realm="`+f.realm+`"`) + case "digest": + w.Header().Set("WWW-Authenticate", fmt.Sprintf( + `Digest realm="%s", qop="auth", nonce="%s", opaque="%s", algorithm=%s, stale=false`, + f.realm, f.nonce, f.opaque, f.algo)) + } + w.WriteHeader(http.StatusUnauthorized) + return false +} + +// checkDigest 服务端重算 RFC 2617 摘要响应。 +func (f *fakeDav) checkDigest(r *http.Request) bool { + h := r.Header.Get("Authorization") + if !strings.HasPrefix(h, "Digest ") { + return false + } + p := parseChallengeParams(strings.TrimSpace(h[len("Digest "):])) + ha1 := hashHex(algoName(f.algo), f.username+":"+f.realm+":"+f.password) + ha2 := hashHex(algoName(f.algo), strings.ToUpper(r.Method)+":"+r.URL.RequestURI()) + got := hashHex(algoName(f.algo), ha1+":"+f.nonce+":"+p["nc"]+":"+p["cnonce"]+":"+p["qop"]+":"+ha2) + return p["username"] == f.username && p["response"] == got +} + +func basicAuth(user, pass string) string { + return base64.StdEncoding.EncodeToString([]byte(user + ":" + pass)) +} + +func (f *fakeDav) record(method, path string, status int) { + f.logs = append(f.logs, davLog{Method: method, Path: path, Status: status}) +} + +// maybeFail 命中失败注入时返回 true(已写出 503)。 +func (f *fakeDav) maybeFail(w http.ResponseWriter, method string) bool { + if f.failNext[method] > 0 { + f.failNext[method]-- + w.WriteHeader(http.StatusServiceUnavailable) + return true + } + return false +} + +func (f *fakeDav) ServeHTTP(w http.ResponseWriter, r *http.Request) { + f.mu.Lock() + defer f.mu.Unlock() + if !f.auth(w, r) { + f.record(r.Method, r.URL.Path, http.StatusUnauthorized) + return + } + p := strings.Trim(r.URL.Path, "/") + + switch r.Method { + case http.MethodPut: + if f.maybeFail(w, "PUT") { + f.record(r.Method, p, 503) + return + } + if parent := parentOf(p); parent != "" && !f.dirs[parent] { + w.WriteHeader(http.StatusConflict) // 强制客户端先建目录 + f.record(r.Method, p, 409) + return + } + body, _ := io.ReadAll(r.Body) + f.files[p] = body + f.record(r.Method, p, 201) + w.WriteHeader(http.StatusCreated) + case http.MethodGet: + if f.maybeFail(w, "GET") { + f.record(r.Method, p, 503) + return + } + data, ok := f.files[p] + if !ok { + w.WriteHeader(http.StatusNotFound) + f.record(r.Method, p, 404) + return + } + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Accept-Ranges", "bytes") + if rng := r.Header.Get("Range"); rng != "" { + start, end := int64(0), int64(len(data))-1 + spec := strings.TrimPrefix(rng, "bytes=") + if strings.HasSuffix(spec, "-") { // bytes=N- → 到文件尾 + if s, err := strconv.ParseInt(strings.TrimSuffix(spec, "-"), 10, 64); err == nil { + start = s + } + } else if _, err := fmt.Sscanf(spec, "%d-%d", &start, &end); err != nil { + w.WriteHeader(http.StatusBadRequest) + f.record(r.Method, p, 400) + return + } + if start < 0 || start >= int64(len(data)) { + w.WriteHeader(http.StatusRequestedRangeNotSatisfiable) + f.record(r.Method, p, 416) + return + } + if end >= int64(len(data)) { + end = int64(len(data)) - 1 + } + w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, len(data))) + w.WriteHeader(http.StatusPartialContent) + _, _ = w.Write(data[start : end+1]) + f.record(r.Method, p, 206) + return + } + w.WriteHeader(http.StatusOK) + _, _ = w.Write(data) + f.record(r.Method, p, 200) + case http.MethodHead: + data, ok := f.files[p] + if !ok { + w.WriteHeader(http.StatusNotFound) + f.record(r.Method, p, 404) + return + } + w.Header().Set("Content-Length", strconv.Itoa(len(data))) + w.WriteHeader(http.StatusOK) + f.record(r.Method, p, 200) + case "PROPFIND": + depth := r.Header.Get("Depth") + self, isDirSelf := f.stat(p) + if !isDirSelf { + w.WriteHeader(http.StatusNotFound) + f.record(r.Method, p, 404) + return + } + var b strings.Builder + b.WriteString(`` + + ``) + f.writeResponse(&b, p, self) + if depth == "1" && self.isDir { + for _, name := range f.children(p) { + child := name + cs, cd := f.stat(child) + f.writeResponse(&b, child, davStat{isDir: cd, size: cs.size}) + } + } + b.WriteString(``) + w.Header().Set("Content-Type", "application/xml; charset=utf-8") + w.WriteHeader(http.StatusMultiStatus) + _, _ = w.Write([]byte(b.String())) + f.record(r.Method, p, 207) + case "MKCOL": + if f.dirs[p] || f.files[p] != nil { + w.WriteHeader(http.StatusMethodNotAllowed) // 已存在 + f.record(r.Method, p, 405) + return + } + if parent := parentOf(p); parent != "" && !f.dirs[parent] { + w.WriteHeader(http.StatusConflict) + f.record(r.Method, p, 409) + return + } + f.dirs[p] = true + w.WriteHeader(http.StatusCreated) + f.record(r.Method, p, 201) + case http.MethodDelete: + if _, ok := f.files[p]; ok { + delete(f.files, p) + w.WriteHeader(http.StatusNoContent) + f.record(r.Method, p, 204) + return + } + if f.dirs[p] { + // 递归删除目录 + prefix := p + "/" + for name := range f.files { + if strings.HasPrefix(name, prefix) { + delete(f.files, name) + } + } + for name := range f.dirs { + if name == p || strings.HasPrefix(name+"/", prefix) { + delete(f.dirs, name) + } + } + w.WriteHeader(http.StatusNoContent) + f.record(r.Method, p, 204) + return + } + w.WriteHeader(http.StatusNotFound) + f.record(r.Method, p, 404) + default: + w.WriteHeader(http.StatusMethodNotAllowed) + f.record(r.Method, p, 405) + } +} + +type davStat struct { + isDir bool + size int +} + +func (f *fakeDav) stat(p string) (davStat, bool) { + if data, ok := f.files[p]; ok { + return davStat{size: len(data)}, true + } + if f.dirs[p] { + return davStat{isDir: true}, true + } + return davStat{}, false +} + +func (f *fakeDav) children(p string) []string { + var out []string + prefix := p + "/" + for name := range f.files { + if strings.HasPrefix(name, prefix) && !strings.Contains(strings.TrimPrefix(name, prefix), "/") { + out = append(out, name) + } + } + for name := range f.dirs { + if strings.HasPrefix(name, prefix) && !strings.Contains(strings.TrimPrefix(name, prefix), "/") { + out = append(out, name) + } + } + return out +} + +func (f *fakeDav) writeResponse(b *strings.Builder, href string, st davStat) { + b.WriteString(`/` + href + ``) + if st.isDir { + b.WriteString(``) + } + b.WriteString(`` + strconv.Itoa(st.size) + + `application/octet-stream` + + `HTTP/1.1 200 OK`) +} + +func parentOf(p string) string { + if i := strings.LastIndex(p, "/"); i > 0 { + return p[:i] + } + return "" +} + +// newTestDav 构造 WebDAV 引擎 + 假服务。 +func newTestDav(t *testing.T, mode string, tweak func(o *WebDAVOptions)) (*WebDAVStorage, *fakeDav, *int32) { + t.Helper() + f := newFakeDav(mode) + var conns int32 + srv := httptest.NewUnstartedServer(f) + srv.Config.ConnState = func(c net.Conn, cs http.ConnState) { + if cs == http.StateNew { + atomic.AddInt32(&conns, 1) + } + } + srv.Start() + t.Cleanup(srv.Close) + opts := WebDAVOptions{ + BaseURL: srv.URL, + Username: f.username, + Password: f.password, + RootPath: "fcb_root", + MaxRetries: 3, + } + if tweak != nil { + tweak(&opts) + } + st, err := NewWebDAVStorage(opts) + if err != nil { + t.Fatalf("NewWebDAVStorage: %v", err) + } + return st, f, &conns +} + +// TestWebDAVBasicCRUD Basic 认证下的完整 CRUD 与 Range。 +func TestWebDAVBasicCRUD(t *testing.T) { + st, f, _ := newTestDav(t, "basic", nil) + ctx := context.Background() + + // 健康检查:根目录 404 → MKCOL 自建 + if err := st.HealthCheck(ctx); err != nil { + t.Fatalf("HealthCheck: %v", err) + } + if !f.dirs["fcb_root"] { + t.Fatalf("根目录应被自动创建") + } + + data := []byte("WebDAV 引擎数据 0123456789 ABCDEF") + n, err := st.SaveFile(ctx, bytes.NewReader(data), "2025/08/w.bin") + if err != nil { + t.Fatalf("SaveFile: %v", err) + } + if n != int64(len(data)) { + t.Fatalf("n = %d", n) + } + if string(f.files["fcb_root/2025/08/w.bin"]) != string(data) { + t.Fatalf("PUT 内容不匹配") + } + // 按需建目录:两级目录都应已创建 + if !f.dirs["fcb_root/2025"] || !f.dirs["fcb_root/2025/08"] { + t.Fatalf("目录未按需创建: %v %v", f.dirs["fcb_root/2025"], f.dirs["fcb_root/2025/08"]) + } + + meta, err := st.Stat(ctx, "2025/08/w.bin") + if err != nil { + t.Fatalf("Stat: %v", err) + } + if meta.Size != int64(len(data)) || !meta.AcceptRanges { + t.Fatalf("Stat = %+v", meta) + } + if ok, _ := st.FileExists(ctx, "2025/08/w.bin"); !ok { + t.Fatalf("FileExists 应为 true") + } + + // 完整下载(对齐 go-api 约定) + dl, err := st.Open(ctx, "2025/08/w.bin", nil) + if err != nil { + t.Fatalf("Open: %v", err) + } + got, err := io.ReadAll(dl) + _ = dl.Close() + if err != nil { + t.Fatalf("read: %v", err) + } + if !bytes.Equal(got, data) { + t.Fatalf("full mismatch") + } + if dl.Start != 0 || dl.End != int64(len(data))-1 || dl.Total != int64(len(data)) { + t.Fatalf("full offsets = %d,%d,%d", dl.Start, dl.End, dl.Total) + } + + // Range 下载 + dl, err = st.Open(ctx, "2025/08/w.bin", &Range{Start: 2, End: 7}) + if err != nil { + t.Fatalf("Open range: %v", err) + } + got, err = io.ReadAll(dl) + _ = dl.Close() + if err != nil { + t.Fatalf("read range: %v", err) + } + if !bytes.Equal(got, data[2:8]) { + t.Fatalf("range mismatch") + } + if dl.Start != 2 || dl.End != 7 || dl.Total != int64(len(data)) { + t.Fatalf("range offsets = %d,%d,%d", dl.Start, dl.End, dl.Total) + } + + // 416(起点越界)/ 404 + if _, err := st.Open(ctx, "2025/08/w.bin", &Range{Start: int64(len(data)) + 9, End: -1}); err == nil || + !strings.Contains(err.Error(), ErrRangeNotSatisfiable.Error()) { + t.Fatalf("want ErrRangeNotSatisfiable, got %v", err) + } + if _, err := st.Open(ctx, "no/such.bin", nil); err == nil || + !strings.Contains(err.Error(), ErrNotFound.Error()) { + t.Fatalf("want ErrNotFound, got %v", err) + } + + // 删除 + 空父目录清理 + if err := st.DeleteFile(ctx, "2025/08/w.bin"); err != nil { + t.Fatalf("DeleteFile: %v", err) + } + if ok, _ := st.FileExists(ctx, "2025/08/w.bin"); ok { + t.Fatalf("删除后仍存在") + } + if _, err := st.Stat(ctx, "2025/08/w.bin"); err == nil || + !strings.Contains(err.Error(), ErrNotFound.Error()) { + t.Fatalf("Stat 应 ErrNotFound, got %v", err) + } +} + +// TestWebDAVDigestAuth Digest(MD5)认证协商。 +func TestWebDAVDigestAuth(t *testing.T) { + st, f, _ := newTestDav(t, "digest", nil) + ctx := context.Background() + if err := st.HealthCheck(ctx); err != nil { + t.Fatalf("HealthCheck(digest): %v", err) + } + // HealthCheck 流程应观察到 401 挑战(客户端先 Basic 探测 → 401 → Digest 重试) + saw401 := false + for _, l := range f.logs { + if l.Status == 401 { + saw401 = true + } + } + if !saw401 { + t.Fatalf("未观察到 401 挑战: %+v", f.logs) + } + + // 认证后的 PROPFIND(Stat 已有目录)应得到 207 + if _, err := st.Stat(ctx, ""); err == nil { + // Stat("") 非法路径属预期;这里换用 FileExists 对已有根目录探测 + _ = err + } + data := []byte("digest 内容") + if _, err := st.SaveFile(ctx, bytes.NewReader(data), "d.bin"); err != nil { + t.Fatalf("SaveFile(digest): %v", err) + } + dl, err := st.Open(ctx, "d.bin", nil) + if err != nil { + t.Fatalf("Open(digest): %v", err) + } + got, _ := io.ReadAll(dl) + _ = dl.Close() + if !bytes.Equal(got, data) { + t.Fatalf("digest 下载内容不匹配") + } + // 全链路完成:确认存在成功的 2xx/207 请求 + saw2xx := false + for _, l := range f.logs { + if l.Status == 207 || l.Status == 201 || l.Status == 200 { + saw2xx = true + } + } + if !saw2xx { + t.Fatalf("认证后应有成功请求: %+v", f.logs) + } +} + +// TestWebDAVDigestSHA256 Digest(SHA-256)算法。 +func TestWebDAVDigestSHA256(t *testing.T) { + st, _, _ := newTestDav(t, "digest", nil) + st.auth.mu.Lock() + st.auth.algorithm = "sha-256" + st.auth.mu.Unlock() + // 服务端也切换到 SHA-256 重算摘要 + st2, f, _ := newTestDav(t, "digest", nil) + f.algo = "SHA-256" + // 先让客户端完成一次 MD5 协商拿到挑战参数,再切 SHA-256 会 401 失败—— + // 因此这里直接对 SHA-256 服务端做完整链路(client 首次探测 Basic→401→Digest)。 + _ = st + ctx := context.Background() + if err := st2.HealthCheck(ctx); err != nil { + t.Fatalf("HealthCheck(SHA-256): %v", err) + } +} + +// TestWebDAVDigestWrongPassword 凭据错误 → 明确报错而非重试风暴。 +func TestWebDAVDigestWrongPassword(t *testing.T) { + f := newFakeDav("digest") + srv := httptest.NewServer(f) + defer srv.Close() + st, err := NewWebDAVStorage(WebDAVOptions{ + BaseURL: srv.URL, Username: f.username, Password: "WRONG", + RootPath: "r", MaxRetries: 1, BaseBackoff: 1, + }) + if err != nil { + t.Fatal(err) + } + if err := st.HealthCheck(context.Background()); err == nil || + !strings.Contains(err.Error(), "401") { + t.Fatalf("错误凭据应报 401 相关错误, got %v", err) + } +} + +// TestWebDAVRetryGet 5xx 指数退避重试(GET 幂等)。 +func TestWebDAVRetryGet(t *testing.T) { + st, f, _ := newTestDav(t, "basic", func(o *WebDAVOptions) { o.BaseBackoff = 5 }) + ctx := context.Background() + data := []byte("retry target") + if err := st.HealthCheck(ctx); err != nil { + t.Fatal(err) + } + if _, err := st.SaveFile(ctx, bytes.NewReader(data), "r.bin"); err != nil { + t.Fatal(err) + } + f.mu.Lock() + f.failNext["GET"] = 2 + f.mu.Unlock() + dl, err := st.Open(ctx, "r.bin", nil) + if err != nil { + t.Fatalf("503×2 后应重试成功: %v", err) + } + got, _ := io.ReadAll(dl) + _ = dl.Close() + if !bytes.Equal(got, data) { + t.Fatalf("content mismatch") + } + // 验证确实发了 3 次 GET + gets := 0 + for _, l := range f.logs { + if l.Method == "GET" && strings.HasSuffix(l.Path, "r.bin") { + gets++ + } + } + if gets != 3 { + t.Fatalf("GET 次数 = %d, want 3", gets) + } +} + +// TestWebDAVRetryPut 可重放 body(seekable)PUT 失败重试;不可重放不重试。 +func TestWebDAVRetryPut(t *testing.T) { + st, f, _ := newTestDav(t, "basic", func(o *WebDAVOptions) { o.BaseBackoff = 5 }) + ctx := context.Background() + if err := st.HealthCheck(ctx); err != nil { + t.Fatal(err) + } + // seekable:重试成功 + f.mu.Lock() + f.failNext["PUT"] = 1 + f.mu.Unlock() + data := []byte("put with retry") + if _, err := st.SaveFile(ctx, bytes.NewReader(data), "pr.bin"); err != nil { + t.Fatalf("PUT 重试应成功: %v", err) + } + puts := 0 + for _, l := range f.logs { + if l.Method == "PUT" && strings.HasSuffix(l.Path, "pr.bin") { + puts++ + } + } + if puts != 2 { + t.Fatalf("PUT 次数 = %d, want 2", puts) + } + // 非 seekable(io.Pipe):不重试,直接失败 + f.mu.Lock() + f.failNext["PUT"] = 1 + f.mu.Unlock() + pr, pw := io.Pipe() + go func() { + _, _ = pw.Write([]byte("non-seekable")) + _ = pw.Close() + }() + if _, err := st.SaveFile(ctx, pr, "ns.bin"); err == nil { + t.Fatalf("非重放 PUT 注入 503 应失败") + } +} + +// TestWebDAVConnectionReuse 连接复用:多次请求不应各建一条 TCP 连接。 +func TestWebDAVConnectionReuse(t *testing.T) { + st, _, conns := newTestDav(t, "basic", nil) + ctx := context.Background() + if err := st.HealthCheck(ctx); err != nil { + t.Fatal(err) + } + for i := 0; i < 12; i++ { + if _, err := st.SaveFile(ctx, bytes.NewReader([]byte("x")), fmt.Sprintf("reuse/%d.bin", i)); err != nil { + t.Fatal(err) + } + if _, err := st.Stat(ctx, fmt.Sprintf("reuse/%d.bin", i)); err != nil { + t.Fatal(err) + } + } + // 25 次请求(12 PUT + 12 PROPFIND + 1 HealthCheck 的 PROPFIND/MKCOL)只允许极少量新连接 + if got := atomic.LoadInt32(conns); got > 4 { + t.Fatalf("新建 TCP 连接数 = %d,连接复用失效(应 ≤4)", got) + } +} + +// TestWebDAVPipeStreaming io.Pipe 流式转发:完整读取 + 提前关闭。 +func TestWebDAVPipeStreaming(t *testing.T) { + st, _, _ := newTestDav(t, "basic", nil) + ctx := context.Background() + if err := st.HealthCheck(ctx); err != nil { + t.Fatal(err) + } + big := bytes.Repeat([]byte("0123456789abcdef"), 4096) // 64KB + if _, err := st.SaveFile(ctx, bytes.NewReader(big), "big.bin"); err != nil { + t.Fatal(err) + } + dl, err := st.Open(ctx, "big.bin", nil) + if err != nil { + t.Fatal(err) + } + got, err := io.ReadAll(dl) + if err != nil { + t.Fatalf("read pipe: %v", err) + } + _ = dl.Close() + if !bytes.Equal(got, big) { + t.Fatalf("pipe content mismatch") + } + + // 提前关闭:后续读取返回错误且不挂死 + dl2, err := st.Open(ctx, "big.bin", nil) + if err != nil { + t.Fatal(err) + } + buf := make([]byte, 10) + if _, err := io.ReadFull(dl2, buf); err != nil { + t.Fatalf("read head: %v", err) + } + if err := dl2.Close(); err != nil { + t.Fatalf("early close: %v", err) + } + // ctx 取消同样会终止流 + cctx, cancel := context.WithCancel(context.Background()) + dl3, err := st.Open(cctx, "big.bin", nil) + if err != nil { + t.Fatal(err) + } + cancel() + time.Sleep(20 * time.Millisecond) + _, err = dl3.Read(buf) + if err == nil { + _ = dl3.Close() + t.Fatalf("ctx 取消后读取应报错") + } + _ = dl3.Close() +} + +// TestWebDAVChunkMerge 分片保存/合并/清理。 +func TestWebDAVChunkMerge(t *testing.T) { + st, f, _ := newTestDav(t, "basic", nil) + ctx := context.Background() + if err := st.HealthCheck(ctx); err != nil { + t.Fatal(err) + } + savePath := "2025/09/merged.bin" + uploadID := "uid-webdav" + chunks := [][]byte{[]byte("AAA"), []byte("BB"), []byte("CCCC")} + hashes := make([]string, len(chunks)) + for i, c := range chunks { + n, err := st.SaveChunk(ctx, uploadID, i, bytes.NewReader(c), savePath) + if err != nil { + t.Fatalf("SaveChunk %d: %v", i, err) + } + if n != int64(len(c)) { + t.Fatalf("chunk %d size = %d", i, n) + } + hashes[i] = sha256Hex(c) + } + size, fileHash, err := st.MergeChunks(ctx, uploadID, len(chunks), func(i int) (string, error) { + return hashes[i], nil + }, savePath) + if err != nil { + t.Fatalf("MergeChunks: %v", err) + } + if size != 9 || fileHash != sha256Hex(bytes.Join(chunks, nil)) { + t.Fatalf("merge result = %d %s", size, fileHash) + } + if string(f.files["fcb_root/"+savePath]) != "AAABBCCCC" { + t.Fatalf("合并内容错误: %q", f.files["fcb_root/"+savePath]) + } + // 分片目录已清理 + for k := range f.files { + if strings.Contains(k, "chunks/"+uploadID) { + t.Fatalf("分片残留: %s", k) + } + } + if f.dirs["fcb_root/2025/09/chunks/"+uploadID] { + t.Fatalf("分片目录残留") + } +} + +// TestWebDAVCleanChunks 清理与哈希失败路径。 +func TestWebDAVCleanChunks(t *testing.T) { + st, f, _ := newTestDav(t, "basic", nil) + ctx := context.Background() + if err := st.HealthCheck(ctx); err != nil { + t.Fatal(err) + } + for i := 0; i < 2; i++ { + if _, err := st.SaveChunk(ctx, "uidc", i, strings.NewReader("z"), "c.bin"); err != nil { + t.Fatal(err) + } + } + if err := st.CleanChunks(ctx, "uidc", "c.bin"); err != nil { + t.Fatalf("CleanChunks: %v", err) + } + if len(f.files) != 0 { + t.Fatalf("分片未清理: %v", f.files) + } + // 幂等 + if err := st.CleanChunks(ctx, "uidc", "c.bin"); err != nil { + t.Fatalf("CleanChunks idempotent: %v", err) + } + // 哈希不匹配 + if _, err := st.SaveChunk(ctx, "uidm", 0, strings.NewReader("real"), "m.bin"); err != nil { + t.Fatal(err) + } + if _, _, err := st.MergeChunks(ctx, "uidm", 1, func(i int) (string, error) { + return sha256Hex([]byte("wrong")), nil + }, "m.bin"); err == nil || !strings.Contains(err.Error(), ErrHashMismatch.Error()) { + t.Fatalf("want ErrHashMismatch, got %v", err) + } +} + +// TestWebDAVTimeout 非流式操作超时:PROPFIND 响应慢于 Timeout(1s)→ context deadline exceeded。 +func TestWebDAVTimeout(t *testing.T) { + f := newFakeDav("basic") + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "PROPFIND" { + time.Sleep(1500 * time.Millisecond) // > Timeout 1s + } + f.ServeHTTP(w, r) + })) + defer srv.Close() + st, err := NewWebDAVStorage(WebDAVOptions{ + BaseURL: srv.URL, Username: f.username, Password: f.password, + RootPath: "r", MaxRetries: 0, Timeout: 1, BaseBackoff: 5, + }) + if err != nil { + t.Fatal(err) + } + start := time.Now() + err = st.HealthCheck(context.Background()) + if err == nil { + t.Fatalf("超时应报错") + } + if !strings.Contains(err.Error(), "context deadline exceeded") { + t.Fatalf("应为超时错误, got %v", err) + } + // 单次尝试 1s 超时 + 一次重试 ≈ 2s;若超时未生效会拖满 2×1.5s + if elapsed := time.Since(start); elapsed > 3500*time.Millisecond { + t.Fatalf("超时未生效(耗时 %v)", elapsed) + } +} + +// TestWebDAVPresignNotSupported 预签名 → ErrNotSupported。 +func TestWebDAVPresignNotSupported(t *testing.T) { + st, _, _ := newTestDav(t, "basic", nil) + ctx := context.Background() + if _, err := st.PresignGetURL(ctx, "x.bin", 60); err == nil || + !strings.Contains(err.Error(), ErrNotSupported.Error()) { + t.Fatalf("want ErrNotSupported, got %v", err) + } + if _, err := st.PresignPutURL(ctx, "x.bin", 60); err == nil || + !strings.Contains(err.Error(), ErrNotSupported.Error()) { + t.Fatalf("want ErrNotSupported, got %v", err) + } +} + +// TestWebDAVFactoryRegistry 工厂构造 + Digest 全链路。 +func TestWebDAVFactoryRegistry(t *testing.T) { + f := newFakeDav("digest") + srv := httptest.NewServer(f) + defer srv.Close() + prev := engineOptions.WebDAV + engineOptions.WebDAV = WebDAVOptions{ + BaseURL: srv.URL, Username: f.username, Password: f.password, + RootPath: "factory_root", MaxRetries: 3, BaseBackoff: 5, + } + defer func() { engineOptions.WebDAV = prev }() + st, err := NewEngine(context.Background(), "webdav") + if err != nil { + t.Fatalf("NewEngine(webdav): %v", err) + } + ctx := context.Background() + if err := st.HealthCheck(ctx); err != nil { + t.Fatalf("HealthCheck: %v", err) + } + if _, err := st.SaveFile(ctx, strings.NewReader("factory"), "f.txt"); err != nil { + t.Fatalf("SaveFile: %v", err) + } + dl, err := st.Open(ctx, "f.txt", nil) + if err != nil { + t.Fatalf("Open: %v", err) + } + got, _ := io.ReadAll(dl) + _ = dl.Close() + if string(got) != "factory" { + t.Fatalf("content = %q", got) + } +} diff --git a/server/web/dist/assets/AdminLayout-DJ2FR9f-.js b/server/web/dist/assets/AdminLayout-DJ2FR9f-.js new file mode 100644 index 0000000..3465e3b --- /dev/null +++ b/server/web/dist/assets/AdminLayout-DJ2FR9f-.js @@ -0,0 +1 @@ +import{d as p,u as v,i as h,I as g,c as w,G as s,b as e,t as n,f as t,A as d,w as k,F as y,O as r,q as m,B as R,o as V}from"./index-DYsKpclu.js";import{_ as x}from"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";import{u as A}from"./auth-B7MDTgxJ.js";import"./admin-DEOkRyTC.js";const B={class:"admin-shell"},C={class:"admin-aside"},b={class:"aside-title"},L=["aria-label"],N={class:"admin-main"},q=p({__name:"AdminLayout",setup(S){const{t:a}=v(),u=R(),o=A(),l=h();g(async()=>{o.isAuthed&&!o.checked&&await o.verify()});async function _(){await o.logout(),l.success(a("admin.nav.loggedOut")),u.replace({name:"admin-login"})}return(F,c)=>{const i=r("RouterLink"),f=r("RouterView");return V(),w(y,null,[s(x),e("div",B,[e("aside",C,[e("div",b,n(t(a)("admin.nav.title")),1),e("nav",{class:"aside-menu","aria-label":t(a)("admin.nav.menu")},[s(i,{to:{name:"admin-files"}},{default:d(()=>[m("📁 "+n(t(a)("admin.nav.files")),1)]),_:1}),s(i,{to:{name:"admin-audit"}},{default:d(()=>[m("🛡 "+n(t(a)("admin.nav.audit")),1)]),_:1}),s(i,{to:{name:"admin-settings"}},{default:d(()=>[m("⚙️ "+n(t(a)("admin.nav.settings")),1)]),_:1}),c[0]||(c[0]=e("div",{class:"aside-sep"},null,-1)),e("a",{href:"#",onClick:k(_,["prevent"])},"🚪 "+n(t(a)("admin.nav.logout")),1)],8,L)]),e("div",N,[s(f)])])],64)}}});export{q as default}; diff --git a/server/web/dist/assets/AuditView-BHN4e5xt.js b/server/web/dist/assets/AuditView-BHN4e5xt.js new file mode 100644 index 0000000..fd90743 --- /dev/null +++ b/server/web/dist/assets/AuditView-BHN4e5xt.js @@ -0,0 +1 @@ +import{d as U,u as I,i as M,I as S,c as u,b as t,t as a,f as l,C as r,Z as w,D as v,w as $,q as x,F as E,r as F,G as q,j as g,R as T,H as R,K as j,e as B,$ as H,s as _,o as c,_ as L}from"./index-DYsKpclu.js";import{e as O}from"./admin-DEOkRyTC.js";import{_ as G}from"./Pager.vue_vue_type_script_setup_true_lang-B3GeH0mY.js";const K={class:"toolbar"},P={class:"page-title"},Z={class:"page-sub"},J={value:""},Q={value:"upload"},W={value:"download"},X={value:""},Y={value:"success"},tt={value:"failed"},et={value:"denied"},at={class:"btn",type:"submit"},nt={key:0,class:"loading-block"},lt={key:1,class:"card empty"},st={key:2,class:"table-wrap"},it={class:"table"},ot=["title"],dt={class:"code-cell"},ut={class:"ip-cell"},ct=["title"],rt=U({__name:"AuditView",setup(mt){const{t:n}=I(),z=M(),p=g([]),b=g(0),f=g(!1),o=T({action:"",result:"",ip:"",start:"",end:""}),d=T({page:1,size:20});function h(s,i=!1){if(!s)return"";const e=new Date(s);return Number.isNaN(e.getTime())?"":(i&&e.setHours(23,59,59,999),e.toISOString())}async function m(){f.value=!0;try{const s=await O({page:d.page,size:d.size,action:o.action||void 0,result:o.result||void 0,ip:o.ip.trim()||void 0,startTime:h(o.start)||void 0,endTime:h(o.end,!0)||void 0});p.value=s.data,b.value=s.total}catch(s){z.error(s instanceof R?s.msg:n("admin.audit.loadFailed"))}finally{f.value=!1}}function D(){d.page=1,m()}function V(){o.action="",o.result="",o.ip="",o.start="",o.end="",d.page=1,m()}function k(s,i){d.page=s,d.size=i,m()}function A(s){return s==="upload"?n("admin.audit.actionUpload"):s==="download"?n("admin.audit.actionDownload"):s||"-"}function y(s){return s==="success"?{cls:"badge-success",text:n("common.success")}:s==="denied"?{cls:"badge-warn",text:n("common.denied")}:s==="failed"?{cls:"badge-danger",text:n("common.failed")}:{cls:"badge-muted",text:s||"-"}}function C(s){const i=[s.deviceOs,s.deviceBrowser].filter(Boolean),e=s.deviceType?` · ${s.deviceType}`:"";return i.length?i.join(" · ")+e:"-"}function N(s){const i=s.transferredBytes;if(i==null)return"-";const e=s.sizeBytes;return e!=null&&e!==i?`${_(i)} / ${_(e)}`:_(i)}return S(m),(s,i)=>(c(),u("div",null,[t("div",K,[t("div",null,[t("h2",P,a(l(n)("admin.audit.title")),1),t("p",Z,a(l(n)("admin.audit.subtitle")),1)])]),t("form",{class:"filter-bar",onSubmit:$(D,["prevent"])},[t("label",null,[t("span",null,a(l(n)("admin.audit.action")),1),r(t("select",{"onUpdate:modelValue":i[0]||(i[0]=e=>o.action=e),class:"select"},[t("option",J,a(l(n)("common.all")),1),t("option",Q,a(l(n)("admin.audit.actionUpload")),1),t("option",W,a(l(n)("admin.audit.actionDownload")),1)],512),[[w,o.action]])]),t("label",null,[t("span",null,a(l(n)("admin.audit.result")),1),r(t("select",{"onUpdate:modelValue":i[1]||(i[1]=e=>o.result=e),class:"select"},[t("option",X,a(l(n)("common.all")),1),t("option",Y,a(l(n)("common.success")),1),t("option",tt,a(l(n)("common.failed")),1),t("option",et,a(l(n)("common.denied")),1)],512),[[w,o.result]])]),t("label",null,[t("span",null,a(l(n)("admin.audit.filterIp")),1),r(t("input",{"onUpdate:modelValue":i[2]||(i[2]=e=>o.ip=e),class:"input",placeholder:"10.0.0.1",style:{"max-width":"150px"}},null,512),[[v,o.ip]])]),t("label",null,[t("span",null,a(l(n)("admin.audit.filterStart")),1),r(t("input",{"onUpdate:modelValue":i[3]||(i[3]=e=>o.start=e),class:"input",type:"datetime-local"},null,512),[[v,o.start]])]),t("label",null,[t("span",null,a(l(n)("admin.audit.filterEnd")),1),r(t("input",{"onUpdate:modelValue":i[4]||(i[4]=e=>o.end=e),class:"input",type:"datetime-local"},null,512),[[v,o.end]])]),t("button",at,a(l(n)("common.query")),1),t("button",{class:"btn btn-ghost",type:"button",onClick:V},a(l(n)("common.reset")),1)],32),f.value?(c(),u("div",nt,[i[5]||(i[5]=t("span",{class:"spin","aria-hidden":"true"},null,-1)),x(" "+a(l(n)("common.loading")),1)])):p.value.length?(c(),u("div",st,[t("table",it,[t("thead",null,[t("tr",null,[t("th",null,a(l(n)("admin.audit.colTime")),1),t("th",null,a(l(n)("admin.audit.colAction")),1),t("th",null,a(l(n)("admin.audit.colResult")),1),t("th",null,a(l(n)("admin.audit.colFile")),1),t("th",null,a(l(n)("admin.audit.colCode")),1),t("th",null,a(l(n)("admin.audit.colBytes")),1),t("th",null,a(l(n)("admin.audit.colIp")),1),t("th",null,a(l(n)("admin.audit.colDevice")),1),t("th",null,a(l(n)("admin.audit.colDuration")),1),t("th",null,a(l(n)("admin.audit.colUaError")),1)])]),t("tbody",null,[(c(!0),u(E,null,F(p.value,e=>(c(),u("tr",{key:e.id},[t("td",null,a(l(j)(e.createdAt)),1),t("td",null,[t("span",{class:B(["badge",e.action==="upload"?"":"badge-muted"])},a(A(e.action)),3)]),t("td",null,[t("span",{class:B(["badge",y(e.result).cls])},a(y(e.result).text),3)]),t("td",{class:"wrap",title:e.fileName},a(e.fileName||"-"),9,ot),t("td",dt,a(e.fileCode||"-"),1),t("td",null,a(N(e)),1),t("td",ut,a(e.ip||"-"),1),t("td",null,a(C(e)),1),t("td",null,a(l(H)(e.durationMs)),1),t("td",{class:"wrap ua-cell",title:e.errorMsg||e.userAgent},a(e.errorMsg||e.userAgent||"-"),9,ct)]))),128))])])])):(c(),u("div",lt,[i[6]||(i[6]=t("div",{class:"empty-icon"},"🛡",-1)),x(" "+a(l(n)("admin.audit.empty")),1)])),q(G,{page:d.page,size:d.size,total:b.value,onChange:k},null,8,["page","size","total"])]))}}),gt=L(rt,[["__scopeId","data-v-dcdef1a5"]]);export{gt as default}; diff --git a/server/web/dist/assets/AuditView-f2PBwbQe.css b/server/web/dist/assets/AuditView-f2PBwbQe.css new file mode 100644 index 0000000..29e8225 --- /dev/null +++ b/server/web/dist/assets/AuditView-f2PBwbQe.css @@ -0,0 +1 @@ +.filter-bar[data-v-dcdef1a5]{display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;background:var(--glass-bg-soft);border:1px solid var(--glass-border);border-radius:var(--radius);padding:14px 16px;margin-bottom:16px}.filter-bar label[data-v-dcdef1a5]{display:flex;flex-direction:column;gap:4px;font-size:12.5px;color:var(--c-text-2);font-weight:600}.filter-bar .select[data-v-dcdef1a5],.filter-bar .input[data-v-dcdef1a5]{min-width:130px}.ip-cell[data-v-dcdef1a5]{font-family:var(--mono);font-size:12.5px}.ua-cell[data-v-dcdef1a5]{max-width:260px;overflow:hidden;text-overflow:ellipsis;color:var(--c-text-3)} diff --git a/server/web/dist/assets/DocsView-Ds78oqBY.js b/server/web/dist/assets/DocsView-Ds78oqBY.js new file mode 100644 index 0000000..8339951 --- /dev/null +++ b/server/web/dist/assets/DocsView-Ds78oqBY.js @@ -0,0 +1 @@ +import{d as R,u as V,i as z,j as y,I,J as $,z as E,A as O,N as W,b as c,C as j,D as G,f as h,c as l,q as x,t as u,F as C,r as A,w as T,e as M,g as S,h as H,B as K,o as r}from"./index-DYsKpclu.js";import{P as U}from"./PageShell-D87JkPkG.js";import{d as B,a as J,l as Q}from"./docsSource-BXkkn0HE.js";import{k as X}from"./markdown-B5D8JARp.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";function Y(m,o){const n=m.toLowerCase().replace(/[^\p{L}\p{N}\s-]/gu,"").trim().replace(/\s+/g,"-")||"section";let d=n,i=2;for(;o.has(d);)d=`${n}-${i++}`;return o.add(d),d}const Z=new Set(["script","style","iframe","object","embed","form","link","meta","base","svg","math","frame","frameset","applet","template","noscript","title"]),tt=new Set(["srcdoc","sandbox","formaction","action","xlink:href","srcset","poster","background","dynsrc","lowsrc","data"]);function et(m,o){const n=m.trim();return o&&/^data:image\//i.test(n)?!0:/^(https?:|mailto:|\/|#|\.\/)/i.test(n)||!/^[a-z][a-z0-9+.-]*:/i.test(n)}function st(m){const o=new DOMParser().parseFromString(m,"text/html");for(const n of[...o.body.querySelectorAll("*")]){const d=n.tagName.toLowerCase();if(Z.has(d)){n.remove();continue}for(const i of[...n.attributes]){const e=i.name.toLowerCase();if(e.startsWith("on")||tt.has(e)){n.removeAttribute(i.name);continue}(e==="href"||e==="src"||e.endsWith(":src")||e.endsWith(":href"))&&!et(i.value,e==="src")&&n.removeAttribute(i.name)}}return o.body.innerHTML}function ot(m){const o=X.parse(m,{gfm:!0,breaks:!1}),n=new DOMParser().parseFromString(o,"text/html");for(const e of[...n.body.querySelectorAll("a[href]")]){const f=e.getAttribute("href")??"";/^https?:\/\//i.test(f)&&(e.setAttribute("target","_blank"),e.setAttribute("rel","noopener noreferrer"))}const d=new Set,i=[];for(const e of[...n.body.querySelectorAll("h1, h2, h3")]){const f=Number(e.tagName.substring(1)),p=(e.textContent??"").trim();if(!p)continue;const v=Y(p,d);e.setAttribute("id",v),f>=2&&i.push({id:v,text:p,level:f})}return{html:st(n.body.innerHTML),toc:i}}const nt={class:"docs-shell"},at={class:"docs-sidebar"},rt=["placeholder"],lt={key:0,class:"empty",style:{padding:"20px 8px"}},ct={class:"hint"},it=["aria-label"],ut=["onClick"],dt={key:0,class:"badge badge-muted",style:{"margin-left":"6px"}},ht={key:0,class:"hint",style:{padding:"0 11px"}},mt={key:1,class:"doc-toc"},ft={class:"toc-title"},pt=["href","onClick"],vt={class:"docs-content"},gt={key:0,class:"loading-block"},yt={key:1,class:"empty"},_t={style:{"font-weight":"600",color:"var(--c-text)"}},bt={class:"hint",style:{"max-width":"420px",margin:"0 auto"}},kt={key:2,class:"empty"},wt=["innerHTML"],At=R({__name:"DocsView",setup(m){const{t:o}=V(),n=W(),d=K(),i=z(),e=y([...B]),f=y(""),p=y(""),v=y(""),_=y([]),k=y(!1),b=y("");I(async()=>{const a=await J();a.length&&(e.value=[...B,...a]),L()});const N=()=>{if(typeof n.params.slug=="string")return n.params.slug;const a=n.query.p;return typeof a=="string"?a:""};async function L(){const a=N(),s=e.value;if(!s.length)return;const t=s.find(g=>g.slug===a)??s[0];if(t&&!(t.slug===f.value&&p.value)){f.value=t.slug,k.value=!0;try{p.value=await Q(t);const{html:g,toc:P}=ot(p.value);v.value=g,_.value=P}catch{v.value="",_.value=[],i.error(o("docs.loadFailed",{title:t.title}))}finally{k.value=!1}}}$(()=>n.fullPath,()=>{L()});function F(a){d.push({name:"docs-detail",params:{slug:a}})}function q(a){document.getElementById(a)?.scrollIntoView({behavior:"smooth"})}const D=H(()=>{const a=b.value.trim().toLowerCase();return a?e.value.map(s=>{const g=(s.embedded??"").toLowerCase().split(a).length-1+(s.title.toLowerCase().includes(a)?1:0);return{doc:s,hits:g}}).filter(s=>s.hits>0).sort((s,t)=>t.hits-s.hits):e.value.map(s=>({doc:s,hits:-1}))}),w=H(()=>b.value.trim().length>0);return(a,s)=>(r(),E(U,null,{default:O(()=>[c("div",nt,[c("aside",at,[j(c("input",{"onUpdate:modelValue":s[0]||(s[0]=t=>b.value=t),class:"input docs-search",placeholder:h(o)("docs.searchPlaceholder")},null,8,rt),[[G,b.value]]),e.value.length?(r(),l(C,{key:1},[c("nav",{class:"doc-list","aria-label":h(o)("docs.sidebar")},[(r(!0),l(C,null,A(D.value,t=>(r(),l("a",{key:t.doc.slug,class:M({active:t.doc.slug===f.value}),href:"#",onClick:T(g=>F(t.doc.slug),["prevent"])},[x(u(t.doc.title)+" ",1),w.value&&t.hits>0?(r(),l("span",dt,u(t.hits),1)):S("",!0)],10,ut))),128))],8,it),w.value&&!D.value.length?(r(),l("p",ht,u(h(o)("docs.noMatch")),1)):S("",!0),_.value.length&&!w.value?(r(),l("div",mt,[c("div",ft,u(h(o)("docs.tocTitle")),1),(r(!0),l(C,null,A(_.value,t=>(r(),l("a",{key:t.id,href:`#${t.id}`,class:M({"lvl-3":t.level>=3}),onClick:T(g=>q(t.id),["prevent"])},u(t.text),11,pt))),128))])):S("",!0)],64)):(r(),l("div",lt,[x(u(h(o)("docs.notGenerated")),1),s[1]||(s[1]=c("br",null,null,-1)),c("span",ct,u(h(o)("docs.buildHint")),1)]))]),c("article",vt,[k.value?(r(),l("div",gt,[s[2]||(s[2]=c("span",{class:"spin","aria-hidden":"true"},null,-1)),x(" "+u(h(o)("docs.loading")),1)])):e.value.length?v.value?(r(),l("div",{key:3,class:"markdown-body",innerHTML:v.value},null,8,wt)):(r(),l("div",kt,u(h(o)("docs.emptyContent")),1)):(r(),l("div",yt,[s[3]||(s[3]=c("div",{class:"empty-icon"},"📚",-1)),c("p",_t,u(h(o)("docs.preparing")),1),c("p",bt,u(h(o)("docs.preparingHint")),1)]))])])]),_:1}))}});export{At as default}; diff --git a/server/web/dist/assets/FilesView-ZJ5eqPp5.js b/server/web/dist/assets/FilesView-ZJ5eqPp5.js new file mode 100644 index 0000000..0af9a57 --- /dev/null +++ b/server/web/dist/assets/FilesView-ZJ5eqPp5.js @@ -0,0 +1 @@ +import{d as N,I as V,P as Y,z as J,c as f,w as $,b as e,t as a,Q as X,n as Z,g as L,T as ee,o as p,u as te,a as ne,i as ie,f as l,C as S,D as T,q as z,F as se,r as le,G as D,A as ae,j as _,R as A,H as x,e as B,s as oe,K as E,k as w,p as de,M as ce}from"./index-DYsKpclu.js";import{a as ue,b as re,c as me,d as pe}from"./admin-DEOkRyTC.js";import{p as fe}from"./share-CqDDLJWI.js";import{_ as he}from"./Pager.vue_vue_type_script_setup_true_lang-B3GeH0mY.js";const be=["aria-label"],ve={class:"modal-title"},ge=N({__name:"AppModal",props:{open:{type:Boolean},title:{},width:{}},emits:["close"],setup(h,{emit:t}){const k=h,d=t;function r(b){b.key==="Escape"&&k.open&&d("close")}return V(()=>document.addEventListener("keydown",r)),Y(()=>document.removeEventListener("keydown",r)),(b,g)=>(p(),J(ee,{to:"body"},[h.open?(p(),f("div",{key:0,class:"modal-overlay",onClick:g[0]||(g[0]=$(m=>d("close"),["self"]))},[e("div",{class:"modal",style:Z(h.width?{maxWidth:h.width}:void 0),role:"dialog","aria-modal":"true","aria-label":h.title},[e("h3",ve,a(h.title),1),X(b.$slots,"default")],12,be)])):L("",!0)]))}}),ye={class:"toolbar"},_e={class:"page-title"},xe={class:"page-sub"},ke=["placeholder"],Ce={class:"btn",type:"submit"},Se=["disabled","title"],Te={key:0,class:"loading-block"},$e={key:1,class:"card empty"},Fe={key:2,class:"table-wrap"},ze={class:"table"},De={style:{width:"36px"}},Ae=["checked"],Be={style:{"min-width":"210px"}},Ee=["checked","onChange"],we={class:"code-cell"},Ne=["title"],Ve={class:"row-actions"},Le=["onClick"],Me=["onClick"],Ue=["onClick"],Ie=["onClick"],Re=["onClick"],He={class:"field"},Pe={class:"field"},Oe={class:"field"},je=["value"],qe={class:"modal-actions"},Ke=["disabled"],Je=N({__name:"FilesView",setup(h){const{t}=te(),k=ne(),d=ie(),r=_([]),b=_(0),g=_(!1),m=A({page:1,size:10,keyword:""}),c=_(new Set);async function v(){g.value=!0;try{const n=await ue({...m});r.value=n.data,b.value=n.total,c.value=new Set}catch(n){d.error(n instanceof x?n.msg:t("admin.files.loadFailed"))}finally{g.value=!1}}function M(){m.page=1,v()}function U(n,i){m.page=n,m.size=i,v()}function I(n){const i=new Set(c.value);i.has(n)?i.delete(n):i.add(n),c.value=i}const F=()=>r.value.length>0&&r.value.every(n=>c.value.has(n.id));function R(){c.value=F()?new Set:new Set(r.value.map(n=>n.id))}async function H(n){if(window.confirm(t("admin.files.confirmDelete",{name:n.name||n.code})))try{await me(n.id),d.success(t("admin.files.deleteSuccess")),v()}catch(i){d.error(i instanceof x?i.msg:t("admin.files.deleteFailed"))}}async function P(){if(c.value.size&&window.confirm(t("admin.files.confirmBatchDelete",{count:c.value.size})))try{await re([...c.value]),d.success(t("admin.files.batchDeleteSuccess")),v()}catch(n){d.error(n instanceof x?n.msg:t("admin.files.batchDeleteFailed"))}}const y=_(!1),C=_(!1),o=A({id:0,code:"",expired_at:"",expired_count:null,original:null});function O(n){o.id=n.id,o.code=n.code,o.original=n,o.expired_at=n.expiredAt?j(n.expiredAt):"",o.expired_count=n.expiredCount,y.value=!0}function j(n){const i=new Date(n);if(Number.isNaN(i.getTime()))return"";const s=u=>`${u}`.padStart(2,"0");return`${i.getFullYear()}-${s(i.getMonth()+1)}-${s(i.getDate())}T${s(i.getHours())}:${s(i.getMinutes())}`}async function q(){const n=o.original;if(!n)return;const i={id:o.id};o.code.trim()&&o.code.trim()!==n.code&&(i.code=o.code.trim());const s=o.expired_count;if(s!==null&&s!==n.expiredCount&&(i.expired_count=s),o.expired_at){const u=new Date(o.expired_at).toISOString();u!==n.expiredAt&&(i.expired_at=u)}if(Object.keys(i).length===1){d.info(t("admin.files.nothingChanged")),y.value=!1;return}C.value=!0;try{await pe(i),d.success(t("admin.files.updateSuccess")),y.value=!1,v()}catch(u){d.error(u instanceof x?u.msg:t("admin.files.updateFailed"))}finally{C.value=!1}}async function K(n){await w(de(n.code,k.shareLinkBase))?d.success(t("admin.files.linkCopied")):d.error(t("common.copyFailed"))}async function W(n){await w(n.code)?d.success(t("admin.files.codeCopied")):d.error(t("common.copyFailed"))}async function G(n){try{const i=await fe(n.code);ce(new Blob([i],{type:"text/plain;charset=utf-8"}),n.name||`${n.code}.txt`)}catch(i){d.error(i instanceof x?i.msg:t("admin.files.fetchTextFailed"))}}function Q(n){return n.expiredCount===null||n.expiredCount<0?t("admin.files.remainingUnlimited"):t("admin.files.remainingCount",{n:n.expiredCount})}return V(v),(n,i)=>(p(),f("div",null,[e("div",ye,[e("div",null,[e("h2",_e,a(l(t)("admin.files.title")),1),e("p",xe,a(l(t)("admin.files.totalRecords",{total:b.value})),1)]),e("form",{class:"toolbar-actions",onSubmit:$(M,["prevent"])},[S(e("input",{"onUpdate:modelValue":i[0]||(i[0]=s=>m.keyword=s),class:"input",placeholder:l(t)("admin.files.searchPlaceholder"),style:{"max-width":"220px"}},null,8,ke),[[T,m.keyword]]),e("button",Ce,a(l(t)("common.search")),1),e("button",{class:"btn btn-ghost",type:"button",onClick:v},a(l(t)("common.refresh")),1),e("button",{class:"btn btn-danger-ghost",type:"button",disabled:!c.value.size,title:c.value.size?l(t)("admin.files.deleteSelectedTitle",{count:c.value.size}):l(t)("admin.files.selectFirst"),onClick:P},a(c.value.size?l(t)("admin.files.batchDeleteWithCount",{count:c.value.size}):l(t)("admin.files.batchDelete")),9,Se)],32)]),g.value?(p(),f("div",Te,[i[6]||(i[6]=e("span",{class:"spin","aria-hidden":"true"},null,-1)),z(" "+a(l(t)("admin.files.loading")),1)])):r.value.length?(p(),f("div",Fe,[e("table",ze,[e("thead",null,[e("tr",null,[e("th",De,[e("input",{type:"checkbox",checked:F(),onChange:R},null,40,Ae)]),e("th",null,a(l(t)("admin.files.colCode")),1),e("th",null,a(l(t)("admin.files.colName")),1),e("th",null,a(l(t)("admin.files.colType")),1),e("th",null,a(l(t)("admin.files.colSize")),1),e("th",null,a(l(t)("admin.files.colUsed")),1),e("th",null,a(l(t)("admin.files.colRemaining")),1),e("th",null,a(l(t)("admin.files.colExpireAt")),1),e("th",null,a(l(t)("admin.files.colStatus")),1),e("th",null,a(l(t)("admin.files.colCreatedAt")),1),e("th",Be,a(l(t)("common.actions")),1)])]),e("tbody",null,[(p(!0),f(se,null,le(r.value,s=>(p(),f("tr",{key:s.id},[e("td",null,[e("input",{type:"checkbox",checked:c.value.has(s.id),onChange:u=>I(s.id)},null,40,Ee)]),e("td",we,a(s.code),1),e("td",{class:"wrap",title:s.name},a(s.name||"-"),9,Ne),e("td",null,[e("span",{class:B(["badge",s.isText?"badge-muted":""])},a(s.isText?l(t)("common.text"):l(t)("common.file")),3)]),e("td",null,a(s.isText?"-":l(oe)(s.size)),1),e("td",null,a(s.usedCount),1),e("td",null,a(Q(s)),1),e("td",null,a(s.expiredAt?l(E)(s.expiredAt):l(t)("time.permanent")),1),e("td",null,[e("span",{class:B(["badge",s.isExpired?"badge-danger":"badge-success"])},a(s.isExpired?l(t)("admin.files.statusExpired"):l(t)("admin.files.statusValid")),3)]),e("td",null,a(l(E)(s.createdAt)),1),e("td",null,[e("div",Ve,[e("button",{class:"btn btn-ghost btn-sm",type:"button",onClick:u=>W(s)},a(l(t)("admin.files.copyCode")),9,Le),e("button",{class:"btn btn-ghost btn-sm",type:"button",onClick:u=>K(s)},a(l(t)("admin.files.copyLink")),9,Me),e("button",{class:"btn btn-ghost btn-sm",type:"button",onClick:u=>O(s)},a(l(t)("admin.files.edit")),9,Ue),s.isText?(p(),f("button",{key:0,class:"btn btn-ghost btn-sm",type:"button",onClick:u=>G(s)},a(l(t)("admin.files.fetchText")),9,Ie)):L("",!0),e("button",{class:"btn btn-danger-ghost btn-sm",type:"button",onClick:u=>H(s)},a(l(t)("admin.files.delete")),9,Re)])])]))),128))])])])):(p(),f("div",$e,[i[7]||(i[7]=e("div",{class:"empty-icon"},"🗂",-1)),z(" "+a(l(t)("admin.files.empty")),1)])),D(he,{page:m.page,size:m.size,total:b.value,onChange:U},null,8,["page","size","total"]),D(ge,{open:y.value,title:l(t)("admin.files.editModalTitle"),onClose:i[5]||(i[5]=s=>y.value=!1)},{default:ae(()=>[e("form",{onSubmit:$(q,["prevent"])},[e("div",He,[e("label",null,a(l(t)("admin.files.colCode")),1),S(e("input",{"onUpdate:modelValue":i[1]||(i[1]=s=>o.code=s),class:"input input-mono",maxlength:"32"},null,512),[[T,o.code]])]),e("div",Pe,[e("label",null,a(l(t)("admin.files.expireAtHint")),1),S(e("input",{"onUpdate:modelValue":i[2]||(i[2]=s=>o.expired_at=s),class:"input",type:"datetime-local"},null,512),[[T,o.expired_at]])]),e("div",Oe,[e("label",null,a(l(t)("admin.files.expireCountHint")),1),e("input",{class:"input",type:"number",value:o.expired_count??-1,onInput:i[3]||(i[3]=s=>o.expired_count=Number(s.target.value))},null,40,je)]),e("div",qe,[e("button",{class:"btn btn-ghost",type:"button",onClick:i[4]||(i[4]=s=>y.value=!1)},a(l(t)("common.cancel")),1),e("button",{class:"btn",type:"submit",disabled:C.value},a(l(t)("common.save")),9,Ke)])],32)]),_:1},8,["open","title"])]))}});export{Je as default}; diff --git a/server/web/dist/assets/HomeView-D38U_AAJ.js b/server/web/dist/assets/HomeView-D38U_AAJ.js new file mode 100644 index 0000000..ee841dd --- /dev/null +++ b/server/web/dist/assets/HomeView-D38U_AAJ.js @@ -0,0 +1 @@ +import{d as W,u as Y,a as G,o as m,c as h,b as t,n as L,e as K,t as i,f as n,g as B,F as H,r as ne,h as $,E as q,_ as Z,i as ee,j as w,k as le,p as ae,l as se,w as O,m as ie,q as P,s as R,v as I,x as A,y as ue,z as re,A as ce,B as de,C as N,D as j,G as X,H as ve}from"./index-DYsKpclu.js";import{P as pe}from"./PageShell-D87JkPkG.js";import{s as me,a as he}from"./share-CqDDLJWI.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";const fe={class:"field-row"},ye={class:"field-sub"},xe=["max","value"],be={class:"field-sub"},ke=["value"],_e=["value"],Se={key:0,class:"hint",style:{color:"var(--c-warn)"}},ge={key:1,class:"hint"},Ce={key:2,class:"hint"},$e={key:3,class:"hint"},ze=W({__name:"ExpirePicker",props:{value:{},style:{},compact:{type:Boolean}},emits:["update:value","update:style"],setup(a,{emit:e}){const o=a,c=e,{t:r,te:C}=Y(),f=G(),z=$(()=>{const g=f.expireStyle.length?f.expireStyle:["day","hour","minute","forever","count"],d=q.filter(x=>g.includes(x.value)),v=g.filter(x=>!q.some(E=>E.value===x)).map(x=>({value:x,label:x}));return[...d,...v]}),S=$(()=>o.style==="forever"),y=$(()=>o.style==="count"),p=$(()=>f.maxSaveCount>0?f.maxSaveCount:9999),b=$(()=>f.maxSaveSeconds>0?f.maxSaveSeconds:0),V={day:86400,hour:3600,minute:60};function u(g){const d=b.value,v=V[g];return v?d<=0?9999:Math.max(1,Math.floor(d/v)):g==="count"?p.value:9999}const l=$(()=>{if(y.value&&f.maxSaveCount>0&&o.value>f.maxSaveCount)return r("expire.maxCountHint",{n:f.maxSaveCount});if(!S.value&&!y.value&&b.value>0){const g=u(o.style);if(o.value>g){const d=q.find(v=>v.value===o.style)?.label??o.style;return r("expire.maxSecondsHint",{value:`${g} ${d}`})}}return""}),T=$(()=>q.find(g=>g.value===o.style)?.label??o.style);function M(g,d){const v=`expireStyle.${g}`;return C(v)?r(v):d}function F(g){const d=g.target.value;c("update:style",d),d==="count"&&o.value>p.value&&c("update:value",1),d==="minute"&&o.value<1&&c("update:value",10)}return(g,d)=>(m(),h(H,null,[t("div",fe,[S.value?B("",!0):(m(),h("label",{key:0,class:K(["expire-value",{compact:a.compact}]),style:L(a.compact?"flex:0 0 110px":"")},[t("span",ye,i(n(r)("expire.value")),1),t("input",{class:"input",type:"number",min:1,max:u(a.style),value:a.value,onInput:d[0]||(d[0]=v=>c("update:value",Math.max(1,Number(v.target.value)||1)))},null,40,xe)],6)),t("label",{style:L(S.value?"flex:1":"")},[t("span",be,i(n(r)("expire.label")),1),t("select",{class:"select",value:a.style,onChange:F},[(m(!0),h(H,null,ne(z.value,v=>(m(),h("option",{key:v.value,value:v.value},i(S.value&&v.value==="forever"?n(r)("expire.foreverOption"):v.value==="count"?n(r)("expire.countOption"):M(v.value,v.label)),9,_e))),128))],40,ke)],4)]),l.value?(m(),h("p",Se,i(l.value),1)):a.style==="count"?(m(),h("p",ge,i(n(r)("expire.countHint")),1)):S.value?(m(),h("p",$e,i(n(r)("expire.foreverHint")),1)):(m(),h("p",Ce,i(n(r)("expire.timeHint",{value:a.value,unit:T.value})),1))],64))}}),Q=Z(ze,[["__scopeId","data-v-d049fcf9"]]),we={class:"card result-card"},Te={class:"result-head"},Ve={class:"badge badge-success"},Ee={key:0,class:"result-name"},Me={class:"field"},Be=["title"],De={class:"field"},Fe={class:"link-row"},Pe=["value"],Ue={class:"result-meta"},Le={key:0},He={class:"hint",style:{"margin-top":"10px"}},Oe=W({__name:"ResultCard",props:{code:{},name:{},expireValue:{},expireStyle:{}},setup(a){const e=a,{t:o}=Y(),c=ee(),r=G(),C=w(null),f=$(()=>ae(e.code,r.shareLinkBase));async function z(y){const p=y==="link"?f.value:e.code;await le(p)?(C.value=y,c.success(o(y==="link"?"result.linkCopied":"result.codeCopied")),setTimeout(()=>C.value=null,1600)):c.error(o("result.copyFailed"))}const S=$(()=>e.expireStyle==="forever"?o("result.forever"):`${e.expireValue??"-"} ${se(e.expireStyle??"")}`);return(y,p)=>(m(),h("div",we,[t("div",Te,[t("span",Ve,i(n(o)("result.badge")),1),a.name?(m(),h("span",Ee,i(a.name),1)):B("",!0)]),t("div",Me,[t("label",null,i(n(o)("result.code")),1),t("button",{class:"code-display code-copy",type:"button",title:n(o)("result.clickCopyCode"),onClick:p[0]||(p[0]=b=>z("code"))},i(a.code),9,Be)]),t("div",De,[t("label",null,i(n(o)("result.link")),1),t("div",Fe,[t("input",{class:"input input-mono",value:f.value,readonly:"",onFocus:p[1]||(p[1]=b=>b.target.select())},null,40,Pe),t("button",{class:"btn btn-ghost",type:"button",onClick:p[2]||(p[2]=b=>z("link"))},i(C.value==="link"?n(o)("common.copied"):n(o)("result.copyLink")),1)])]),t("div",Ue,[a.expireStyle?(m(),h("span",Le,i(n(o)("result.expires",{value:S.value})),1)):B("",!0)]),t("p",He,i(n(o)("result.hint")),1)]))}}),Re=Z(Oe,[["__scopeId","data-v-8bebf3d9"]]),Ie=["aria-label"],Ae={class:"dz-main"},qe={class:"dz-sub"},Ne={key:1,class:"file-chip"},je={class:"fc-name"},Xe={class:"fc-size"},Ke=["title"],We={key:2,class:"hint",style:{color:"var(--c-danger)"}},Ye=["accept"],Ge=W({__name:"FileDrop",props:{modelValue:{},maxSize:{},disabled:{type:Boolean},acceptTypes:{}},emits:["update:modelValue"],setup(a,{emit:e}){const o=a,c=e,{t:r}=Y(),C=w(null),f=w(!1),z=w(""),S=$(()=>{const u=(o.acceptTypes??[]).map(l=>l.trim()).filter(Boolean);return!u.length||u.some(l=>l==="*"||l==="*/*")?"":u.map(l=>l.includes("/")||l.startsWith(".")?l:`.${l.toLowerCase()}`).join(",")}),y=$(()=>{const u=(o.acceptTypes??[]).filter(l=>l&&l!=="*"&&l!=="*/*");return u.length?r("drop.typeHint",{types:u.join(", ")}):""});function p(u){if(z.value="",!!u){if(o.maxSize&&u.size>o.maxSize){z.value=r("drop.tooLarge",{size:R(u.size),limit:R(o.maxSize)}),c("update:modelValue",null);return}c("update:modelValue",u)}}function b(u){f.value=!1,!o.disabled&&p(u.dataTransfer?.files?.[0])}function V(u){const l=u.target;p(l.files?.[0]),l.value=""}return(u,l)=>(m(),h("div",null,[a.modelValue?(m(),h("div",Ne,[l[6]||(l[6]=t("span",{"aria-hidden":"true"},"📄",-1)),t("span",je,i(a.modelValue.name),1),t("span",Xe,i(n(R)(a.modelValue.size)),1),t("button",{class:"fc-remove",type:"button",title:n(r)("drop.remove"),onClick:l[4]||(l[4]=T=>c("update:modelValue",null))},"✕",8,Ke)])):(m(),h("div",{key:0,class:K(["dropzone",{dragover:f.value,disabled:a.disabled}]),role:"button",tabindex:"0","aria-label":n(r)("drop.aria"),onClick:l[0]||(l[0]=T=>!a.disabled&&C.value?.click()),onKeydown:l[1]||(l[1]=ie(O(T=>!a.disabled&&C.value?.click(),["prevent"]),["enter"])),onDragover:l[2]||(l[2]=O(T=>f.value=!0,["prevent"])),onDragleave:l[3]||(l[3]=T=>f.value=!1),onDrop:O(b,["prevent"])},[l[5]||(l[5]=t("div",{class:"dz-icon","aria-hidden":"true"},"📦",-1)),t("div",Ae,i(n(r)("drop.zone")),1),t("div",qe,[a.maxSize?(m(),h(H,{key:0},[P(i(n(r)("drop.maxSize",{size:n(R)(a.maxSize)})),1)],64)):(m(),h(H,{key:1},[P(i(n(r)("drop.noLimit")),1)],64)),y.value?(m(),h(H,{key:2},[P(" · "+i(y.value),1)],64)):B("",!0)])],42,Ie)),z.value?(m(),h("p",We,i(z.value),1)):B("",!0),t("input",{ref_key:"inputRef",ref:C,type:"file",hidden:"",accept:S.value,onChange:V},null,40,Ye)]))}}),Ze=Z(Ge,[["__scopeId","data-v-1b57fe5c"]]);function Je(a,e,o,c){return I(A.chunkInit,{method:"POST",json:{file_name:a,file_size:e,chunk_size:o,file_hash:c},timeout:6e4})}async function Qe(a,e,o){const c=new FormData;c.append("upload_id",a),c.append("chunk_index",String(e)),c.append("chunk",o,`chunk-${e}`),await I(A.chunkUpload(a,e),{method:"POST",formData:c,timeout:12e4})}function et(a){return I(A.chunkStatus(a),{timeout:6e4})}function tt(a,e,o,c=""){return I(A.chunkFinish(a),{method:"POST",json:{expire_value:e,expire_style:o,code:c},timeout:3e5})}async function ot(a){await I(A.chunkCancel(a),{method:"DELETE",timeout:6e4})}function nt(a){return a<=10*1024*1024?{chunkSize:1*1024*1024,concurrency:3}:a<=100*1024*1024?{chunkSize:5*1024*1024,concurrency:3}:a<=512*1024*1024?{chunkSize:10*1024*1024,concurrency:2}:{chunkSize:20*1024*1024,concurrency:2}}function lt(a,e){const{file:o,expireValue:c,expireStyle:r,customCode:C,onProgress:f}=a;let z=!1;return{promise:(async()=>{const y=nt(o.size),p=Math.max(1,Math.ceil(o.size/y.chunkSize));let b;try{b=await ue(await o.arrayBuffer())}catch{b=`nofp-${o.size}-${o.lastModified}`}const V=await e.init(o.name,o.size,y.chunkSize,b),u=V.upload_id,l=V.chunk_size||y.chunkSize,T=V.total_chunks||p,M=new Set(V.uploaded_chunks??[]),F=()=>{if(!f)return;let x=0;for(const E of M){const D=E*l;x+=Math.max(0,Math.min(l,o.size-D))}f(Math.min(100,Math.round(x/o.size*100)),x,o.size)};F();const g=async x=>{for(let E=0;E<2;E++)try{const D=x*l,U=o.slice(D,Math.min(D+l,o.size));await e.uploadOne(u,x,U),M.add(x),F();return}catch(D){if(E===1)throw D}};let d=0;const v=async()=>{for(;!z;){const x=d++;if(x>=T)return;M.has(x)||await g(x)}};if(await Promise.all(Array.from({length:y.concurrency},()=>v())),z)throw await e.cancel(u).catch(()=>{}),new DOMException("上传已取消","AbortError");if(M.sizez=!0}}const at={class:"hero"},st=["placeholder"],it={class:"btn",type:"submit"},ut={class:"card share-card"},rt={class:"tabs",role:"tablist"},ct={class:"field"},dt=["placeholder"],vt={class:"field"},pt={for:"share-custom-code"},mt=["placeholder"],ht={class:"field"},ft=["disabled"],yt={key:0,class:"spin","aria-hidden":"true"},xt={class:"field"},bt={class:"field"},kt={class:"field"},_t={for:"share-custom-code-file"},St=["placeholder"],gt={key:0,class:"field"},Ct={class:"progress"},$t={class:"hint"},zt=["disabled"],wt={key:0,class:"spin","aria-hidden":"true"},Tt={key:1,class:"empty"},Vt={key:2,style:{"margin-top":"18px"}},Et=222*1024,Pt=W({__name:"HomeView",setup(a){const{t:e}=Y(),o=de(),c=G(),r=ee(),C=w("text"),f=w("");function z(){const k=f.value.trim();if(!k){r.error(e("home.pickupRequired"));return}o.push({name:"pickup",params:{code:k}})}const S=w(1),y=w("day"),p=w(!1),b=w(null);function V(k,s){return k instanceof ve?k.code===423?e("home.rateLimited"):k.code===428?e("home.notInitialized"):k.msg||s:s}const u=w(""),l=/^[A-Za-z0-9]{4,8}$/,T=w(""),M=$(()=>new TextEncoder().encode(T.value).length),F=$(()=>M.value>Et);async function g(){const k=T.value;if(!k.trim()){r.error(e("home.textRequired"));return}if(F.value){r.error(e("home.textTooLong"));return}if(u.value.trim()&&!l.test(u.value.trim())){r.error(e("home.customCodeInvalid"));return}p.value=!0;try{const s=await me(k,S.value,y.value,u.value.trim());b.value={code:s.code},r.success(e("home.textShared"))}catch(s){r.error(V(s,e("home.shareFailed")))}finally{p.value=!1}}const d=w(null),v=w(null),x=w(null),E=$(()=>c.openUpload),D=$(()=>!!(d.value&&c.enableChunk&&d.value.size>8*1024*1024)),U=$(()=>c.effectiveMaxFileSize);async function te(){if(!d.value){r.error(e("home.fileRequired"));return}if(U.value&&d.value.size>U.value){r.error(e("home.fileTooLarge",{size:R(U.value)}));return}p.value=!0,v.value=0,b.value=null;try{if(u.value.trim()&&!l.test(u.value.trim())){r.error(e("home.customCodeInvalid"));return}if(D.value){const k=lt({file:d.value,expireValue:S.value,expireStyle:y.value,customCode:u.value.trim(),onProgress:_=>v.value=_},{init:Je,uploadOne:Qe,status:et,finish:tt,cancel:ot});x.value=k;const s=await k.promise;b.value={code:s.code,name:s.name}}else{const k=await he(d.value,S.value,y.value,s=>v.value=s,u.value.trim());b.value={code:k.code,name:k.name||d.value.name}}r.success(e("home.fileShared")),d.value=null}catch(k){k instanceof DOMException&&k.name==="AbortError"?r.info(e("home.uploadCancelled")):r.error(V(k,e("home.uploadFailed")))}finally{p.value=!1,v.value=null,x.value=null}}function oe(){x.value?.cancel()}function J(k){C.value=k,b.value=null,v.value=null}return(k,s)=>(m(),re(pe,null,{default:ce(()=>[t("section",at,[t("h1",null,i(n(e)("home.heroTitle",{name:n(c).displayName})),1),t("p",null,i(n(c).description||n(e)("home.heroDesc")),1),t("form",{class:"quick-pickup",onSubmit:O(z,["prevent"])},[N(t("input",{"onUpdate:modelValue":s[0]||(s[0]=_=>f.value=_),class:"input",placeholder:n(e)("home.pickupPlaceholder"),maxlength:"32",autocomplete:"off"},null,8,st),[[j,f.value]]),t("button",it,i(n(e)("home.pickupButton")),1)],32)]),t("section",ut,[t("div",rt,[t("button",{class:K(["tab",{active:C.value==="text"}]),type:"button",role:"tab",onClick:s[1]||(s[1]=_=>J("text"))},i(n(e)("home.tabText")),3),t("button",{class:K(["tab",{active:C.value==="file"}]),type:"button",role:"tab",onClick:s[2]||(s[2]=_=>J("file"))},i(n(e)("home.tabFile")),3)]),C.value==="text"?(m(),h("form",{key:0,style:{"margin-top":"18px"},onSubmit:O(g,["prevent"])},[t("div",ct,[t("label",null,i(n(e)("home.textContent")),1),N(t("textarea",{"onUpdate:modelValue":s[3]||(s[3]=_=>T.value=_),class:"textarea",placeholder:n(e)("home.textPlaceholder"),spellcheck:"false"},null,8,dt),[[j,T.value]]),t("p",{class:"hint",style:L(F.value?"color: var(--c-danger)":"")},i(n(e)("home.textBytes",{bytes:M.value.toLocaleString()})),5)]),t("div",vt,[t("label",pt,i(n(e)("home.customCode")),1),N(t("input",{id:"share-custom-code","onUpdate:modelValue":s[4]||(s[4]=_=>u.value=_),class:"input input-mono",placeholder:n(e)("home.customCodeHint"),maxlength:"8",autocomplete:"off"},null,8,mt),[[j,u.value]])]),t("div",ht,[X(Q,{value:S.value,"onUpdate:value":s[5]||(s[5]=_=>S.value=_),style:L(y.value),"onUpdate:style":s[6]||(s[6]=_=>y.value=_)},null,8,["value","style"])]),t("button",{class:"btn btn-block",type:"submit",disabled:p.value||F.value},[p.value?(m(),h("span",yt)):B("",!0),P(" "+i(n(e)("home.generateCode")),1)],8,ft)],32)):(m(),h("form",{key:1,style:{"margin-top":"18px"},onSubmit:O(te,["prevent"])},[E.value?(m(),h(H,{key:0},[t("div",xt,[X(Ze,{modelValue:d.value,"onUpdate:modelValue":s[7]||(s[7]=_=>d.value=_),"max-size":U.value||void 0,"accept-types":n(c).allowedFileTypes,disabled:p.value},null,8,["modelValue","max-size","accept-types","disabled"])]),t("div",bt,[t("div",kt,[t("label",_t,i(n(e)("home.customCode")),1),N(t("input",{id:"share-custom-code-file","onUpdate:modelValue":s[8]||(s[8]=_=>u.value=_),class:"input input-mono",placeholder:n(e)("home.customCodeHint"),maxlength:"8",autocomplete:"off"},null,8,St),[[j,u.value]])]),X(Q,{value:S.value,"onUpdate:value":s[9]||(s[9]=_=>S.value=_),style:L(y.value),"onUpdate:style":s[10]||(s[10]=_=>y.value=_)},null,8,["value","style"])]),v.value!==null?(m(),h("div",gt,[t("div",Ct,[t("i",{style:L({width:`${v.value}%`})},null,4)]),t("p",$t,[P(i(D.value?n(e)("home.chunkedUploading"):n(e)("home.uploading"))+" "+i(v.value)+"% ",1),p.value?(m(),h("button",{key:0,class:"btn btn-ghost btn-sm",type:"button",style:{"margin-left":"8px"},onClick:oe},i(n(e)("common.cancel")),1)):B("",!0)])])):B("",!0),t("button",{class:"btn btn-block",type:"submit",disabled:p.value||!d.value},[p.value?(m(),h("span",wt)):B("",!0),P(" "+i(p.value?n(e)("home.uploadingDots"):n(e)("home.uploadAndShare")),1)],8,zt)],64)):(m(),h("div",Tt,[s[12]||(s[12]=t("div",{class:"empty-icon"},"🚫",-1)),P(" "+i(n(e)("home.uploadDisabled")),1)]))],32)),b.value?(m(),h("div",Vt,[X(Re,{code:b.value.code,name:b.value.name,"expire-value":S.value,"expire-style":y.value},null,8,["code","name","expire-value","expire-style"]),t("button",{class:"btn btn-ghost btn-block",type:"button",style:{"margin-top":"12px"},onClick:s[11]||(s[11]=_=>b.value=null)},i(n(e)("home.shareAnother")),1)])):B("",!0)])]),_:1}))}});export{Pt as default}; diff --git a/server/web/dist/assets/HomeView-DcN_X0wH.css b/server/web/dist/assets/HomeView-DcN_X0wH.css new file mode 100644 index 0000000..bced542 --- /dev/null +++ b/server/web/dist/assets/HomeView-DcN_X0wH.css @@ -0,0 +1 @@ +.field-sub[data-v-d049fcf9]{display:block;font-size:13px;font-weight:600;color:var(--c-text-2);margin-bottom:6px}.result-head[data-v-8bebf3d9]{display:flex;align-items:center;gap:10px;margin-bottom:14px}.result-name[data-v-8bebf3d9]{font-weight:600;word-break:break-all}.link-row[data-v-8bebf3d9]{display:flex;gap:8px}.link-row .input[data-v-8bebf3d9]{flex:1}.result-meta[data-v-8bebf3d9]{display:flex;align-items:center;gap:6px;color:var(--c-text-2);font-size:13px;flex-wrap:wrap}.code-copy[data-v-8bebf3d9]{display:block;width:100%;cursor:pointer;appearance:none;-webkit-appearance:none;transition:filter .15s}.code-copy[data-v-8bebf3d9]:hover{filter:brightness(.96)}.code-copy[data-v-8bebf3d9]:active{filter:brightness(.92)}.dropzone.disabled[data-v-1b57fe5c]{opacity:.55;cursor:not-allowed} diff --git a/server/web/dist/assets/LoginView-BgHqRIwi.css b/server/web/dist/assets/LoginView-BgHqRIwi.css new file mode 100644 index 0000000..732dd2b --- /dev/null +++ b/server/web/dist/assets/LoginView-BgHqRIwi.css @@ -0,0 +1 @@ +.login-head[data-v-26e7f2a4]{text-align:center;margin-bottom:18px}.login-logo[data-v-26e7f2a4]{width:48px;height:48px;object-fit:contain;margin-bottom:8px} diff --git a/server/web/dist/assets/LoginView-BnVXNxFO.js b/server/web/dist/assets/LoginView-BnVXNxFO.js new file mode 100644 index 0000000..f56ec7c --- /dev/null +++ b/server/web/dist/assets/LoginView-BnVXNxFO.js @@ -0,0 +1 @@ +import{d as w,u as b,a as y,z as x,A as k,b as e,f as t,t as a,w as V,C as B,D as C,c as m,g as _,q as N,j as r,N as S,B as q,o as d,H as L,_ as P}from"./index-DYsKpclu.js";import{P as A}from"./PageShell-D87JkPkG.js";import{u as D}from"./auth-B7MDTgxJ.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";import"./admin-DEOkRyTC.js";const E={class:"card",style:{"max-width":"380px",margin:"8vh auto 0"}},I={class:"login-head"},M=["src"],R={class:"card-title"},T={class:"card-sub"},U={class:"field"},j={for:"admin-password"},z=["placeholder"],H={key:0,class:"hint",style:{color:"var(--c-danger)","margin-bottom":"12px"}},F=["disabled"],G={key:0,class:"spin","aria-hidden":"true"},J={class:"hint",style:{"margin-top":"16px"}},K=w({__name:"LoginView",setup(O){const{t:s}=b(),c=S(),g=q(),h=D(),u=y(),i=r(""),l=r(!1),n=r("");async function f(){if(!i.value){n.value=s("admin.login.required");return}l.value=!0,n.value="";try{await h.login(i.value);const o=typeof c.query.redirect=="string"?c.query.redirect:"/admin/files";g.replace(o)}catch(o){n.value=o instanceof L?o.code===401?s("admin.login.wrongPassword"):o.msg:s("admin.login.failed"),i.value=""}finally{l.value=!1}}return(o,p)=>(d(),x(A,null,{default:k(()=>[e("section",E,[e("div",I,[e("img",{src:t(u).displayLogoUrl,alt:"Logo",class:"login-logo"},null,8,M),e("h1",R,a(t(s)("admin.login.title")),1),e("p",T,a(t(s)("admin.login.subtitle",{name:t(u).displayName})),1)]),e("form",{onSubmit:V(f,["prevent"])},[e("div",U,[e("label",j,a(t(s)("admin.login.password")),1),B(e("input",{id:"admin-password","onUpdate:modelValue":p[0]||(p[0]=v=>i.value=v),class:"input",type:"password",placeholder:t(s)("admin.login.passwordPlaceholder"),autocomplete:"current-password",autofocus:""},null,8,z),[[C,i.value]])]),n.value?(d(),m("p",H,a(n.value),1)):_("",!0),e("button",{class:"btn btn-block",type:"submit",disabled:l.value},[l.value?(d(),m("span",G)):_("",!0),N(" "+a(t(s)("admin.login.submit")),1)],8,F)],32),e("p",J,a(t(s)("admin.login.hint")),1)])]),_:1}))}}),$=P(K,[["__scopeId","data-v-26e7f2a4"]]);export{$ as default}; diff --git a/server/web/dist/assets/NotFoundView-CbfqSXyJ.js b/server/web/dist/assets/NotFoundView-CbfqSXyJ.js new file mode 100644 index 0000000..c8687d6 --- /dev/null +++ b/server/web/dist/assets/NotFoundView-CbfqSXyJ.js @@ -0,0 +1 @@ +import{d as c,u as i,z as d,A as a,b as t,t as e,f as s,G as l,q as p,O as u,o as _}from"./index-DYsKpclu.js";import{P as m}from"./PageShell-D87JkPkG.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";const f={class:"card empty",style:{"max-width":"480px",margin:"10vh auto 0"}},h={style:{"font-weight":"600",color:"var(--c-text)"}},x={class:"hint"},F=c({__name:"NotFoundView",setup(y){const{t:o}=i();return(g,n)=>{const r=u("RouterLink");return _(),d(m,null,{default:a(()=>[t("div",f,[n[0]||(n[0]=t("div",{class:"empty-icon"},"🧭",-1)),t("p",h,e(s(o)("notFound.title")),1),t("p",x,e(s(o)("notFound.desc")),1),l(r,{class:"btn",to:"/",style:{"margin-top":"10px"}},{default:a(()=>[p(e(s(o)("notFound.back")),1)]),_:1})])]),_:1})}}});export{F as default}; diff --git a/server/web/dist/assets/OpenApiView-4SbIV1Bx.js b/server/web/dist/assets/OpenApiView-4SbIV1Bx.js new file mode 100644 index 0000000..e34fb0d --- /dev/null +++ b/server/web/dist/assets/OpenApiView-4SbIV1Bx.js @@ -0,0 +1,267 @@ +import{d as jU,u as PU,I as NU,ax as Xw,z as TU,A as IU,j as rA,b as Lc,t as Km,f as Gm,e as MU,c as RU,g as DU,h as nA,o as aA,_ as FU}from"./index-DYsKpclu.js";import{P as LU}from"./PageShell-D87JkPkG.js";import{g as $U}from"./swagger-CqkleIqs.js";import{e as oA}from"./docsSource-BXkkn0HE.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";var Qw={exports:{}};var iA;function BU(){return iA||(iA=1,(()=>{var eE={67526(w,N){N.byteLength=function(_){var T=m(_),I=T[0],j=T[1];return 3*(I+j)/4-j},N.toByteArray=function(_){var T,I,j=m(_),M=j[0],z=j[1],K=new v((function(te,ie,se){return 3*(ie+se)/4-se})(0,M,z)),Y=0,B=z>0?M-4:M;for(I=0;I>16&255,K[Y++]=T>>8&255,K[Y++]=255&T;return z===2&&(T=h[_.charCodeAt(I)]<<2|h[_.charCodeAt(I+1)]>>4,K[Y++]=255&T),z===1&&(T=h[_.charCodeAt(I)]<<10|h[_.charCodeAt(I+1)]<<4|h[_.charCodeAt(I+2)]>>2,K[Y++]=T>>8&255,K[Y++]=255&T),K},N.fromByteArray=function(_){for(var T,I=_.length,j=I%3,M=[],z=16383,K=0,Y=I-j;KY?Y:K+z));return j===1?(T=_[I-1],M.push(s[T>>2]+s[T<<4&63]+"==")):j===2&&(T=(_[I-2]<<8)+_[I-1],M.push(s[T>>10]+s[T>>4&63]+s[T<<2&63]+"=")),M.join("")};for(var s=[],h=[],v=typeof Uint8Array<"u"?Uint8Array:Array,f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",y=0;y<64;++y)s[y]=f[y],h[f.charCodeAt(y)]=y;function m(S){var _=S.length;if(_%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var T=S.indexOf("=");return T===-1&&(T=_),[T,T===_?0:4-T%4]}function g(S,_,T){for(var I,j,M=[],z=_;z>18&63]+s[j>>12&63]+s[j>>6&63]+s[63&j]);return M.join("")}h[45]=62,h[95]=63},48287(w,N,s){const h=s(67526),v=s(251),f=typeof Symbol=="function"&&typeof Symbol.for=="function"?Symbol.for("nodejs.util.inspect.custom"):null;N.Buffer=g,N.SlowBuffer=function(U){return+U!=U&&(U=0),g.alloc(+U)},N.INSPECT_MAX_BYTES=50;const y=2147483647;function m(pe){if(pe>y)throw new RangeError('The value "'+pe+'" is invalid for option "size"');const U=new Uint8Array(pe);return Object.setPrototypeOf(U,g.prototype),U}function g(pe,U,Z){if(typeof pe=="number"){if(typeof U=="string")throw new TypeError('The "string" argument must be of type string. Received type number');return T(pe)}return S(pe,U,Z)}function S(pe,U,Z){if(typeof pe=="string")return(function(Fe,St){if(typeof St=="string"&&St!==""||(St="utf8"),!g.isEncoding(St))throw new TypeError("Unknown encoding: "+St);const Bt=0|z(Fe,St);let Qe=m(Bt);const $t=Qe.write(Fe,St);return $t!==Bt&&(Qe=Qe.slice(0,$t)),Qe})(pe,U);if(ArrayBuffer.isView(pe))return(function(Fe){if(Gt(Fe,Uint8Array)){const St=new Uint8Array(Fe);return j(St.buffer,St.byteOffset,St.byteLength)}return I(Fe)})(pe);if(pe==null)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof pe);if(Gt(pe,ArrayBuffer)||pe&&Gt(pe.buffer,ArrayBuffer)||typeof SharedArrayBuffer<"u"&&(Gt(pe,SharedArrayBuffer)||pe&&Gt(pe.buffer,SharedArrayBuffer)))return j(pe,U,Z);if(typeof pe=="number")throw new TypeError('The "value" argument must not be of type number. Received type number');const ne=pe.valueOf&&pe.valueOf();if(ne!=null&&ne!==pe)return g.from(ne,U,Z);const ye=(function(Fe){if(g.isBuffer(Fe)){const St=0|M(Fe.length),Bt=m(St);return Bt.length===0||Fe.copy(Bt,0,0,St),Bt}if(Fe.length!==void 0)return typeof Fe.length!="number"||Sr(Fe.length)?m(0):I(Fe);if(Fe.type==="Buffer"&&Array.isArray(Fe.data))return I(Fe.data)})(pe);if(ye)return ye;if(typeof Symbol<"u"&&Symbol.toPrimitive!=null&&typeof pe[Symbol.toPrimitive]=="function")return g.from(pe[Symbol.toPrimitive]("string"),U,Z);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof pe)}function _(pe){if(typeof pe!="number")throw new TypeError('"size" argument must be of type number');if(pe<0)throw new RangeError('The value "'+pe+'" is invalid for option "size"')}function T(pe){return _(pe),m(pe<0?0:0|M(pe))}function I(pe){const U=pe.length<0?0:0|M(pe.length),Z=m(U);for(let ne=0;ne=y)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+y.toString(16)+" bytes");return 0|pe}function z(pe,U){if(g.isBuffer(pe))return pe.length;if(ArrayBuffer.isView(pe)||Gt(pe,ArrayBuffer))return pe.byteLength;if(typeof pe!="string")throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof pe);const Z=pe.length,ne=arguments.length>2&&arguments[2]===!0;if(!ne&&Z===0)return 0;let ye=!1;for(;;)switch(U){case"ascii":case"latin1":case"binary":return Z;case"utf8":case"utf-8":return ur(pe).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*Z;case"hex":return Z>>>1;case"base64":return ar(pe).length;default:if(ye)return ne?-1:ur(pe).length;U=(""+U).toLowerCase(),ye=!0}}function K(pe,U,Z){let ne=!1;if((U===void 0||U<0)&&(U=0),U>this.length||((Z===void 0||Z>this.length)&&(Z=this.length),Z<=0)||(Z>>>=0)<=(U>>>=0))return"";for(pe||(pe="utf8");;)switch(pe){case"hex":return nt(this,U,Z);case"utf8":case"utf-8":return ke(this,U,Z);case"ascii":return He(this,U,Z);case"latin1":case"binary":return qe(this,U,Z);case"base64":return ge(this,U,Z);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return gt(this,U,Z);default:if(ne)throw new TypeError("Unknown encoding: "+pe);pe=(pe+"").toLowerCase(),ne=!0}}function Y(pe,U,Z){const ne=pe[U];pe[U]=pe[Z],pe[Z]=ne}function B(pe,U,Z,ne,ye){if(pe.length===0)return-1;if(typeof Z=="string"?(ne=Z,Z=0):Z>2147483647?Z=2147483647:Z<-2147483648&&(Z=-2147483648),Sr(Z=+Z)&&(Z=ye?0:pe.length-1),Z<0&&(Z=pe.length+Z),Z>=pe.length){if(ye)return-1;Z=pe.length-1}else if(Z<0){if(!ye)return-1;Z=0}if(typeof U=="string"&&(U=g.from(U,ne)),g.isBuffer(U))return U.length===0?-1:X(pe,U,Z,ne,ye);if(typeof U=="number")return U&=255,typeof Uint8Array.prototype.indexOf=="function"?ye?Uint8Array.prototype.indexOf.call(pe,U,Z):Uint8Array.prototype.lastIndexOf.call(pe,U,Z):X(pe,[U],Z,ne,ye);throw new TypeError("val must be string, number or Buffer")}function X(pe,U,Z,ne,ye){let Ee,Fe=1,St=pe.length,Bt=U.length;if(ne!==void 0&&((ne=String(ne).toLowerCase())==="ucs2"||ne==="ucs-2"||ne==="utf16le"||ne==="utf-16le")){if(pe.length<2||U.length<2)return-1;Fe=2,St/=2,Bt/=2,Z/=2}function Qe($t,Pt){return Fe===1?$t[Pt]:$t.readUInt16BE(Pt*Fe)}if(ye){let $t=-1;for(Ee=Z;EeSt&&(Z=St-Bt),Ee=Z;Ee>=0;Ee--){let $t=!0;for(let Pt=0;Ptye&&(ne=ye):ne=ye;const Ee=U.length;let Fe;for(ne>Ee/2&&(ne=Ee/2),Fe=0;Fe>8,Qe=St%256,$t.push(Qe),$t.push(Bt);return $t})(U,pe.length-Z),pe,Z,ne)}function ge(pe,U,Z){return U===0&&Z===pe.length?h.fromByteArray(pe):h.fromByteArray(pe.slice(U,Z))}function ke(pe,U,Z){Z=Math.min(pe.length,Z);const ne=[];let ye=U;for(;ye239?4:Ee>223?3:Ee>191?2:1;if(ye+St<=Z){let Bt,Qe,$t,Pt;switch(St){case 1:Ee<128&&(Fe=Ee);break;case 2:Bt=pe[ye+1],(192&Bt)==128&&(Pt=(31&Ee)<<6|63&Bt,Pt>127&&(Fe=Pt));break;case 3:Bt=pe[ye+1],Qe=pe[ye+2],(192&Bt)==128&&(192&Qe)==128&&(Pt=(15&Ee)<<12|(63&Bt)<<6|63&Qe,Pt>2047&&(Pt<55296||Pt>57343)&&(Fe=Pt));break;case 4:Bt=pe[ye+1],Qe=pe[ye+2],$t=pe[ye+3],(192&Bt)==128&&(192&Qe)==128&&(192&$t)==128&&(Pt=(15&Ee)<<18|(63&Bt)<<12|(63&Qe)<<6|63&$t,Pt>65535&&Pt<1114112&&(Fe=Pt))}}Fe===null?(Fe=65533,St=1):Fe>65535&&(Fe-=65536,ne.push(Fe>>>10&1023|55296),Fe=56320|1023&Fe),ne.push(Fe),ye+=St}return(function(Fe){const St=Fe.length;if(St<=Ve)return String.fromCharCode.apply(String,Fe);let Bt="",Qe=0;for(;Qe"u"||typeof console.error!="function"||console.error("This browser lacks typed array (Uint8Array) support which is required by `buffer` v5.x. Use `buffer` v4.x if you require old browser support."),Object.defineProperty(g.prototype,"parent",{enumerable:!0,get:function(){if(g.isBuffer(this))return this.buffer}}),Object.defineProperty(g.prototype,"offset",{enumerable:!0,get:function(){if(g.isBuffer(this))return this.byteOffset}}),g.poolSize=8192,g.from=function(pe,U,Z){return S(pe,U,Z)},Object.setPrototypeOf(g.prototype,Uint8Array.prototype),Object.setPrototypeOf(g,Uint8Array),g.alloc=function(pe,U,Z){return(function(ye,Ee,Fe){return _(ye),ye<=0?m(ye):Ee!==void 0?typeof Fe=="string"?m(ye).fill(Ee,Fe):m(ye).fill(Ee):m(ye)})(pe,U,Z)},g.allocUnsafe=function(pe){return T(pe)},g.allocUnsafeSlow=function(pe){return T(pe)},g.isBuffer=function(U){return U!=null&&U._isBuffer===!0&&U!==g.prototype},g.compare=function(U,Z){if(Gt(U,Uint8Array)&&(U=g.from(U,U.offset,U.byteLength)),Gt(Z,Uint8Array)&&(Z=g.from(Z,Z.offset,Z.byteLength)),!g.isBuffer(U)||!g.isBuffer(Z))throw new TypeError('The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array');if(U===Z)return 0;let ne=U.length,ye=Z.length;for(let Ee=0,Fe=Math.min(ne,ye);Eeye.length?(g.isBuffer(Fe)||(Fe=g.from(Fe)),Fe.copy(ye,Ee)):Uint8Array.prototype.set.call(ye,Fe,Ee);else{if(!g.isBuffer(Fe))throw new TypeError('"list" argument must be an Array of Buffers');Fe.copy(ye,Ee)}Ee+=Fe.length}return ye},g.byteLength=z,g.prototype._isBuffer=!0,g.prototype.swap16=function(){const U=this.length;if(U%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(let Z=0;ZZ&&(U+=" ... "),""},f&&(g.prototype[f]=g.prototype.inspect),g.prototype.compare=function(U,Z,ne,ye,Ee){if(Gt(U,Uint8Array)&&(U=g.from(U,U.offset,U.byteLength)),!g.isBuffer(U))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof U);if(Z===void 0&&(Z=0),ne===void 0&&(ne=U?U.length:0),ye===void 0&&(ye=0),Ee===void 0&&(Ee=this.length),Z<0||ne>U.length||ye<0||Ee>this.length)throw new RangeError("out of range index");if(ye>=Ee&&Z>=ne)return 0;if(ye>=Ee)return-1;if(Z>=ne)return 1;if(this===U)return 0;let Fe=(Ee>>>=0)-(ye>>>=0),St=(ne>>>=0)-(Z>>>=0);const Bt=Math.min(Fe,St),Qe=this.slice(ye,Ee),$t=U.slice(Z,ne);for(let Pt=0;Pt>>=0,isFinite(ne)?(ne>>>=0,ye===void 0&&(ye="utf8")):(ye=ne,ne=void 0)}const Ee=this.length-Z;if((ne===void 0||ne>Ee)&&(ne=Ee),U.length>0&&(ne<0||Z<0)||Z>this.length)throw new RangeError("Attempt to write outside buffer bounds");ye||(ye="utf8");let Fe=!1;for(;;)switch(ye){case"hex":return te(this,U,Z,ne);case"utf8":case"utf-8":return ie(this,U,Z,ne);case"ascii":case"latin1":case"binary":return se(this,U,Z,ne);case"base64":return Te(this,U,Z,ne);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return he(this,U,Z,ne);default:if(Fe)throw new TypeError("Unknown encoding: "+ye);ye=(""+ye).toLowerCase(),Fe=!0}},g.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};const Ve=4096;function He(pe,U,Z){let ne="";Z=Math.min(pe.length,Z);for(let ye=U;yene)&&(Z=ne);let ye="";for(let Ee=U;EeZ)throw new RangeError("Trying to access beyond buffer length")}function u(pe,U,Z,ne,ye,Ee){if(!g.isBuffer(pe))throw new TypeError('"buffer" argument must be a Buffer instance');if(U>ye||Upe.length)throw new RangeError("Index out of range")}function at(pe,U,Z,ne,ye){H(U,ne,ye,pe,Z,7);let Ee=Number(U&BigInt(4294967295));pe[Z++]=Ee,Ee>>=8,pe[Z++]=Ee,Ee>>=8,pe[Z++]=Ee,Ee>>=8,pe[Z++]=Ee;let Fe=Number(U>>BigInt(32)&BigInt(4294967295));return pe[Z++]=Fe,Fe>>=8,pe[Z++]=Fe,Fe>>=8,pe[Z++]=Fe,Fe>>=8,pe[Z++]=Fe,Z}function Xe(pe,U,Z,ne,ye){H(U,ne,ye,pe,Z,7);let Ee=Number(U&BigInt(4294967295));pe[Z+7]=Ee,Ee>>=8,pe[Z+6]=Ee,Ee>>=8,pe[Z+5]=Ee,Ee>>=8,pe[Z+4]=Ee;let Fe=Number(U>>BigInt(32)&BigInt(4294967295));return pe[Z+3]=Fe,Fe>>=8,pe[Z+2]=Fe,Fe>>=8,pe[Z+1]=Fe,Fe>>=8,pe[Z]=Fe,Z+8}function Se(pe,U,Z,ne,ye,Ee){if(Z+ne>pe.length)throw new RangeError("Index out of range");if(Z<0)throw new RangeError("Index out of range")}function De(pe,U,Z,ne,ye){return U=+U,Z>>>=0,ye||Se(pe,0,Z,4),v.write(pe,U,Z,ne,23,4),Z+4}function Ke(pe,U,Z,ne,ye){return U=+U,Z>>>=0,ye||Se(pe,0,Z,8),v.write(pe,U,Z,ne,52,8),Z+8}g.prototype.slice=function(U,Z){const ne=this.length;(U=~~U)<0?(U+=ne)<0&&(U=0):U>ne&&(U=ne),(Z=Z===void 0?ne:~~Z)<0?(Z+=ne)<0&&(Z=0):Z>ne&&(Z=ne),Z>>=0,Z>>>=0,ne||Re(U,Z,this.length);let ye=this[U],Ee=1,Fe=0;for(;++Fe>>=0,Z>>>=0,ne||Re(U,Z,this.length);let ye=this[U+--Z],Ee=1;for(;Z>0&&(Ee*=256);)ye+=this[U+--Z]*Ee;return ye},g.prototype.readUint8=g.prototype.readUInt8=function(U,Z){return U>>>=0,Z||Re(U,1,this.length),this[U]},g.prototype.readUint16LE=g.prototype.readUInt16LE=function(U,Z){return U>>>=0,Z||Re(U,2,this.length),this[U]|this[U+1]<<8},g.prototype.readUint16BE=g.prototype.readUInt16BE=function(U,Z){return U>>>=0,Z||Re(U,2,this.length),this[U]<<8|this[U+1]},g.prototype.readUint32LE=g.prototype.readUInt32LE=function(U,Z){return U>>>=0,Z||Re(U,4,this.length),(this[U]|this[U+1]<<8|this[U+2]<<16)+16777216*this[U+3]},g.prototype.readUint32BE=g.prototype.readUInt32BE=function(U,Z){return U>>>=0,Z||Re(U,4,this.length),16777216*this[U]+(this[U+1]<<16|this[U+2]<<8|this[U+3])},g.prototype.readBigUInt64LE=yt((function(U){we(U>>>=0,"offset");const Z=this[U],ne=this[U+7];Z!==void 0&&ne!==void 0||ct(U,this.length-8);const ye=Z+256*this[++U]+65536*this[++U]+this[++U]*2**24,Ee=this[++U]+256*this[++U]+65536*this[++U]+ne*2**24;return BigInt(ye)+(BigInt(Ee)<>>=0,"offset");const Z=this[U],ne=this[U+7];Z!==void 0&&ne!==void 0||ct(U,this.length-8);const ye=Z*2**24+65536*this[++U]+256*this[++U]+this[++U],Ee=this[++U]*2**24+65536*this[++U]+256*this[++U]+ne;return(BigInt(ye)<>>=0,Z>>>=0,ne||Re(U,Z,this.length);let ye=this[U],Ee=1,Fe=0;for(;++Fe=Ee&&(ye-=Math.pow(2,8*Z)),ye},g.prototype.readIntBE=function(U,Z,ne){U>>>=0,Z>>>=0,ne||Re(U,Z,this.length);let ye=Z,Ee=1,Fe=this[U+--ye];for(;ye>0&&(Ee*=256);)Fe+=this[U+--ye]*Ee;return Ee*=128,Fe>=Ee&&(Fe-=Math.pow(2,8*Z)),Fe},g.prototype.readInt8=function(U,Z){return U>>>=0,Z||Re(U,1,this.length),128&this[U]?-1*(255-this[U]+1):this[U]},g.prototype.readInt16LE=function(U,Z){U>>>=0,Z||Re(U,2,this.length);const ne=this[U]|this[U+1]<<8;return 32768&ne?4294901760|ne:ne},g.prototype.readInt16BE=function(U,Z){U>>>=0,Z||Re(U,2,this.length);const ne=this[U+1]|this[U]<<8;return 32768&ne?4294901760|ne:ne},g.prototype.readInt32LE=function(U,Z){return U>>>=0,Z||Re(U,4,this.length),this[U]|this[U+1]<<8|this[U+2]<<16|this[U+3]<<24},g.prototype.readInt32BE=function(U,Z){return U>>>=0,Z||Re(U,4,this.length),this[U]<<24|this[U+1]<<16|this[U+2]<<8|this[U+3]},g.prototype.readBigInt64LE=yt((function(U){we(U>>>=0,"offset");const Z=this[U],ne=this[U+7];Z!==void 0&&ne!==void 0||ct(U,this.length-8);const ye=this[U+4]+256*this[U+5]+65536*this[U+6]+(ne<<24);return(BigInt(ye)<>>=0,"offset");const Z=this[U],ne=this[U+7];Z!==void 0&&ne!==void 0||ct(U,this.length-8);const ye=(Z<<24)+65536*this[++U]+256*this[++U]+this[++U];return(BigInt(ye)<>>=0,Z||Re(U,4,this.length),v.read(this,U,!0,23,4)},g.prototype.readFloatBE=function(U,Z){return U>>>=0,Z||Re(U,4,this.length),v.read(this,U,!1,23,4)},g.prototype.readDoubleLE=function(U,Z){return U>>>=0,Z||Re(U,8,this.length),v.read(this,U,!0,52,8)},g.prototype.readDoubleBE=function(U,Z){return U>>>=0,Z||Re(U,8,this.length),v.read(this,U,!1,52,8)},g.prototype.writeUintLE=g.prototype.writeUIntLE=function(U,Z,ne,ye){U=+U,Z>>>=0,ne>>>=0,!ye&&u(this,U,Z,ne,Math.pow(2,8*ne)-1,0);let Ee=1,Fe=0;for(this[Z]=255&U;++Fe>>=0,ne>>>=0,!ye&&u(this,U,Z,ne,Math.pow(2,8*ne)-1,0);let Ee=ne-1,Fe=1;for(this[Z+Ee]=255&U;--Ee>=0&&(Fe*=256);)this[Z+Ee]=U/Fe&255;return Z+ne},g.prototype.writeUint8=g.prototype.writeUInt8=function(U,Z,ne){return U=+U,Z>>>=0,ne||u(this,U,Z,1,255,0),this[Z]=255&U,Z+1},g.prototype.writeUint16LE=g.prototype.writeUInt16LE=function(U,Z,ne){return U=+U,Z>>>=0,ne||u(this,U,Z,2,65535,0),this[Z]=255&U,this[Z+1]=U>>>8,Z+2},g.prototype.writeUint16BE=g.prototype.writeUInt16BE=function(U,Z,ne){return U=+U,Z>>>=0,ne||u(this,U,Z,2,65535,0),this[Z]=U>>>8,this[Z+1]=255&U,Z+2},g.prototype.writeUint32LE=g.prototype.writeUInt32LE=function(U,Z,ne){return U=+U,Z>>>=0,ne||u(this,U,Z,4,4294967295,0),this[Z+3]=U>>>24,this[Z+2]=U>>>16,this[Z+1]=U>>>8,this[Z]=255&U,Z+4},g.prototype.writeUint32BE=g.prototype.writeUInt32BE=function(U,Z,ne){return U=+U,Z>>>=0,ne||u(this,U,Z,4,4294967295,0),this[Z]=U>>>24,this[Z+1]=U>>>16,this[Z+2]=U>>>8,this[Z+3]=255&U,Z+4},g.prototype.writeBigUInt64LE=yt((function(U,Z=0){return at(this,U,Z,BigInt(0),BigInt("0xffffffffffffffff"))})),g.prototype.writeBigUInt64BE=yt((function(U,Z=0){return Xe(this,U,Z,BigInt(0),BigInt("0xffffffffffffffff"))})),g.prototype.writeIntLE=function(U,Z,ne,ye){if(U=+U,Z>>>=0,!ye){const Bt=Math.pow(2,8*ne-1);u(this,U,Z,ne,Bt-1,-Bt)}let Ee=0,Fe=1,St=0;for(this[Z]=255&U;++Ee>>=0,!ye){const Bt=Math.pow(2,8*ne-1);u(this,U,Z,ne,Bt-1,-Bt)}let Ee=ne-1,Fe=1,St=0;for(this[Z+Ee]=255&U;--Ee>=0&&(Fe*=256);)U<0&&St===0&&this[Z+Ee+1]!==0&&(St=1),this[Z+Ee]=(U/Fe|0)-St&255;return Z+ne},g.prototype.writeInt8=function(U,Z,ne){return U=+U,Z>>>=0,ne||u(this,U,Z,1,127,-128),U<0&&(U=255+U+1),this[Z]=255&U,Z+1},g.prototype.writeInt16LE=function(U,Z,ne){return U=+U,Z>>>=0,ne||u(this,U,Z,2,32767,-32768),this[Z]=255&U,this[Z+1]=U>>>8,Z+2},g.prototype.writeInt16BE=function(U,Z,ne){return U=+U,Z>>>=0,ne||u(this,U,Z,2,32767,-32768),this[Z]=U>>>8,this[Z+1]=255&U,Z+2},g.prototype.writeInt32LE=function(U,Z,ne){return U=+U,Z>>>=0,ne||u(this,U,Z,4,2147483647,-2147483648),this[Z]=255&U,this[Z+1]=U>>>8,this[Z+2]=U>>>16,this[Z+3]=U>>>24,Z+4},g.prototype.writeInt32BE=function(U,Z,ne){return U=+U,Z>>>=0,ne||u(this,U,Z,4,2147483647,-2147483648),U<0&&(U=4294967295+U+1),this[Z]=U>>>24,this[Z+1]=U>>>16,this[Z+2]=U>>>8,this[Z+3]=255&U,Z+4},g.prototype.writeBigInt64LE=yt((function(U,Z=0){return at(this,U,Z,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),g.prototype.writeBigInt64BE=yt((function(U,Z=0){return Xe(this,U,Z,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),g.prototype.writeFloatLE=function(U,Z,ne){return De(this,U,Z,!0,ne)},g.prototype.writeFloatBE=function(U,Z,ne){return De(this,U,Z,!1,ne)},g.prototype.writeDoubleLE=function(U,Z,ne){return Ke(this,U,Z,!0,ne)},g.prototype.writeDoubleBE=function(U,Z,ne){return Ke(this,U,Z,!1,ne)},g.prototype.copy=function(U,Z,ne,ye){if(!g.isBuffer(U))throw new TypeError("argument should be a Buffer");if(ne||(ne=0),ye||ye===0||(ye=this.length),Z>=U.length&&(Z=U.length),Z||(Z=0),ye>0&&ye=this.length)throw new RangeError("Index out of range");if(ye<0)throw new RangeError("sourceEnd out of bounds");ye>this.length&&(ye=this.length),U.length-Z>>=0,ne=ne===void 0?this.length:ne>>>0,U||(U=0),typeof U=="number")for(Ee=Z;Ee=ne+4;Z-=3)U=`_${pe.slice(Z-3,Z)}${U}`;return`${pe.slice(0,Z)}${U}`}function H(pe,U,Z,ne,ye,Ee){if(pe>Z||pe= 0${Fe} and < 2${Fe} ** ${8*(Ee+1)}${Fe}`:`>= -(2${Fe} ** ${8*(Ee+1)-1}${Fe}) and < 2 ** ${8*(Ee+1)-1}${Fe}`,new ft.ERR_OUT_OF_RANGE("value",St,pe)}(function(St,Bt,Qe){we(Bt,"offset"),St[Bt]!==void 0&&St[Bt+Qe]!==void 0||ct(Bt,St.length-(Qe+1))})(ne,ye,Ee)}function we(pe,U){if(typeof pe!="number")throw new ft.ERR_INVALID_ARG_TYPE(U,"number",pe)}function ct(pe,U,Z){throw Math.floor(pe)!==pe?(we(pe,Z),new ft.ERR_OUT_OF_RANGE("offset","an integer",pe)):U<0?new ft.ERR_BUFFER_OUT_OF_BOUNDS:new ft.ERR_OUT_OF_RANGE("offset",`>= 0 and <= ${U}`,pe)}Nt("ERR_BUFFER_OUT_OF_BOUNDS",(function(pe){return pe?`${pe} is outside of buffer bounds`:"Attempt to access memory outside buffer bounds"}),RangeError),Nt("ERR_INVALID_ARG_TYPE",(function(pe,U){return`The "${pe}" argument must be of type number. Received type ${typeof U}`}),TypeError),Nt("ERR_OUT_OF_RANGE",(function(pe,U,Z){let ne=`The value of "${pe}" is out of range.`,ye=Z;return Number.isInteger(Z)&&Math.abs(Z)>4294967296?ye=Wt(String(Z)):typeof Z=="bigint"&&(ye=String(Z),(Z>BigInt(2)**BigInt(32)||Z<-(BigInt(2)**BigInt(32)))&&(ye=Wt(ye)),ye+="n"),ne+=` It must be ${U}. Received ${ye}`,ne}),RangeError);const mt=/[^+/0-9A-Za-z-_]/g;function ur(pe,U){let Z;U=U||1/0;const ne=pe.length;let ye=null;const Ee=[];for(let Fe=0;Fe55295&&Z<57344){if(!ye){if(Z>56319){(U-=3)>-1&&Ee.push(239,191,189);continue}if(Fe+1===ne){(U-=3)>-1&&Ee.push(239,191,189);continue}ye=Z;continue}if(Z<56320){(U-=3)>-1&&Ee.push(239,191,189),ye=Z;continue}Z=65536+(ye-55296<<10|Z-56320)}else ye&&(U-=3)>-1&&Ee.push(239,191,189);if(ye=null,Z<128){if((U-=1)<0)break;Ee.push(Z)}else if(Z<2048){if((U-=2)<0)break;Ee.push(Z>>6|192,63&Z|128)}else if(Z<65536){if((U-=3)<0)break;Ee.push(Z>>12|224,Z>>6&63|128,63&Z|128)}else{if(!(Z<1114112))throw new Error("Invalid code point");if((U-=4)<0)break;Ee.push(Z>>18|240,Z>>12&63|128,Z>>6&63|128,63&Z|128)}}return Ee}function ar(pe){return h.toByteArray((function(Z){if((Z=(Z=Z.split("=")[0]).trim().replace(mt,"")).length<2)return"";for(;Z.length%4!=0;)Z+="=";return Z})(pe))}function Tt(pe,U,Z,ne){let ye;for(ye=0;ye=U.length||ye>=pe.length);++ye)U[ye+Z]=pe[ye];return ye}function Gt(pe,U){return pe instanceof U||pe!=null&&pe.constructor!=null&&pe.constructor.name!=null&&pe.constructor.name===U.name}function Sr(pe){return pe!=pe}const kt=(function(){const pe="0123456789abcdef",U=new Array(256);for(let Z=0;Z<16;++Z){const ne=16*Z;for(let ye=0;ye<16;++ye)U[ne+ye]=pe[Z]+pe[ye]}return U})();function yt(pe){return typeof BigInt>"u"?Zt:pe}function Zt(){throw new Error("BigInt not supported")}},13144(w,N,s){var h=s(66743),v=s(11002),f=s(10076),y=s(47119);w.exports=y||h.call(f,v)},12205(w,N,s){var h=s(66743),v=s(11002),f=s(13144);w.exports=function(){return f(h,v,arguments)}},11002(w){w.exports=Function.prototype.apply},10076(w){w.exports=Function.prototype.call},73126(w,N,s){var h=s(66743),v=s(69675),f=s(10076),y=s(13144);w.exports=function(g){if(g.length<1||typeof g[0]!="function")throw new v("a function is required");return y(h,f,g)}},47119(w){w.exports=typeof Reflect<"u"&&Reflect&&Reflect.apply},10487(w,N,s){var h=s(96897),v=s(30655),f=s(73126),y=s(12205);w.exports=function(g){var S=f(arguments),_=g.length-(arguments.length-1);return h(S,1+(_>0?_:0),!0)},v?v(w.exports,"apply",{value:y}):w.exports.apply=y},36556(w,N,s){var h=s(70453),v=s(73126),f=v([h("%String.prototype.indexOf%")]);w.exports=function(m,g){var S=h(m,!!g);return typeof S=="function"&&f(m,".prototype.")>-1?v([S]):S}},17965(w,N,s){var h=s(16426),v={"text/plain":"Text","text/html":"Url",default:"Text"};w.exports=function(y,m){var g,S,_,T,I,j,M=!1;m||(m={}),g=m.debug||!1;try{if(_=h(),T=document.createRange(),I=document.getSelection(),(j=document.createElement("span")).textContent=y,j.ariaHidden="true",j.style.all="unset",j.style.position="fixed",j.style.top=0,j.style.clip="rect(0, 0, 0, 0)",j.style.whiteSpace="pre",j.style.webkitUserSelect="text",j.style.MozUserSelect="text",j.style.msUserSelect="text",j.style.userSelect="text",j.addEventListener("copy",(function(z){if(z.stopPropagation(),m.format)if(z.preventDefault(),z.clipboardData===void 0){g&&console.warn("unable to use e.clipboardData"),g&&console.warn("trying IE specific stuff"),window.clipboardData.clearData();var K=v[m.format]||v.default;window.clipboardData.setData(K,y)}else z.clipboardData.clearData(),z.clipboardData.setData(m.format,y);m.onCopy&&(z.preventDefault(),m.onCopy(z.clipboardData))})),document.body.appendChild(j),T.selectNodeContents(j),I.addRange(T),!document.execCommand("copy"))throw new Error("copy command was unsuccessful");M=!0}catch(z){g&&console.error("unable to copy using execCommand: ",z),g&&console.warn("trying IE specific stuff");try{window.clipboardData.setData(m.format||"text",y),m.onCopy&&m.onCopy(window.clipboardData),M=!0}catch(K){g&&console.error("unable to copy using clipboardData: ",K),g&&console.error("falling back to prompt"),S=(function(B){var X=(/mac os x/i.test(navigator.userAgent)?"⌘":"Ctrl")+"+C";return B.replace(/#{\s*key\s*}/g,X)})("message"in m?m.message:"Copy to clipboard: #{key}, Enter"),window.prompt(S,y)}}finally{I&&(typeof I.removeRange=="function"?I.removeRange(T):I.removeAllRanges()),j&&document.body.removeChild(j),_()}return M}},2205(w,N,s){var h;h=s.g!==void 0?s.g:this,w.exports=(function(v){if(v.CSS&&v.CSS.escape)return v.CSS.escape;var f=function(y){if(arguments.length==0)throw new TypeError("`CSS.escape` requires an argument.");for(var m,g=String(y),S=g.length,_=-1,T="",I=g.charCodeAt(0);++_=1&&m<=31||m==127||_==0&&m>=48&&m<=57||_==1&&m>=48&&m<=57&&I==45?"\\"+m.toString(16)+" ":_==0&&S==1&&m==45||!(m>=128||m==45||m==95||m>=48&&m<=57||m>=65&&m<=90||m>=97&&m<=122)?"\\"+g.charAt(_):g.charAt(_):T+="�";return T};return v.CSS||(v.CSS={}),v.CSS.escape=f,f})(h)},81919(w,N,s){var h=s(48287).Buffer;function v(S){return S instanceof h||S instanceof Date||S instanceof RegExp}function f(S){if(S instanceof h){var _=h.alloc?h.alloc(S.length):new h(S.length);return S.copy(_),_}if(S instanceof Date)return new Date(S.getTime());if(S instanceof RegExp)return new RegExp(S);throw new Error("Unexpected situation")}function y(S){var _=[];return S.forEach((function(T,I){typeof T=="object"&&T!==null?Array.isArray(T)?_[I]=y(T):v(T)?_[I]=f(T):_[I]=g({},T):_[I]=T})),_}function m(S,_){return _==="__proto__"?void 0:S[_]}var g=w.exports=function(){if(arguments.length<1||typeof arguments[0]!="object")return!1;if(arguments.length<2)return arguments[0];var S,_,T=arguments[0];return Array.prototype.slice.call(arguments,1).forEach((function(I){typeof I!="object"||I===null||Array.isArray(I)||Object.keys(I).forEach((function(j){return _=m(T,j),(S=m(I,j))===T?void 0:typeof S!="object"||S===null?void(T[j]=S):Array.isArray(S)?void(T[j]=y(S)):v(S)?void(T[j]=f(S)):typeof _!="object"||_===null||Array.isArray(_)?void(T[j]=g({},S)):void(T[j]=g(_,S))}))})),T}},14744(w){var N=function(T){return(function(j){return!!j&&typeof j=="object"})(T)&&!(function(j){var M=Object.prototype.toString.call(j);return M==="[object RegExp]"||M==="[object Date]"||(function(K){return K.$$typeof===s})(j)})(T)},s=typeof Symbol=="function"&&Symbol.for?Symbol.for("react.element"):60103;function h(_,T){return T.clone!==!1&&T.isMergeableObject(_)?g((function(j){return Array.isArray(j)?[]:{}})(_),_,T):_}function v(_,T,I){return _.concat(T).map((function(j){return h(j,I)}))}function f(_){return Object.keys(_).concat((function(I){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(I).filter((function(j){return Object.propertyIsEnumerable.call(I,j)})):[]})(_))}function y(_,T){try{return T in _}catch{return!1}}function m(_,T,I){var j={};return I.isMergeableObject(_)&&f(_).forEach((function(M){j[M]=h(_[M],I)})),f(T).forEach((function(M){(function(K,Y){return y(K,Y)&&!(Object.hasOwnProperty.call(K,Y)&&Object.propertyIsEnumerable.call(K,Y))})(_,M)||(y(_,M)&&I.isMergeableObject(T[M])?j[M]=(function(K,Y){if(!Y.customMerge)return g;var B=Y.customMerge(K);return typeof B=="function"?B:g})(M,I)(_[M],T[M],I):j[M]=h(T[M],I))})),j}function g(_,T,I){(I=I||{}).arrayMerge=I.arrayMerge||v,I.isMergeableObject=I.isMergeableObject||N,I.cloneUnlessOtherwiseSpecified=h;var j=Array.isArray(T);return j===Array.isArray(_)?j?I.arrayMerge(_,T,I):m(_,T,I):h(T,I)}g.all=function(T,I){if(!Array.isArray(T))throw new Error("first argument should be an array");return T.reduce((function(j,M){return g(j,M,I)}),{})};var S=g;w.exports=S},30041(w,N,s){var h=s(30655),v=s(58068),f=s(69675),y=s(75795);w.exports=function(g,S,_){if(!g||typeof g!="object"&&typeof g!="function")throw new f("`obj` must be an object or a function`");if(typeof S!="string"&&typeof S!="symbol")throw new f("`property` must be a string or a symbol`");if(arguments.length>3&&typeof arguments[3]!="boolean"&&arguments[3]!==null)throw new f("`nonEnumerable`, if provided, must be a boolean or null");if(arguments.length>4&&typeof arguments[4]!="boolean"&&arguments[4]!==null)throw new f("`nonWritable`, if provided, must be a boolean or null");if(arguments.length>5&&typeof arguments[5]!="boolean"&&arguments[5]!==null)throw new f("`nonConfigurable`, if provided, must be a boolean or null");if(arguments.length>6&&typeof arguments[6]!="boolean")throw new f("`loose`, if provided, must be a boolean");var T=arguments.length>3?arguments[3]:null,I=arguments.length>4?arguments[4]:null,j=arguments.length>5?arguments[5]:null,M=arguments.length>6&&arguments[6],z=!!y&&y(g,S);if(h)h(g,S,{configurable:j===null&&z?z.configurable:!j,enumerable:T===null&&z?z.enumerable:!T,value:_,writable:I===null&&z?z.writable:!I});else{if(!M&&(T||I||j))throw new v("This environment does not support defining a property as non-configurable, non-writable, or non-enumerable.");g[S]=_}}},78004(w){class N{constructor(v,f){this.low=v,this.high=f,this.length=1+f-v}overlaps(v){return!(this.highv.high)}touches(v){return!(this.high+1v.high)}add(v){return new N(Math.min(this.low,v.low),Math.max(this.high,v.high))}subtract(v){return v.low<=this.low&&v.high>=this.high?[]:v.low>this.low&&v.highv+f.length),0)}add(v,f){var y=m=>{for(var g=0;g{for(var g=0;g{for(var S=0;S{for(var y=f.low;y<=f.high;)v.push(y),y++;return v}),[])}subranges(){return this.ranges.map((v=>({low:v.low,high:v.high,length:1+v.high-v.low})))}}w.exports=s},7176(w,N,s){var h,v=s(73126),f=s(75795);try{h=[].__proto__===Array.prototype}catch(S){if(!S||typeof S!="object"||!("code"in S)||S.code!=="ERR_PROTO_ACCESS")throw S}var y=!!h&&f&&f(Object.prototype,"__proto__"),m=Object,g=m.getPrototypeOf;w.exports=y&&typeof y.get=="function"?v([y.get]):typeof g=="function"&&function(_){return g(_==null?_:m(_))}},30655(w){var N=Object.defineProperty||!1;if(N)try{N({},"a",{value:1})}catch{N=!1}w.exports=N},41237(w){w.exports=EvalError},69383(w){w.exports=Error},79290(w){w.exports=RangeError},79538(w){w.exports=ReferenceError},58068(w){w.exports=SyntaxError},69675(w){w.exports=TypeError},35345(w){w.exports=URIError},79612(w){w.exports=Object},37007(w){var N,s=typeof Reflect=="object"?Reflect:null,h=s&&typeof s.apply=="function"?s.apply:function(Y,B,X){return Function.prototype.apply.call(Y,B,X)};N=s&&typeof s.ownKeys=="function"?s.ownKeys:Object.getOwnPropertySymbols?function(Y){return Object.getOwnPropertyNames(Y).concat(Object.getOwnPropertySymbols(Y))}:function(Y){return Object.getOwnPropertyNames(Y)};var v=Number.isNaN||function(Y){return Y!=Y};function f(){f.init.call(this)}w.exports=f,w.exports.once=function(Y,B){return new Promise((function(X,te){function ie(Te){Y.removeListener(B,se),te(Te)}function se(){typeof Y.removeListener=="function"&&Y.removeListener("error",ie),X([].slice.call(arguments))}z(Y,B,se,{once:!0}),B!=="error"&&(function(he,ge,ke){typeof he.on=="function"&&z(he,"error",ge,ke)})(Y,ie,{once:!0})}))},f.EventEmitter=f,f.prototype._events=void 0,f.prototype._eventsCount=0,f.prototype._maxListeners=void 0;var y=10;function m(K){if(typeof K!="function")throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof K)}function g(K){return K._maxListeners===void 0?f.defaultMaxListeners:K._maxListeners}function S(K,Y,B,X){var te,ie,se;if(m(B),(ie=K._events)===void 0?(ie=K._events=Object.create(null),K._eventsCount=0):(ie.newListener!==void 0&&(K.emit("newListener",Y,B.listener?B.listener:B),ie=K._events),se=ie[Y]),se===void 0)se=ie[Y]=B,++K._eventsCount;else if(typeof se=="function"?se=ie[Y]=X?[B,se]:[se,B]:X?se.unshift(B):se.push(B),(te=g(K))>0&&se.length>te&&!se.warned){se.warned=!0;var Te=new Error("Possible EventEmitter memory leak detected. "+se.length+" "+String(Y)+" listeners added. Use emitter.setMaxListeners() to increase limit");Te.name="MaxListenersExceededWarning",Te.emitter=K,Te.type=Y,Te.count=se.length,(function(ge){console&&console.warn&&console.warn(ge)})(Te)}return K}function _(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,arguments.length===0?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function T(K,Y,B){var X={fired:!1,wrapFn:void 0,target:K,type:Y,listener:B},te=_.bind(X);return te.listener=B,X.wrapFn=te,te}function I(K,Y,B){var X=K._events;if(X===void 0)return[];var te=X[Y];return te===void 0?[]:typeof te=="function"?B?[te.listener||te]:[te]:B?(function(se){for(var Te=new Array(se.length),he=0;he0&&(se=B[0]),se instanceof Error)throw se;var Te=new Error("Unhandled error."+(se?" ("+se.message+")":""));throw Te.context=se,Te}var he=ie[Y];if(he===void 0)return!1;if(typeof he=="function")h(he,this,B);else{var ge=he.length,ke=M(he,ge);for(X=0;X=0;se--)if(X[se]===B||X[se].listener===B){Te=X[se].listener,ie=se;break}if(ie<0)return this;ie===0?X.shift():(function(ge,ke){for(;ke+1=0;te--)this.removeListener(Y,B[te]);return this},f.prototype.listeners=function(Y){return I(this,Y,!0)},f.prototype.rawListeners=function(Y){return I(this,Y,!1)},f.listenerCount=function(K,Y){return typeof K.listenerCount=="function"?K.listenerCount(Y):j.call(K,Y)},f.prototype.listenerCount=j,f.prototype.eventNames=function(){return this._eventsCount>0?N(this._events):[]}},85587(w,N,s){var h=s(26311),v=f(Error);function f(y){return m.displayName=y.displayName||y.name,m;function m(g){return g&&(g=h.apply(null,arguments)),new y(g)}}w.exports=v,v.eval=f(EvalError),v.range=f(RangeError),v.reference=f(ReferenceError),v.syntax=f(SyntaxError),v.type=f(TypeError),v.uri=f(URIError),v.create=f},82682(w,N,s){var h=s(69600),v=Object.prototype.toString,f=Object.prototype.hasOwnProperty;w.exports=function(m,g,S){if(!h(g))throw new TypeError("iterator must be a function");var _;arguments.length>=3&&(_=S),(function(I){return v.call(I)==="[object Array]"})(m)?(function(I,j,M){for(var z=0,K=I.length;z0?parseInt(Y):null};_"u"?h:AggregateError,"%Array%":Array,"%ArrayBuffer%":typeof ArrayBuffer>"u"?h:ArrayBuffer,"%ArrayIteratorPrototype%":ge&&ke?ke([][Symbol.iterator]()):h,"%AsyncFromSyncIteratorPrototype%":h,"%AsyncFunction%":gt,"%AsyncGenerator%":gt,"%AsyncGeneratorFunction%":gt,"%AsyncIteratorPrototype%":gt,"%Atomics%":typeof Atomics>"u"?h:Atomics,"%BigInt%":typeof BigInt>"u"?h:BigInt,"%BigInt64Array%":typeof BigInt64Array>"u"?h:BigInt64Array,"%BigUint64Array%":typeof BigUint64Array>"u"?h:BigUint64Array,"%Boolean%":Boolean,"%DataView%":typeof DataView>"u"?h:DataView,"%Date%":Date,"%decodeURI%":decodeURI,"%decodeURIComponent%":decodeURIComponent,"%encodeURI%":encodeURI,"%encodeURIComponent%":encodeURIComponent,"%Error%":f,"%eval%":eval,"%EvalError%":y,"%Float32Array%":typeof Float32Array>"u"?h:Float32Array,"%Float64Array%":typeof Float64Array>"u"?h:Float64Array,"%FinalizationRegistry%":typeof FinalizationRegistry>"u"?h:FinalizationRegistry,"%Function%":X,"%GeneratorFunction%":gt,"%Int8Array%":typeof Int8Array>"u"?h:Int8Array,"%Int16Array%":typeof Int16Array>"u"?h:Int16Array,"%Int32Array%":typeof Int32Array>"u"?h:Int32Array,"%isFinite%":isFinite,"%isNaN%":isNaN,"%IteratorPrototype%":ge&&ke?ke(ke([][Symbol.iterator]())):h,"%JSON%":typeof JSON=="object"?JSON:h,"%Map%":typeof Map>"u"?h:Map,"%MapIteratorPrototype%":typeof Map<"u"&&ge&&ke?ke(new Map()[Symbol.iterator]()):h,"%Math%":Math,"%Number%":Number,"%Object%":v,"%Object.getOwnPropertyDescriptor%":ie,"%parseFloat%":parseFloat,"%parseInt%":parseInt,"%Promise%":typeof Promise>"u"?h:Promise,"%Proxy%":typeof Proxy>"u"?h:Proxy,"%RangeError%":m,"%ReferenceError%":g,"%Reflect%":typeof Reflect>"u"?h:Reflect,"%RegExp%":RegExp,"%Set%":typeof Set>"u"?h:Set,"%SetIteratorPrototype%":typeof Set<"u"&&ge&&ke?ke(new Set()[Symbol.iterator]()):h,"%SharedArrayBuffer%":typeof SharedArrayBuffer>"u"?h:SharedArrayBuffer,"%String%":String,"%StringIteratorPrototype%":ge&&ke?ke(""[Symbol.iterator]()):h,"%Symbol%":ge?Symbol:h,"%SyntaxError%":S,"%ThrowTypeError%":he,"%TypedArray%":Re,"%TypeError%":_,"%Uint8Array%":typeof Uint8Array>"u"?h:Uint8Array,"%Uint8ClampedArray%":typeof Uint8ClampedArray>"u"?h:Uint8ClampedArray,"%Uint16Array%":typeof Uint16Array>"u"?h:Uint16Array,"%Uint32Array%":typeof Uint32Array>"u"?h:Uint32Array,"%URIError%":T,"%WeakMap%":typeof WeakMap>"u"?h:WeakMap,"%WeakRef%":typeof WeakRef>"u"?h:WeakRef,"%WeakSet%":typeof WeakSet>"u"?h:WeakSet,"%Function.prototype.call%":nt,"%Function.prototype.apply%":qe,"%Object.defineProperty%":se,"%Object.getPrototypeOf%":Ve,"%Math.abs%":I,"%Math.floor%":j,"%Math.max%":M,"%Math.min%":z,"%Math.pow%":K,"%Math.round%":Y,"%Math.sign%":B,"%Reflect.getPrototypeOf%":He};if(ke)try{null.error}catch(ar){var at=ke(ke(ar));u["%Error.prototype%"]=at}var Xe=function ar(Tt){var Gt;if(Tt==="%AsyncFunction%")Gt=te("async function () {}");else if(Tt==="%GeneratorFunction%")Gt=te("function* () {}");else if(Tt==="%AsyncGeneratorFunction%")Gt=te("async function* () {}");else if(Tt==="%AsyncGenerator%"){var Sr=ar("%AsyncGeneratorFunction%");Sr&&(Gt=Sr.prototype)}else if(Tt==="%AsyncIteratorPrototype%"){var kt=ar("%AsyncGenerator%");kt&&ke&&(Gt=ke(kt.prototype))}return u[Tt]=Gt,Gt},Se={__proto__:null,"%ArrayBufferPrototype%":["ArrayBuffer","prototype"],"%ArrayPrototype%":["Array","prototype"],"%ArrayProto_entries%":["Array","prototype","entries"],"%ArrayProto_forEach%":["Array","prototype","forEach"],"%ArrayProto_keys%":["Array","prototype","keys"],"%ArrayProto_values%":["Array","prototype","values"],"%AsyncFunctionPrototype%":["AsyncFunction","prototype"],"%AsyncGenerator%":["AsyncGeneratorFunction","prototype"],"%AsyncGeneratorPrototype%":["AsyncGeneratorFunction","prototype","prototype"],"%BooleanPrototype%":["Boolean","prototype"],"%DataViewPrototype%":["DataView","prototype"],"%DatePrototype%":["Date","prototype"],"%ErrorPrototype%":["Error","prototype"],"%EvalErrorPrototype%":["EvalError","prototype"],"%Float32ArrayPrototype%":["Float32Array","prototype"],"%Float64ArrayPrototype%":["Float64Array","prototype"],"%FunctionPrototype%":["Function","prototype"],"%Generator%":["GeneratorFunction","prototype"],"%GeneratorPrototype%":["GeneratorFunction","prototype","prototype"],"%Int8ArrayPrototype%":["Int8Array","prototype"],"%Int16ArrayPrototype%":["Int16Array","prototype"],"%Int32ArrayPrototype%":["Int32Array","prototype"],"%JSONParse%":["JSON","parse"],"%JSONStringify%":["JSON","stringify"],"%MapPrototype%":["Map","prototype"],"%NumberPrototype%":["Number","prototype"],"%ObjectPrototype%":["Object","prototype"],"%ObjProto_toString%":["Object","prototype","toString"],"%ObjProto_valueOf%":["Object","prototype","valueOf"],"%PromisePrototype%":["Promise","prototype"],"%PromiseProto_then%":["Promise","prototype","then"],"%Promise_all%":["Promise","all"],"%Promise_reject%":["Promise","reject"],"%Promise_resolve%":["Promise","resolve"],"%RangeErrorPrototype%":["RangeError","prototype"],"%ReferenceErrorPrototype%":["ReferenceError","prototype"],"%RegExpPrototype%":["RegExp","prototype"],"%SetPrototype%":["Set","prototype"],"%SharedArrayBufferPrototype%":["SharedArrayBuffer","prototype"],"%StringPrototype%":["String","prototype"],"%SymbolPrototype%":["Symbol","prototype"],"%SyntaxErrorPrototype%":["SyntaxError","prototype"],"%TypedArrayPrototype%":["TypedArray","prototype"],"%TypeErrorPrototype%":["TypeError","prototype"],"%Uint8ArrayPrototype%":["Uint8Array","prototype"],"%Uint8ClampedArrayPrototype%":["Uint8ClampedArray","prototype"],"%Uint16ArrayPrototype%":["Uint16Array","prototype"],"%Uint32ArrayPrototype%":["Uint32Array","prototype"],"%URIErrorPrototype%":["URIError","prototype"],"%WeakMapPrototype%":["WeakMap","prototype"],"%WeakSetPrototype%":["WeakSet","prototype"]},De=s(66743),Ke=s(9957),ft=De.call(nt,Array.prototype.concat),Nt=De.call(qe,Array.prototype.splice),Wt=De.call(nt,String.prototype.replace),H=De.call(nt,String.prototype.slice),we=De.call(nt,RegExp.prototype.exec),ct=/[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g,mt=/\\(\\)?/g,ur=function(Tt,Gt){var Sr,kt=Tt;if(Ke(Se,kt)&&(kt="%"+(Sr=Se[kt])[0]+"%"),Ke(u,kt)){var yt=u[kt];if(yt===gt&&(yt=Xe(kt)),yt===void 0&&!Gt)throw new _("intrinsic "+Tt+" exists, but is not available. Please file an issue!");return{alias:Sr,name:kt,value:yt}}throw new S("intrinsic "+Tt+" does not exist!")};w.exports=function(Tt,Gt){if(typeof Tt!="string"||Tt.length===0)throw new _("intrinsic name must be a non-empty string");if(arguments.length>1&&typeof Gt!="boolean")throw new _('"allowMissing" argument must be a boolean');if(we(/^%?[^%]*%?$/,Tt)===null)throw new S("`%` may not be present anywhere but at the beginning and end of the intrinsic name");var Sr=(function($t){var Pt=H($t,0,1),rn=H($t,-1);if(Pt==="%"&&rn!=="%")throw new S("invalid intrinsic syntax, expected closing `%`");if(rn==="%"&&Pt!=="%")throw new S("invalid intrinsic syntax, expected opening `%`");var kr=[];return Wt($t,ct,(function(An,Wr,Jn,Ea){kr[kr.length]=Jn?Wt(Ea,mt,"$1"):Wr||An})),kr})(Tt),kt=Sr.length>0?Sr[0]:"",yt=ur("%"+kt+"%",Gt),Zt=yt.name,pe=yt.value,U=!1,Z=yt.alias;Z&&(kt=Z[0],Nt(Sr,ft([0,1],Z)));for(var ne=1,ye=!0;ne=Sr.length){var Bt=ie(pe,Ee);pe=(ye=!!Bt)&&"get"in Bt&&!("originalValue"in Bt.get)?Bt.get:pe[Ee]}else ye=Ke(pe,Ee),pe=pe[Ee];ye&&!U&&(u[Zt]=pe)}}return pe}},71064(w,N,s){var h=s(79612);w.exports=h.getPrototypeOf||null},48648(w){w.exports=typeof Reflect<"u"&&Reflect.getPrototypeOf||null},93628(w,N,s){var h=s(48648),v=s(71064),f=s(7176);w.exports=h?function(m){return h(m)}:v?function(m){if(!m||typeof m!="object"&&typeof m!="function")throw new TypeError("getProto: not an object");return v(m)}:f?function(m){return f(m)}:null},6549(w){w.exports=Object.getOwnPropertyDescriptor},75795(w,N,s){var h=s(6549);if(h)try{h([],"length")}catch{h=null}w.exports=h},30592(w,N,s){var h=s(30655),v=function(){return!!h};v.hasArrayLengthDefineBug=function(){if(!h)return null;try{return h([],"length",{value:1}).length!==1}catch{return!0}},w.exports=v},64039(w,N,s){var h=typeof Symbol<"u"&&Symbol,v=s(41333);w.exports=function(){return typeof h=="function"&&typeof Symbol=="function"&&typeof h("foo")=="symbol"&&typeof Symbol("bar")=="symbol"&&v()}},41333(w){w.exports=function(){if(typeof Symbol!="function"||typeof Object.getOwnPropertySymbols!="function")return!1;if(typeof Symbol.iterator=="symbol")return!0;var s={},h=Symbol("test"),v=Object(h);if(typeof h=="string"||Object.prototype.toString.call(h)!=="[object Symbol]"||Object.prototype.toString.call(v)!=="[object Symbol]")return!1;for(var f in s[h]=42,s)return!1;if(typeof Object.keys=="function"&&Object.keys(s).length!==0||typeof Object.getOwnPropertyNames=="function"&&Object.getOwnPropertyNames(s).length!==0)return!1;var y=Object.getOwnPropertySymbols(s);if(y.length!==1||y[0]!==h||!Object.prototype.propertyIsEnumerable.call(s,h))return!1;if(typeof Object.getOwnPropertyDescriptor=="function"){var m=Object.getOwnPropertyDescriptor(s,h);if(m.value!==42||m.enumerable!==!0)return!1}return!0}},49092(w,N,s){var h=s(41333);w.exports=function(){return h()&&!!Symbol.toStringTag}},9957(w,N,s){var h=Function.prototype.call,v=Object.prototype.hasOwnProperty,f=s(66743);w.exports=f.call(h,v)},45981(w){function N(ne){return ne instanceof Map?ne.clear=ne.delete=ne.set=function(){throw new Error("map is read-only")}:ne instanceof Set&&(ne.add=ne.clear=ne.delete=function(){throw new Error("set is read-only")}),Object.freeze(ne),Object.getOwnPropertyNames(ne).forEach((function(ye){var Ee=ne[ye];typeof Ee!="object"||Object.isFrozen(Ee)||N(Ee)})),ne}var s=N,h=N;s.default=h;class v{constructor(ye){ye.data===void 0&&(ye.data={}),this.data=ye.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function f(ne){return ne.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function y(ne,...ye){const Ee=Object.create(null);for(const Fe in ne)Ee[Fe]=ne[Fe];return ye.forEach((function(Fe){for(const St in Fe)Ee[St]=Fe[St]})),Ee}const m=ne=>!!ne.kind;class g{constructor(ye,Ee){this.buffer="",this.classPrefix=Ee.classPrefix,ye.walk(this)}addText(ye){this.buffer+=f(ye)}openNode(ye){if(!m(ye))return;let Ee=ye.kind;ye.sublanguage||(Ee=`${this.classPrefix}${Ee}`),this.span(Ee)}closeNode(ye){m(ye)&&(this.buffer+="")}value(){return this.buffer}span(ye){this.buffer+=``}}class S{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(ye){this.top.children.push(ye)}openNode(ye){const Ee={kind:ye,children:[]};this.add(Ee),this.stack.push(Ee)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(ye){return this.constructor._walk(ye,this.rootNode)}static _walk(ye,Ee){return typeof Ee=="string"?ye.addText(Ee):Ee.children&&(ye.openNode(Ee),Ee.children.forEach((Fe=>this._walk(ye,Fe))),ye.closeNode(Ee)),ye}static _collapse(ye){typeof ye!="string"&&ye.children&&(ye.children.every((Ee=>typeof Ee=="string"))?ye.children=[ye.children.join("")]:ye.children.forEach((Ee=>{S._collapse(Ee)})))}}class _ extends S{constructor(ye){super(),this.options=ye}addKeyword(ye,Ee){ye!==""&&(this.openNode(Ee),this.addText(ye),this.closeNode())}addText(ye){ye!==""&&this.add(ye)}addSublanguage(ye,Ee){const Fe=ye.root;Fe.kind=Ee,Fe.sublanguage=!0,this.add(Fe)}toHTML(){return new g(this,this.options).value()}finalize(){return!0}}function T(ne){return ne?typeof ne=="string"?ne:ne.source:null}const I=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,j="[a-zA-Z]\\w*",M="[a-zA-Z_]\\w*",z="\\b\\d+(\\.\\d+)?",K="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",Y="\\b(0b[01]+)",B={begin:"\\\\[\\s\\S]",relevance:0},X={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[B]},te={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[B]},ie={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},se=function(ne,ye,Ee={}){const Fe=y({className:"comment",begin:ne,end:ye,contains:[]},Ee);return Fe.contains.push(ie),Fe.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),Fe},Te=se("//","$"),he=se("/\\*","\\*/"),ge=se("#","$"),ke={className:"number",begin:z,relevance:0},Ve={className:"number",begin:K,relevance:0},He={className:"number",begin:Y,relevance:0},qe={className:"number",begin:z+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},nt={begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[B,{begin:/\[/,end:/\]/,relevance:0,contains:[B]}]}]},gt={className:"title",begin:j,relevance:0},Re={className:"title",begin:M,relevance:0},u={begin:"\\.\\s*"+M,relevance:0};var at=Object.freeze({__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:j,UNDERSCORE_IDENT_RE:M,NUMBER_RE:z,C_NUMBER_RE:K,BINARY_NUMBER_RE:Y,RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(ne={})=>{const ye=/^#![ ]*\//;return ne.binary&&(ne.begin=(function(...Fe){return Fe.map((St=>T(St))).join("")})(ye,/.*\b/,ne.binary,/\b.*/)),y({className:"meta",begin:ye,end:/$/,relevance:0,"on:begin":(Ee,Fe)=>{Ee.index!==0&&Fe.ignoreMatch()}},ne)},BACKSLASH_ESCAPE:B,APOS_STRING_MODE:X,QUOTE_STRING_MODE:te,PHRASAL_WORDS_MODE:ie,COMMENT:se,C_LINE_COMMENT_MODE:Te,C_BLOCK_COMMENT_MODE:he,HASH_COMMENT_MODE:ge,NUMBER_MODE:ke,C_NUMBER_MODE:Ve,BINARY_NUMBER_MODE:He,CSS_NUMBER_MODE:qe,REGEXP_MODE:nt,TITLE_MODE:gt,UNDERSCORE_TITLE_MODE:Re,METHOD_GUARD:u,END_SAME_AS_BEGIN:function(ne){return Object.assign(ne,{"on:begin":(ye,Ee)=>{Ee.data._beginMatch=ye[1]},"on:end":(ye,Ee)=>{Ee.data._beginMatch!==ye[1]&&Ee.ignoreMatch()}})}});function Xe(ne,ye){ne.input[ne.index-1]==="."&&ye.ignoreMatch()}function Se(ne,ye){ye&&ne.beginKeywords&&(ne.begin="\\b("+ne.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",ne.__beforeBegin=Xe,ne.keywords=ne.keywords||ne.beginKeywords,delete ne.beginKeywords,ne.relevance===void 0&&(ne.relevance=0))}function De(ne,ye){Array.isArray(ne.illegal)&&(ne.illegal=(function(...Fe){return"("+Fe.map((St=>T(St))).join("|")+")"})(...ne.illegal))}function Ke(ne,ye){if(ne.match){if(ne.begin||ne.end)throw new Error("begin & end are not supported with match");ne.begin=ne.match,delete ne.match}}function ft(ne,ye){ne.relevance===void 0&&(ne.relevance=1)}const Nt=["of","and","for","in","not","or","if","then","parent","list","value"];function Wt(ne,ye,Ee="keyword"){const Fe={};return typeof ne=="string"?St(Ee,ne.split(" ")):Array.isArray(ne)?St(Ee,ne):Object.keys(ne).forEach((function(Bt){Object.assign(Fe,Wt(ne[Bt],ye,Bt))})),Fe;function St(Bt,Qe){ye&&(Qe=Qe.map(($t=>$t.toLowerCase()))),Qe.forEach((function($t){const Pt=$t.split("|");Fe[Pt[0]]=[Bt,H(Pt[0],Pt[1])]}))}}function H(ne,ye){return ye?Number(ye):(function(Fe){return Nt.includes(Fe.toLowerCase())})(ne)?0:1}function we(ne,{plugins:ye}){function Ee(Bt,Qe){return new RegExp(T(Bt),"m"+(ne.case_insensitive?"i":"")+(Qe?"g":""))}class Fe{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(Qe,$t){$t.position=this.position++,this.matchIndexes[this.matchAt]=$t,this.regexes.push([$t,Qe]),this.matchAt+=(function(rn){return new RegExp(rn.toString()+"|").exec("").length-1})(Qe)+1}compile(){this.regexes.length===0&&(this.exec=()=>null);const Qe=this.regexes.map(($t=>$t[1]));this.matcherRe=Ee((function(Pt,rn="|"){let kr=0;return Pt.map((An=>{kr+=1;const Wr=kr;let Jn=T(An),Ea="";for(;Jn.length>0;){const Zn=I.exec(Jn);if(!Zn){Ea+=Jn;break}Ea+=Jn.substring(0,Zn.index),Jn=Jn.substring(Zn.index+Zn[0].length),Zn[0][0]==="\\"&&Zn[1]?Ea+="\\"+String(Number(Zn[1])+Wr):(Ea+=Zn[0],Zn[0]==="("&&kr++)}return Ea})).map((An=>`(${An})`)).join(rn)})(Qe),!0),this.lastIndex=0}exec(Qe){this.matcherRe.lastIndex=this.lastIndex;const $t=this.matcherRe.exec(Qe);if(!$t)return null;const Pt=$t.findIndex(((kr,An)=>An>0&&kr!==void 0)),rn=this.matchIndexes[Pt];return $t.splice(0,Pt),Object.assign($t,rn)}}class St{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(Qe){if(this.multiRegexes[Qe])return this.multiRegexes[Qe];const $t=new Fe;return this.rules.slice(Qe).forEach((([Pt,rn])=>$t.addRule(Pt,rn))),$t.compile(),this.multiRegexes[Qe]=$t,$t}resumingScanAtSamePosition(){return this.regexIndex!==0}considerAll(){this.regexIndex=0}addRule(Qe,$t){this.rules.push([Qe,$t]),$t.type==="begin"&&this.count++}exec(Qe){const $t=this.getMatcher(this.regexIndex);$t.lastIndex=this.lastIndex;let Pt=$t.exec(Qe);if(this.resumingScanAtSamePosition()&&!(Pt&&Pt.index===this.lastIndex)){const rn=this.getMatcher(0);rn.lastIndex=this.lastIndex+1,Pt=rn.exec(Qe)}return Pt&&(this.regexIndex+=Pt.position+1,this.regexIndex===this.count&&this.considerAll()),Pt}}if(ne.compilerExtensions||(ne.compilerExtensions=[]),ne.contains&&ne.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return ne.classNameAliases=y(ne.classNameAliases||{}),(function Bt(Qe,$t){const Pt=Qe;if(Qe.isCompiled)return Pt;[Ke].forEach((kr=>kr(Qe,$t))),ne.compilerExtensions.forEach((kr=>kr(Qe,$t))),Qe.__beforeBegin=null,[Se,De,ft].forEach((kr=>kr(Qe,$t))),Qe.isCompiled=!0;let rn=null;if(typeof Qe.keywords=="object"&&(rn=Qe.keywords.$pattern,delete Qe.keywords.$pattern),Qe.keywords&&(Qe.keywords=Wt(Qe.keywords,ne.case_insensitive)),Qe.lexemes&&rn)throw new Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return rn=rn||Qe.lexemes||/\w+/,Pt.keywordPatternRe=Ee(rn,!0),$t&&(Qe.begin||(Qe.begin=/\B|\b/),Pt.beginRe=Ee(Qe.begin),Qe.endSameAsBegin&&(Qe.end=Qe.begin),Qe.end||Qe.endsWithParent||(Qe.end=/\B|\b/),Qe.end&&(Pt.endRe=Ee(Qe.end)),Pt.terminatorEnd=T(Qe.end)||"",Qe.endsWithParent&&$t.terminatorEnd&&(Pt.terminatorEnd+=(Qe.end?"|":"")+$t.terminatorEnd)),Qe.illegal&&(Pt.illegalRe=Ee(Qe.illegal)),Qe.contains||(Qe.contains=[]),Qe.contains=[].concat(...Qe.contains.map((function(kr){return(function(Wr){return Wr.variants&&!Wr.cachedVariants&&(Wr.cachedVariants=Wr.variants.map((function(Jn){return y(Wr,{variants:null},Jn)}))),Wr.cachedVariants?Wr.cachedVariants:ct(Wr)?y(Wr,{starts:Wr.starts?y(Wr.starts):null}):Object.isFrozen(Wr)?y(Wr):Wr})(kr==="self"?Qe:kr)}))),Qe.contains.forEach((function(kr){Bt(kr,Pt)})),Qe.starts&&Bt(Qe.starts,$t),Pt.matcher=(function(An){const Wr=new St;return An.contains.forEach((Jn=>Wr.addRule(Jn.begin,{rule:Jn,type:"begin"}))),An.terminatorEnd&&Wr.addRule(An.terminatorEnd,{type:"end"}),An.illegal&&Wr.addRule(An.illegal,{type:"illegal"}),Wr})(Pt),Pt})(ne)}function ct(ne){return!!ne&&(ne.endsWithParent||ct(ne.starts))}function mt(ne){const ye={props:["language","code","autodetect"],data:function(){return{detectedLanguage:"",unknownLanguage:!1}},computed:{className(){return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){if(!this.autoDetect&&!ne.getLanguage(this.language))return console.warn(`The language "${this.language}" you specified could not be found.`),this.unknownLanguage=!0,f(this.code);let Ee={};return this.autoDetect?(Ee=ne.highlightAuto(this.code),this.detectedLanguage=Ee.language):(Ee=ne.highlight(this.language,this.code,this.ignoreIllegals),this.detectedLanguage=this.language),Ee.value},autoDetect(){return!this.language||(function(Fe){return!!(Fe||Fe==="")})(this.autodetect)},ignoreIllegals:()=>!0},render(Ee){return Ee("pre",{},[Ee("code",{class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{Component:ye,VuePlugin:{install(Ee){Ee.component("highlightjs",ye)}}}}const ur={"after:highlightElement":({el:ne,result:ye,text:Ee})=>{const Fe=Tt(ne);if(!Fe.length)return;const St=document.createElement("div");St.innerHTML=ye.value,ye.value=(function(Qe,$t,Pt){let rn=0,kr="";const An=[];function Wr(){return Qe.length&&$t.length?Qe[0].offset!==$t[0].offset?Qe[0].offset<$t[0].offset?Qe:$t:$t[0].event==="start"?Qe:$t:Qe.length?Qe:$t}function Jn(un){function ti(Oo){return" "+Oo.nodeName+'="'+f(Oo.value)+'"'}kr+="<"+ar(un)+[].map.call(un.attributes,ti).join("")+">"}function Ea(un){kr+=""}function Zn(un){(un.event==="start"?Jn:Ea)(un.node)}for(;Qe.length||$t.length;){let un=Wr();if(kr+=f(Pt.substring(rn,un[0].offset)),rn=un[0].offset,un===Qe){An.reverse().forEach(Ea);do Zn(un.splice(0,1)[0]),un=Wr();while(un===Qe&&un.length&&un[0].offset===rn);An.reverse().forEach(Jn)}else un[0].event==="start"?An.push(un[0].node):An.pop(),Zn(un.splice(0,1)[0])}return kr+f(Pt.substr(rn))})(Fe,Tt(St),Ee)}};function ar(ne){return ne.nodeName.toLowerCase()}function Tt(ne){const ye=[];return(function Ee(Fe,St){for(let Bt=Fe.firstChild;Bt;Bt=Bt.nextSibling)Bt.nodeType===3?St+=Bt.nodeValue.length:Bt.nodeType===1&&(ye.push({event:"start",offset:St,node:Bt}),St=Ee(Bt,St),ar(Bt).match(/br|hr|img|input/)||ye.push({event:"stop",offset:St,node:Bt}));return St})(ne,0),ye}const Gt={},Sr=ne=>{console.error(ne)},kt=(ne,...ye)=>{console.log(`WARN: ${ne}`,...ye)},yt=(ne,ye)=>{Gt[`${ne}/${ye}`]||(console.log(`Deprecated as of ${ne}. ${ye}`),Gt[`${ne}/${ye}`]=!0)},Zt=f,pe=y,U=Symbol("nomatch");var Z=(function(ne){const ye=Object.create(null),Ee=Object.create(null),Fe=[];let St=!0;const Bt=/(^(<[^>]+>|\t|)+|\n)/gm,Qe="Could not find the language '{}', did you forget to load/include a language module?",$t={disableAutodetect:!0,name:"Plain text",contains:[]};let Pt={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:_};function rn(sr){return Pt.noHighlightRe.test(sr)}function kr(sr,br,tn,Cr){let Mr="",Nn="";typeof br=="object"?(Mr=sr,tn=br.ignoreIllegals,Nn=br.language,Cr=void 0):(yt("10.7.0","highlight(lang, code, ...args) has been deprecated."),yt("10.7.0",`Please use highlight(code, options) instead. +https://github.com/highlightjs/highlight.js/issues/2277`),Nn=sr,Mr=br);const nn={code:Mr,language:Nn};ds("before:highlight",nn);const fn=nn.result?nn.result:An(nn.language,nn.code,tn,Cr);return fn.code=nn.code,ds("after:highlight",fn),fn}function An(sr,br,tn,Cr){function Mr(Yt,gr){const mr=ea.case_insensitive?gr[0].toLowerCase():gr[0];return Object.prototype.hasOwnProperty.call(Yt.keywords,mr)&&Yt.keywords[mr]}function Nn(){Rr.subLanguage!=null?(function(){if(xn==="")return;let gr=null;if(typeof Rr.subLanguage=="string"){if(!ye[Rr.subLanguage])return void Nr.addText(xn);gr=An(Rr.subLanguage,xn,!0,ko[Rr.subLanguage]),ko[Rr.subLanguage]=gr.top}else gr=Wr(xn,Rr.subLanguage.length?Rr.subLanguage:null);Rr.relevance>0&&(fi+=gr.relevance),Nr.addSublanguage(gr.emitter,gr.language)})():(function(){if(!Rr.keywords)return void Nr.addText(xn);let gr=0;Rr.keywordPatternRe.lastIndex=0;let mr=Rr.keywordPatternRe.exec(xn),Vr="";for(;mr;){Vr+=xn.substring(gr,mr.index);const Gr=Mr(Rr,mr);if(Gr){const[sa,Sa]=Gr;if(Nr.addText(Vr),Vr="",fi+=Sa,sa.startsWith("_"))Vr+=mr[0];else{const Ja=ea.classNameAliases[sa]||sa;Nr.addKeyword(mr[0],Ja)}}else Vr+=mr[0];gr=Rr.keywordPatternRe.lastIndex,mr=Rr.keywordPatternRe.exec(xn)}Vr+=xn.substr(gr),Nr.addText(Vr)})(),xn=""}function nn(Yt){return Yt.className&&Nr.openNode(ea.classNameAliases[Yt.className]||Yt.className),Rr=Object.create(Yt,{parent:{value:Rr}}),Rr}function fn(Yt,gr,mr){let Vr=(function(sa,Sa){const Ja=sa&&sa.exec(Sa);return Ja&&Ja.index===0})(Yt.endRe,mr);if(Vr){if(Yt["on:end"]){const Gr=new v(Yt);Yt["on:end"](gr,Gr),Gr.isMatchIgnored&&(Vr=!1)}if(Vr){for(;Yt.endsParent&&Yt.parent;)Yt=Yt.parent;return Yt}}if(Yt.endsWithParent)return fn(Yt.parent,gr,mr)}function Ln(Yt){return Rr.matcher.regexIndex===0?(xn+=Yt[0],1):(ms=!0,0)}function Hr(Yt){const gr=Yt[0],mr=Yt.rule,Vr=new v(mr),Gr=[mr.__beforeBegin,mr["on:begin"]];for(const sa of Gr)if(sa&&(sa(Yt,Vr),Vr.isMatchIgnored))return Ln(gr);return mr&&mr.endSameAsBegin&&(mr.endRe=(function(Sa){return new RegExp(Sa.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")})(gr)),mr.skip?xn+=gr:(mr.excludeBegin&&(xn+=gr),Nn(),mr.returnBegin||mr.excludeBegin||(xn=gr)),nn(mr),mr.returnBegin?0:gr.length}function ia(Yt){const gr=Yt[0],mr=br.substr(Yt.index),Vr=fn(Rr,Yt,mr);if(!Vr)return U;const Gr=Rr;Gr.skip?xn+=gr:(Gr.returnEnd||Gr.excludeEnd||(xn+=gr),Nn(),Gr.excludeEnd&&(xn=gr));do Rr.className&&Nr.closeNode(),Rr.skip||Rr.subLanguage||(fi+=Rr.relevance),Rr=Rr.parent;while(Rr!==Vr.parent);return Vr.starts&&(Vr.endSameAsBegin&&(Vr.starts.endRe=Vr.endRe),nn(Vr.starts)),Gr.returnEnd?0:gr.length}let za={};function fs(Yt,gr){const mr=gr&&gr[0];if(xn+=Yt,mr==null)return Nn(),0;if(za.type==="begin"&&gr.type==="end"&&za.index===gr.index&&mr===""){if(xn+=br.slice(gr.index,gr.index+1),!St){const Vr=new Error("0 width match regex");throw Vr.languageName=sr,Vr.badRule=za.rule,Vr}return 1}if(za=gr,gr.type==="begin")return Hr(gr);if(gr.type==="illegal"&&!tn){const Vr=new Error('Illegal lexeme "'+mr+'" for mode "'+(Rr.className||"")+'"');throw Vr.mode=Rr,Vr}if(gr.type==="end"){const Vr=ia(gr);if(Vr!==U)return Vr}if(gr.type==="illegal"&&mr==="")return 1;if(Jo>1e5&&Jo>3*gr.index)throw new Error("potential infinite loop, way more iterations than matches");return xn+=mr,mr.length}const ea=Fn(sr);if(!ea)throw Sr(Qe.replace("{}",sr)),new Error('Unknown language: "'+sr+'"');const di=we(ea,{plugins:Fe});let Va="",Rr=Cr||di;const ko={},Nr=new Pt.__emitter(Pt);(function(){const gr=[];for(let mr=Rr;mr!==ea;mr=mr.parent)mr.className&&gr.unshift(mr.className);gr.forEach((mr=>Nr.openNode(mr)))})();let xn="",fi=0,ta=0,Jo=0,ms=!1;try{for(Rr.matcher.considerAll();;){Jo++,ms?ms=!1:Rr.matcher.considerAll(),Rr.matcher.lastIndex=ta;const Yt=Rr.matcher.exec(br);if(!Yt)break;const gr=fs(br.substring(ta,Yt.index),Yt);ta=Yt.index+gr}return fs(br.substr(ta)),Nr.closeAllNodes(),Nr.finalize(),Va=Nr.toHTML(),{relevance:Math.floor(fi),value:Va,language:sr,illegal:!1,emitter:Nr,top:Rr}}catch(Yt){if(Yt.message&&Yt.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:Yt.message,context:br.slice(ta-100,ta+100),mode:Yt.mode},sofar:Va,relevance:0,value:Zt(br),emitter:Nr};if(St)return{illegal:!1,relevance:0,value:Zt(br),emitter:Nr,language:sr,top:Rr,errorRaised:Yt};throw Yt}}function Wr(sr,br){br=br||Pt.languages||Object.keys(ye);const tn=(function(Hr){const ia={relevance:0,emitter:new Pt.__emitter(Pt),value:Zt(Hr),illegal:!1,top:$t};return ia.emitter.addText(Hr),ia})(sr),Cr=br.filter(Fn).filter(Vo).map((Ln=>An(Ln,sr,!1)));Cr.unshift(tn);const Mr=Cr.sort(((Ln,Hr)=>{if(Ln.relevance!==Hr.relevance)return Hr.relevance-Ln.relevance;if(Ln.language&&Hr.language){if(Fn(Ln.language).supersetOf===Hr.language)return 1;if(Fn(Hr.language).supersetOf===Ln.language)return-1}return 0})),[Nn,nn]=Mr,fn=Nn;return fn.second_best=nn,fn}const Jn={"before:highlightElement":({el:sr})=>{Pt.useBR&&(sr.innerHTML=sr.innerHTML.replace(/\n/g,"").replace(//g,` +`))},"after:highlightElement":({result:sr})=>{Pt.useBR&&(sr.value=sr.value.replace(/\n/g,"
"))}},Ea=/^(<[^>]+>|\t)+/gm,Zn={"after:highlightElement":({result:sr})=>{Pt.tabReplace&&(sr.value=sr.value.replace(Ea,(br=>br.replace(/\t/g,Pt.tabReplace))))}};function un(sr){let br=null;const tn=(function(nn){let fn=nn.className+" ";fn+=nn.parentNode?nn.parentNode.className:"";const Ln=Pt.languageDetectRe.exec(fn);if(Ln){const Hr=Fn(Ln[1]);return Hr||(kt(Qe.replace("{}",Ln[1])),kt("Falling back to no-highlight mode for this block.",nn)),Hr?Ln[1]:"no-highlight"}return fn.split(/\s+/).find((Hr=>rn(Hr)||Fn(Hr)))})(sr);if(rn(tn))return;ds("before:highlightElement",{el:sr,language:tn}),br=sr;const Cr=br.textContent,Mr=tn?kr(Cr,{language:tn,ignoreIllegals:!0}):Wr(Cr);ds("after:highlightElement",{el:sr,result:Mr,text:Cr}),sr.innerHTML=Mr.value,(function(nn,fn,Ln){const Hr=fn?Ee[fn]:Ln;nn.classList.add("hljs"),Hr&&nn.classList.add(Hr)})(sr,tn,Mr.language),sr.result={language:Mr.language,re:Mr.relevance,relavance:Mr.relevance},Mr.second_best&&(sr.second_best={language:Mr.second_best.language,re:Mr.second_best.relevance,relavance:Mr.second_best.relevance})}const ti=()=>{ti.called||(ti.called=!0,yt("10.6.0","initHighlighting() is deprecated. Use highlightAll() instead."),document.querySelectorAll("pre code").forEach(un))};let Oo=!1;function Li(){if(document.readyState==="loading")return void(Oo=!0);document.querySelectorAll("pre code").forEach(un)}function Fn(sr){return sr=(sr||"").toLowerCase(),ye[sr]||ye[Ee[sr]]}function zo(sr,{languageName:br}){typeof sr=="string"&&(sr=[sr]),sr.forEach((tn=>{Ee[tn.toLowerCase()]=br}))}function Vo(sr){const br=Fn(sr);return br&&!br.disableAutodetect}function ds(sr,br){const tn=sr;Fe.forEach((function(Cr){Cr[tn]&&Cr[tn](br)}))}typeof window<"u"&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(function(){Oo&&Li()}),!1),Object.assign(ne,{highlight:kr,highlightAuto:Wr,highlightAll:Li,fixMarkup:function(br){return yt("10.2.0","fixMarkup will be removed entirely in v11.0"),yt("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"),(function(Cr){return Pt.tabReplace||Pt.useBR?Cr.replace(Bt,(Mr=>Mr===` +`?Pt.useBR?"
":Mr:Pt.tabReplace?Mr.replace(/\t/g,Pt.tabReplace):Mr)):Cr})(br)},highlightElement:un,highlightBlock:function(br){return yt("10.7.0","highlightBlock will be removed entirely in v12.0"),yt("10.7.0","Please use highlightElement now."),un(br)},configure:function(br){br.useBR&&(yt("10.3.0","'useBR' will be removed entirely in v11.0"),yt("10.3.0","Please see https://github.com/highlightjs/highlight.js/issues/2559")),Pt=pe(Pt,br)},initHighlighting:ti,initHighlightingOnLoad:function(){yt("10.6.0","initHighlightingOnLoad() is deprecated. Use highlightAll() instead."),Oo=!0},registerLanguage:function(br,tn){let Cr=null;try{Cr=tn(ne)}catch(Mr){if(Sr("Language definition for '{}' could not be registered.".replace("{}",br)),!St)throw Mr;Sr(Mr),Cr=$t}Cr.name||(Cr.name=br),ye[br]=Cr,Cr.rawDefinition=tn.bind(null,ne),Cr.aliases&&zo(Cr.aliases,{languageName:br})},unregisterLanguage:function(br){delete ye[br];for(const tn of Object.keys(Ee))Ee[tn]===br&&delete Ee[tn]},listLanguages:function(){return Object.keys(ye)},getLanguage:Fn,registerAliases:zo,requireLanguage:function(br){yt("10.4.0","requireLanguage will be removed entirely in v11."),yt("10.4.0","Please see https://github.com/highlightjs/highlight.js/pull/2844");const tn=Fn(br);if(tn)return tn;throw new Error("The '{}' language is required, but not loaded.".replace("{}",br))},autoDetection:Vo,inherit:pe,addPlugin:function(br){(function(Cr){Cr["before:highlightBlock"]&&!Cr["before:highlightElement"]&&(Cr["before:highlightElement"]=Mr=>{Cr["before:highlightBlock"](Object.assign({block:Mr.el},Mr))}),Cr["after:highlightBlock"]&&!Cr["after:highlightElement"]&&(Cr["after:highlightElement"]=Mr=>{Cr["after:highlightBlock"](Object.assign({block:Mr.el},Mr))})})(br),Fe.push(br)},vuePlugin:mt(ne).VuePlugin}),ne.debugMode=function(){St=!1},ne.safeMode=function(){St=!0},ne.versionString="10.7.3";for(const sr in at)typeof at[sr]=="object"&&s(at[sr]);return Object.assign(ne,at),ne.addPlugin(Jn),ne.addPlugin(ur),ne.addPlugin(Zn),ne})({});w.exports=Z},35344(w){function N(...s){return s.map((h=>(function(f){return f?typeof f=="string"?f:f.source:null})(h))).join("")}w.exports=function(h){const v={},f={begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[v]}]};Object.assign(v,{className:"variable",variants:[{begin:N(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},f]});const y={className:"subst",begin:/\$\(/,end:/\)/,contains:[h.BACKSLASH_ESCAPE]},m={begin:/<<-?\s*(?=\w+)/,starts:{contains:[h.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,className:"string"})]}},g={className:"string",begin:/"/,end:/"/,contains:[h.BACKSLASH_ESCAPE,v,y]};y.contains.push(g);const S={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},h.NUMBER_MODE,v]},_=h.SHEBANG({binary:`(${["fish","bash","zsh","sh","csh","ksh","tcsh","dash","scsh"].join("|")})`,relevance:10}),T={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[h.inherit(h.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z._-]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp"},contains:[_,h.SHEBANG(),T,S,h.HASH_COMMENT_MODE,m,g,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},v]}}},73402(w){function N(...s){return s.map((h=>(function(f){return f?typeof f=="string"?f:f.source:null})(h))).join("")}w.exports=function(h){const v="HTTP/(2|1\\.[01])",f={className:"attribute",begin:N("^",/[A-Za-z][A-Za-z0-9-]*/,"(?=\\:\\s)"),starts:{contains:[{className:"punctuation",begin:/: /,relevance:0,starts:{end:"$",relevance:0}}]}},y=[f,{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}];return{name:"HTTP",aliases:["https"],illegal:/\S/,contains:[{begin:"^(?="+v+" \\d{3})",end:/$/,contains:[{className:"meta",begin:v},{className:"number",begin:"\\b\\d{3}\\b"}],starts:{end:/\b\B/,illegal:/\S/,contains:y}},{begin:"(?=^[A-Z]+ (.*?) "+v+"$)",end:/$/,contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{className:"meta",begin:v},{className:"keyword",begin:"[A-Z]+"}],starts:{end:/\b\B/,illegal:/\S/,contains:y}},h.inherit(f,{relevance:0})]}}},95089(w){const N="[A-Za-z$_][0-9A-Za-z$_]*",s=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],h=["true","false","null","undefined","NaN","Infinity"],v=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer","BigInt64Array","BigUint64Array","BigInt"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function f(m){return y("(?=",m,")")}function y(...m){return m.map((g=>(function(_){return _?typeof _=="string"?_:_.source:null})(g))).join("")}w.exports=function(g){const S=N,_="<>",T="",I={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(Ve,He)=>{const qe=Ve[0].length+Ve.index,nt=Ve.input[qe];nt!=="<"?nt===">"&&(((gt,{after:Re})=>{const u="",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:g.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:j,contains:ge}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:_,end:T},{begin:I.begin,"on:begin":I.isTrulyOpeningTag,end:I.end}],subLanguage:"xml",contains:[{begin:I.begin,end:I.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:j,contains:["self",g.inherit(g.TITLE_MODE,{begin:S}),ke],illegal:/%/},{beginKeywords:"while if switch catch for"},{className:"function",begin:g.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,contains:[ke,g.inherit(g.TITLE_MODE,{begin:S})]},{variants:[{begin:"\\."+S},{begin:"\\$"+S}],relevance:0},{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{beginKeywords:"extends"},g.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/,end:/[{;]/,excludeEnd:!0,contains:[g.inherit(g.TITLE_MODE,{begin:S}),"self",ke]},{begin:"(get|set)\\s+(?="+S+"\\()",end:/\{/,keywords:"get set",contains:[g.inherit(g.TITLE_MODE,{begin:S}),{begin:/\(\)/},ke]},{begin:/\$[(.]/}]}}},65772(w){w.exports=function(s){const h={literal:"true false null"},v=[s.C_LINE_COMMENT_MODE,s.C_BLOCK_COMMENT_MODE],f=[s.QUOTE_STRING_MODE,s.C_NUMBER_MODE],y={end:",",endsWithParent:!0,excludeEnd:!0,contains:f,keywords:h},m={begin:/\{/,end:/\}/,contains:[{className:"attr",begin:/"/,end:/"/,contains:[s.BACKSLASH_ESCAPE],illegal:"\\n"},s.inherit(y,{begin:/:/})].concat(v),illegal:"\\S"},g={begin:"\\[",end:"\\]",contains:[s.inherit(y)],illegal:"\\S"};return f.push(m,g),v.forEach((function(S){f.push(S)})),{name:"JSON",contains:f,keywords:h,illegal:"\\S"}}},26571(w){w.exports=function(s){const h={$pattern:/-?[A-z\.\-]+\b/,keyword:"if else foreach return do while until elseif begin for trap data dynamicparam end break throw param continue finally in switch exit filter try process catch hidden static parameter",built_in:"ac asnp cat cd CFS chdir clc clear clhy cli clp cls clv cnsn compare copy cp cpi cpp curl cvpa dbp del diff dir dnsn ebp echo|0 epal epcsv epsn erase etsn exsn fc fhx fl ft fw gal gbp gc gcb gci gcm gcs gdr gerr ghy gi gin gjb gl gm gmo gp gps gpv group gsn gsnp gsv gtz gu gv gwmi h history icm iex ihy ii ipal ipcsv ipmo ipsn irm ise iwmi iwr kill lp ls man md measure mi mount move mp mv nal ndr ni nmo npssc nsn nv ogv oh popd ps pushd pwd r rbp rcjb rcsn rd rdr ren ri rjb rm rmdir rmo rni rnp rp rsn rsnp rujb rv rvpa rwmi sajb sal saps sasv sbp sc scb select set shcm si sl sleep sls sort sp spjb spps spsv start stz sujb sv swmi tee trcm type wget where wjb write"},v={begin:"`[\\s\\S]",relevance:0},f={className:"variable",variants:[{begin:/\$\B/},{className:"keyword",begin:/\$this/},{begin:/\$[\w\d][\w\d_:]*/}]},y={className:"string",variants:[{begin:/"/,end:/"/},{begin:/@"/,end:/^"@/}],contains:[v,f,{className:"variable",begin:/\$[A-z]/,end:/[^A-z]/}]},m={className:"string",variants:[{begin:/'/,end:/'/},{begin:/@'/,end:/^'@/}]},g=s.inherit(s.COMMENT(null,null),{variants:[{begin:/#/,end:/$/},{begin:/<#/,end:/#>/}],contains:[{className:"doctag",variants:[{begin:/\.(synopsis|description|example|inputs|outputs|notes|link|component|role|functionality)/},{begin:/\.(parameter|forwardhelptargetname|forwardhelpcategory|remotehelprunspace|externalhelp)\s+\S+/}]}]}),S={className:"built_in",variants:[{begin:"(".concat("Add|Clear|Close|Copy|Enter|Exit|Find|Format|Get|Hide|Join|Lock|Move|New|Open|Optimize|Pop|Push|Redo|Remove|Rename|Reset|Resize|Search|Select|Set|Show|Skip|Split|Step|Switch|Undo|Unlock|Watch|Backup|Checkpoint|Compare|Compress|Convert|ConvertFrom|ConvertTo|Dismount|Edit|Expand|Export|Group|Import|Initialize|Limit|Merge|Mount|Out|Publish|Restore|Save|Sync|Unpublish|Update|Approve|Assert|Build|Complete|Confirm|Deny|Deploy|Disable|Enable|Install|Invoke|Register|Request|Restart|Resume|Start|Stop|Submit|Suspend|Uninstall|Unregister|Wait|Debug|Measure|Ping|Repair|Resolve|Test|Trace|Connect|Disconnect|Read|Receive|Send|Write|Block|Grant|Protect|Revoke|Unblock|Unprotect|Use|ForEach|Sort|Tee|Where",")+(-)[\\w\\d]+")}]},_={className:"class",beginKeywords:"class enum",end:/\s*[{]/,excludeEnd:!0,relevance:0,contains:[s.TITLE_MODE]},T={className:"function",begin:/function\s+/,end:/\s*\{|$/,excludeEnd:!0,returnBegin:!0,relevance:0,contains:[{begin:"function",relevance:0,className:"keyword"},{className:"title",begin:/\w[\w\d]*((-)[\w\d]+)*/,relevance:0},{begin:/\(/,end:/\)/,className:"params",relevance:0,contains:[f]}]},I={begin:/using\s/,end:/$/,returnBegin:!0,contains:[y,m,{className:"keyword",begin:/(using|assembly|command|module|namespace|type)/}]},j={variants:[{className:"operator",begin:"(".concat("-and|-as|-band|-bnot|-bor|-bxor|-casesensitive|-ccontains|-ceq|-cge|-cgt|-cle|-clike|-clt|-cmatch|-cne|-cnotcontains|-cnotlike|-cnotmatch|-contains|-creplace|-csplit|-eq|-exact|-f|-file|-ge|-gt|-icontains|-ieq|-ige|-igt|-ile|-ilike|-ilt|-imatch|-in|-ine|-inotcontains|-inotlike|-inotmatch|-ireplace|-is|-isnot|-isplit|-join|-le|-like|-lt|-match|-ne|-not|-notcontains|-notin|-notlike|-notmatch|-or|-regex|-replace|-shl|-shr|-split|-wildcard|-xor",")\\b")},{className:"literal",begin:/(-)[\w\d]+/,relevance:0}]},M={className:"function",begin:/\[.*\]\s*[\w]+[ ]??\(/,end:/$/,returnBegin:!0,relevance:0,contains:[{className:"keyword",begin:"(".concat(h.keyword.toString().replace(/\s/g,"|"),")\\b"),endsParent:!0,relevance:0},s.inherit(s.TITLE_MODE,{endsParent:!0})]},z=[M,g,v,s.NUMBER_MODE,y,m,S,f,{className:"literal",begin:/\$(null|true|false)\b/},{className:"selector-tag",begin:/@\B/,relevance:0}],K={begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[].concat("self",z,{begin:"("+["string","char","byte","int","long","bool","decimal","single","double","DateTime","xml","array","hashtable","void"].join("|")+")",className:"built_in",relevance:0},{className:"type",begin:/[\.\w\d]+/,relevance:0})};return M.contains.unshift(K),{name:"PowerShell",aliases:["ps","ps1"],case_insensitive:!0,keywords:h,contains:z.concat(_,T,I,j,K)}}},17285(w){function N(f){return f?typeof f=="string"?f:f.source:null}function s(f){return h("(?=",f,")")}function h(...f){return f.map((y=>N(y))).join("")}function v(...f){return"("+f.map((y=>N(y))).join("|")+")"}w.exports=function(y){const m=h(/[A-Z_]/,(function(z){return h("(",z,")?")})(/[A-Z0-9_.-]*:/),/[A-Z0-9_.-]*/),g={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},S={begin:/\s/,contains:[{className:"meta-keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},_=y.inherit(S,{begin:/\(/,end:/\)/}),T=y.inherit(y.APOS_STRING_MODE,{className:"meta-string"}),I=y.inherit(y.QUOTE_STRING_MODE,{className:"meta-string"}),j={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin://,relevance:10,contains:[S,I,T,_,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[S,_,I,T]}]}]},y.COMMENT(//,{relevance:10}),{begin://,relevance:10},g,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[j],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[j],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:h(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",begin:m,relevance:0,starts:j}]},{className:"tag",begin:h(/<\//,s(h(m,/>/))),contains:[{className:"name",begin:m,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}},17533(w){w.exports=function(s){var h="true false yes no null",v="[\\w#;/?:@&=+$,.~*'()[\\]]+",f={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[s.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},y=s.inherit(f,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),m={className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},g={end:",",endsWithParent:!0,excludeEnd:!0,keywords:h,relevance:0},S={begin:/\{/,end:/\}/,contains:[g],illegal:"\\n",relevance:0},_={begin:"\\[",end:"\\]",contains:[g],illegal:"\\n",relevance:0},T=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ ]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ ]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ ]|$)"}]},{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string",begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+v},{className:"type",begin:"!<"+v+">"},{className:"type",begin:"!"+v},{className:"type",begin:"!!"+v},{className:"meta",begin:"&"+s.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+s.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",relevance:0},s.HASH_COMMENT_MODE,{beginKeywords:h,keywords:{literal:h}},m,{className:"number",begin:s.C_NUMBER_RE+"\\b",relevance:0},S,_,f],I=[...T];return I.pop(),I.push(y),g.contains=I,{name:"YAML",case_insensitive:!0,aliases:["yml"],contains:T}}},251(w,N){N.read=function(s,h,v,f,y){var m,g,S=8*y-f-1,_=(1<>1,I=-7,j=v?y-1:0,M=v?-1:1,z=s[h+j];for(j+=M,m=z&(1<<-I)-1,z>>=-I,I+=S;I>0;m=256*m+s[h+j],j+=M,I-=8);for(g=m&(1<<-I)-1,m>>=-I,I+=f;I>0;g=256*g+s[h+j],j+=M,I-=8);if(m===0)m=1-T;else{if(m===_)return g?NaN:1/0*(z?-1:1);g+=Math.pow(2,f),m-=T}return(z?-1:1)*g*Math.pow(2,m-f)},N.write=function(s,h,v,f,y,m){var g,S,_,T=8*m-y-1,I=(1<>1,M=y===23?Math.pow(2,-24)-Math.pow(2,-77):0,z=f?0:m-1,K=f?1:-1,Y=h<0||h===0&&1/h<0?1:0;for(h=Math.abs(h),isNaN(h)||h===1/0?(S=isNaN(h)?1:0,g=I):(g=Math.floor(Math.log(h)/Math.LN2),h*(_=Math.pow(2,-g))<1&&(g--,_*=2),(h+=g+j>=1?M/_:M*Math.pow(2,1-j))*_>=2&&(g++,_/=2),g+j>=I?(S=0,g=I):g+j>=1?(S=(h*_-1)*Math.pow(2,y),g+=j):(S=h*Math.pow(2,j-1)*Math.pow(2,y),g=0));y>=8;s[v+z]=255&S,z+=K,S/=256,y-=8);for(g=g<0;s[v+z]=255&g,z+=K,g/=256,T-=8);s[v+z-K]|=128*Y}},49568(w,N,s){s.r(N),s.d(N,{Collection:()=>he,Iterable:()=>ip,List:()=>Wo,Map:()=>vi,OrderedMap:()=>Ra,OrderedSet:()=>Da,PairSorting:()=>cc,Range:()=>zi,Record:()=>jn,Repeat:()=>vl,Seq:()=>ar,Set:()=>No,Stack:()=>ul,default:()=>sp,fromJS:()=>Zc,get:()=>Ia,getIn:()=>lc,has:()=>Wa,hasIn:()=>ml,hash:()=>kr,is:()=>Qe,isAssociative:()=>Te,isCollection:()=>B,isImmutable:()=>Re,isIndexed:()=>se,isKeyed:()=>te,isList:()=>Bi,isMap:()=>Fe,isOrdered:()=>at,isOrderedMap:()=>St,isOrderedSet:()=>pl,isPlainObject:()=>sa,isRecord:()=>gt,isSeq:()=>qe,isSet:()=>Ui,isStack:()=>cl,isValueObject:()=>Bt,merge:()=>nc,mergeDeep:()=>Ku,mergeDeepWith:()=>$c,mergeWith:()=>Hu,remove:()=>gs,removeIn:()=>Zl,set:()=>ys,setIn:()=>Wu,update:()=>Ao,updateIn:()=>lo,version:()=>tu});var h="delete",v=32,f=31,y={};function m(A){A&&(A.value=!0)}function g(){}function S(A){return A.size===void 0&&(A.size=A.__iterate(T)),A.size}function _(A,O){if(typeof O!="number"){var D=O>>>0;if(""+D!==O||D===4294967295)return NaN;O=D}return O<0?S(A)+O:O}function T(){return!0}function I(A,O,D){return(A===0&&!K(A)||D!==void 0&&A<=-D)&&(O===void 0||D!==void 0&&O>=D)}function j(A,O){return z(A,O,0)}function M(A,O){return z(A,O,O)}function z(A,O,D){return A===void 0?D:K(A)?O===1/0?O:0|Math.max(0,O+A):O===void 0||O===A?A:0|Math.min(O,A)}function K(A){return A<0||A===0&&1/A==-1/0}var Y="@@__IMMUTABLE_ITERABLE__@@";function B(A){return!!(A&&A[Y])}var X="@@__IMMUTABLE_KEYED__@@";function te(A){return!!(A&&A[X])}var ie="@@__IMMUTABLE_INDEXED__@@";function se(A){return!!(A&&A[ie])}function Te(A){return te(A)||se(A)}var he=function(O){return B(O)?O:ar(O)},ge=(function(A){function O(D){return te(D)?D:Tt(D)}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O})(he),ke=(function(A){function O(D){return se(D)?D:Gt(D)}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O})(he),Ve=(function(A){function O(D){return B(D)&&!Te(D)?D:Sr(D)}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O})(he);he.Keyed=ge,he.Indexed=ke,he.Set=Ve;var He="@@__IMMUTABLE_SEQ__@@";function qe(A){return!!(A&&A[He])}var nt="@@__IMMUTABLE_RECORD__@@";function gt(A){return!!(A&&A[nt])}function Re(A){return B(A)||gt(A)}var u="@@__IMMUTABLE_ORDERED__@@";function at(A){return!!(A&&A[u])}var Xe=typeof Symbol=="function"&&Symbol.iterator,Se="@@iterator",De=Xe||Se,Ke=function(O){this.next=O};function ft(A,O,D,F){var q=A===0?O:A===1?D:[O,D];return F?F.value=q:F={value:q,done:!1},F}function Nt(){return{value:void 0,done:!0}}function Wt(A){return!!Array.isArray(A)||!!ct(A)}function H(A){return A&&typeof A.next=="function"}function we(A){var O=ct(A);return O&&O.call(A)}function ct(A){var O=A&&(Xe&&A[Xe]||A[Se]);if(typeof O=="function")return O}Ke.prototype.toString=function(){return"[Iterator]"},Ke.KEYS=0,Ke.VALUES=1,Ke.ENTRIES=2,Ke.prototype.inspect=Ke.prototype.toSource=function(){return this.toString()},Ke.prototype[De]=function(){return this};var mt=Object.prototype.hasOwnProperty;function ur(A){return!(!Array.isArray(A)&&typeof A!="string")||A&&typeof A=="object"&&Number.isInteger(A.length)&&A.length>=0&&(A.length===0?Object.keys(A).length===1:A.hasOwnProperty(A.length-1))}var ar=(function(A){function O(D){return D==null?U():Re(D)?D.toSeq():(function(q){var ae=ye(q);if(ae)return(function(fe){var Pe=ct(fe);return Pe&&Pe===fe.entries})(q)?ae.fromEntrySeq():(function(fe){var Pe=ct(fe);return Pe&&Pe===fe.keys})(q)?ae.toSetSeq():ae;if(typeof q=="object")return new yt(q);throw new TypeError("Expected Array or collection object of values, or keyed object: "+q)})(D)}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O.prototype.toSeq=function(){return this},O.prototype.toString=function(){return this.__toString("Seq {","}")},O.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},O.prototype.__iterate=function(F,q){var ae=this._cache;if(ae){for(var oe=ae.length,fe=0;fe!==oe;){var Pe=ae[q?oe-++fe:fe++];if(F(Pe[1],Pe[0],this)===!1)break}return fe}return this.__iterateUncached(F,q)},O.prototype.__iterator=function(F,q){var ae=this._cache;if(ae){var oe=ae.length,fe=0;return new Ke((function(){if(fe===oe)return{value:void 0,done:!0};var Pe=ae[q?oe-++fe:fe++];return ft(F,Pe[0],Pe[1])}))}return this.__iteratorUncached(F,q)},O})(he),Tt=(function(A){function O(D){return D==null?U().toKeyedSeq():B(D)?te(D)?D.toSeq():D.fromEntrySeq():gt(D)?D.toSeq():Z(D)}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O.prototype.toKeyedSeq=function(){return this},O})(ar),Gt=(function(A){function O(D){return D==null?U():B(D)?te(D)?D.entrySeq():D.toIndexedSeq():gt(D)?D.toSeq().entrySeq():ne(D)}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O.of=function(){return O(arguments)},O.prototype.toIndexedSeq=function(){return this},O.prototype.toString=function(){return this.__toString("Seq [","]")},O})(ar),Sr=(function(A){function O(D){return(B(D)&&!Te(D)?D:Gt(D)).toSetSeq()}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O.of=function(){return O(arguments)},O.prototype.toSetSeq=function(){return this},O})(ar);ar.isSeq=qe,ar.Keyed=Tt,ar.Set=Sr,ar.Indexed=Gt,ar.prototype[He]=!0;var kt=(function(A){function O(D){this._array=D,this.size=D.length}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O.prototype.get=function(F,q){return this.has(F)?this._array[_(this,F)]:q},O.prototype.__iterate=function(F,q){for(var ae=this._array,oe=ae.length,fe=0;fe!==oe;){var Pe=q?oe-++fe:fe++;if(F(ae[Pe],Pe,this)===!1)break}return fe},O.prototype.__iterator=function(F,q){var ae=this._array,oe=ae.length,fe=0;return new Ke((function(){if(fe===oe)return{value:void 0,done:!0};var Pe=q?oe-++fe:fe++;return ft(F,Pe,ae[Pe])}))},O})(Gt),yt=(function(A){function O(D){var F=Object.keys(D).concat(Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(D):[]);this._object=D,this._keys=F,this.size=F.length}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O.prototype.get=function(F,q){return q===void 0||this.has(F)?this._object[F]:q},O.prototype.has=function(F){return mt.call(this._object,F)},O.prototype.__iterate=function(F,q){for(var ae=this._object,oe=this._keys,fe=oe.length,Pe=0;Pe!==fe;){var ze=oe[q?fe-++Pe:Pe++];if(F(ae[ze],ze,this)===!1)break}return Pe},O.prototype.__iterator=function(F,q){var ae=this._object,oe=this._keys,fe=oe.length,Pe=0;return new Ke((function(){if(Pe===fe)return{value:void 0,done:!0};var ze=oe[q?fe-++Pe:Pe++];return ft(F,ze,ae[ze])}))},O})(Tt);yt.prototype[u]=!0;var Zt,pe=(function(A){function O(D){this._collection=D,this.size=D.length||D.size}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O.prototype.__iterateUncached=function(F,q){if(q)return this.cacheResult().__iterate(F,q);var ae=we(this._collection),oe=0;if(H(ae))for(var fe;!(fe=ae.next()).done&&F(fe.value,oe++,this)!==!1;);return oe},O.prototype.__iteratorUncached=function(F,q){if(q)return this.cacheResult().__iterator(F,q);var ae=we(this._collection);if(!H(ae))return new Ke(Nt);var oe=0;return new Ke((function(){var fe=ae.next();return fe.done?fe:ft(F,oe++,fe.value)}))},O})(Gt);function U(){return Zt||(Zt=new kt([]))}function Z(A){var O=ye(A);if(O)return O.fromEntrySeq();if(typeof A=="object")return new yt(A);throw new TypeError("Expected Array or collection object of [k, v] entries, or keyed object: "+A)}function ne(A){var O=ye(A);if(O)return O;throw new TypeError("Expected Array or collection object of values: "+A)}function ye(A){return ur(A)?new kt(A):Wt(A)?new pe(A):void 0}var Ee="@@__IMMUTABLE_MAP__@@";function Fe(A){return!!(A&&A[Ee])}function St(A){return Fe(A)&&at(A)}function Bt(A){return!!(A&&typeof A.equals=="function"&&typeof A.hashCode=="function")}function Qe(A,O){if(A===O||A!=A&&O!=O)return!0;if(!A||!O)return!1;if(typeof A.valueOf=="function"&&typeof O.valueOf=="function"){if((A=A.valueOf())===(O=O.valueOf())||A!=A&&O!=O)return!0;if(!A||!O)return!1}return!!(Bt(A)&&Bt(O)&&A.equals(O))}var $t=typeof Math.imul=="function"&&Math.imul(4294967295,2)===-2?Math.imul:function(O,D){var F=65535&(O|=0),q=65535&(D|=0);return F*q+((O>>>16)*q+F*(D>>>16)<<16>>>0)|0};function Pt(A){return A>>>1&1073741824|3221225471&A}var rn=Object.prototype.valueOf;function kr(A){if(A==null)return An(A);if(typeof A.hashCode=="function")return Pt(A.hashCode(A));var O=(function(F){return F.valueOf!==rn&&typeof F.valueOf=="function"?F.valueOf(F):F})(A);if(O==null)return An(O);switch(typeof O){case"boolean":return O?1108378657:1108378656;case"number":return(function(F){if(F!=F||F===1/0)return 0;var q=0|F;for(q!==F&&(q^=4294967295*F);F>4294967295;)q^=F/=4294967295;return Pt(q)})(O);case"string":return O.length>ds?(function(F){var q=tn[F];return q===void 0&&(q=Wr(F),br===sr&&(br=0,tn={}),br++,tn[F]=q),q})(O):Wr(O);case"object":case"function":return(function(F){var q;if(Li&&(q=Oo.get(F))!==void 0||(q=F[Vo])!==void 0||!un&&((q=F.propertyIsEnumerable&&F.propertyIsEnumerable[Vo])!==void 0||(q=(function(oe){if(oe&&oe.nodeType>0)switch(oe.nodeType){case 1:return oe.uniqueID;case 9:return oe.documentElement&&oe.documentElement.uniqueID}})(F))!==void 0))return q;if(q=ti(),Li)Oo.set(F,q);else{if(Zn!==void 0&&Zn(F)===!1)throw new Error("Non-extensible objects are not allowed as keys.");if(un)Object.defineProperty(F,Vo,{enumerable:!1,configurable:!1,writable:!1,value:q});else if(F.propertyIsEnumerable!==void 0&&F.propertyIsEnumerable===F.constructor.prototype.propertyIsEnumerable)F.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},F.propertyIsEnumerable[Vo]=q;else{if(F.nodeType===void 0)throw new Error("Unable to set a non-enumerable property on object.");F[Vo]=q}}return q})(O);case"symbol":return(function(F){var q=Fn[F];return q!==void 0||(q=ti(),Fn[F]=q),q})(O);default:if(typeof O.toString=="function")return Wr(O.toString());throw new Error("Value type "+typeof O+" cannot be hashed.")}}function An(A){return A===null?1108378658:1108378659}function Wr(A){for(var O=0,D=0;D=0&&(Pe.get=function(ze,tt){return(ze=_(this,ze))>=0&&zeae)return{value:void 0,done:!0};var vr=At.next();return F||ze===1||vr.done?vr:ft(ze,lr-1,ze===0?void 0:vr.value[1],vr)}))},Pe}function fs(A,O,D,F){var q=ta(A);return q.__iterateUncached=function(ae,oe){var fe=this;if(oe)return this.cacheResult().__iterate(ae,oe);var Pe=!0,ze=0;return A.__iterate((function(tt,At,Rt){if(!Pe||!(Pe=O.call(D,tt,At,Rt)))return ze++,ae(tt,F?At:ze-1,fe)})),ze},q.__iteratorUncached=function(ae,oe){var fe=this;if(oe)return this.cacheResult().__iterator(ae,oe);var Pe=A.__iterator(2,oe),ze=!0,tt=0;return new Ke((function(){var At,Rt,lr;do{if((At=Pe.next()).done)return F||ae===1?At:ft(ae,tt++,ae===0?void 0:At.value[1],At);var vr=At.value;Rt=vr[0],lr=vr[1],ze&&(ze=O.call(D,lr,Rt,fe))}while(ze);return ae===2?At:ft(ae,Rt,lr,At)}))},q}function ea(A,O,D){var F=ta(A);return F.__iterateUncached=function(q,ae){if(ae)return this.cacheResult().__iterate(q,ae);var oe=0,fe=!1;return(function Pe(ze,tt){ze.__iterate((function(At,Rt){return(!O||tt0}function ko(A,O,D,F){var q=ta(A),ae=new kt(D).map((function(oe){return oe.size}));return q.size=F?ae.max():ae.min(),q.__iterate=function(oe,fe){for(var Pe,ze=this.__iterator(1,fe),tt=0;!(Pe=ze.next()).done&&oe(Pe.value,tt++,this)!==!1;);return tt},q.__iteratorUncached=function(oe,fe){var Pe=D.map((function(At){return At=he(At),we(fe?At.reverse():At)})),ze=0,tt=!1;return new Ke((function(){var At;return tt||(At=Pe.map((function(Rt){return Rt.next()})),tt=F?At.every((function(Rt){return Rt.done})):At.some((function(Rt){return Rt.done}))),tt?{value:void 0,done:!0}:ft(oe,ze++,O.apply(null,At.map((function(Rt){return Rt.value}))))}))},q}function Nr(A,O){return A===O?A:qe(A)?O:A.constructor(O)}function xn(A){if(A!==Object(A))throw new TypeError("Expected [K, V] tuple: "+A)}function fi(A){return te(A)?ge:se(A)?ke:Ve}function ta(A){return Object.create((te(A)?Tt:se(A)?Gt:Sr).prototype)}function Jo(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):ar.prototype.cacheResult.call(this)}function ms(A,O){return A===void 0&&O===void 0?0:A===void 0?1:O===void 0?-1:A>O?1:A0;)O[D]=arguments[D+1];if(typeof A!="function")throw new TypeError("Invalid merger function: "+A);return Zs(this,O,A)}function Zs(A,O,D){for(var F=[],q=0;q0;)O[D]=arguments[D+1];return Co(A,O)}function Hu(A,O){for(var D=[],F=arguments.length-2;F-- >0;)D[F]=arguments[F+2];return Co(O,D,A)}function Ku(A){for(var O=[],D=arguments.length-1;D-- >0;)O[D]=arguments[D+1];return $i(A,O)}function $c(A,O){for(var D=[],F=arguments.length-2;F-- >0;)D[F]=arguments[F+2];return $i(O,D,A)}function $i(A,O,D){return Co(A,O,(function(q){function ae(oe,fe,Pe){return Sa(oe)&&Sa(fe)&&(function(tt,At){var Rt=ar(tt),lr=ar(At);return se(Rt)===se(lr)&&te(Rt)===te(lr)})(oe,fe)?Co(oe,[fe],ae):q?q(oe,fe,Pe):fe}return ae})(D))}function Co(A,O,D){if(!Sa(A))throw new TypeError("Cannot merge into non-data-structure value: "+A);if(Re(A))return typeof D=="function"&&A.mergeWith?A.mergeWith.apply(A,[D].concat(O)):A.merge?A.merge.apply(A,O):A.concat.apply(A,O);for(var F=Array.isArray(A),q=A,ae=F?ke:ge,oe=F?function(Pe){q===A&&(q=Xl(q)),q.push(Pe)}:function(Pe,ze){if(!Yl(ze)){var tt=mt.call(q,ze),At=tt&&D?D(q[ze],Pe,ze):Pe;tt&&At===q[ze]||(q===A&&(q=Xl(q)),q[ze]=At)}},fe=0;fe0;)O[D]=arguments[D+1];return $i(this,O,A)}function bs(A){for(var O=[],D=arguments.length-1;D-- >0;)O[D]=arguments[D+1];return lo(this,A,co(),(function(F){return Co(F,O)}))}function ws(A){for(var O=[],D=arguments.length-1;D-- >0;)O[D]=arguments[D+1];return lo(this,A,co(),(function(F){return $i(F,O)}))}function jo(A){var O=this.asMutable();return A(O),O.wasAltered()?O.__ensureOwner(this.__ownerID):this}function ac(){return this.__ownerID?this:this.__ensureOwner(new g)}function el(){return this.__ensureOwner()}function Es(){return this.__altered}var vi=(function(A){function O(D){return D==null?co():Fe(D)&&!at(D)?D:co().withMutations((function(F){var q=A(D);mr(q.size),q.forEach((function(ae,oe){return F.set(oe,ae)}))}))}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O.of=function(){for(var F=[],q=arguments.length;q--;)F[q]=arguments[q];return co().withMutations((function(ae){for(var oe=0;oe=F.length)throw new Error("Missing value for key: "+F[oe]);ae.set(F[oe],F[oe+1])}}))},O.prototype.toString=function(){return this.__toString("Map {","}")},O.prototype.get=function(F,q){return this._root?this._root.get(0,void 0,F,q):q},O.prototype.set=function(F,q){return nl(this,F,q)},O.prototype.remove=function(F){return nl(this,F,y)},O.prototype.deleteAll=function(F){var q=he(F);return q.size===0?this:this.withMutations((function(ae){q.forEach((function(oe){return ae.remove(oe)}))}))},O.prototype.clear=function(){return this.size===0?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):co()},O.prototype.sort=function(F){return Ra(di(this,F))},O.prototype.sortBy=function(F,q){return Ra(di(this,q,F))},O.prototype.map=function(F,q){var ae=this;return this.withMutations((function(oe){oe.forEach((function(fe,Pe){oe.set(Pe,F.call(q,fe,Pe,ae))}))}))},O.prototype.__iterator=function(F,q){return new jd(this,F,q)},O.prototype.__iterate=function(F,q){var ae=this,oe=0;return this._root&&this._root.iterate((function(fe){return oe++,F(fe[1],fe[0],ae)}),q),oe},O.prototype.__ensureOwner=function(F){return F===this.__ownerID?this:F?yr(this.size,this._root,F,this.__hash):this.size===0?co():(this.__ownerID=F,this.__altered=!1,this)},O})(ge);vi.isMap=Fe;var bn=vi.prototype;bn[Ee]=!0,bn[h]=bn.remove,bn.removeAll=bn.deleteAll,bn.setIn=Ql,bn.removeIn=bn.deleteIn=ec,bn.update=vs,bn.updateIn=an,bn.merge=bn.concat=tc,bn.mergeWith=rc,bn.mergeDeep=gi,bn.mergeDeepWith=yi,bn.mergeIn=bs,bn.mergeDeepIn=ws,bn.withMutations=jo,bn.wasAltered=Es,bn.asImmutable=el,bn["@@transducer/init"]=bn.asMutable=ac,bn["@@transducer/step"]=function(A,O){return A.set(O[0],O[1])},bn["@@transducer/result"]=function(A){return A.asImmutable()};var tl=function(O,D){this.ownerID=O,this.entries=D};tl.prototype.get=function(O,D,F,q){for(var ae=this.entries,oe=0,fe=ae.length;oe=zc)return(function(_n,ya,Fa,ai){_n||(_n=new g);for(var Tn=new Ma(_n,kr(Fa),[Fa,ai]),Hn=0;Hn>>O)&f),oe=this.bitmap;return oe&ae?this.nodes[Gu(oe&ae-1)].get(O+5,D,F,q):q},rl.prototype.update=function(O,D,F,q,ae,oe,fe){F===void 0&&(F=kr(q));var Pe=(D===0?F:F>>>D)&f,ze=1<=rh)return(function(Tn,Hn,na,aa,Bn){for(var Ga=0,Ya=new Array(v),po=0;na!==0;po++,na>>>=1)Ya[po]=1&na?Hn[Ga++]:void 0;return Ya[aa]=Bn,new bi(Tn,Ga+1,Ya)})(O,lr,tt,Pe,Ar);if(At&&!Ar&&lr.length===2&&Uc(lr[1^Rt]))return lr[1^Rt];if(At&&Ar&&lr.length===1&&Uc(Ar))return Ar;var _n=O&&O===this.ownerID,ya=At?Ar?tt:tt^ze:tt|ze,Fa=At?Ar?ic(lr,Rt,Ar,_n):(function(Tn,Hn,na){var aa=Tn.length-1;if(na&&Hn===aa)return Tn.pop(),Tn;for(var Bn=new Array(aa),Ga=0,Ya=0;Ya>>O)&f,oe=this.nodes[ae];return oe?oe.get(O+5,D,F,q):q},bi.prototype.update=function(O,D,F,q,ae,oe,fe){F===void 0&&(F=kr(q));var Pe=(D===0?F:F>>>D)&f,ze=ae===y,tt=this.nodes,At=tt[Pe];if(ze&&!At)return this;var Rt=wi(At,O,D+5,F,q,ae,oe,fe);if(Rt===At)return this;var lr=this.count;if(At){if(!Rt&&--lr=Yu&&(q=this._buildIndex()),q!==void 0){var ae=q[Ea(O)];if(ae!==void 0)for(var oe=0;oe>>D)&f,fe=(D===0?F:F>>>D)&f,Pe=oe===fe?[al(A,O,D+5,F,q)]:(ae=new Ma(O,F,q),oe>1&1431655765))+(A>>2&858993459))+(A>>4)&252645135,A+=A>>8,127&(A+=A>>16)}function ic(A,O,D,F){var q=F?A:Yt(A);return q[O]=D,q}var zc=8,rh=16,uo=8,Yu=16,Vc="@@__IMMUTABLE_LIST__@@";function Bi(A){return!!(A&&A[Vc])}var Wo=(function(A){function O(D){var F=Ss();if(D==null)return F;if(Bi(D))return D;var q=A(D),ae=q.size;return ae===0?F:(mr(ae),ae>0&&ae=0&&F=oe.size||fe<0)return oe.withMutations((function(Rt){fe<0?ri(Rt,fe).set(0,Pe):ri(Rt,0,fe+1).set(fe,Pe)}));fe+=oe._origin;var ze=oe._tail,tt=oe._root,At={value:!1};return fe>=sl(oe._capacity)?ze=Qu(ze,oe.__ownerID,0,fe,Pe,At):tt=Qu(tt,oe.__ownerID,oe._level,fe,Pe,At),At.value?oe.__ownerID?(oe._root=tt,oe._tail=ze,oe.__hash=void 0,oe.__altered=!0,oe):ol(oe._origin,oe._capacity,oe._level,tt,ze):oe})(this,F,q)},O.prototype.remove=function(F){return this.has(F)?F===0?this.shift():F===this.size-1?this.pop():this.splice(F,1):this},O.prototype.insert=function(F,q){return this.splice(F,0,q)},O.prototype.clear=function(){return this.size===0?this:this.__ownerID?(this.size=this._origin=this._capacity=0,this._level=5,this._root=this._tail=this.__hash=void 0,this.__altered=!0,this):Ss()},O.prototype.push=function(){var F=arguments,q=this.size;return this.withMutations((function(ae){ri(ae,0,q+F.length);for(var oe=0;oe>>D&f;if(q>=this.array.length)return new Ha([],O);var ae,oe=q===0;if(D>0){var fe=this.array[q];if((ae=fe&&fe.removeBefore(O,D-5,F))===fe&&oe)return this}if(oe&&!ae)return this;var Pe=il(this,O);if(!oe)for(var ze=0;ze>>D&f;if(ae>=this.array.length)return this;if(D>0){var oe=this.array[ae];if((q=oe&&oe.removeAfter(O,D-5,F))===oe&&ae===this.array.length-1)return this}var fe=il(this,O);return fe.array.splice(ae+1),q&&(fe.array[ae]=q),fe};var nh,Ei={};function Xu(A,O){var D=A._origin,F=A._capacity,q=sl(F),ae=A._tail;return oe(A._root,A._level,0);function oe(fe,Pe,ze){return Pe===0?(function(At,Rt){var lr=Rt===q?ae&&ae.array:At&&At.array,vr=Rt>D?0:D-Rt,Ar=F-Rt;return Ar>v&&(Ar=v),function(){if(vr===Ar)return Ei;var _n=O?--Ar:vr++;return lr&&lr[_n]}})(fe,ze):(function(At,Rt,lr){var vr,Ar=At&&At.array,_n=lr>D?0:D-lr>>Rt,ya=1+(F-lr>>Rt);return ya>v&&(ya=v),function(){for(;;){if(vr){var Fa=vr();if(Fa!==Ei)return Fa;vr=null}if(_n===ya)return Ei;var ai=O?--ya:_n++;vr=oe(Ar&&Ar[ai],Rt-5,lr+(ai<>>D&f,Pe=A&&fe0){var ze=A&&A.array[fe],tt=Qu(ze,O,D-5,F,q,ae);return tt===ze?A:((oe=il(A,O)).array[fe]=tt,oe)}return Pe&&A.array[fe]===q?A:(ae&&m(ae),oe=il(A,O),q===void 0&&fe===oe.array.length-1?oe.array.pop():oe.array[fe]=q,oe)}function il(A,O){return O&&A&&O===A.ownerID?A:new Ha(A?A.array.slice():[],O)}function ah(A,O){if(O>=sl(A._capacity))return A._tail;if(O<1<0;)D=D.array[O>>>F&f],F-=5;return D}}function ri(A,O,D){(function(Tn,Hn,na){var aa=Tn._origin+(Hn===void 0?0:Hn),Bn=na===void 0?Tn._capacity:na<0?Tn._capacity+na:Tn._origin+na;if(Number.isFinite(Bn)&&Bn>sc||Number.isFinite(aa)&&aa<-sc||Number.isFinite(Bn)&&Number.isFinite(aa)&&Bn-aa>sc)throw new RangeError("Invalid List size: a List cannot hold more than "+sc+" (2 ** 30) values.")})(A,O,D),O!==void 0&&(O|=0),D!==void 0&&(D|=0);var F=A.__ownerID||new g,q=A._origin,ae=A._capacity,oe=q+O,fe=D===void 0?ae:D<0?ae+D:q+D;if(oe===q&&fe===ae)return A;if(oe>=fe)return A.clear();for(var Pe=A._level,ze=A._root,tt=0;oe+tt<0;)ze=new Ha(ze&&ze.array.length?[void 0,ze]:[],F),tt+=Jc(Pe+=5);tt&&(oe+=tt,q+=tt,fe+=tt,ae+=tt);for(var At=sl(ae),Rt=sl(fe);Rt>=Jc(Pe+5);)ze=new Ha(ze&&ze.array.length?[ze]:[],F),Pe+=5;var lr=A._tail,vr=RtAt?new Ha([],F):lr;if(lr&&Rt>At&&oe5;_n-=5){var ya=At>>>_n&f;Ar=Ar.array[ya]=il(Ar.array[ya],F)}Ar.array[At>>>5&f]=lr}if(fe=Rt)oe-=Rt,fe-=Rt,Pe=5,ze=null,vr=vr&&vr.removeBefore(F,0,oe);else if(oe>q||Rt>>Pe&f;if(Fa!==Rt>>>Pe&f)break;Fa&&(tt+=(1<q&&(ze=ze.removeBefore(F,Pe,oe-tt)),ze&&Rt>>5<<5}var sc=Math.pow(2,30);function Jc(A){return A<31?1<=v&&oe.size>=2*ae.size?(F=(q=oe.filter((function(ze,tt){return ze!==void 0&&fe!==tt}))).toKeyedSeq().map((function(ze){return ze[0]})).flip().toMap(),A.__ownerID&&(F.__ownerID=q.__ownerID=A.__ownerID)):(F=ae.remove(O),q=fe===oe.size-1?oe.pop():oe.set(fe,void 0))}else if(Pe){if(D===oe.get(fe)[1])return A;F=ae,q=oe.set(fe,[O,D])}else F=ae.set(O,oe.size),q=oe.set(oe.size,[O,D]);return A.__ownerID?(A.size=F.size,A._map=F,A._list=q,A.__hash=void 0,A.__altered=!0,A):Wc(F,q)}Ra.isOrderedMap=St,Ra.prototype[u]=!0,Ra.prototype[h]=Ra.prototype.remove;var ep="@@__IMMUTABLE_STACK__@@";function cl(A){return!!(A&&A[ep])}var ul=(function(A){function O(D){return D==null?Si():cl(D)?D:Si().pushAll(D)}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O.of=function(){return this(arguments)},O.prototype.toString=function(){return this.__toString("Stack [","]")},O.prototype.get=function(F,q){var ae=this._head;for(F=_(this,F);ae&&F--;)ae=ae.next;return ae?ae.value:q},O.prototype.peek=function(){return this._head&&this._head.value},O.prototype.push=function(){var F=arguments;if(arguments.length===0)return this;for(var q=this.size+arguments.length,ae=this._head,oe=arguments.length-1;oe>=0;oe--)ae={value:F[oe],next:ae};return this.__ownerID?(this.size=q,this._head=ae,this.__hash=void 0,this.__altered=!0,this):qi(q,ae)},O.prototype.pushAll=function(F){if((F=A(F)).size===0)return this;if(this.size===0&&cl(F))return F;mr(F.size);var q=this.size,ae=this._head;return F.__iterate((function(oe){q++,ae={value:oe,next:ae}}),!0),this.__ownerID?(this.size=q,this._head=ae,this.__hash=void 0,this.__altered=!0,this):qi(q,ae)},O.prototype.pop=function(){return this.slice(1)},O.prototype.clear=function(){return this.size===0?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):Si()},O.prototype.slice=function(F,q){if(I(F,q,this.size))return this;var ae=j(F,this.size);if(M(q,this.size)!==this.size)return A.prototype.slice.call(this,F,q);for(var oe=this.size-ae,fe=this._head;ae--;)fe=fe.next;return this.__ownerID?(this.size=oe,this._head=fe,this.__hash=void 0,this.__altered=!0,this):qi(oe,fe)},O.prototype.__ensureOwner=function(F){return F===this.__ownerID?this:F?qi(this.size,this._head,F,this.__hash):this.size===0?Si():(this.__ownerID=F,this.__altered=!1,this)},O.prototype.__iterate=function(F,q){var ae=this;if(q)return new kt(this.toArray()).__iterate((function(Pe,ze){return F(Pe,ze,ae)}),q);for(var oe=0,fe=this._head;fe&&F(fe.value,oe++,this)!==!1;)fe=fe.next;return oe},O.prototype.__iterator=function(F,q){if(q)return new kt(this.toArray()).__iterator(F,q);var ae=0,oe=this._head;return new Ke((function(){if(oe){var fe=oe.value;return oe=oe.next,ft(F,ae++,fe)}return{value:void 0,done:!0}}))},O})(ke);ul.isStack=cl;var tp,xa=ul.prototype;function qi(A,O,D,F){var q=Object.create(xa);return q.size=A,q._head=O,q.__ownerID=D,q.__hash=F,q.__altered=!1,q}function Si(){return tp||(tp=qi(0))}xa[ep]=!0,xa.shift=xa.pop,xa.unshift=xa.push,xa.unshiftAll=xa.pushAll,xa.withMutations=jo,xa.wasAltered=Es,xa.asImmutable=el,xa["@@transducer/init"]=xa.asMutable=ac,xa["@@transducer/step"]=function(A,O){return A.unshift(O)},xa["@@transducer/result"]=function(A){return A.asImmutable()};var rp="@@__IMMUTABLE_SET__@@";function Ui(A){return!!(A&&A[rp])}function pl(A){return Ui(A)&&at(A)}function hl(A,O){if(A===O)return!0;if(!B(O)||A.size!==void 0&&O.size!==void 0&&A.size!==O.size||A.__hash!==void 0&&O.__hash!==void 0&&A.__hash!==O.__hash||te(A)!==te(O)||se(A)!==se(O)||at(A)!==at(O))return!1;if(A.size===0&&O.size===0)return!0;var D=!Te(A);if(at(A)){var F=A.entries();return O.every((function(Pe,ze){var tt=F.next().value;return tt&&Qe(tt[1],Pe)&&(D||Qe(tt[0],ze))}))&&F.next().done}var q=!1;if(A.size===void 0)if(O.size===void 0)typeof A.cacheResult=="function"&&A.cacheResult();else{q=!0;var ae=A;A=O,O=ae}var oe=!0,fe=O.__iterate((function(Pe,ze){if(D?!A.has(Pe):q?!Qe(Pe,A.get(ze,y)):!Qe(A.get(ze,y),Pe))return oe=!1,!1}));return oe&&A.size===fe}function xi(A,O){var D=function(F){A.prototype[F]=O[F]};return Object.keys(O).forEach(D),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(O).forEach(D),A}function xs(A){if(!A||typeof A!="object")return A;if(!B(A)){if(!Sa(A))return A;A=ar(A)}if(te(A)){var O={};return A.__iterate((function(F,q){Yl(q)||(O[q]=xs(F))})),O}var D=[];return A.__iterate((function(F){D.push(xs(F))})),D}var No=(function(A){function O(D){return D==null?ni():Ui(D)&&!at(D)?D:ni().withMutations((function(F){var q=A(D);mr(q.size),q.forEach((function(ae){return F.add(ae)}))}))}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O.of=function(){return this(arguments)},O.fromKeys=function(F){return this(ge(F).keySeq())},O.intersect=function(F){return(F=he(F).toArray()).length?ra.intersect.apply(O(F.pop()),F):ni()},O.union=function(F){return(F=he(F).toArray()).length?ra.union.apply(O(F.pop()),F):ni()},O.prototype.toString=function(){return this.__toString("Set {","}")},O.prototype.has=function(F){return this._map.has(F)},O.prototype.add=function(F){return dl(this,this._map.set(F,F))},O.prototype.remove=function(F){return dl(this,this._map.remove(F))},O.prototype.clear=function(){return dl(this,this._map.clear())},O.prototype.map=function(F,q){var ae=this,oe=!1,fe=dl(this,this._map.mapEntries((function(Pe){var ze=Pe[1],tt=F.call(q,ze,ze,ae);return tt!==ze&&(oe=!0),[tt,tt]}),q));return oe?fe:this},O.prototype.union=function(){for(var F=[],q=arguments.length;q--;)F[q]=arguments[q];return(F=F.filter((function(ae){return ae.size!==0}))).length===0?this:this.size!==0||this.__ownerID||F.length!==1?this.withMutations((function(ae){for(var oe=0;oe=0&&q=0&&ae>>-15,461845907),Pe=$t(Pe<<13|Pe>>>-13,5),Pe=Pe+3864292196^fe,Pe=$t(Pe^Pe>>>16,2246822507),Pe=$t(Pe^Pe>>>13,3266489909),Pe=Pt(Pe^Pe>>>16),Pe})(D.__iterate(q?F?function(oe,fe){ae=31*ae+yl(kr(oe),kr(fe))|0}:function(oe,fe){ae=ae+yl(kr(oe),kr(fe))|0}:F?function(oe){ae=31*ae+kr(oe)|0}:function(oe){ae=ae+kr(oe)|0}),ae)})(this))}});var la=he.prototype;la[Y]=!0,la[De]=la.values,la.toJSON=la.toArray,la.__toStringMapper=Ja,la.inspect=la.toSource=function(){return this.toString()},la.chain=la.flatMap,la.contains=la.includes,xi(ge,{flip:function(){return Nr(this,fn(this))},mapEntries:function(O,D){var F=this,q=0;return Nr(this,this.toSeq().map((function(ae,oe){return O.call(D,[oe,ae],q++,F)})).fromEntrySeq())},mapKeys:function(O,D){var F=this;return Nr(this,this.toSeq().flip().map((function(q,ae){return O.call(D,q,ae,F)})).flip())}});var Vi=ge.prototype;Vi[X]=!0,Vi[De]=la.entries,Vi.toJSON=gl,Vi.__toStringMapper=function(A,O){return Ja(O)+": "+Ja(A)},xi(ke,{toKeyedSeq:function(){return new Cr(this,!1)},filter:function(O,D){return Nr(this,ia(this,O,D,!1))},findIndex:function(O,D){var F=this.findEntry(O,D);return F?F[0]:-1},indexOf:function(O){var D=this.keyOf(O);return D===void 0?-1:D},lastIndexOf:function(O){var D=this.lastKeyOf(O);return D===void 0?-1:D},reverse:function(){return Nr(this,Hr(this,!1))},slice:function(O,D){return Nr(this,za(this,O,D,!1))},splice:function(O,D){var F=arguments.length;if(D=Math.max(D||0,0),F===0||F===2&&!D)return this;O=j(O,O<0?this.count():this.size);var q=this.slice(0,O);return Nr(this,F===1?q:q.concat(Yt(arguments,2),this.slice(O+D)))},findLastIndex:function(O,D){var F=this.findLastEntry(O,D);return F?F[0]:-1},first:function(O){return this.get(0,O)},flatten:function(O){return Nr(this,ea(this,O,!1))},get:function(O,D){return(O=_(this,O))<0||this.size===1/0||this.size!==void 0&&O>this.size?D:this.find((function(F,q){return q===O}),void 0,D)},has:function(O){return(O=_(this,O))>=0&&(this.size!==void 0?this.size===1/0||OO?-1:0}function yl(A,O){return A^O+2654435769+(A<<6)+(A>>2)}Wi.has=la.includes,Wi.contains=Wi.includes,Wi.keys=Wi.values,xi(Tt,Vi),xi(Gt,Ji),xi(Sr,Wi);var Da=(function(A){function O(D){return D==null?_i():pl(D)?D:_i().withMutations((function(F){var q=Ve(D);mr(q.size),q.forEach((function(ae){return F.add(ae)}))}))}return A&&(O.__proto__=A),O.prototype=Object.create(A&&A.prototype),O.prototype.constructor=O,O.of=function(){return this(arguments)},O.fromKeys=function(F){return this(ge(F).keySeq())},O.prototype.toString=function(){return this.__toString("OrderedSet {","}")},O})(No);Da.isOrderedSet=pl;var Hi,Ho=Da.prototype;function _a(A,O){var D=Object.create(Ho);return D.size=A?A.size:0,D._map=A,D.__ownerID=O,D}function _i(){return Hi||(Hi=_a(ll()))}Ho[u]=!0,Ho.zip=Ji.zip,Ho.zipWith=Ji.zipWith,Ho.zipAll=Ji.zipAll,Ho.__empty=_i,Ho.__make=_a;var cc={LeftThenRight:-1,RightThenLeft:1},jn=function(O,D){var F;(function(fe){if(gt(fe))throw new Error("Can not call `Record` with an immutable Record as default values. Use a plain javascript object instead.");if(Re(fe))throw new Error("Can not call `Record` with an immutable Collection as default values. Use a plain javascript object instead.");if(fe===null||typeof fe!="object")throw new Error("Can not call `Record` with a non-object as default values. Use a plain javascript object instead.")})(O);var q=function(fe){var Pe=this;if(fe instanceof q)return fe;if(!(this instanceof q))return new q(fe);if(!F){F=!0;var ze=Object.keys(O),tt=ae._indices={};ae._name=D,ae._keys=ze,ae._defaultValues=O;for(var At=0;At2?[]:void 0,{"":A})}function eu(A,O,D,F,q,ae){if(typeof D!="string"&&!Re(D)&&(ur(D)||Wt(D)||sa(D))){if(~A.indexOf(D))throw new TypeError("Cannot convert circular structure to Immutable");A.push(D),q&&F!==""&&q.push(F);var oe=O.call(ae,F,ar(D).map((function(fe,Pe){return eu(A,O,fe,Pe,q,D)})),q&&q.slice());return A.pop(),q&&q.pop(),oe}return D}function op(A,O){return se(O)?O.toList():te(O)?O.toMap():O.toSet()}var tu="4.3.9",ip=he;const sp={version:tu,Collection:he,Iterable:he,Seq:ar,Map:vi,OrderedMap:Ra,List:Wo,Stack:ul,Set:No,OrderedSet:Da,PairSorting:cc,Record:jn,Range:zi,Repeat:vl,is:Qe,fromJS:Zc,hash:kr,isImmutable:Re,isCollection:B,isKeyed:te,isIndexed:se,isAssociative:Te,isOrdered:at,isValueObject:Bt,isPlainObject:sa,isSeq:qe,isList:Bi,isMap:Fe,isOrderedMap:St,isStack:cl,isSet:Ui,isOrderedSet:pl,isRecord:gt,get:Ia,getIn:lc,has:Wa,hasIn:ml,merge:nc,mergeDeep:Ku,mergeWith:Hu,mergeDeepWith:$c,remove:gs,removeIn:Zl,set:ys,setIn:Wu,update:Ao,updateIn:lo}},56698(w){typeof Object.create=="function"?w.exports=function(s,h){h&&(s.super_=h,s.prototype=Object.create(h.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}))}:w.exports=function(s,h){if(h){s.super_=h;var v=function(){};v.prototype=h.prototype,s.prototype=new v,s.prototype.constructor=s}}},69600(w){var N,s,h=Function.prototype.toString,v=typeof Reflect=="object"&&Reflect!==null&&Reflect.apply;if(typeof v=="function"&&typeof Object.defineProperty=="function")try{N=Object.defineProperty({},"length",{get:function(){throw s}}),s={},v((function(){throw 42}),null,N)}catch(j){j!==s&&(v=null)}else v=null;var f=/^\s*class\b/,y=function(M){try{var z=h.call(M);return f.test(z)}catch{return!1}},m=function(M){try{return!y(M)&&(h.call(M),!0)}catch{return!1}},g=Object.prototype.toString,S=typeof Symbol=="function"&&!!Symbol.toStringTag,_=!(0 in[,]),T=function(){return!1};if(typeof document=="object"){var I=document.all;g.call(I)===g.call(document.all)&&(T=function(M){if((_||!M)&&(M===void 0||typeof M=="object"))try{var z=g.call(M);return(z==="[object HTMLAllCollection]"||z==="[object HTML document.all class]"||z==="[object HTMLCollection]"||z==="[object Object]")&&M("")==null}catch{}return!1})}w.exports=v?function(M){if(T(M))return!0;if(!M||typeof M!="function"&&typeof M!="object")return!1;try{v(M,null,N)}catch(z){if(z!==s)return!1}return!y(M)&&m(M)}:function(M){if(T(M))return!0;if(!M||typeof M!="function"&&typeof M!="object")return!1;if(S)return m(M);if(y(M))return!1;var z=g.call(M);return!(z!=="[object Function]"&&z!=="[object GeneratorFunction]"&&!/^\[object HTML/.test(z))&&m(M)}},35680(w,N,s){var h=s(25767);w.exports=function(f){return!!h(f)}},64634(w){var N={}.toString;w.exports=Array.isArray||function(s){return N.call(s)=="[object Array]"}},5419(w){w.exports=function(N,s,h,v){var f=new Blob(v!==void 0?[v,N]:[N],{type:h||"application/octet-stream"});if(window.navigator.msSaveBlob!==void 0)window.navigator.msSaveBlob(f,s);else{var y=window.URL&&window.URL.createObjectURL?window.URL.createObjectURL(f):window.webkitURL.createObjectURL(f),m=document.createElement("a");m.style.display="none",m.href=y,m.setAttribute("download",s),m.download===void 0&&m.setAttribute("target","_blank"),document.body.appendChild(m),m.click(),setTimeout((function(){document.body.removeChild(m),window.URL.revokeObjectURL(y)}),200)}}},20181(w,N,s){var h=/^\s+|\s+$/g,v=/^[-+]0x[0-9a-f]+$/i,f=/^0b[01]+$/i,y=/^0o[0-7]+$/i,m=parseInt,g=typeof s.g=="object"&&s.g&&s.g.Object===Object&&s.g,S=typeof self=="object"&&self&&self.Object===Object&&self,_=g||S||Function("return this")(),T=Object.prototype.toString,I=Math.max,j=Math.min,M=function(){return _.Date.now()};function z(Y){var B=typeof Y;return!!Y&&(B=="object"||B=="function")}function K(Y){if(typeof Y=="number")return Y;if((function(ie){return typeof ie=="symbol"||(function(Te){return!!Te&&typeof Te=="object"})(ie)&&T.call(ie)=="[object Symbol]"})(Y))return NaN;if(z(Y)){var B=typeof Y.valueOf=="function"?Y.valueOf():Y;Y=z(B)?B+"":B}if(typeof Y!="string")return Y===0?Y:+Y;Y=Y.replace(h,"");var X=f.test(Y);return X||y.test(Y)?m(Y.slice(2),X?2:8):v.test(Y)?NaN:+Y}w.exports=function(B,X,te){var ie,se,Te,he,ge,ke,Ve=0,He=!1,qe=!1,nt=!0;if(typeof B!="function")throw new TypeError("Expected a function");function gt(Se){var De=ie,Ke=se;return ie=se=void 0,Ve=Se,he=B.apply(Ke,De)}function Re(Se){var De=Se-ke;return ke===void 0||De>=X||De<0||qe&&Se-Ve>=Te}function u(){var Se=M();if(Re(Se))return at(Se);ge=setTimeout(u,(function(Ke){var ft=X-(Ke-ke);return qe?j(ft,Te-(Ke-Ve)):ft})(Se))}function at(Se){return ge=void 0,nt&&ie?gt(Se):(ie=se=void 0,he)}function Xe(){var Se=M(),De=Re(Se);if(ie=arguments,se=this,ke=Se,De){if(ge===void 0)return(function(ft){return Ve=ft,ge=setTimeout(u,X),He?gt(ft):he})(ke);if(qe)return ge=setTimeout(u,X),gt(ke)}return ge===void 0&&(ge=setTimeout(u,X)),he}return X=K(X)||0,z(te)&&(He=!!te.leading,Te=(qe="maxWait"in te)?I(K(te.maxWait)||0,X):Te,nt="trailing"in te?!!te.trailing:nt),Xe.cancel=function(){ge!==void 0&&clearTimeout(ge),Ve=0,ie=ke=se=ge=void 0},Xe.flush=function(){return ge===void 0?he:at(M())},Xe}},55580(w,N,s){var h=s(56110)(s(9325),"DataView");w.exports=h},21549(w,N,s){var h=s(22032),v=s(63862),f=s(66721),y=s(12749),m=s(35749);function g(S){var _=-1,T=S==null?0:S.length;for(this.clear();++_-1}},70695(w,N,s){var h=s(78096),v=s(72428),f=s(56449),y=s(3656),m=s(30361),g=s(37167),S=Object.prototype.hasOwnProperty;w.exports=function(T,I){var j=f(T),M=!j&&v(T),z=!j&&!M&&y(T),K=!j&&!M&&!z&&g(T),Y=j||M||z||K,B=Y?h(T.length,String):[],X=B.length;for(var te in T)!I&&!S.call(T,te)||Y&&(te=="length"||z&&(te=="offset"||te=="parent")||K&&(te=="buffer"||te=="byteLength"||te=="byteOffset")||m(te,X))||B.push(te);return B}},34932(w){w.exports=function(s,h){for(var v=-1,f=s==null?0:s.length,y=Array(f);++v0&&g(j)?m>1?f(j,m-1,g,S,_):h(_,j):S||(_[_.length]=j)}return _}},86649(w,N,s){var h=s(83221)();w.exports=h},30641(w,N,s){var h=s(86649),v=s(95950);w.exports=function(y,m){return y&&h(y,m,v)}},47422(w,N,s){var h=s(31769),v=s(77797);w.exports=function(y,m){for(var g=0,S=(m=h(m,y)).length;y!=null&&gy?0:y+h),(v=v>y?y:v)<0&&(v+=y),y=h>v?0:v-h>>>0,h>>>=0;for(var m=Array(y);++f=g?f:h(f,y,m)}},49653(w,N,s){var h=s(37828);w.exports=function(f){var y=new f.constructor(f.byteLength);return new h(y).set(new h(f)),y}},93290(w,N,s){w=s.nmd(w);var h=s(9325),v=N&&!N.nodeType&&N,f=v&&w&&!w.nodeType&&w,y=f&&f.exports===v?h.Buffer:void 0,m=y?y.allocUnsafe:void 0;w.exports=function(S,_){if(_)return S.slice();var T=S.length,I=m?m(T):new S.constructor(T);return S.copy(I),I}},76169(w,N,s){var h=s(49653);w.exports=function(f,y){var m=y?h(f.buffer):f.buffer;return new f.constructor(m,f.byteOffset,f.byteLength)}},73201(w){var N=/\w*$/;w.exports=function(h){var v=new h.constructor(h.source,N.exec(h));return v.lastIndex=h.lastIndex,v}},93736(w,N,s){var h=s(51873),v=h?h.prototype:void 0,f=v?v.valueOf:void 0;w.exports=function(m){return f?Object(f.call(m)):{}}},71961(w,N,s){var h=s(49653);w.exports=function(f,y){var m=y?h(f.buffer):f.buffer;return new f.constructor(m,f.byteOffset,f.length)}},91596(w){var N=Math.max;w.exports=function(h,v,f,y){for(var m=-1,g=h.length,S=f.length,_=-1,T=v.length,I=N(g-S,0),j=Array(T+I),M=!y;++_1?g[_-1]:void 0,I=_>2?g[2]:void 0;for(T=y.length>3&&typeof T=="function"?(_--,T):void 0,I&&v(g[0],g[1],I)&&(T=_<3?void 0:T,_=1),m=Object(m);++S<_;){var j=g[S];j&&y(m,j,S,T)}return m}))}},38329(w,N,s){var h=s(64894);w.exports=function(f,y){return function(m,g){if(m==null)return m;if(!h(m))return f(m,g);for(var S=m.length,_=y?S:-1,T=Object(m);(y?_--:++_-1?T[I?g[j]:j]:void 0}}},37471(w,N,s){var h=s(91596),v=s(53320),f=s(58523),y=s(82819),m=s(18073),g=s(11287),S=s(68294),_=s(36306),T=s(9325);w.exports=function I(j,M,z,K,Y,B,X,te,ie,se){var Te=128&M,he=1&M,ge=2&M,ke=24&M,Ve=512&M,He=ge?void 0:y(j);return function qe(){for(var nt=arguments.length,gt=Array(nt),Re=nt;Re--;)gt[Re]=arguments[Re];if(ke)var u=g(qe),at=f(gt,u);if(K&&(gt=h(gt,K,Y,ke)),B&&(gt=v(gt,B,X,ke)),nt-=at,ke&&nt1&>.reverse(),Te&&ieM))return!1;var K=I.get(m),Y=I.get(g);if(K&&Y)return K==g&&Y==m;var B=-1,X=!0,te=2&S?new h:void 0;for(I.set(m,g),I.set(g,m);++B1?"& ":"")+v[y],v=v.join(f>2?", ":" "),h.replace(N,`{ +/* [wrapped with `+v+`] */ +`)}},45891(w,N,s){var h=s(51873),v=s(72428),f=s(56449),y=h?h.isConcatSpreadable:void 0;w.exports=function(g){return f(g)||v(g)||!!(y&&g&&g[y])}},30361(w){var N=/^(?:0|[1-9]\d*)$/;w.exports=function(h,v){var f=typeof h;return!!(v=v??9007199254740991)&&(f=="number"||f!="symbol"&&N.test(h))&&h>-1&&h%1==0&&h-1}},31175(w,N,s){var h=s(26025);w.exports=function(f,y){var m=this.__data__,g=h(m,f);return g<0?(++this.size,m.push([f,y])):m[g][1]=y,this}},63040(w,N,s){var h=s(21549),v=s(80079),f=s(68223);w.exports=function(){this.size=0,this.__data__={hash:new h,map:new(f||v),string:new h}}},17670(w,N,s){var h=s(12651);w.exports=function(f){var y=h(this,f).delete(f);return this.size-=y?1:0,y}},90289(w,N,s){var h=s(12651);w.exports=function(f){return h(this,f).get(f)}},4509(w,N,s){var h=s(12651);w.exports=function(f){return h(this,f).has(f)}},72949(w,N,s){var h=s(12651);w.exports=function(f,y){var m=h(this,f),g=m.size;return m.set(f,y),this.size+=m.size==g?0:1,this}},20317(w){w.exports=function(s){var h=-1,v=Array(s.size);return s.forEach((function(f,y){v[++h]=[y,f]})),v}},67197(w){w.exports=function(s,h){return function(v){return v!=null&&v[s]===h&&(h!==void 0||s in Object(v))}}},62224(w,N,s){var h=s(50104);w.exports=function(f){var y=h(f,(function(g){return m.size===500&&m.clear(),g})),m=y.cache;return y}},3209(w,N,s){var h=s(91596),v=s(53320),f=s(36306),y="__lodash_placeholder__",m=128,g=Math.min;w.exports=function(_,T){var I=_[1],j=T[1],M=I|j,z=M<131,K=j==m&&I==8||j==m&&I==256&&_[7].length<=T[8]||j==384&&T[7].length<=T[8]&&I==8;if(!z&&!K)return _;1&j&&(_[2]=T[2],M|=1&I?0:4);var Y=T[3];if(Y){var B=_[3];_[3]=B?h(B,Y,T[4]):Y,_[4]=B?f(_[3],y):T[4]}return(Y=T[5])&&(B=_[5],_[5]=B?v(B,Y,T[6]):Y,_[6]=B?f(_[5],y):T[6]),(Y=T[7])&&(_[7]=Y),j&m&&(_[8]=_[8]==null?T[8]:g(_[8],T[8])),_[9]==null&&(_[9]=T[9]),_[0]=T[0],_[1]=M,_}},48152(w,N,s){var h=s(28303),v=h&&new h;w.exports=v},81042(w,N,s){var h=s(56110)(Object,"create");w.exports=h},3650(w,N,s){var h=s(74335)(Object.keys,Object);w.exports=h},90181(w){w.exports=function(s){var h=[];if(s!=null)for(var v in Object(s))h.push(v);return h}},86009(w,N,s){w=s.nmd(w);var h=s(34840),v=N&&!N.nodeType&&N,f=v&&w&&!w.nodeType&&w,y=f&&f.exports===v&&h.process,m=(function(){try{var g=f&&f.require&&f.require("util").types;return g||y&&y.binding&&y.binding("util")}catch{}})();w.exports=m},59350(w){var N=Object.prototype.toString;w.exports=function(h){return N.call(h)}},74335(w){w.exports=function(s,h){return function(v){return s(h(v))}}},56757(w,N,s){var h=s(91033),v=Math.max;w.exports=function(y,m,g){return m=v(m===void 0?y.length-1:m,0),function(){for(var S=arguments,_=-1,T=v(S.length-m,0),I=Array(T);++_0){if(++v>=800)return arguments[0]}else v=0;return h.apply(void 0,arguments)}}},51420(w,N,s){var h=s(80079);w.exports=function(){this.__data__=new h,this.size=0}},90938(w){w.exports=function(s){var h=this.__data__,v=h.delete(s);return this.size=h.size,v}},63605(w){w.exports=function(s){return this.__data__.get(s)}},29817(w){w.exports=function(s){return this.__data__.has(s)}},80945(w,N,s){var h=s(80079),v=s(68223),f=s(53661);w.exports=function(m,g){var S=this.__data__;if(S instanceof h){var _=S.__data__;if(!v||_.length<199)return _.push([m,g]),this.size=++S.size,this;S=this.__data__=new f(_)}return S.set(m,g),this.size=S.size,this}},76959(w){w.exports=function(s,h,v){for(var f=v-1,y=s.length;++f=_||He<0||te&&Ve-B>=M}function he(){var Ve=v();if(Te(Ve))return ge(Ve);K=setTimeout(he,(function(qe){var nt=_-(qe-Y);return te?m(nt,M-(qe-B)):nt})(Ve))}function ge(Ve){return K=void 0,ie&&I?se(Ve):(I=j=void 0,z)}function ke(){var Ve=v(),He=Te(Ve);if(I=arguments,j=this,Y=Ve,He){if(K===void 0)return(function(nt){return B=nt,K=setTimeout(he,_),X?se(nt):z})(Y);if(te)return clearTimeout(K),K=setTimeout(he,_),se(Y)}return K===void 0&&(K=setTimeout(he,_)),z}return _=f(_)||0,h(T)&&(X=!!T.leading,M=(te="maxWait"in T)?y(f(T.maxWait)||0,_):M,ie="trailing"in T?!!T.trailing:ie),ke.cancel=function(){K!==void 0&&clearTimeout(K),B=0,I=Y=j=K=void 0},ke.flush=function(){return K===void 0?z:ge(v())},ke}},50828(w,N,s){var h=s(24647),v=s(13222),f=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,y=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]","g");w.exports=function(g){return(g=v(g))&&g.replace(f,h).replace(y,"")}},75288(w){w.exports=function(s,h){return s===h||s!=s&&h!=h}},60680(w,N,s){var h=s(13222),v=/[\\^$.*+?()[\]{}|]/g,f=RegExp(v.source);w.exports=function(m){return(m=h(m))&&f.test(m)?m.replace(v,"\\$&"):m}},7309(w,N,s){var h=s(62006)(s(24713));w.exports=h},24713(w,N,s){var h=s(2523),v=s(15389),f=s(61489),y=Math.max;w.exports=function(g,S,_){var T=g==null?0:g.length;if(!T)return-1;var I=_==null?0:f(_);return I<0&&(I=y(T+I,0)),h(g,v(S,3),I)}},35970(w,N,s){var h=s(83120);w.exports=function(f){return f!=null&&f.length?h(f,1):[]}},73424(w,N,s){var h=s(16962),v=s(2874),f=Array.prototype.push;function y(S,_){return _==2?function(T,I){return S(T,I)}:function(T){return S(T)}}function m(S){for(var _=S?S.length:0,T=Array(_);_--;)T[_]=S[_];return T}function g(S,_){return function(){var T=arguments.length;if(T){for(var I=Array(T);T--;)I[T]=arguments[T];var j=I[0]=_.apply(void 0,I);return S.apply(void 0,I),j}}}w.exports=function S(_,T,I,j){var M=typeof T=="function",z=T===Object(T);if(z&&(j=I,I=T,T=void 0),I==null)throw new TypeError;j||(j={});var K=!("cap"in j)||j.cap,Y=!("curry"in j)||j.curry,B=!("fixed"in j)||j.fixed,X=!("immutable"in j)||j.immutable,te=!("rearg"in j)||j.rearg,ie=M?I:v,se="curry"in j&&j.curry,Te="fixed"in j&&j.fixed,he="rearg"in j&&j.rearg,ge=M?I.runInContext():void 0,ke=M?I:{ary:_.ary,assign:_.assign,clone:_.clone,curry:_.curry,forEach:_.forEach,isArray:_.isArray,isError:_.isError,isFunction:_.isFunction,isWeakMap:_.isWeakMap,iteratee:_.iteratee,keys:_.keys,rearg:_.rearg,toInteger:_.toInteger,toPath:_.toPath},Ve=ke.ary,He=ke.assign,qe=ke.clone,nt=ke.curry,gt=ke.forEach,Re=ke.isArray,u=ke.isError,at=ke.isFunction,Xe=ke.isWeakMap,Se=ke.keys,De=ke.rearg,Ke=ke.toInteger,ft=ke.toPath,Nt=Se(h.aryMethod),Wt={castArray:function(kt){return function(){var yt=arguments[0];return Re(yt)?kt(m(yt)):kt.apply(void 0,arguments)}},iteratee:function(kt){return function(){var yt=arguments[1],Zt=kt(arguments[0],yt),pe=Zt.length;return K&&typeof yt=="number"?(yt=yt>2?yt-2:1,pe&&pe<=yt?Zt:y(Zt,yt)):Zt}},mixin:function(kt){return function(yt){var Zt=this;if(!at(Zt))return kt(Zt,Object(yt));var pe=[];return gt(Se(yt),(function(U){at(yt[U])&&pe.push([U,Zt.prototype[U]])})),kt(Zt,Object(yt)),gt(pe,(function(U){var Z=U[1];at(Z)?Zt.prototype[U[0]]=Z:delete Zt.prototype[U[0]]})),Zt}},nthArg:function(kt){return function(yt){var Zt=yt<0?1:Ke(yt)+1;return nt(kt(yt),Zt)}},rearg:function(kt){return function(yt,Zt){var pe=Zt?Zt.length:0;return nt(kt(yt,Zt),pe)}},runInContext:function(kt){return function(yt){return S(_,kt(yt),j)}}};function H(kt,yt){if(K){var Zt=h.iterateeRearg[kt];if(Zt)return(function(Z,ne){return ar(Z,(function(ye){var Ee=ne.length;return(function(St,Bt){return Bt==2?function(Qe,$t){return St.apply(void 0,arguments)}:function(Qe){return St.apply(void 0,arguments)}})(De(y(ye,Ee),ne),Ee)}))})(yt,Zt);var pe=!M&&h.iterateeAry[kt];if(pe)return(function(Z,ne){return ar(Z,(function(ye){return typeof ye=="function"?y(ye,ne):ye}))})(yt,pe)}return yt}function we(kt,yt,Zt){if(B&&(Te||!h.skipFixed[kt])){var pe=h.methodSpread[kt],U=pe&&pe.start;return U===void 0?Ve(yt,Zt):(function(ne,ye){return function(){for(var Ee=arguments.length,Fe=Ee-1,St=Array(Ee);Ee--;)St[Ee]=arguments[Ee];var Bt=St[ye],Qe=St.slice(0,ye);return Bt&&f.apply(Qe,Bt),ye!=Fe&&f.apply(Qe,St.slice(ye+1)),ne.apply(this,Qe)}})(yt,U)}return yt}function ct(kt,yt,Zt){return te&&Zt>1&&(he||!h.skipRearg[kt])?De(yt,h.methodRearg[kt]||h.aryRearg[Zt]):yt}function mt(kt,yt){for(var Zt=-1,pe=(yt=ft(yt)).length,U=pe-1,Z=qe(Object(kt)),ne=Z;ne!=null&&++Zt1?nt($t,Pt):$t})(0,pe=H(U,pe),ye),!1}})),!pe})),pe||(pe=Z),pe==yt&&(pe=se?nt(pe,1):function(){return yt.apply(this,arguments)}),pe.convert=ur(U,yt),pe.placeholder=yt.placeholder=Zt,pe}if(!z)return Tt(T,I,ie);var Gt=I,Sr=[];return gt(Nt,(function(kt){gt(h.aryMethod[kt],(function(yt){var Zt=Gt[h.remap[yt]||yt];Zt&&Sr.push([yt,Tt(yt,Zt,Gt)])}))})),gt(Se(Gt),(function(kt){var yt=Gt[kt];if(typeof yt=="function"){for(var Zt=Sr.length;Zt--;)if(Sr[Zt][0]==kt)return;yt.convert=ur(kt,yt),Sr.push([kt,yt])}})),gt(Sr,(function(kt){Gt[kt[0]]=kt[1]})),Gt.convert=function(yt){return Gt.runInContext.convert(yt)(void 0)},Gt.placeholder=Gt,gt(Se(Gt),(function(kt){gt(h.realToAlias[kt]||[],(function(yt){Gt[yt]=Gt[kt]}))})),Gt}},16962(w,N){N.aliasToReal={each:"forEach",eachRight:"forEachRight",entries:"toPairs",entriesIn:"toPairsIn",extend:"assignIn",extendAll:"assignInAll",extendAllWith:"assignInAllWith",extendWith:"assignInWith",first:"head",conforms:"conformsTo",matches:"isMatch",property:"get",__:"placeholder",F:"stubFalse",T:"stubTrue",all:"every",allPass:"overEvery",always:"constant",any:"some",anyPass:"overSome",apply:"spread",assoc:"set",assocPath:"set",complement:"negate",compose:"flowRight",contains:"includes",dissoc:"unset",dissocPath:"unset",dropLast:"dropRight",dropLastWhile:"dropRightWhile",equals:"isEqual",identical:"eq",indexBy:"keyBy",init:"initial",invertObj:"invert",juxt:"over",omitAll:"omit",nAry:"ary",path:"get",pathEq:"matchesProperty",pathOr:"getOr",paths:"at",pickAll:"pick",pipe:"flow",pluck:"map",prop:"get",propEq:"matchesProperty",propOr:"getOr",props:"at",symmetricDifference:"xor",symmetricDifferenceBy:"xorBy",symmetricDifferenceWith:"xorWith",takeLast:"takeRight",takeLastWhile:"takeRightWhile",unapply:"rest",unnest:"flatten",useWith:"overArgs",where:"conformsTo",whereEq:"isMatch",zipObj:"zipObject"},N.aryMethod={1:["assignAll","assignInAll","attempt","castArray","ceil","create","curry","curryRight","defaultsAll","defaultsDeepAll","floor","flow","flowRight","fromPairs","invert","iteratee","memoize","method","mergeAll","methodOf","mixin","nthArg","over","overEvery","overSome","rest","reverse","round","runInContext","spread","template","trim","trimEnd","trimStart","uniqueId","words","zipAll"],2:["add","after","ary","assign","assignAllWith","assignIn","assignInAllWith","at","before","bind","bindAll","bindKey","chunk","cloneDeepWith","cloneWith","concat","conformsTo","countBy","curryN","curryRightN","debounce","defaults","defaultsDeep","defaultTo","delay","difference","divide","drop","dropRight","dropRightWhile","dropWhile","endsWith","eq","every","filter","find","findIndex","findKey","findLast","findLastIndex","findLastKey","flatMap","flatMapDeep","flattenDepth","forEach","forEachRight","forIn","forInRight","forOwn","forOwnRight","get","groupBy","gt","gte","has","hasIn","includes","indexOf","intersection","invertBy","invoke","invokeMap","isEqual","isMatch","join","keyBy","lastIndexOf","lt","lte","map","mapKeys","mapValues","matchesProperty","maxBy","meanBy","merge","mergeAllWith","minBy","multiply","nth","omit","omitBy","overArgs","pad","padEnd","padStart","parseInt","partial","partialRight","partition","pick","pickBy","propertyOf","pull","pullAll","pullAt","random","range","rangeRight","rearg","reject","remove","repeat","restFrom","result","sampleSize","some","sortBy","sortedIndex","sortedIndexOf","sortedLastIndex","sortedLastIndexOf","sortedUniqBy","split","spreadFrom","startsWith","subtract","sumBy","take","takeRight","takeRightWhile","takeWhile","tap","throttle","thru","times","trimChars","trimCharsEnd","trimCharsStart","truncate","union","uniqBy","uniqWith","unset","unzipWith","without","wrap","xor","zip","zipObject","zipObjectDeep"],3:["assignInWith","assignWith","clamp","differenceBy","differenceWith","findFrom","findIndexFrom","findLastFrom","findLastIndexFrom","getOr","includesFrom","indexOfFrom","inRange","intersectionBy","intersectionWith","invokeArgs","invokeArgsMap","isEqualWith","isMatchWith","flatMapDepth","lastIndexOfFrom","mergeWith","orderBy","padChars","padCharsEnd","padCharsStart","pullAllBy","pullAllWith","rangeStep","rangeStepRight","reduce","reduceRight","replace","set","slice","sortedIndexBy","sortedLastIndexBy","transform","unionBy","unionWith","update","xorBy","xorWith","zipWith"],4:["fill","setWith","updateWith"]},N.aryRearg={2:[1,0],3:[2,0,1],4:[3,2,0,1]},N.iterateeAry={dropRightWhile:1,dropWhile:1,every:1,filter:1,find:1,findFrom:1,findIndex:1,findIndexFrom:1,findKey:1,findLast:1,findLastFrom:1,findLastIndex:1,findLastIndexFrom:1,findLastKey:1,flatMap:1,flatMapDeep:1,flatMapDepth:1,forEach:1,forEachRight:1,forIn:1,forInRight:1,forOwn:1,forOwnRight:1,map:1,mapKeys:1,mapValues:1,partition:1,reduce:2,reduceRight:2,reject:1,remove:1,some:1,takeRightWhile:1,takeWhile:1,times:1,transform:2},N.iterateeRearg={mapKeys:[1],reduceRight:[1,0]},N.methodRearg={assignInAllWith:[1,0],assignInWith:[1,2,0],assignAllWith:[1,0],assignWith:[1,2,0],differenceBy:[1,2,0],differenceWith:[1,2,0],getOr:[2,1,0],intersectionBy:[1,2,0],intersectionWith:[1,2,0],isEqualWith:[1,2,0],isMatchWith:[2,1,0],mergeAllWith:[1,0],mergeWith:[1,2,0],padChars:[2,1,0],padCharsEnd:[2,1,0],padCharsStart:[2,1,0],pullAllBy:[2,1,0],pullAllWith:[2,1,0],rangeStep:[1,2,0],rangeStepRight:[1,2,0],setWith:[3,1,2,0],sortedIndexBy:[2,1,0],sortedLastIndexBy:[2,1,0],unionBy:[1,2,0],unionWith:[1,2,0],updateWith:[3,1,2,0],xorBy:[1,2,0],xorWith:[1,2,0],zipWith:[1,2,0]},N.methodSpread={assignAll:{start:0},assignAllWith:{start:0},assignInAll:{start:0},assignInAllWith:{start:0},defaultsAll:{start:0},defaultsDeepAll:{start:0},invokeArgs:{start:2},invokeArgsMap:{start:2},mergeAll:{start:0},mergeAllWith:{start:0},partial:{start:1},partialRight:{start:1},without:{start:1},zipAll:{start:0}},N.mutate={array:{fill:!0,pull:!0,pullAll:!0,pullAllBy:!0,pullAllWith:!0,pullAt:!0,remove:!0,reverse:!0},object:{assign:!0,assignAll:!0,assignAllWith:!0,assignIn:!0,assignInAll:!0,assignInAllWith:!0,assignInWith:!0,assignWith:!0,defaults:!0,defaultsAll:!0,defaultsDeep:!0,defaultsDeepAll:!0,merge:!0,mergeAll:!0,mergeAllWith:!0,mergeWith:!0},set:{set:!0,setWith:!0,unset:!0,update:!0,updateWith:!0}},N.realToAlias=(function(){var s=Object.prototype.hasOwnProperty,h=N.aliasToReal,v={};for(var f in h){var y=h[f];s.call(v,y)?v[y].push(f):v[y]=[f]}return v})(),N.remap={assignAll:"assign",assignAllWith:"assignWith",assignInAll:"assignIn",assignInAllWith:"assignInWith",curryN:"curry",curryRightN:"curryRight",defaultsAll:"defaults",defaultsDeepAll:"defaultsDeep",findFrom:"find",findIndexFrom:"findIndex",findLastFrom:"findLast",findLastIndexFrom:"findLastIndex",getOr:"get",includesFrom:"includes",indexOfFrom:"indexOf",invokeArgs:"invoke",invokeArgsMap:"invokeMap",lastIndexOfFrom:"lastIndexOf",mergeAll:"merge",mergeAllWith:"mergeWith",padChars:"pad",padCharsEnd:"padEnd",padCharsStart:"padStart",propertyOf:"get",rangeStep:"range",rangeStepRight:"rangeRight",restFrom:"rest",spreadFrom:"spread",trimChars:"trim",trimCharsEnd:"trimEnd",trimCharsStart:"trimStart",zipAll:"zip"},N.skipFixed={castArray:!0,flow:!0,flowRight:!0,iteratee:!0,mixin:!0,rearg:!0,runInContext:!0},N.skipRearg={add:!0,assign:!0,assignIn:!0,bind:!0,bindKey:!0,concat:!0,difference:!0,divide:!0,eq:!0,gt:!0,gte:!0,isEqual:!0,lt:!0,lte:!0,matchesProperty:!0,merge:!0,multiply:!0,overArgs:!0,partial:!0,partialRight:!0,propertyOf:!0,random:!0,range:!0,rangeRight:!0,subtract:!0,zip:!0,zipObject:!0,zipObjectDeep:!0}},47934(w,N,s){w.exports={ary:s(64626),assign:s(74733),clone:s(32629),curry:s(49747),forEach:s(83729),isArray:s(56449),isError:s(23546),isFunction:s(1882),isWeakMap:s(47886),iteratee:s(33855),keys:s(88984),rearg:s(84195),toInteger:s(61489),toPath:s(42072)}},56367(w,N,s){w.exports=s(77731)},79920(w,N,s){var h=s(73424),v=s(47934);w.exports=function(y,m,g){return h(v,y,m,g)}},2874(w){w.exports={}},77731(w,N,s){var h=s(79920)("set",s(63560));h.placeholder=s(2874),w.exports=h},58156(w,N,s){var h=s(47422);w.exports=function(f,y,m){var g=f==null?void 0:h(f,y);return g===void 0?m:g}},61448(w,N,s){var h=s(20426),v=s(49326);w.exports=function(y,m){return y!=null&&v(y,m,h)}},80631(w,N,s){var h=s(28077),v=s(49326);w.exports=function(y,m){return y!=null&&v(y,m,h)}},83488(w){w.exports=function(s){return s}},72428(w,N,s){var h=s(27534),v=s(40346),f=Object.prototype,y=f.hasOwnProperty,m=f.propertyIsEnumerable,g=h((function(){return arguments})())?h:function(S){return v(S)&&y.call(S,"callee")&&!m.call(S,"callee")};w.exports=g},56449(w){var N=Array.isArray;w.exports=N},64894(w,N,s){var h=s(1882),v=s(30294);w.exports=function(y){return y!=null&&v(y.length)&&!h(y)}},83693(w,N,s){var h=s(64894),v=s(40346);w.exports=function(y){return v(y)&&h(y)}},53812(w,N,s){var h=s(72552),v=s(40346);w.exports=function(y){return y===!0||y===!1||v(y)&&h(y)=="[object Boolean]"}},3656(w,N,s){w=s.nmd(w);var h=s(9325),v=s(89935),f=N&&!N.nodeType&&N,y=f&&w&&!w.nodeType&&w,m=y&&y.exports===f?h.Buffer:void 0,g=(m?m.isBuffer:void 0)||v;w.exports=g},62193(w,N,s){var h=s(88984),v=s(5861),f=s(72428),y=s(56449),m=s(64894),g=s(3656),S=s(55527),_=s(37167),T=Object.prototype.hasOwnProperty;w.exports=function(j){if(j==null)return!0;if(m(j)&&(y(j)||typeof j=="string"||typeof j.splice=="function"||g(j)||_(j)||f(j)))return!j.length;var M=v(j);if(M=="[object Map]"||M=="[object Set]")return!j.size;if(S(j))return!h(j).length;for(var z in j)if(T.call(j,z))return!1;return!0}},2404(w,N,s){var h=s(60270);w.exports=function(f,y){return h(f,y)}},23546(w,N,s){var h=s(72552),v=s(40346),f=s(11331);w.exports=function(m){if(!v(m))return!1;var g=h(m);return g=="[object Error]"||g=="[object DOMException]"||typeof m.message=="string"&&typeof m.name=="string"&&!f(m)}},1882(w,N,s){var h=s(72552),v=s(23805);w.exports=function(y){if(!v(y))return!1;var m=h(y);return m=="[object Function]"||m=="[object GeneratorFunction]"||m=="[object AsyncFunction]"||m=="[object Proxy]"}},30294(w){w.exports=function(s){return typeof s=="number"&&s>-1&&s%1==0&&s<=9007199254740991}},87730(w,N,s){var h=s(29172),v=s(27301),f=s(86009),y=f&&f.isMap,m=y?v(y):h;w.exports=m},5187(w){w.exports=function(s){return s===null}},98023(w,N,s){var h=s(72552),v=s(40346);w.exports=function(y){return typeof y=="number"||v(y)&&h(y)=="[object Number]"}},23805(w){w.exports=function(s){var h=typeof s;return s!=null&&(h=="object"||h=="function")}},40346(w){w.exports=function(s){return s!=null&&typeof s=="object"}},11331(w,N,s){var h=s(72552),v=s(28879),f=s(40346),y=Function.prototype,m=Object.prototype,g=y.toString,S=m.hasOwnProperty,_=g.call(Object);w.exports=function(I){if(!f(I)||h(I)!="[object Object]")return!1;var j=v(I);if(j===null)return!0;var M=S.call(j,"constructor")&&j.constructor;return typeof M=="function"&&M instanceof M&&g.call(M)==_}},38440(w,N,s){var h=s(16038),v=s(27301),f=s(86009),y=f&&f.isSet,m=y?v(y):h;w.exports=m},85015(w,N,s){var h=s(72552),v=s(56449),f=s(40346);w.exports=function(m){return typeof m=="string"||!v(m)&&f(m)&&h(m)=="[object String]"}},44394(w,N,s){var h=s(72552),v=s(40346);w.exports=function(y){return typeof y=="symbol"||v(y)&&h(y)=="[object Symbol]"}},37167(w,N,s){var h=s(4901),v=s(27301),f=s(86009),y=f&&f.isTypedArray,m=y?v(y):h;w.exports=m},47886(w,N,s){var h=s(5861),v=s(40346);w.exports=function(y){return v(y)&&h(y)=="[object WeakMap]"}},33855(w,N,s){var h=s(9999),v=s(15389);w.exports=function(y){return v(typeof y=="function"?y:h(y,1))}},95950(w,N,s){var h=s(70695),v=s(88984),f=s(64894);w.exports=function(m){return f(m)?h(m):v(m)}},37241(w,N,s){var h=s(70695),v=s(72903),f=s(64894);w.exports=function(m){return f(m)?h(m,!0):v(m)}},68090(w){w.exports=function(s){var h=s==null?0:s.length;return h?s[h-1]:void 0}},50104(w,N,s){var h=s(53661);function v(f,y){if(typeof f!="function"||y!=null&&typeof y!="function")throw new TypeError("Expected a function");var m=function(){var g=arguments,S=y?y.apply(this,g):g[0],_=m.cache;if(_.has(S))return _.get(S);var T=f.apply(this,g);return m.cache=_.set(S,T)||_,T};return m.cache=new(v.Cache||h),m}v.Cache=h,w.exports=v},55364(w,N,s){var h=s(85250),v=s(20999)((function(f,y,m){h(f,y,m)}));w.exports=v},6048(w){w.exports=function(s){if(typeof s!="function")throw new TypeError("Expected a function");return function(){var h=arguments;switch(h.length){case 0:return!s.call(this);case 1:return!s.call(this,h[0]);case 2:return!s.call(this,h[0],h[1]);case 3:return!s.call(this,h[0],h[1],h[2])}return!s.apply(this,h)}}},63950(w){w.exports=function(){}},10124(w,N,s){var h=s(9325);w.exports=function(){return h.Date.now()}},90179(w,N,s){var h=s(34932),v=s(9999),f=s(19931),y=s(31769),m=s(21791),g=s(53138),S=s(38816),_=s(83349),T=S((function(I,j){var M={};if(I==null)return M;var z=!1;j=h(j,(function(Y){return Y=y(Y,I),z||(z=Y.length>1),Y})),m(I,_(I),M),z&&(M=v(M,7,g));for(var K=j.length;K--;)f(M,j[K]);return M}));w.exports=T},50583(w,N,s){var h=s(47237),v=s(17255),f=s(28586),y=s(77797);w.exports=function(g){return f(g)?h(y(g)):v(g)}},84195(w,N,s){var h=s(66977),v=s(38816),f=v((function(y,m){return h(y,256,void 0,void 0,void 0,m)}));w.exports=f},40860(w,N,s){var h=s(40882),v=s(80909),f=s(15389),y=s(85558),m=s(56449);w.exports=function(S,_,T){var I=m(S)?h:y,j=arguments.length<3;return I(S,f(_,4),T,j,v)}},63560(w,N,s){var h=s(73170);w.exports=function(f,y,m){return f==null?f:h(f,y,m)}},42426(w,N,s){var h=s(14248),v=s(15389),f=s(90916),y=s(56449),m=s(36800);w.exports=function(S,_,T){var I=y(S)?h:f;return T&&m(S,_,T)&&(_=void 0),I(S,v(_,3))}},63345(w){w.exports=function(){return[]}},89935(w){w.exports=function(){return!1}},17400(w,N,s){var h=s(99374),v=1/0;w.exports=function(y){return y?(y=h(y))===v||y===-1/0?17976931348623157e292*(y<0?-1:1):y==y?y:0:y===0?y:0}},61489(w,N,s){var h=s(17400);w.exports=function(f){var y=h(f),m=y%1;return y==y?m?y-m:y:0}},80218(w,N,s){var h=s(13222);w.exports=function(f){return h(f).toLowerCase()}},99374(w,N,s){var h=s(54128),v=s(23805),f=s(44394),y=/^[-+]0x[0-9a-f]+$/i,m=/^0b[01]+$/i,g=/^0o[0-7]+$/i,S=parseInt;w.exports=function(T){if(typeof T=="number")return T;if(f(T))return NaN;if(v(T)){var I=typeof T.valueOf=="function"?T.valueOf():T;T=v(I)?I+"":I}if(typeof T!="string")return T===0?T:+T;T=h(T);var j=m.test(T);return j||g.test(T)?S(T.slice(2),j?2:8):y.test(T)?NaN:+T}},42072(w,N,s){var h=s(34932),v=s(23007),f=s(56449),y=s(44394),m=s(61802),g=s(77797),S=s(13222);w.exports=function(T){return f(T)?h(T,g):y(T)?[T]:v(m(S(T)))}},69884(w,N,s){var h=s(21791),v=s(37241);w.exports=function(y){return h(y,v(y))}},13222(w,N,s){var h=s(77556);w.exports=function(f){return f==null?"":h(f)}},55808(w,N,s){var h=s(12507)("toUpperCase");w.exports=h},66645(w,N,s){var h=s(1733),v=s(45434),f=s(13222),y=s(22225);w.exports=function(g,S,_){return g=f(g),(S=_?void 0:S)===void 0?v(g)?y(g):h(g):g.match(S)||[]}},53758(w,N,s){var h=s(30980),v=s(56017),f=s(94033),y=s(56449),m=s(40346),g=s(80257),S=Object.prototype.hasOwnProperty;function _(T){if(m(T)&&!y(T)&&!(T instanceof h)){if(T instanceof v)return T;if(S.call(T,"__wrapped__"))return g(T)}return new v(T)}_.prototype=f.prototype,_.prototype.constructor=_,w.exports=_},47248(w,N,s){var h=s(16547),v=s(51234);w.exports=function(y,m){return v(y||[],m||[],h)}},43768(w,N,s){var h=s(45981),v=s(85587);N.highlight=y,N.highlightAuto=function(_,T){var I,j,M,z,K=T||{},Y=K.subset||h.listLanguages();K.prefix;var B=Y.length,X=-1;if(typeof _!="string")throw v("Expected `string` for value, got `%s`",_);for(j={relevance:0,language:null,value:[]},I={relevance:0,language:null,value:[]};++Xj.relevance&&(j=M),M.relevance>I.relevance&&(j=I,I=M));return j.language&&(I.secondBest=j),I},N.registerLanguage=function(_,T){h.registerLanguage(_,T)},N.listLanguages=function(){return h.listLanguages()},N.registerAlias=function(_,T){var I,j=_;T&&((j={})[_]=T);for(I in j)h.registerAliases(j[I],{languageName:I})},m.prototype.addText=function(_){var T,I,j=this.stack;_!==""&&(T=j[j.length-1],(I=T.children[T.children.length-1])&&I.type==="text"?I.value+=_:T.children.push({type:"text",value:_}))},m.prototype.addKeyword=function(_,T){this.openNode(T),this.addText(_),this.closeNode()},m.prototype.addSublanguage=function(_,T){var I=this.stack,j=I[I.length-1],M=_.rootNode.children,z=T?{type:"element",tagName:"span",properties:{className:[T]},children:M}:M;j.children=j.children.concat(z)},m.prototype.openNode=function(_){var T=this.stack,I=this.options.classPrefix+_,j=T[T.length-1],M={type:"element",tagName:"span",properties:{className:[I]},children:[]};j.children.push(M),T.push(M)},m.prototype.closeNode=function(){this.stack.pop()},m.prototype.closeAllNodes=g,m.prototype.finalize=g,m.prototype.toHTML=function(){return""};var f="hljs-";function y(S,_,T){var I,j=h.configure({}),M=(T||{}).prefix;if(typeof S!="string")throw v("Expected `string` for name, got `%s`",S);if(!h.getLanguage(S))throw v("Unknown language: `%s` is not registered",S);if(typeof _!="string")throw v("Expected `string` for value, got `%s`",_);if(M==null&&(M=f),h.configure({__emitter:m,classPrefix:M}),I=h.highlight(_,{language:S,ignoreIllegals:!0}),h.configure(j||{}),I.errorRaised)throw I.errorRaised;return{relevance:I.relevance,language:I.language,value:I.emitter.rootNode.children}}function m(S){this.options=S,this.rootNode={children:[]},this.stack=[this.rootNode]}function g(){}},71514(w){w.exports=Math.abs},58968(w){w.exports=Math.floor},94459(w){w.exports=Number.isNaN||function(s){return s!=s}},6188(w){w.exports=Math.max},68002(w){w.exports=Math.min},75880(w){w.exports=Math.pow},70414(w){w.exports=Math.round},73093(w,N,s){var h=s(94459);w.exports=function(f){return h(f)||f===0?f:f<0?-1:1}},92340(w,N,s){const h=s(6048);function v(y){return typeof y=="string"?m=>m.element===y:y.constructor&&y.extend?m=>m instanceof y:y}class f{constructor(m){this.elements=m||[]}toValue(){return this.elements.map((m=>m.toValue()))}map(m,g){return this.elements.map(m,g)}flatMap(m,g){return this.map(m,g).reduce(((S,_)=>S.concat(_)),[])}compactMap(m,g){const S=[];return this.forEach((_=>{const T=m.bind(g)(_);T&&S.push(T)})),S}filter(m,g){return m=v(m),new f(this.elements.filter(m,g))}reject(m,g){return m=v(m),new f(this.elements.filter(h(m),g))}find(m,g){return m=v(m),this.elements.find(m,g)}forEach(m,g){this.elements.forEach(m,g)}reduce(m,g){return this.elements.reduce(m,g)}includes(m){return this.elements.some((g=>g.equals(m)))}shift(){return this.elements.shift()}unshift(m){this.elements.unshift(this.refract(m))}push(m){return this.elements.push(this.refract(m)),this}add(m){this.push(m)}get(m){return this.elements[m]}getValue(m){const g=this.elements[m];if(g)return g.toValue()}get length(){return this.elements.length}get isEmpty(){return this.elements.length===0}get first(){return this.elements[0]}}typeof Symbol<"u"&&(f.prototype[Symbol.iterator]=function(){return this.elements[Symbol.iterator]()}),w.exports=f},55973(w){class N{constructor(h,v){this.key=h,this.value=v}clone(){const h=new N;return this.key&&(h.key=this.key.clone()),this.value&&(h.value=this.value.clone()),h}}w.exports=N},3110(w,N,s){const h=s(5187),v=s(85015),f=s(98023),y=s(53812),m=s(23805),g=s(85105),S=s(86804);class _{constructor(I){this.elementMap={},this.elementDetection=[],this.Element=S.Element,this.KeyValuePair=S.KeyValuePair,I&&I.noDefault||this.useDefault(),this._attributeElementKeys=[],this._attributeElementArrayKeys=[]}use(I){return I.namespace&&I.namespace({base:this}),I.load&&I.load({base:this}),this}useDefault(){return this.register("null",S.NullElement).register("string",S.StringElement).register("number",S.NumberElement).register("boolean",S.BooleanElement).register("array",S.ArrayElement).register("object",S.ObjectElement).register("member",S.MemberElement).register("ref",S.RefElement).register("link",S.LinkElement),this.detect(h,S.NullElement,!1).detect(v,S.StringElement,!1).detect(f,S.NumberElement,!1).detect(y,S.BooleanElement,!1).detect(Array.isArray,S.ArrayElement,!1).detect(m,S.ObjectElement,!1),this}register(I,j){return this._elements=void 0,this.elementMap[I]=j,this}unregister(I){return this._elements=void 0,delete this.elementMap[I],this}detect(I,j,M){return M===void 0||M?this.elementDetection.unshift([I,j]):this.elementDetection.push([I,j]),this}toElement(I){if(I instanceof this.Element)return I;let j;for(let M=0;M{const j=I[0].toUpperCase()+I.substr(1);this._elements[j]=this.elementMap[I]}))),this._elements}get serialiser(){return new g(this)}}g.prototype.Namespace=_,w.exports=_},10866(w,N,s){const h=s(6048),v=s(92340);class f extends v{map(m,g){return this.elements.map((S=>m.bind(g)(S.value,S.key,S)))}filter(m,g){return new f(this.elements.filter((S=>m.bind(g)(S.value,S.key,S))))}reject(m,g){return this.filter(h(m.bind(g)))}forEach(m,g){return this.elements.forEach(((S,_)=>{m.bind(g)(S.value,S.key,S,_)}))}keys(){return this.map(((m,g)=>g.toValue()))}values(){return this.map((m=>m.toValue()))}}w.exports=f},86804(w,N,s){const h=s(10316),v=s(41067),f=s(71167),y=s(40239),m=s(12242),g=s(6233),S=s(87726),_=s(61045),T=s(86303),I=s(14540),j=s(92340),M=s(10866),z=s(55973);function K(Y){return Y instanceof h?Y:typeof Y=="string"?new f(Y):typeof Y=="number"?new y(Y):typeof Y=="boolean"?new m(Y):Y===null?new v:Array.isArray(Y)?new g(Y.map(K)):typeof Y=="object"?new _(Y):Y}h.prototype.ObjectElement=_,h.prototype.RefElement=I,h.prototype.MemberElement=S,h.prototype.refract=K,j.prototype.refract=K,w.exports={Element:h,NullElement:v,StringElement:f,NumberElement:y,BooleanElement:m,ArrayElement:g,MemberElement:S,ObjectElement:_,LinkElement:T,RefElement:I,refract:K,ArraySlice:j,ObjectSlice:M,KeyValuePair:z}},86303(w,N,s){const h=s(10316);w.exports=class extends h{constructor(f,y,m){super(f||[],y,m),this.element="link"}get relation(){return this.attributes.get("relation")}set relation(f){this.attributes.set("relation",f)}get href(){return this.attributes.get("href")}set href(f){this.attributes.set("href",f)}}},14540(w,N,s){const h=s(10316);w.exports=class extends h{constructor(f,y,m){super(f||[],y,m),this.element="ref",this.path||(this.path="element")}get path(){return this.attributes.get("path")}set path(f){this.attributes.set("path",f)}}},34035(w,N,s){const h=s(3110),v=s(86804);N.g$=h,N.KeyValuePair=s(55973),N.G6=v.ArraySlice,N.ot=v.ObjectSlice,N.Hg=v.Element,N.Om=v.StringElement,N.kT=v.NumberElement,N.bd=v.BooleanElement,N.Os=v.NullElement,N.wE=v.ArrayElement,N.Sh=v.ObjectElement,N.Pr=v.MemberElement,N.sI=v.RefElement,N.Ft=v.LinkElement,N.e=v.refract,s(85105),s(75147)},6233(w,N,s){const h=s(6048),v=s(10316),f=s(92340);class y extends v{constructor(g,S,_){super(g||[],S,_),this.element="array"}primitive(){return"array"}get(g){return this.content[g]}getValue(g){const S=this.get(g);if(S)return S.toValue()}getIndex(g){return this.content[g]}set(g,S){return this.content[g]=this.refract(S),this}remove(g){const S=this.content.splice(g,1);return S.length?S[0]:null}map(g,S){return this.content.map(g,S)}flatMap(g,S){return this.map(g,S).reduce(((_,T)=>_.concat(T)),[])}compactMap(g,S){const _=[];return this.forEach((T=>{const I=g.bind(S)(T);I&&_.push(I)})),_}filter(g,S){return new f(this.content.filter(g,S))}reject(g,S){return this.filter(h(g),S)}reduce(g,S){let _,T;S!==void 0?(_=0,T=this.refract(S)):(_=1,T=this.primitive()==="object"?this.first.value:this.first);for(let I=_;I{g.bind(S)(_,this.refract(T))}))}shift(){return this.content.shift()}unshift(g){this.content.unshift(this.refract(g))}push(g){return this.content.push(this.refract(g)),this}add(g){this.push(g)}findElements(g,S){const _=S||{},T=!!_.recursive,I=_.results===void 0?[]:_.results;return this.forEach(((j,M,z)=>{T&&j.findElements!==void 0&&j.findElements(g,{results:I,recursive:T}),g(j,M,z)&&I.push(j)})),I}find(g){return new f(this.findElements(g,{recursive:!0}))}findByElement(g){return this.find((S=>S.element===g))}findByClass(g){return this.find((S=>S.classes.includes(g)))}getById(g){return this.find((S=>S.id.toValue()===g)).first}includes(g){return this.content.some((S=>S.equals(g)))}contains(g){return this.includes(g)}empty(){return new this.constructor([])}"fantasy-land/empty"(){return this.empty()}concat(g){return new this.constructor(this.content.concat(g.content))}"fantasy-land/concat"(g){return this.concat(g)}"fantasy-land/map"(g){return new this.constructor(this.map(g))}"fantasy-land/chain"(g){return this.map((S=>g(S)),this).reduce(((S,_)=>S.concat(_)),this.empty())}"fantasy-land/filter"(g){return new this.constructor(this.content.filter(g))}"fantasy-land/reduce"(g,S){return this.content.reduce(g,S)}get length(){return this.content.length}get isEmpty(){return this.content.length===0}get first(){return this.getIndex(0)}get second(){return this.getIndex(1)}get last(){return this.getIndex(this.length-1)}}y.empty=function(){return new this},y["fantasy-land/empty"]=y.empty,typeof Symbol<"u"&&(y.prototype[Symbol.iterator]=function(){return this.content[Symbol.iterator]()}),w.exports=y},12242(w,N,s){const h=s(10316);w.exports=class extends h{constructor(f,y,m){super(f,y,m),this.element="boolean"}primitive(){return"boolean"}}},10316(w,N,s){const h=s(2404),v=s(55973),f=s(92340);class y{constructor(g,S,_){S&&(this.meta=S),_&&(this.attributes=_),this.content=g}freeze(){Object.isFrozen(this)||(this._meta&&(this.meta.parent=this,this.meta.freeze()),this._attributes&&(this.attributes.parent=this,this.attributes.freeze()),this.children.forEach((g=>{g.parent=this,g.freeze()}),this),this.content&&Array.isArray(this.content)&&Object.freeze(this.content),Object.freeze(this))}primitive(){}clone(){const g=new this.constructor;return g.element=this.element,this.meta.length&&(g._meta=this.meta.clone()),this.attributes.length&&(g._attributes=this.attributes.clone()),this.content?this.content.clone?g.content=this.content.clone():Array.isArray(this.content)?g.content=this.content.map((S=>S.clone())):g.content=this.content:g.content=this.content,g}toValue(){return this.content instanceof y?this.content.toValue():this.content instanceof v?{key:this.content.key.toValue(),value:this.content.value?this.content.value.toValue():void 0}:this.content&&this.content.map?this.content.map((g=>g.toValue()),this):this.content}toRef(g){if(this.id.toValue()==="")throw Error("Cannot create reference to an element that does not contain an ID");const S=new this.RefElement(this.id.toValue());return g&&(S.path=g),S}findRecursive(...g){if(arguments.length>1&&!this.isFrozen)throw new Error("Cannot find recursive with multiple element names without first freezing the element. Call `element.freeze()`");const S=g.pop();let _=new f;const T=(j,M)=>(j.push(M),j),I=(j,M)=>{M.element===S&&j.push(M);const z=M.findRecursive(S);return z&&z.reduce(T,j),M.content instanceof v&&(M.content.key&&I(j,M.content.key),M.content.value&&I(j,M.content.value)),j};return this.content&&(this.content.element&&I(_,this.content),Array.isArray(this.content)&&this.content.reduce(I,_)),g.isEmpty||(_=_.filter((j=>{let M=j.parents.map((z=>z.element));for(const z in g){const K=g[z],Y=M.indexOf(K);if(Y===-1)return!1;M=M.splice(0,Y)}return!0}))),_}set(g){return this.content=g,this}equals(g){return h(this.toValue(),g)}getMetaProperty(g,S){if(!this.meta.hasKey(g)){if(this.isFrozen){const _=this.refract(S);return _.freeze(),_}this.meta.set(g,S)}return this.meta.get(g)}setMetaProperty(g,S){this.meta.set(g,S)}get element(){return this._storedElement||"element"}set element(g){this._storedElement=g}get content(){return this._content}set content(g){if(g instanceof y)this._content=g;else if(g instanceof f)this.content=g.elements;else if(typeof g=="string"||typeof g=="number"||typeof g=="boolean"||g==="null"||g==null)this._content=g;else if(g instanceof v)this._content=g;else if(Array.isArray(g))this._content=g.map(this.refract);else{if(typeof g!="object")throw new Error("Cannot set content to given value");this._content=Object.keys(g).map((S=>new this.MemberElement(S,g[S])))}}get meta(){if(!this._meta){if(this.isFrozen){const g=new this.ObjectElement;return g.freeze(),g}this._meta=new this.ObjectElement}return this._meta}set meta(g){g instanceof this.ObjectElement?this._meta=g:this.meta.set(g||{})}get attributes(){if(!this._attributes){if(this.isFrozen){const g=new this.ObjectElement;return g.freeze(),g}this._attributes=new this.ObjectElement}return this._attributes}set attributes(g){g instanceof this.ObjectElement?this._attributes=g:this.attributes.set(g||{})}get id(){return this.getMetaProperty("id","")}set id(g){this.setMetaProperty("id",g)}get classes(){return this.getMetaProperty("classes",[])}set classes(g){this.setMetaProperty("classes",g)}get title(){return this.getMetaProperty("title","")}set title(g){this.setMetaProperty("title",g)}get description(){return this.getMetaProperty("description","")}set description(g){this.setMetaProperty("description",g)}get links(){return this.getMetaProperty("links",[])}set links(g){this.setMetaProperty("links",g)}get isFrozen(){return Object.isFrozen(this)}get parents(){let{parent:g}=this;const S=new f;for(;g;)S.push(g),g=g.parent;return S}get children(){if(Array.isArray(this.content))return new f(this.content);if(this.content instanceof v){const g=new f([this.content.key]);return this.content.value&&g.push(this.content.value),g}return this.content instanceof y?new f([this.content]):new f}get recursiveChildren(){const g=new f;return this.children.forEach((S=>{g.push(S),S.recursiveChildren.forEach((_=>{g.push(_)}))})),g}}w.exports=y},87726(w,N,s){const h=s(55973),v=s(10316);w.exports=class extends v{constructor(y,m,g,S){super(new h,g,S),this.element="member",this.key=y,this.value=m}get key(){return this.content.key}set key(y){this.content.key=this.refract(y)}get value(){return this.content.value}set value(y){this.content.value=this.refract(y)}}},41067(w,N,s){const h=s(10316);w.exports=class extends h{constructor(f,y,m){super(f||null,y,m),this.element="null"}primitive(){return"null"}set(){return new Error("Cannot set the value of null")}}},40239(w,N,s){const h=s(10316);w.exports=class extends h{constructor(f,y,m){super(f,y,m),this.element="number"}primitive(){return"number"}}},61045(w,N,s){const h=s(6048),v=s(23805),f=s(6233),y=s(87726),m=s(10866);w.exports=class extends f{constructor(S,_,T){super(S||[],_,T),this.element="object"}primitive(){return"object"}toValue(){return this.content.reduce(((S,_)=>(S[_.key.toValue()]=_.value?_.value.toValue():void 0,S)),{})}get(S){const _=this.getMember(S);if(_)return _.value}getMember(S){if(S!==void 0)return this.content.find((_=>_.key.toValue()===S))}remove(S){let _=null;return this.content=this.content.filter((T=>T.key.toValue()!==S||(_=T,!1))),_}getKey(S){const _=this.getMember(S);if(_)return _.key}set(S,_){if(v(S))return Object.keys(S).forEach((j=>{this.set(j,S[j])})),this;const T=S,I=this.getMember(T);return I?I.value=_:this.content.push(new y(T,_)),this}keys(){return this.content.map((S=>S.key.toValue()))}values(){return this.content.map((S=>S.value.toValue()))}hasKey(S){return this.content.some((_=>_.key.equals(S)))}items(){return this.content.map((S=>[S.key.toValue(),S.value.toValue()]))}map(S,_){return this.content.map((T=>S.bind(_)(T.value,T.key,T)))}compactMap(S,_){const T=[];return this.forEach(((I,j,M)=>{const z=S.bind(_)(I,j,M);z&&T.push(z)})),T}filter(S,_){return new m(this.content).filter(S,_)}reject(S,_){return this.filter(h(S),_)}forEach(S,_){return this.content.forEach((T=>S.bind(_)(T.value,T.key,T)))}}},71167(w,N,s){const h=s(10316);w.exports=class extends h{constructor(f,y,m){super(f,y,m),this.element="string"}primitive(){return"string"}get length(){return this.content.length}}},75147(w,N,s){const h=s(85105);w.exports=class extends h{serialise(f){if(!(f instanceof this.namespace.elements.Element))throw new TypeError(`Given element \`${f}\` is not an Element instance`);let y;f._attributes&&f.attributes.get("variable")&&(y=f.attributes.get("variable"));const m={element:f.element};f._meta&&f._meta.length>0&&(m.meta=this.serialiseObject(f.meta));const g=f.element==="enum"||f.attributes.keys().indexOf("enumerations")!==-1;if(g){const S=this.enumSerialiseAttributes(f);S&&(m.attributes=S)}else if(f._attributes&&f._attributes.length>0){let{attributes:S}=f;S.get("metadata")&&(S=S.clone(),S.set("meta",S.get("metadata")),S.remove("metadata")),f.element==="member"&&y&&(S=S.clone(),S.remove("variable")),S.length>0&&(m.attributes=this.serialiseObject(S))}if(g)m.content=this.enumSerialiseContent(f,m);else if(this[`${f.element}SerialiseContent`])m.content=this[`${f.element}SerialiseContent`](f,m);else if(f.content!==void 0){let S;y&&f.content.key?(S=f.content.clone(),S.key.attributes.set("variable",y),S=this.serialiseContent(S)):S=this.serialiseContent(f.content),this.shouldSerialiseContent(f,S)&&(m.content=S)}else this.shouldSerialiseContent(f,f.content)&&f instanceof this.namespace.elements.Array&&(m.content=[]);return m}shouldSerialiseContent(f,y){return f.element==="parseResult"||f.element==="httpRequest"||f.element==="httpResponse"||f.element==="category"||f.element==="link"||y!==void 0&&(!Array.isArray(y)||y.length!==0)}refSerialiseContent(f,y){return delete y.attributes,{href:f.toValue(),path:f.path.toValue()}}sourceMapSerialiseContent(f){return f.toValue()}dataStructureSerialiseContent(f){return[this.serialiseContent(f.content)]}enumSerialiseAttributes(f){const y=f.attributes.clone(),m=y.remove("enumerations")||new this.namespace.elements.Array([]),g=y.get("default");let S=y.get("samples")||new this.namespace.elements.Array([]);if(g&&g.content&&(g.content.attributes&&g.content.attributes.remove("typeAttributes"),y.set("default",new this.namespace.elements.Array([g.content]))),S.forEach((_=>{_.content&&_.content.element&&_.content.attributes.remove("typeAttributes")})),f.content&&m.length!==0&&S.unshift(f.content),S=S.map((_=>_ instanceof this.namespace.elements.Array?[_]:new this.namespace.elements.Array([_.content]))),S.length&&y.set("samples",S),y.length>0)return this.serialiseObject(y)}enumSerialiseContent(f){if(f._attributes){const y=f.attributes.get("enumerations");if(y&&y.length>0)return y.content.map((m=>{const g=m.clone();return g.attributes.remove("typeAttributes"),this.serialise(g)}))}if(f.content){const y=f.content.clone();return y.attributes.remove("typeAttributes"),[this.serialise(y)]}return[]}deserialise(f){if(typeof f=="string")return new this.namespace.elements.String(f);if(typeof f=="number")return new this.namespace.elements.Number(f);if(typeof f=="boolean")return new this.namespace.elements.Boolean(f);if(f===null)return new this.namespace.elements.Null;if(Array.isArray(f))return new this.namespace.elements.Array(f.map(this.deserialise,this));const y=this.namespace.getElementClass(f.element),m=new y;m.element!==f.element&&(m.element=f.element),f.meta&&this.deserialiseObject(f.meta,m.meta),f.attributes&&this.deserialiseObject(f.attributes,m.attributes);const g=this.deserialiseContent(f.content);if(g===void 0&&m.content!==null||(m.content=g),m.element==="enum"){m.content&&m.attributes.set("enumerations",m.content);let S=m.attributes.get("samples");if(m.attributes.remove("samples"),S){const T=S;S=new this.namespace.elements.Array,T.forEach((j=>{j.forEach((M=>{const z=new y(M);z.element=m.element,S.push(z)}))}));const I=S.shift();m.content=I?I.content:void 0,m.attributes.set("samples",S)}else m.content=void 0;let _=m.attributes.get("default");if(_&&_.length>0){_=_.get(0);const T=new y(_);T.element=m.element,m.attributes.set("default",T)}}else if(m.element==="dataStructure"&&Array.isArray(m.content))[m.content]=m.content;else if(m.element==="category"){const S=m.attributes.get("meta");S&&(m.attributes.set("metadata",S),m.attributes.remove("meta"))}else m.element==="member"&&m.key&&m.key._attributes&&m.key._attributes.getValue("variable")&&(m.attributes.set("variable",m.key.attributes.get("variable")),m.key.attributes.remove("variable"));return m}serialiseContent(f){if(f instanceof this.namespace.elements.Element)return this.serialise(f);if(f instanceof this.namespace.KeyValuePair){const y={key:this.serialise(f.key)};return f.value&&(y.value=this.serialise(f.value)),y}return f&&f.map?f.map(this.serialise,this):f}deserialiseContent(f){if(f){if(f.element)return this.deserialise(f);if(f.key){const y=new this.namespace.KeyValuePair(this.deserialise(f.key));return f.value&&(y.value=this.deserialise(f.value)),y}if(f.map)return f.map(this.deserialise,this)}return f}shouldRefract(f){return!!(f._attributes&&f.attributes.keys().length||f._meta&&f.meta.keys().length)||f.element!=="enum"&&(f.element!==f.primitive()||f.element==="member")}convertKeyToRefract(f,y){return this.shouldRefract(y)?this.serialise(y):y.element==="enum"?this.serialiseEnum(y):y.element==="array"?y.map((m=>this.shouldRefract(m)||f==="default"?this.serialise(m):m.element==="array"||m.element==="object"||m.element==="enum"?m.children.map((g=>this.serialise(g))):m.toValue())):y.element==="object"?(y.content||[]).map(this.serialise,this):y.toValue()}serialiseEnum(f){return f.children.map((y=>this.serialise(y)))}serialiseObject(f){const y={};return f.forEach(((m,g)=>{if(m){const S=g.toValue();y[S]=this.convertKeyToRefract(S,m)}})),y}deserialiseObject(f,y){Object.keys(f).forEach((m=>{y.set(m,this.deserialise(f[m]))}))}}},85105(w){w.exports=class{constructor(s){this.namespace=s||new this.Namespace}serialise(s){if(!(s instanceof this.namespace.elements.Element))throw new TypeError(`Given element \`${s}\` is not an Element instance`);const h={element:s.element};s._meta&&s._meta.length>0&&(h.meta=this.serialiseObject(s.meta)),s._attributes&&s._attributes.length>0&&(h.attributes=this.serialiseObject(s.attributes));const v=this.serialiseContent(s.content);return v!==void 0&&(h.content=v),h}deserialise(s){if(!s.element)throw new Error("Given value is not an object containing an element name");const h=new(this.namespace.getElementClass(s.element));h.element!==s.element&&(h.element=s.element),s.meta&&this.deserialiseObject(s.meta,h.meta),s.attributes&&this.deserialiseObject(s.attributes,h.attributes);const v=this.deserialiseContent(s.content);return v===void 0&&h.content!==null||(h.content=v),h}serialiseContent(s){if(s instanceof this.namespace.elements.Element)return this.serialise(s);if(s instanceof this.namespace.KeyValuePair){const h={key:this.serialise(s.key)};return s.value&&(h.value=this.serialise(s.value)),h}return s&&s.map?s.length===0?void 0:s.map(this.serialise,this):s}deserialiseContent(s){if(s){if(s.element)return this.deserialise(s);if(s.key){const h=new this.namespace.KeyValuePair(this.deserialise(s.key));return s.value&&(h.value=this.deserialise(s.value)),h}if(s.map)return s.map(this.deserialise,this)}return s}serialiseObject(s){const h={};if(s.forEach(((v,f)=>{v&&(h[f.toValue()]=this.serialise(v))})),Object.keys(h).length!==0)return h}deserialiseObject(s,h){Object.keys(s).forEach((v=>{h.set(v,this.deserialise(s[v]))}))}}},76578(w){w.exports=["Float16Array","Float32Array","Float64Array","Int8Array","Int16Array","Int32Array","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array"]},65606(w){var N,s,h=w.exports={};function v(){throw new Error("setTimeout has not been defined")}function f(){throw new Error("clearTimeout has not been defined")}function y(z){if(N===setTimeout)return setTimeout(z,0);if((N===v||!N)&&setTimeout)return N=setTimeout,setTimeout(z,0);try{return N(z,0)}catch{try{return N.call(null,z,0)}catch{return N.call(this,z,0)}}}(function(){try{N=typeof setTimeout=="function"?setTimeout:v}catch{N=v}try{s=typeof clearTimeout=="function"?clearTimeout:f}catch{s=f}})();var m,g=[],S=!1,_=-1;function T(){S&&m&&(S=!1,m.length?g=m.concat(g):_=-1,g.length&&I())}function I(){if(!S){var z=y(T);S=!0;for(var K=g.length;K;){for(m=g,g=[];++_1)for(var Y=1;Yf)throw new RangeError("requested too many random bytes");var T=y.allocUnsafe(S);if(S>0)if(S>v)for(var I=0;I=0||(qe[Ve]=ge[Ve]);return qe})(B,X);if(Object.getOwnPropertySymbols){var Te=Object.getOwnPropertySymbols(B);for(ie=0;ie=0||Object.prototype.propertyIsEnumerable.call(B,te)&&(se[te]=B[te])}return se}function S(B,X){var te=Object.keys(B);if(Object.getOwnPropertySymbols){var ie=Object.getOwnPropertySymbols(B);X&&(ie=ie.filter((function(se){return Object.getOwnPropertyDescriptor(B,se).enumerable}))),te.push.apply(te,ie)}return te}function _(B){for(var X=1;X"u"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch{return!1}})();return function(){var ie,se=z(B);if(X){var Te=z(this).constructor;ie=Reflect.construct(se,arguments,Te)}else ie=se.apply(this,arguments);return(function(ge,ke){if(ke&&(h(ke)==="object"||typeof ke=="function"))return ke;if(ke!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return M(ge)})(this,ie)}}function M(B){if(B===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return B}function z(B){return z=Object.setPrototypeOf?Object.getPrototypeOf:function(te){return te.__proto__||Object.getPrototypeOf(te)},z(B)}function K(B,X,te){return X in B?Object.defineProperty(B,X,{value:te,enumerable:!0,configurable:!0,writable:!0}):B[X]=te,B}var Y=(function(B){(function(se,Te){if(typeof Te!="function"&&Te!==null)throw new TypeError("Super expression must either be null or a function");se.prototype=Object.create(Te&&Te.prototype,{constructor:{value:se,writable:!0,configurable:!0}}),Object.defineProperty(se,"prototype",{writable:!1}),Te&&I(se,Te)})(te,B);var X=j(te);function te(ie){var se;(function(ge,ke){if(!(ge instanceof ke))throw new TypeError("Cannot call a class as a function")})(this,te),K(M(se=X.call(this,ie)),"onChange",(function(he){he.persist();var ge=se.state.value,ke=se.props.minLength;se.setState({value:he.target.value},(function(){var Ve=se.state.value;Ve.length>=ke?se.notify(he):ge.length>Ve.length&&se.notify(_(_({},he),{},{target:_(_({},he.target),{},{value:""})}))}))})),K(M(se),"onKeyDown",(function(he){he.key==="Enter"&&se.forceNotify(he);var ge=se.props.onKeyDown;ge&&(he.persist(),ge(he))})),K(M(se),"onBlur",(function(he){se.forceNotify(he);var ge=se.props.onBlur;ge&&(he.persist(),ge(he))})),K(M(se),"createNotifier",(function(he){if(he<0)se.notify=function(){return null};else if(he===0)se.notify=se.doNotify;else{var ge=(0,f.default)((function(ke){se.isDebouncing=!1,se.doNotify(ke)}),he);se.notify=function(ke){se.isDebouncing=!0,ge(ke)},se.flush=function(){return ge.flush()},se.cancel=function(){se.isDebouncing=!1,ge.cancel()}}})),K(M(se),"doNotify",(function(){se.props.onChange.apply(void 0,arguments)})),K(M(se),"forceNotify",(function(he){var ge=se.props.debounceTimeout;if(se.isDebouncing||!(ge>0)){se.cancel&&se.cancel();var ke=se.state.value,Ve=se.props.minLength;ke.length>=Ve?se.doNotify(he):se.doNotify(_(_({},he),{},{target:_(_({},he.target),{},{value:ke})}))}})),se.isDebouncing=!1,se.state={value:ie.value===void 0||ie.value===null?"":ie.value};var Te=se.props.debounceTimeout;return se.createNotifier(Te),se}return(function(se,Te,he){return Te&&T(se.prototype,Te),Object.defineProperty(se,"prototype",{writable:!1}),se})(te,[{key:"componentDidUpdate",value:function(se){if(!this.isDebouncing){var Te=this.props,he=Te.value,ge=Te.debounceTimeout,ke=se.debounceTimeout,Ve=se.value,He=this.state.value;he!==void 0&&Ve!==he&&He!==he&&this.setState({value:he}),ge!==ke&&this.createNotifier(ge)}}},{key:"componentWillUnmount",value:function(){this.flush&&this.flush()}},{key:"render",value:function(){var se,Te,he=this.props,ge=he.element,ke=(he.onChange,he.value,he.minLength,he.debounceTimeout,he.forceNotifyByEnter),Ve=he.forceNotifyOnBlur,He=he.onKeyDown,qe=he.onBlur,nt=he.inputRef,gt=g(he,y),Re=this.state.value;se=ke?{onKeyDown:this.onKeyDown}:He?{onKeyDown:He}:{},Te=Ve?{onBlur:this.onBlur}:qe?{onBlur:qe}:{};var u=nt?{ref:nt}:{};return v.default.createElement(ge,_(_(_(_({},gt),{},{onChange:this.onChange,value:Re},se),Te),u))}}]),te})(v.default.PureComponent);N.DebounceInput=Y,K(Y,"defaultProps",{element:"input",type:"text",onKeyDown:void 0,onBlur:void 0,value:void 0,minLength:0,debounceTimeout:100,forceNotifyByEnter:!0,forceNotifyOnBlur:!0,inputRef:void 0})},24677(w,N,s){var h=s(81214).DebounceInput;h.DebounceInput=h,w.exports=h},22551(w,N,s){var h=s(96540),v=s(69982);function f(i){for(var l="https://reactjs.org/docs/error-decoder.html?invariant="+i,b=1;b"u"||window.document===void 0||window.document.createElement===void 0),T=Object.prototype.hasOwnProperty,I=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,j={},M={};function z(i,l,b,C,$,ee,ue){this.acceptsBooleans=l===2||l===3||l===4,this.attributeName=C,this.attributeNamespace=$,this.mustUseProperty=b,this.propertyName=i,this.type=l,this.sanitizeURL=ee,this.removeEmptyString=ue}var K={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach((function(i){K[i]=new z(i,0,!1,i,null,!1,!1)})),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach((function(i){var l=i[0];K[l]=new z(l,1,!1,i[1],null,!1,!1)})),["contentEditable","draggable","spellCheck","value"].forEach((function(i){K[i]=new z(i,2,!1,i.toLowerCase(),null,!1,!1)})),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach((function(i){K[i]=new z(i,2,!1,i,null,!1,!1)})),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach((function(i){K[i]=new z(i,3,!1,i.toLowerCase(),null,!1,!1)})),["checked","multiple","muted","selected"].forEach((function(i){K[i]=new z(i,3,!0,i,null,!1,!1)})),["capture","download"].forEach((function(i){K[i]=new z(i,4,!1,i,null,!1,!1)})),["cols","rows","size","span"].forEach((function(i){K[i]=new z(i,6,!1,i,null,!1,!1)})),["rowSpan","start"].forEach((function(i){K[i]=new z(i,5,!1,i.toLowerCase(),null,!1,!1)}));var Y=/[\-:]([a-z])/g;function B(i){return i[1].toUpperCase()}function X(i,l,b,C){var $=K.hasOwnProperty(l)?K[l]:null;($!==null?$.type!==0:C||!(2Ce}return!1})(l,b,$,C)&&(b=null),C||$===null?(function(ue){return!!T.call(M,ue)||!T.call(j,ue)&&(I.test(ue)?M[ue]=!0:(j[ue]=!0,!1))})(l)&&(b===null?i.removeAttribute(l):i.setAttribute(l,""+b)):$.mustUseProperty?i[$.propertyName]=b===null?$.type!==3&&"":b:(l=$.attributeName,C=$.attributeNamespace,b===null?i.removeAttribute(l):(b=($=$.type)===3||$===4&&b===!0?"":""+b,C?i.setAttributeNS(C,l,b):i.setAttribute(l,b))))}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach((function(i){var l=i.replace(Y,B);K[l]=new z(l,1,!1,i,null,!1,!1)})),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach((function(i){var l=i.replace(Y,B);K[l]=new z(l,1,!1,i,"http://www.w3.org/1999/xlink",!1,!1)})),["xml:base","xml:lang","xml:space"].forEach((function(i){var l=i.replace(Y,B);K[l]=new z(l,1,!1,i,"http://www.w3.org/XML/1998/namespace",!1,!1)})),["tabIndex","crossOrigin"].forEach((function(i){K[i]=new z(i,1,!1,i.toLowerCase(),null,!1,!1)})),K.xlinkHref=new z("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach((function(i){K[i]=new z(i,1,!1,i.toLowerCase(),null,!0,!0)}));var te=h.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,ie=Symbol.for("react.element"),se=Symbol.for("react.portal"),Te=Symbol.for("react.fragment"),he=Symbol.for("react.strict_mode"),ge=Symbol.for("react.profiler"),ke=Symbol.for("react.provider"),Ve=Symbol.for("react.context"),He=Symbol.for("react.forward_ref"),qe=Symbol.for("react.suspense"),nt=Symbol.for("react.suspense_list"),gt=Symbol.for("react.memo"),Re=Symbol.for("react.lazy"),u=Symbol.for("react.offscreen"),at=Symbol.iterator;function Xe(i){return i===null||typeof i!="object"?null:typeof(i=at&&i[at]||i["@@iterator"])=="function"?i:null}var Se,De=Object.assign;function Ke(i){if(Se===void 0)try{throw Error()}catch(b){var l=b.stack.trim().match(/\n( *(at )?)/);Se=l&&l[1]||""}return` +`+Se+i}var ft=!1;function Nt(i,l){if(!i||ft)return"";ft=!0;var b=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(l)if(l=function(){throw Error()},Object.defineProperty(l.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(l,[])}catch(Ue){var C=Ue}Reflect.construct(i,[],l)}else{try{l.call()}catch(Ue){C=Ue}i.call(l.prototype)}else{try{throw Error()}catch(Ue){C=Ue}i()}}catch(Ue){if(Ue&&C&&typeof Ue.stack=="string"){for(var $=Ue.stack.split(` +`),ee=C.stack.split(` +`),ue=$.length-1,Ce=ee.length-1;1<=ue&&0<=Ce&&$[ue]!==ee[Ce];)Ce--;for(;1<=ue&&0<=Ce;ue--,Ce--)if($[ue]!==ee[Ce]){if(ue!==1||Ce!==1)do if(ue--,0>--Ce||$[ue]!==ee[Ce]){var Me=` +`+$[ue].replace(" at new "," at ");return i.displayName&&Me.includes("")&&(Me=Me.replace("",i.displayName)),Me}while(1<=ue&&0<=Ce);break}}}finally{ft=!1,Error.prepareStackTrace=b}return(i=i?i.displayName||i.name:"")?Ke(i):""}function Wt(i){switch(i.tag){case 5:return Ke(i.type);case 16:return Ke("Lazy");case 13:return Ke("Suspense");case 19:return Ke("SuspenseList");case 0:case 2:case 15:return i=Nt(i.type,!1);case 11:return i=Nt(i.type.render,!1);case 1:return i=Nt(i.type,!0);default:return""}}function H(i){if(i==null)return null;if(typeof i=="function")return i.displayName||i.name||null;if(typeof i=="string")return i;switch(i){case Te:return"Fragment";case se:return"Portal";case ge:return"Profiler";case he:return"StrictMode";case qe:return"Suspense";case nt:return"SuspenseList"}if(typeof i=="object")switch(i.$$typeof){case Ve:return(i.displayName||"Context")+".Consumer";case ke:return(i._context.displayName||"Context")+".Provider";case He:var l=i.render;return(i=i.displayName)||(i=(i=l.displayName||l.name||"")!==""?"ForwardRef("+i+")":"ForwardRef"),i;case gt:return(l=i.displayName||null)!==null?l:H(i.type)||"Memo";case Re:l=i._payload,i=i._init;try{return H(i(l))}catch{}}return null}function we(i){var l=i.type;switch(i.tag){case 24:return"Cache";case 9:return(l.displayName||"Context")+".Consumer";case 10:return(l._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return i=(i=l.render).displayName||i.name||"",l.displayName||(i!==""?"ForwardRef("+i+")":"ForwardRef");case 7:return"Fragment";case 5:return l;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return H(l);case 8:return l===he?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof l=="function")return l.displayName||l.name||null;if(typeof l=="string")return l}return null}function ct(i){switch(typeof i){case"boolean":case"number":case"string":case"undefined":case"object":return i;default:return""}}function mt(i){var l=i.type;return(i=i.nodeName)&&i.toLowerCase()==="input"&&(l==="checkbox"||l==="radio")}function ur(i){i._valueTracker||(i._valueTracker=(function(b){var C=mt(b)?"checked":"value",$=Object.getOwnPropertyDescriptor(b.constructor.prototype,C),ee=""+b[C];if(!b.hasOwnProperty(C)&&$!==void 0&&typeof $.get=="function"&&typeof $.set=="function"){var ue=$.get,Ce=$.set;return Object.defineProperty(b,C,{configurable:!0,get:function(){return ue.call(this)},set:function(Me){ee=""+Me,Ce.call(this,Me)}}),Object.defineProperty(b,C,{enumerable:$.enumerable}),{getValue:function(){return ee},setValue:function(Me){ee=""+Me},stopTracking:function(){b._valueTracker=null,delete b[C]}}}})(i))}function ar(i){if(!i)return!1;var l=i._valueTracker;if(!l)return!0;var b=l.getValue(),C="";return i&&(C=mt(i)?i.checked?"true":"false":i.value),(i=C)!==b&&(l.setValue(i),!0)}function Tt(i){if((i=i||(typeof document<"u"?document:void 0))===void 0)return null;try{return i.activeElement||i.body}catch{return i.body}}function Gt(i,l){var b=l.checked;return De({},l,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:b??i._wrapperState.initialChecked})}function Sr(i,l){var b=l.defaultValue==null?"":l.defaultValue,C=l.checked!=null?l.checked:l.defaultChecked;b=ct(l.value!=null?l.value:b),i._wrapperState={initialChecked:C,initialValue:b,controlled:l.type==="checkbox"||l.type==="radio"?l.checked!=null:l.value!=null}}function kt(i,l){(l=l.checked)!=null&&X(i,"checked",l,!1)}function yt(i,l){kt(i,l);var b=ct(l.value),C=l.type;if(b!=null)C==="number"?(b===0&&i.value===""||i.value!=b)&&(i.value=""+b):i.value!==""+b&&(i.value=""+b);else if(C==="submit"||C==="reset")return void i.removeAttribute("value");l.hasOwnProperty("value")?pe(i,l.type,b):l.hasOwnProperty("defaultValue")&&pe(i,l.type,ct(l.defaultValue)),l.checked==null&&l.defaultChecked!=null&&(i.defaultChecked=!!l.defaultChecked)}function Zt(i,l,b){if(l.hasOwnProperty("value")||l.hasOwnProperty("defaultValue")){var C=l.type;if(!(C!=="submit"&&C!=="reset"||l.value!==void 0&&l.value!==null))return;l=""+i._wrapperState.initialValue,b||l===i.value||(i.value=l),i.defaultValue=l}(b=i.name)!==""&&(i.name=""),i.defaultChecked=!!i._wrapperState.initialChecked,b!==""&&(i.name=b)}function pe(i,l,b){l==="number"&&Tt(i.ownerDocument)===i||(b==null?i.defaultValue=""+i._wrapperState.initialValue:i.defaultValue!==""+b&&(i.defaultValue=""+b))}var U=Array.isArray;function Z(i,l,b,C){if(i=i.options,l){l={};for(var $=0;$"+l.valueOf().toString()+"",l=Qe.firstChild;i.firstChild;)i.removeChild(i.firstChild);for(;l.firstChild;)i.appendChild(l.firstChild)}},typeof MSApp<"u"&&MSApp.execUnsafeLocalFunction?function(i,l,b,C){MSApp.execUnsafeLocalFunction((function(){return $t(i,l)}))}:$t);function rn(i,l){if(l){var b=i.firstChild;if(b&&b===i.lastChild&&b.nodeType===3)return void(b.nodeValue=l)}i.textContent=l}var kr={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},An=["Webkit","ms","Moz","O"];function Wr(i,l,b){return l==null||typeof l=="boolean"||l===""?"":b||typeof l!="number"||l===0||kr.hasOwnProperty(i)&&kr[i]?(""+l).trim():l+"px"}function Jn(i,l){for(var b in i=i.style,l)if(l.hasOwnProperty(b)){var C=b.indexOf("--")===0,$=Wr(b,l[b],C);b==="float"&&(b="cssFloat"),C?i.setProperty(b,$):i[b]=$}}Object.keys(kr).forEach((function(i){An.forEach((function(l){l=l+i.charAt(0).toUpperCase()+i.substring(1),kr[l]=kr[i]}))}));var Ea=De({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Zn(i,l){if(l){if(Ea[i]&&(l.children!=null||l.dangerouslySetInnerHTML!=null))throw Error(f(137,i));if(l.dangerouslySetInnerHTML!=null){if(l.children!=null)throw Error(f(60));if(typeof l.dangerouslySetInnerHTML!="object"||!("__html"in l.dangerouslySetInnerHTML))throw Error(f(61))}if(l.style!=null&&typeof l.style!="object")throw Error(f(62))}}function un(i,l){if(i.indexOf("-")===-1)return typeof l.is=="string";switch(i){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var ti=null;function Oo(i){return(i=i.target||i.srcElement||window).correspondingUseElement&&(i=i.correspondingUseElement),i.nodeType===3?i.parentNode:i}var Li=null,Fn=null,zo=null;function Vo(i){if(i=cp(i)){if(typeof Li!="function")throw Error(f(280));var l=i.stateNode;l&&(l=up(l),Li(i.stateNode,i.type,l))}}function ds(i){Fn?zo?zo.push(i):zo=[i]:Fn=i}function sr(){if(Fn){var i=Fn,l=zo;if(zo=Fn=null,Vo(i),l)for(i=0;i>>=0,l===0?32:31-(Yl(l)/Xl|0)|0},Yl=Math.log,Xl=Math.LN2,gs=64,ys=4194304;function lo(i){switch(i&-i){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return 4194240&i;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return 130023424&i;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return i}}function mi(i,l){var b=i.pendingLanes;if(b===0)return 0;var C=0,$=i.suspendedLanes,ee=i.pingedLanes,ue=268435455&b;if(ue!==0){var Ce=ue&~$;Ce!==0?C=lo(Ce):(ee&=ue)!==0&&(C=lo(ee))}else(ue=b&~$)!==0?C=lo(ue):ee!==0&&(C=lo(ee));if(C===0)return 0;if(l!==0&&l!==C&&!(l&$)&&(($=C&-C)>=(ee=l&-l)||$===16&&4194240&ee))return l;if(4&C&&(C|=16&b),(l=i.entangledLanes)!==0)for(i=i.entanglements,l&=C;0b;b++)l.push(i);return l}function Ao(i,l,b){i.pendingLanes|=l,l!==536870912&&(i.suspendedLanes=0,i.pingedLanes=0),(i=i.eventTimes)[l=31-Ia(l)]=b}function vs(i,l){var b=i.entangledLanes|=l;for(i=i.entanglements;b;){var C=31-Ia(b),$=1<=Si),pl=" ",hl=!1;function xi(i,l){switch(i){case"keyup":return xa.indexOf(l.keyCode)!==-1;case"keydown":return l.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function xs(i){return typeof(i=i.detail)=="object"&&"data"in i?i.data:null}var No=!1,Kc={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function ra(i){var l=i&&i.nodeName&&i.nodeName.toLowerCase();return l==="input"?!!Kc[i.type]:l==="textarea"}function dl(i,l,b,C){ds(C),0<(l=vr(l,"onChange")).length&&(b=new $n("onChange","change",null,b,C),i.push({event:b,listeners:l}))}var fl=null,ni=null;function Gc(i){oe(i,0)}function zi(i){if(ar(ru(i)))return i}function lc(i,l){if(i==="change")return l}var Yc=!1;if(_){var ml;if(_){var gl="oninput"in document;if(!gl){var la=document.createElement("div");la.setAttribute("oninput","return;"),gl=typeof la.oninput=="function"}ml=gl}else ml=!1;Yc=ml&&(!document.documentMode||9=l)return{node:C,offset:l-i};i=b}e:{for(;C;){if(C.nextSibling){C=C.nextSibling;break e}C=C.parentNode}C=void 0}C=Xc(C)}}function yl(i,l){return!(!i||!l)&&(i===l||(!i||i.nodeType!==3)&&(l&&l.nodeType===3?yl(i,l.parentNode):"contains"in i?i.contains(l):!!i.compareDocumentPosition&&!!(16&i.compareDocumentPosition(l))))}function Da(){for(var i=window,l=Tt();l instanceof i.HTMLIFrameElement;){try{var b=typeof l.contentWindow.location.href=="string"}catch{b=!1}if(!b)break;l=Tt((i=l.contentWindow).document)}return l}function Hi(i){var l=i&&i.nodeName&&i.nodeName.toLowerCase();return l&&(l==="input"&&(i.type==="text"||i.type==="search"||i.type==="tel"||i.type==="url"||i.type==="password")||l==="textarea"||i.contentEditable==="true")}function Ho(i){var l=Da(),b=i.focusedElem,C=i.selectionRange;if(l!==b&&b&&b.ownerDocument&&yl(b.ownerDocument.documentElement,b)){if(C!==null&&Hi(b)){if(l=C.start,(i=C.end)===void 0&&(i=l),"selectionStart"in b)b.selectionStart=l,b.selectionEnd=Math.min(i,b.value.length);else if((i=(l=b.ownerDocument||document)&&l.defaultView||window).getSelection){i=i.getSelection();var $=b.textContent.length,ee=Math.min(C.start,$);C=C.end===void 0?ee:Math.min(C.end,$),!i.extend&&ee>C&&($=C,C=ee,ee=$),$=ca(b,ee);var ue=ca(b,C);$&&ue&&(i.rangeCount!==1||i.anchorNode!==$.node||i.anchorOffset!==$.offset||i.focusNode!==ue.node||i.focusOffset!==ue.offset)&&((l=l.createRange()).setStart($.node,$.offset),i.removeAllRanges(),ee>C?(i.addRange(l),i.extend(ue.node,ue.offset)):(l.setEnd(ue.node,ue.offset),i.addRange(l)))}}for(l=[],i=b;i=i.parentNode;)i.nodeType===1&&l.push({element:i,left:i.scrollLeft,top:i.scrollTop});for(typeof b.focus=="function"&&b.focus(),b=0;b=document.documentMode,_i=null,cc=null,jn=null,mn=!1;function uc(i,l,b){var C=b.window===b?b.document:b.nodeType===9?b:b.ownerDocument;mn||_i==null||_i!==Tt(C)||("selectionStart"in(C=_i)&&Hi(C)?C={start:C.selectionStart,end:C.selectionEnd}:C={anchorNode:(C=(C.ownerDocument&&C.ownerDocument.defaultView||window).getSelection()).anchorNode,anchorOffset:C.anchorOffset,focusNode:C.focusNode,focusOffset:C.focusOffset},jn&&_s(jn,C)||(jn=C,0<(C=vr(cc,"onSelect")).length&&(l=new $n("onSelect","select",null,l,b),i.push({event:l,listeners:C}),l.target=_i)))}function Os(i,l){var b={};return b[i.toLowerCase()]=l.toLowerCase(),b["Webkit"+i]="webkit"+l,b["Moz"+i]="moz"+l,b}var To={animationend:Os("Animation","AnimationEnd"),animationiteration:Os("Animation","AnimationIteration"),animationstart:Os("Animation","AnimationStart"),transitionend:Os("Transition","TransitionEnd")},Qc={},pc={};function vl(i){if(Qc[i])return Qc[i];if(!To[i])return i;var l,b=To[i];for(l in b)if(b.hasOwnProperty(l)&&l in pc)return Qc[i]=b[l];return i}_&&(pc=document.createElement("div").style,"AnimationEvent"in window||(delete To.animationend.animation,delete To.animationiteration.animation,delete To.animationstart.animation),"TransitionEvent"in window||delete To.transitionend.transition);var Zc=vl("animationend"),eu=vl("animationiteration"),op=vl("animationstart"),tu=vl("transitionend"),ip=new Map,sp="abort auxClick cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel".split(" ");function A(i,l){ip.set(i,l),g(l,[i])}for(var O=0;Onu||(i.current=Td[nu],Td[nu]=null,nu--)}function Cn(i,l){nu++,Td[nu]=i.current,i.current=l}var xl={},Xa=Sl(xl),Oa=Sl(!1),ks=xl;function hc(i,l){var b=i.type.contextTypes;if(!b)return xl;var C=i.stateNode;if(C&&C.__reactInternalMemoizedUnmaskedChildContext===l)return C.__reactInternalMemoizedMaskedChildContext;var $,ee={};for($ in b)ee[$]=l[$];return C&&((i=i.stateNode).__reactInternalMemoizedUnmaskedChildContext=l,i.__reactInternalMemoizedMaskedChildContext=ee),ee}function Ko(i){return(i=i.childContextTypes)!=null}function sh(){qn(Oa),qn(Xa)}function Xm(i,l,b){if(Xa.current!==xl)throw Error(f(168));Cn(Xa,l),Cn(Oa,b)}function Id(i,l,b){var C=i.stateNode;if(l=l.childContextTypes,typeof C.getChildContext!="function")return b;for(var $ in C=C.getChildContext())if(!($ in l))throw Error(f(108,we(i)||"Unknown",$));return De({},b,C)}function lh(i){return i=(i=i.stateNode)&&i.__reactInternalMemoizedMergedChildContext||xl,ks=Xa.current,Cn(Xa,i),Cn(Oa,Oa.current),!0}function Yr(i,l,b){var C=i.stateNode;if(!C)throw Error(f(169));b?(i=Id(i,l,ks),C.__reactInternalMemoizedMergedChildContext=i,qn(Oa),qn(Xa),Cn(Xa,i)):qn(Oa),Cn(Oa,b)}var As=null,ch=!1,Md=!1;function Qm(i){As===null?As=[i]:As.push(i)}function _l(){if(!Md&&As!==null){Md=!0;var i=0,l=an;try{var b=As;for(an=1;i>=ue,$-=ue,Yi=1<<32-Ia(l)+$|b<<$|C,js=ee+i}else Yi=1<xr?(Qr=Mt,Mt=null):Qr=Mt.sibling;var sn=lt(Ze,Mt,rt[xr],wt);if(sn===null){Mt===null&&(Mt=Qr);break}i&&Mt&&sn.alternate===null&&l(Ze,Mt),Le=ee(sn,Le,xr),Fr===null?nr=sn:Fr.sibling=sn,Fr=sn,Mt=Qr}if(xr===rt.length)return b(Ze,Mt),Un&&dc(Ze,xr),nr;if(Mt===null){for(;xrxr?(Qr=Mt,Mt=null):Qr=Mt.sibling;var wr=lt(Ze,Mt,sn.value,wt);if(wr===null){Mt===null&&(Mt=Qr);break}i&&Mt&&wr.alternate===null&&l(Ze,Mt),Le=ee(wr,Le,xr),Fr===null?nr=wr:Fr.sibling=wr,Fr=wr,Mt=Qr}if(sn.done)return b(Ze,Mt),Un&&dc(Ze,xr),nr;if(Mt===null){for(;!sn.done;xr++,sn=rt.next())(sn=st(Ze,sn.value,wt))!==null&&(Le=ee(sn,Le,xr),Fr===null?nr=sn:Fr.sibling=sn,Fr=sn);return Un&&dc(Ze,xr),nr}for(Mt=C(Ze,Mt);!sn.done;xr++,sn=rt.next())(sn=vt(Mt,Ze,xr,sn.value,wt))!==null&&(i&&sn.alternate!==null&&Mt.delete(sn.key===null?xr:sn.key),Le=ee(sn,Le,xr),Fr===null?nr=sn:Fr.sibling=sn,Fr=sn);return i&&Mt.forEach((function(_r){return l(Ze,_r)})),Un&&dc(Ze,xr),nr}return function Ze(Le,rt,wt,nr){if(typeof wt=="object"&&wt!==null&&wt.type===Te&&wt.key===null&&(wt=wt.props.children),typeof wt=="object"&&wt!==null){switch(wt.$$typeof){case ie:e:{for(var Fr=wt.key,Mt=rt;Mt!==null;){if(Mt.key===Fr){if((Fr=wt.type)===Te){if(Mt.tag===7){b(Le,Mt.sibling),(rt=$(Mt,wt.props.children)).return=Le,Le=rt;break e}}else if(Mt.elementType===Fr||typeof Fr=="object"&&Fr!==null&&Fr.$$typeof===Re&&rg(Fr)===Mt.type){b(Le,Mt.sibling),(rt=$(Mt,wt.props)).ref=fp(Le,Mt,wt),rt.return=Le,Le=rt;break e}b(Le,Mt);break}l(Le,Mt),Mt=Mt.sibling}wt.type===Te?((rt=Xo(wt.props.children,Le.mode,nr,wt.key)).return=Le,Le=rt):((nr=ku(wt.type,wt.key,wt.props,null,Le.mode,nr)).ref=fp(Le,rt,wt),nr.return=Le,Le=nr)}return ue(Le);case se:e:{for(Mt=wt.key;rt!==null;){if(rt.key===Mt){if(rt.tag===4&&rt.stateNode.containerInfo===wt.containerInfo&&rt.stateNode.implementation===wt.implementation){b(Le,rt.sibling),(rt=$(rt,wt.children||[])).return=Le,Le=rt;break e}b(Le,rt);break}l(Le,rt),rt=rt.sibling}(rt=qh(wt,Le.mode,nr)).return=Le,Le=rt}return ue(Le);case Re:return Ze(Le,rt,(Mt=wt._init)(wt._payload),nr)}if(U(wt))return Lt(Le,rt,wt,nr);if(Xe(wt))return xt(Le,rt,wt,nr);mp(Le,wt)}return typeof wt=="string"&&wt!==""||typeof wt=="number"?(wt=""+wt,rt!==null&&rt.tag===6?(b(Le,rt.sibling),(rt=$(rt,wt)).return=Le,Le=rt):(b(Le,rt),(rt=Tp(wt,Le.mode,nr)).return=Le,Le=rt),ue(Le)):b(Le,rt)}}var fc=ph(!0),ng=ph(!1),hh=Sl(null),dh=null,lu=null,Xr=null;function cu(){Xr=lu=dh=null}function uu(i){var l=hh.current;qn(hh),i._currentValue=l}function qd(i,l,b){for(;i!==null;){var C=i.alternate;if((i.childLanes&l)!==l?(i.childLanes|=l,C!==null&&(C.childLanes|=l)):C!==null&&(C.childLanes&l)!==l&&(C.childLanes|=l),i===b)break;i=i.return}}function pu(i,l){dh=i,Xr=lu=null,(i=i.dependencies)!==null&&i.firstContext!==null&&(i.lanes&l&&(Do=!0),i.firstContext=null)}function oi(i){var l=i._currentValue;if(Xr!==i)if(i={context:i,memoizedValue:l,next:null},lu===null){if(dh===null)throw Error(f(308));lu=i,dh.dependencies={lanes:0,firstContext:i}}else lu=lu.next=i;return l}var Ol=null;function gp(i){Ol===null?Ol=[i]:Ol.push(i)}function ag(i,l,b,C){var $=l.interleaved;return $===null?(b.next=b,gp(l)):(b.next=$.next,$.next=b),l.interleaved=b,Ps(i,C)}function Ps(i,l){i.lanes|=l;var b=i.alternate;for(b!==null&&(b.lanes|=l),b=i,i=i.return;i!==null;)i.childLanes|=l,(b=i.alternate)!==null&&(b.childLanes|=l),b=i,i=i.return;return b.tag===3?b.stateNode:null}var kl=!1;function yp(i){i.updateQueue={baseState:i.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function og(i,l){i=i.updateQueue,l.updateQueue===i&&(l.updateQueue={baseState:i.baseState,firstBaseUpdate:i.firstBaseUpdate,lastBaseUpdate:i.lastBaseUpdate,shared:i.shared,effects:i.effects})}function Ns(i,l){return{eventTime:i,lane:l,tag:0,payload:null,callback:null,next:null}}function Ts(i,l,b){var C=i.updateQueue;if(C===null)return null;if(C=C.shared,2&pn){var $=C.pending;return $===null?l.next=l:(l.next=$.next,$.next=l),C.pending=l,Ps(i,b)}return($=C.interleaved)===null?(l.next=l,gp(C)):(l.next=$.next,$.next=l),C.interleaved=l,Ps(i,b)}function hu(i,l,b){if((l=l.updateQueue)!==null&&(l=l.shared,4194240&b)){var C=l.lanes;b|=C&=i.pendingLanes,l.lanes=b,vs(i,b)}}function fh(i,l){var b=i.updateQueue,C=i.alternate;if(C!==null&&b===(C=C.updateQueue)){var $=null,ee=null;if((b=b.firstBaseUpdate)!==null){do{var ue={eventTime:b.eventTime,lane:b.lane,tag:b.tag,payload:b.payload,callback:b.callback,next:null};ee===null?$=ee=ue:ee=ee.next=ue,b=b.next}while(b!==null);ee===null?$=ee=l:ee=ee.next=l}else $=ee=l;return b={baseState:C.baseState,firstBaseUpdate:$,lastBaseUpdate:ee,shared:C.shared,effects:C.effects},void(i.updateQueue=b)}(i=b.lastBaseUpdate)===null?b.firstBaseUpdate=l:i.next=l,b.lastBaseUpdate=l}function mh(i,l,b,C){var $=i.updateQueue;kl=!1;var ee=$.firstBaseUpdate,ue=$.lastBaseUpdate,Ce=$.shared.pending;if(Ce!==null){$.shared.pending=null;var Me=Ce,Ue=Me.next;Me.next=null,ue===null?ee=Ue:ue.next=Ue,ue=Me;var ot=i.alternate;ot!==null&&(Ce=(ot=ot.updateQueue).lastBaseUpdate)!==ue&&(Ce===null?ot.firstBaseUpdate=Ue:Ce.next=Ue,ot.lastBaseUpdate=Me)}if(ee!==null){var st=$.baseState;for(ue=0,ot=Ue=Me=null,Ce=ee;;){var lt=Ce.lane,vt=Ce.eventTime;if((C<)===lt){ot!==null&&(ot=ot.next={eventTime:vt,lane:0,tag:Ce.tag,payload:Ce.payload,callback:Ce.callback,next:null});e:{var Lt=i,xt=Ce;switch(lt=l,vt=b,xt.tag){case 1:if(typeof(Lt=xt.payload)=="function"){st=Lt.call(vt,st,lt);break e}st=Lt;break e;case 3:Lt.flags=-65537&Lt.flags|128;case 0:if((lt=typeof(Lt=xt.payload)=="function"?Lt.call(vt,st,lt):Lt)==null)break e;st=De({},st,lt);break e;case 2:kl=!0}}Ce.callback!==null&&Ce.lane!==0&&(i.flags|=64,(lt=$.effects)===null?$.effects=[Ce]:lt.push(Ce))}else vt={eventTime:vt,lane:lt,tag:Ce.tag,payload:Ce.payload,callback:Ce.callback,next:null},ot===null?(Ue=ot=vt,Me=st):ot=ot.next=vt,ue|=lt;if((Ce=Ce.next)===null){if((Ce=$.shared.pending)===null)break;Ce=(lt=Ce).next,lt.next=null,$.lastBaseUpdate=lt,$.shared.pending=null}}if(ot===null&&(Me=st),$.baseState=Me,$.firstBaseUpdate=Ue,$.lastBaseUpdate=ot,(l=$.shared.interleaved)!==null){$=l;do ue|=$.lane,$=$.next;while($!==l)}else ee===null&&($.shared.lanes=0);Ds|=ue,i.lanes=ue,i.memoizedState=st}}function Ud(i,l,b){if(i=l.effects,l.effects=null,i!==null)for(l=0;lb?b:4,i(!0);var C=Wd.transition;Wd.transition={};try{i(!1),l()}finally{an=b,Wd.transition=C}}function ef(){return ii().memoizedState}function fg(i,l,b){var C=Tl(i);b={lane:C,action:b,hasEagerState:!1,eagerState:null,next:null},gg(i)?gc(l,b):(b=ag(i,l,b,C))!==null&&(Lo(b,i,C,to()),Yo(b,l,C))}function mg(i,l,b){var C=Tl(i),$={lane:C,action:b,hasEagerState:!1,eagerState:null,next:null};if(gg(i))gc(l,$);else{var ee=i.alternate;if(i.lanes===0&&(ee===null||ee.lanes===0)&&(ee=l.lastRenderedReducer)!==null)try{var ue=l.lastRenderedState,Ce=ee(ue,b);if($.hasEagerState=!0,$.eagerState=Ce,Ka(Ce,ue)){var Me=l.interleaved;return Me===null?($.next=$,gp(l)):($.next=Me.next,Me.next=$),void(l.interleaved=$)}}catch{}(b=ag(i,l,$,C))!==null&&(Lo(b,i,C,$=to()),Yo(b,l,C))}}function gg(i){var l=i.alternate;return i===Dr||l!==null&&l===Dr}function gc(i,l){wp=mc=!0;var b=i.pending;b===null?l.next=l:(l.next=b.next,b.next=l),i.pending=l}function Yo(i,l,b){if(4194240&b){var C=l.lanes;b|=C&=i.pendingLanes,l.lanes=b,vs(i,b)}}var _p={readContext:oi,useCallback:Qa,useContext:Qa,useEffect:Qa,useImperativeHandle:Qa,useInsertionEffect:Qa,useLayoutEffect:Qa,useMemo:Qa,useReducer:Qa,useRef:Qa,useState:Qa,useDebugValue:Qa,useDeferredValue:Qa,useTransition:Qa,useMutableSource:Qa,useSyncExternalStore:Qa,useId:Qa,unstable_isNewReconciler:!1},tf={readContext:oi,useCallback:function(i,l){return Xi().memoizedState=[i,l===void 0?null:l],i},useContext:oi,useEffect:pg,useImperativeHandle:function(i,l,b){return b=b!=null?b.concat([i]):null,Sh(4194308,4,_h.bind(null,l,i),b)},useLayoutEffect:function(i,l){return Sh(4194308,4,i,l)},useInsertionEffect:function(i,l){return Sh(4,2,i,l)},useMemo:function(i,l){var b=Xi();return l=l===void 0?null:l,i=i(),b.memoizedState=[i,l],i},useReducer:function(i,l,b){var C=Xi();return l=b!==void 0?b(l):l,C.memoizedState=C.baseState=l,i={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:i,lastRenderedState:l},C.queue=i,i=i.dispatch=fg.bind(null,Dr,i),[C.memoizedState,i]},useRef:function(i){return i={current:i},Xi().memoizedState=i},useState:Gd,useDebugValue:Xd,useDeferredValue:function(i){return Xi().memoizedState=i},useTransition:function(){var i=Gd(!1),l=i[0];return i=l0.bind(null,i[1]),Xi().memoizedState=i,[l,i]},useMutableSource:function(){},useSyncExternalStore:function(i,l,b){var C=Dr,$=Xi();if(Un){if(b===void 0)throw Error(f(407));b=b()}else{if(b=l(),Ca===null)throw Error(f(349));30&ba||Eh(C,l,b)}$.memoizedState=b;var ee={value:b,getSnapshot:l};return $.queue=ee,pg(cg.bind(null,C,ee,i),[i]),C.flags|=2048,Sp(9,lg.bind(null,C,ee,b,l),void 0,null),b},useId:function(){var i=Xi(),l=Ca.identifierPrefix;if(Un){var b=js;l=":"+l+"R"+(b=(Yi&~(1<<32-Ia(Yi)-1)).toString(32)+b),0<(b=Ep++)&&(l+="H"+b.toString(32)),l+=":"}else l=":"+l+"r"+(b=s0++).toString(32)+":";return i.memoizedState=l},unstable_isNewReconciler:!1},rf={readContext:oi,useCallback:gu,useContext:oi,useEffect:xh,useImperativeHandle:dg,useInsertionEffect:hg,useLayoutEffect:Ci,useMemo:Qd,useReducer:wh,useRef:Yd,useState:function(){return wh(mu)},useDebugValue:Xd,useDeferredValue:function(i){return Zd(ii(),ka.memoizedState,i)},useTransition:function(){return[wh(mu)[0],ii().memoizedState]},useMutableSource:ig,useSyncExternalStore:sg,useId:ef,unstable_isNewReconciler:!1},yg={readContext:oi,useCallback:gu,useContext:oi,useEffect:xh,useImperativeHandle:dg,useInsertionEffect:hg,useLayoutEffect:Ci,useMemo:Qd,useReducer:Kd,useRef:Yd,useState:function(){return Kd(mu)},useDebugValue:Xd,useDeferredValue:function(i){var l=ii();return ka===null?l.memoizedState=i:Zd(l,ka.memoizedState,i)},useTransition:function(){return[Kd(mu)[0],ii().memoizedState]},useMutableSource:ig,useSyncExternalStore:sg,useId:ef,unstable_isNewReconciler:!1};function si(i,l){if(i&&i.defaultProps){for(var b in l=De({},l),i=i.defaultProps)l[b]===void 0&&(l[b]=i[b]);return l}return l}function Oh(i,l,b,C){b=(b=b(C,l=i.memoizedState))==null?l:De({},l,b),i.memoizedState=b,i.lanes===0&&(i.updateQueue.baseState=b)}var kh={isMounted:function(i){return!!(i=i._reactInternals)&&Va(i)===i},enqueueSetState:function(i,l,b){i=i._reactInternals;var C=to(),$=Tl(i),ee=Ns(C,$);ee.payload=l,b!=null&&(ee.callback=b),(l=Ts(i,ee,$))!==null&&(Lo(l,i,$,C),hu(l,i,$))},enqueueReplaceState:function(i,l,b){i=i._reactInternals;var C=to(),$=Tl(i),ee=Ns(C,$);ee.tag=1,ee.payload=l,b!=null&&(ee.callback=b),(l=Ts(i,ee,$))!==null&&(Lo(l,i,$,C),hu(l,i,$))},enqueueForceUpdate:function(i,l){i=i._reactInternals;var b=to(),C=Tl(i),$=Ns(b,C);$.tag=2,l!=null&&($.callback=l),(l=Ts(i,$,C))!==null&&(Lo(l,i,C,b),hu(l,i,C))}};function vg(i,l,b,C,$,ee,ue){return typeof(i=i.stateNode).shouldComponentUpdate=="function"?i.shouldComponentUpdate(C,ee,ue):!l.prototype||!l.prototype.isPureReactComponent||!_s(b,C)||!_s($,ee)}function bg(i,l,b){var C=!1,$=xl,ee=l.contextType;return typeof ee=="object"&&ee!==null?ee=oi(ee):($=Ko(l)?ks:Xa.current,ee=(C=(C=l.contextTypes)!=null)?hc(i,$):xl),l=new l(b,ee),i.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,l.updater=kh,i.stateNode=l,l._reactInternals=i,C&&((i=i.stateNode).__reactInternalMemoizedUnmaskedChildContext=$,i.__reactInternalMemoizedMaskedChildContext=ee),l}function wg(i,l,b,C){i=l.state,typeof l.componentWillReceiveProps=="function"&&l.componentWillReceiveProps(b,C),typeof l.UNSAFE_componentWillReceiveProps=="function"&&l.UNSAFE_componentWillReceiveProps(b,C),l.state!==i&&kh.enqueueReplaceState(l,l.state,null)}function nf(i,l,b,C){var $=i.stateNode;$.props=b,$.state=i.memoizedState,$.refs={},yp(i);var ee=l.contextType;typeof ee=="object"&&ee!==null?$.context=oi(ee):(ee=Ko(l)?ks:Xa.current,$.context=hc(i,ee)),$.state=i.memoizedState,typeof(ee=l.getDerivedStateFromProps)=="function"&&(Oh(i,l,ee,b),$.state=i.memoizedState),typeof l.getDerivedStateFromProps=="function"||typeof $.getSnapshotBeforeUpdate=="function"||typeof $.UNSAFE_componentWillMount!="function"&&typeof $.componentWillMount!="function"||(l=$.state,typeof $.componentWillMount=="function"&&$.componentWillMount(),typeof $.UNSAFE_componentWillMount=="function"&&$.UNSAFE_componentWillMount(),l!==$.state&&kh.enqueueReplaceState($,$.state,null),mh(i,b,$,C),$.state=i.memoizedState),typeof $.componentDidMount=="function"&&(i.flags|=4194308)}function yu(i,l){try{var b="",C=l;do b+=Wt(C),C=C.return;while(C);var $=b}catch(ee){$=` +Error generating stack: `+ee.message+` +`+ee.stack}return{value:i,source:l,stack:$,digest:null}}function af(i,l,b){return{value:i,source:null,stack:b??null,digest:l??null}}function Ah(i,l){try{console.error(l.value)}catch(b){setTimeout((function(){throw b}))}}var c0=typeof WeakMap=="function"?WeakMap:Map;function vu(i,l,b){(b=Ns(-1,b)).tag=3,b.payload={element:null};var C=l.value;return b.callback=function(){bc||(bc=!0,Cp=C),Ah(0,l)},b}function Eg(i,l,b){(b=Ns(-1,b)).tag=3;var C=i.type.getDerivedStateFromError;if(typeof C=="function"){var $=l.value;b.payload=function(){return C($)},b.callback=function(){Ah(0,l)}}var ee=i.stateNode;return ee!==null&&typeof ee.componentDidCatch=="function"&&(b.callback=function(){Ah(0,l),typeof C!="function"&&(Pi===null?Pi=new Set([this]):Pi.add(this));var ue=l.stack;this.componentDidCatch(l.value,{componentStack:ue!==null?ue:""})}),b}function ji(i,l,b){var C=i.pingCache;if(C===null){C=i.pingCache=new c0;var $=new Set;C.set(l,$)}else($=C.get(l))===void 0&&($=new Set,C.set(l,$));$.has(b)||($.add(b),i=f0.bind(null,i,l,b),l.then(i,i))}function Sg(i){do{var l;if((l=i.tag===13)&&(l=(l=i.memoizedState)===null||l.dehydrated!==null),l)return i;i=i.return}while(i!==null);return null}function xg(i,l,b,C,$){return 1&i.mode?(i.flags|=65536,i.lanes=$,i):(i===l?i.flags|=65536:(i.flags|=128,b.flags|=131072,b.flags&=-52805,b.tag===1&&(b.alternate===null?b.tag=17:((l=Ns(-1,1)).tag=2,Ts(b,l,1))),b.lanes|=1),i)}var u0=te.ReactCurrentOwner,Do=!1;function Za(i,l,b,C){l.child=i===null?ng(l,null,b,C):fc(l,i.child,b,C)}function _g(i,l,b,C,$){b=b.render;var ee=l.ref;return pu(l,$),C=Is(i,l,b,C,ee,$),b=Hd(),i===null||Do?(Un&&b&&Rd(l),l.flags|=1,Za(i,l,C,$),l.child):(l.updateQueue=i.updateQueue,l.flags&=-2053,i.lanes&=~$,Ms(i,l,$))}function Og(i,l,b,C,$){if(i===null){var ee=b.type;return typeof ee!="function"||Pp(ee)||ee.defaultProps!==void 0||b.compare!==null||b.defaultProps!==void 0?((i=ku(b.type,null,C,l,l.mode,$)).ref=l.ref,i.return=l,l.child=i):(l.tag=15,l.type=ee,kg(i,l,ee,C,$))}if(ee=i.child,!(i.lanes&$)){var ue=ee.memoizedProps;if((b=(b=b.compare)!==null?b:_s)(ue,C)&&i.ref===l.ref)return Ms(i,l,$)}return l.flags|=1,(i=pa(ee,C)).ref=l.ref,i.return=l,l.child=i}function kg(i,l,b,C,$){if(i!==null){var ee=i.memoizedProps;if(_s(ee,C)&&i.ref===l.ref){if(Do=!1,l.pendingProps=C=ee,!(i.lanes&$))return l.lanes=i.lanes,Ms(i,l,$);131072&i.flags&&(Do=!0)}}return eo(i,l,b,C,$)}function of(i,l,b){var C=l.pendingProps,$=C.children,ee=i!==null?i.memoizedState:null;if(C.mode==="hidden")if(1&l.mode){if(!(1073741824&b))return i=ee!==null?ee.baseLanes|b:b,l.lanes=l.childLanes=1073741824,l.memoizedState={baseLanes:i,cachePool:null,transitions:null},l.updateQueue=null,Cn(jl,fo),fo|=i,null;l.memoizedState={baseLanes:0,cachePool:null,transitions:null},C=ee!==null?ee.baseLanes:b,Cn(jl,fo),fo|=C}else l.memoizedState={baseLanes:0,cachePool:null,transitions:null},Cn(jl,fo),fo|=b;else ee!==null?(C=ee.baseLanes|b,l.memoizedState=null):C=b,Cn(jl,fo),fo|=C;return Za(i,l,$,b),l.child}function li(i,l){var b=l.ref;(i===null&&b!==null||i!==null&&i.ref!==b)&&(l.flags|=512,l.flags|=2097152)}function eo(i,l,b,C,$){var ee=Ko(b)?ks:Xa.current;return ee=hc(l,ee),pu(l,$),b=Is(i,l,b,C,ee,$),C=Hd(),i===null||Do?(Un&&C&&Rd(l),l.flags|=1,Za(i,l,b,$),l.child):(l.updateQueue=i.updateQueue,l.flags&=-2053,i.lanes&=~$,Ms(i,l,$))}function Ag(i,l,b,C,$){if(Ko(b)){var ee=!0;lh(l)}else ee=!1;if(pu(l,$),l.stateNode===null)Th(i,l),bg(l,b,C),nf(l,b,C,$),C=!0;else if(i===null){var ue=l.stateNode,Ce=l.memoizedProps;ue.props=Ce;var Me=ue.context,Ue=b.contextType;typeof Ue=="object"&&Ue!==null?Ue=oi(Ue):Ue=hc(l,Ue=Ko(b)?ks:Xa.current);var ot=b.getDerivedStateFromProps,st=typeof ot=="function"||typeof ue.getSnapshotBeforeUpdate=="function";st||typeof ue.UNSAFE_componentWillReceiveProps!="function"&&typeof ue.componentWillReceiveProps!="function"||(Ce!==C||Me!==Ue)&&wg(l,ue,C,Ue),kl=!1;var lt=l.memoizedState;ue.state=lt,mh(l,C,ue,$),Me=l.memoizedState,Ce!==C||lt!==Me||Oa.current||kl?(typeof ot=="function"&&(Oh(l,b,ot,C),Me=l.memoizedState),(Ce=kl||vg(l,b,Ce,C,lt,Me,Ue))?(st||typeof ue.UNSAFE_componentWillMount!="function"&&typeof ue.componentWillMount!="function"||(typeof ue.componentWillMount=="function"&&ue.componentWillMount(),typeof ue.UNSAFE_componentWillMount=="function"&&ue.UNSAFE_componentWillMount()),typeof ue.componentDidMount=="function"&&(l.flags|=4194308)):(typeof ue.componentDidMount=="function"&&(l.flags|=4194308),l.memoizedProps=C,l.memoizedState=Me),ue.props=C,ue.state=Me,ue.context=Ue,C=Ce):(typeof ue.componentDidMount=="function"&&(l.flags|=4194308),C=!1)}else{ue=l.stateNode,og(i,l),Ce=l.memoizedProps,Ue=l.type===l.elementType?Ce:si(l.type,Ce),ue.props=Ue,st=l.pendingProps,lt=ue.context,typeof(Me=b.contextType)=="object"&&Me!==null?Me=oi(Me):Me=hc(l,Me=Ko(b)?ks:Xa.current);var vt=b.getDerivedStateFromProps;(ot=typeof vt=="function"||typeof ue.getSnapshotBeforeUpdate=="function")||typeof ue.UNSAFE_componentWillReceiveProps!="function"&&typeof ue.componentWillReceiveProps!="function"||(Ce!==st||lt!==Me)&&wg(l,ue,C,Me),kl=!1,lt=l.memoizedState,ue.state=lt,mh(l,C,ue,$);var Lt=l.memoizedState;Ce!==st||lt!==Lt||Oa.current||kl?(typeof vt=="function"&&(Oh(l,b,vt,C),Lt=l.memoizedState),(Ue=kl||vg(l,b,Ue,C,lt,Lt,Me)||!1)?(ot||typeof ue.UNSAFE_componentWillUpdate!="function"&&typeof ue.componentWillUpdate!="function"||(typeof ue.componentWillUpdate=="function"&&ue.componentWillUpdate(C,Lt,Me),typeof ue.UNSAFE_componentWillUpdate=="function"&&ue.UNSAFE_componentWillUpdate(C,Lt,Me)),typeof ue.componentDidUpdate=="function"&&(l.flags|=4),typeof ue.getSnapshotBeforeUpdate=="function"&&(l.flags|=1024)):(typeof ue.componentDidUpdate!="function"||Ce===i.memoizedProps&<===i.memoizedState||(l.flags|=4),typeof ue.getSnapshotBeforeUpdate!="function"||Ce===i.memoizedProps&<===i.memoizedState||(l.flags|=1024),l.memoizedProps=C,l.memoizedState=Lt),ue.props=C,ue.state=Lt,ue.context=Me,C=Ue):(typeof ue.componentDidUpdate!="function"||Ce===i.memoizedProps&<===i.memoizedState||(l.flags|=4),typeof ue.getSnapshotBeforeUpdate!="function"||Ce===i.memoizedProps&<===i.memoizedState||(l.flags|=1024),C=!1)}return Ch(i,l,b,C,ee,$)}function Ch(i,l,b,C,$,ee){li(i,l);var ue=!!(128&l.flags);if(!C&&!ue)return $&&Yr(l,b,!1),Ms(i,l,ee);C=l.stateNode,u0.current=l;var Ce=ue&&typeof b.getDerivedStateFromError!="function"?null:C.render();return l.flags|=1,i!==null&&ue?(l.child=fc(l,i.child,null,ee),l.child=fc(l,null,Ce,ee)):Za(i,l,Ce,ee),l.memoizedState=C.state,$&&Yr(l,b,!0),l.child}function Cg(i){var l=i.stateNode;l.pendingContext?Xm(0,l.pendingContext,l.pendingContext!==l.context):l.context&&Xm(0,l.context,!1),zd(i,l.containerInfo)}function sf(i,l,b,C,$){return su(),$d($),l.flags|=256,Za(i,l,b,C),l.child}var jg,lf,Pg,jh={dehydrated:null,treeContext:null,retryLane:0};function Ph(i){return{baseLanes:i,cachePool:null,transitions:null}}function cf(i,l,b){var C,$=l.pendingProps,ee=Wn.current,ue=!1,Ce=!!(128&l.flags);if((C=Ce)||(C=(i===null||i.memoizedState!==null)&&!!(2&ee)),C?(ue=!0,l.flags&=-129):i!==null&&i.memoizedState===null||(ee|=1),Cn(Wn,1&ee),i===null)return hp(l),(i=l.memoizedState)!==null&&(i=i.dehydrated)!==null?(1&l.mode?i.data==="$!"?l.lanes=8:l.lanes=1073741824:l.lanes=1,null):(Ce=$.children,i=$.fallback,ue?($=l.mode,ue=l.child,Ce={mode:"hidden",children:Ce},1&$||ue===null?ue=Np(Ce,$,0,null):(ue.childLanes=0,ue.pendingProps=Ce),i=Xo(i,$,b,null),ue.return=l,i.return=l,ue.sibling=i,l.child=ue,l.child.memoizedState=Ph(b),l.memoizedState=jh,i):uf(l,Ce));if((ee=i.memoizedState)!==null&&(C=ee.dehydrated)!==null)return(function(ot,st,lt,vt,Lt,xt,Ze){if(lt)return 256&st.flags?(st.flags&=-257,Nh(ot,st,Ze,vt=af(Error(f(422))))):st.memoizedState!==null?(st.child=ot.child,st.flags|=128,null):(xt=vt.fallback,Lt=st.mode,vt=Np({mode:"visible",children:vt.children},Lt,0,null),(xt=Xo(xt,Lt,Ze,null)).flags|=2,vt.return=st,xt.return=st,vt.sibling=xt,st.child=vt,1&st.mode&&fc(st,ot.child,null,Ze),st.child.memoizedState=Ph(Ze),st.memoizedState=jh,xt);if(!(1&st.mode))return Nh(ot,st,Ze,null);if(Lt.data==="$!"){if(vt=Lt.nextSibling&&Lt.nextSibling.dataset)var Le=vt.dgst;return vt=Le,Nh(ot,st,Ze,vt=af(xt=Error(f(419)),vt,void 0))}if(Le=!!(Ze&ot.childLanes),Do||Le){if((vt=Ca)!==null){switch(Ze&-Ze){case 4:Lt=2;break;case 16:Lt=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:Lt=32;break;case 536870912:Lt=268435456;break;default:Lt=0}(Lt=Lt&(vt.suspendedLanes|Ze)?0:Lt)!==0&&Lt!==xt.retryLane&&(xt.retryLane=Lt,Ps(ot,Lt),Lo(vt,ot,Lt,-1))}return xf(),Nh(ot,st,Ze,vt=af(Error(f(421))))}return Lt.data==="$?"?(st.flags|=128,st.child=ot.child,st=m0.bind(null,ot),Lt._reactRetry=st,null):(ot=xt.treeContext,Ro=bl(Lt.nextSibling),Go=st,Un=!0,Oi=null,ot!==null&&(Io[Mo++]=Yi,Io[Mo++]=js,Io[Mo++]=Cs,Yi=ot.id,js=ot.overflow,Cs=st),st=uf(st,vt.children),st.flags|=4096,st)})(i,l,Ce,$,C,ee,b);if(ue){ue=$.fallback,Ce=l.mode,C=(ee=i.child).sibling;var Me={mode:"hidden",children:$.children};return 1&Ce||l.child===ee?($=pa(ee,Me)).subtreeFlags=14680064&ee.subtreeFlags:(($=l.child).childLanes=0,$.pendingProps=Me,l.deletions=null),C!==null?ue=pa(C,ue):(ue=Xo(ue,Ce,b,null)).flags|=2,ue.return=l,$.return=l,$.sibling=ue,l.child=$,$=ue,ue=l.child,Ce=(Ce=i.child.memoizedState)===null?Ph(b):{baseLanes:Ce.baseLanes|b,cachePool:null,transitions:Ce.transitions},ue.memoizedState=Ce,ue.childLanes=i.childLanes&~b,l.memoizedState=jh,$}return i=(ue=i.child).sibling,$=pa(ue,{mode:"visible",children:$.children}),!(1&l.mode)&&($.lanes=b),$.return=l,$.sibling=null,i!==null&&((b=l.deletions)===null?(l.deletions=[i],l.flags|=16):b.push(i)),l.child=$,l.memoizedState=null,$}function uf(i,l){return(l=Np({mode:"visible",children:l},i.mode,0,null)).return=i,i.child=l}function Nh(i,l,b,C){return C!==null&&$d(C),fc(l,i.child,null,b),(i=uf(l,l.pendingProps.children)).flags|=2,l.memoizedState=null,i}function Ng(i,l,b){i.lanes|=l;var C=i.alternate;C!==null&&(C.lanes|=l),qd(i.return,l,b)}function pf(i,l,b,C,$){var ee=i.memoizedState;ee===null?i.memoizedState={isBackwards:l,rendering:null,renderingStartTime:0,last:C,tail:b,tailMode:$}:(ee.isBackwards=l,ee.rendering=null,ee.renderingStartTime=0,ee.last=C,ee.tail=b,ee.tailMode=$)}function Tg(i,l,b){var C=l.pendingProps,$=C.revealOrder,ee=C.tail;if(Za(i,l,C.children,b),2&(C=Wn.current))C=1&C|2,l.flags|=128;else{if(i!==null&&128&i.flags)e:for(i=l.child;i!==null;){if(i.tag===13)i.memoizedState!==null&&Ng(i,b,l);else if(i.tag===19)Ng(i,b,l);else if(i.child!==null){i.child.return=i,i=i.child;continue}if(i===l)break e;for(;i.sibling===null;){if(i.return===null||i.return===l)break e;i=i.return}i.sibling.return=i.return,i=i.sibling}C&=1}if(Cn(Wn,C),1&l.mode)switch($){case"forwards":for(b=l.child,$=null;b!==null;)(i=b.alternate)!==null&&vh(i)===null&&($=b),b=b.sibling;(b=$)===null?($=l.child,l.child=null):($=b.sibling,b.sibling=null),pf(l,!1,$,b,ee);break;case"backwards":for(b=null,$=l.child,l.child=null;$!==null;){if((i=$.alternate)!==null&&vh(i)===null){l.child=$;break}i=$.sibling,$.sibling=b,b=$,$=i}pf(l,!0,b,null,ee);break;case"together":pf(l,!1,null,null,void 0);break;default:l.memoizedState=null}else l.memoizedState=null;return l.child}function Th(i,l){!(1&l.mode)&&i!==null&&(i.alternate=null,l.alternate=null,l.flags|=2)}function Ms(i,l,b){if(i!==null&&(l.dependencies=i.dependencies),Ds|=l.lanes,!(b&l.childLanes))return null;if(i!==null&&l.child!==i.child)throw Error(f(153));if(l.child!==null){for(b=pa(i=l.child,i.pendingProps),l.child=b,b.return=l;i.sibling!==null;)i=i.sibling,(b=b.sibling=pa(i,i.pendingProps)).return=l;b.sibling=null}return l.child}function bu(i,l){if(!Un)switch(i.tailMode){case"hidden":l=i.tail;for(var b=null;l!==null;)l.alternate!==null&&(b=l),l=l.sibling;b===null?i.tail=null:b.sibling=null;break;case"collapsed":b=i.tail;for(var C=null;b!==null;)b.alternate!==null&&(C=b),b=b.sibling;C===null?l||i.tail===null?i.tail=null:i.tail.sibling=null:C.sibling=null}}function La(i){var l=i.alternate!==null&&i.alternate.child===i.child,b=0,C=0;if(l)for(var $=i.child;$!==null;)b|=$.lanes|$.childLanes,C|=14680064&$.subtreeFlags,C|=14680064&$.flags,$.return=i,$=$.sibling;else for($=i.child;$!==null;)b|=$.lanes|$.childLanes,C|=$.subtreeFlags,C|=$.flags,$.return=i,$=$.sibling;return i.subtreeFlags|=C,i.childLanes=b,l}function Ig(i,l,b){var C=l.pendingProps;switch(Dd(l),l.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return La(l),null;case 1:case 17:return Ko(l.type)&&sh(),La(l),null;case 3:return C=l.stateNode,va(),qn(Oa),qn(Xa),Jd(),C.pendingContext&&(C.context=C.pendingContext,C.pendingContext=null),i!==null&&i.child!==null||(dp(l)?l.flags|=4:i===null||i.memoizedState.isDehydrated&&!(256&l.flags)||(l.flags|=1024,Oi!==null&&(wf(Oi),Oi=null))),La(l),null;case 5:yh(l);var $=Al(bp.current);if(b=l.type,i!==null&&l.stateNode!=null)lf(i,l,b,C),i.ref!==l.ref&&(l.flags|=512,l.flags|=2097152);else{if(!C){if(l.stateNode===null)throw Error(f(166));return La(l),null}if(i=Al(ki.current),dp(l)){C=l.stateNode,b=l.type;var ee=l.memoizedProps;switch(C[Ki]=l,C[lp]=ee,i=!!(1&l.mode),b){case"dialog":fe("cancel",C),fe("close",C);break;case"iframe":case"object":case"embed":fe("load",C);break;case"video":case"audio":for($=0;$<\/script>",i=i.removeChild(i.firstChild)):typeof C.is=="string"?i=ue.createElement(b,{is:C.is}):(i=ue.createElement(b),b==="select"&&(ue=i,C.multiple?ue.multiple=!0:C.size&&(ue.size=C.size))):i=ue.createElementNS(i,b),i[Ki]=l,i[lp]=C,jg(i,l),l.stateNode=i;e:{switch(ue=un(b,C),b){case"dialog":fe("cancel",i),fe("close",i),$=C;break;case"iframe":case"object":case"embed":fe("load",i),$=C;break;case"video":case"audio":for($=0;$Pl&&(l.flags|=128,C=!0,bu(ee,!1),l.lanes=4194304)}else{if(!C)if((i=vh(ue))!==null){if(l.flags|=128,C=!0,(b=i.updateQueue)!==null&&(l.updateQueue=b,l.flags|=4),bu(ee,!0),ee.tail===null&&ee.tailMode==="hidden"&&!ue.alternate&&!Un)return La(l),null}else 2*Yt()-ee.renderingStartTime>Pl&&b!==1073741824&&(l.flags|=128,C=!0,bu(ee,!1),l.lanes=4194304);ee.isBackwards?(ue.sibling=l.child,l.child=ue):((b=ee.last)!==null?b.sibling=ue:l.child=ue,ee.last=ue)}return ee.tail!==null?(l=ee.tail,ee.rendering=l,ee.tail=l.sibling,ee.renderingStartTime=Yt(),l.sibling=null,b=Wn.current,Cn(Wn,C?1&b|2:1&b),l):(La(l),null);case 22:case 23:return Sf(),C=l.memoizedState!==null,i!==null&&i.memoizedState!==null!==C&&(l.flags|=8192),C&&1&l.mode?1073741824&fo&&(La(l),6&l.subtreeFlags&&(l.flags|=8192)):La(l),null;case 24:case 25:return null}throw Error(f(156,l.tag))}function Mg(i,l){switch(Dd(l),l.tag){case 1:return Ko(l.type)&&sh(),65536&(i=l.flags)?(l.flags=-65537&i|128,l):null;case 3:return va(),qn(Oa),qn(Xa),Jd(),65536&(i=l.flags)&&!(128&i)?(l.flags=-65537&i|128,l):null;case 5:return yh(l),null;case 13:if(qn(Wn),(i=l.memoizedState)!==null&&i.dehydrated!==null){if(l.alternate===null)throw Error(f(340));su()}return 65536&(i=l.flags)?(l.flags=-65537&i|128,l):null;case 19:return qn(Wn),null;case 4:return va(),null;case 10:return uu(l.type._context),null;case 22:case 23:return Sf(),null;default:return null}}jg=function(i,l){for(var b=l.child;b!==null;){if(b.tag===5||b.tag===6)i.appendChild(b.stateNode);else if(b.tag!==4&&b.child!==null){b.child.return=b,b=b.child;continue}if(b===l)break;for(;b.sibling===null;){if(b.return===null||b.return===l)return;b=b.return}b.sibling.return=b.return,b=b.sibling}},lf=function(i,l,b,C){var $=i.memoizedProps;if($!==C){i=l.stateNode,Al(ki.current);var ee,ue=null;switch(b){case"input":$=Gt(i,$),C=Gt(i,C),ue=[];break;case"select":$=De({},$,{value:void 0}),C=De({},C,{value:void 0}),ue=[];break;case"textarea":$=ne(i,$),C=ne(i,C),ue=[];break;default:typeof $.onClick!="function"&&typeof C.onClick=="function"&&(i.onclick=Hn)}for(Ue in Zn(b,C),b=null,$)if(!C.hasOwnProperty(Ue)&&$.hasOwnProperty(Ue)&&$[Ue]!=null)if(Ue==="style"){var Ce=$[Ue];for(ee in Ce)Ce.hasOwnProperty(ee)&&(b||(b={}),b[ee]="")}else Ue!=="dangerouslySetInnerHTML"&&Ue!=="children"&&Ue!=="suppressContentEditableWarning"&&Ue!=="suppressHydrationWarning"&&Ue!=="autoFocus"&&(m.hasOwnProperty(Ue)?ue||(ue=[]):(ue=ue||[]).push(Ue,null));for(Ue in C){var Me=C[Ue];if(Ce=$?.[Ue],C.hasOwnProperty(Ue)&&Me!==Ce&&(Me!=null||Ce!=null))if(Ue==="style")if(Ce){for(ee in Ce)!Ce.hasOwnProperty(ee)||Me&&Me.hasOwnProperty(ee)||(b||(b={}),b[ee]="");for(ee in Me)Me.hasOwnProperty(ee)&&Ce[ee]!==Me[ee]&&(b||(b={}),b[ee]=Me[ee])}else b||(ue||(ue=[]),ue.push(Ue,b)),b=Me;else Ue==="dangerouslySetInnerHTML"?(Me=Me?Me.__html:void 0,Ce=Ce?Ce.__html:void 0,Me!=null&&Ce!==Me&&(ue=ue||[]).push(Ue,Me)):Ue==="children"?typeof Me!="string"&&typeof Me!="number"||(ue=ue||[]).push(Ue,""+Me):Ue!=="suppressContentEditableWarning"&&Ue!=="suppressHydrationWarning"&&(m.hasOwnProperty(Ue)?(Me!=null&&Ue==="onScroll"&&fe("scroll",i),ue||Ce===Me||(ue=[])):(ue=ue||[]).push(Ue,Me))}b&&(ue=ue||[]).push("style",b);var Ue=ue;(l.updateQueue=Ue)&&(l.flags|=4)}},Pg=function(i,l,b,C){b!==C&&(l.flags|=4)};var Ih=!1,$a=!1,Rg=typeof WeakSet=="function"?WeakSet:Set,cr=null;function wu(i,l){var b=i.ref;if(b!==null)if(typeof b=="function")try{b(null)}catch(C){Gn(i,l,C)}else b.current=null}function Dg(i,l,b){try{b()}catch(C){Gn(i,l,C)}}var Fg=!1;function Op(i,l,b){var C=l.updateQueue;if((C=C!==null?C.lastEffect:null)!==null){var $=C=C.next;do{if(($.tag&i)===i){var ee=$.destroy;$.destroy=void 0,ee!==void 0&&Dg(l,b,ee)}$=$.next}while($!==C)}}function Eu(i,l){if((l=(l=l.updateQueue)!==null?l.lastEffect:null)!==null){var b=l=l.next;do{if((b.tag&i)===i){var C=b.create;b.destroy=C()}b=b.next}while(b!==l)}}function Mh(i){var l=i.ref;if(l!==null){var b=i.stateNode;i.tag,i=b,typeof l=="function"?l(i):l.current=i}}function Lg(i){var l=i.alternate;l!==null&&(i.alternate=null,Lg(l)),i.child=null,i.deletions=null,i.sibling=null,i.tag===5&&(l=i.stateNode)!==null&&(delete l[Ki],delete l[lp],delete l[ih],delete l[o0],delete l[i0]),i.stateNode=null,i.return=null,i.dependencies=null,i.memoizedProps=null,i.memoizedState=null,i.pendingProps=null,i.stateNode=null,i.updateQueue=null}function hf(i){return i.tag===5||i.tag===3||i.tag===4}function $g(i){e:for(;;){for(;i.sibling===null;){if(i.return===null||hf(i.return))return null;i=i.return}for(i.sibling.return=i.return,i=i.sibling;i.tag!==5&&i.tag!==6&&i.tag!==18;){if(2&i.flags||i.child===null||i.tag===4)continue e;i.child.return=i,i=i.child}if(!(2&i.flags))return i.stateNode}}function df(i,l,b){var C=i.tag;if(C===5||C===6)i=i.stateNode,l?b.nodeType===8?b.parentNode.insertBefore(i,l):b.insertBefore(i,l):(b.nodeType===8?(l=b.parentNode).insertBefore(i,b):(l=b).appendChild(i),(b=b._reactRootContainer)!=null||l.onclick!==null||(l.onclick=Hn));else if(C!==4&&(i=i.child)!==null)for(df(i,l,b),i=i.sibling;i!==null;)df(i,l,b),i=i.sibling}function Rh(i,l,b){var C=i.tag;if(C===5||C===6)i=i.stateNode,l?b.insertBefore(i,l):b.appendChild(i);else if(C!==4&&(i=i.child)!==null)for(Rh(i,l,b),i=i.sibling;i!==null;)Rh(i,l,b),i=i.sibling}var ho=null,ci=!1;function Cl(i,l,b){for(b=b.child;b!==null;)ff(i,l,b),b=b.sibling}function ff(i,l,b){if(Wa&&typeof Wa.onCommitFiberUnmount=="function")try{Wa.onCommitFiberUnmount(Ja,b)}catch{}switch(b.tag){case 5:$a||wu(b,l);case 6:var C=ho,$=ci;ho=null,Cl(i,l,b),ci=$,(ho=C)!==null&&(ci?(i=ho,b=b.stateNode,i.nodeType===8?i.parentNode.removeChild(b):i.removeChild(b)):ho.removeChild(b.stateNode));break;case 18:ho!==null&&(ci?(i=ho,b=b.stateNode,i.nodeType===8?Pd(i.parentNode,b):i.nodeType===1&&Pd(i,b),Po(i)):Pd(ho,b.stateNode));break;case 4:C=ho,$=ci,ho=b.stateNode.containerInfo,ci=!0,Cl(i,l,b),ho=C,ci=$;break;case 0:case 11:case 14:case 15:if(!$a&&(C=b.updateQueue)!==null&&(C=C.lastEffect)!==null){$=C=C.next;do{var ee=$,ue=ee.destroy;ee=ee.tag,ue!==void 0&&(2&ee||4&ee)&&Dg(b,l,ue),$=$.next}while($!==C)}Cl(i,l,b);break;case 1:if(!$a&&(wu(b,l),typeof(C=b.stateNode).componentWillUnmount=="function"))try{C.props=b.memoizedProps,C.state=b.memoizedState,C.componentWillUnmount()}catch(Ce){Gn(b,l,Ce)}Cl(i,l,b);break;case 21:Cl(i,l,b);break;case 22:1&b.mode?($a=(C=$a)||b.memoizedState!==null,Cl(i,l,b),$a=C):Cl(i,l,b);break;default:Cl(i,l,b)}}function Bg(i){var l=i.updateQueue;if(l!==null){i.updateQueue=null;var b=i.stateNode;b===null&&(b=i.stateNode=new Rg),l.forEach((function(C){var $=ey.bind(null,i,C);b.has(C)||(b.add(C),C.then($,$))}))}}function Qi(i,l){var b=l.deletions;if(b!==null)for(var C=0;C$&&($=ue),C&=~ee}if(C=$,10<(C=(120>(C=Yt()-C)?120:480>C?480:1080>C?1080:1920>C?1920:3e3>C?3e3:4320>C?4320:1960*p0(C/1960))-C)){i.timeoutHandle=Ga(_u.bind(null,i,Fo,Zi),C);break}_u(i,Fo,Zi);break;default:throw Error(f(329))}}}return $o(i,Yt()),i.callbackNode===b?Wg.bind(null,i):null}function bf(i,l){var b=vc;return i.current.memoizedState.isDehydrated&&(Ec(i,l).flags|=256),(i=Bh(i,l))!==2&&(l=Fo,Fo=b,l!==null&&wf(l)),i}function wf(i){Fo===null?Fo=i:Fo.push.apply(Fo,i)}function Il(i,l){for(l&=~kp,l&=~Su,i.suspendedLanes|=l,i.pingedLanes&=~l,i=i.expirationTimes;0i?16:i,Nl===null)var C=!1;else{if(i=Nl,Nl=null,Fh=0,6&pn)throw Error(f(331));var $=pn;for(pn|=4,cr=i.current;cr!==null;){var ee=cr,ue=ee.child;if(16&cr.flags){var Ce=ee.deletions;if(Ce!==null){for(var Me=0;MeYt()-Ap?Ec(i,0):kp|=b),$o(i,l)}function Zg(i,l){l===0&&(1&i.mode?(l=ys,!(130023424&(ys<<=1))&&(ys=4194304)):l=1);var b=to();(i=Ps(i,l))!==null&&(Ao(i,l,b),$o(i,b))}function m0(i){var l=i.memoizedState,b=0;l!==null&&(b=l.retryLane),Zg(i,b)}function ey(i,l){var b=0;switch(i.tag){case 13:var C=i.stateNode,$=i.memoizedState;$!==null&&(b=$.retryLane);break;case 19:C=i.stateNode;break;default:throw Error(f(314))}C!==null&&C.delete(l),Zg(i,b)}function ty(i,l){return fi(i,l)}function g0(i,l,b,C){this.tag=i,this.key=b,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=l,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=C,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Bo(i,l,b,C){return new g0(i,l,b,C)}function Pp(i){return!(!(i=i.prototype)||!i.isReactComponent)}function pa(i,l){var b=i.alternate;return b===null?((b=Bo(i.tag,l,i.key,i.mode)).elementType=i.elementType,b.type=i.type,b.stateNode=i.stateNode,b.alternate=i,i.alternate=b):(b.pendingProps=l,b.type=i.type,b.flags=0,b.subtreeFlags=0,b.deletions=null),b.flags=14680064&i.flags,b.childLanes=i.childLanes,b.lanes=i.lanes,b.child=i.child,b.memoizedProps=i.memoizedProps,b.memoizedState=i.memoizedState,b.updateQueue=i.updateQueue,l=i.dependencies,b.dependencies=l===null?null:{lanes:l.lanes,firstContext:l.firstContext},b.sibling=i.sibling,b.index=i.index,b.ref=i.ref,b}function ku(i,l,b,C,$,ee){var ue=2;if(C=i,typeof i=="function")Pp(i)&&(ue=1);else if(typeof i=="string")ue=5;else e:switch(i){case Te:return Xo(b.children,$,ee,l);case he:ue=8,$|=8;break;case ge:return(i=Bo(12,b,l,2|$)).elementType=ge,i.lanes=ee,i;case qe:return(i=Bo(13,b,l,$)).elementType=qe,i.lanes=ee,i;case nt:return(i=Bo(19,b,l,$)).elementType=nt,i.lanes=ee,i;case u:return Np(b,$,ee,l);default:if(typeof i=="object"&&i!==null)switch(i.$$typeof){case ke:ue=10;break e;case Ve:ue=9;break e;case He:ue=11;break e;case gt:ue=14;break e;case Re:ue=16,C=null;break e}throw Error(f(130,i==null?i:typeof i,""))}return(l=Bo(ue,b,l,$)).elementType=i,l.type=C,l.lanes=ee,l}function Xo(i,l,b,C){return(i=Bo(7,i,C,l)).lanes=b,i}function Np(i,l,b,C){return(i=Bo(22,i,C,l)).elementType=u,i.lanes=b,i.stateNode={isHidden:!1},i}function Tp(i,l,b){return(i=Bo(6,i,null,l)).lanes=b,i}function qh(i,l,b){return(l=Bo(4,i.children!==null?i.children:[],i.key,l)).lanes=b,l.stateNode={containerInfo:i.containerInfo,pendingChildren:null,implementation:i.implementation},l}function ry(i,l,b,C,$){this.tag=l,this.containerInfo=i,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=ec(0),this.expirationTimes=ec(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=ec(0),this.identifierPrefix=C,this.onRecoverableError=$,this.mutableSourceEagerHydrationData=null}function zn(i,l,b,C,$,ee,ue,Ce,Me){return i=new ry(i,l,b,Ce,Me),l===1?(l=1,ee===!0&&(l|=8)):l=0,ee=Bo(3,null,null,l),i.current=ee,ee.stateNode=i,ee.memoizedState={element:C,isDehydrated:b,cache:null,transitions:null,pendingSuspenseBoundaries:null},yp(ee),i}function ny(i){if(!i)return xl;e:{if(Va(i=i._reactInternals)!==i||i.tag!==1)throw Error(f(170));var l=i;do{switch(l.tag){case 3:l=l.stateNode.context;break e;case 1:if(Ko(l.type)){l=l.stateNode.__reactInternalMemoizedMergedChildContext;break e}}l=l.return}while(l!==null);throw Error(f(171))}if(i.tag===1){var b=i.type;if(Ko(b))return Id(i,b,l)}return l}function Sc(i,l,b,C,$,ee,ue,Ce,Me){return(i=zn(b,C,!0,i,0,ee,0,Ce,Me)).context=ny(null),b=i.current,(ee=Ns(C=to(),$=Tl(b))).callback=l??null,Ts(b,ee,$),i.current.lanes=$,Ao(i,$,C),$o(i,C),i}function Uh(i,l,b,C){var $=l.current,ee=to(),ue=Tl($);return b=ny(b),l.context===null?l.context=b:l.pendingContext=b,(l=Ns(ee,ue)).payload={element:i},(C=C===void 0?null:C)!==null&&(l.callback=C),(i=Ts($,l,ue))!==null&&(Lo(i,$,ue,ee),hu(i,$,ue)),ue}function Ml(i){return(i=i.current).child?(i.child.tag,i.child.stateNode):null}function Rl(i,l){if((i=i.memoizedState)!==null&&i.dehydrated!==null){var b=i.retryLane;i.retryLane=b!==0&&b6?X-6:0),ie=6;ie>",M[z]!=null?_.apply(void 0,[M,z,K,Y,B].concat(te)):j?new Error("Required "+Y+" `"+B+"` was not specified in `"+K+"`."):void 0}var I=T.bind(null,!1);return I.isRequired=T.bind(null,!0),I}function S(_,T){return(function(j,M){return g((function(K,Y,B,X,te){var ie=K[Y];if(!M(ie)){var se=m(ie);return new Error("Invalid "+X+" `"+te+"` of type `"+se+"` supplied to `"+B+"`, expected `"+j+"`.")}return null}))})("Iterable."+_,(function(I){return v.Iterable.isIterable(I)&&T(I)}))}(h={listOf:y,mapOf:y,orderedMapOf:y,setOf:y,orderedSetOf:y,stackOf:y,iterableOf:y,recordOf:y,shape:y,contains:y,mapContains:y,orderedMapContains:y,list:f,map:f,orderedMap:f,set:f,orderedSet:f,stack:f,seq:f,record:f,iterable:f}).iterable.indexed=S("Indexed",v.Iterable.isIndexed),h.iterable.keyed=S("Keyed",v.Iterable.isKeyed),w.exports=h},15287(w,N){var s=Symbol.for("react.element"),h=Symbol.for("react.portal"),v=Symbol.for("react.fragment"),f=Symbol.for("react.strict_mode"),y=Symbol.for("react.profiler"),m=Symbol.for("react.provider"),g=Symbol.for("react.context"),S=Symbol.for("react.forward_ref"),_=Symbol.for("react.suspense"),T=Symbol.for("react.memo"),I=Symbol.for("react.lazy"),j=Symbol.iterator,M={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},z=Object.assign,K={};function Y(Se,De,Ke){this.props=Se,this.context=De,this.refs=K,this.updater=Ke||M}function B(){}function X(Se,De,Ke){this.props=Se,this.context=De,this.refs=K,this.updater=Ke||M}Y.prototype.isReactComponent={},Y.prototype.setState=function(Se,De){if(typeof Se!="object"&&typeof Se!="function"&&Se!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,Se,De,"setState")},Y.prototype.forceUpdate=function(Se){this.updater.enqueueForceUpdate(this,Se,"forceUpdate")},B.prototype=Y.prototype;var te=X.prototype=new B;te.constructor=X,z(te,Y.prototype),te.isPureReactComponent=!0;var ie=Array.isArray,se=Object.prototype.hasOwnProperty,Te={current:null},he={key:!0,ref:!0,__self:!0,__source:!0};function ge(Se,De,Ke){var ft,Nt={},Wt=null,H=null;if(De!=null)for(ft in De.ref!==void 0&&(H=De.ref),De.key!==void 0&&(Wt=""+De.key),De)se.call(De,ft)&&!he.hasOwnProperty(ft)&&(Nt[ft]=De[ft]);var we=arguments.length-2;if(we===1)Nt.children=Ke;else if(12?"one of ".concat(f," ").concat(v.slice(0,y-1).join(", "),", or ")+v[y-1]:y===2?"one of ".concat(f," ").concat(v[0]," or ").concat(v[1]):"of ".concat(f," ").concat(v[0])}return"of ".concat(f," ").concat(String(v))}s("ERR_INVALID_OPT_VALUE",(function(v,f){return'The value "'+f+'" is invalid for option "'+v+'"'}),TypeError),s("ERR_INVALID_ARG_TYPE",(function(v,f,y){var m,g;if(typeof f=="string"&&(function(T,I,j){return T.substr(0,I.length)===I})(f,"not ")?(m="must not be",f=f.replace(/^not /,"")):m="must be",(function(T,I,j){return(j===void 0||j>T.length)&&(j=T.length),T.substring(j-I.length,j)===I})(v," argument"))g="The ".concat(v," ").concat(m," ").concat(h(f,"type"));else{var S=(function(T,I,j){return typeof j!="number"&&(j=0),!(j+I.length>T.length)&&T.indexOf(I,j)!==-1})(v,".")?"property":"argument";g='The "'.concat(v,'" ').concat(S," ").concat(m," ").concat(h(f,"type"))}return g+=". Received type ".concat(typeof y)}),TypeError),s("ERR_STREAM_PUSH_AFTER_EOF","stream.push() after EOF"),s("ERR_METHOD_NOT_IMPLEMENTED",(function(v){return"The "+v+" method is not implemented"})),s("ERR_STREAM_PREMATURE_CLOSE","Premature close"),s("ERR_STREAM_DESTROYED",(function(v){return"Cannot call "+v+" after a stream was destroyed"})),s("ERR_MULTIPLE_CALLBACK","Callback called multiple times"),s("ERR_STREAM_CANNOT_PIPE","Cannot pipe, not readable"),s("ERR_STREAM_WRITE_AFTER_END","write after end"),s("ERR_STREAM_NULL_VALUES","May not write null values to stream",TypeError),s("ERR_UNKNOWN_ENCODING",(function(v){return"Unknown encoding: "+v}),TypeError),s("ERR_STREAM_UNSHIFT_AFTER_END_EVENT","stream.unshift() after end event"),w.exports.F=N},25382(w,N,s){var h=s(65606),v=Object.keys||function(j){var M=[];for(var z in j)M.push(z);return M};w.exports=_;var f=s(45412),y=s(16708);s(56698)(_,f);for(var m=v(y.prototype),g=0;g0)if(typeof we=="string"||Tt.objectMode||Object.getPrototypeOf(we)===m.prototype||(we=(function(Sr){return m.from(Sr)})(we)),mt)Tt.endEmitted?se(H,new ie):Ve(H,Tt,we,!0);else if(Tt.ended)se(H,new X);else{if(Tt.destroyed)return!1;Tt.reading=!1,Tt.decoder&&!ct?(we=Tt.decoder.write(we),Tt.objectMode||we.length!==0?Ve(H,Tt,we,!1):Re(H,Tt)):Ve(H,Tt,we,!1)}else mt||(Tt.reading=!1,Re(H,Tt));return!Tt.ended&&(Tt.lengthwe.highWaterMark&&(we.highWaterMark=(function(mt){return mt>=He?mt=He:(mt--,mt|=mt>>>1,mt|=mt>>>2,mt|=mt>>>4,mt|=mt>>>8,mt|=mt>>>16,mt++),mt})(H)),H<=we.length?H:we.ended?we.length:(we.needReadable=!0,0))}function nt(H){var we=H._readableState;S("emitReadable",we.needReadable,we.emittedReadable),we.needReadable=!1,we.emittedReadable||(S("emitReadable",we.flowing),we.emittedReadable=!0,v.nextTick(gt,H))}function gt(H){var we=H._readableState;S("emitReadable_",we.destroyed,we.length,we.ended),we.destroyed||!we.length&&!we.ended||(H.emit("readable"),we.emittedReadable=!1),we.needReadable=!we.flowing&&!we.ended&&we.length<=we.highWaterMark,De(H)}function Re(H,we){we.readingMore||(we.readingMore=!0,v.nextTick(u,H,we))}function u(H,we){for(;!we.reading&&!we.ended&&(we.length0,we.resumeScheduled&&!we.paused?we.flowing=!0:H.listenerCount("data")>0&&H.resume()}function Xe(H){S("readable nexttick read 0"),H.read(0)}function Se(H,we){S("resume",we.reading),we.reading||H.read(0),we.resumeScheduled=!1,H.emit("resume"),De(H),we.flowing&&!we.reading&&H.read(0)}function De(H){var we=H._readableState;for(S("flow",we.flowing);we.flowing&&H.read()!==null;);}function Ke(H,we){return we.length===0?null:(we.objectMode?ct=we.buffer.shift():!H||H>=we.length?(ct=we.decoder?we.buffer.join(""):we.buffer.length===1?we.buffer.first():we.buffer.concat(we.length),we.buffer.clear()):ct=we.buffer.consume(H,we.decoder),ct);var ct}function ft(H){var we=H._readableState;S("endReadable",we.endEmitted),we.endEmitted||(we.ended=!0,v.nextTick(Nt,we,H))}function Nt(H,we){if(S("endReadableNT",H.endEmitted,H.length),!H.endEmitted&&H.length===0&&(H.endEmitted=!0,we.readable=!1,we.emit("end"),H.autoDestroy)){var ct=we._writableState;(!ct||ct.autoDestroy&&ct.finished)&&we.destroy()}}function Wt(H,we){for(var ct=0,mt=H.length;ct=we.highWaterMark:we.length>0)||we.ended))return S("read: emitReadable",we.length,we.ended),we.length===0&&we.ended?ft(this):nt(this),null;if((H=qe(H,we))===0&&we.ended)return we.length===0&&ft(this),null;var mt,ur=we.needReadable;return S("need readable",ur),(we.length===0||we.length-H0?Ke(H,we):null)===null?(we.needReadable=we.length<=we.highWaterMark,H=0):(we.length-=H,we.awaitDrain=0),we.length===0&&(we.ended||(we.needReadable=!0),ct!==H&&we.ended&&ft(this)),mt!==null&&this.emit("data",mt),mt},ge.prototype._read=function(H){se(this,new te("_read()"))},ge.prototype.pipe=function(H,we){var ct=this,mt=this._readableState;switch(mt.pipesCount){case 0:mt.pipes=H;break;case 1:mt.pipes=[mt.pipes,H];break;default:mt.pipes.push(H)}mt.pipesCount+=1,S("pipe count=%d opts=%j",mt.pipesCount,we);var ur=(!we||we.end!==!1)&&H!==v.stdout&&H!==v.stderr?Tt:U;function ar(Z,ne){S("onunpipe"),Z===ct&&ne&&ne.hasUnpiped===!1&&(ne.hasUnpiped=!0,(function(){S("cleanup"),H.removeListener("close",Zt),H.removeListener("finish",pe),H.removeListener("drain",Gt),H.removeListener("error",yt),H.removeListener("unpipe",ar),ct.removeListener("end",Tt),ct.removeListener("end",U),ct.removeListener("data",kt),Sr=!0,!mt.awaitDrain||H._writableState&&!H._writableState.needDrain||Gt()})())}function Tt(){S("onend"),H.end()}mt.endEmitted?v.nextTick(ur):ct.once("end",ur),H.on("unpipe",ar);var Gt=(function(ne){return function(){var Ee=ne._readableState;S("pipeOnDrain",Ee.awaitDrain),Ee.awaitDrain&&Ee.awaitDrain--,Ee.awaitDrain===0&&f(ne,"data")&&(Ee.flowing=!0,De(ne))}})(ct);H.on("drain",Gt);var Sr=!1;function kt(Z){S("ondata");var ne=H.write(Z);S("dest.write",ne),ne===!1&&((mt.pipesCount===1&&mt.pipes===H||mt.pipesCount>1&&Wt(mt.pipes,H)!==-1)&&!Sr&&(S("false write response, pause",mt.awaitDrain),mt.awaitDrain++),ct.pause())}function yt(Z){S("onerror",Z),U(),H.removeListener("error",yt),f(H,"error")===0&&se(H,Z)}function Zt(){H.removeListener("finish",pe),U()}function pe(){S("onfinish"),H.removeListener("close",Zt),U()}function U(){S("unpipe"),ct.unpipe(H)}return ct.on("data",kt),(function(ne,ye,Ee){if(typeof ne.prependListener=="function")return ne.prependListener(ye,Ee);ne._events&&ne._events[ye]?Array.isArray(ne._events[ye])?ne._events[ye].unshift(Ee):ne._events[ye]=[Ee,ne._events[ye]]:ne.on(ye,Ee)})(H,"error",yt),H.once("close",Zt),H.once("finish",pe),H.emit("pipe",ct),mt.flowing||(S("pipe resume"),ct.resume()),H},ge.prototype.unpipe=function(H){var we=this._readableState,ct={hasUnpiped:!1};if(we.pipesCount===0)return this;if(we.pipesCount===1)return H&&H!==we.pipes||(H||(H=we.pipes),we.pipes=null,we.pipesCount=0,we.flowing=!1,H&&H.emit("unpipe",this,ct)),this;if(!H){var mt=we.pipes,ur=we.pipesCount;we.pipes=null,we.pipesCount=0,we.flowing=!1;for(var ar=0;ar0,mt.flowing!==!1&&this.resume()):H==="readable"&&(mt.endEmitted||mt.readableListening||(mt.readableListening=mt.needReadable=!0,mt.flowing=!1,mt.emittedReadable=!1,S("on readable",mt.length,mt.reading),mt.length?nt(this):mt.reading||v.nextTick(Xe,this))),ct},ge.prototype.addListener=ge.prototype.on,ge.prototype.removeListener=function(H,we){var ct=y.prototype.removeListener.call(this,H,we);return H==="readable"&&v.nextTick(at,this),ct},ge.prototype.removeAllListeners=function(H){var we=y.prototype.removeAllListeners.apply(this,arguments);return H!=="readable"&&H!==void 0||v.nextTick(at,this),we},ge.prototype.resume=function(){var H=this._readableState;return H.flowing||(S("resume"),H.flowing=!H.readableListening,(function(ct,mt){mt.resumeScheduled||(mt.resumeScheduled=!0,v.nextTick(Se,ct,mt))})(this,H)),H.paused=!1,this},ge.prototype.pause=function(){return S("call pause flowing=%j",this._readableState.flowing),this._readableState.flowing!==!1&&(S("pause"),this._readableState.flowing=!1,this.emit("pause")),this._readableState.paused=!0,this},ge.prototype.wrap=function(H){var we=this,ct=this._readableState,mt=!1;for(var ur in H.on("end",(function(){if(S("wrapped end"),ct.decoder&&!ct.ended){var Tt=ct.decoder.end();Tt&&Tt.length&&we.push(Tt)}we.push(null)})),H.on("data",(function(Tt){S("wrapped data"),ct.decoder&&(Tt=ct.decoder.write(Tt)),ct.objectMode&&Tt==null||(ct.objectMode||Tt&&Tt.length)&&(we.push(Tt)||(mt=!0,H.pause()))})),H)this[ur]===void 0&&typeof H[ur]=="function"&&(this[ur]=(function(Gt){return function(){return H[Gt].apply(H,arguments)}})(ur));for(var ar=0;ar-1))throw new ie(u);return this._writableState.defaultEncoding=u,this},Object.defineProperty(ge.prototype,"writableBuffer",{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}}),Object.defineProperty(ge.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),ge.prototype._write=function(Re,u,at){at(new z("_write()"))},ge.prototype._writev=null,ge.prototype.end=function(Re,u,at){var Xe=this._writableState;return typeof Re=="function"?(at=Re,Re=null,u=null):typeof u=="function"&&(at=u,u=null),Re!=null&&this.write(Re,u),Xe.corked&&(Xe.corked=1,this.uncork()),Xe.ending||(function(De,Ke,ft){Ke.ending=!0,gt(De,Ke),ft&&(Ke.finished?v.nextTick(ft):De.once("finish",ft)),Ke.ended=!0,De.writable=!1})(this,Xe,at),this},Object.defineProperty(ge.prototype,"writableLength",{enumerable:!1,get:function(){return this._writableState.length}}),Object.defineProperty(ge.prototype,"destroyed",{enumerable:!1,get:function(){return this._writableState!==void 0&&this._writableState.destroyed},set:function(u){this._writableState&&(this._writableState.destroyed=u)}}),ge.prototype.destroy=T.destroy,ge.prototype._undestroy=T.undestroy,ge.prototype._destroy=function(Re,u){u(Re)}},2955(w,N,s){var h,v=s(65606);function f(X,te,ie){return(te=(function(Te){var he=(function(ke,Ve){if(typeof ke!="object"||ke===null)return ke;var He=ke[Symbol.toPrimitive];if(He!==void 0){var qe=He.call(ke,Ve);if(typeof qe!="object")return qe;throw new TypeError("@@toPrimitive must return a primitive value.")}return(Ve==="string"?String:Number)(ke)})(Te,"string");return typeof he=="symbol"?he:String(he)})(te))in X?Object.defineProperty(X,te,{value:ie,enumerable:!0,configurable:!0,writable:!0}):X[te]=ie,X}var y=s(86238),m=Symbol("lastResolve"),g=Symbol("lastReject"),S=Symbol("error"),_=Symbol("ended"),T=Symbol("lastPromise"),I=Symbol("handlePromise"),j=Symbol("stream");function M(X,te){return{value:X,done:te}}function z(X){var te=X[m];if(te!==null){var ie=X[j].read();ie!==null&&(X[T]=null,X[m]=null,X[g]=null,te(M(ie,!1)))}}function K(X){v.nextTick(z,X)}var Y=Object.getPrototypeOf((function(){})),B=Object.setPrototypeOf((f(h={get stream(){return this[j]},next:function(){var te=this,ie=this[S];if(ie!==null)return Promise.reject(ie);if(this[_])return Promise.resolve(M(void 0,!0));if(this[j].destroyed)return new Promise((function(ge,ke){v.nextTick((function(){te[S]?ke(te[S]):ge(M(void 0,!0))}))}));var se,Te=this[T];if(Te)se=new Promise((function(ke,Ve){return function(He,qe){ke.then((function(){Ve[_]?He(M(void 0,!0)):Ve[I](He,qe)}),qe)}})(Te,this));else{var he=this[j].read();if(he!==null)return Promise.resolve(M(he,!1));se=new Promise(this[I])}return this[T]=se,se}},Symbol.asyncIterator,(function(){return this})),f(h,"return",(function(){var te=this;return new Promise((function(ie,se){te[j].destroy(null,(function(Te){Te?se(Te):ie(M(void 0,!0))}))}))})),h),Y);w.exports=function(te){var ie,se=Object.create(B,(f(ie={},j,{value:te,writable:!0}),f(ie,m,{value:null,writable:!0}),f(ie,g,{value:null,writable:!0}),f(ie,S,{value:null,writable:!0}),f(ie,_,{value:te._readableState.endEmitted,writable:!0}),f(ie,I,{value:function(he,ge){var ke=se[j].read();ke?(se[T]=null,se[m]=null,se[g]=null,he(M(ke,!1))):(se[m]=he,se[g]=ge)},writable:!0}),ie));return se[T]=null,y(te,(function(Te){if(Te&&Te.code!=="ERR_STREAM_PREMATURE_CLOSE"){var he=se[g];return he!==null&&(se[T]=null,se[m]=null,se[g]=null,he(Te)),void(se[S]=Te)}var ge=se[m];ge!==null&&(se[T]=null,se[m]=null,se[g]=null,ge(M(void 0,!0))),se[_]=!0})),te.on("readable",K.bind(null,se)),se}},80345(w,N,s){function h(T,I){var j=Object.keys(T);if(Object.getOwnPropertySymbols){var M=Object.getOwnPropertySymbols(T);I&&(M=M.filter((function(z){return Object.getOwnPropertyDescriptor(T,z).enumerable}))),j.push.apply(j,M)}return j}function v(T){for(var I=1;I0?this.tail.next=M:this.head=M,this.tail=M,++this.length}},{key:"unshift",value:function(j){var M={data:j,next:this.head};this.length===0&&(this.tail=M),this.head=M,++this.length}},{key:"shift",value:function(){if(this.length!==0){var j=this.head.data;return this.length===1?this.head=this.tail=null:this.head=this.head.next,--this.length,j}}},{key:"clear",value:function(){this.head=this.tail=null,this.length=0}},{key:"join",value:function(j){if(this.length===0)return"";for(var M=this.head,z=""+M.data;M=M.next;)z+=j+M.data;return z}},{key:"concat",value:function(j){if(this.length===0)return g.alloc(0);for(var M,z,K,Y=g.allocUnsafe(j>>>0),B=this.head,X=0;B;)M=B.data,z=Y,K=X,g.prototype.copy.call(M,z,K),X+=B.data.length,B=B.next;return Y}},{key:"consume",value:function(j,M){var z;return jY.length?Y.length:j;if(B===Y.length?K+=Y:K+=Y.slice(0,j),(j-=B)===0){B===Y.length?(++z,M.next?this.head=M.next:this.head=this.tail=null):(this.head=M,M.data=Y.slice(B));break}++z}return this.length-=z,K}},{key:"_getBuffer",value:function(j){var M=g.allocUnsafe(j),z=this.head,K=1;for(z.data.copy(M),j-=z.data.length;z=z.next;){var Y=z.data,B=j>Y.length?Y.length:j;if(Y.copy(M,M.length-j,0,B),(j-=B)===0){B===Y.length?(++K,z.next?this.head=z.next:this.head=this.tail=null):(this.head=z,z.data=Y.slice(B));break}++K}return this.length-=K,M}},{key:_,value:function(j,M){return S(this,v(v({},M),{},{depth:0,customInspect:!1}))}}]),T})()},75896(w,N,s){var h=s(65606);function v(m,g){y(m,g),f(m)}function f(m){m._writableState&&!m._writableState.emitClose||m._readableState&&!m._readableState.emitClose||m.emit("close")}function y(m,g){m.emit("error",g)}w.exports={destroy:function(g,S){var _=this,T=this._readableState&&this._readableState.destroyed,I=this._writableState&&this._writableState.destroyed;return T||I?(S?S(g):g&&(this._writableState?this._writableState.errorEmitted||(this._writableState.errorEmitted=!0,h.nextTick(y,this,g)):h.nextTick(y,this,g)),this):(this._readableState&&(this._readableState.destroyed=!0),this._writableState&&(this._writableState.destroyed=!0),this._destroy(g||null,(function(j){!S&&j?_._writableState?_._writableState.errorEmitted?h.nextTick(f,_):(_._writableState.errorEmitted=!0,h.nextTick(v,_,j)):h.nextTick(v,_,j):S?(h.nextTick(f,_),S(j)):h.nextTick(f,_)})),this)},undestroy:function(){this._readableState&&(this._readableState.destroyed=!1,this._readableState.reading=!1,this._readableState.ended=!1,this._readableState.endEmitted=!1),this._writableState&&(this._writableState.destroyed=!1,this._writableState.ended=!1,this._writableState.ending=!1,this._writableState.finalCalled=!1,this._writableState.prefinished=!1,this._writableState.finished=!1,this._writableState.errorEmitted=!1)},errorOrDestroy:function(g,S){var _=g._readableState,T=g._writableState;_&&_.autoDestroy||T&&T.autoDestroy?g.destroy(S):g.emit("error",S)}}},86238(w,N,s){var h=s(86048).F.ERR_STREAM_PREMATURE_CLOSE;function v(){}w.exports=function f(y,m,g){if(typeof m=="function")return f(y,null,m);m||(m={}),g=(function(te){var ie=!1;return function(){if(!ie){ie=!0;for(var se=arguments.length,Te=new Array(se),he=0;he0,(function(te){M||(M=te),te&&K.forEach(g),X||(K.forEach(g),z(M))}))}));return I.reduce(S)}},65291(w,N,s){var h=s(86048).F.ERR_INVALID_OPT_VALUE;w.exports={getHighWaterMark:function(f,y,m,g){var S=(function(T,I,j){return T.highWaterMark!=null?T.highWaterMark:I?T[j]:null})(y,g,m);if(S!=null){if(!isFinite(S)||Math.floor(S)!==S||S<0)throw new h(g?m:"highWaterMark",S);return Math.floor(S)}return f.objectMode?16:16384}}},40345(w,N,s){w.exports=s(37007).EventEmitter},84977(w,N,s){Object.defineProperty(N,"__esModule",{value:!0});var h=(function(y){return y&&y.__esModule?y:{default:y}})(s(49568)),v=s(55674);N.default=function(f){var y=arguments.length>1&&arguments[1]!==void 0?arguments[1]:h.default.Map,m=Object.keys(f);return function(){var g=arguments.length>0&&arguments[0]!==void 0?arguments[0]:y(),S=arguments[1];return g.withMutations((function(_){m.forEach((function(T){var I=(0,f[T])(_.get(T),S);(0,v.validateNextState)(I,T,S),_.set(T,I)}))}))}},w.exports=N.default},89593(w,N,s){N.H=void 0;var h=(function(f){return f&&f.__esModule?f:{default:f}})(s(84977));N.H=h.default},48590(w,N){Object.defineProperty(N,"__esModule",{value:!0}),N.default=function(s){return s&&s.type==="@@redux/INIT"?"initialState argument passed to createStore":"previous state received by the reducer"},w.exports=N.default},82261(w,N,s){Object.defineProperty(N,"__esModule",{value:!0});var h=f(s(49568)),v=f(s(48590));function f(y){return y&&y.__esModule?y:{default:y}}N.default=function(y,m,g){var S=Object.keys(m);if(!S.length)return"Store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers.";var _=(0,v.default)(g);if(h.default.isImmutable?!h.default.isImmutable(y):!h.default.Iterable.isIterable(y))return"The "+_+' is of unexpected type. Expected argument to be an instance of Immutable.Collection or Immutable.Record with the following properties: "'+S.join('", "')+'".';var T=y.toSeq().keySeq().toArray().filter((function(I){return!m.hasOwnProperty(I)}));return T.length>0?"Unexpected "+(T.length===1?"property":"properties")+' "'+T.join('", "')+'" found in '+_+'. Expected to find one of the known reducer property names instead: "'+S.join('", "')+'". Unexpected properties will be ignored.':null},w.exports=N.default},55674(w,N,s){Object.defineProperty(N,"__esModule",{value:!0}),N.validateNextState=N.getUnexpectedInvocationParameterMessage=N.getStateName=void 0;var h=y(s(48590)),v=y(s(82261)),f=y(s(27374));function y(m){return m&&m.__esModule?m:{default:m}}N.getStateName=h.default,N.getUnexpectedInvocationParameterMessage=v.default,N.validateNextState=f.default},27374(w,N){Object.defineProperty(N,"__esModule",{value:!0}),N.default=function(s,h,v){if(s===void 0)throw new Error('Reducer "'+h+'" returned undefined when handling "'+v.type+'" action. To ignore an action, you must explicitly return the previous state.')},w.exports=N.default},75208(w){var N,s="";w.exports=function(v,f){if(typeof v!="string")throw new TypeError("expected a string");if(f===1)return v;if(f===2)return v+v;var y=v.length*f;if(N!==v||N===void 0)N=v,s="";else if(s.length>=y)return s.substr(0,y);for(;y>s.length&&f>1;)1&f&&(s+=v),f>>=1,v+=v;return s=(s+=v).substr(0,y)}},92063(w){w.exports=function(s,h){if(h=h.split(":")[0],!(s=+s))return!1;switch(h){case"http":case"ws":return s!==80;case"https":case"wss":return s!==443;case"ftp":return s!==21;case"gopher":return s!==70;case"file":return!1}return s!==0}},27096(w,N,s){const h=s(87586),v=s(6205),f=s(10023),y=s(8048);w.exports=m=>{var g,S,_=0,T={type:v.ROOT,stack:[]},I=T,j=T.stack,M=[],z=he=>{h.error(m,"Nothing to repeat at column "+(he-1))},K=h.strToChars(m);for(g=K.length;_({type:h.POSITION,value:"b"}),N.nonWordBoundary=()=>({type:h.POSITION,value:"B"}),N.begin=()=>({type:h.POSITION,value:"^"}),N.end=()=>({type:h.POSITION,value:"$"})},10023(w,N,s){const h=s(6205),v=()=>[{type:h.RANGE,from:48,to:57}],f=()=>[{type:h.CHAR,value:95},{type:h.RANGE,from:97,to:122},{type:h.RANGE,from:65,to:90}].concat(v()),y=()=>[{type:h.CHAR,value:9},{type:h.CHAR,value:10},{type:h.CHAR,value:11},{type:h.CHAR,value:12},{type:h.CHAR,value:13},{type:h.CHAR,value:32},{type:h.CHAR,value:160},{type:h.CHAR,value:5760},{type:h.RANGE,from:8192,to:8202},{type:h.CHAR,value:8232},{type:h.CHAR,value:8233},{type:h.CHAR,value:8239},{type:h.CHAR,value:8287},{type:h.CHAR,value:12288},{type:h.CHAR,value:65279}];N.words=()=>({type:h.SET,set:f(),not:!1}),N.notWords=()=>({type:h.SET,set:f(),not:!0}),N.ints=()=>({type:h.SET,set:v(),not:!1}),N.notInts=()=>({type:h.SET,set:v(),not:!0}),N.whitespace=()=>({type:h.SET,set:y(),not:!1}),N.notWhitespace=()=>({type:h.SET,set:y(),not:!0}),N.anyChar=()=>({type:h.SET,set:[{type:h.CHAR,value:10},{type:h.CHAR,value:13},{type:h.CHAR,value:8232},{type:h.CHAR,value:8233}],not:!0})},6205(w){w.exports={ROOT:0,GROUP:1,POSITION:2,SET:3,RANGE:4,REPETITION:5,REFERENCE:6,CHAR:7}},87586(w,N,s){const h=s(6205),v=s(10023),f={0:0,t:9,n:10,v:11,f:12,r:13};N.strToChars=function(y){return y=y.replace(/(\[\\b\])|(\\)?\\(?:u([A-F0-9]{4})|x([A-F0-9]{2})|(0?[0-7]{2})|c([@A-Z[\\\]^?])|([0tnvfr]))/g,(function(m,g,S,_,T,I,j,M){if(S)return m;var z=g?8:_?parseInt(_,16):T?parseInt(T,16):I?parseInt(I,8):j?"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ ?".indexOf(j):f[M],K=String.fromCharCode(z);return/[[\]{}^$.|?*+()]/.test(K)&&(K="\\"+K),K}))},N.tokenizeClass=(y,m)=>{for(var g,S,_=[],T=/\\(?:(w)|(d)|(s)|(W)|(D)|(S))|((?:(?:\\)(.)|([^\]\\]))-(?:\\)?([^\]]))|(\])|(?:\\)?([^])/g;(g=T.exec(y))!=null;)if(g[1])_.push(v.words());else if(g[2])_.push(v.ints());else if(g[3])_.push(v.whitespace());else if(g[4])_.push(v.notWords());else if(g[5])_.push(v.notInts());else if(g[6])_.push(v.notWhitespace());else if(g[7])_.push({type:h.RANGE,from:(g[8]||g[9]).charCodeAt(0),to:g[10].charCodeAt(0)});else{if(!(S=g[12]))return[_,T.lastIndex];_.push({type:h.CHAR,value:S.charCodeAt(0)})}N.error(m,"Unterminated character class")},N.error=(y,m)=>{throw new SyntaxError("Invalid regular expression: /"+y+"/: "+m)}},92861(w,N,s){var h=s(48287),v=h.Buffer;function f(m,g){for(var S in m)g[S]=m[S]}function y(m,g,S){return v(m,g,S)}v.from&&v.alloc&&v.allocUnsafe&&v.allocUnsafeSlow?w.exports=h:(f(h,N),N.Buffer=y),y.prototype=Object.create(v.prototype),f(v,y),y.from=function(m,g,S){if(typeof m=="number")throw new TypeError("Argument must not be a number");return v(m,g,S)},y.alloc=function(m,g,S){if(typeof m!="number")throw new TypeError("Argument must be a number");var _=v(m);return g!==void 0?typeof S=="string"?_.fill(g,S):_.fill(g):_.fill(0),_},y.allocUnsafe=function(m){if(typeof m!="number")throw new TypeError("Argument must be a number");return v(m)},y.allocUnsafeSlow=function(m){if(typeof m!="number")throw new TypeError("Argument must be a number");return h.SlowBuffer(m)}},29844(w,N){function s(Xe,Se){var De=Xe.length;Xe.push(Se);e:for(;0>>1,ft=Xe[Ke];if(!(0>>1;Kef(H,De))wef(ct,H)?(Xe[Ke]=ct,Xe[we]=De,Ke=we):(Xe[Ke]=H,Xe[Wt]=De,Ke=Wt);else{if(!(wef(ct,De)))break e;Xe[Ke]=ct,Xe[we]=De,Ke=we}}}return Se}function f(Xe,Se){var De=Xe.sortIndex-Se.sortIndex;return De!==0?De:Xe.id-Se.id}if(typeof performance=="object"&&typeof performance.now=="function"){var y=performance;N.unstable_now=function(){return y.now()}}else{var m=Date,g=m.now();N.unstable_now=function(){return m.now()-g}}var S=[],_=[],T=1,I=null,j=3,M=!1,z=!1,K=!1,Y=typeof setTimeout=="function"?setTimeout:null,B=typeof clearTimeout=="function"?clearTimeout:null,X=typeof setImmediate<"u"?setImmediate:null;function te(Xe){for(var Se=h(_);Se!==null;){if(Se.callback===null)v(_);else{if(!(Se.startTime<=Xe))break;v(_),Se.sortIndex=Se.expirationTime,s(S,Se)}Se=h(_)}}function ie(Xe){if(K=!1,te(Xe),!z)if(h(S)!==null)z=!0,u(se);else{var Se=h(_);Se!==null&&at(ie,Se.startTime-Xe)}}function se(Xe,Se){z=!1,K&&(K=!1,B(ke),ke=-1),M=!0;var De=j;try{for(te(Se),I=h(S);I!==null&&(!(I.expirationTime>Se)||Xe&&!qe());){var Ke=I.callback;if(typeof Ke=="function"){I.callback=null,j=I.priorityLevel;var ft=Ke(I.expirationTime<=Se);Se=N.unstable_now(),typeof ft=="function"?I.callback=ft:I===h(S)&&v(S),te(Se)}else v(S);I=h(S)}if(I!==null)var Nt=!0;else{var Wt=h(_);Wt!==null&&at(ie,Wt.startTime-Se),Nt=!1}return Nt}finally{I=null,j=De,M=!1}}typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);var Te,he=!1,ge=null,ke=-1,Ve=5,He=-1;function qe(){return!(N.unstable_now()-HeXe||125Ke?(Xe.sortIndex=De,s(_,Xe),h(S)===null&&Xe===h(_)&&(K?(B(ke),ke=-1):K=!0,at(ie,De-Ke))):(Xe.sortIndex=ft,s(S,Xe),z||M||(z=!0,u(se))),Xe},N.unstable_shouldYield=qe,N.unstable_wrapCallback=function(Xe){var Se=j;return function(){var De=j;j=Se;try{return Xe.apply(this,arguments)}finally{j=De}}}},69982(w,N,s){w.exports=s(29844)},20334(w,N,s){var h=s(48287).Buffer;class v extends Error{constructor(S){super(v._prepareSuperMessage(S)),Object.defineProperty(this,"name",{value:"NonError",configurable:!0,writable:!0}),Error.captureStackTrace&&Error.captureStackTrace(this,v)}static _prepareSuperMessage(S){try{return JSON.stringify(S)}catch{return String(S)}}}const f=[{property:"name",enumerable:!1},{property:"message",enumerable:!1},{property:"stack",enumerable:!1},{property:"code",enumerable:!0}],y=Symbol(".toJSON called"),m=({from:g,seen:S,to_:_,forceEnumerable:T,maxDepth:I,depth:j})=>{const M=_||(Array.isArray(g)?[]:{});if(S.push(g),j>=I)return M;if(typeof g.toJSON=="function"&&g[y]!==!0)return(z=>{z[y]=!0;const K=z.toJSON();return delete z[y],K})(g);for(const[z,K]of Object.entries(g))typeof h=="function"&&h.isBuffer(K)?M[z]="[object Buffer]":typeof K!="function"&&(K&&typeof K=="object"?S.includes(g[z])?M[z]="[Circular]":(j++,M[z]=m({from:g[z],seen:S.slice(),forceEnumerable:T,maxDepth:I,depth:j})):M[z]=K);for(const{property:z,enumerable:K}of f)typeof g[z]=="string"&&Object.defineProperty(M,z,{value:g[z],enumerable:!!T||K,configurable:!0,writable:!0});return M};w.exports={serializeError:(g,S={})=>{const{maxDepth:_=Number.POSITIVE_INFINITY}=S;return typeof g=="object"&&g!==null?m({from:g,seen:[],forceEnumerable:!0,maxDepth:_,depth:0}):typeof g=="function"?`[Function: ${g.name||"anonymous"}]`:g},deserializeError:(g,S={})=>{const{maxDepth:_=Number.POSITIVE_INFINITY}=S;if(g instanceof Error)return g;if(typeof g=="object"&&g!==null&&!Array.isArray(g)){const T=new Error;return m({from:g,seen:[],to_:T,maxDepth:_,depth:0}),T}return new v(g)}}},96897(w,N,s){var h=s(70453),v=s(30041),f=s(30592)(),y=s(75795),m=s(69675),g=h("%Math.floor%");w.exports=function(_,T){if(typeof _!="function")throw new m("`fn` is not a function");if(typeof T!="number"||T<0||T>4294967295||g(T)!==T)throw new m("`length` must be a positive 32-bit integer");var I=arguments.length>2&&!!arguments[2],j=!0,M=!0;if("length"in _&&y){var z=y(_,"length");z&&!z.configurable&&(j=!1),z&&!z.writable&&(M=!1)}return(j||M||!I)&&(f?v(_,"length",T,!0,!0):v(_,"length",T)),_}},90392(w,N,s){var h=s(92861).Buffer,v=s(15377);function f(y,m){this._block=h.alloc(y),this._finalSize=m,this._blockSize=y,this._len=0}f.prototype.update=function(y,m){y=v(y,m||"utf8");for(var g=this._block,S=this._blockSize,_=y.length,T=this._len,I=0;I<_;){for(var j=T%S,M=Math.min(_-I,S-j),z=0;z=this._finalSize&&(this._update(this._block),this._block.fill(0));var g=8*this._len;if(g<=4294967295)this._block.writeUInt32BE(g,this._blockSize-4);else{var S=(4294967295&g)>>>0,_=(g-S)/4294967296;this._block.writeUInt32BE(_,this._blockSize-8),this._block.writeUInt32BE(S,this._blockSize-4)}this._update(this._block);var T=this._hash();return y?T.toString(y):T},f.prototype._update=function(){throw new Error("_update must be implemented by subclass")},w.exports=f},62802(w,N,s){w.exports=function(v){var f=v.toLowerCase(),y=w.exports[f];if(!y)throw new Error(f+" is not supported (we accept pull requests)");return new y},w.exports.sha=s(27816),w.exports.sha1=s(63737),w.exports.sha224=s(26710),w.exports.sha256=s(24107),w.exports.sha384=s(32827),w.exports.sha512=s(82890)},27816(w,N,s){var h=s(56698),v=s(90392),f=s(92861).Buffer,y=[1518500249,1859775393,-1894007588,-899497514],m=new Array(80);function g(){this.init(),this._w=m,v.call(this,64,56)}function S(T){return T<<30|T>>>2}function _(T,I,j,M){return T===0?I&j|~I&M:T===2?I&j|I&M|j&M:I^j^M}h(g,v),g.prototype.init=function(){return this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},g.prototype._update=function(T){for(var I,j=this._w,M=0|this._a,z=0|this._b,K=0|this._c,Y=0|this._d,B=0|this._e,X=0;X<16;++X)j[X]=T.readInt32BE(4*X);for(;X<80;++X)j[X]=j[X-3]^j[X-8]^j[X-14]^j[X-16];for(var te=0;te<80;++te){var ie=~~(te/20),se=0|((I=M)<<5|I>>>27)+_(ie,z,K,Y)+B+j[te]+y[ie];B=Y,Y=K,K=S(z),z=M,M=se}this._a=M+this._a|0,this._b=z+this._b|0,this._c=K+this._c|0,this._d=Y+this._d|0,this._e=B+this._e|0},g.prototype._hash=function(){var T=f.allocUnsafe(20);return T.writeInt32BE(0|this._a,0),T.writeInt32BE(0|this._b,4),T.writeInt32BE(0|this._c,8),T.writeInt32BE(0|this._d,12),T.writeInt32BE(0|this._e,16),T},w.exports=g},63737(w,N,s){var h=s(56698),v=s(90392),f=s(92861).Buffer,y=[1518500249,1859775393,-1894007588,-899497514],m=new Array(80);function g(){this.init(),this._w=m,v.call(this,64,56)}function S(I){return I<<5|I>>>27}function _(I){return I<<30|I>>>2}function T(I,j,M,z){return I===0?j&M|~j&z:I===2?j&M|j&z|M&z:j^M^z}h(g,v),g.prototype.init=function(){return this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},g.prototype._update=function(I){for(var j,M=this._w,z=0|this._a,K=0|this._b,Y=0|this._c,B=0|this._d,X=0|this._e,te=0;te<16;++te)M[te]=I.readInt32BE(4*te);for(;te<80;++te)M[te]=(j=M[te-3]^M[te-8]^M[te-14]^M[te-16])<<1|j>>>31;for(var ie=0;ie<80;++ie){var se=~~(ie/20),Te=S(z)+T(se,K,Y,B)+X+M[ie]+y[se]|0;X=B,B=Y,Y=_(K),K=z,z=Te}this._a=z+this._a|0,this._b=K+this._b|0,this._c=Y+this._c|0,this._d=B+this._d|0,this._e=X+this._e|0},g.prototype._hash=function(){var I=f.allocUnsafe(20);return I.writeInt32BE(0|this._a,0),I.writeInt32BE(0|this._b,4),I.writeInt32BE(0|this._c,8),I.writeInt32BE(0|this._d,12),I.writeInt32BE(0|this._e,16),I},w.exports=g},26710(w,N,s){var h=s(56698),v=s(24107),f=s(90392),y=s(92861).Buffer,m=new Array(64);function g(){this.init(),this._w=m,f.call(this,64,56)}h(g,v),g.prototype.init=function(){return this._a=3238371032,this._b=914150663,this._c=812702999,this._d=4144912697,this._e=4290775857,this._f=1750603025,this._g=1694076839,this._h=3204075428,this},g.prototype._hash=function(){var S=y.allocUnsafe(28);return S.writeInt32BE(this._a,0),S.writeInt32BE(this._b,4),S.writeInt32BE(this._c,8),S.writeInt32BE(this._d,12),S.writeInt32BE(this._e,16),S.writeInt32BE(this._f,20),S.writeInt32BE(this._g,24),S},w.exports=g},24107(w,N,s){var h=s(56698),v=s(90392),f=s(92861).Buffer,y=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],m=new Array(64);function g(){this.init(),this._w=m,v.call(this,64,56)}function S(M,z,K){return K^M&(z^K)}function _(M,z,K){return M&z|K&(M|z)}function T(M){return(M>>>2|M<<30)^(M>>>13|M<<19)^(M>>>22|M<<10)}function I(M){return(M>>>6|M<<26)^(M>>>11|M<<21)^(M>>>25|M<<7)}function j(M){return(M>>>7|M<<25)^(M>>>18|M<<14)^M>>>3}h(g,v),g.prototype.init=function(){return this._a=1779033703,this._b=3144134277,this._c=1013904242,this._d=2773480762,this._e=1359893119,this._f=2600822924,this._g=528734635,this._h=1541459225,this},g.prototype._update=function(M){for(var z,K=this._w,Y=0|this._a,B=0|this._b,X=0|this._c,te=0|this._d,ie=0|this._e,se=0|this._f,Te=0|this._g,he=0|this._h,ge=0;ge<16;++ge)K[ge]=M.readInt32BE(4*ge);for(;ge<64;++ge)K[ge]=0|(((z=K[ge-2])>>>17|z<<15)^(z>>>19|z<<13)^z>>>10)+K[ge-7]+j(K[ge-15])+K[ge-16];for(var ke=0;ke<64;++ke){var Ve=he+I(ie)+S(ie,se,Te)+y[ke]+K[ke]|0,He=T(Y)+_(Y,B,X)|0;he=Te,Te=se,se=ie,ie=te+Ve|0,te=X,X=B,B=Y,Y=Ve+He|0}this._a=Y+this._a|0,this._b=B+this._b|0,this._c=X+this._c|0,this._d=te+this._d|0,this._e=ie+this._e|0,this._f=se+this._f|0,this._g=Te+this._g|0,this._h=he+this._h|0},g.prototype._hash=function(){var M=f.allocUnsafe(32);return M.writeInt32BE(this._a,0),M.writeInt32BE(this._b,4),M.writeInt32BE(this._c,8),M.writeInt32BE(this._d,12),M.writeInt32BE(this._e,16),M.writeInt32BE(this._f,20),M.writeInt32BE(this._g,24),M.writeInt32BE(this._h,28),M},w.exports=g},32827(w,N,s){var h=s(56698),v=s(82890),f=s(90392),y=s(92861).Buffer,m=new Array(160);function g(){this.init(),this._w=m,f.call(this,128,112)}h(g,v),g.prototype.init=function(){return this._ah=3418070365,this._bh=1654270250,this._ch=2438529370,this._dh=355462360,this._eh=1731405415,this._fh=2394180231,this._gh=3675008525,this._hh=1203062813,this._al=3238371032,this._bl=914150663,this._cl=812702999,this._dl=4144912697,this._el=4290775857,this._fl=1750603025,this._gl=1694076839,this._hl=3204075428,this},g.prototype._hash=function(){var S=y.allocUnsafe(48);function _(T,I,j){S.writeInt32BE(T,j),S.writeInt32BE(I,j+4)}return _(this._ah,this._al,0),_(this._bh,this._bl,8),_(this._ch,this._cl,16),_(this._dh,this._dl,24),_(this._eh,this._el,32),_(this._fh,this._fl,40),S},w.exports=g},82890(w,N,s){var h=s(56698),v=s(90392),f=s(92861).Buffer,y=[1116352408,3609767458,1899447441,602891725,3049323471,3964484399,3921009573,2173295548,961987163,4081628472,1508970993,3053834265,2453635748,2937671579,2870763221,3664609560,3624381080,2734883394,310598401,1164996542,607225278,1323610764,1426881987,3590304994,1925078388,4068182383,2162078206,991336113,2614888103,633803317,3248222580,3479774868,3835390401,2666613458,4022224774,944711139,264347078,2341262773,604807628,2007800933,770255983,1495990901,1249150122,1856431235,1555081692,3175218132,1996064986,2198950837,2554220882,3999719339,2821834349,766784016,2952996808,2566594879,3210313671,3203337956,3336571891,1034457026,3584528711,2466948901,113926993,3758326383,338241895,168717936,666307205,1188179964,773529912,1546045734,1294757372,1522805485,1396182291,2643833823,1695183700,2343527390,1986661051,1014477480,2177026350,1206759142,2456956037,344077627,2730485921,1290863460,2820302411,3158454273,3259730800,3505952657,3345764771,106217008,3516065817,3606008344,3600352804,1432725776,4094571909,1467031594,275423344,851169720,430227734,3100823752,506948616,1363258195,659060556,3750685593,883997877,3785050280,958139571,3318307427,1322822218,3812723403,1537002063,2003034995,1747873779,3602036899,1955562222,1575990012,2024104815,1125592928,2227730452,2716904306,2361852424,442776044,2428436474,593698344,2756734187,3733110249,3204031479,2999351573,3329325298,3815920427,3391569614,3928383900,3515267271,566280711,3940187606,3454069534,4118630271,4000239992,116418474,1914138554,174292421,2731055270,289380356,3203993006,460393269,320620315,685471733,587496836,852142971,1086792851,1017036298,365543100,1126000580,2618297676,1288033470,3409855158,1501505948,4234509866,1607167915,987167468,1816402316,1246189591],m=new Array(160);function g(){this.init(),this._w=m,v.call(this,128,112)}function S(B,X,te){return te^B&(X^te)}function _(B,X,te){return B&X|te&(B|X)}function T(B,X){return(B>>>28|X<<4)^(X>>>2|B<<30)^(X>>>7|B<<25)}function I(B,X){return(B>>>14|X<<18)^(B>>>18|X<<14)^(X>>>9|B<<23)}function j(B,X){return(B>>>1|X<<31)^(B>>>8|X<<24)^B>>>7}function M(B,X){return(B>>>1|X<<31)^(B>>>8|X<<24)^(B>>>7|X<<25)}function z(B,X){return(B>>>19|X<<13)^(X>>>29|B<<3)^B>>>6}function K(B,X){return(B>>>19|X<<13)^(X>>>29|B<<3)^(B>>>6|X<<26)}function Y(B,X){return B>>>0>>0?1:0}h(g,v),g.prototype.init=function(){return this._ah=1779033703,this._bh=3144134277,this._ch=1013904242,this._dh=2773480762,this._eh=1359893119,this._fh=2600822924,this._gh=528734635,this._hh=1541459225,this._al=4089235720,this._bl=2227873595,this._cl=4271175723,this._dl=1595750129,this._el=2917565137,this._fl=725511199,this._gl=4215389547,this._hl=327033209,this},g.prototype._update=function(B){for(var X=this._w,te=0|this._ah,ie=0|this._bh,se=0|this._ch,Te=0|this._dh,he=0|this._eh,ge=0|this._fh,ke=0|this._gh,Ve=0|this._hh,He=0|this._al,qe=0|this._bl,nt=0|this._cl,gt=0|this._dl,Re=0|this._el,u=0|this._fl,at=0|this._gl,Xe=0|this._hl,Se=0;Se<32;Se+=2)X[Se]=B.readInt32BE(4*Se),X[Se+1]=B.readInt32BE(4*Se+4);for(;Se<160;Se+=2){var De=X[Se-30],Ke=X[Se-30+1],ft=j(De,Ke),Nt=M(Ke,De),Wt=z(De=X[Se-4],Ke=X[Se-4+1]),H=K(Ke,De),we=X[Se-14],ct=X[Se-14+1],mt=X[Se-32],ur=X[Se-32+1],ar=Nt+ct|0,Tt=ft+we+Y(ar,Nt)|0;Tt=(Tt=Tt+Wt+Y(ar=ar+H|0,H)|0)+mt+Y(ar=ar+ur|0,ur)|0,X[Se]=Tt,X[Se+1]=ar}for(var Gt=0;Gt<160;Gt+=2){Tt=X[Gt],ar=X[Gt+1];var Sr=_(te,ie,se),kt=_(He,qe,nt),yt=T(te,He),Zt=T(He,te),pe=I(he,Re),U=I(Re,he),Z=y[Gt],ne=y[Gt+1],ye=S(he,ge,ke),Ee=S(Re,u,at),Fe=Xe+U|0,St=Ve+pe+Y(Fe,Xe)|0;St=(St=(St=St+ye+Y(Fe=Fe+Ee|0,Ee)|0)+Z+Y(Fe=Fe+ne|0,ne)|0)+Tt+Y(Fe=Fe+ar|0,ar)|0;var Bt=Zt+kt|0,Qe=yt+Sr+Y(Bt,Zt)|0;Ve=ke,Xe=at,ke=ge,at=u,ge=he,u=Re,he=Te+St+Y(Re=gt+Fe|0,gt)|0,Te=se,gt=nt,se=ie,nt=qe,ie=te,qe=He,te=St+Qe+Y(He=Fe+Bt|0,Fe)|0}this._al=this._al+He|0,this._bl=this._bl+qe|0,this._cl=this._cl+nt|0,this._dl=this._dl+gt|0,this._el=this._el+Re|0,this._fl=this._fl+u|0,this._gl=this._gl+at|0,this._hl=this._hl+Xe|0,this._ah=this._ah+te+Y(this._al,He)|0,this._bh=this._bh+ie+Y(this._bl,qe)|0,this._ch=this._ch+se+Y(this._cl,nt)|0,this._dh=this._dh+Te+Y(this._dl,gt)|0,this._eh=this._eh+he+Y(this._el,Re)|0,this._fh=this._fh+ge+Y(this._fl,u)|0,this._gh=this._gh+ke+Y(this._gl,at)|0,this._hh=this._hh+Ve+Y(this._hl,Xe)|0},g.prototype._hash=function(){var B=f.allocUnsafe(64);function X(te,ie,se){B.writeInt32BE(te,se),B.writeInt32BE(ie,se+4)}return X(this._ah,this._al,0),X(this._bh,this._bl,8),X(this._ch,this._cl,16),X(this._dh,this._dl,24),X(this._eh,this._el,32),X(this._fh,this._fl,40),X(this._gh,this._gl,48),X(this._hh,this._hl,56),B},w.exports=g},8068(w){var N=(()=>{var s=Object.defineProperty,h=Object.getOwnPropertyDescriptor,v=Object.getOwnPropertyNames,f=Object.getOwnPropertySymbols,y=Object.prototype.hasOwnProperty,m=Object.prototype.propertyIsEnumerable,g=(Y,B,X)=>B in Y?s(Y,B,{enumerable:!0,configurable:!0,writable:!0,value:X}):Y[B]=X,S=(Y,B)=>{for(var X in B||(B={}))y.call(B,X)&&g(Y,X,B[X]);if(f)for(var X of f(B))m.call(B,X)&&g(Y,X,B[X]);return Y},_=(Y,B,X)=>g(Y,typeof B!="symbol"?B+"":B,X),T={};((Y,B)=>{for(var X in B)s(Y,X,{get:B[X],enumerable:!0})})(T,{DEFAULT_OPTIONS:()=>j,DEFAULT_UUID_LENGTH:()=>I,default:()=>K});var I=6,j={dictionary:"alphanum",shuffle:!0,debug:!1,length:I,counter:0},M=class{constructor(B={}){_(this,"counter"),_(this,"debug"),_(this,"dict"),_(this,"version"),_(this,"dictIndex",0),_(this,"dictRange",[]),_(this,"lowerBound",0),_(this,"upperBound",0),_(this,"dictLength",0),_(this,"uuidLength"),_(this,"_digit_first_ascii",48),_(this,"_digit_last_ascii",58),_(this,"_alpha_lower_first_ascii",97),_(this,"_alpha_lower_last_ascii",123),_(this,"_hex_last_ascii",103),_(this,"_alpha_upper_first_ascii",65),_(this,"_alpha_upper_last_ascii",91),_(this,"_number_dict_ranges",{digits:[this._digit_first_ascii,this._digit_last_ascii]}),_(this,"_alpha_dict_ranges",{lowerCase:[this._alpha_lower_first_ascii,this._alpha_lower_last_ascii],upperCase:[this._alpha_upper_first_ascii,this._alpha_upper_last_ascii]}),_(this,"_alpha_lower_dict_ranges",{lowerCase:[this._alpha_lower_first_ascii,this._alpha_lower_last_ascii]}),_(this,"_alpha_upper_dict_ranges",{upperCase:[this._alpha_upper_first_ascii,this._alpha_upper_last_ascii]}),_(this,"_alphanum_dict_ranges",{digits:[this._digit_first_ascii,this._digit_last_ascii],lowerCase:[this._alpha_lower_first_ascii,this._alpha_lower_last_ascii],upperCase:[this._alpha_upper_first_ascii,this._alpha_upper_last_ascii]}),_(this,"_alphanum_lower_dict_ranges",{digits:[this._digit_first_ascii,this._digit_last_ascii],lowerCase:[this._alpha_lower_first_ascii,this._alpha_lower_last_ascii]}),_(this,"_alphanum_upper_dict_ranges",{digits:[this._digit_first_ascii,this._digit_last_ascii],upperCase:[this._alpha_upper_first_ascii,this._alpha_upper_last_ascii]}),_(this,"_hex_dict_ranges",{decDigits:[this._digit_first_ascii,this._digit_last_ascii],alphaDigits:[this._alpha_lower_first_ascii,this._hex_last_ascii]}),_(this,"_dict_ranges",{_number_dict_ranges:this._number_dict_ranges,_alpha_dict_ranges:this._alpha_dict_ranges,_alpha_lower_dict_ranges:this._alpha_lower_dict_ranges,_alpha_upper_dict_ranges:this._alpha_upper_dict_ranges,_alphanum_dict_ranges:this._alphanum_dict_ranges,_alphanum_lower_dict_ranges:this._alphanum_lower_dict_ranges,_alphanum_upper_dict_ranges:this._alphanum_upper_dict_ranges,_hex_dict_ranges:this._hex_dict_ranges}),_(this,"log",((...he)=>{const ge=[...he];ge[0]="[short-unique-id] ".concat(he[0]),this.debug!==!0||typeof console>"u"||console===null||console.log(...ge)})),_(this,"_normalizeDictionary",((he,ge)=>{let ke;if(he&&Array.isArray(he)&&he.length>1)ke=he;else{ke=[],this.dictIndex=0;const Ve="_".concat(he,"_dict_ranges"),He=this._dict_ranges[Ve];let qe=0;for(const[,gt]of Object.entries(He)){const[Re,u]=gt;qe+=Math.abs(u-Re)}ke=new Array(qe);let nt=0;for(const[,gt]of Object.entries(He)){this.dictRange=gt,this.lowerBound=this.dictRange[0],this.upperBound=this.dictRange[1];const Re=this.lowerBound<=this.upperBound,u=this.lowerBound,at=this.upperBound;if(Re)for(let Xe=u;Xeat;Xe--)ke[nt++]=String.fromCharCode(Xe),this.dictIndex=Xe}ke.length=nt}if(ge)for(let Ve=ke.length-1;Ve>0;Ve--){const He=Math.floor(Math.random()*(Ve+1));[ke[Ve],ke[He]]=[ke[He],ke[Ve]]}return ke})),_(this,"setDictionary",((he,ge)=>{this.dict=this._normalizeDictionary(he,ge),this.dictLength=this.dict.length,this.setCounter(0)})),_(this,"seq",(()=>this.sequentialUUID())),_(this,"sequentialUUID",(()=>{const he=this.dictLength,ge=this.dict;let ke=this.counter;const Ve=[];do{const qe=ke%he;ke=Math.trunc(ke/he),Ve.push(ge[qe])}while(ke!==0);const He=Ve.join("");return this.counter+=1,He})),_(this,"rnd",((he=this.uuidLength||I)=>this.randomUUID(he))),_(this,"randomUUID",((he=this.uuidLength||I)=>{if(he==null||he<1)throw new Error("Invalid UUID Length Provided");const ge=new Array(he),ke=this.dictLength,Ve=this.dict;for(let He=0;Hethis.formattedUUID(he,ge))),_(this,"formattedUUID",((he,ge)=>{const ke={$r:this.randomUUID,$s:this.sequentialUUID,$t:this.stamp};return he.replace(/\$[rs]\d{0,}|\$t0|\$t[1-9]\d{1,}/g,(Ve=>{const He=Ve.slice(0,2),qe=Number.parseInt(Ve.slice(2),10);return He==="$s"?ke[He]().padStart(qe,"0"):He==="$t"&&ge?ke[He](qe,ge):ke[He](qe)}))})),_(this,"availableUUIDs",((he=this.uuidLength)=>Number.parseFloat(([...new Set(this.dict)].length**he).toFixed(0)))),_(this,"_collisionCache",new Map),_(this,"approxMaxBeforeCollision",((he=this.availableUUIDs(this.uuidLength))=>{const ge=he,ke=this._collisionCache.get(ge);if(ke!==void 0)return ke;const Ve=Number.parseFloat(Math.sqrt(Math.PI/2*he).toFixed(20));return this._collisionCache.set(ge,Ve),Ve})),_(this,"collisionProbability",((he=this.availableUUIDs(this.uuidLength),ge=this.uuidLength)=>Number.parseFloat((this.approxMaxBeforeCollision(he)/this.availableUUIDs(ge)).toFixed(20)))),_(this,"uniqueness",((he=this.availableUUIDs(this.uuidLength))=>{const ge=Number.parseFloat((1-this.approxMaxBeforeCollision(he)/he).toFixed(20));return ge>1?1:ge<0?0:ge})),_(this,"getVersion",(()=>this.version)),_(this,"stamp",((he,ge)=>{const ke=Math.floor(+(ge||new Date)/1e3).toString(16);if(typeof he=="number"&&he===0)return ke;if(typeof he!="number"||he<10)throw new Error(["Param finalLength must be a number greater than or equal to 10,","or 0 if you want the raw hexadecimal timestamp"].join(` +`));const Ve=he-9,He=Math.round(Math.random()*(Ve>15?15:Ve)),qe=this.randomUUID(Ve);return"".concat(qe.substring(0,He)).concat(ke).concat(qe.substring(He)).concat(He.toString(16))})),_(this,"parseStamp",((he,ge)=>{if(ge&&!/t0|t[1-9]\d{1,}/.test(ge))throw new Error("Cannot extract date from a formated UUID with no timestamp in the format");const ke=ge?ge.replace(/\$[rs]\d{0,}|\$t0|\$t[1-9]\d{1,}/g,(He=>{const qe={$r:Re=>[...Array(Re)].map((()=>"r")).join(""),$s:Re=>[...Array(Re)].map((()=>"s")).join(""),$t:Re=>[...Array(Re)].map((()=>"t")).join("")},nt=He.slice(0,2),gt=Number.parseInt(He.slice(2),10);return qe[nt](gt)})).replace(/^(.*?)(t{8,})(.*)$/g,((He,qe,nt)=>he.substring(qe.length,qe.length+nt.length))):he;if(ke.length===8)return new Date(1e3*Number.parseInt(ke,16));if(ke.length<10)throw new Error("Stamp length invalid");const Ve=Number.parseInt(ke.substring(ke.length-1),16);return new Date(1e3*Number.parseInt(ke.substring(Ve,Ve+8),16))})),_(this,"setCounter",(he=>{this.counter=he})),_(this,"validate",((he,ge)=>{const ke=ge?this._normalizeDictionary(ge):this.dict;return he.split("").every((Ve=>ke.includes(Ve)))}));const X=S(S({},j),B);this.counter=0,this.debug=!1,this.dict=[],this.version="5.3.2";const{dictionary:te,shuffle:ie,length:se,counter:Te}=X;this.uuidLength=se,this.setDictionary(te,ie),this.setCounter(Te),this.debug=X.debug,this.log(this.dict),this.log("Generator instantiated with Dictionary Size ".concat(this.dictLength," and counter set to ").concat(this.counter)),this.log=this.log.bind(this),this.setDictionary=this.setDictionary.bind(this),this.setCounter=this.setCounter.bind(this),this.seq=this.seq.bind(this),this.sequentialUUID=this.sequentialUUID.bind(this),this.rnd=this.rnd.bind(this),this.randomUUID=this.randomUUID.bind(this),this.fmt=this.fmt.bind(this),this.formattedUUID=this.formattedUUID.bind(this),this.availableUUIDs=this.availableUUIDs.bind(this),this.approxMaxBeforeCollision=this.approxMaxBeforeCollision.bind(this),this.collisionProbability=this.collisionProbability.bind(this),this.uniqueness=this.uniqueness.bind(this),this.getVersion=this.getVersion.bind(this),this.stamp=this.stamp.bind(this),this.parseStamp=this.parseStamp.bind(this)}};_(M,"default",M);var z,K=M;return z=T,((Y,B,X,te)=>{if(B&&typeof B=="object"||typeof B=="function")for(let ie of v(B))y.call(Y,ie)||ie===X||s(Y,ie,{get:()=>B[ie],enumerable:!(te=h(B,ie))||te.enumerable});return Y})(s({},"__esModule",{value:!0}),z)})();w.exports=N.default,typeof window<"u"&&(N=N.default)},88310(w,N,s){w.exports=v;var h=s(37007).EventEmitter;function v(){h.call(this)}s(56698)(v,h),v.Readable=s(45412),v.Writable=s(16708),v.Duplex=s(25382),v.Transform=s(74610),v.PassThrough=s(63600),v.finished=s(86238),v.pipeline=s(57758),v.Stream=v,v.prototype.pipe=function(f,y){var m=this;function g(z){f.writable&&f.write(z)===!1&&m.pause&&m.pause()}function S(){m.readable&&m.resume&&m.resume()}m.on("data",g),f.on("drain",S),f._isStdio||y&&y.end===!1||(m.on("end",T),m.on("close",I));var _=!1;function T(){_||(_=!0,f.end())}function I(){_||(_=!0,typeof f.destroy=="function"&&f.destroy())}function j(z){if(M(),h.listenerCount(this,"error")===0)throw z}function M(){m.removeListener("data",g),f.removeListener("drain",S),m.removeListener("end",T),m.removeListener("close",I),m.removeListener("error",j),f.removeListener("error",j),m.removeListener("end",M),m.removeListener("close",M),f.removeListener("close",M)}return m.on("error",j),f.on("error",j),m.on("end",M),m.on("close",M),f.on("close",M),f.emit("pipe",m),f}},83141(w,N,s){var h=s(92861).Buffer,v=h.isEncoding||function(M){switch((M=""+M)&&M.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}};function f(M){var z;switch(this.encoding=(function(Y){var B=(function(te){if(!te)return"utf8";for(var ie;;)switch(te){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return te;default:if(ie)return;te=(""+te).toLowerCase(),ie=!0}})(Y);if(typeof B!="string"&&(h.isEncoding===v||!v(Y)))throw new Error("Unknown encoding: "+Y);return B||Y})(M),this.encoding){case"utf16le":this.text=g,this.end=S,z=4;break;case"utf8":this.fillLast=m,z=4;break;case"base64":this.text=_,this.end=T,z=3;break;default:return this.write=I,void(this.end=j)}this.lastNeed=0,this.lastTotal=0,this.lastChar=h.allocUnsafe(z)}function y(M){return M<=127?0:M>>5==6?2:M>>4==14?3:M>>3==30?4:M>>6==2?-1:-2}function m(M){var z=this.lastTotal-this.lastNeed,K=(function(B,X,te){if((192&X[0])!=128)return B.lastNeed=0,"�";if(B.lastNeed>1&&X.length>1){if((192&X[1])!=128)return B.lastNeed=1,"�";if(B.lastNeed>2&&X.length>2&&(192&X[2])!=128)return B.lastNeed=2,"�"}})(this,M);return K!==void 0?K:this.lastNeed<=M.length?(M.copy(this.lastChar,z,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(M.copy(this.lastChar,z,0,M.length),void(this.lastNeed-=M.length))}function g(M,z){if((M.length-z)%2==0){var K=M.toString("utf16le",z);if(K){var Y=K.charCodeAt(K.length-1);if(Y>=55296&&Y<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=M[M.length-2],this.lastChar[1]=M[M.length-1],K.slice(0,-1)}return K}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=M[M.length-1],M.toString("utf16le",z,M.length-1)}function S(M){var z=M&&M.length?this.write(M):"";if(this.lastNeed){var K=this.lastTotal-this.lastNeed;return z+this.lastChar.toString("utf16le",0,K)}return z}function _(M,z){var K=(M.length-z)%3;return K===0?M.toString("base64",z):(this.lastNeed=3-K,this.lastTotal=3,K===1?this.lastChar[0]=M[M.length-1]:(this.lastChar[0]=M[M.length-2],this.lastChar[1]=M[M.length-1]),M.toString("base64",z,M.length-K))}function T(M){var z=M&&M.length?this.write(M):"";return this.lastNeed?z+this.lastChar.toString("base64",0,3-this.lastNeed):z}function I(M){return M.toString(this.encoding)}function j(M){return M&&M.length?this.write(M):""}N.I=f,f.prototype.write=function(M){if(M.length===0)return"";var z,K;if(this.lastNeed){if((z=this.fillLast(M))===void 0)return"";K=this.lastNeed,this.lastNeed=0}else K=0;return K=0?(he>0&&(te.lastNeed=he-1),he):--Te=0?(he>0&&(te.lastNeed=he-2),he):--Te=0?(he>0&&(he===2?he=0:te.lastNeed=he-3),he):0))})(this,z,K);if(!this.lastNeed)return z.toString("utf8",K);this.lastTotal=Y;var B=z.length-(Y-this.lastNeed);return z.copy(this.lastChar,0,B),z.toString("utf8",K,B)},f.prototype.fillLast=function(M){if(this.lastNeed<=M.length)return M.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);M.copy(this.lastChar,this.lastTotal-this.lastNeed,0,M.length),this.lastNeed-=M.length}},15377(w,N,s){var h=s(92861).Buffer,v=s(64634),f=s(74372),y=ArrayBuffer.isView||function(T){try{return f(T),!0}catch{return!1}},m=typeof Uint8Array<"u",g=typeof ArrayBuffer<"u"&&typeof Uint8Array<"u",S=g&&(h.prototype instanceof Uint8Array||h.TYPED_ARRAY_SUPPORT);w.exports=function(T,I){if(T instanceof h)return T;if(typeof T=="string")return h.from(T,I);if(g&&y(T)){if(T.byteLength===0)return h.alloc(0);if(S){var j=h.from(T.buffer,T.byteOffset,T.byteLength);if(j.byteLength===T.byteLength)return j}var M=T instanceof Uint8Array?T:new Uint8Array(T.buffer,T.byteOffset,T.byteLength),z=h.from(M);if(z.length===T.byteLength)return z}if(m&&T instanceof Uint8Array)return h.from(T);var K=v(T);if(K)for(var Y=0;Y255||~~B!==B)throw new RangeError("Array items must be numbers in the range 0-255.")}if(K||h.isBuffer(T)&&T.constructor&&typeof T.constructor.isBuffer=="function"&&T.constructor.isBuffer(T))return h.from(T);throw new TypeError('The "data" argument must be a string, an Array, a Buffer, a Uint8Array, or a DataView.')}},16426(w){w.exports=function(){var N=document.getSelection();if(!N.rangeCount)return function(){};for(var s=document.activeElement,h=[],v=0;v=2&&(te=te.slice(2)):z(se)?te=ie[4]:se?Te&&(te=te.slice(2)):ge>=2&&z(X.protocol)&&(te=ie[4]),{protocol:se,slashes:Te||z(se),slashesCount:ge,rest:te}}function Y(B,X,te){if(B=(B=T(B)).replace(y,""),!(this instanceof Y))return new Y(B,X,te);var ie,se,Te,he,ge,ke,Ve=I.slice(),He=typeof X,qe=this,nt=0;for(He!=="object"&&He!=="string"&&(te=X,X=null),te&&typeof te!="function"&&(te=v.parse),ie=!(se=K(B||"",X=M(X))).protocol&&!se.slashes,qe.slashes=se.slashes||ie&&X.slashes,qe.protocol=se.protocol||X.protocol||"",B=se.rest,(se.protocol==="file:"&&(se.slashesCount!==2||_.test(B))||!se.slashes&&(se.protocol||se.slashesCount<2||!z(qe.protocol)))&&(Ve[3]=[/(.*)/,"pathname"]);nt"u"?s.g:globalThis,T=v(),I=y("String.prototype.slice"),j=Object.getPrototypeOf,M=y("Array.prototype.indexOf",!0)||function(Y,B){for(var X=0;X-1?B:B==="Object"&&(function(te){var ie=!1;return h(z,(function(se,Te){if(!ie)try{se(te),ie=I(Te,1)}catch{}})),ie})(Y)}return m?(function(te){var ie=!1;return h(z,(function(se,Te){if(!ie)try{"$"+se(te)===Te&&(ie=I(Te,1))}catch{}})),ie})(Y):null}},26657(w,N,s){var h=s(75208),v=function(g){return/<\/+[^>]+>/.test(g)},f=function(g){return/<[^>]+\/>/.test(g)};function y(m){return v(m)?"ClosingTag":(function(S){return(function(T){return/<[^>!]+>/.test(T)})(S)&&!v(S)&&!f(S)})(m)?"OpeningTag":f(m)?"SelfClosingTag":"Text"}w.exports=function(m){var g=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},S=g.indentor,_=g.textNodesOnSameLine,T=0,I=[];S=S||" ";var j=(function(z){return(function(Y){return Y.split(/(<\/?[^>]+>)/g).filter((function(B){return B.trim()!==""}))})(z).map((function(K){return{value:K,type:y(K)}}))})(m).map((function(M,z,K){var Y=M.value,B=M.type;B==="ClosingTag"&&T--;var X=h(S,T),te=X+Y;if(B==="OpeningTag"&&T++,_){var ie=K[z-1],se=K[z-2];B==="ClosingTag"&&ie.type==="Text"&&se.type==="OpeningTag"&&(te=""+X+se.value+ie.value+Y,I.push(z-2,z-1))}return te}));return I.forEach((function(M){return j[M]=null})),j.filter((function(M){return!!M})).join(` +`)}},31499(w){var N={"&":"&",'"':""","'":"'","<":"<",">":">"};w.exports=function(h){return h&&h.replace?h.replace(/([&"<>'])/g,(function(v,f){return N[f]})):h}},19123(w,N,s){var h=s(65606),v=s(31499),f=s(88310).Stream;function y(g,S,_){var T,I=(function(X,te){return new Array(te||0).join(X||"")})(S,_=_||0),j=g;if(typeof g=="object"&&(j=g[T=Object.keys(g)[0]])&&j._elem)return j._elem.name=T,j._elem.icount=_,j._elem.indent=S,j._elem.indents=I,j._elem.interrupt=j,j._elem;var M,z=[],K=[];function Y(B){Object.keys(B).forEach((function(X){z.push((function(ie,se){return ie+'="'+v(se)+'"'})(X,B[X]))}))}switch(typeof j){case"object":if(j===null)break;j._attr&&Y(j._attr),j._cdata&&K.push(("/g,"]]]]>")+"]]>"),j.forEach&&(M=!1,K.push(""),j.forEach((function(B){typeof B=="object"?Object.keys(B)[0]=="_attr"?Y(B._attr):K.push(y(B,S,_+1)):(K.pop(),M=!0,K.push(v(B)))})),M||K.push(""));break;default:K.push(v(j))}return{name:T,interrupt:!1,attributes:z,content:K,icount:_,indents:I,indent:S}}function m(g,S,_){if(typeof S!="object")return g(!1,S);var T=S.interrupt?1:S.content.length;function I(){for(;S.content.length;){var M=S.content.shift();if(M!==void 0){if(j(M))return;m(g,M)}}g(!1,(T>1?S.indents:"")+(S.name?"":"")+(S.indent&&!_?` +`:"")),_&&_()}function j(M){return!!M.interrupt&&(M.interrupt.append=g,M.interrupt.end=I,M.interrupt=!1,g(!0),!0)}if(g(!1,S.indents+(S.name?"<"+S.name:"")+(S.attributes.length?" "+S.attributes.join(" "):"")+(T?S.name?">":"":S.name?"/>":"")+(S.indent&&T>1?` +`:"")),!T)return g(!1,S.indent?` +`:"");j(S)||I()}w.exports=function(S,_){typeof _!="object"&&(_={indent:_});var T=_.stream?new f:null,I="",j=!1,M=_.indent?_.indent===!0?" ":_.indent:"",z=!0;function K(te){z?h.nextTick(te):te()}function Y(te,ie){if(ie!==void 0&&(I+=ie),te&&!j&&(T=T||new f,j=!0),te&&j){var se=I;K((function(){T.emit("data",se)})),I=""}}function B(te,ie){m(Y,y(te,M,M?1:0),ie)}function X(){if(T){var te=I;K((function(){T.emit("data",te),T.emit("end"),T.readable=!1,T.emit("close")}))}}return K((function(){z=!1})),_.declaration&&(function(ie){var se={version:"1.0",encoding:ie.encoding||"UTF-8"};ie.standalone&&(se.standalone=ie.standalone),B({"?xml":{_attr:se}}),I=I.replace("/>","?>")})(_.declaration),S&&S.forEach?S.forEach((function(te,ie){var se;ie+1===S.length&&(se=X),B(te,se)})):B(S,X),T?(T.readable=!0,T):I},w.exports.element=w.exports.Element=function(){var S={_elem:y(Array.prototype.slice.call(arguments)),push:function(_){if(!this.append)throw new Error("not assigned to a parent!");var T=this,I=this._elem.indent;m(this.append,y(_,I,this._elem.icount+(I?1:0)),(function(){T.append(!0)}))},close:function(_){_!==void 0&&this.push(_),this.end&&this.end()}};return S}},86215(w,N){var s,h,v;h=[],s=(function(){var f=function(j){return j&&"getComputedStyle"in window&&window.getComputedStyle(j)["scroll-behavior"]==="smooth"};if(typeof window>"u"||!("document"in window))return{};var y=function(j,M,z){var K;M=M||999,z||z===0||(z=9);var Y=function(he){K=he},B=function(){clearTimeout(K),Y(0)},X=function(he){return Math.max(0,j.getTopOf(he)-z)},te=function(he,ge,ke){if(B(),ge===0||ge&&ge<0||f(j.body))j.toY(he),ke&&ke();else{var Ve=j.getY(),He=Math.max(0,he)-Ve,qe=new Date().getTime();ge=ge||Math.min(Math.abs(He),M),(function nt(){Y(setTimeout((function(){var gt=Math.min(1,(new Date().getTime()-qe)/ge),Re=Math.max(0,Math.floor(Ve+He*(gt<.5?2*gt*gt:gt*(4-2*gt)-1)));j.toY(Re),gt<1&&j.getHeight()+Reqe?ie(he,ge,ke):He+z>gt?te(He-qe+z,ge,ke):ke&&ke()},Te=function(he,ge,ke,Ve){te(Math.max(0,j.getTopOf(he)-j.getHeight()/2+(ke||he.getBoundingClientRect().height/2)),ge,Ve)};return{setup:function(he,ge){return(he===0||he)&&(M=he),(ge===0||ge)&&(z=ge),{defaultDuration:M,edgeOffset:z}},to:ie,toY:te,intoView:se,center:Te,stop:B,moving:function(){return!!K},getY:j.getY,getTopOf:j.getTopOf}},m=document.documentElement,g=function(){return window.scrollY||m.scrollTop},S=y({body:document.scrollingElement||document.body,toY:function(j){window.scrollTo(0,j)},getY:g,getHeight:function(){return window.innerHeight||m.clientHeight},getTopOf:function(j){return j.getBoundingClientRect().top+g()-m.offsetTop}});if(S.createScroller=function(j,M,z){return y({body:j,toY:function(K){j.scrollTop=K},getY:function(){return j.scrollTop},getHeight:function(){return Math.min(j.clientHeight,window.innerHeight||m.clientHeight)},getTopOf:function(K){return K.offsetTop}},M,z)},"addEventListener"in window&&!window.noZensmooth&&!f(document.body)){var _="history"in window&&"pushState"in history,T=_&&"scrollRestoration"in history;T&&(history.scrollRestoration="auto"),window.addEventListener("load",(function(){T&&(setTimeout((function(){history.scrollRestoration="manual"}),9),window.addEventListener("popstate",(function(j){j.state&&"zenscrollY"in j.state&&S.toY(j.state.zenscrollY)}),!1)),window.location.hash&&setTimeout((function(){var j=S.setup().edgeOffset;if(j){var M=document.getElementById(window.location.href.split("#")[1]);if(M){var z=Math.max(0,S.getTopOf(M)-j),K=S.getY()-z;0<=K&&K<9&&window.scrollTo(0,z)}}}),9)}),!1);var I=new RegExp("(^|\\s)noZensmooth(\\s|$)");window.addEventListener("click",(function(j){for(var M=j.target;M&&M.tagName!=="A";)M=M.parentNode;if(!(!M||j.which!==1||j.shiftKey||j.metaKey||j.ctrlKey||j.altKey)){if(T){var z=history.state&&typeof history.state=="object"?history.state:{};z.zenscrollY=S.getY();try{history.replaceState(z,"")}catch{}}var K=M.getAttribute("href")||"";if(K.indexOf("#")===0&&!I.test(M.className)){var Y=0,B=document.getElementById(K.substring(1));if(K!=="#"){if(!B)return;Y=S.getTopOf(B)}j.preventDefault();var X=function(){window.location=K},te=S.setup().edgeOffset;te&&(Y=Math.max(0,Y-te),_&&(X=function(){history.pushState({},"",K)})),S.toY(Y,null,X)}}}),!1)}return S})(),(v=typeof s=="function"?s.apply(N,h):s)===void 0||(w.exports=v)},15340(){},79838(){},8628(w,N,s){w.exports=s(76343)},48675(w,N,s){w.exports=s(20850)},7666(w,N,s){var h=s(84851),v=s(953);function f(){var y;return w.exports=f=h?v(y=h).call(y):function(m){for(var g=1;g"u"?s.g:globalThis;w.exports=function(){for(var y=[],m=0;mM;)if((j=T[M++])!=j)return!0}else for(;I>M;M++)if((m||M in T)&&T[M]===S)return m||M||0;return!m&&-1}};w.exports={includes:y(!0),indexOf:y(!1)}},93427(w,N,s){var h=s(1907);w.exports=h([].slice)},45807(w,N,s){var h=s(1907),v=h({}.toString),f=h("".slice);w.exports=function(y){return f(v(y),8,-1)}},73948(w,N,s){var h=s(52623),v=s(62250),f=s(45807),y=s(76264)("toStringTag"),m=Object,g=f((function(){return arguments})())==="Arguments";w.exports=h?f:function(S){var _,T,I;return S===void 0?"Undefined":S===null?"Null":typeof(T=(function(j,M){try{return j[M]}catch{}})(_=m(S),y))=="string"?T:g?f(_):(I=f(_))==="Object"&&v(_.callee)?"Arguments":I}},19595(w,N,s){var h=s(49724),v=s(11042),f=s(13846),y=s(74284);w.exports=function(m,g,S){for(var _=v(g),T=y.f,I=f.f,j=0;j<_.length;j++){var M=_[j];h(m,M)||S&&h(S,M)||T(m,M,I(g,M))}}},25735(w,N,s){var h=s(76264)("match");w.exports=function(v){var f=/./;try{"/./"[v](f)}catch{try{return f[h]=!1,"/./"[v](f)}catch{}}return!1}},57382(w,N,s){var h=s(98828);w.exports=!h((function(){function v(){}return v.prototype.constructor=null,Object.getPrototypeOf(new v)!==v.prototype}))},59550(w){w.exports=function(N,s){return{value:N,done:s}}},61626(w,N,s){var h=s(39447),v=s(74284),f=s(75817);w.exports=h?function(y,m,g){return v.f(y,m,f(1,g))}:function(y,m,g){return y[m]=g,y}},75817(w){w.exports=function(N,s){return{enumerable:!(1&N),configurable:!(2&N),writable:!(4&N),value:s}}},68055(w,N,s){var h=s(61626);w.exports=function(v,f,y,m){return m&&m.enumerable?v[f]=y:h(v,f,y),v}},2532(w,N,s){var h=s(45951),v=Object.defineProperty;w.exports=function(f,y){try{v(h,f,{value:y,configurable:!0,writable:!0})}catch{h[f]=y}return y}},39447(w,N,s){var h=s(98828);w.exports=!h((function(){return Object.defineProperty({},1,{get:function(){return 7}})[1]!==7}))},49552(w,N,s){var h=s(45951),v=s(46285),f=h.document,y=v(f)&&v(f.createElement);w.exports=function(m){return y?f.createElement(m):{}}},19287(w){w.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},80376(w){w.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},96794(w,N,s){var h=s(45951).navigator,v=h&&h.userAgent;w.exports=v?String(v):""},20798(w,N,s){var h,v,f=s(45951),y=s(96794),m=f.process,g=f.Deno,S=m&&m.versions||g&&g.version,_=S&&S.v8;_&&(v=(h=_.split("."))[0]>0&&h[0]<4?1:+(h[0]+h[1])),!v&&y&&(!(h=y.match(/Edge\/(\d+)/))||h[1]>=74)&&(h=y.match(/Chrome\/(\d+)/))&&(v=+h[1]),w.exports=v},85762(w,N,s){var h=s(1907),v=Error,f=h("".replace),y=String(new v("zxcasd").stack),m=/\n\s*at [^:]*:[^\n]*/,g=m.test(y);w.exports=function(S,_){if(g&&typeof S=="string"&&!v.prepareStackTrace)for(;_--;)S=f(S,m,"");return S}},85884(w,N,s){var h=s(61626),v=s(85762),f=s(23888),y=Error.captureStackTrace;w.exports=function(m,g,S,_){f&&(y?y(m,g):h(m,"stack",v(S,_)))}},23888(w,N,s){var h=s(98828),v=s(75817);w.exports=!h((function(){var f=new Error("a");return!("stack"in f)||(Object.defineProperty(f,"stack",v(1,7)),f.stack!==7)}))},11091(w,N,s){var h=s(45951),v=s(76024),f=s(92361),y=s(62250),m=s(13846).f,g=s(7463),S=s(92046),_=s(28311),T=s(61626),I=s(49724);s(36128);var j=function(M){var z=function(K,Y,B){if(this instanceof z){switch(arguments.length){case 0:return new M;case 1:return new M(K);case 2:return new M(K,Y)}return new M(K,Y,B)}return v(M,this,arguments)};return z.prototype=M.prototype,z};w.exports=function(M,z){var K,Y,B,X,te,ie,se,Te,he,ge=M.target,ke=M.global,Ve=M.stat,He=M.proto,qe=ke?h:Ve?h[ge]:h[ge]&&h[ge].prototype,nt=ke?S:S[ge]||T(S,ge,{})[ge],gt=nt.prototype;for(X in z)Y=!(K=g(ke?X:ge+(Ve?".":"#")+X,M.forced))&&qe&&I(qe,X),ie=nt[X],Y&&(se=M.dontCallGetSet?(he=m(qe,X))&&he.value:qe[X]),te=Y&&se?se:z[X],(K||He||typeof ie!=typeof te)&&(Te=M.bind&&Y?_(te,h):M.wrap&&Y?j(te):He&&y(te)?f(te):te,(M.sham||te&&te.sham||ie&&ie.sham)&&T(Te,"sham",!0),T(nt,X,Te),He&&(I(S,B=ge+"Prototype")||T(S,B,{}),T(S[B],X,te),M.real&>&&(K||!gt[X])&&T(gt,X,te)))}},98828(w){w.exports=function(N){try{return!!N()}catch{return!0}}},76024(w,N,s){var h=s(41505),v=Function.prototype,f=v.apply,y=v.call;w.exports=typeof Reflect=="object"&&Reflect.apply||(h?y.bind(f):function(){return y.apply(f,arguments)})},28311(w,N,s){var h=s(92361),v=s(82159),f=s(41505),y=h(h.bind);w.exports=function(m,g){return v(m),g===void 0?m:f?y(m,g):function(){return m.apply(g,arguments)}}},41505(w,N,s){var h=s(98828);w.exports=!h((function(){var v=(function(){}).bind();return typeof v!="function"||v.hasOwnProperty("prototype")}))},44673(w,N,s){var h=s(1907),v=s(82159),f=s(46285),y=s(49724),m=s(93427),g=s(41505),S=Function,_=h([].concat),T=h([].join),I={};w.exports=g?S.bind:function(M){var z=v(this),K=z.prototype,Y=m(arguments,1),B=function(){var te=_(Y,m(arguments));return this instanceof B?(function(ie,se,Te){if(!y(I,se)){for(var he=[],ge=0;geie;ie++)if((Te=u(K[ie]))&&S(z,Te))return Te;return new M(!1)}X=_(K,te)}for(he=He?K.next:X.next;!(ge=v(he,X)).done;){try{Te=u(ge.value)}catch(at){I(X,"throw",at)}if(typeof Te=="object"&&Te&&S(z,Te))return Te}return new M(!1)}},40154(w,N,s){var h=s(13930),v=s(36624),f=s(29367);w.exports=function(y,m,g){var S,_;v(y);try{if(!(S=f(y,"return"))){if(m==="throw")throw g;return g}S=h(S,y)}catch(T){_=!0,S=T}if(m==="throw")throw g;if(_)throw S;return v(S),g}},47181(w,N,s){var h=s(95116).IteratorPrototype,v=s(58075),f=s(75817),y=s(14840),m=s(93742),g=function(){return this};w.exports=function(S,_,T,I){var j=_+" Iterator";return S.prototype=v(h,{next:f(+!I,T)}),y(S,j,!1,!0),m[j]=g,S}},60183(w,N,s){var h=s(11091),v=s(13930),f=s(7376),y=s(36833),m=s(62250),g=s(47181),S=s(15972),_=s(79192),T=s(14840),I=s(61626),j=s(68055),M=s(76264),z=s(93742),K=s(95116),Y=y.PROPER,B=y.CONFIGURABLE,X=K.IteratorPrototype,te=K.BUGGY_SAFARI_ITERATORS,ie=M("iterator"),se="keys",Te="values",he="entries",ge=function(){return this};w.exports=function(ke,Ve,He,qe,nt,gt,Re){g(He,Ve,qe);var u,at,Xe,Se=function(we){if(we===nt&&Wt)return Wt;if(!te&&we&&we in ft)return ft[we];switch(we){case se:return function(){return new He(this,we)};case Te:return function(){return new He(this,we)};case he:return function(){return new He(this,we)}}return function(){return new He(this)}},De=Ve+" Iterator",Ke=!1,ft=ke.prototype,Nt=ft[ie]||ft["@@iterator"]||nt&&ft[nt],Wt=!te&&Nt||Se(nt),H=Ve==="Array"&&ft.entries||Nt;if(H&&(u=S(H.call(new ke)))!==Object.prototype&&u.next&&(f||S(u)===X||(_?_(u,X):m(u[ie])||j(u,ie,ge)),T(u,De,!0,!0),f&&(z[De]=ge)),Y&&nt===Te&&Nt&&Nt.name!==Te&&(!f&&B?I(ft,"name",Te):(Ke=!0,Wt=function(){return v(Nt,this)})),nt)if(at={values:Se(Te),keys:gt?Wt:Se(se),entries:Se(he)},Re)for(Xe in at)(te||Ke||!(Xe in ft))&&j(ft,Xe,at[Xe]);else h({target:Ve,proto:!0,forced:te||Ke},at);return f&&!Re||ft[ie]===Wt||j(ft,ie,Wt,{name:nt}),z[Ve]=Wt,at}},95116(w,N,s){var h,v,f,y=s(98828),m=s(62250),g=s(46285),S=s(58075),_=s(15972),T=s(68055),I=s(76264),j=s(7376),M=I("iterator"),z=!1;[].keys&&("next"in(f=[].keys())?(v=_(_(f)))!==Object.prototype&&(h=v):z=!0),!g(h)||y((function(){var K={};return h[M].call(K)!==K}))?h={}:j&&(h=S(h)),m(h[M])||T(h,M,(function(){return this})),w.exports={IteratorPrototype:h,BUGGY_SAFARI_ITERATORS:z}},93742(w){w.exports={}},20575(w,N,s){var h=s(3121);w.exports=function(v){return h(v.length)}},41176(w){var N=Math.ceil,s=Math.floor;w.exports=Math.trunc||function(v){var f=+v;return(f>0?s:N)(f)}},32096(w,N,s){var h=s(90160);w.exports=function(v,f){return v===void 0?arguments.length<2?"":f:h(v)}},12074(w,N,s){var h=s(72087),v=TypeError;w.exports=function(f){if(h(f))throw new v("The method doesn't accept regular expressions");return f}},29538(w,N,s){var h=s(39447),v=s(1907),f=s(13930),y=s(98828),m=s(2875),g=s(87170),S=s(22574),_=s(39298),T=s(16946),I=Object.assign,j=Object.defineProperty,M=v([].concat);w.exports=!I||y((function(){if(h&&I({b:1},I(j({},"a",{enumerable:!0,get:function(){j(this,"b",{value:3,enumerable:!1})}}),{b:2})).b!==1)return!0;var z={},K={},Y=Symbol("assign detection"),B="abcdefghijklmnopqrst";return z[Y]=7,B.split("").forEach((function(X){K[X]=X})),I({},z)[Y]!==7||m(I({},K)).join("")!==B}))?function(K,Y){for(var B=_(K),X=arguments.length,te=1,ie=g.f,se=S.f;X>te;)for(var Te,he=T(arguments[te++]),ge=ie?M(m(he),ie(he)):m(he),ke=ge.length,Ve=0;ke>Ve;)Te=ge[Ve++],h&&!f(se,he,Te)||(B[Te]=he[Te]);return B}:I},58075(w,N,s){var h,v=s(36624),f=s(42220),y=s(80376),m=s(38530),g=s(62416),S=s(49552),_=s(92522),T="prototype",I="script",j=_("IE_PROTO"),M=function(){},z=function(B){return"<"+I+">"+B+""},K=function(B){B.write(z("")),B.close();var X=B.parentWindow.Object;return B=null,X},Y=function(){try{h=new ActiveXObject("htmlfile")}catch{}var B,X,te;Y=typeof document<"u"?document.domain&&h?K(h):(X=S("iframe"),te="java"+I+":",X.style.display="none",g.appendChild(X),X.src=String(te),(B=X.contentWindow.document).open(),B.write(z("document.F=Object")),B.close(),B.F):K(h);for(var ie=y.length;ie--;)delete Y[T][y[ie]];return Y()};m[j]=!0,w.exports=Object.create||function(X,te){var ie;return X!==null?(M[T]=v(X),ie=new M,M[T]=null,ie[j]=X):ie=Y(),te===void 0?ie:f.f(ie,te)}},42220(w,N,s){var h=s(39447),v=s(58661),f=s(74284),y=s(36624),m=s(4993),g=s(2875);N.f=h&&!v?Object.defineProperties:function(_,T){y(_);for(var I,j=m(T),M=g(T),z=M.length,K=0;z>K;)f.f(_,I=M[K++],j[I]);return _}},74284(w,N,s){var h=s(39447),v=s(73648),f=s(58661),y=s(36624),m=s(70470),g=TypeError,S=Object.defineProperty,_=Object.getOwnPropertyDescriptor,T="enumerable",I="configurable",j="writable";N.f=h?f?function(z,K,Y){if(y(z),K=m(K),y(Y),typeof z=="function"&&K==="prototype"&&"value"in Y&&j in Y&&!Y[j]){var B=_(z,K);B&&B[j]&&(z[K]=Y.value,Y={configurable:I in Y?Y[I]:B[I],enumerable:T in Y?Y[T]:B[T],writable:!1})}return S(z,K,Y)}:S:function(z,K,Y){if(y(z),K=m(K),y(Y),v)try{return S(z,K,Y)}catch{}if("get"in Y||"set"in Y)throw new g("Accessors not supported");return"value"in Y&&(z[K]=Y.value),z}},13846(w,N,s){var h=s(39447),v=s(13930),f=s(22574),y=s(75817),m=s(4993),g=s(70470),S=s(49724),_=s(73648),T=Object.getOwnPropertyDescriptor;N.f=h?T:function(j,M){if(j=m(j),M=g(M),_)try{return T(j,M)}catch{}if(S(j,M))return y(!v(f.f,j,M),j[M])}},24443(w,N,s){var h=s(23045),v=s(80376).concat("length","prototype");N.f=Object.getOwnPropertyNames||function(y){return h(y,v)}},87170(w,N){N.f=Object.getOwnPropertySymbols},15972(w,N,s){var h=s(49724),v=s(62250),f=s(39298),y=s(92522),m=s(57382),g=y("IE_PROTO"),S=Object,_=S.prototype;w.exports=m?S.getPrototypeOf:function(T){var I=f(T);if(h(I,g))return I[g];var j=I.constructor;return v(j)&&I instanceof j?j.prototype:I instanceof S?_:null}},88280(w,N,s){var h=s(1907);w.exports=h({}.isPrototypeOf)},23045(w,N,s){var h=s(1907),v=s(49724),f=s(4993),y=s(74436).indexOf,m=s(38530),g=h([].push);w.exports=function(S,_){var T,I=f(S),j=0,M=[];for(T in I)!v(m,T)&&v(I,T)&&g(M,T);for(;_.length>j;)v(I,T=_[j++])&&(~y(M,T)||g(M,T));return M}},2875(w,N,s){var h=s(23045),v=s(80376);w.exports=Object.keys||function(y){return h(y,v)}},22574(w,N){var s={}.propertyIsEnumerable,h=Object.getOwnPropertyDescriptor,v=h&&!s.call({1:2},1);N.f=v?function(y){var m=h(this,y);return!!m&&m.enumerable}:s},79192(w,N,s){var h=s(51871),v=s(46285),f=s(74239),y=s(10043);w.exports=Object.setPrototypeOf||("__proto__"in{}?(function(){var m,g=!1,S={};try{(m=h(Object.prototype,"__proto__","set"))(S,[]),g=S instanceof Array}catch{}return function(T,I){return f(T),y(I),v(T)&&(g?m(T,I):T.__proto__=I),T}})():void 0)},54878(w,N,s){var h=s(52623),v=s(73948);w.exports=h?{}.toString:function(){return"[object "+v(this)+"]"}},60581(w,N,s){var h=s(13930),v=s(62250),f=s(46285),y=TypeError;w.exports=function(m,g){var S,_;if(g==="string"&&v(S=m.toString)&&!f(_=h(S,m))||v(S=m.valueOf)&&!f(_=h(S,m))||g!=="string"&&v(S=m.toString)&&!f(_=h(S,m)))return _;throw new y("Can't convert object to primitive value")}},11042(w,N,s){var h=s(85582),v=s(1907),f=s(24443),y=s(87170),m=s(36624),g=v([].concat);w.exports=h("Reflect","ownKeys")||function(_){var T=f.f(m(_)),I=y.f;return I?g(T,I(_)):T}},92046(w){w.exports={}},54829(w,N,s){var h=s(74284).f;w.exports=function(v,f,y){y in v||h(v,y,{configurable:!0,get:function(){return f[y]},set:function(m){f[y]=m}})}},74239(w,N,s){var h=s(87136),v=TypeError;w.exports=function(f){if(h(f))throw new v("Can't call method on "+f);return f}},14840(w,N,s){var h=s(52623),v=s(74284).f,f=s(61626),y=s(49724),m=s(54878),g=s(76264)("toStringTag");w.exports=function(S,_,T,I){var j=T?S:S&&S.prototype;j&&(y(j,g)||v(j,g,{configurable:!0,value:_}),I&&!h&&f(j,"toString",m))}},92522(w,N,s){var h=s(85816),v=s(6499),f=h("keys");w.exports=function(y){return f[y]||(f[y]=v(y))}},36128(w,N,s){var h=s(7376),v=s(45951),f=s(2532),y="__core-js_shared__",m=w.exports=v[y]||f(y,{});(m.versions||(m.versions=[])).push({version:"3.40.0",mode:h?"pure":"global",copyright:"© 2014-2025 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.40.0/LICENSE",source:"https://github.com/zloirock/core-js"})},85816(w,N,s){var h=s(36128);w.exports=function(v,f){return h[v]||(h[v]=f||{})}},11470(w,N,s){var h=s(1907),v=s(65482),f=s(90160),y=s(74239),m=h("".charAt),g=h("".charCodeAt),S=h("".slice),_=function(T){return function(I,j){var M,z,K=f(y(I)),Y=v(j),B=K.length;return Y<0||Y>=B?T?"":void 0:(M=g(K,Y))<55296||M>56319||Y+1===B||(z=g(K,Y+1))<56320||z>57343?T?m(K,Y):M:T?S(K,Y,Y+2):z-56320+(M-55296<<10)+65536}};w.exports={codeAt:_(!1),charAt:_(!0)}},19846(w,N,s){var h=s(20798),v=s(98828),f=s(45951).String;w.exports=!!Object.getOwnPropertySymbols&&!v((function(){var y=Symbol("symbol detection");return!f(y)||!(Object(y)instanceof Symbol)||!Symbol.sham&&h&&h<41}))},34849(w,N,s){var h=s(65482),v=Math.max,f=Math.min;w.exports=function(y,m){var g=h(y);return g<0?v(g+m,0):f(g,m)}},4993(w,N,s){var h=s(16946),v=s(74239);w.exports=function(f){return h(v(f))}},65482(w,N,s){var h=s(41176);w.exports=function(v){var f=+v;return f!=f||f===0?0:h(f)}},3121(w,N,s){var h=s(65482),v=Math.min;w.exports=function(f){var y=h(f);return y>0?v(y,9007199254740991):0}},39298(w,N,s){var h=s(74239),v=Object;w.exports=function(f){return v(h(f))}},46028(w,N,s){var h=s(13930),v=s(46285),f=s(25594),y=s(29367),m=s(60581),g=s(76264),S=TypeError,_=g("toPrimitive");w.exports=function(T,I){if(!v(T)||f(T))return T;var j,M=y(T,_);if(M){if(I===void 0&&(I="default"),j=h(M,T,I),!v(j)||f(j))return j;throw new S("Can't convert object to primitive value")}return I===void 0&&(I="number"),m(T,I)}},70470(w,N,s){var h=s(46028),v=s(25594);w.exports=function(f){var y=h(f,"string");return v(y)?y:y+""}},52623(w,N,s){var h={};h[s(76264)("toStringTag")]="z",w.exports=String(h)==="[object z]"},90160(w,N,s){var h=s(73948),v=String;w.exports=function(f){if(h(f)==="Symbol")throw new TypeError("Cannot convert a Symbol value to a string");return v(f)}},4640(w){var N=String;w.exports=function(s){try{return N(s)}catch{return"Object"}}},6499(w,N,s){var h=s(1907),v=0,f=Math.random(),y=h(1 .toString);w.exports=function(m){return"Symbol("+(m===void 0?"":m)+")_"+y(++v+f,36)}},51175(w,N,s){var h=s(19846);w.exports=h&&!Symbol.sham&&typeof Symbol.iterator=="symbol"},58661(w,N,s){var h=s(39447),v=s(98828);w.exports=h&&v((function(){return Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype!==42}))},40551(w,N,s){var h=s(45951),v=s(62250),f=h.WeakMap;w.exports=v(f)&&/native code/.test(String(f))},76264(w,N,s){var h=s(45951),v=s(85816),f=s(49724),y=s(6499),m=s(19846),g=s(51175),S=h.Symbol,_=v("wks"),T=g?S.for||S:S&&S.withoutSetter||y;w.exports=function(I){return f(_,I)||(_[I]=m&&f(S,I)?S[I]:T("Symbol."+I)),_[I]}},19358(w,N,s){var h=s(85582),v=s(49724),f=s(61626),y=s(88280),m=s(79192),g=s(19595),S=s(54829),_=s(34084),T=s(32096),I=s(39259),j=s(85884),M=s(39447),z=s(7376);w.exports=function(K,Y,B,X){var te="stackTraceLimit",ie=X?2:1,se=K.split("."),Te=se[se.length-1],he=h.apply(null,se);if(he){var ge=he.prototype;if(!z&&v(ge,"cause")&&delete ge.cause,!B)return he;var ke=h("Error"),Ve=Y((function(He,qe){var nt=T(X?qe:He,void 0),gt=X?new he(He):new he;return nt!==void 0&&f(gt,"message",nt),j(gt,Ve,gt.stack,2),this&&y(ge,this)&&_(gt,this,Ve),arguments.length>ie&&I(gt,arguments[ie]),gt}));if(Ve.prototype=ge,Te!=="Error"?m?m(Ve,ke):g(Ve,ke,{name:!0}):M&&te in he&&(S(Ve,he,te),S(Ve,he,"prepareStackTrace")),g(Ve,he),!z)try{ge.name!==Te&&f(ge,"name",Te),ge.constructor=Ve}catch{}return Ve}}},36371(w,N,s){var h=s(11091),v=s(85582),f=s(76024),y=s(98828),m=s(19358),g="AggregateError",S=v(g),_=!y((function(){return S([1]).errors[0]!==1}))&&y((function(){return S([1],g,{cause:7}).cause!==7}));h({global:!0,constructor:!0,arity:2,forced:_},{AggregateError:m(g,(function(T){return function(j,M){return f(T,this,arguments)}}),_,!0)})},82048(w,N,s){var h=s(11091),v=s(88280),f=s(15972),y=s(79192),m=s(19595),g=s(58075),S=s(61626),_=s(75817),T=s(39259),I=s(85884),j=s(24823),M=s(32096),z=s(76264)("toStringTag"),K=Error,Y=[].push,B=function(ie,se){var Te,he=v(X,this);y?Te=y(new K,he?f(this):X):(Te=he?this:g(X),S(Te,z,"Error")),se!==void 0&&S(Te,"message",M(se)),I(Te,B,Te.stack,1),arguments.length>2&&T(Te,arguments[2]);var ge=[];return j(ie,Y,{that:ge}),S(Te,"errors",ge),Te};y?y(B,K):m(B,K,{name:!0});var X=B.prototype=g(K.prototype,{constructor:_(1,B),message:_(1,""),name:_(1,"AggregateError")});h({global:!0,constructor:!0,arity:2},{AggregateError:B})},64502(w,N,s){s(82048)},19748(w,N,s){var h=s(11091),v=s(74436).includes,f=s(98828),y=s(42156);h({target:"Array",proto:!0,forced:f((function(){return!Array(1).includes()}))},{includes:function(g){return v(this,g,arguments.length>1?arguments[1]:void 0)}}),y("includes")},99363(w,N,s){var h=s(4993),v=s(42156),f=s(93742),y=s(64932),m=s(74284).f,g=s(60183),S=s(59550),_=s(7376),T=s(39447),I="Array Iterator",j=y.set,M=y.getterFor(I);w.exports=g(Array,"Array",(function(K,Y){j(this,{type:I,target:h(K),index:0,kind:Y})}),(function(){var K=M(this),Y=K.target,B=K.index++;if(!Y||B>=Y.length)return K.target=null,S(void 0,!0);switch(K.kind){case"keys":return S(B,!1);case"values":return S(Y[B],!1)}return S([B,Y[B]],!1)}),"values");var z=f.Arguments=f.Array;if(v("keys"),v("values"),v("entries"),!_&&T&&z.name!=="values")try{m(z,"name",{value:"values"})}catch{}},96605(w,N,s){var h=s(11091),v=s(45951),f=s(76024),y=s(19358),m="WebAssembly",g=v[m],S=new Error("e",{cause:7}).cause!==7,_=function(I,j){var M={};M[I]=y(I,j,S),h({global:!0,constructor:!0,arity:1,forced:S},M)},T=function(I,j){if(g&&g[I]){var M={};M[I]=y(m+"."+I,j,S),h({target:m,stat:!0,constructor:!0,arity:1,forced:S},M)}};_("Error",(function(I){return function(M){return f(I,this,arguments)}})),_("EvalError",(function(I){return function(M){return f(I,this,arguments)}})),_("RangeError",(function(I){return function(M){return f(I,this,arguments)}})),_("ReferenceError",(function(I){return function(M){return f(I,this,arguments)}})),_("SyntaxError",(function(I){return function(M){return f(I,this,arguments)}})),_("TypeError",(function(I){return function(M){return f(I,this,arguments)}})),_("URIError",(function(I){return function(M){return f(I,this,arguments)}})),T("CompileError",(function(I){return function(M){return f(I,this,arguments)}})),T("LinkError",(function(I){return function(M){return f(I,this,arguments)}})),T("RuntimeError",(function(I){return function(M){return f(I,this,arguments)}}))},79307(w,N,s){var h=s(11091),v=s(44673);h({target:"Function",proto:!0,forced:Function.bind!==v},{bind:v})},71340(w,N,s){var h=s(11091),v=s(29538);h({target:"Object",stat:!0,arity:2,forced:Object.assign!==v},{assign:v})},19770(w,N,s){var h=s(11091),v=s(1907),f=s(12074),y=s(74239),m=s(90160),g=s(25735),S=v("".indexOf);h({target:"String",proto:!0,forced:!g("includes")},{includes:function(T){return!!~S(m(y(this)),m(f(T)),arguments.length>1?arguments[1]:void 0)}})},7057(w,N,s){var h=s(11470).charAt,v=s(90160),f=s(64932),y=s(60183),m=s(59550),g="String Iterator",S=f.set,_=f.getterFor(g);y(String,"String",(function(T){S(this,{type:g,string:v(T),index:0})}),(function(){var I,j=_(this),M=j.string,z=j.index;return z>=M.length?m(void 0,!0):(I=h(M,z),j.index+=I.length,m(I,!1))}))},91599(w,N,s){s(64502)},12560(w,N,s){s(99363);var h=s(19287),v=s(45951),f=s(14840),y=s(93742);for(var m in h)f(v[m],m),y[m]=y.Array},694(w,N,s){s(91599);var h=s(37257);s(12560),w.exports=h},19709(w,N,s){var h=s(23034);w.exports=h},76343(w,N,s){var h=s(36880);w.exports=h},40975(w,N,s){var h=s(9748);w.exports=h}},hs={};function Be(w){var N=hs[w];if(N!==void 0)return N.exports;var s=hs[w]={id:w,loaded:!1,exports:{}};return eE[w].call(s.exports,s,s.exports,Be),s.loaded=!0,s.exports}Be.n=w=>{var N=w&&w.__esModule?()=>w.default:()=>w;return Be.d(N,{a:N}),N},Be.d=(w,N)=>{for(var s in N)Be.o(N,s)&&!Be.o(w,s)&&Object.defineProperty(w,s,{enumerable:!0,get:N[s]})},Be.g=(function(){if(typeof globalThis=="object")return globalThis;try{return this||new Function("return this")()}catch{if(typeof window=="object")return window}})(),Be.o=(w,N)=>Object.prototype.hasOwnProperty.call(w,N),Be.r=w=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(w,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(w,"__esModule",{value:!0})},Be.nmd=w=>(w.paths=[],w.children||(w.children=[]),w);var Cd={};(()=>{Be.d(Cd,{default:()=>bU});var w={};Be.r(w),Be.d(w,{CLEAR:()=>pe,CLEAR_BY:()=>U,NEW_AUTH_ERR:()=>Zt,NEW_SPEC_ERR:()=>kt,NEW_SPEC_ERR_BATCH:()=>yt,NEW_THROWN_ERR:()=>Gt,NEW_THROWN_ERR_BATCH:()=>Sr,clear:()=>St,clearBy:()=>Bt,newAuthErr:()=>Fe,newSpecErr:()=>ye,newSpecErrBatch:()=>Ee,newThrownErr:()=>Z,newThrownErrBatch:()=>ne});var N={};Be.r(N),Be.d(N,{AUTHORIZE:()=>an,AUTHORIZE_OAUTH2:()=>rc,CONFIGURE_AUTH:()=>Zs,LOGOUT:()=>tc,RESTORE_AUTHORIZATION:()=>nc,SHOW_AUTH_POPUP:()=>vs,authPopup:()=>rl,authorize:()=>Ku,authorizeAccessCodeWithBasicAuthentication:()=>el,authorizeAccessCodeWithFormParams:()=>ac,authorizeApplication:()=>jo,authorizeOauth2:()=>yi,authorizeOauth2WithPersistOption:()=>bs,authorizePassword:()=>ws,authorizeRequest:()=>Es,authorizeWithPersistOption:()=>$c,configureAuth:()=>vi,logout:()=>$i,logoutWithPersistOption:()=>Co,persistAuthorizationIfNeeded:()=>tl,preAuthorizeImplicit:()=>gi,restoreAuthorization:()=>bn,showDefinitions:()=>Hu});var s={};Be.r(s),Be.d(s,{authorized:()=>zc,definitionsForRequirements:()=>ic,definitionsToAuthorize:()=>Uc,getConfigs:()=>uo,getDefinitionsByNames:()=>Gu,isAuthorized:()=>rh,selectAuthPath:()=>al,shownDefinitions:()=>wi});var h={};Be.r(h),Be.d(h,{TOGGLE_CONFIGS:()=>Bn,UPDATE_CONFIGS:()=>aa,downloadConfig:()=>n0,getConfigByUrl:()=>a0,loaded:()=>po,toggle:()=>Ya,update:()=>Ga});var v={};Be.r(v),Be.d(v,{get:()=>Pd});var f={};Be.r(f),Be.d(f,{transform:()=>Sl});var y={};Be.r(y),Be.d(y,{transform:()=>xl});var m={};Be.r(m),Be.d(m,{allErrors:()=>hc,lastError:()=>Ko});var g={};Be.r(g),Be.d(g,{SHOW:()=>Cs,UPDATE_FILTER:()=>Io,UPDATE_LAYOUT:()=>iu,UPDATE_MODE:()=>Mo,changeMode:()=>Zm,show:()=>dc,updateFilter:()=>js,updateLayout:()=>Yi});var S={};Be.r(S),Be.d(S,{current:()=>Dd,currentFilter:()=>Go,isShown:()=>Ro,showSummary:()=>Oi,whatMode:()=>Un});var _={};Be.r(_),Be.d(_,{taggedOperations:()=>eg});var T={};Be.r(T),Be.d(T,{getActiveLanguage:()=>hh,getDefaultExpanded:()=>dh,getGenerators:()=>fc,getSnippetGenerators:()=>ng});var I={};Be.r(I),Be.d(I,{JsonSchemaArrayItemFile:()=>wh,JsonSchemaArrayItemText:()=>mu,JsonSchemaForm:()=>Hd,JsonSchema_array:()=>ii,JsonSchema_boolean:()=>Kd,JsonSchema_object:()=>sg,JsonSchema_string:()=>Xi});var j={};Be.r(j),Be.d(j,{allowTryItOutFor:()=>Op,basePath:()=>Tg,canExecuteScheme:()=>Rs,consumes:()=>Ph,consumesOptionsFor:()=>Qi,contentTypeValues:()=>Cl,currentProducesFor:()=>ff,definitions:()=>pf,externalDocs:()=>Cg,findDefinition:()=>Ng,getOAS3RequiredRequestBodyContentType:()=>zg,getParameter:()=>$g,hasHost:()=>df,host:()=>Th,info:()=>Ch,isMediaTypeSchemaPropertiesEqual:()=>Vg,isOAS3:()=>Ag,lastError:()=>Sg,mutatedRequestFor:()=>Fg,mutatedRequests:()=>cr,operationScheme:()=>mf,operationWithMeta:()=>hf,operations:()=>jh,operationsWithRootInherited:()=>bu,operationsWithTags:()=>Mg,parameterInclusionSettingFor:()=>Mh,parameterValues:()=>Rh,parameterWithMeta:()=>Lg,parameterWithMetaByIdentity:()=>Eu,parametersIncludeIn:()=>ho,parametersIncludeType:()=>ci,paths:()=>lf,produces:()=>cf,producesOptionsFor:()=>Bg,requestFor:()=>Dg,requests:()=>Rg,responseFor:()=>wu,responses:()=>$a,schemes:()=>Ms,security:()=>uf,securityDefinitions:()=>Nh,semver:()=>jg,spec:()=>eo,specJS:()=>_g,specJson:()=>Za,specJsonWithResolvedSubtrees:()=>li,specResolved:()=>Og,specResolvedSubtree:()=>kg,specSource:()=>Do,specStr:()=>u0,tagDetails:()=>Ig,taggedOperations:()=>Ih,tags:()=>La,url:()=>xg,validOperationMethods:()=>Pg,validateBeforeExecute:()=>Ug,validationErrors:()=>qg,version:()=>sf});var M={};Be.r(M),Be.d(M,{CLEAR_REQUEST:()=>Pl,CLEAR_RESPONSE:()=>Ap,CLEAR_VALIDATE_PARAMS:()=>Zi,LOG_REQUEST:()=>Fo,SET_MUTATED_REQUEST:()=>vc,SET_REQUEST:()=>kp,SET_RESPONSE:()=>Su,SET_SCHEME:()=>xu,UPDATE_EMPTY_PARAM_INCLUSION:()=>yc,UPDATE_JSON:()=>jl,UPDATE_OPERATION_META_VALUE:()=>bc,UPDATE_PARAM:()=>Kn,UPDATE_RESOLVED:()=>Cp,UPDATE_RESOLVED_SUBTREE:()=>Pi,UPDATE_SPEC:()=>wa,UPDATE_URL:()=>fo,VALIDATE_PARAMS:()=>Ds,changeConsumesValue:()=>Ec,changeParam:()=>bf,changeParamByIdentity:()=>wf,changeProducesValue:()=>Kg,clearRequest:()=>_u,clearResponse:()=>Xg,clearValidateParams:()=>Sf,execute:()=>Yg,executeRequest:()=>d0,invalidateResolvedSubtreeCache:()=>Hg,logRequest:()=>h0,parseToJson:()=>$h,requestResolvedSubtree:()=>Wg,resolveSpec:()=>Tl,setMutatedRequest:()=>Bh,setRequest:()=>xf,setResponse:()=>Gg,setScheme:()=>Ou,updateEmptyParamInclusion:()=>wc,updateJsonSpec:()=>Lh,updateResolved:()=>jp,updateResolvedSubtree:()=>Il,updateSpec:()=>Fh,updateUrl:()=>vf,validateParams:()=>Ef});var z={};Be.r(z),Be.d(z,{executeRequest:()=>Zg,updateJsonSpec:()=>f0,updateSpec:()=>Gn,validateParams:()=>m0});var K={};Be.r(K),Be.d(K,{JsonPatchError:()=>zn,_areEquals:()=>xc,applyOperation:()=>Rl,applyPatch:()=>es,applyReducer:()=>ay,deepClone:()=>ny,getValueByPointer:()=>Ml,validate:()=>Ip,validator:()=>Au});var Y={};Be.r(Y),Be.d(Y,{compare:()=>i,generate:()=>_f,observe:()=>Mp,unobserve:()=>y0});var B={};Be.r(B),Be.d(B,{hasElementSourceMap:()=>Gh,includesClasses:()=>Yh,includesSymbols:()=>FC,isAnnotationElement:()=>RC,isArrayElement:()=>vo,isBooleanElement:()=>Ff,isCommentElement:()=>DC,isElement:()=>da,isLinkElement:()=>aS,isMemberElement:()=>Bs,isNullElement:()=>tb,isNumberElement:()=>eb,isObjectElement:()=>hn,isParseResultElement:()=>iS,isPrimitiveElement:()=>$p,isRefElement:()=>oS,isStringElement:()=>Ir});var X={};Be.r(X),Be.d(X,{isJSONReferenceElement:()=>cb,isJSONSchemaElement:()=>LS,isLinkDescriptionElement:()=>p3,isMediaElement:()=>$S});var te={};Be.r(te),Be.d(te,{isBooleanJsonSchemaElement:()=>sN,isCallbackElement:()=>WP,isComponentsElement:()=>HP,isContactElement:()=>KP,isDiscriminatorElement:()=>hN,isExampleElement:()=>GP,isExternalDocumentationElement:()=>YP,isHeaderElement:()=>ad,isInfoElement:()=>XP,isLicenseElement:()=>QP,isLinkElement:()=>ZP,isMediaTypeElement:()=>Jy,isOpenApi3_0Element:()=>tN,isOpenapiElement:()=>eN,isOperationElement:()=>HS,isParameterElement:()=>rN,isPathItemElement:()=>gb,isPathsElement:()=>nN,isReferenceElement:()=>Mn,isRequestBodyElement:()=>aN,isResponseElement:()=>Vy,isResponsesElement:()=>oN,isSchemaElement:()=>iN,isSecurityRequirementElement:()=>lN,isSecuritySchemeElement:()=>cN,isServerElement:()=>uN,isServerVariableElement:()=>pN,isServersElement:()=>KS});var ie={};Be.r(ie),Be.d(ie,{isJSONReferenceElement:()=>cb,isJSONSchemaElement:()=>y4,isLinkDescriptionElement:()=>v4,isMediaElement:()=>$S});var se={};Be.r(se),Be.d(se,{isJSONReferenceElement:()=>cb,isJSONSchemaElement:()=>_4,isLinkDescriptionElement:()=>O4});var Te={};Be.r(Te),Be.d(Te,{isJSONSchemaElement:()=>z4,isLinkDescriptionElement:()=>V4});var he={};Be.r(he),Be.d(he,{isJSONSchemaElement:()=>Q4,isLinkDescriptionElement:()=>Z4});var ge={};Be.r(ge),Be.d(ge,{isBooleanJsonSchemaElement:()=>a1,isCallbackElement:()=>MI,isComponentsElement:()=>RI,isContactElement:()=>DI,isExampleElement:()=>FI,isExternalDocumentationElement:()=>LI,isHeaderElement:()=>$I,isInfoElement:()=>BI,isJsonSchemaDialectElement:()=>A2,isLicenseElement:()=>qI,isLinkElement:()=>UI,isMediaTypeElement:()=>t5,isOpenApi3_1Element:()=>C2,isOpenapiElement:()=>zI,isOperationElement:()=>j2,isParameterElement:()=>VI,isPathItemElement:()=>zp,isPathItemElementExternal:()=>JI,isPathsElement:()=>WI,isReferenceElement:()=>Vp,isReferenceElementExternal:()=>HI,isRequestBodyElement:()=>KI,isResponseElement:()=>GI,isResponsesElement:()=>YI,isSchemaElement:()=>Vs,isSecurityRequirementElement:()=>XI,isSecuritySchemeElement:()=>QI,isServerElement:()=>ZI,isServerVariableElement:()=>e5});var ke={};Be.r(ke),Be.d(ke,{cookie:()=>HM,header:()=>WM,path:()=>zM,query:()=>VM});var Ve={};Be.r(Ve),Be.d(Ve,{Button:()=>C7,Col:()=>k7,Collapse:()=>T7,Container:()=>O7,Input:()=>P7,Link:()=>t_,Row:()=>A7,Select:()=>N7,TextArea:()=>j7});var He={};Be.r(He),Be.d(He,{basePath:()=>XF,consumes:()=>QF,definitions:()=>WF,findDefinition:()=>JF,hasHost:()=>HF,host:()=>YF,produces:()=>ZF,schemes:()=>eL,securityDefinitions:()=>KF,validOperationMethods:()=>GF});var qe={};Be.r(qe),Be.d(qe,{definitionsToAuthorize:()=>tL});var nt={};Be.r(nt),Be.d(nt,{callbacksOperations:()=>lL,findSchema:()=>sL,isOAS3:()=>oL,isOAS30:()=>aL,isSwagger2:()=>nL,servers:()=>iL});var gt={};Be.r(gt),Be.d(gt,{CLEAR_REQUEST_BODY_VALIDATE_ERROR:()=>Fv,CLEAR_REQUEST_BODY_VALUE:()=>kw,SET_REQUEST_BODY_VALIDATE_ERROR:()=>Ow,UPDATE_ACTIVE_EXAMPLES_MEMBER:()=>Ew,UPDATE_REQUEST_BODY_INCLUSION:()=>ww,UPDATE_REQUEST_BODY_VALUE:()=>vw,UPDATE_REQUEST_BODY_VALUE_RETAIN_FLAG:()=>bw,UPDATE_REQUEST_CONTENT_TYPE:()=>Sw,UPDATE_RESPONSE_CONTENT_TYPE:()=>xw,UPDATE_SELECTED_SERVER:()=>yw,UPDATE_SERVER_VARIABLE_VALUE:()=>_w,clearRequestBodyValidateError:()=>DL,clearRequestBodyValue:()=>LL,initRequestBodyValidateError:()=>FL,setActiveExamplesMember:()=>NL,setRequestBodyInclusion:()=>PL,setRequestBodyValidateError:()=>RL,setRequestBodyValue:()=>CL,setRequestContentType:()=>TL,setResponseContentType:()=>IL,setRetainRequestBodyValueFlag:()=>jL,setSelectedServer:()=>AL,setServerVariableValue:()=>ML});var Re={};Be.r(Re),Be.d(Re,{activeExamplesMember:()=>KL,hasUserEditedBody:()=>JL,requestBodyErrors:()=>HL,requestBodyInclusionSetting:()=>WL,requestBodyValue:()=>UL,requestContentType:()=>GL,responseContentType:()=>YL,selectDefaultRequestBodyValue:()=>VL,selectedServer:()=>qL,serverEffectiveValue:()=>ZL,serverVariableValue:()=>XL,serverVariables:()=>QL,shouldRetainRequestBodyValue:()=>zL,validOperationMethods:()=>r$,validateBeforeExecute:()=>e$,validateShallowRequired:()=>t$});var u=Be(96540);function at(t){return`Minified Redux error #${t}; visit https://redux.js.org/Errors?code=${t} for the full message or use the non-minified dev environment for full errors. `}var Xe=typeof Symbol=="function"&&Symbol.observable||"@@observable",Se=()=>Math.random().toString(36).substring(7).split("").join("."),De={INIT:`@@redux/INIT${Se()}`,REPLACE:`@@redux/REPLACE${Se()}`};function Ke(t){if(typeof t!="object"||t===null)return!1;let e=t;for(;Object.getPrototypeOf(e)!==null;)e=Object.getPrototypeOf(e);return Object.getPrototypeOf(t)===e||Object.getPrototypeOf(t)===null}function ft(t,e,r){if(typeof t!="function")throw new Error(at(2));if(typeof e=="function"&&typeof r=="function"||typeof r=="function"&&typeof arguments[3]=="function")throw new Error(at(0));if(typeof e=="function"&&r===void 0&&(r=e,e=void 0),r!==void 0){if(typeof r!="function")throw new Error(at(1));return r(ft)(t,e)}let n=t,a=e,o=new Map,p=o,c=0,d=!1;function E(){p===o&&(p=new Map,o.forEach(((R,L)=>{p.set(L,R)})))}function x(){if(d)throw new Error(at(3));return a}function k(R){if(typeof R!="function")throw new Error(at(4));if(d)throw new Error(at(5));let L=!0;E();const V=c++;return p.set(V,R),function(){if(L){if(d)throw new Error(at(6));L=!1,E(),p.delete(V),o=null}}}function P(R){if(!Ke(R))throw new Error(at(7));if(R.type===void 0)throw new Error(at(8));if(typeof R.type!="string")throw new Error(at(17));if(d)throw new Error(at(9));try{d=!0,a=n(a,R)}finally{d=!1}return(o=p).forEach((L=>{L()})),R}return P({type:De.INIT}),{dispatch:P,subscribe:k,getState:x,replaceReducer:function(L){if(typeof L!="function")throw new Error(at(10));n=L,P({type:De.REPLACE})},[Xe]:function(){const L=k;return{subscribe(V){if(typeof V!="object"||V===null)throw new Error(at(11));function Q(){const J=V;J.next&&J.next(x())}return Q(),{unsubscribe:L(Q)}},[Xe](){return this}}}}}function Nt(t,e){return function(...r){return e(t.apply(this,r))}}function Wt(...t){return t.length===0?e=>e:t.length===1?t[0]:t.reduce(((e,r)=>(...n)=>e(r(...n))))}var H=Be(49568),we=Be(81919),ct=Be.n(we),mt=Be(89593),ur=Be(20334),ar=Be(55364),Tt=Be.n(ar);const Gt="err_new_thrown_err",Sr="err_new_thrown_err_batch",kt="err_new_spec_err",yt="err_new_spec_err_batch",Zt="err_new_auth_err",pe="err_clear",U="err_clear_by";function Z(t){return{type:Gt,payload:(0,ur.serializeError)(t)}}function ne(t){return{type:Sr,payload:t}}function ye(t){return{type:kt,payload:t}}function Ee(t){return{type:yt,payload:t}}function Fe(t){return{type:Zt,payload:t}}function St(t={}){return{type:pe,payload:t}}function Bt(t=()=>!0){return{type:U,payload:t}}const Qe=(function(){var e={location:{},history:{},open:()=>{},close:()=>{},File:function(){},FormData:function(){}};if(typeof window>"u")return e;try{e=window;for(var r of["File","Blob","FormData"])r in window&&(e[r]=window[r])}catch(n){console.error(n)}return e})();Be(84058),Be(55808);var $t=Be(50104),Pt=Be.n($t),rn=Be(7309),kr=Be.n(rn),An=Be(42426),Wr=Be.n(An),Jn=Be(75288),Ea=Be.n(Jn),Zn=Be(1882),un=Be.n(Zn),ti=Be(2205),Oo=Be.n(ti),Li=Be(53209),Fn=Be.n(Li),zo=Be(62802),Vo=Be.n(zo);const ds=H.default.Set.of("type","format","items","default","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","maxItems","minItems","uniqueItems","enum","multipleOf");function sr(t,{isOAS3:e}={}){if(!H.default.Map.isMap(t))return{schema:H.default.Map(),parameterContentMediaType:null};if(!e)return t.get("in")==="body"?{schema:t.get("schema",H.default.Map()),parameterContentMediaType:null}:{schema:t.filter(((r,n)=>ds.includes(n))),parameterContentMediaType:null};if(t.get("content")){const r=t.get("content",H.default.Map({})).keySeq().first();return{schema:t.getIn(["content",r,"schema"],H.default.Map()),parameterContentMediaType:r}}return{schema:t.get("schema")?t.get("schema",H.default.Map()):H.default.Map(),parameterContentMediaType:null}}var br=Be(48287).Buffer;const tn="default",Cr=t=>H.default.Iterable.isIterable(t),Mr=t=>Cr(t)?t.toJS():t;function Nn(t){return Hr(t)?Mr(t):{}}function nn(t){if(Cr(t)||t instanceof Qe.File||!Hr(t))return t;if(Array.isArray(t))return H.default.Seq(t).map(nn).toList();if(un()(t.entries)){const e=(function(n){if(!un()(n.entries))return n;const a={},o="_**[]",p={};for(let c of n.entries())a[c[0]]||p[c[0]]&&p[c[0]].containsMultiple?(p[c[0]]||(p[c[0]]={containsMultiple:!0,length:1},a[`${c[0]}${o}${p[c[0]].length}`]=a[c[0]],delete a[c[0]]),p[c[0]].length+=1,a[`${c[0]}${o}${p[c[0]].length}`]=c[1]):a[c[0]]=c[1];return a})(t);return H.default.OrderedMap(e).map(nn)}return H.default.OrderedMap(t).map(nn)}function fn(t){return Array.isArray(t)?t:[t]}function Ln(t){return typeof t=="function"}function Hr(t){return!!t&&typeof t=="object"}function ia(t){return typeof t=="function"}function za(t){return Array.isArray(t)}const fs=Pt();function ea(t,e){return Object.keys(t).reduce(((r,n)=>(r[n]=e(t[n],n),r)),{})}function di(t,e){return Object.keys(t).reduce(((r,n)=>{let a=e(t[n],n);return a&&typeof a=="object"&&Object.assign(r,a),r}),{})}function Va(t){return({dispatch:e,getState:r})=>n=>a=>typeof a=="function"?a(t()):n(a)}function Rr(t,e,r,n,a,o){if(!e)return[];let p=[],c=e.get("nullable"),d=e.get("required"),E=e.get("maximum"),x=e.get("minimum"),k=e.get("type"),P=e.get("format"),R=e.get("maxLength"),L=e.get("minLength"),V=e.get("uniqueItems"),Q=e.get("maxItems"),J=e.get("minItems"),re=e.get("pattern");const me=r||d===!0,le=t!=null,ve=me||le&&k==="array"||!(!me&&!le),be=c&&t===null;if(me&&!le&&!be&&!n&&!k)return p.push("Required field is not provided"),p;if(be||!k||!ve)return[];let ce=k==="string"&&t,_e=k==="array"&&Array.isArray(t)&&t.length,Ae=k==="array"&&H.default.List.isList(t)&&t.count(),xe=k==="array"&&typeof t=="string"&&t;const Oe=[ce,_e,Ae,k==="file"&&t instanceof Qe.File,k==="boolean"&&(t||t===!1),k==="number"&&(t||t===0),k==="integer"&&(t||t===0),k==="object"&&typeof t=="object"&&t!==null,k==="object"&&typeof t=="string"&&t],je=(o?Oe:Oe.concat(xe)).some((G=>!!G));if(me&&!je&&!n)return p.push("Required field is not provided"),p;if(k==="object"&&(a===null||a==="application/json")){let G=t;if(typeof t=="string")try{G=JSON.parse(t)}catch{return p.push("Parameter string value must be valid JSON"),p}e&&e.has("required")&&ia(d.isList)&&d.isList()&&d.forEach((de=>{G[de]===void 0&&p.push({propKey:de,error:"Required property not found"})})),e&&e.has("properties")&&e.get("properties").forEach(((de,Ne)=>{const Ye=Rr(G[Ne],de,!1,n,a,o);p.push(...Ye.map((ht=>({propKey:Ne,error:ht}))))}))}if(re){let G=((de,Ne)=>{if(!new RegExp(Ne).test(de))return"Value must follow pattern "+Ne})(t,re);G&&p.push(G)}if(J&&k==="array"){let G=((de,Ne)=>{if(!de&&Ne>=1||de&&de.length{if(de&&de.length>Ne)return`Array must not contain more then ${Ne} item${Ne===1?"":"s"}`})(t,Q);G&&p.push({needRemove:!0,error:G})}if(V&&k==="array"){let G=((de,Ne)=>{if(de&&(Ne==="true"||Ne===!0)){const Ye=(0,H.fromJS)(de),ht=Ye.toSet();if(de.length>ht.size){let Ct=(0,H.Set)();if(Ye.forEach(((_t,qt)=>{Ye.filter((It=>ia(It.equals)?It.equals(_t):It===_t)).size>1&&(Ct=Ct.add(qt))})),Ct.size!==0)return Ct.map((_t=>({index:_t,error:"No duplicates allowed."}))).toArray()}}})(t,V);G&&p.push(...G)}if(R||R===0){let G=((de,Ne)=>{if(de.length>Ne)return`Value must be no longer than ${Ne} character${Ne!==1?"s":""}`})(t,R);G&&p.push(G)}if(L){let G=((de,Ne)=>{if(de.length{if(de>Ne)return`Value must be less than or equal to ${Ne}`})(t,E);G&&p.push(G)}if(x||x===0){let G=((de,Ne)=>{if(de{if(isNaN(Date.parse(de)))return"Value must be a DateTime"})(t):P==="uuid"?(de=>{if(de=de.toString().toLowerCase(),!/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}[)}]?$/.test(de))return"Value must be a Guid"})(t):(de=>{if(de&&typeof de!="string")return"Value must be a string"})(t),!G)return p;p.push(G)}else if(k==="boolean"){let G=(de=>{if(de!=="true"&&de!=="false"&&de!==!0&&de!==!1)return"Value must be a boolean"})(t);if(!G)return p;p.push(G)}else if(k==="number"){let G=(de=>{if(!/^-?\d+(\.?\d+)?$/.test(de))return"Value must be a number"})(t);if(!G)return p;p.push(G)}else if(k==="integer"){let G=(de=>{if(!/^-?\d+$/.test(de))return"Value must be an integer"})(t);if(!G)return p;p.push(G)}else if(k==="array"){if(!_e&&!Ae)return p;t&&t.forEach(((G,de)=>{const Ne=Rr(G,e.get("items"),!1,n,a,o);p.push(...Ne.map((Ye=>({index:de,error:Ye}))))}))}else if(k==="file"){let G=(de=>{if(de&&!(de instanceof Qe.File))return"Value must be a file"})(t);if(!G)return p;p.push(G)}return p}const ko=t=>{let e;return e=t instanceof br?t:br.from(t.toString(),"utf-8"),e.toString("base64")},Nr={operationsSorter:{alpha:(t,e)=>t.get("path").localeCompare(e.get("path")),method:(t,e)=>t.get("method").localeCompare(e.get("method"))},tagsSorter:{alpha:(t,e)=>t.localeCompare(e)}},xn=t=>{let e=[];for(let r in t){let n=t[r];n!==void 0&&n!==""&&e.push([r,"=",encodeURIComponent(n).replace(/%20/g,"+")].join(""))}return e.join("&")},fi=(t,e,r)=>!!kr()(r,(n=>Ea()(t[n],e[n])));function ta(t){return!(!t||t.indexOf("localhost")>=0||t.indexOf("127.0.0.1")>=0||t==="none")}const Jo=t=>typeof t=="string"||t instanceof String?t.trim().replace(/\s/g,"%20"):"",ms=t=>Oo()(Jo(t).replace(/%20/g,"_")),Yt=t=>/^x-/.test(t),gr=t=>H.Map.isMap(t)?t.filter(((e,r)=>Yt(r))):Object.keys(t).filter((e=>Yt(e))),mr=t=>t.filter(((e,r)=>/^pattern|maxLength|minLength|maximum|minimum/.test(r)));function Vr(t,e,r=()=>!0){if(typeof t!="object"||Array.isArray(t)||t===null||!e)return t;const n=Object.assign({},t);return Object.keys(n).forEach((a=>{a===e&&r(n[a],a)?delete n[a]:n[a]=Vr(n[a],e,r)})),n}function Gr(t){if(typeof t=="string")return t;if(t&&t.toJS&&(t=t.toJS()),typeof t=="object"&&t!==null)try{return JSON.stringify(t,null,2)}catch{return String(t)}return t==null?"":t.toString()}function sa(t,{returnAll:e=!1,allowHashes:r=!0}={}){if(!H.default.Map.isMap(t))throw new Error("paramToIdentifier: received a non-Im.Map parameter as input");const n=t.get("name"),a=t.get("in");let o=[];return t&&t.hashCode&&a&&n&&r&&o.push(`${a}.${n}.hash-${t.hashCode()}`),a&&n&&o.push(`${a}.${n}`),o.push(n),e?o:o[0]||""}function Sa(t,e){return sa(t,{returnAll:!0}).map((r=>e[r])).filter((r=>r!==void 0))[0]}function Ja(t){return t.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}const Wa=t=>!t||!(!Cr(t)||!t.isEmpty()),Ia=t=>t;function Yl(t,e,r){let n=[Va(r)];return ft(t,e,(Qe.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__||Wt)((function(...o){return p=>(c,d)=>{const E=p(c,d);let x=()=>{throw new Error(at(15))};const k={getState:E.getState,dispatch:(R,...L)=>x(R,...L)},P=o.map((R=>R(k)));return x=Wt(...P)(E.dispatch),{...E,dispatch:x}}})(...n)))}class Xl{constructor(e={}){ct()(this,{state:{},plugins:[],system:{configs:{},fn:{},components:{},rootInjects:{},statePlugins:{}},boundSystem:{},toolbox:{}},e),this.getSystem=this._getSystem.bind(this),this.store=(function(n,a,o){return Yl(n,a,o)})(Ia,(0,H.fromJS)(this.state),this.getSystem),this.buildSystem(!1),this.register(this.plugins)}getStore(){return this.store}register(e,r=!0){var n=gs(e,this.getSystem());lo(this.system,n),r&&this.buildSystem(),ys.call(this.system,e,this.getSystem())&&this.buildSystem()}buildSystem(e=!0){let r=this.getStore().dispatch,n=this.getStore().getState;this.boundSystem=Object.assign({},this.getRootInjects(),this.getWrappedAndBoundActions(r),this.getWrappedAndBoundSelectors(n,this.getSystem),this.getStateThunks(n),this.getFn(),this.getConfigs()),e&&this.rebuildReducer()}_getSystem(){return this.boundSystem}getRootInjects(){return Object.assign({getSystem:this.getSystem,getStore:this.getStore.bind(this),getComponents:this.getComponents.bind(this),getState:this.getStore().getState,getConfigs:this._getConfigs.bind(this),Im:H.default,React:u},this.system.rootInjects||{})}_getConfigs(){return this.system.configs}getConfigs(){return{configs:this.system.configs}}setConfigs(e){this.system.configs=e}rebuildReducer(){this.store.replaceReducer((function(r,n){return(function(o,p){let c=Object.keys(o).reduce(((d,E)=>(d[E]=(function(k,P){return(R=new H.Map,L)=>{if(!k)return R;let V=k[L.type];if(V){const Q=mi(V,P)(R,L);return Q===null?R:Q}return R}})(o[E],p),d)),{});return Object.keys(c).length?(0,mt.H)(c):Ia})(ea(r,(a=>a.reducers)),n)})(this.system.statePlugins,this.getSystem))}getType(e){let r=e[0].toUpperCase()+e.slice(1);return di(this.system.statePlugins,((n,a)=>{let o=n[e];if(o)return{[a+r]:o}}))}getSelectors(){return this.getType("selectors")}getActions(){return ea(this.getType("actions"),(e=>di(e,((r,n)=>{if(Ln(r))return{[n]:r}}))))}getWrappedAndBoundActions(e){return ea(this.getBoundActions(e),((r,n)=>{let a=this.system.statePlugins[n.slice(0,-7)].wrapActions;return a?ea(r,((o,p)=>{let c=a[p];return c?(Array.isArray(c)||(c=[c]),c.reduce(((d,E)=>{let x=(...k)=>E(d,this.getSystem())(...k);if(!Ln(x))throw new TypeError("wrapActions needs to return a function that returns a new function (ie the wrapped action)");return mi(x,this.getSystem)}),o||Function.prototype)):o})):r}))}getWrappedAndBoundSelectors(e,r){return ea(this.getBoundSelectors(e,r),((n,a)=>{let o=[a.slice(0,-9)],p=this.system.statePlugins[o].wrapSelectors;return p?ea(n,((c,d)=>{let E=p[d];return E?(Array.isArray(E)||(E=[E]),E.reduce(((x,k)=>{let P=(...R)=>k(x,this.getSystem())(e().getIn(o),...R);if(!Ln(P))throw new TypeError("wrapSelector needs to return a function that returns a new function (ie the wrapped action)");return P}),c||Function.prototype)):c})):n}))}getStates(e){return Object.keys(this.system.statePlugins).reduce(((r,n)=>(r[n]=e.get(n),r)),{})}getStateThunks(e){return Object.keys(this.system.statePlugins).reduce(((r,n)=>(r[n]=()=>e().get(n),r)),{})}getFn(){return{fn:this.system.fn}}getComponents(e){const r=this.system.components[e];return Array.isArray(r)?r.reduce(((n,a)=>a(n,this.getSystem()))):e!==void 0?this.system.components[e]:this.system.components}getBoundSelectors(e,r){return ea(this.getSelectors(),((n,a)=>{let o=[a.slice(0,-9)];return ea(n,(p=>(...c)=>{let d=mi(p,this.getSystem).apply(null,[e().getIn(o),...c]);return typeof d=="function"&&(d=mi(d,this.getSystem)(r())),d}))}))}getBoundActions(e){e=e||this.getStore().dispatch;const r=this.getActions(),n=a=>typeof a!="function"?ea(a,(o=>n(o))):(...o)=>{var p=null;try{p=a(...o)}catch(c){p={type:Gt,error:!0,payload:(0,ur.serializeError)(c)}}finally{return p}};return ea(r,(a=>(function(p,c){if(typeof p=="function")return Nt(p,c);if(typeof p!="object"||p===null)throw new Error(at(16));const d={};for(const E in p){const x=p[E];typeof x=="function"&&(d[E]=Nt(x,c))}return d})(n(a),e)))}getMapStateToProps(){return()=>Object.assign({},this.getSystem())}getMapDispatchToProps(e){return r=>ct()({},this.getWrappedAndBoundActions(r),this.getFn(),e)}}function gs(t,e){return Hr(t)&&!za(t)?Tt()({},t):ia(t)?gs(t(e),e):za(t)?t.map((r=>gs(r,e))).reduce(lo,{components:e.getComponents()}):{}}function ys(t,e,{hasLoaded:r}={}){let n=r;return Hr(t)&&!za(t)&&typeof t.afterLoad=="function"&&(n=!0,mi(t.afterLoad,e.getSystem).call(this,e)),ia(t)?ys.call(this,t(e),e,{hasLoaded:n}):za(t)?t.map((a=>ys.call(this,a,e,{hasLoaded:n}))):n}function lo(t={},e={}){if(!Hr(t))return{};if(!Hr(e))return t;e.wrapComponents&&(ea(e.wrapComponents,((n,a)=>{const o=t.components&&t.components[a];o&&Array.isArray(o)?(t.components[a]=o.concat([n]),delete e.wrapComponents[a]):o&&(t.components[a]=[o,n],delete e.wrapComponents[a])})),Object.keys(e.wrapComponents).length||delete e.wrapComponents);const{statePlugins:r}=t;if(Hr(r))for(let n in r){const a=r[n];if(!Hr(a))continue;const{wrapActions:o,wrapSelectors:p}=a;if(Hr(o))for(let c in o){let d=o[c];Array.isArray(d)||(d=[d],o[c]=d),e&&e.statePlugins&&e.statePlugins[n]&&e.statePlugins[n].wrapActions&&e.statePlugins[n].wrapActions[c]&&(e.statePlugins[n].wrapActions[c]=o[c].concat(e.statePlugins[n].wrapActions[c]))}if(Hr(p))for(let c in p){let d=p[c];Array.isArray(d)||(d=[d],p[c]=d),e&&e.statePlugins&&e.statePlugins[n]&&e.statePlugins[n].wrapSelectors&&e.statePlugins[n].wrapSelectors[c]&&(e.statePlugins[n].wrapSelectors[c]=p[c].concat(e.statePlugins[n].wrapSelectors[c]))}}return ct()(t,e)}function mi(t,e,{logErrors:r=!0}={}){return typeof t!="function"?t:function(...n){try{return t.call(this,...n)}catch(a){if(r){const{uncaughtExceptionHandler:o}=e().getConfigs();typeof o=="function"?o(a):console.error(a)}return null}}}const Wu=t=>{t.preventDefault();const e=t.currentTarget.closest(".swagger-ui")?.querySelector("#operations");e&&(e.focus(),e.scrollIntoView())},Ql=()=>u.createElement("a",{href:"#operations",className:"swagger-ui__skip-link",onClick:Wu},"Skip to operations"),Zl=()=>({components:{SkipToOperations:Ql}});var ec=Be(61160),Ao=Be.n(ec);const vs="show_popup",an="authorize",tc="logout",rc="authorize_oauth2",Zs="configure_auth",nc="restore_authorization";function Hu(t){return{type:vs,payload:t}}function Ku(t){return{type:an,payload:t}}const $c=t=>({authActions:e})=>{e.authorize(t),e.persistAuthorizationIfNeeded()};function $i(t){return{type:tc,payload:t}}const Co=t=>({authActions:e})=>{e.logout(t),e.persistAuthorizationIfNeeded()},gi=t=>({authActions:e,errActions:r})=>{let{auth:n,token:a,isValid:o}=t,{schema:p,name:c}=n,d=p.get("flow");delete Qe.swaggerUIRedirectOauth2,d==="accessCode"||o||r.newAuthErr({authId:c,source:"auth",level:"warning",message:"Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"}),a.error?r.newAuthErr({authId:c,source:"auth",level:"error",message:JSON.stringify(a)}):e.authorizeOauth2WithPersistOption({auth:n,token:a})};function yi(t){return{type:rc,payload:t}}const bs=t=>({authActions:e})=>{e.authorizeOauth2(t),e.persistAuthorizationIfNeeded()},ws=t=>({authActions:e})=>{let{schema:r,name:n,username:a,password:o,passwordType:p,clientId:c,clientSecret:d}=t,E={grant_type:"password",scope:t.scopes.join(" "),username:a,password:o},x={};switch(p){case"request-body":(function(P,R,L){R&&Object.assign(P,{client_id:R}),L&&Object.assign(P,{client_secret:L})})(E,c,d);break;case"basic":x.Authorization="Basic "+ko(c+":"+d);break;default:console.warn(`Warning: invalid passwordType ${p} was passed, not including client id and secret`)}return e.authorizeRequest({body:xn(E),url:r.get("tokenUrl"),name:n,headers:x,query:{},auth:t})},jo=t=>({authActions:e})=>{let{schema:r,scopes:n,name:a,clientId:o,clientSecret:p}=t,c={Authorization:"Basic "+ko(o+":"+p)},d={grant_type:"client_credentials",scope:n.join(" ")};return e.authorizeRequest({body:xn(d),name:a,url:r.get("tokenUrl"),auth:t,headers:c})},ac=({auth:t,redirectUrl:e})=>({authActions:r})=>{let{schema:n,name:a,clientId:o,clientSecret:p,codeVerifier:c}=t,d={grant_type:"authorization_code",code:t.code,client_id:o,client_secret:p,redirect_uri:e,code_verifier:c};return r.authorizeRequest({body:xn(d),name:a,url:n.get("tokenUrl"),auth:t})},el=({auth:t,redirectUrl:e})=>({authActions:r})=>{let{schema:n,name:a,clientId:o,clientSecret:p,codeVerifier:c}=t,d={Authorization:"Basic "+ko(o+":"+p)},E={grant_type:"authorization_code",code:t.code,client_id:o,redirect_uri:e,code_verifier:c};return r.authorizeRequest({body:xn(E),name:a,url:n.get("tokenUrl"),auth:t,headers:d})},Es=t=>({fn:e,getConfigs:r,authActions:n,errActions:a,oas3Selectors:o,specSelectors:p,authSelectors:c})=>{let d,{body:E,query:x={},headers:k={},name:P,url:R,auth:L}=t,{additionalQueryStringParams:V}=c.getConfigs()||{};if(p.isOAS3()){let re=o.serverEffectiveValue(o.selectedServer());d=Ao()(R,re,!0)}else d=Ao()(R,p.url(),!0);typeof V=="object"&&(d.query=Object.assign({},d.query,V));const Q=d.toString();let J=Object.assign({Accept:"application/json, text/plain, */*","Content-Type":"application/x-www-form-urlencoded","X-Requested-With":"XMLHttpRequest"},k);e.fetch({url:Q,method:"post",headers:J,query:x,body:E,requestInterceptor:r().requestInterceptor,responseInterceptor:r().responseInterceptor}).then((function(re){let me=JSON.parse(re.data),le=me&&(me.error||""),ve=me&&(me.parseError||"");re.ok?le||ve?a.newAuthErr({authId:P,level:"error",source:"auth",message:JSON.stringify(me)}):n.authorizeOauth2WithPersistOption({auth:L,token:me}):a.newAuthErr({authId:P,level:"error",source:"auth",message:re.statusText})})).catch((re=>{let me=new Error(re).message;if(re.response&&re.response.data){const le=re.response.data;try{const ve=typeof le=="string"?JSON.parse(le):le;ve.error&&(me+=`, error: ${ve.error}`),ve.error_description&&(me+=`, description: ${ve.error_description}`)}catch{}}a.newAuthErr({authId:P,level:"error",source:"auth",message:me})}))};function vi(t){return{type:Zs,payload:t}}function bn(t){return{type:nc,payload:t}}const tl=()=>({authSelectors:t,getConfigs:e})=>{if(!e().persistAuthorization)return;const r=t.authorized().toJS();localStorage.setItem("authorized",JSON.stringify(r))},rl=(t,e)=>()=>{Qe.swaggerUIRedirectOauth2=e,Qe.open(t)},bi={[vs]:(t,{payload:e})=>t.set("showDefinitions",e),[an]:(t,{payload:e})=>{let r=(0,H.fromJS)(e),n=t.get("authorized")||(0,H.Map)();return r.entrySeq().forEach((([a,o])=>{if(!ia(o.getIn))return t.set("authorized",n);let p=o.getIn(["schema","type"]);if(p==="apiKey"||p==="http")n=n.set(a,o);else if(p==="basic"){let c=o.getIn(["value","username"]),d=o.getIn(["value","password"]);n=n.setIn([a,"value"],{username:c,header:"Basic "+ko(c+":"+d)}),n=n.setIn([a,"schema"],o.get("schema"))}})),t.set("authorized",n)},[rc]:(t,{payload:e})=>{let r,{auth:n,token:a}=e;n.token=Object.assign({},a),r=(0,H.fromJS)(n);let o=t.get("authorized")||(0,H.Map)();return o=o.set(r.get("name"),r),t.set("authorized",o)},[tc]:(t,{payload:e})=>{let r=t.get("authorized").withMutations((n=>{e.forEach((a=>{n.delete(a)}))}));return t.set("authorized",r)},[Zs]:(t,{payload:e})=>t.set("configs",e),[nc]:(t,{payload:e})=>t.set("authorized",(0,H.fromJS)(e.authorized))};function Po(t,e="expected a function, instead received "+typeof t){if(typeof t!="function")throw new TypeError(e)}var Ma=t=>Array.isArray(t)?t:[t];function oc(t){const e=Array.isArray(t[0])?t[0]:t;return(function(n,a="expected all items to be functions, instead received the following types: "){if(!n.every((o=>typeof o=="function"))){const o=n.map((p=>typeof p=="function"?`function ${p.name||"unnamed"}()`:typeof p)).join(", ");throw new TypeError(`${a}[${o}]`)}})(e,"createSelector expects all input-selectors to be functions, but received the following types: "),e}var jd=typeof WeakRef<"u"?WeakRef:class{constructor(t){this.value=t}deref(){return this.value}};function Bc(t,e={}){let r={s:0,v:void 0,o:null,p:null};const{resultEqualityCheck:n}=e;let a,o=0;function p(){let c=r;const{length:d}=arguments;for(let k=0,P=d;k{r={s:0,v:void 0,o:null,p:null},p.resetResultsCount()},p.resultsCount=()=>o,p.resetResultsCount=()=>{o=0},p}function qc(t,...e){const r=typeof t=="function"?{memoize:t,memoizeOptions:e}:t,n=(...a)=>{let o,p=0,c=0,d={},E=a.pop();typeof E=="object"&&(d=E,E=a.pop()),Po(E,`createSelector expects an output function after the inputs, but received: [${typeof E}]`);const x={...r,...d},{memoize:k,memoizeOptions:P=[],argsMemoize:R=Bc,argsMemoizeOptions:L=[]}=x,V=Ma(P),Q=Ma(L),J=oc(a),re=k((function(){return p++,E.apply(null,arguments)}),...V),me=R((function(){c++;const ve=(function(ce,_e){const Ae=[],{length:xe}=ce;for(let Oe=0;Oec,resetDependencyRecomputations:()=>{c=0},lastResult:()=>o,recomputations:()=>p,resetRecomputations:()=>{p=0},memoize:k,argsMemoize:R})};return Object.assign(n,{withTypes:()=>n}),n}var yr=qc(Bc),co=Object.assign(((t,e=yr)=>{(function(a,o="expected an object, instead received "+typeof a){if(typeof a!="object")throw new TypeError(o)})(t,"createStructuredSelector expects first argument to be an object where each property is a selector, instead received a "+typeof t);const r=Object.keys(t);return e(r.map((n=>t[n])),((...n)=>n.reduce(((a,o,p)=>(a[r[p]]=o,a)),{})))}),{withTypes:()=>co});const nl=t=>t,wi=yr(nl,(t=>t.get("showDefinitions"))),Uc=yr(nl,(()=>({specSelectors:t})=>{let e=t.securityDefinitions()||(0,H.Map)({}),r=(0,H.List)();return e.entrySeq().forEach((([n,a])=>{let o=(0,H.Map)();o=o.set(n,a),r=r.push(o)})),r})),al=(t,e)=>({specSelectors:r})=>(0,H.List)(r.isOAS3()?["components","securitySchemes",e]:["securityDefinitions",e]),Gu=(t,e)=>({specSelectors:r})=>{console.warn("WARNING: getDefinitionsByNames is deprecated and will be removed in the next major version.");let n=r.securityDefinitions(),a=(0,H.List)();return e.valueSeq().forEach((o=>{let p=(0,H.Map)();o.entrySeq().forEach((([c,d])=>{let E,x=n.get(c);x.get("type")==="oauth2"&&d.size&&(E=x.get("scopes"),E.keySeq().forEach((k=>{d.contains(k)||(E=E.delete(k))})),x=x.set("allowedScopes",E)),p=p.set(c,x)})),a=a.push(p)})),a},ic=(t,e=(0,H.List)())=>({authSelectors:r})=>{const n=r.definitionsToAuthorize()||(0,H.List)();let a=(0,H.List)();return n.forEach((o=>{let p=e.find((c=>c.get(o.keySeq().first())));p&&(o.forEach(((c,d)=>{if(c.get("type")==="oauth2"){const E=p.get(d);let x=c.get("scopes");H.List.isList(E)&&H.Map.isMap(x)&&(x.keySeq().forEach((k=>{E.contains(k)||(x=x.delete(k))})),o=o.set(d,c.set("scopes",x)))}})),a=a.push(o))})),a},zc=yr(nl,(t=>t.get("authorized")||(0,H.Map)())),rh=(t,e)=>({authSelectors:r})=>{let n=r.authorized();return H.List.isList(e)?!!e.toJS().filter((a=>Object.keys(a).map((o=>!!n.get(o))).indexOf(!1)===-1)).length:null},uo=yr(nl,(t=>t.get("configs"))),Yu=(t,{authSelectors:e,specSelectors:r})=>({path:n,method:a,operation:o,extras:p})=>{let c={authorized:e.authorized()&&e.authorized().toJS(),definitions:r.securityDefinitions()&&r.securityDefinitions().toJS(),specSecurity:r.security()&&r.security().toJS()};return t({path:n,method:a,operation:o,securities:c,...p})},Vc=(t,e)=>r=>{const{getConfigs:n,authActions:a}=e,o=n();if(t(r),o.persistAuthorization){const p=localStorage.getItem("authorized");p&&a.restoreAuthorization({authorized:JSON.parse(p)})}},Bi=(t,e)=>r=>{if(t(r),e.getConfigs().persistAuthorization)try{const[{schema:n,value:a}]=Object.values(r),o=(0,H.fromJS)(n),p=o.get("type")==="apiKey",c=o.get("in")==="cookie";p&&c&&(document.cookie=`${o.get("name")}=${a}; SameSite=None; Secure`)}catch(n){console.error("Error persisting cookie based apiKey in document.cookie.",n)}},Wo=(t,e)=>r=>{const n=e.getConfigs(),a=e.authSelectors.authorized();try{n.persistAuthorization&&Array.isArray(r)&&r.forEach((o=>{const p=a.get(o,{}),c=p.getIn(["schema","type"])==="apiKey",d=p.getIn(["schema","in"])==="cookie";if(c&&d){const E=p.getIn(["schema","name"]);document.cookie=`${E}=; Max-Age=-99999999`}}))}catch(o){console.error("Error deleting cookie based apiKey from document.cookie.",o)}t(r)};var $n=Be(90179),Ha=Be.n($n);class nh extends u.Component{mapStateToProps(e,r){return{state:e,ownProps:Ha()(r,Object.keys(r.getSystem()))}}render(){const{getComponent:e,ownProps:r}=this.props,n=e("LockIcon");return u.createElement(n,r)}}const Ei=nh;class Xu extends u.Component{mapStateToProps(e,r){return{state:e,ownProps:Ha()(r,Object.keys(r.getSystem()))}}render(){const{getComponent:e,ownProps:r}=this.props,n=e("UnlockIcon");return u.createElement(n,r)}}const ol=Xu;function Ss(){return{afterLoad(t){this.rootInjects=this.rootInjects||{},this.rootInjects.initOAuth=t.authActions.configureAuth,this.rootInjects.preauthorizeApiKey=il.bind(null,t),this.rootInjects.preauthorizeBasic=Qu.bind(null,t)},components:{LockAuthIcon:Ei,UnlockAuthIcon:ol,LockAuthOperationIcon:Ei,UnlockAuthOperationIcon:ol},statePlugins:{auth:{reducers:bi,actions:N,selectors:s,wrapActions:{authorize:Bi,logout:Wo}},configs:{wrapActions:{loaded:Vc}},spec:{wrapActions:{execute:Yu}}}}}function Qu(t,e,r,n){const{authActions:{authorize:a},specSelectors:{specJson:o,isOAS3:p}}=t,c=p()?["components","securitySchemes"]:["securityDefinitions"],d=o().getIn([...c,e]);return d?a({[e]:{value:{username:r,password:n},schema:d.toJS()}}):null}function il(t,e,r){const{authActions:{authorize:n},specSelectors:{specJson:a,isOAS3:o}}=t,p=o()?["components","securitySchemes"]:["securityDefinitions"],c=a().getIn([...p,e]);return c?n({[e]:{value:r,schema:c.toJS()}}):null}function ah(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var ri,sl,sc,Jc,Zu,Ra,Wc,ll,Hc,ep,cl,ul,tp,xa,qi,Si,rp,Ui,pl,hl,xi,xs,No,Kc,ra,dl,fl,ni,Gc,zi,lc,Yc,ml,gl,la,Vi,Ji,Wi,np,ap,oh,Ka,_s,Xc,ca={},yl={},Da={};function Hi(){if(ri)return Da;function t(e){return e==null}return ri=1,Da.isNothing=t,Da.isObject=function(r){return typeof r=="object"&&r!==null},Da.toArray=function(r){return Array.isArray(r)?r:t(r)?[]:[r]},Da.repeat=function(r,n){let a="";for(let o=0;o=48&&o<=55}function n(o){return o>=48&&o<=57}function a(o){let p=o,c=1,d=p[0];if(d!=="-"&&d!=="+"||(d==="-"&&(c=-1),p=p.slice(1),d=p[0]),p==="0")return 0;if(d==="0"){if(p[1]==="b")return c*parseInt(p.slice(2),2);if(p[1]==="x")return c*parseInt(p.slice(2),16);if(p[1]==="o")return c*parseInt(p.slice(2),8)}return c*parseInt(p,10)}return xs=new e("tag:yaml.org,2002:int",{kind:"scalar",resolve:function(p){if(p===null)return!1;const c=p.length;let d=0,E=!1;if(!c)return!1;let x=p[d];if(x!=="-"&&x!=="+"||(x=p[++d]),x==="0"){if(d+1===c)return!0;if(x=p[++d],x==="b"){for(d++;d=48&&k<=57||k>=65&&k<=70||k>=97&&k<=102))return!1;E=!0}return E&&isFinite(a(p))}if(x==="o"){for(d++;d=0?"0b"+o.toString(2):"-0b"+o.toString(2).slice(1)},octal:function(o){return o>=0?"0o"+o.toString(8):"-0o"+o.toString(8).slice(1)},decimal:function(o){return o.toString(10)},hexadecimal:function(o){return o>=0?"0x"+o.toString(16).toUpperCase():"-0x"+o.toString(16).toUpperCase().slice(1)}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}}),xs}function pc(){if(ra)return Kc;ra=1;const t=Hi(),e=_a(),r=new RegExp("^(?:[-+]?(?:[0-9]+)(?:\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),n=new RegExp("^(?:[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),a=/^[-+]?[0-9]+e/;return Kc=new e("tag:yaml.org,2002:float",{kind:"scalar",resolve:function(p){return p!==null&&!!r.test(p)&&(!!isFinite(parseFloat(p,10))||n.test(p))},construct:function(p){let c=p.toLowerCase();const d=c[0]==="-"?-1:1;return"+-".indexOf(c[0])>=0&&(c=c.slice(1)),c===".inf"?d===1?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:c===".nan"?NaN:d*parseFloat(c,10)},predicate:function(p){return Object.prototype.toString.call(p)==="[object Number]"&&(p%1!=0||t.isNegativeZero(p))},represent:function(p,c){if(isNaN(p))switch(c){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===p)switch(c){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===p)switch(c){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(t.isNegativeZero(p))return"-0.0";const d=p.toString(10);return a.test(d)?d.replace("e",".e"):d},defaultStyle:"lowercase"}),Kc}function vl(){return fl?dl:(fl=1,dl=uc().extend({implicit:[Os(),To(),Qc(),pc()]}))}function Zc(){return Gc?ni:(Gc=1,ni=vl())}function eu(){if(lc)return zi;lc=1;const t=_a(),e=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),r=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");return zi=new t("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:function(a){return a!==null&&(e.exec(a)!==null||r.exec(a)!==null)},construct:function(a){let o=0,p=null,c=e.exec(a);if(c===null&&(c=r.exec(a)),c===null)throw new Error("Date resolve error");const d=+c[1],E=+c[2]-1,x=+c[3];if(!c[4])return new Date(Date.UTC(d,E,x));const k=+c[4],P=+c[5],R=+c[6];if(c[7]){for(o=c[7].slice(0,3);o.length<3;)o+="0";o=+o}c[9]&&(p=6e4*(60*+c[10]+ +(c[11]||0)),c[9]==="-"&&(p=-p));const L=new Date(Date.UTC(d,E,x,k,P,R,o));return p&&L.setTime(L.getTime()-p),L},instanceOf:Date,represent:function(a){return a.toISOString()}}),zi}function op(){if(ml)return Yc;ml=1;const t=_a();return Yc=new t("tag:yaml.org,2002:merge",{kind:"scalar",resolve:function(r){return r==="<<"||r===null}})}function tu(){if(la)return gl;la=1;const t=_a(),e=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= +\r`;return gl=new t("tag:yaml.org,2002:binary",{kind:"scalar",resolve:function(n){if(n===null)return!1;let a=0;const o=n.length,p=e;for(let c=0;c64)){if(d<0)return!1;a+=6}}return a%8==0},construct:function(n){const a=n.replace(/[\r\n=]/g,""),o=a.length,p=e;let c=0;const d=[];for(let x=0;x>16&255),d.push(c>>8&255),d.push(255&c)),c=c<<6|p.indexOf(a.charAt(x));const E=o%4*6;return E===0?(d.push(c>>16&255),d.push(c>>8&255),d.push(255&c)):E===18?(d.push(c>>10&255),d.push(c>>2&255)):E===12&&d.push(c>>4&255),new Uint8Array(d)},predicate:function(n){return Object.prototype.toString.call(n)==="[object Uint8Array]"},represent:function(n){let a="",o=0;const p=n.length,c=e;for(let E=0;E>18&63],a+=c[o>>12&63],a+=c[o>>6&63],a+=c[63&o]),o=(o<<8)+n[E];const d=p%3;return d===0?(a+=c[o>>18&63],a+=c[o>>12&63],a+=c[o>>6&63],a+=c[63&o]):d===2?(a+=c[o>>10&63],a+=c[o>>4&63],a+=c[o<<2&63],a+=c[64]):d===1&&(a+=c[o>>2&63],a+=c[o<<4&63],a+=c[64],a+=c[64]),a}}),gl}function ip(){if(Ji)return Vi;Ji=1;const t=_a(),e=Object.prototype.hasOwnProperty,r=Object.prototype.toString;return Vi=new t("tag:yaml.org,2002:omap",{kind:"sequence",resolve:function(a){if(a===null)return!0;const o={},p=a;for(let c=0,d=p.length;cdr&&(tr=" ... ",bt=Xt-dr+tr.length),ut-Xt>dr&&(Er=" ...",ut=Xt+dr-Er.length),{str:tr+it.slice(bt,ut).replace(/\t/g,"→")+Er,pos:Xt-bt+tr.length}}function Ft(it,bt){return $e.repeat(" ",bt-it.length)+it}return Jc=function(bt,ut){if(ut=Object.create(ut||null),!bt.buffer)return null;ut.maxLength||(ut.maxLength=79),typeof ut.indent!="number"&&(ut.indent=1),typeof ut.linesBefore!="number"&&(ut.linesBefore=3),typeof ut.linesAfter!="number"&&(ut.linesAfter=2);const Xt=/\r?\n|\r|\0/g,zt=[0],tr=[];let Er,dr=-1;for(;Er=Xt.exec(bt.buffer);)tr.push(Er.index),zt.push(Er.index+Er[0].length),bt.position<=Er.index&&dr<0&&(dr=zt.length-2);dr<0&&(dr=zt.length-1);let qr="";const en=Math.min(bt.line+ut.linesAfter,tr.length).toString().length,kn=ut.maxLength-(ut.indent+en+3);for(let jt=1;jt<=ut.linesBefore&&!(dr-jt<0);jt++){const We=Je(bt.buffer,zt[dr-jt],tr[dr-jt],bt.position-(zt[dr]-zt[dr-jt]),kn);qr=$e.repeat(" ",ut.indent)+Ft((bt.line-jt+1).toString(),en)+" | "+We.str+` +`+qr}const ln=Je(bt.buffer,zt[dr],tr[dr],bt.position,kn);qr+=$e.repeat(" ",ut.indent)+Ft((bt.line+1).toString(),en)+" | "+ln.str+` +`,qr+=$e.repeat("-",ut.indent+en+3+ln.pos)+`^ +`;for(let jt=1;jt<=ut.linesAfter&&!(dr+jt>=tr.length);jt++){const We=Je(bt.buffer,zt[dr+jt],tr[dr+jt],bt.position-(zt[dr]-zt[dr+jt]),kn);qr+=$e.repeat(" ",ut.indent)+Ft((bt.line+jt+1).toString(),en)+" | "+We.str+` +`}return qr.replace(/\n$/,"")},Jc})(),n=O(),a=Object.prototype.hasOwnProperty,o=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,p=/[\x85\u2028\u2029]/,c=/[,\[\]{}]/,d=/^(?:!|!!|![0-9A-Za-z-]+!)$/,E=/^(?:!|[^,\[\]{}])(?:%[0-9a-f]{2}|[0-9a-z\-#;/?:@&=+$,_.!~*'()\[\]])*$/i;function x(W){return Object.prototype.toString.call(W)}function k(W){return W===10||W===13}function P(W){return W===9||W===32}function R(W){return W===9||W===32||W===10||W===13}function L(W){return W===44||W===91||W===93||W===123||W===125}function V(W){if(W>=48&&W<=57)return W-48;const $e=32|W;return $e>=97&&$e<=102?$e-97+10:-1}function Q(W){switch(W){case 48:return"\0";case 97:return"\x07";case 98:return"\b";case 116:case 9:return" ";case 110:return` +`;case 118:return"\v";case 102:return"\f";case 114:return"\r";case 101:return"\x1B";case 32:return" ";case 34:return'"';case 47:return"/";case 92:return"\\";case 78:return"…";case 95:return" ";case 76:return"\u2028";case 80:return"\u2029";default:return""}}function J(W){return W<=65535?String.fromCharCode(W):String.fromCharCode(55296+(W-65536>>10),56320+(W-65536&1023))}function re(W,$e,Je){$e==="__proto__"?Object.defineProperty(W,$e,{configurable:!0,enumerable:!0,writable:!0,value:Je}):W[$e]=Je}const me=new Array(256),le=new Array(256);for(let W=0;W<256;W++)me[W]=Q(W)?1:0,le[W]=Q(W);function ve(W,$e){this.input=W,this.filename=$e.filename||null,this.schema=$e.schema||n,this.onWarning=$e.onWarning||null,this.legacy=$e.legacy||!1,this.json=$e.json||!1,this.listener=$e.listener||null,this.maxDepth=typeof $e.maxDepth=="number"?$e.maxDepth:100,this.maxTotalMergeKeys=typeof $e.maxTotalMergeKeys=="number"?$e.maxTotalMergeKeys:1e4,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=W.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.depth=0,this.totalMergeKeys=0,this.firstTabInLine=-1,this.documents=[],this.anchorMapTransactions=[]}function be(W,$e){const Je={name:W.filename,buffer:W.input.slice(0,-1),position:W.position,line:W.line,column:W.position-W.lineStart};return Je.snippet=r(Je),new e($e,Je)}function ce(W,$e){throw be(W,$e)}function _e(W,$e){W.onWarning&&W.onWarning.call(null,be(W,$e))}function Ae(W,$e,Je){const Ft=W.anchorMapTransactions;if(Ft.length!==0){const it=Ft[Ft.length-1];a.call(it,$e)||(it[$e]={existed:a.call(W.anchorMap,$e),value:W.anchorMap[$e]})}W.anchorMap[$e]=Je}function xe(W){return{position:W.position,line:W.line,lineStart:W.lineStart,lineIndent:W.lineIndent,firstTabInLine:W.firstTabInLine,tag:W.tag,anchor:W.anchor,kind:W.kind,result:W.result}}function Oe(W,$e){W.position=$e.position,W.line=$e.line,W.lineStart=$e.lineStart,W.lineIndent=$e.lineIndent,W.firstTabInLine=$e.firstTabInLine,W.tag=$e.tag,W.anchor=$e.anchor,W.kind=$e.kind,W.result=$e.result}const je={YAML:function($e,Je,Ft){$e.version!==null&&ce($e,"duplication of %YAML directive"),Ft.length!==1&&ce($e,"YAML directive accepts exactly one argument");const it=/^([0-9]+)\.([0-9]+)$/.exec(Ft[0]);it===null&&ce($e,"ill-formed argument of the YAML directive");const bt=parseInt(it[1],10),ut=parseInt(it[2],10);bt!==1&&ce($e,"unacceptable YAML version of the document"),$e.version=Ft[0],$e.checkLineBreaks=ut<2,ut!==1&&ut!==2&&_e($e,"unsupported YAML version of the document")},TAG:function($e,Je,Ft){let it;Ft.length!==2&&ce($e,"TAG directive accepts exactly two arguments");const bt=Ft[0];it=Ft[1],d.test(bt)||ce($e,"ill-formed tag handle (first argument) of the TAG directive"),a.call($e.tagMap,bt)&&ce($e,'there is a previously declared suffix for "'+bt+'" tag handle'),E.test(it)||ce($e,"ill-formed tag prefix (second argument) of the TAG directive");try{it=decodeURIComponent(it)}catch{ce($e,"tag prefix is malformed: "+it)}$e.tagMap[bt]=it}};function G(W,$e,Je,Ft){if($e=32&&Xt<=1114111||ce(W,"expected valid JSON character")}else o.test(it)&&ce(W,"the stream contains non-printable characters");W.result+=it}}function de(W,$e,Je,Ft){t.isObject(Je)||ce(W,"cannot merge mappings; the provided source object is unacceptable");const it=Object.keys(Je);for(let bt=0,ut=it.length;btW.maxTotalMergeKeys&&ce(W,"merge keys exceeded maxTotalMergeKeys ("+W.maxTotalMergeKeys+")"),a.call($e,Xt)||(re($e,Xt,Je[Xt]),Ft[Xt]=!0)}}function Ne(W,$e,Je,Ft,it,bt,ut,Xt,zt){if(Array.isArray(it))for(let tr=0,Er=(it=Array.prototype.slice.call(it)).length;tr1&&(W.result+=t.repeat(` +`,$e-1))}function qt(W,$e){const Je=W.tag,Ft=W.anchor,it=[];let bt=!1;if(W.firstTabInLine!==-1)return!1;W.anchor!==null&&Ae(W,W.anchor,it);let ut=W.input.charCodeAt(W.position);for(;ut!==0&&(W.firstTabInLine!==-1&&(W.position=W.firstTabInLine,ce(W,"tab characters must not be used in indentation")),ut===45)&&R(W.input.charCodeAt(W.position+1));){if(bt=!0,W.position++,ht(W,!0,-1)&&W.lineIndent<=$e){it.push(null),ut=W.input.charCodeAt(W.position);continue}const Xt=W.line;if(or(W,$e,3,!1,!0),it.push(W.result),ht(W,!0,-1),ut=W.input.charCodeAt(W.position),(W.line===Xt||W.lineIndent>$e)&&ut!==0)ce(W,"bad indentation of a sequence entry");else if(W.lineIndent<$e)break}return!!bt&&(W.tag=Je,W.anchor=Ft,W.kind="sequence",W.result=it,!0)}function It(W,$e,Je){let Ft,it,bt,ut;const Xt=W.tag,zt=W.anchor,tr={},Er=Object.create(null);let dr=null,qr=null,en=null,kn=!1,ln=!1;if(W.firstTabInLine!==-1)return!1;W.anchor!==null&&Ae(W,W.anchor,tr);let jt=W.input.charCodeAt(W.position);for(;jt!==0;){kn||W.firstTabInLine===-1||(W.position=W.firstTabInLine,ce(W,"tab characters must not be used in indentation"));const We=W.input.charCodeAt(W.position+1),Kr=W.line;if(jt!==63&&jt!==58||!R(We)){if(it=W.line,bt=W.lineStart,ut=W.position,!or(W,Je,2,!1,!0))break;if(W.line===Kr){for(jt=W.input.charCodeAt(W.position);P(jt);)jt=W.input.charCodeAt(++W.position);if(jt===58)jt=W.input.charCodeAt(++W.position),R(jt)||ce(W,"a whitespace character is expected after the key-value separator within a block mapping"),kn&&(Ne(W,tr,Er,dr,qr,null,it,bt,ut),dr=qr=en=null),ln=!0,kn=!1,Ft=!1,dr=W.tag,qr=W.result;else{if(!ln)return W.tag=Xt,W.anchor=zt,!0;ce(W,"can not read an implicit mapping pair; a colon is missed")}}else{if(!ln)return W.tag=Xt,W.anchor=zt,!0;ce(W,"can not read a block mapping entry; a multiline key may not be an implicit key")}}else jt===63?(kn&&(Ne(W,tr,Er,dr,qr,null,it,bt,ut),dr=qr=en=null),ln=!0,kn=!0,Ft=!0):kn?(kn=!1,Ft=!0):ce(W,"incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line"),W.position+=1,jt=We;if((W.line===Kr||W.lineIndent>$e)&&(kn&&(it=W.line,bt=W.lineStart,ut=W.position),or(W,$e,4,!0,Ft)&&(kn?qr=W.result:en=W.result),kn||(Ne(W,tr,Er,dr,qr,en,it,bt,ut),dr=qr=en=null),ht(W,!0,-1),jt=W.input.charCodeAt(W.position)),(W.line===Kr||W.lineIndent>$e)&&jt!==0)ce(W,"bad indentation of a mapping entry");else if(W.lineIndent<$e)break}return kn&&Ne(W,tr,Er,dr,qr,null,it,bt,ut),ln&&(W.tag=Xt,W.anchor=zt,W.kind="mapping",W.result=tr),ln}function Ot(W){let $e,Je,Ft=!1,it=!1,bt=W.input.charCodeAt(W.position);if(bt!==33)return!1;W.tag!==null&&ce(W,"duplication of a tag property"),bt=W.input.charCodeAt(++W.position),bt===60?(Ft=!0,bt=W.input.charCodeAt(++W.position)):bt===33?(it=!0,$e="!!",bt=W.input.charCodeAt(++W.position)):$e="!";let ut=W.position;if(Ft){do bt=W.input.charCodeAt(++W.position);while(bt!==0&&bt!==62);W.position=0;tr-=1){const Er=Xt[zt[tr]];Er.existed?ut.anchorMap[zt[tr]]=Er.value:delete ut.anchorMap[zt[tr]]}})(W),Oe(W,it),!1)}function or(W,$e,Je,Ft,it){let bt,ut,Xt,zt,tr,Er=1,dr=!1,qr=!1,en=null;W.depth>=W.maxDepth&&ce(W,"nesting exceeded maxDepth ("+W.maxDepth+")"),W.depth+=1,W.listener!==null&&W.listener("open",W),W.tag=null,W.anchor=null,W.kind=null,W.result=null;const kn=bt=ut=Je===4||Je===3;if(Ft&&ht(W,!0,-1)&&(dr=!0,W.lineIndent>$e?Er=1:W.lineIndent===$e?Er=0:W.lineIndent<$e&&(Er=-1)),Er===1)for(;;){const ln=W.input.charCodeAt(W.position),jt=xe(W);if(dr&&(ln===33&&W.tag!==null||ln===38&&W.anchor!==null)||!Ot(W)&&!et(W))break;en===null&&(en=jt),ht(W,!0,-1)?(dr=!0,ut=kn,W.lineIndent>$e?Er=1:W.lineIndent===$e?Er=0:W.lineIndent<$e&&(Er=-1)):ut=!1}if(ut&&(ut=dr||it),Er===1||Je===4)if(zt=Je===1||Je===2?$e:$e+1,tr=W.position-W.lineStart,Er===1)if(ut&&(qt(W,tr)||It(W,tr,zt))||(function(jt,We){let Kr,Lr,zr,yn=!0;const En=jt.tag;let Pn;const Ua=jt.anchor;let Dn,Di,vn,cn;const hi=Object.create(null);let Hl,Xs,Zo,so=jt.input.charCodeAt(jt.position);if(so===91)Dn=93,cn=!1,Pn=[];else{if(so!==123)return!1;Dn=125,cn=!0,Pn={}}for(jt.anchor!==null&&Ae(jt,jt.anchor,Pn),so=jt.input.charCodeAt(++jt.position);so!==0;){if(ht(jt,!0,We),so=jt.input.charCodeAt(jt.position),so===Dn)return jt.position++,jt.tag=En,jt.anchor=Ua,jt.kind=cn?"mapping":"sequence",jt.result=Pn,!0;yn?so===44&&ce(jt,"expected the node content, but found ','"):ce(jt,"missed comma between flow collection entries"),Xs=Hl=Zo=null,Di=vn=!1,so===63&&R(jt.input.charCodeAt(jt.position+1))&&(Di=vn=!0,jt.position++,ht(jt,!0,We)),Kr=jt.line,Lr=jt.lineStart,zr=jt.position,or(jt,We,1,!1,!0),Xs=jt.tag,Hl=jt.result,ht(jt,!0,We),so=jt.input.charCodeAt(jt.position),!vn&&jt.line!==Kr||so!==58||(Di=!0,so=jt.input.charCodeAt(++jt.position),ht(jt,!0,We),or(jt,We,1,!1,!0),Zo=jt.result),cn?Ne(jt,Pn,hi,Xs,Hl,Zo,Kr,Lr,zr):Di?Pn.push(Ne(jt,null,hi,Xs,Hl,Zo,Kr,Lr,zr)):Pn.push(Hl),ht(jt,!0,We),so=jt.input.charCodeAt(jt.position),so===44?(yn=!0,so=jt.input.charCodeAt(++jt.position)):yn=!1}ce(jt,"unexpected end of the stream within a flow collection")})(W,zt))qr=!0;else{const ln=W.input.charCodeAt(W.position);en!==null&&kn&&!ut&&ln!==124&&ln!==62&&Dt(W,en,en.position-en.lineStart,zt)||bt&&(function(We,Kr){let Lr,zr,yn=1,En=!1,Pn=!1,Ua=Kr,Dn=0,Di=!1,vn=We.input.charCodeAt(We.position);if(vn===124)Lr=!1;else{if(vn!==62)return!1;Lr=!0}for(We.kind="scalar",We.result="";vn!==0;)if(vn=We.input.charCodeAt(++We.position),vn===43||vn===45)yn===1?yn=vn===43?3:2:ce(We,"repeat of a chomping mode identifier");else{if(!((zr=(cn=vn)>=48&&cn<=57?cn-48:-1)>=0))break;zr===0?ce(We,"bad explicit indentation width of a block scalar; it cannot be less than one"):Pn?ce(We,"repeat of an indentation width identifier"):(Ua=Kr+zr-1,Pn=!0)}var cn;if(P(vn)){do vn=We.input.charCodeAt(++We.position);while(P(vn));if(vn===35)do vn=We.input.charCodeAt(++We.position);while(!k(vn)&&vn!==0)}for(;vn!==0;){for(Ye(We),We.lineIndent=0,vn=We.input.charCodeAt(We.position);(!Pn||We.lineIndentUa&&(Ua=We.lineIndent),k(vn)){Dn++;continue}if(Pn||Ua!==0||ce(We,"missing indentation for block scalar"),We.lineIndent0){let Ua=yn,Dn=0;for(;Ua>0;Ua--)En=We.input.charCodeAt(++We.position),(yn=V(En))>=0?Dn=(Dn<<4)+yn:ce(We,"expected hexadecimal character");We.result+=J(Dn),We.position++}else ce(We,"unknown escape sequence");Lr=zr=We.position}else k(En)?(G(We,Lr,zr,!0),_t(We,ht(We,!1,Kr)),Lr=zr=We.position):We.position===We.lineStart&&Ct(We)?ce(We,"unexpected end of the document within a double quoted scalar"):(We.position++,P(En)||(zr=We.position))}var Pn;ce(We,"unexpected end of the stream within a double quoted scalar")})(W,zt)?qr=!0:(function(We){let Kr=We.input.charCodeAt(We.position);if(Kr!==42)return!1;Kr=We.input.charCodeAt(++We.position);const Lr=We.position;for(;Kr!==0&&!R(Kr)&&!L(Kr);)Kr=We.input.charCodeAt(++We.position);We.position===Lr&&ce(We,"name of an alias node must contain at least one character");const zr=We.input.slice(Lr,We.position);return a.call(We.anchorMap,zr)||ce(We,'unidentified alias "'+zr+'"'),We.result=We.anchorMap[zr],ht(We,!0,-1),!0})(W)?(qr=!0,W.tag===null&&W.anchor===null||ce(W,"alias node should not have any properties")):(function(We,Kr,Lr){let zr,yn,En,Pn,Ua,Dn;const Di=We.kind,vn=We.result;let cn=We.input.charCodeAt(We.position);if(R(cn)||L(cn)||cn===35||cn===38||cn===42||cn===33||cn===124||cn===62||cn===39||cn===34||cn===37||cn===64||cn===96)return!1;if(cn===63||cn===45){const hi=We.input.charCodeAt(We.position+1);if(R(hi)||Lr&&L(hi))return!1}for(We.kind="scalar",We.result="",zr=yn=We.position,En=!1;cn!==0;){if(cn===58){const hi=We.input.charCodeAt(We.position+1);if(R(hi)||Lr&&L(hi))break}else if(cn===35){if(R(We.input.charCodeAt(We.position-1)))break}else{if(We.position===We.lineStart&&Ct(We)||Lr&&L(cn))break;if(k(cn)){if(Pn=We.line,Ua=We.lineStart,Dn=We.lineIndent,ht(We,!1,-1),We.lineIndent>=Kr){En=!0,cn=We.input.charCodeAt(We.position);continue}We.position=yn,We.line=Pn,We.lineStart=Ua,We.lineIndent=Dn;break}}En&&(G(We,zr,yn,!1),_t(We,We.line-Pn),zr=yn=We.position,En=!1),P(cn)||(yn=We.position+1),cn=We.input.charCodeAt(++We.position)}return G(We,zr,yn,!1),!!We.result||(We.kind=Di,We.result=vn,!1)})(W,zt,Je===1)&&(qr=!0,W.tag===null&&(W.tag="?")),W.anchor!==null&&Ae(W,W.anchor,W.result)}else Er===0&&(qr=ut&&qt(W,tr));if(W.tag===null)W.anchor!==null&&Ae(W,W.anchor,W.result);else if(W.tag==="?"){W.result!==null&&W.kind!=="scalar"&&ce(W,'unacceptable node kind for ! tag; it should be "scalar", not "'+W.kind+'"');for(let ln=0,jt=W.implicitTypes.length;ln"),W.result!==null&&Xt.kind!==W.kind&&ce(W,"unacceptable node kind for !<"+W.tag+'> tag; it should be "'+Xt.kind+'", not "'+W.kind+'"'),Xt.resolve(W.result,W.tag)?(W.result=Xt.construct(W.result,W.tag),W.anchor!==null&&Ae(W,W.anchor,W.result)):ce(W,"cannot resolve a node with !<"+W.tag+"> explicit tag")}return W.listener!==null&&W.listener("close",W),W.depth-=1,W.tag!==null||W.anchor!==null||qr}function Ut(W){const $e=W.position;let Je,Ft=!1;for(W.version=null,W.checkLineBreaks=W.legacy,W.tagMap=Object.create(null),W.anchorMap=Object.create(null);(Je=W.input.charCodeAt(W.position))!==0&&(ht(W,!0,-1),Je=W.input.charCodeAt(W.position),!(W.lineIndent>0||Je!==37));){Ft=!0,Je=W.input.charCodeAt(++W.position);let it=W.position;for(;Je!==0&&!R(Je);)Je=W.input.charCodeAt(++W.position);const bt=W.input.slice(it,W.position),ut=[];for(bt.length<1&&ce(W,"directive name must not be less than one character in length");Je!==0;){for(;P(Je);)Je=W.input.charCodeAt(++W.position);if(Je===35){do Je=W.input.charCodeAt(++W.position);while(Je!==0&&!k(Je));break}if(k(Je))break;for(it=W.position;Je!==0&&!R(Je);)Je=W.input.charCodeAt(++W.position);ut.push(W.input.slice(it,W.position))}Je!==0&&Ye(W),a.call(je,bt)?je[bt](W,bt,ut):_e(W,'unknown document directive "'+bt+'"')}ht(W,!0,-1),W.lineIndent===0&&W.input.charCodeAt(W.position)===45&&W.input.charCodeAt(W.position+1)===45&&W.input.charCodeAt(W.position+2)===45?(W.position+=3,ht(W,!0,-1)):Ft&&ce(W,"directives end mark is expected"),or(W,W.lineIndent-1,4,!1,!0),ht(W,!0,-1),W.checkLineBreaks&&p.test(W.input.slice($e,W.position))&&_e(W,"non-ASCII line breaks are interpreted as content"),W.documents.push(W.result),W.position===W.lineStart&&Ct(W)?W.input.charCodeAt(W.position)===46&&(W.position+=3,ht(W,!0,-1)):W.position=32&&G<=126||G>=161&&G<=55295&&G!==8232&&G!==8233||G>=57344&&G<=65533&&G!==o||G>=65536&&G<=1114111}function V(G){return L(G)&&G!==o&&G!==13&&G!==10}function Q(G,de,Ne){const Ye=V(G),ht=Ye&&!R(G);return(Ne?Ye:Ye&&G!==44&&G!==91&&G!==93&&G!==123&&G!==125)&&G!==35&&!(de===58&&!ht)||V(de)&&!R(de)&&G===35||de===58&&ht}function J(G,de){const Ne=G.charCodeAt(de);let Ye;return Ne>=55296&&Ne<=56319&&de+1=56320&&Ye<=57343)?1024*(Ne-55296)+Ye-56320+65536:Ne}function re(G){return/^\n* /.test(G)}function me(G,de,Ne,Ye,ht,Ct,_t,qt){let It,Ot=0,et=null,Dt=!1,or=!1;const Ut=Ye!==-1;let pr=-1,W=(function(Je){return L(Je)&&Je!==o&&!R(Je)&&Je!==45&&Je!==63&&Je!==58&&Je!==44&&Je!==91&&Je!==93&&Je!==123&&Je!==125&&Je!==35&&Je!==38&&Je!==42&&Je!==33&&Je!==124&&Je!==61&&Je!==62&&Je!==39&&Je!==34&&Je!==37&&Je!==64&&Je!==96})(J(G,0))&&(function(Je){return!R(Je)&&Je!==58})(J(G,G.length-1));if(de||_t)for(It=0;It=65536?It+=2:It++){if(Ot=J(G,It),!L(Ot))return 5;W=W&&Q(Ot,et,qt),et=Ot}else{for(It=0;It=65536?It+=2:It++){if(Ot=J(G,It),Ot===10)Dt=!0,Ut&&(or=or||It-pr-1>Ye&&G[pr+1]!==" ",pr=It);else if(!L(Ot))return 5;W=W&&Q(Ot,et,qt),et=Ot}or=or||Ut&&It-pr-1>Ye&&G[pr+1]!==" "}return Dt||or?Ne>9&&re(G)?5:_t?Ct===2?5:2:or?4:3:!W||_t||ht(G)?Ct===2?5:2:1}function le(G,de,Ne,Ye,ht){G.dump=(function(){if(de.length===0)return G.quotingType===2?'""':"''";if(!G.noCompatMode&&(c.indexOf(de)!==-1||d.test(de)))return G.quotingType===2?'"'+de+'"':"'"+de+"'";const Ct=G.indent*Math.max(1,Ne),_t=G.lineWidth===-1?-1:Math.max(Math.min(G.lineWidth,40),G.lineWidth-Ct),qt=Ye||G.flowLevel>-1&&Ne>=G.flowLevel;switch(me(de,qt,G.indent,_t,(function(Ot){return(function(Dt,or){for(let Ut=0,pr=Dt.implicitTypes.length;Ut"+ve(de,G.indent)+be(k((function(Ot,et){const Dt=/(\n+)([^\n]*)/g;let or,Ut,pr=(function(){let $e=Ot.indexOf(` +`);return $e=$e!==-1?$e:Ot.length,Dt.lastIndex=$e,ce(Ot.slice(0,$e),et)})(),W=Ot[0]===` +`||Ot[0]===" ";for(;Ut=Dt.exec(Ot);){const $e=Ut[1],Je=Ut[2];or=Je[0]===" ",pr+=$e+(W||or||Je===""?"":` +`)+ce(Je,et),W=or}return pr})(de,_t),Ct));case 5:return'"'+(function(Ot){let et="",Dt=0;for(let or=0;or=65536?or+=2:or++){Dt=J(Ot,or);const Ut=p[Dt];!Ut&&L(Dt)?(et+=Ot[or],Dt>=65536&&(et+=Ot[or+1])):et+=Ut||E(Dt)}return et})(de)+'"';default:throw new e("impossible error: invalid scalar style")}})()}function ve(G,de){const Ne=re(G)?String(de):"",Ye=G[G.length-1]===` +`;return Ne+(Ye&&(G[G.length-2]===` +`||G===` +`)?"+":Ye?"":"-")+` +`}function be(G){return G[G.length-1]===` +`?G.slice(0,-1):G}function ce(G,de){if(G===""||G[0]===" ")return G;const Ne=/ [^ ]/g;let Ye,ht,Ct=0,_t=0,qt=0,It="";for(;Ye=Ne.exec(G);)qt=Ye.index,qt-Ct>de&&(ht=_t>Ct?_t:qt,It+=` +`+G.slice(Ct,ht),Ct=ht+1),_t=qt;return It+=` +`,G.length-Ct>de&&_t>Ct?It+=G.slice(Ct,_t)+` +`+G.slice(_t+1):It+=G.slice(Ct),It.slice(1)}function _e(G,de,Ne,Ye){let ht="";const Ct=G.tag;for(let _t=0,qt=Ne.length;_t tag resolver accepts not "'+qt+'" style');It=_t.represent[qt](de,qt)}G.dump=It}return!0}}return!1}function xe(G,de,Ne,Ye,ht,Ct,_t){G.tag=null,G.dump=Ne,Ae(G,Ne,!1)||Ae(G,Ne,!0);const qt=n.call(G.dump),It=Ye;Ye&&(Ye=G.flowLevel<0||G.flowLevel>de);const Ot=qt==="[object Object]"||qt==="[object Array]";let et,Dt;if(Ot&&(et=G.duplicates.indexOf(Ne),Dt=et!==-1),(G.tag!==null&&G.tag!=="?"||Dt||G.indent!==2&&de>0)&&(ht=!1),Dt&&G.usedDuplicates[et])G.dump="*ref_"+et;else{if(Ot&&Dt&&!G.usedDuplicates[et]&&(G.usedDuplicates[et]=!0),qt==="[object Object]")Ye&&Object.keys(G.dump).length!==0?((function(Ut,pr,W,$e){let Je="";const Ft=Ut.tag,it=Object.keys(W);if(Ut.sortKeys===!0)it.sort();else if(typeof Ut.sortKeys=="function")it.sort(Ut.sortKeys);else if(Ut.sortKeys)throw new e("sortKeys must be a boolean or a function");for(let bt=0,ut=it.length;bt1024;Er&&(Ut.dump&&Ut.dump.charCodeAt(0)===10?Xt+="?":Xt+="? "),Xt+=Ut.dump,Er&&(Xt+=P(Ut,pr)),xe(Ut,pr+1,tr,!0,Er)&&(Ut.dump&&Ut.dump.charCodeAt(0)===10?Xt+=":":Xt+=": ",Xt+=Ut.dump,Je+=Xt)}Ut.tag=Ft,Ut.dump=Je||"{}"})(G,de,G.dump,ht),Dt&&(G.dump="&ref_"+et+G.dump)):((function(Ut,pr,W){let $e="";const Je=Ut.tag,Ft=Object.keys(W);for(let it=0,bt=Ft.length;it1024&&(ut+="? "),ut+=Ut.dump+(Ut.condenseFlow?'"':"")+":"+(Ut.condenseFlow?"":" "),xe(Ut,pr,zt,!1,!1)&&(ut+=Ut.dump,$e+=ut))}Ut.tag=Je,Ut.dump="{"+$e+"}"})(G,de,G.dump),Dt&&(G.dump="&ref_"+et+" "+G.dump));else if(qt==="[object Array]")Ye&&G.dump.length!==0?(G.noArrayIndent&&!_t&&de>0?_e(G,de-1,G.dump,ht):_e(G,de,G.dump,ht),Dt&&(G.dump="&ref_"+et+G.dump)):((function(Ut,pr,W){let $e="";const Je=Ut.tag;for(let Ft=0,it=W.length;Ft",G.dump=or+" "+G.dump}}return!0}function Oe(G,de){const Ne=[],Ye=[];je(G,Ne,Ye);const ht=Ye.length;for(let Ct=0;Ct()=>{},n0=t=>e=>{const{fn:{fetch:r}}=e;return r(t)},a0=(t,e)=>r=>{const{specActions:n,configsActions:a}=r;if(t)return a.downloadConfig(t).then(o,o);function o(p){p instanceof Error||p.status>=400?(n.updateLoadingStatus("failedConfig"),n.updateLoadingStatus("failedConfig"),n.updateUrl(""),console.error(p.statusText+" "+t.url),e(null)):e(((c,d)=>{try{return Pe.load(c)}catch(E){return d&&d.errActions.newThrownErr(new Error(E)),{}}})(p.text,r))}},Pd=(t,e)=>t.getIn(Array.isArray(e)?e:[e]),bl={[aa]:(t,e)=>t.merge((0,H.fromJS)(e.payload)),[Bn]:(t,e)=>{const r=e.payload,n=t.get(r);return t.set(r,!n)}};function Nd(){return{statePlugins:{configs:{reducers:bl,actions:h,selectors:v}}}}const wl=t=>t?history.pushState(null,null,`#${t}`):window.location.hash="";var Ki=Be(86215),lp=Be.n(Ki);const Gi="layout_scroll_to",ih="layout_clear_scroll",o0={fn:{getScrollParent:function(e,r){const n=document.documentElement;let a=getComputedStyle(e);const o=a.position==="absolute",p=r?/(auto|scroll|hidden)/:/(auto|scroll)/;if(a.position==="fixed")return n;for(let c=e;c=c.parentElement;)if(a=getComputedStyle(c),(!o||a.position!=="static")&&p.test(a.overflow+a.overflowY+a.overflowX))return c;return n}},statePlugins:{layout:{actions:{scrollToElement:(t,e)=>r=>{try{e=e||r.fn.getScrollParent(t),lp().createScroller(e).to(t)}catch(n){console.error(n)}},scrollTo:t=>({type:Gi,payload:Array.isArray(t)?t:[t]}),clearScrollTo:()=>({type:ih}),readyToScroll:(t,e)=>r=>{const n=r.layoutSelectors.getScrollToKey();H.default.is(n,(0,H.fromJS)(t))&&(r.layoutActions.scrollToElement(e),r.layoutActions.clearScrollTo())},parseDeepLinkHash:t=>({layoutActions:e,layoutSelectors:r,getConfigs:n})=>{if(n().deepLinking&&t){let a=t.slice(1);a[0]==="!"&&(a=a.slice(1)),a[0]==="/"&&(a=a.slice(1));const o=a.split("/").map((x=>x||"")),p=r.isShownKeyFromUrlHashArray(o),[c,d="",E=""]=p;if(c==="operations"){const x=r.isShownKeyFromUrlHashArray([d]);d.indexOf("_")>-1&&(console.warn("Warning: escaping deep link whitespace with `_` will be unsupported in v4.0, use `%20` instead."),e.show(x.map((k=>k.replace(/_/g," "))),!0)),e.show(x,!0)}(d.indexOf("_")>-1||E.indexOf("_")>-1)&&(console.warn("Warning: escaping deep link whitespace with `_` will be unsupported in v4.0, use `%20` instead."),e.show(p.map((x=>x.replace(/_/g," "))),!0)),e.show(p,!0),e.scrollTo(p)}}},selectors:{getScrollToKey:t=>t.get("scrollToKey"),isShownKeyFromUrlHashArray(t,e){const[r,n]=e;return n?["operations",r,n]:r?["operations-tag",r]:[]},urlHashArrayFromIsShownKey(t,e){let[r,n,a]=e;return r=="operations"?[n,a]:r=="operations-tag"?[n]:[]}},reducers:{[Gi]:(t,e)=>t.set("scrollToKey",H.default.fromJS(e.payload)),[ih]:t=>t.delete("scrollToKey")},wrapActions:{show:(t,{getConfigs:e,layoutSelectors:r})=>(...n)=>{if(t(...n),e().deepLinking)try{let[a,o]=n;a=Array.isArray(a)?a:[a];const p=r.urlHashArrayFromIsShownKey(a);if(!p.length)return;const[c,d]=p;if(!o)return wl("/");p.length===2?wl(Jo(`/${encodeURIComponent(c)}/${encodeURIComponent(d)}`)):p.length===1&&wl(Jo(`/${encodeURIComponent(c)}`))}catch(a){console.error(a)}}}}}};var i0=Be(2209),El=Be.n(i0);const cp=(t,e)=>class extends u.Component{onLoad=n=>{const{operation:a}=this.props,{tag:o,operationId:p}=a.toObject();let{isShownKey:c}=a.toObject();c=c||["operations",o,p],e.layoutActions.readyToScroll(c,n)};render(){return u.createElement("span",{ref:this.onLoad},u.createElement(t,this.props))}},ru=(t,e)=>class extends u.Component{onLoad=n=>{const{tag:a}=this.props,o=["operations-tag",a];e.layoutActions.readyToScroll(o,n)};render(){return u.createElement("span",{ref:this.onLoad},u.createElement(t,this.props))}};function up(){return[o0,{statePlugins:{configs:{wrapActions:{loaded:(t,e)=>(...r)=>{t(...r);const n=decodeURIComponent(window.location.hash);e.layoutActions.parseDeepLinkHash(n)}}}},wrapComponents:{operation:cp,OperationTag:ru}}]}var Td=Be(40860),nu=Be.n(Td);function Sl(t){return t.map((e=>{let r="is not of a type(s)",n=e.get("message").indexOf(r);if(n>-1){let a=e.get("message").slice(n+19).split(",");return e.set("message",e.get("message").slice(0,n)+(function(p){return p.reduce(((c,d,E,x)=>E===x.length-1&&x.length>1?c+"or "+d:x[E+1]&&x.length>2?c+d+", ":x[E+1]?c+d+" ":c+d),"should be a")})(a))}return e}))}var qn=Be(58156),Cn=Be.n(qn);function xl(t,{jsSpec:e}){return t}const Xa=[f,y];function Oa(t){let e={jsSpec:{}};return nu()(Xa,((n,a)=>{try{return a.transform(n,e).filter((o=>!!o))}catch(o){return console.error("Transformer error:",o),n}}),t).filter((n=>!!n)).map((n=>(!n.get("line")&&n.get("path"),n)))}let ks={line:0,level:"error",message:"Unknown error"};const hc=yr((t=>t),(t=>t.get("errors",(0,H.List)()))),Ko=yr(hc,(t=>t.last()));function sh(t){return{statePlugins:{err:{reducers:{[Gt]:(e,{payload:r})=>{let n=Object.assign(ks,r,{type:"thrown"});return e.update("errors",(a=>(a||(0,H.List)()).push((0,H.fromJS)(n)))).update("errors",(a=>Oa(a)))},[Sr]:(e,{payload:r})=>(r=r.map((n=>(0,H.fromJS)(Object.assign(ks,n,{type:"thrown"})))),e.update("errors",(n=>(n||(0,H.List)()).concat((0,H.fromJS)(r)))).update("errors",(n=>Oa(n)))),[kt]:(e,{payload:r})=>{let n=(0,H.fromJS)(r);return n=n.set("type","spec"),e.update("errors",(a=>(a||(0,H.List)()).push((0,H.fromJS)(n)).sortBy((o=>o.get("line"))))).update("errors",(a=>Oa(a)))},[yt]:(e,{payload:r})=>(r=r.map((n=>(0,H.fromJS)(Object.assign(ks,n,{type:"spec"})))),e.update("errors",(n=>(n||(0,H.List)()).concat((0,H.fromJS)(r)))).update("errors",(n=>Oa(n)))),[Zt]:(e,{payload:r})=>{let n=(0,H.fromJS)(Object.assign({},r));return n=n.set("type","auth"),e.update("errors",(a=>(a||(0,H.List)()).push((0,H.fromJS)(n)))).update("errors",(a=>Oa(a)))},[pe]:(e,{payload:r})=>{if(!r||!e.get("errors"))return e;let n=e.get("errors").filter((a=>a.keySeq().every((o=>{const p=a.get(o),c=r[o];return!c||p!==c}))));return e.merge({errors:n})},[U]:(e,{payload:r})=>{if(!r||typeof r!="function")return e;let n=e.get("errors").filter((a=>r(a)));return e.merge({errors:n})}},actions:w,selectors:m}}}}function Xm(t,e){return t.filter(((r,n)=>n.indexOf(e)!==-1))}function Id(){return{fn:{opsFilter:Xm}}}var lh=Be(7666),Yr=Be.n(lh);const As=({className:t=null,width:e=20,height:r=20,...n})=>u.createElement("svg",Yr()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",className:t,width:e,height:r,"aria-hidden":"true",focusable:"false"},n),u.createElement("path",{fill:"currentColor",d:"M 17.418 14.908 C 17.69 15.176 18.127 15.176 18.397 14.908 C 18.667 14.64 18.668 14.207 18.397 13.939 L 10.489 6.109 C 10.219 5.841 9.782 5.841 9.51 6.109 L 1.602 13.939 C 1.332 14.207 1.332 14.64 1.602 14.908 C 1.873 15.176 2.311 15.176 2.581 14.908 L 10 7.767 L 17.418 14.908 Z"})),ch=({className:t=null,width:e=20,height:r=20,...n})=>u.createElement("svg",Yr()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",className:t,width:e,height:r,"aria-hidden":"true",focusable:"false"},n),u.createElement("path",{fill:"currentColor",d:"M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"})),Md=({className:t=null,width:e=20,height:r=20,...n})=>u.createElement("svg",Yr()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",className:t,width:e,height:r,"aria-hidden":"true",focusable:"false"},n),u.createElement("path",{fill:"currentColor",d:"M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"})),Qm=({className:t=null,width:e=20,height:r=20,...n})=>u.createElement("svg",Yr()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",className:t,width:e,height:r,"aria-hidden":"true",focusable:"false"},n),u.createElement("path",{fill:"currentColor",d:"M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"})),_l=({className:t=null,width:e=15,height:r=16,...n})=>u.createElement("svg",Yr()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 15 16",className:t,width:e,height:r,"aria-hidden":"true",focusable:"false"},n),u.createElement("g",{transform:"translate(2, -1)"},u.createElement("path",{fill:"#ffffff",fillRule:"evenodd",d:"M2 13h4v1H2v-1zm5-6H2v1h5V7zm2 3V8l-3 3 3 3v-2h5v-2H9zM4.5 9H2v1h2.5V9zM2 12h2.5v-1H2v1zm9 1h1v2c-.02.28-.11.52-.3.7-.19.18-.42.28-.7.3H1c-.55 0-1-.45-1-1V4c0-.55.45-1 1-1h3c0-1.11.89-2 2-2 1.11 0 2 .89 2 2h3c.55 0 1 .45 1 1v5h-1V6H1v9h10v-2zM2 5h8c0-.55-.45-1-1-1H8c-.55 0-1-.45-1-1s-.45-1-1-1-1 .45-1 1-.45 1-1 1H3c-.55 0-1 .45-1 1z"}))),au=({className:t=null,width:e=20,height:r=20,...n})=>u.createElement("svg",Yr()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",className:t,width:e,height:r,"aria-hidden":"true",focusable:"false"},n),u.createElement("path",{fill:"currentColor",d:"M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"})),ou=({className:t=null,width:e=20,height:r=20,...n})=>u.createElement("svg",Yr()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",className:t,width:e,height:r,"aria-hidden":"true",focusable:"false"},n),u.createElement("path",{fill:"currentColor",d:"M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"})),pp=()=>({components:{ArrowUpIcon:As,ArrowDownIcon:ch,ArrowIcon:Md,CloseIcon:Qm,CopyIcon:_l,LockIcon:au,UnlockIcon:ou}}),iu="layout_update_layout",Io="layout_update_filter",Mo="layout_update_mode",Cs="layout_show";function Yi(t){return{type:iu,payload:t}}function js(t){return{type:Io,payload:t}}function dc(t,e=!0){return t=fn(t),{type:Cs,payload:{thing:t,shown:e}}}function Zm(t,e=""){return t=fn(t),{type:Mo,payload:{thing:t,mode:e}}}const Rd={[iu]:(t,e)=>t.set("layout",e.payload),[Io]:(t,e)=>t.set("filter",e.payload),[Cs]:(t,e)=>{const r=e.payload.shown,n=(0,H.fromJS)(e.payload.thing);return t.update("shown",(0,H.fromJS)({}),(a=>a.set(n,r)))},[Mo]:(t,e)=>{let r=e.payload.thing,n=e.payload.mode;return t.setIn(["modes"].concat(r),(n||"")+"")}},Dd=t=>t.get("layout"),Go=t=>t.get("filter"),Ro=(t,e,r)=>(e=fn(e),t.get("shown",(0,H.fromJS)({})).get((0,H.fromJS)(e),r)),Un=(t,e,r="")=>(e=fn(e),t.getIn(["modes",...e],r)),Oi=yr((t=>t),(t=>!Ro(t,"editor"))),eg=(t,e)=>(r,...n)=>{let a=t(r,...n);const{fn:o,layoutSelectors:p,getConfigs:c}=e.getSystem(),d=c(),{maxDisplayedTags:E}=d;let x=p.currentFilter();return x&&x!==!0&&(a=o.opsFilter(a,x)),E>=0&&(a=a.slice(0,E)),a};function Fd(){return{statePlugins:{layout:{reducers:Rd,actions:g,selectors:S},spec:{wrapSelectors:_}}}}function uh({configs:t}){const e={debug:0,info:1,log:2,warn:3,error:4},r=p=>e[p]||-1;let{logLevel:n}=t,a=r(n);function o(p,...c){r(p)>=a&&console[p](...c)}return o.warn=o.bind(null,"warn"),o.error=o.bind(null,"error"),o.info=o.bind(null,"info"),o.debug=o.bind(null,"debug"),{rootInjects:{log:o}}}let hp=!1;function Ld(){return{statePlugins:{spec:{wrapActions:{updateSpec:t=>(...e)=>(hp=!0,t(...e)),updateJsonSpec:(t,e)=>(...r)=>{const n=e.getConfigs().onComplete;return hp&&typeof n=="function"&&(setTimeout(n,0),hp=!1),t(...r)}}}}}}const dp=t=>{const e="_**[]";return t.indexOf(e)<0?t:t.split(e)[0].trim()},tg=t=>t==="-d "||/^[_\/-]/g.test(t)?t:"'"+t.replace(/'/g,"'\\''")+"'",su=t=>(t=t.replace(/\^/g,"^^").replace(/\\"/g,'\\\\"').replace(/"/g,'""').replace(/\n/g,`^ +`))==="-d "?t.replace(/-d /g,`-d ^ +`):/^[_\/-]/g.test(t)?t:'"'+t+'"',$d=t=>t==="-d "?t:/\n/.test(t)?`@" +${t.replace(/`/g,"``").replace(/\$/g,"`$")} +"@`:/^[_\/-]/.test(t)?t:`'${t.replace(/'/g,"''")}'`,Bd=(t,e,r,n="")=>{let a=!1,o="";const p=(...R)=>o+=" "+R.map(e).join(" "),c=(...R)=>o+=R.map(e).join(" "),d=()=>o+=` ${r}`,E=(R=1)=>o+=" ".repeat(R);let x=t.get("headers");o+="curl"+n;const k=t.get("curlOptions");if(H.List.isList(k)&&!k.isEmpty()&&p(...t.get("curlOptions")),p("-X",t.get("method")),d(),E(),c(`${t.get("url")}`),x&&x.size)for(let R of t.get("headers").entries()){d(),E();let[L,V]=R;c("-H",`${L}: ${V}`),a=a||/^content-type$/i.test(L)&&/^multipart\/form-data$/i.test(V)}const P=t.get("body");if(P)if(a&&["POST","PUT","PATCH"].includes(t.get("method")))for(let[R,L]of P.entrySeq()){let V=dp(R);d(),E(),c("-F"),L instanceof Qe.File&&typeof L.valueOf()=="string"?p(`${V}=${L.data}${L.type?`;type=${L.type}`:""}`):L instanceof Qe.File?p(`${V}=@${L.name}${L.type?`;type=${L.type}`:""}`):p(`${V}=${L}`)}else if(P instanceof Qe.File)d(),E(),c(`--data-binary '@${P.name}'`);else{d(),E(),c("-d ");let R=P;H.Map.isMap(R)?c((function(V){let Q=[];for(let[J,re]of V.get("body").entrySeq()){let me=dp(J);re instanceof Qe.File?Q.push(` "${me}": { + "name": "${re.name}"${re.type?`, + "type": "${re.type}"`:""} + }`):Q.push(` "${me}": ${JSON.stringify(re,null,2).replace(/(\r\n|\r|\n)/g,` + `)}`)}return`{ +${Q.join(`, +`)} +}`})(t)):(typeof R!="string"&&(R=JSON.stringify(R)),c(R))}else P||t.get("method")!=="POST"||(d(),E(),c("-d ''"));return o},fp=t=>Bd(t,$d,"`\n",".exe"),mp=t=>Bd(t,tg,`\\ +`),rg=t=>Bd(t,su,`^ +`),ph=t=>t||(0,H.Map)(),fc=yr(ph,(t=>{const e=t.get("languages"),r=t.get("generators",(0,H.Map)());return!e||e.isEmpty()?r:r.filter(((n,a)=>e.includes(a)))})),ng=t=>({fn:e})=>fc(t).map(((r,n)=>{const a=(o=>e[`requestSnippetGenerator_${o}`])(n);return typeof a!="function"?null:r.set("fn",a)})).filter((r=>r)),hh=yr(ph,(t=>t.get("activeLanguage"))),dh=yr(ph,(t=>t.get("defaultExpanded")));var lu=Be(46942),Xr=Be.n(lu),cu=Be(59399);const uu="Copy request snippet to clipboard",qd={cursor:"pointer",lineHeight:1,display:"inline-flex",backgroundColor:"rgb(250, 250, 250)",paddingBottom:"0",paddingTop:"0",border:"1px solid rgb(51, 51, 51)",borderRadius:"4px 4px 0 0",boxShadow:"none",borderBottom:"none"},pu={cursor:"pointer",lineHeight:1,display:"inline-flex",backgroundColor:"rgb(51, 51, 51)",boxShadow:"none",border:"1px solid rgb(51, 51, 51)",paddingBottom:"0",paddingTop:"0",borderRadius:"4px 4px 0 0",marginTop:"-5px",marginRight:"-5px",marginLeft:"-5px",zIndex:"9999",borderBottom:"none"},oi=({request:t,requestSnippetsSelectors:e,getComponent:r})=>{const n=(0,u.useRef)(null),a=r("ArrowUpIcon"),o=r("ArrowDownIcon"),p=r("SyntaxHighlighter",!0),[c,d]=(0,u.useState)(e.getSnippetGenerators()?.keySeq().first()),[E,x]=(0,u.useState)(e?.getDefaultExpanded()),k=e.getSnippetGenerators(),P=k.get(c),R=P.get("fn")(t),L=()=>{x(!E)},V=J=>J===c?pu:qd,Q=J=>{const{target:re,deltaY:me}=J,{scrollHeight:le,offsetHeight:ve,scrollTop:be}=re;le>ve&&(be===0&&me<0||ve+be>=le&&me>0)&&J.preventDefault()};return(0,u.useEffect)((()=>{}),[]),(0,u.useEffect)((()=>{const J=Array.from(n.current.childNodes).filter((re=>!!re.nodeType&&re.classList?.contains("curl-command")));return J.forEach((re=>re.addEventListener("mousewheel",Q,{passive:!1}))),()=>{J.forEach((re=>re.removeEventListener("mousewheel",Q)))}}),[t]),u.createElement("div",{className:"request-snippets",ref:n},u.createElement("div",{style:{width:"100%",display:"flex",justifyContent:"flex-start",alignItems:"center",marginBottom:"15px"}},u.createElement("h4",{onClick:()=>L(),style:{cursor:"pointer"}},"Snippets"),u.createElement("button",{onClick:()=>L(),style:{border:"none",background:"none"},title:E?"Collapse operation":"Expand operation"},E?u.createElement(o,{className:"arrow",width:"10",height:"10"}):u.createElement(a,{className:"arrow",width:"10",height:"10"}))),E&&u.createElement("div",{className:"curl-command"},u.createElement("div",{style:{paddingLeft:"15px",paddingRight:"10px",width:"100%",display:"flex"}},k.entrySeq().map((([J,re])=>u.createElement("div",{className:Xr()("btn",{active:J===c}),style:V(J),key:J,onClick:()=>(me=>{c!==me&&d(me)})(J)},u.createElement("h4",{style:J===c?{color:"white"}:{}},re.get("title")))))),u.createElement("div",{className:"copy-to-clipboard",title:uu,"aria-label":uu},u.createElement(cu.CopyToClipboard,{text:R},u.createElement("button",{"aria-label":uu,title:uu,type:"button"}))),u.createElement("div",null,u.createElement(p,{language:P.get("syntax"),className:"curl microlight",renderPlainText:({children:J,PlainTextViewer:re})=>u.createElement(re,{className:"curl"},J)},R))))},Ol=()=>({components:{RequestSnippets:oi},fn:{requestSnippetGenerator_curl_bash:mp,requestSnippetGenerator_curl_cmd:rg,requestSnippetGenerator_curl_powershell:fp},statePlugins:{requestSnippets:{selectors:T}}});class gp extends u.Component{static defaultProps={collapsedContent:"{...}",expanded:!1,title:null,onToggle:()=>{},hideSelfOnExpand:!1,specPath:H.default.List([])};constructor(e,r){super(e,r);let{expanded:n,collapsedContent:a}=this.props;this.state={expanded:n,collapsedContent:a||gp.defaultProps.collapsedContent}}componentDidMount(){const{hideSelfOnExpand:e,expanded:r,modelName:n}=this.props;e&&r&&this.props.onToggle(n,r)}UNSAFE_componentWillReceiveProps(e){this.props.expanded!==e.expanded&&this.setState({expanded:e.expanded})}toggleCollapsed=()=>{this.props.onToggle&&this.props.onToggle(this.props.modelName,!this.state.expanded),this.setState({expanded:!this.state.expanded})};onLoad=e=>{if(e&&this.props.layoutSelectors){const r=this.props.layoutSelectors.getScrollToKey();H.default.is(r,this.props.specPath)&&this.toggleCollapsed(),this.props.layoutActions.readyToScroll(this.props.specPath,e.parentElement)}};render(){const{title:e,classes:r}=this.props;return this.state.expanded&&this.props.hideSelfOnExpand?u.createElement("span",{className:r||""},this.props.children):u.createElement("span",{className:r||"",ref:this.onLoad},u.createElement("button",{"aria-expanded":this.state.expanded,className:"model-box-control",onClick:this.toggleCollapsed},e&&u.createElement("span",{className:"pointer"},e),u.createElement("span",{className:"model-toggle"+(this.state.expanded?"":" collapsed")}),!this.state.expanded&&u.createElement("span",null,this.state.collapsedContent)),this.state.expanded&&this.props.children)}}const ag=({initialTab:t,isExecute:e,schema:r,example:n})=>{const a=(0,u.useMemo)((()=>({example:"example",model:"model"})),[]),o=(0,u.useMemo)((()=>Object.keys(a)),[a]).includes(t)&&r&&!e?t:a.example,p=(x=>{const k=(0,u.useRef)();return(0,u.useEffect)((()=>{k.current=x})),k.current})(e),[c,d]=(0,u.useState)(o),E=(0,u.useCallback)((x=>{d(x.target.dataset.name)}),[]);return(0,u.useEffect)((()=>{p&&!e&&n&&d(a.example)}),[p,e,n]),{activeTab:c,onTabChange:E,tabs:a}},Ps=({schema:t,example:e,isExecute:r=!1,specPath:n,includeWriteOnly:a=!1,includeReadOnly:o=!1,getComponent:p,getConfigs:c,specSelectors:d})=>{const{defaultModelRendering:E,defaultModelExpandDepth:x}=c(),k=p("ModelWrapper"),P=p("HighlightCode",!0),R=Fn()(5).toString("base64"),L=Fn()(5).toString("base64"),V=Fn()(5).toString("base64"),Q=Fn()(5).toString("base64"),J=d.isOAS3(),{activeTab:re,tabs:me,onTabChange:le}=ag({initialTab:E,isExecute:r,schema:t,example:e});return u.createElement("div",{className:"model-example"},u.createElement("ul",{className:"tab",role:"tablist"},u.createElement("li",{className:Xr()("tabitem",{active:re===me.example}),role:"presentation"},u.createElement("button",{"aria-controls":L,"aria-selected":re===me.example,className:"tablinks","data-name":"example",id:R,onClick:le,role:"tab"},r?"Edit Value":"Example Value")),t&&u.createElement("li",{className:Xr()("tabitem",{active:re===me.model}),role:"presentation"},u.createElement("button",{"aria-controls":Q,"aria-selected":re===me.model,className:Xr()("tablinks",{inactive:r}),"data-name":"model",id:V,onClick:le,role:"tab"},J?"Schema":"Model"))),re===me.example&&u.createElement("div",{"aria-hidden":re!==me.example,"aria-labelledby":R,"data-name":"examplePanel",id:L,role:"tabpanel",tabIndex:"0"},e||u.createElement(P,null,"(no example available")),re===me.model&&u.createElement("div",{className:"model-container","aria-hidden":re===me.example,"aria-labelledby":V,"data-name":"modelPanel",id:Q,role:"tabpanel",tabIndex:"0"},u.createElement(k,{schema:t,getComponent:p,getConfigs:c,specSelectors:d,expandDepth:x,specPath:n,includeReadOnly:o,includeWriteOnly:a})))};class kl extends u.Component{onToggle=(e,r)=>{this.props.layoutActions&&this.props.layoutActions.show(this.props.fullPath,r)};render(){let{getComponent:e,getConfigs:r}=this.props;const n=e("Model");let a;return this.props.layoutSelectors&&(a=this.props.layoutSelectors.isShown(this.props.fullPath)),u.createElement("div",{className:"model-box"},u.createElement(n,Yr()({},this.props,{getConfigs:r,expanded:a,depth:1,onToggle:this.onToggle,expandDepth:this.props.expandDepth||0})))}}function yp(t){return yp=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},yp(t)}function og(t,e){for(var r=0;r1&&arguments[1]!==void 0?arguments[1]:{},r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{},n=(function(p,c){return function(d){if(typeof d=="string")return(0,H.is)(c[d],p[d]);if(Array.isArray(d))return(0,H.is)(ki(c,d),ki(p,d));throw new TypeError("Invalid key: expected Array or string: "+d)}})(e,r),a=t||Object.keys((function(p){for(var c=1;c1&&arguments[1]!==void 0?arguments[1]:{};return!du(this.updateOnProps,this.props,n,"updateOnProps")||!du(this.updateOnStates,this.state,a,"updateOnStates")}}]),e})(u.Component);var Al,zd=Be(5556),va=Be.n(zd);function gh(){return gh=Object.assign?Object.assign.bind():function(t){for(var e=1;eu.createElement("svg",gh({xmlns:"http://www.w3.org/2000/svg",width:200,height:200,className:"rolling-load_svg__lds-rolling",preserveAspectRatio:"xMidYMid",style:{backgroundImage:"none",backgroundPosition:"initial initial",backgroundRepeat:"initial initial"},viewBox:"0 0 100 100"},t),Al||(Al=u.createElement("circle",{cx:50,cy:50,r:35,fill:"none",stroke:"#555",strokeDasharray:"164.93361431346415 56.97787143782138",strokeWidth:10},u.createElement("animateTransform",{attributeName:"transform",begin:"0s",calcMode:"linear",dur:"1s",keyTimes:"0;1",repeatCount:"indefinite",type:"rotate",values:"0 50 50;360 50 50"})))),Wn=t=>{const e=t.replace(/~1/g,"/").replace(/~0/g,"~");try{return decodeURIComponent(e)}catch{return e}};class vh extends bp{static propTypes={schema:El().map.isRequired,getComponent:va().func.isRequired,getConfigs:va().func.isRequired,specSelectors:va().object.isRequired,name:va().string,displayName:va().string,isRef:va().bool,required:va().bool,expandDepth:va().number,depth:va().number,specPath:El().list.isRequired,includeReadOnly:va().bool,includeWriteOnly:va().bool};getModelName=e=>e.indexOf("#/definitions/")!==-1?Wn(e.replace(/^.*#\/definitions\//,"")):e.indexOf("#/components/schemas/")!==-1?Wn(e.replace(/^.*#\/components\/schemas\//,"")):void 0;getRefSchema=e=>{let{specSelectors:r}=this.props;return r.findDefinition(e)};render(){let{getComponent:e,getConfigs:r,specSelectors:n,schema:a,required:o,name:p,isRef:c,specPath:d,displayName:E,includeReadOnly:x,includeWriteOnly:k}=this.props;const P=e("ObjectModel"),R=e("ArrayModel"),L=e("PrimitiveModel");let V="object",Q=a&&a.get("$$ref"),J=a&&a.get("$ref");if(!p&&Q&&(p=this.getModelName(Q)),J){const me=this.getModelName(J),le=this.getRefSchema(me);H.Map.isMap(le)?(a=le.mergeDeep(a),Q||(a=a.set("$$ref",J),Q=J)):H.Map.isMap(a)&&a.size===1&&(a=null,p=J)}if(!a)return u.createElement("strong",{className:"model model-title"},u.createElement("span",{className:"model-title__text"},E||p),!J&&u.createElement(yh,{height:"20px",width:"20px"}));const re=n.isOAS3()&&a.get("deprecated");switch(c=c!==void 0?c:!!Q,V=a&&a.get("type")||V,V){case"object":return u.createElement(P,Yr()({className:"object"},this.props,{specPath:d,getConfigs:r,schema:a,name:p,deprecated:re,isRef:c,includeReadOnly:x,includeWriteOnly:k}));case"array":return u.createElement(R,Yr()({className:"array"},this.props,{getConfigs:r,schema:a,name:p,deprecated:re,required:o,includeReadOnly:x,includeWriteOnly:k}));default:return u.createElement(L,Yr()({},this.props,{getComponent:e,getConfigs:r,schema:a,name:p,deprecated:re,required:o}))}}}class Vd extends u.Component{getSchemaBasePath=()=>this.props.specSelectors.isOAS3()?["components","schemas"]:["definitions"];getCollapsedContent=()=>" ";handleToggle=(e,r)=>{const{layoutActions:n}=this.props;n.show([...this.getSchemaBasePath(),e],r),r&&this.props.specActions.requestResolvedSubtree([...this.getSchemaBasePath(),e])};onLoadModels=e=>{e&&this.props.layoutActions.readyToScroll(this.getSchemaBasePath(),e)};onLoadModel=e=>{if(e){const r=e.getAttribute("data-name");this.props.layoutActions.readyToScroll([...this.getSchemaBasePath(),r],e)}};render(){let{specSelectors:e,getComponent:r,layoutSelectors:n,layoutActions:a,getConfigs:o}=this.props,p=e.definitions(),{docExpansion:c,defaultModelsExpandDepth:d}=o();if(!p.size||d<0)return null;const E=this.getSchemaBasePath();let x=n.isShown(E,d>0&&c!=="none");const k=e.isOAS3(),P=r("ModelWrapper"),R=r("Collapse"),L=r("ModelCollapse"),V=r("JumpToPath",!0),Q=r("ArrowUpIcon"),J=r("ArrowDownIcon");return u.createElement("section",{className:x?"models is-open":"models",ref:this.onLoadModels},u.createElement("h4",null,u.createElement("button",{"aria-expanded":x,className:"models-control",onClick:()=>a.show(E,!x)},u.createElement("span",null,k?"Schemas":"Models"),x?u.createElement(Q,null):u.createElement(J,null))),u.createElement(R,{isOpened:x},p.entrySeq().map((([re])=>{const me=[...E,re],le=H.default.List(me),ve=e.specResolvedSubtree(me),be=e.specJson().getIn(me),ce=H.Map.isMap(ve)?ve:H.default.Map(),_e=H.Map.isMap(be)?be:H.default.Map(),Ae=ce.get("title")||_e.get("title")||re,xe=n.isShown(me,!1);xe&&ce.size===0&&_e.size>0&&this.props.specActions.requestResolvedSubtree(me);const Oe=u.createElement(P,{name:re,expandDepth:d,schema:ce||H.default.Map(),displayName:Ae,fullPath:me,specPath:le,getComponent:r,specSelectors:e,getConfigs:o,layoutSelectors:n,layoutActions:a,includeReadOnly:!0,includeWriteOnly:!0}),je=u.createElement("span",{className:"model-box"},u.createElement("strong",{className:"model model-title"},Ae));return u.createElement("div",{id:`model-${re}`,className:"model-container",key:`models-section-${re}`,"data-name":re,ref:this.onLoadModel},u.createElement("span",{className:"models-jump-to-path"},u.createElement(V,{path:le})),u.createElement(L,{classes:"model-box",collapsedContent:this.getCollapsedContent(re),onToggle:this.handleToggle,title:je,displayName:Ae,modelName:re,specPath:le,layoutSelectors:n,layoutActions:a,hideSelfOnExpand:!0,expanded:d>0&&xe},Oe))})).toArray()))}}const Jd=({value:t,getComponent:e})=>{let r=e("ModelCollapse"),n=u.createElement("span",null,"Array [ ",t.count()," ]");return u.createElement("span",{className:"prop-enum"},"Enum:",u.createElement("br",null),u.createElement(r,{collapsedContent:n},"[ ",t.map(String).join(", ")," ]"))};function fu(t){return t.match(/^(?:[a-z]+:)?\/\//i)}function Wd(t,e){return t?fu(t)?(function(n){return n.match(/^\/\//i)?`${window.location.protocol}${n}`:n})(t):new URL(t,e).href:e}function ba(t,e,{selectedServer:r=""}={}){try{return(function(a,o,{selectedServer:p=""}={}){if(!a)return;if(fu(a))return a;const c=Wd(p,o);return fu(c)?new URL(a,c).href:new URL(a,window.location.href).href})(t,e,{selectedServer:r})}catch{return}}function Dr(t){if(typeof t!="string"||t.trim()==="")return"";const e=t.trim(),r="about:blank";try{const n=`https://base${String(Math.random()).slice(2)}`,a=new URL(e,n),o=a.protocol.slice(0,-1);return["javascript","data","vbscript"].includes(o.toLowerCase())?r:a.origin===n?e.startsWith("/")?`${a.pathname}${a.search}${a.hash}`:e.startsWith("./")||e.startsWith("../")?`${e.match(/^(\.\.?\/)+/)[0]}${a.pathname.substring(1)}${a.search}${a.hash}`:`${a.pathname.substring(1)}${a.search}${a.hash}`:String(a)}catch{return r}}class ka extends u.Component{render(){let{schema:e,name:r,displayName:n,isRef:a,getComponent:o,getConfigs:p,depth:c,onToggle:d,expanded:E,specPath:x,...k}=this.props,{specSelectors:P,expandDepth:R,includeReadOnly:L,includeWriteOnly:V}=k;const{isOAS3:Q}=P,J=c>2||c===2&&x.last()!=="items";if(!e)return null;const{showExtensions:re}=p(),me=re?gr(e):(0,H.List)();let le=e.get("description"),ve=e.get("properties"),be=e.get("additionalProperties"),ce=e.get("title")||n||r,_e=e.get("required"),Ae=e.filter(((pr,W)=>["maxProperties","minProperties","nullable","example"].indexOf(W)!==-1)),xe=e.get("deprecated"),Oe=e.getIn(["externalDocs","url"]),je=e.getIn(["externalDocs","description"]);const G=o("JumpToPath",!0),de=o("Markdown",!0),Ne=o("Model"),Ye=o("ModelCollapse"),ht=o("Property"),Ct=o("Link"),_t=o("ModelExtensions"),qt=()=>u.createElement("span",{className:"model-jump-to-path"},u.createElement(G,{path:x})),It=u.createElement("span",null,u.createElement("span",null,"{"),"...",u.createElement("span",null,"}"),a?u.createElement(qt,null):""),Ot=P.isOAS3()?e.get("allOf"):null,et=P.isOAS3()?e.get("anyOf"):null,Dt=P.isOAS3()?e.get("oneOf"):null,or=P.isOAS3()?e.get("not"):null,Ut=ce&&u.createElement("strong",{className:"model-title"},a&&e.get("$$ref")&&u.createElement("span",{className:Xr()("model-hint",{"model-hint--embedded":J})},e.get("$$ref")),u.createElement("span",{className:"model-title__text"},ce));return u.createElement("span",{className:"model"},u.createElement(Ye,{modelName:r,title:Ut,onToggle:d,expanded:!!E||c<=R,collapsedContent:It},u.createElement("span",{className:"brace-open object"},"{"),a?u.createElement(qt,null):null,u.createElement("span",{className:"inner-object"},u.createElement("table",{className:"model"},u.createElement("tbody",null,le?u.createElement("tr",{className:"description"},u.createElement("td",null,"description:"),u.createElement("td",null,u.createElement(de,{source:le}))):null,Oe&&u.createElement("tr",{className:"external-docs"},u.createElement("td",null,"externalDocs:"),u.createElement("td",null,u.createElement(Ct,{target:"_blank",href:Dr(Oe)},je||Oe))),xe?u.createElement("tr",{className:"property"},u.createElement("td",null,"deprecated:"),u.createElement("td",null,"true")):null,ve&&ve.size?ve.entrySeq().filter((([,pr])=>(!pr.get("readOnly")||L)&&(!pr.get("writeOnly")||V))).map((([pr,W])=>{let $e=Q()&&W.get("deprecated"),Je=H.List.isList(_e)&&_e.contains(pr),Ft=["property-row"];return $e&&Ft.push("deprecated"),Je&&Ft.push("required"),u.createElement("tr",{key:pr,className:Ft.join(" ")},u.createElement("td",null,pr,Je&&u.createElement("span",{className:"star"},"*")),u.createElement("td",null,u.createElement(Ne,Yr()({key:`object-${r}-${pr}_${W}`},k,{required:Je,getComponent:o,specPath:x.push("properties",pr),getConfigs:p,schema:W,depth:c+1}))))})).toArray():null,me.size===0?null:u.createElement(u.Fragment,null,u.createElement("tr",null,u.createElement("td",null," ")),u.createElement(_t,{extensions:me,propClass:"extension"})),be&&be.size?u.createElement("tr",null,u.createElement("td",null,"< * >:"),u.createElement("td",null,u.createElement(Ne,Yr()({},k,{required:!1,getComponent:o,specPath:x.push("additionalProperties"),getConfigs:p,schema:be,depth:c+1})))):null,Ot?u.createElement("tr",null,u.createElement("td",null,"allOf ->"),u.createElement("td",null,Ot.map(((pr,W)=>u.createElement("div",{key:W},u.createElement(Ne,Yr()({},k,{required:!1,getComponent:o,specPath:x.push("allOf",W),getConfigs:p,schema:pr,depth:c+1}))))))):null,et?u.createElement("tr",null,u.createElement("td",null,"anyOf ->"),u.createElement("td",null,et.map(((pr,W)=>u.createElement("div",{key:W},u.createElement(Ne,Yr()({},k,{required:!1,getComponent:o,specPath:x.push("anyOf",W),getConfigs:p,schema:pr,depth:c+1}))))))):null,Dt?u.createElement("tr",null,u.createElement("td",null,"oneOf ->"),u.createElement("td",null,Dt.map(((pr,W)=>u.createElement("div",{key:W},u.createElement(Ne,Yr()({},k,{required:!1,getComponent:o,specPath:x.push("oneOf",W),getConfigs:p,schema:pr,depth:c+1}))))))):null,or?u.createElement("tr",null,u.createElement("td",null,"not ->"),u.createElement("td",null,u.createElement("div",null,u.createElement(Ne,Yr()({},k,{required:!1,getComponent:o,specPath:x.push("not"),getConfigs:p,schema:or,depth:c+1}))))):null))),u.createElement("span",{className:"brace-close"},"}")),Ae.size?Ae.entrySeq().map((([pr,W])=>u.createElement(ht,{key:`${pr}-${W}`,propKey:pr,propVal:W,propClass:"property"}))):null)}}class Aa extends u.Component{render(){let{getComponent:e,getConfigs:r,schema:n,depth:a,expandDepth:o,name:p,displayName:c,specPath:d}=this.props,E=n.get("description"),x=n.get("items"),k=n.get("title")||c||p,P=n.filter(((ve,be)=>["type","items","description","$$ref","externalDocs"].indexOf(be)===-1)),R=n.getIn(["externalDocs","url"]),L=n.getIn(["externalDocs","description"]);const V=e("Markdown",!0),Q=e("ModelCollapse"),J=e("Model"),re=e("Property"),me=e("Link"),le=k&&u.createElement("strong",{className:"model-title"},u.createElement("span",{className:"model-title__text"},k));return u.createElement("span",{className:"model"},u.createElement(Q,{title:le,expanded:a<=o,collapsedContent:"[...]"},"[",P.size?P.entrySeq().map((([ve,be])=>u.createElement(re,{key:`${ve}-${be}`,propKey:ve,propVal:be,propClass:"property"}))):null,E?u.createElement(V,{source:E}):P.size?u.createElement("div",{className:"markdown"}):null,R&&u.createElement("div",{className:"external-docs"},u.createElement(me,{target:"_blank",href:Dr(R)},L||R)),u.createElement("span",null,u.createElement(J,Yr()({},this.props,{getConfigs:r,specPath:d.push("items"),name:null,schema:x,required:!1,depth:a+1}))),"]"))}}const mc="property primitive";class wp extends u.Component{render(){let{schema:e,getComponent:r,getConfigs:n,name:a,displayName:o,depth:p,expandDepth:c}=this.props;const{showExtensions:d}=n();if(!e||!e.get)return u.createElement("div",null);let E=e.get("type"),x=e.get("format"),k=e.get("xml"),P=e.get("enum"),R=e.get("title")||o||a,L=e.get("description");const V=gr(e);let Q=e.filter(((xe,Oe)=>["enum","type","format","description","$$ref","externalDocs"].indexOf(Oe)===-1)).filterNot(((xe,Oe)=>V.has(Oe))),J=e.getIn(["externalDocs","url"]),re=e.getIn(["externalDocs","description"]);const me=r("Markdown",!0),le=r("EnumModel"),ve=r("Property"),be=r("ModelCollapse"),ce=r("Link"),_e=r("ModelExtensions"),Ae=R&&u.createElement("strong",{className:"model-title"},u.createElement("span",{className:"model-title__text"},R));return u.createElement("span",{className:"model"},u.createElement(be,{title:Ae,expanded:p<=c,collapsedContent:"[...]"},u.createElement("span",{className:"prop"},a&&p>1&&u.createElement("span",{className:"prop-name"},R),u.createElement("span",{className:"prop-type"},E),x&&u.createElement("span",{className:"prop-format"},"($",x,")"),Q.size?Q.entrySeq().map((([xe,Oe])=>u.createElement(ve,{key:`${xe}-${Oe}`,propKey:xe,propVal:Oe,propClass:mc}))):null,d&&V.size>0?u.createElement(_e,{extensions:V,propClass:`${mc} extension`}):null,L?u.createElement(me,{source:L}):null,J&&u.createElement("div",{className:"external-docs"},u.createElement(ce,{target:"_blank",href:Dr(J)},re||J)),k&&k.size?u.createElement("span",null,u.createElement("br",null),u.createElement("span",{className:mc},"xml:"),k.entrySeq().map((([xe,Oe])=>u.createElement("span",{key:`${xe}-${Oe}`,className:mc},u.createElement("br",null),"   ",xe,": ",String(Oe)))).toArray()):null,P&&u.createElement(le,{value:P,getComponent:r}))))}}class Ep extends u.Component{UNSAFE_componentWillMount(){let{schemes:e}=this.props;this.setScheme(e.first())}UNSAFE_componentWillReceiveProps(e){this.props.currentScheme&&e.schemes.includes(this.props.currentScheme)||this.setScheme(e.schemes.first())}onChange=e=>{this.setScheme(e.target.value)};setScheme=e=>{let{path:r,method:n,specActions:a}=this.props;a.setScheme(e,r,n)};render(){let{schemes:e,currentScheme:r}=this.props;return u.createElement("label",{htmlFor:"schemes"},u.createElement("span",{className:"schemes-title"},"Schemes"),u.createElement("select",{onChange:this.onChange,value:r,id:"schemes"},e.valueSeq().map((n=>u.createElement("option",{value:n,key:n},n))).toArray()))}}class s0 extends u.Component{render(){const{specActions:e,specSelectors:r,getComponent:n}=this.props,a=r.operationScheme(),o=r.schemes(),p=n("schemes");return o&&o.size?u.createElement(p,{currentScheme:a,schemes:o,specActions:e}):null}}var Qa=Be(24677),bh=Be.n(Qa);const Is={value:"",onChange:()=>{},schema:{},keyName:"",required:!1,errors:(0,H.List)()};class Hd extends u.Component{static defaultProps=Is;componentDidMount(){const{dispatchInitialValue:e,value:r,onChange:n}=this.props;e?n(r):e===!1&&n("")}render(){let{schema:e,errors:r,value:n,onChange:a,getComponent:o,fn:p,disabled:c}=this.props;const d=e&&e.get?e.get("format"):null,E=e&&e.get?e.get("type"):null,x=p.getSchemaObjectType(e),k=p.isFileUploadIntended(e);let P=L=>o(L,!1,{failSilently:!0}),R=E?P(d?`JsonSchema_${E}_${d}`:`JsonSchema_${E}`):o("JsonSchema_string");return k||!H.List.isList(E)||x!=="array"&&x!=="object"||(R=o("JsonSchema_object")),R||(R=o("JsonSchema_string")),u.createElement(R,Yr()({},this.props,{errors:r,fn:p,getComponent:o,value:n,onChange:a,schema:e,disabled:c}))}}class Xi extends u.Component{static defaultProps=Is;onChange=e=>{const r=this.props.schema&&this.props.schema.get("type")==="file"?e.target.files[0]:e.target.value;this.props.onChange(r,this.props.keyName)};onEnumChange=e=>this.props.onChange(e);render(){let{getComponent:e,value:r,schema:n,errors:a,required:o,description:p,disabled:c}=this.props;const d=n&&n.get?n.get("enum"):null,E=n&&n.get?n.get("format"):null,x=n&&n.get?n.get("type"):null,k=n&&n.get?n.get("in"):null;if(r?(Cr(r)||typeof r=="object")&&(r=Gr(r)):r="",a=a.toJS?a.toJS():[],d){const L=e("Select");return u.createElement(L,{className:a.length?"invalid":"",title:a.length?a:"",allowedValues:[...d],value:r,allowEmptyValue:!o,disabled:c,onChange:this.onEnumChange})}const P=c||k&&k==="formData"&&!("FormData"in window),R=e("Input");return x&&x==="file"?u.createElement(R,{type:"file",className:a.length?"invalid":"",title:a.length?a:"",onChange:this.onChange,disabled:P}):u.createElement(bh(),{type:E&&E==="password"?"password":"text",className:a.length?"invalid":"",title:a.length?a:"",value:r,minLength:0,debounceTimeout:350,placeholder:p,onChange:this.onChange,disabled:P})}}class ii extends u.PureComponent{static defaultProps=Is;constructor(e,r){super(e,r),this.state={value:Eh(e.value),schema:e.schema}}UNSAFE_componentWillReceiveProps(e){const r=Eh(e.value);r!==this.state.value&&this.setState({value:r}),e.schema!==this.state.schema&&this.setState({schema:e.schema})}onChange=()=>{this.props.onChange(this.state.value)};onItemChange=(e,r)=>{this.setState((({value:n})=>({value:n.set(r,e)})),this.onChange)};removeItem=e=>{this.setState((({value:r})=>({value:r.delete(e)})),this.onChange)};addItem=()=>{const{fn:e}=this.props;let r=Eh(this.state.value);this.setState((()=>({value:r.push(e.getSampleSchema(this.state.schema.get("items"),!1,{includeWriteOnly:!0}))})),this.onChange)};onEnumChange=e=>{this.setState((()=>({value:e})),this.onChange)};render(){let{getComponent:e,required:r,schema:n,errors:a,fn:o,disabled:p}=this.props;a=a.toJS?a.toJS():Array.isArray(a)?a:[];const c=a.filter((ve=>typeof ve=="string")),d=a.filter((ve=>ve.needRemove!==void 0)).map((ve=>ve.error)),E=this.state.value,x=!!(E&&E.count&&E.count()>0),k=n.getIn(["items","enum"]),P=n.get("items"),R=o.getSchemaObjectType(P),L=o.getSchemaObjectTypeLabel(P),V=n.getIn(["items","format"]),Q=n.get("items");let J,re=!1,me=R==="file"||R==="string"&&V==="binary";if(R&&V?J=e(`JsonSchema_${R}_${V}`):R!=="boolean"&&R!=="array"&&R!=="object"||(J=e(`JsonSchema_${R}`)),!H.List.isList(P?.get("type"))||R!=="array"&&R!=="object"||(J=e("JsonSchema_object")),J||me||(re=!0),k){const ve=e("Select");return u.createElement(ve,{className:a.length?"invalid":"",title:a.length?a:"",multiple:!0,value:E,disabled:p,allowedValues:k,allowEmptyValue:!r,onChange:this.onEnumChange})}const le=e("Button");return u.createElement("div",{className:"json-schema-array"},x?E.map(((ve,be)=>{const ce=(0,H.fromJS)([...a.filter((_e=>_e.index===be)).map((_e=>_e.error))]);return u.createElement("div",{key:be,className:"json-schema-form-item"},me?u.createElement(wh,{value:ve,onChange:_e=>this.onItemChange(_e,be),disabled:p,errors:ce,getComponent:e}):re?u.createElement(mu,{value:ve,onChange:_e=>this.onItemChange(_e,be),disabled:p,errors:ce}):u.createElement(J,Yr()({},this.props,{value:ve,onChange:_e=>this.onItemChange(_e,be),disabled:p,errors:ce,schema:Q,getComponent:e,fn:o})),p?null:u.createElement(le,{className:`btn btn-sm json-schema-form-item-remove ${d.length?"invalid":null}`,title:d.length?d:"",onClick:()=>this.removeItem(be)}," - "))})):null,p?null:u.createElement(le,{className:`btn btn-sm json-schema-form-item-add ${c.length?"invalid":null}`,title:c.length?c:"",onClick:this.addItem},"Add ",L," item"))}}class mu extends u.Component{static defaultProps=Is;onChange=e=>{const r=e.target.value;this.props.onChange(r,this.props.keyName)};render(){let{value:e,errors:r,description:n,disabled:a}=this.props;return e?(Cr(e)||typeof e=="object")&&(e=Gr(e)):e="",r=r.toJS?r.toJS():[],u.createElement(bh(),{type:"text",className:r.length?"invalid":"",title:r.length?r:"",value:e,minLength:0,debounceTimeout:350,placeholder:n,onChange:this.onChange,disabled:a})}}class wh extends u.Component{static defaultProps=Is;onFileChange=e=>{const r=e.target.files[0];this.props.onChange(r,this.props.keyName)};render(){let{getComponent:e,errors:r,disabled:n}=this.props;const a=e("Input"),o=n||!("FormData"in window);return u.createElement(a,{type:"file",className:r.length?"invalid":"",title:r.length?r:"",onChange:this.onFileChange,disabled:o})}}class Kd extends u.Component{static defaultProps=Is;onEnumChange=e=>this.props.onChange(e);render(){let{getComponent:e,value:r,errors:n,schema:a,required:o,disabled:p}=this.props;n=n.toJS?n.toJS():[];let c=a&&a.get?a.get("enum"):null,d=!c||!o,E=!c&&["true","false"];const x=e("Select");return u.createElement(x,{className:n.length?"invalid":"",title:n.length?n:"",value:String(r),disabled:p,allowedValues:c?[...c]:E,allowEmptyValue:d,onChange:this.onEnumChange})}}const ig=t=>t.map((e=>{const r=e.propKey!==void 0?e.propKey:e.index;let n=typeof e=="string"?e:typeof e.error=="string"?e.error:null;if(!r&&n)return n;let a=e.error,o=`/${e.propKey}`;for(;typeof a=="object";){const p=a.propKey!==void 0?a.propKey:a.index;if(p===void 0||(o+=`/${p}`,!a.error))break;a=a.error}return`${o}: ${a}`}));class sg extends u.PureComponent{constructor(){super()}static defaultProps=Is;onChange=e=>{this.props.onChange(e)};handleOnChange=e=>{const r=e.target.value;this.onChange(r)};render(){let{getComponent:e,value:r,errors:n,disabled:a}=this.props;const o=e("TextArea");return n=n.toJS?n.toJS():Array.isArray(n)?n:[],u.createElement("div",null,u.createElement(o,{className:Xr()({invalid:n.length}),title:n.length?ig(n).join(", "):"",value:Gr(r),disabled:a,onChange:this.handleOnChange}))}}function Eh(t){return H.List.isList(t)?t:Array.isArray(t)?(0,H.fromJS)(t):(0,H.List)()}const lg=({extensions:t,propClass:e=""})=>t.entrySeq().map((([r,n])=>{const a=Mr(n)??null;return u.createElement("tr",{key:r,className:e},u.createElement("td",null,r),u.createElement("td",null,JSON.stringify(a)))})).toArray();var cg=Be(11331),Ai=Be.n(cg);const ug=(t,e)=>{const r=H.Map.isMap(t);if(!r&&!Ai()(t))return!1;const n=r?t.get("type"):t.type;return e===n||Array.isArray(e)&&e.includes(n)},Gd=(t,e=new WeakSet)=>{if(t==null||e.has(t))return"any";e.add(t);const{type:r,items:n}=t;return Object.hasOwn(t,"items")?n?`array<${Gd(n,e)}>`:"array":r},Sp=t=>Gd(Mr(t)),Yd=()=>({components:{modelExample:Ps,ModelWrapper:kl,ModelCollapse:gp,Model:vh,Models:Vd,EnumModel:Jd,ObjectModel:ka,ArrayModel:Aa,PrimitiveModel:wp,ModelExtensions:lg,schemes:Ep,SchemesContainer:s0,...I},fn:{hasSchemaType:ug,getSchemaObjectTypeLabel:Sp}});var Sh=Be(19123),xp=Be.n(Sh),pg=Be(41859),xh=Be.n(pg),hg=Be(62193),Ci=Be.n(hg);const _h=t=>e=>Array.isArray(t)&&Array.isArray(e)&&t.length===e.length&&t.every(((r,n)=>r===e[n])),dg=(...t)=>t;class Xd extends Map{delete(e){const r=Array.from(this.keys()).find(_h(e));return super.delete(r)}get(e){const r=Array.from(this.keys()).find(_h(e));return super.get(r)}has(e){return Array.from(this.keys()).findIndex(_h(e))!==-1}}const gu=(t,e=dg)=>{const{Cache:r}=Pt();Pt().Cache=Xd;const n=Pt()(t,e);return Pt().Cache=r,n},Qd={string:t=>t.pattern?(e=>{try{const r=new RegExp("(?<=(?"user@example.com","string_date-time":()=>new Date().toISOString(),string_date:()=>new Date().toISOString().substring(0,10),string_time:()=>new Date().toISOString().substring(11),string_uuid:()=>"3fa85f64-5717-4562-b3fc-2c963f66afa6",string_hostname:()=>"example.com",string_ipv4:()=>"198.51.100.42",string_ipv6:()=>"2001:0db8:5b96:0000:0000:426f:8e17:642a",number:()=>0,number_float:()=>0,integer:()=>0,boolean:t=>typeof t.default!="boolean"||t.default},Zd=t=>{t=Nn(t);let{type:e,format:r}=t,n=Qd[`${e}_${r}`]||Qd[e];return ia(n)?n(t):"Unknown Type: "+t.type},l0=t=>Vr(t,"$$ref",(e=>typeof e=="string"&&e.indexOf("#")>-1)),ef=["maxProperties","minProperties"],fg=["minItems","maxItems"],mg=["minimum","maximum","exclusiveMinimum","exclusiveMaximum"],gg=["minLength","maxLength"],gc=(t,e,r={})=>{const n={...t};if(["example","default","enum","xml","type",...ef,...fg,...mg,...gg].forEach((a=>(o=>{n[o]===void 0&&e[o]!==void 0&&(n[o]=e[o])})(a))),e.required!==void 0&&Array.isArray(e.required)&&(n.required!==void 0&&n.required.length||(n.required=[]),e.required.forEach((a=>{n.required.includes(a)||n.required.push(a)}))),e.properties){n.properties||(n.properties={});let a=Nn(e.properties);for(let o in a)Object.prototype.hasOwnProperty.call(a,o)&&(a[o]&&a[o].deprecated||a[o]&&a[o].readOnly&&!r.includeReadOnly||a[o]&&a[o].writeOnly&&!r.includeWriteOnly||n.properties[o]||(n.properties[o]=a[o],!e.required&&Array.isArray(e.required)&&e.required.indexOf(o)!==-1&&(n.required?n.required.push(o):n.required=[o])))}return e.items&&(n.items||(n.items={}),n.items=gc(n.items,e.items,r)),n},Yo=(t,e={},r=void 0,n=!1)=>{t&&ia(t.toJS)&&(t=t.toJS());let a=r!==void 0||t&&t.example!==void 0||t&&t.default!==void 0;const o=!a&&t&&t.oneOf&&t.oneOf.length>0,p=!a&&t&&t.anyOf&&t.anyOf.length>0;if(!a&&(o||p)){const G=Nn(o?t.oneOf[0]:t.anyOf[0]);if(!(t=gc(t,G,e)).xml&&G.xml&&(t.xml=G.xml),t.example!==void 0&&G.example!==void 0)a=!0;else if(G.properties){t.properties||(t.properties={});let de=Nn(G.properties);for(let Ne in de)Object.prototype.hasOwnProperty.call(de,Ne)&&(de[Ne]&&de[Ne].deprecated||de[Ne]&&de[Ne].readOnly&&!e.includeReadOnly||de[Ne]&&de[Ne].writeOnly&&!e.includeWriteOnly||t.properties[Ne]||(t.properties[Ne]=de[Ne],!G.required&&Array.isArray(G.required)&&G.required.indexOf(Ne)!==-1&&(t.required?t.required.push(Ne):t.required=[Ne])))}}const c={};let{xml:d,type:E,example:x,properties:k,additionalProperties:P,items:R}=t||{},{includeReadOnly:L,includeWriteOnly:V}=e;d=d||{};let Q,{name:J,prefix:re,namespace:me}=d,le={};n&&(J=J||"notagname",Q=(re?re+":":"")+J,me)&&(c[re?"xmlns:"+re:"xmlns"]=me),n&&(le[Q]=[]);const ve=G=>G.some((de=>Object.prototype.hasOwnProperty.call(t,de)));t&&!E&&(k||P||ve(ef)?E="object":R||ve(fg)?E="array":ve(mg)?(E="number",t.type="number"):a||t.enum||(E="string",t.type="string"));const be=G=>{if(t?.maxItems!=null&&(G=G.slice(0,t?.maxItems)),t?.minItems!=null){let de=0;for(;G.lengtht&&t.maxProperties!==null&&t.maxProperties!==void 0&&Ae>=t.maxProperties,Oe=G=>!t||t.maxProperties===null||t.maxProperties===void 0||!xe()&&(!(de=>!(t&&t.required&&t.required.length&&t.required.includes(de)))(G)||t.maxProperties-Ae-(()=>{if(!t||!t.required)return 0;let de=0;return n?t.required.forEach((Ne=>de+=le[Ne]===void 0?0:1)):t.required.forEach((Ne=>de+=le[Q]?.find((Ye=>Ye[Ne]!==void 0))===void 0?0:1)),t.required.length-de})()>0);if(_e=n?(G,de=void 0)=>{if(t&&ce[G]){if(ce[G].xml=ce[G].xml||{},ce[G].xml.attribute){const Ye=Array.isArray(ce[G].enum)?ce[G].enum[0]:void 0,ht=ce[G].example,Ct=ce[G].default;return void(c[ce[G].xml.name||G]=ht!==void 0?ht:Ct!==void 0?Ct:Ye!==void 0?Ye:Zd(ce[G]))}ce[G].xml.name=ce[G].xml.name||G}else ce[G]||P===!1||(ce[G]={xml:{name:G}});let Ne=Yo(t&&ce[G]||void 0,e,de,n);Oe(G)&&(Ae++,Array.isArray(Ne)?le[Q]=le[Q].concat(Ne):le[Q].push(Ne))}:(G,de)=>{if(Oe(G)){if(Object.prototype.hasOwnProperty.call(t,"discriminator")&&t.discriminator&&Object.prototype.hasOwnProperty.call(t.discriminator,"mapping")&&t.discriminator.mapping&&Object.prototype.hasOwnProperty.call(t,"$$ref")&&t.$$ref&&t.discriminator.propertyName===G){for(let Ne in t.discriminator.mapping)if(t.$$ref.search(t.discriminator.mapping[Ne])!==-1){le[G]=Ne;break}}else le[G]=Yo(ce[G],e,de,n);Ae++}},a){let G;if(G=l0(r!==void 0?r:x!==void 0?x:t.default),!n){if(typeof G=="number"&&E==="string")return`${G}`;if(typeof G!="string"||E==="string")return G;try{return JSON.parse(G)}catch{return G}}if(t||(E=Array.isArray(G)?"array":typeof G),E==="array"){if(!Array.isArray(G)){if(typeof G=="string")return G;G=[G]}const de=t?t.items:void 0;de&&(de.xml=de.xml||d||{},de.xml.name=de.xml.name||d.name);let Ne=G.map((Ye=>Yo(de,e,Ye,n)));return Ne=be(Ne),d.wrapped?(le[Q]=Ne,Ci()(c)||le[Q].push({_attr:c})):le=Ne,le}if(E==="object"){if(typeof G=="string")return G;for(let de in G)Object.prototype.hasOwnProperty.call(G,de)&&(t&&ce[de]&&ce[de].readOnly&&!L||t&&ce[de]&&ce[de].writeOnly&&!V||(t&&ce[de]&&ce[de].xml&&ce[de].xml.attribute?c[ce[de].xml.name||de]=G[de]:_e(de,G[de])));return Ci()(c)||le[Q].push({_attr:c}),le}return le[Q]=Ci()(c)?G:[{_attr:c},G],le}if(E==="object"){for(let G in ce)Object.prototype.hasOwnProperty.call(ce,G)&&(ce[G]&&ce[G].deprecated||ce[G]&&ce[G].readOnly&&!L||ce[G]&&ce[G].writeOnly&&!V||_e(G));if(n&&c&&le[Q].push({_attr:c}),xe())return le;if(P===!0)n?le[Q].push({additionalProp:"Anything can be here"}):le.additionalProp1={},Ae++;else if(P){const G=Nn(P),de=Yo(G,e,void 0,n);if(n&&G.xml&&G.xml.name&&G.xml.name!=="notagname")le[Q].push(de);else{const Ne=G["x-additionalPropertiesName"]||"additionalProp",Ye=t.minProperties!==null&&t.minProperties!==void 0&&AeYo(gc(de,R,e),e,void 0,n)));else if(Array.isArray(R.oneOf))G=R.oneOf.map((de=>Yo(gc(de,R,e),e,void 0,n)));else{if(!(!n||n&&d.wrapped))return Yo(R,e,void 0,n);G=[Yo(R,e,void 0,n)]}return G=be(G),n&&d.wrapped?(le[Q]=G,Ci()(c)||le[Q].push({_attr:c}),le):G}let je;if(t&&Array.isArray(t.enum))je=fn(t.enum)[0];else{if(!t)return;if(je=Zd(t),typeof je=="number"){let G=t.minimum;G!=null&&(t.exclusiveMinimum&&G++,je=G);let de=t.maximum;de!=null&&(t.exclusiveMaximum&&de--,je=de)}if(typeof je=="string"&&(t.maxLength!==null&&t.maxLength!==void 0&&(je=je.slice(0,t.maxLength)),t.minLength!==null&&t.minLength!==void 0)){let G=0;for(;je.length(t.schema&&(t=t.schema),t.properties&&(t.type="object"),t),tf=(t,e,r)=>{const n=Yo(t,e,r,!0);if(n)return typeof n=="string"?n:xp()(n,{declaration:!0,indent:" "})},rf=(t,e,r)=>Yo(t,e,r,!1),yg=(t,e,r)=>[t,JSON.stringify(e),JSON.stringify(r)],si=gu(tf,yg),Oh=gu(rf,yg),kh=t=>Mr(t)?.type??"string",vg=[{when:/json/,shouldStringifyTypes:["string"]}],bg=["object"],wg=t=>(e,r,n,a)=>{const{fn:o}=t(),p=o.memoizedSampleFromSchema(e,r,a),c=typeof p,d=vg.reduce(((E,x)=>x.when.test(n)?[...E,...x.shouldStringifyTypes]:E),bg);return Wr()(d,(E=>E===c))?JSON.stringify(p,null,2):p},nf=t=>(e,r,n,a)=>{const{fn:o}=t(),p=o.getJsonSampleSchema(e,r,n,a);let c;try{c=Pe.dump(Pe.load(p),{lineWidth:-1},{schema:Rt}),c[c.length-1]===` +`&&(c=c.slice(0,c.length-1))}catch(d){return console.error(d),"error: could not generate yaml example"}return c.replace(/\t/g," ")},yu=t=>(e,r,n)=>{const{fn:a}=t();if(e&&!e.xml&&(e.xml={}),e&&!e.xml.name){if(!e.$$ref&&(e.type||e.items||e.properties||e.additionalProperties))return` +`;if(e.$$ref){let o=e.$$ref.match(/\S*\/(\S+)$/);e.xml.name=o[1]}}return a.memoizedCreateXMLExample(e,r,n)},af=t=>(e,r="",n={},a=void 0)=>{const{fn:o}=t();return typeof a?.toJS=="function"&&(a=a.toJS()),/xml/.test(r)?(typeof e?.toJS=="function"&&(e=e.toJS()),o.getXmlSampleSchema(e,n,a)):/(yaml|yml)/.test(r)?o.getYamlSampleSchema(e,n,r,a):o.getJsonSampleSchema(e,n,r,a)},Ah=({getSystem:t})=>{const e=wg(t),r=nf(t),n=yu(t),a=af(t);return{fn:{jsonSchema5:{inferSchema:_p,sampleFromSchema:rf,sampleFromSchemaGeneric:Yo,createXMLExample:tf,memoizedSampleFromSchema:Oh,memoizedCreateXMLExample:si,getJsonSampleSchema:e,getYamlSampleSchema:r,getXmlSampleSchema:n,getSampleSchema:a,mergeJsonSchema:gc},inferSchema:_p,sampleFromSchema:rf,sampleFromSchemaGeneric:Yo,createXMLExample:tf,memoizedSampleFromSchema:Oh,memoizedCreateXMLExample:si,getJsonSampleSchema:e,getYamlSampleSchema:r,getXmlSampleSchema:n,getSampleSchema:a,mergeJsonSchema:gc,getSchemaObjectType:kh}}};var c0=Be(37334),vu=Be.n(c0);const Eg=["get","put","post","delete","options","head","patch","trace","query"],ji=t=>t||(0,H.Map)(),Sg=yr(ji,(t=>t.get("lastError"))),xg=yr(ji,(t=>t.get("url"))),u0=yr(ji,(t=>t.get("spec")||"")),Do=yr(ji,(t=>t.get("specSource")||"not-editor")),Za=yr(ji,(t=>t.get("json",(0,H.Map)()))),_g=yr(Za,(t=>t.toJS())),Og=yr(ji,(t=>t.get("resolved",(0,H.Map)()))),kg=(t,e)=>t.getIn(["resolvedSubtrees",...e],void 0),of=(t,e)=>H.Map.isMap(t)&&H.Map.isMap(e)?e.get("$$ref")?e:(0,H.OrderedMap)().mergeWith(of,t,e):e,li=yr(ji,(t=>(0,H.OrderedMap)().mergeWith(of,t.get("json"),t.get("resolvedSubtrees")))),eo=t=>Za(t),Ag=yr(eo,(()=>!1)),Ch=yr(eo,(t=>gf(t&&t.get("info")))),Cg=yr(eo,(t=>gf(t&&t.get("externalDocs")))),sf=yr(Ch,(t=>t&&t.get("version"))),jg=yr(sf,(t=>/v?([0-9]*)\.([0-9]*)\.([0-9]*)/i.exec(t).slice(1))),lf=yr(li,(t=>t.get("paths"))),Pg=vu()(["get","put","post","delete","options","head","patch"]),jh=yr(lf,(t=>{let e=(0,H.List)();return!H.Map.isMap(t)||t.isEmpty()||t.forEach(((r,n)=>{if(!r||!r.forEach)return{};r.forEach(((a,o)=>{Eg.indexOf(o)<0||(e=e.push((0,H.fromJS)({path:n,method:o,operation:a,id:`${o}-${n}`,specPath:["paths",n,o]})))}))})),e})),Ph=yr(eo,(t=>(0,H.Set)(t.get("consumes")))),cf=yr(eo,(t=>(0,H.Set)(t.get("produces")))),uf=yr(eo,(t=>t.get("security",(0,H.List)()))),Nh=yr(eo,(t=>t.get("securityDefinitions"))),Ng=(t,e)=>{const r=t.getIn(["resolvedSubtrees","definitions",e],null),n=t.getIn(["json","definitions",e],null);return r||n||null},pf=yr(eo,(t=>{const e=t.get("definitions");return H.Map.isMap(e)?e:(0,H.Map)()})),Tg=yr(eo,(t=>t.get("basePath"))),Th=yr(eo,(t=>t.get("host"))),Ms=yr(eo,(t=>t.get("schemes",(0,H.Map)()))),bu=yr([jh,Ph,cf],((t,e,r)=>t.map((n=>n.update("operation",(a=>H.Map.isMap(a)?a.withMutations((o=>(o.get("consumes")||o.update("consumes",(p=>(0,H.Set)(p).merge(e))),o.get("produces")||o.update("produces",(p=>(0,H.Set)(p).merge(r))),o))):(0,H.Map)())))))),La=yr(eo,(t=>{const e=t.get("tags",(0,H.List)());return H.List.isList(e)?e.filter((r=>H.Map.isMap(r))):(0,H.List)()})),Ig=(t,e)=>(La(t)||(0,H.List)()).filter(H.Map.isMap).find((r=>r.get("name")===e),(0,H.Map)()),Mg=yr(bu,La,((t,e)=>t.reduce(((r,n)=>{let a=(0,H.Set)(n.getIn(["operation","tags"]));return a.count()<1?r.update("default",(0,H.List)(),(o=>o.push(n))):a.reduce(((o,p)=>o.update(p,(0,H.List)(),(c=>c.push(n)))),r)}),e.reduce(((r,n)=>r.set(n.get("name"),(0,H.List)())),(0,H.OrderedMap)())))),Ih=t=>({getConfigs:e})=>{let{tagsSorter:r,operationsSorter:n}=e();return Mg(t).sortBy(((a,o)=>o),((a,o)=>{let p=typeof r=="function"?r:Nr.tagsSorter[r];return p?p(a,o):null})).map(((a,o)=>{let p=typeof n=="function"?n:Nr.operationsSorter[n],c=p?a.sort(p):a;return(0,H.Map)({tagDetails:Ig(t,o),operations:c})}))},$a=yr(ji,(t=>t.get("responses",(0,H.Map)()))),Rg=yr(ji,(t=>t.get("requests",(0,H.Map)()))),cr=yr(ji,(t=>t.get("mutatedRequests",(0,H.Map)()))),wu=(t,e,r)=>$a(t).getIn([e,r],null),Dg=(t,e,r)=>Rg(t).getIn([e,r],null),Fg=(t,e,r)=>cr(t).getIn([e,r],null),Op=()=>!0,Eu=(t,e,r)=>{const n=li(t).getIn(["paths",...e,"parameters"],(0,H.OrderedMap)()),a=t.getIn(["meta","paths",...e,"parameters"],(0,H.OrderedMap)());return n.map((o=>{const p=a.get(`${r.get("in")}.${r.get("name")}`),c=a.get(`${r.get("in")}.${r.get("name")}.hash-${r.hashCode()}`);return(0,H.OrderedMap)().merge(o,p,c)})).find((o=>o.get("in")===r.get("in")&&o.get("name")===r.get("name")),(0,H.OrderedMap)())},Mh=(t,e,r,n)=>{const a=`${n}.${r}`;return t.getIn(["meta","paths",...e,"parameter_inclusions",a],!1)},Lg=(t,e,r,n)=>{const a=li(t).getIn(["paths",...e,"parameters"],(0,H.OrderedMap)()).find((o=>o.get("in")===n&&o.get("name")===r),(0,H.OrderedMap)());return Eu(t,e,a)},hf=(t,e,r)=>{const n=li(t).getIn(["paths",e,r],(0,H.OrderedMap)()),a=t.getIn(["meta","paths",e,r],(0,H.OrderedMap)()),o=n.get("parameters",(0,H.List)()).map((p=>Eu(t,[e,r],p)));return(0,H.OrderedMap)().merge(n,a).set("parameters",o)};function $g(t,e,r,n){return e=e||[],t.getIn(["meta","paths",...e,"parameters"],(0,H.fromJS)([])).find((a=>H.Map.isMap(a)&&a.get("name")===r&&a.get("in")===n))||(0,H.Map)()}const df=yr(eo,(t=>{const e=t.get("host");return typeof e=="string"&&e.length>0&&e[0]!=="/"}));function Rh(t,e,r){return e=e||[],hf(t,...e).get("parameters",(0,H.List)()).reduce(((n,a)=>{let o=r&&a.get("in")==="body"?a.get("value_xml"):a.get("value");return H.List.isList(o)&&(o=o.filter((p=>p!==""))),n.set(sa(a,{allowHashes:!1}),o)}),(0,H.fromJS)({}))}function ho(t,e=""){if(H.List.isList(t))return t.some((r=>H.Map.isMap(r)&&r.get("in")===e))}function ci(t,e=""){if(H.List.isList(t))return t.some((r=>H.Map.isMap(r)&&r.get("type")===e))}function Cl(t,e){e=e||[];let r=li(t).getIn(["paths",...e],(0,H.fromJS)({})),n=t.getIn(["meta","paths",...e],(0,H.fromJS)({})),a=ff(t,e);const o=r.get("parameters")||new H.List,p=n.get("consumes_value")?n.get("consumes_value"):ci(o,"file")?"multipart/form-data":ci(o,"formData")?"application/x-www-form-urlencoded":void 0;return(0,H.fromJS)({requestContentType:p,responseContentType:a})}function ff(t,e){e=e||[];const r=li(t).getIn(["paths",...e],null);if(r===null)return;const n=t.getIn(["meta","paths",...e,"produces_value"],null),a=r.getIn(["produces",0],null);return n||a||"application/json"}function Bg(t,e){e=e||[];const r=li(t),n=r.getIn(["paths",...e],null);if(n===null)return;const[a]=e,o=n.get("produces",null),p=r.getIn(["paths",a,"produces"],null),c=r.getIn(["produces"],null);return o||p||c}function Qi(t,e){e=e||[];const r=li(t),n=r.getIn(["paths",...e],null);if(n===null)return;const[a]=e,o=n.get("consumes",null),p=r.getIn(["paths",a,"consumes"],null),c=r.getIn(["consumes"],null);return o||p||c}const mf=(t,e,r)=>{let n=t.get("url").match(/^([a-z][a-z0-9+\-.]*):/),a=Array.isArray(n)?n[1]:null;return t.getIn(["scheme",e,r])||t.getIn(["scheme","_defaultScheme"])||a||""},Rs=(t,e,r)=>["http","https"].indexOf(mf(t,e,r))>-1,qg=(t,e)=>{e=e||[];const r=t.getIn(["meta","paths",...e,"parameters"],(0,H.fromJS)([])),n=[];if(r.length===0)return n;const a=(o,p=[])=>{const c=(d,E)=>{const x=[...E,d.get("propKey")||d.get("index")];return H.Map.isMap(d.get("error"))?a(d.get("error"),x):{error:d.get("error"),path:x}};return H.List.isList(o)?o.map((d=>H.Map.isMap(d)?c(d,p):{error:d,path:p})):c(o,p)};return r.forEach(((o,p)=>{const c=p.split(".").slice(1,-1).join("."),d=o.get("errors");d&&d.count()&&a(d).forEach((({error:E,path:x})=>{n.push(((k,P,R)=>`For '${R}'${(P=P.reduce(((L,V)=>typeof V=="number"?`${L}[${V}]`:L?`${L}.${V}`:V),""))?` at path '${P}'`:""}: ${k}.`)(E,x,c))}))})),n},Ug=(t,e)=>qg(t,e).length===0,zg=(t,e)=>{let r={requestBody:!1,requestContentType:{}},n=t.getIn(["resolvedSubtrees","paths",...e,"requestBody"],(0,H.fromJS)([]));if(n.size<1)return r;n.getIn(["required"])&&(r.requestBody=n.getIn(["required"]));const a=n.getIn(["content"]);return a&&a.entrySeq().forEach((o=>{const p=o[0];if(o[1].getIn(["schema","required"])){const c=o[1].getIn(["schema","required"]).toJS();r.requestContentType[p]=c}})),r},Vg=(t,e,r,n)=>{if((r||n)&&r===n)return!0;let a=t.getIn(["resolvedSubtrees","paths",...e,"requestBody","content"],(0,H.fromJS)([]));if(a.size<2||!r||!n)return!1;let o=a.getIn([r,"schema","properties"],(0,H.fromJS)([])),p=a.getIn([n,"schema","properties"],(0,H.fromJS)([]));return!!o.equals(p)};function gf(t){return H.Map.isMap(t)?t:new H.Map}var Jg=Be(85015),p0=Be.n(Jg),Dh=Be(38221),yf=Be.n(Dh),ui=Be(63560),pn=Be.n(ui),Ca=Be(56367),ua=Be.n(Ca);const wa="spec_update_spec",fo="spec_update_url",jl="spec_update_json",Kn="spec_update_param",yc="spec_update_empty_param_inclusion",Ds="spec_validate_param",Su="spec_set_response",kp="spec_set_request",vc="spec_set_mutated_request",Fo="spec_log_request",Ap="spec_clear_response",Pl="spec_clear_request",Zi="spec_clear_validate_param",bc="spec_update_operation_meta_value",Cp="spec_update_resolved",Pi="spec_update_resolved_subtree",xu="set_scheme",Nl=t=>p0()(t)?t:"";function Fh(t){const e=Nl(t).replace(/\t/g," ");if(typeof t=="string")return{type:wa,payload:e}}function jp(t){return{type:Cp,payload:t}}function vf(t){return{type:fo,payload:t}}function Lh(t){return{type:jl,payload:t}}const $h=t=>({specActions:e,specSelectors:r,errActions:n})=>{let{specStr:a}=r,o=null;try{t=t||a(),n.clear({source:"parser"}),o=Pe.load(t,{schema:Rt})}catch(p){return console.error(p),n.newSpecErr({source:"parser",level:"error",message:p.reason,line:p.mark&&p.mark.line?p.mark.line+1:void 0})}return o&&typeof o=="object"?e.updateJsonSpec(o):e.updateJsonSpec({})};let to=!1;const Tl=(t,e)=>({specActions:r,specSelectors:n,errActions:a,fn:{fetch:o,resolve:p,AST:c={}},getConfigs:d})=>{to||(console.warn("specActions.resolveSpec is deprecated since v3.10.0 and will be removed in v4.0.0; use requestResolvedSubtree instead!"),to=!0);const{modelPropertyMacro:E,parameterMacro:x,requestInterceptor:k,responseInterceptor:P}=d();t===void 0&&(t=n.specJson()),e===void 0&&(e=n.url());let R=c.getLineNumberForPath?c.getLineNumberForPath:()=>{},L=n.specStr();return p({fetch:o,spec:t,baseDoc:String(new URL(e,document.baseURI)),modelPropertyMacro:E,parameterMacro:x,requestInterceptor:k,responseInterceptor:P}).then((({spec:V,errors:Q})=>{if(a.clear({type:"thrown"}),Array.isArray(Q)&&Q.length>0){let J=Q.map((re=>(console.error(re),re.line=re.fullPath?R(L,re.fullPath):null,re.path=re.fullPath?re.fullPath.join("."):null,re.level="error",re.type="thrown",re.source="resolver",Object.defineProperty(re,"message",{enumerable:!0,value:re.message}),re)));a.newThrownErrBatch(J)}return r.updateResolved(V)}))};let Lo=[];const $o=yf()((()=>{const t=Lo.reduce(((e,{path:r,system:n})=>(e.has(n)||e.set(n,[]),e.get(n).push(r),e)),new Map);Lo=[],t.forEach((async(e,r)=>{if(!r)return void console.error("debResolveSubtrees: don't have a system to operate on, aborting.");if(!r.fn.resolveSubtree)return void console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing.");const{errActions:n,errSelectors:a,fn:{resolveSubtree:o,fetch:p,AST:c={}},specSelectors:d,specActions:E}=r,x=c.getLineNumberForPath??vu()(void 0),k=d.specStr(),{modelPropertyMacro:P,parameterMacro:R,requestInterceptor:L,responseInterceptor:V}=r.getConfigs();try{const Q=await e.reduce((async(J,re)=>{let{resultMap:me,specWithCurrentSubtrees:le}=await J;const{errors:ve,spec:be}=await o(le,re,{baseDoc:String(new URL(d.url(),document.baseURI)),modelPropertyMacro:P,parameterMacro:R,requestInterceptor:L,responseInterceptor:V});if(a.allErrors().size&&n.clearBy((ce=>ce.get("type")!=="thrown"||ce.get("source")!=="resolver"||!ce.get("fullPath")?.every(((_e,Ae)=>_e===re[Ae]||re[Ae]===void 0)))),Array.isArray(ve)&&ve.length>0){let ce=ve.map((_e=>(_e.line=_e.fullPath?x(k,_e.fullPath):null,_e.path=_e.fullPath?_e.fullPath.join("."):null,_e.level="error",_e.type="thrown",_e.source="resolver",Object.defineProperty(_e,"message",{enumerable:!0,value:_e.message}),_e)));n.newThrownErrBatch(ce)}return be&&d.isOAS3()&&re[0]==="components"&&re[1]==="securitySchemes"&&await Promise.all(Object.values(be).filter((ce=>ce?.type==="openIdConnect")).map((async ce=>{const _e={url:ce.openIdConnectUrl,requestInterceptor:L,responseInterceptor:V};try{const Ae=await p(_e);Ae instanceof Error||Ae.status>=400?console.error(Ae.statusText+" "+_e.url):ce.openIdConnectData=JSON.parse(Ae.text)}catch(Ae){console.error(Ae)}}))),pn()(me,re,be),le=ua()(re,be,le),{resultMap:me,specWithCurrentSubtrees:le}}),Promise.resolve({resultMap:(d.specResolvedSubtree([])||(0,H.Map)()).toJS(),specWithCurrentSubtrees:d.specJS()}));E.updateResolvedSubtree([],Q.resultMap)}catch(Q){console.error(Q)}}))}),35),Wg=t=>e=>{Lo.find((({path:r,system:n})=>n===e&&r.toString()===t.toString()))||(Lo.push({path:t,system:e}),$o())};function bf(t,e,r,n,a){return{type:Kn,payload:{path:t,value:n,paramName:e,paramIn:r,isXml:a}}}function wf(t,e,r,n){return{type:Kn,payload:{path:t,param:e,value:r,isXml:n}}}const Il=(t,e)=>({type:Pi,payload:{path:t,value:e}}),Hg=()=>({type:Pi,payload:{path:[],value:(0,H.Map)()}}),Ef=(t,e)=>({type:Ds,payload:{pathMethod:t,isOAS3:e}}),wc=(t,e,r,n)=>({type:yc,payload:{pathMethod:t,paramName:e,paramIn:r,includeEmptyValue:n}});function Sf(t){return{type:Zi,payload:{pathMethod:t}}}function Ec(t,e){return{type:bc,payload:{path:t,value:e,key:"consumes_value"}}}function Kg(t,e){return{type:bc,payload:{path:t,value:e,key:"produces_value"}}}const Gg=(t,e,r)=>({payload:{path:t,method:e,res:r},type:Su}),xf=(t,e,r)=>({payload:{path:t,method:e,req:r},type:kp}),Bh=(t,e,r)=>({payload:{path:t,method:e,req:r},type:vc}),h0=t=>({payload:t,type:Fo}),d0=t=>({fn:e,specActions:r,specSelectors:n,getConfigs:a,oas3Selectors:o})=>{let{pathName:p,method:c,operation:d}=t,{requestInterceptor:E,responseInterceptor:x}=a(),k=d.toJS();if(d&&d.get("parameters")&&d.get("parameters").filter((L=>L&&L.get("allowEmptyValue")===!0)).forEach((L=>{if(n.parameterInclusionSettingFor([p,c],L.get("name"),L.get("in"))){t.parameters=t.parameters||{};const V=Sa(L,t.parameters);(!V||V&&V.size===0)&&(t.parameters[L.get("name")]="")}})),t.contextUrl=Ao()(n.url()).toString(),k&&k.operationId?t.operationId=k.operationId:k&&p&&c&&(t.operationId=e.opId(k,p,c)),n.isOAS3()){const L=`${p}:${c}`;t.server=o.selectedServer(L)||o.selectedServer();const V=o.serverVariables({server:t.server,namespace:L}).toJS(),Q=o.serverVariables({server:t.server}).toJS();t.serverVariables=Object.keys(V).length?V:Q,t.requestContentType=o.requestContentType(p,c),t.responseContentType=o.responseContentType(p,c)||"*/*";const J=o.requestBodyValue(p,c),re=o.requestBodyInclusionSetting(p,c);J&&J.toJS?t.requestBody=J.map((me=>H.Map.isMap(me)?me.get("value"):me)).filter(((me,le)=>(Array.isArray(me)?me.length!==0:!Wa(me))||re.get(le))).toJS():t.requestBody=J}let P=Object.assign({},t);P=e.buildRequest(P),r.setRequest(t.pathName,t.method,P),t.requestInterceptor=async L=>{let V=await E.apply(void 0,[L]),Q=Object.assign({},V);return r.setMutatedRequest(t.pathName,t.method,Q),V},t.responseInterceptor=x;const R=Date.now();return e.execute(t).then((L=>{L.duration=Date.now()-R,r.setResponse(t.pathName,t.method,L)})).catch((L=>{L.message==="Failed to fetch"&&(L.name="",L.message=`**Failed to fetch.** +**Possible Reasons:** + - CORS + - Network Failure + - URL scheme must be "http" or "https" for CORS request.`),r.setResponse(t.pathName,t.method,{error:!0,err:L})}))},Yg=({path:t,method:e,...r}={})=>n=>{let{fn:{fetch:a},specSelectors:o,specActions:p}=n,c=o.specJsonWithResolvedSubtrees().toJS(),d=o.operationScheme(t,e),{requestContentType:E,responseContentType:x}=o.contentTypeValues([t,e]).toJS(),k=/xml/i.test(E),P=o.parameterValues([t,e],k).toJS();return p.executeRequest({...r,fetch:a,spec:c,pathName:t,method:e,parameters:P,requestContentType:E,scheme:d,responseContentType:x})};function Xg(t,e){return{type:Ap,payload:{path:t,method:e}}}function _u(t,e){return{type:Pl,payload:{path:t,method:e}}}function Ou(t,e,r){return{type:xu,payload:{scheme:t,path:e,method:r}}}const Qg={[wa]:(t,e)=>typeof e.payload=="string"?t.set("spec",e.payload):t,[fo]:(t,e)=>t.set("url",e.payload+""),[jl]:(t,e)=>t.set("json",nn(e.payload)),[Cp]:(t,e)=>t.setIn(["resolved"],nn(e.payload)),[Pi]:(t,e)=>{const{value:r,path:n}=e.payload;return t.setIn(["resolvedSubtrees",...n],nn(r))},[Kn]:(t,{payload:e})=>{let{path:r,paramName:n,paramIn:a,param:o,value:p,isXml:c}=e,d=o?sa(o):`${a}.${n}`;const E=c?"value_xml":"value";return t.setIn(["meta","paths",...r,"parameters",d,E],(0,H.fromJS)(p))},[yc]:(t,{payload:e})=>{let{pathMethod:r,paramName:n,paramIn:a,includeEmptyValue:o}=e;if(!n||!a)return console.warn("Warning: UPDATE_EMPTY_PARAM_INCLUSION could not generate a paramKey."),t;const p=`${a}.${n}`;return t.setIn(["meta","paths",...r,"parameter_inclusions",p],o)},[Ds]:(t,{payload:{pathMethod:e,isOAS3:r}})=>{const n=li(t).getIn(["paths",...e]),a=Rh(t,e).toJS();return t.updateIn(["meta","paths",...e,"parameters"],(0,H.fromJS)({}),(o=>n.get("parameters",(0,H.List)()).reduce(((p,c)=>{const d=Sa(c,a),E=Mh(t,e,c.get("name"),c.get("in")),x=((k,P,{isOAS3:R=!1,bypassRequiredCheck:L=!1}={})=>{let V=k.get("required"),{schema:Q,parameterContentMediaType:J}=sr(k,{isOAS3:R});return Rr(P,Q,V,L,J,R)})(c,d,{bypassRequiredCheck:E,isOAS3:r});return p.setIn([sa(c),"errors"],(0,H.fromJS)(x))}),o)))},[Zi]:(t,{payload:{pathMethod:e}})=>t.updateIn(["meta","paths",...e,"parameters"],(0,H.fromJS)([]),(r=>r.map((n=>n.set("errors",(0,H.fromJS)([])))))),[Su]:(t,{payload:{res:e,path:r,method:n}})=>{let a;a=e.error?Object.assign({error:!0,name:e.err.name,message:e.err.message,statusCode:e.err.statusCode},e.err.response):e,a.headers=a.headers||{};let o=t.setIn(["responses",r,n],nn(a));return Qe.Blob&&a.data instanceof Qe.Blob&&(o=o.setIn(["responses",r,n,"text"],a.data)),o},[kp]:(t,{payload:{req:e,path:r,method:n}})=>t.setIn(["requests",r,n],nn(e)),[vc]:(t,{payload:{req:e,path:r,method:n}})=>t.setIn(["mutatedRequests",r,n],nn(e)),[bc]:(t,{payload:{path:e,value:r,key:n}})=>{let a=["paths",...e],o=["meta","paths",...e];return t.getIn(["json",...a])||t.getIn(["resolved",...a])||t.getIn(["resolvedSubtrees",...a])?t.setIn([...o,n],(0,H.fromJS)(r)):t},[Ap]:(t,{payload:{path:e,method:r}})=>t.deleteIn(["responses",e,r]),[Pl]:(t,{payload:{path:e,method:r}})=>t.deleteIn(["requests",e,r]),[xu]:(t,{payload:{scheme:e,path:r,method:n}})=>r&&n?t.setIn(["scheme",r,n],e):r||n?void 0:t.setIn(["scheme","_defaultScheme"],e)},Gn=(t,{specActions:e})=>(...r)=>{t(...r),e.parseToJson(...r)},f0=(t,{specActions:e})=>(...r)=>{t(...r),e.invalidateResolvedSubtreeCache();const[n]=r,a=Cn()(n,["paths"])||{};Object.keys(a).forEach((o=>{const p=Cn()(a,[o]);Ai()(p)&&p.$ref&&e.requestResolvedSubtree(["paths",o])})),e.requestResolvedSubtree(["components","securitySchemes"])},Zg=(t,{specActions:e})=>r=>(e.logRequest(r),t(r)),m0=(t,{specSelectors:e})=>r=>t(r,e.isOAS3()),ey=()=>({statePlugins:{spec:{wrapActions:{...z},reducers:{...Qg},actions:{...M},selectors:{...j}}}});var ty=(function(){var t=function(e,r){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,a){n.__proto__=a}||function(n,a){for(var o in a)a.hasOwnProperty(o)&&(n[o]=a[o])},t(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=r===null?Object.create(r):(n.prototype=r.prototype,new n)}})(),g0=Object.prototype.hasOwnProperty;function Bo(t,e){return g0.call(t,e)}function Pp(t){if(Array.isArray(t)){for(var e=new Array(t.length),r=0;r=48&&e<=57))return!1;r++}return!0}function Xo(t){return t.indexOf("/")===-1&&t.indexOf("~")===-1?t:t.replace(/~/g,"~0").replace(/\//g,"~1")}function Np(t){return t.replace(/~1/g,"/").replace(/~0/g,"~")}function Tp(t){if(t===void 0)return!0;if(t){if(Array.isArray(t)){for(var e=0,r=t.length;e0&&c[E-1]=="constructor"))throw new TypeError("JSON-Patch: modifying `__proto__` or `constructor/prototype` prop is banned for security reasons, if this was on purpose, please set `banPrototypeModifications` flag false and pass it to this function. More info in fast-json-patch README");if(r&&k===void 0&&(d[P]===void 0?k=c.slice(0,E).join("/"):E==x-1&&(k=e.path),k!==void 0&&R(e,0,t,k)),E++,Array.isArray(d)){if(P==="-")P=d.length;else{if(r&&!ku(P))throw new zn("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index","OPERATION_PATH_ILLEGAL_ARRAY_INDEX",o,e,t);ku(P)&&(P=~~P)}if(E>=x){if(r&&e.op==="add"&&P>d.length)throw new zn("The specified index MUST NOT be greater than the number of elements in the array","OPERATION_VALUE_OUT_OF_BOUNDS",o,e,t);if((p=Uh[e.op].call(e,d,P,t)).test===!1)throw new zn("Test operation failed","TEST_OPERATION_FAILED",o,e,t);return p}}else if(E>=x){if((p=Sc[e.op].call(e,d,P,t)).test===!1)throw new zn("Test operation failed","TEST_OPERATION_FAILED",o,e,t);return p}if(d=d[P],r&&E0)throw new zn('Operation `path` property must start with "/"',"OPERATION_PATH_INVALID",e,t,r);if((t.op==="move"||t.op==="copy")&&typeof t.from!="string")throw new zn("Operation `from` property is not present (applicable in `move` and `copy` operations)","OPERATION_FROM_REQUIRED",e,t,r);if((t.op==="add"||t.op==="replace"||t.op==="test")&&t.value===void 0)throw new zn("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_REQUIRED",e,t,r);if((t.op==="add"||t.op==="replace"||t.op==="test")&&Tp(t.value))throw new zn("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED",e,t,r);if(r){if(t.op=="add"){var a=t.path.split("/").length,o=n.split("/").length;if(a!==o+1&&a!==o)throw new zn("Cannot perform an `add` operation at the desired path","OPERATION_PATH_CANNOT_ADD",e,t,r)}else if(t.op==="replace"||t.op==="remove"||t.op==="_get"){if(t.path!==n)throw new zn("Cannot perform the operation at a path that does not exist","OPERATION_PATH_UNRESOLVABLE",e,t,r)}else if(t.op==="move"||t.op==="copy"){var p=Ip([{op:"_get",path:t.from,value:void 0}],r);if(p&&p.name==="OPERATION_PATH_UNRESOLVABLE")throw new zn("Cannot perform the operation from a path that does not exist","OPERATION_FROM_UNRESOLVABLE",e,t,r)}}}function Ip(t,e,r){try{if(!Array.isArray(t))throw new zn("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");if(e)es(pa(e),pa(t),r||!0);else{r=r||Au;for(var n=0;n0&&(t.patches=[],t.callback&&t.callback(n)),n}function ju(t,e,r,n,a){if(e!==t){typeof e.toJSON=="function"&&(e=e.toJSON());for(var o=Pp(e),p=Pp(t),c=!1,d=p.length-1;d>=0;d--){var E=t[k=p[d]];if(!Bo(e,k)||e[k]===void 0&&E!==void 0&&Array.isArray(e)===!1)Array.isArray(t)===Array.isArray(e)?(a&&r.push({op:"test",path:n+"/"+Xo(k),value:pa(E)}),r.push({op:"remove",path:n+"/"+Xo(k)}),c=!0):(a&&r.push({op:"test",path:n,value:t}),r.push({op:"replace",path:n,value:e}));else{var x=e[k];typeof E=="object"&&E!=null&&typeof x=="object"&&x!=null&&Array.isArray(E)===Array.isArray(x)?ju(E,x,r,n+"/"+Xo(k),a):E!==x&&(a&&r.push({op:"test",path:n+"/"+Xo(k),value:pa(E)}),r.push({op:"replace",path:n+"/"+Xo(k),value:pa(x)}))}}if(c||o.length!=p.length)for(d=0;da!==void 0&&n?n[a]:n),e)},applyPatch:function(e,r,n){if(n=n||{},(r={...r,path:r.path&&$(r.path)}).op==="merge"){const a=rt(e,r.path);Object.assign(a,r.value),es(e,[ee(r.path,a)])}else if(r.op==="mergeDeep"){const a=rt(e,r.path),o=b()(a,r.value,{customMerge:p=>{if(p==="enum")return(c,d)=>Array.isArray(c)&&Array.isArray(d)?[...new Set([...c,...d])]:b()(c,d)}});e=es(e,[ee(r.path,o)]).newDocument}else if(r.op==="add"&&r.path===""&<(r.value))es(e,Object.keys(r.value).reduce(((a,o)=>(a.push({op:"add",path:`/${$(o)}`,value:r.value[o]}),a)),[]));else if(r.op==="replace"&&r.path===""){let{value:a}=r;n.allowMetaPatches&&r.meta&&Ze(r)&&(Array.isArray(r.value)||lt(r.value))&&(a={...a,...r.meta}),e=a}else if(es(e,[r]),n.allowMetaPatches&&r.meta&&Ze(r)&&(Array.isArray(r.value)||lt(r.value))){const a={...rt(e,r.path),...r.meta};es(e,[ee(r.path,a)])}return e},parentPathMatch:function(e,r){if(!Array.isArray(r))return!1;for(let n=0,a=r.length;n(e+"").replace(/~/g,"~0").replace(/\//g,"~1"))).join("/")}`:t}function ee(t,e,r){return{op:"replace",path:t,value:e,meta:r}}function ue(t,e,r){return st(ot(t.filter(Ze).map((n=>e(n.value,r,n.path)))||[]))}function Ce(t,e,r){return r=r||[],Array.isArray(t)?t.map(((n,a)=>Ce(n,e,r.concat(a)))):lt(t)?Object.keys(t).map((n=>Ce(t[n],e,r.concat(n)))):e(t,r[r.length-1],r)}function Me(t,e,r){let n=[];if((r=r||[]).length>0){const a=e(t,r[r.length-1],r);a&&(n=n.concat(a))}if(Array.isArray(t)){const a=t.map(((o,p)=>Me(o,e,r.concat(p))));a&&(n=n.concat(a))}else if(lt(t)){const a=Object.keys(t).map((o=>Me(t[o],e,r.concat(o))));a&&(n=n.concat(a))}return n=ot(n),n}function Ue(t){return Array.isArray(t)?t:[t]}function ot(t){return[].concat(...t.map((e=>Array.isArray(e)?ot(e):e)))}function st(t){return t.filter((e=>e!==void 0))}function lt(t){return t&&typeof t=="object"}function vt(t){return t&&typeof t=="function"}function Lt(t){if(Le(t)){const{op:e}=t;return e==="add"||e==="remove"||e==="replace"}return!1}function xt(t){return Lt(t)||Le(t)&&t.type==="mutation"}function Ze(t){return xt(t)&&(t.op==="add"||t.op==="replace"||t.op==="merge"||t.op==="mergeDeep")}function Le(t){return t&&typeof t=="object"}function rt(t,e){try{return Ml(t,e)}catch(r){return console.error(r),{}}}var wt=Be(48675);const nr=class extends wt{constructor(e,r,n){if(super(e,r,n),this.name=this.constructor.name,typeof r=="string"&&(this.message=r),typeof Error.captureStackTrace=="function"?Error.captureStackTrace(this,this.constructor):this.stack=new Error(r).stack,n!=null&&typeof n=="object"&&Object.hasOwn(n,"cause")&&!("cause"in this)){const{cause:a}=n;this.cause=a,a instanceof Error&&"stack"in a&&(this.stack=`${this.stack} +CAUSE: ${a.stack}`)}}};class Fr extends Error{static[Symbol.hasInstance](e){return super[Symbol.hasInstance](e)||Function.prototype[Symbol.hasInstance].call(nr,e)}constructor(e,r){if(super(e,r),this.name=this.constructor.name,typeof e=="string"&&(this.message=e),typeof Error.captureStackTrace=="function"?Error.captureStackTrace(this,this.constructor):this.stack=new Error(e).stack,r!=null&&typeof r=="object"&&Object.hasOwn(r,"cause")&&!("cause"in this)){const{cause:n}=r;this.cause=n,n instanceof Error&&"stack"in n&&(this.stack=`${this.stack} +CAUSE: ${n.stack}`)}}}const Mt=Fr,xr=class extends Mt{constructor(e,r){if(super(e,r),r!=null&&typeof r=="object"){const{cause:n,...a}=r;Object.assign(this,a)}}};function Qr(t,e){switch(t){case 0:return function(){return e.apply(this,arguments)};case 1:return function(r){return e.apply(this,arguments)};case 2:return function(r,n){return e.apply(this,arguments)};case 3:return function(r,n,a){return e.apply(this,arguments)};case 4:return function(r,n,a,o){return e.apply(this,arguments)};case 5:return function(r,n,a,o,p){return e.apply(this,arguments)};case 6:return function(r,n,a,o,p,c){return e.apply(this,arguments)};case 7:return function(r,n,a,o,p,c,d){return e.apply(this,arguments)};case 8:return function(r,n,a,o,p,c,d,E){return e.apply(this,arguments)};case 9:return function(r,n,a,o,p,c,d,E,x){return e.apply(this,arguments)};case 10:return function(r,n,a,o,p,c,d,E,x,k){return e.apply(this,arguments)};default:throw new Error("First argument to _arity must be a non-negative integer no greater than ten")}}function sn(t,e){return function(){return e.call(this,t.apply(this,arguments))}}function wr(t){return t!=null&&typeof t=="object"&&t["@@functional/placeholder"]===!0}function _r(t){return function e(r){return arguments.length===0||wr(r)?e:t.apply(this,arguments)}}function Tr(t){return function e(r,n){switch(arguments.length){case 0:return e;case 1:return wr(r)?e:_r((function(a){return t(r,a)}));default:return wr(r)&&wr(n)?e:wr(r)?_r((function(a){return t(a,n)})):wr(n)?_r((function(a){return t(r,a)})):t(r,n)}}}function ha(t){return function e(r,n,a){switch(arguments.length){case 0:return e;case 1:return wr(r)?e:Tr((function(o,p){return t(r,o,p)}));case 2:return wr(r)&&wr(n)?e:wr(r)?Tr((function(o,p){return t(o,n,p)})):wr(n)?Tr((function(o,p){return t(r,o,p)})):_r((function(o){return t(r,n,o)}));default:return wr(r)&&wr(n)&&wr(a)?e:wr(r)&&wr(n)?Tr((function(o,p){return t(o,p,a)})):wr(r)&&wr(a)?Tr((function(o,p){return t(o,n,p)})):wr(n)&&wr(a)?Tr((function(o,p){return t(r,o,p)})):wr(r)?_r((function(o){return t(o,n,a)})):wr(n)?_r((function(o){return t(r,o,a)})):wr(a)?_r((function(o){return t(r,n,o)})):t(r,n,a)}}}const Dl=Array.isArray||function(e){return e!=null&&e.length>=0&&Object.prototype.toString.call(e)==="[object Array]"};function Of(t){return Object.prototype.toString.call(t)==="[object String]"}const iy=_r((function(e){return!!Dl(e)||!!e&&typeof e=="object"&&!Of(e)&&(e.length===0||e.length>0&&e.hasOwnProperty(0)&&e.hasOwnProperty(e.length-1))}));var sy=typeof Symbol<"u"?Symbol.iterator:"@@iterator";function _c(t,e,r){return function(a,o,p){if(iy(p))return t(a,o,p);if(p==null)return o;if(typeof p["fantasy-land/reduce"]=="function")return e(a,o,p,"fantasy-land/reduce");if(p[sy]!=null)return r(a,o,p[sy]());if(typeof p.next=="function")return r(a,o,p);if(typeof p.reduce=="function")return e(a,o,p,"reduce");throw new TypeError("reduce: list must be array or iterable")}}function ly(t,e,r){for(var n=0,a=r.length;n=arguments.length)?d=e[p]:(d=arguments[a],a+=1),n[p]=d,wr(d)?c=!0:o-=1,p+=1}return!c&&o<=0?r.apply(this,n):Qr(Math.max(0,o),w0(t,n,r))}}const ja=Tr((function(e,r){return e===1?_r(r):Qr(e,w0(e,[],r))})),E0=_r((function(e){return ja(e.length,e)}));function S0(t){var e=Object.prototype.toString.call(t);return e==="[object Function]"||e==="[object AsyncFunction]"||e==="[object GeneratorFunction]"||e==="[object AsyncGeneratorFunction]"}function nE(t){for(var e,r=[];!(e=t.next()).done;)r.push(e.value);return r}function hy(t,e,r){for(var n=0,a=r.length;n=0;)ts(r=iE[n],e)&&!mA(a,r)&&(a[a.length]=r),n-=1;return a}):(function(e){return Object(e)!==e?[]:Object.keys(e)}));const Rp=gA,Fs=_r((function(e){return e===null?"Null":e===void 0?"Undefined":Object.prototype.toString.call(e).slice(8,-1)}));function lE(t,e,r,n){var a=nE(t);function o(p,c){return x0(p,c,r.slice(),n.slice())}return!hy((function(p,c){return!hy(o,c,p)}),nE(e),a)}function x0(t,e,r,n){if(Cf(t,e))return!0;var a=Fs(t);if(a!==Fs(e))return!1;if(typeof t["fantasy-land/equals"]=="function"||typeof e["fantasy-land/equals"]=="function")return typeof t["fantasy-land/equals"]=="function"&&t["fantasy-land/equals"](e)&&typeof e["fantasy-land/equals"]=="function"&&e["fantasy-land/equals"](t);if(typeof t.equals=="function"||typeof e.equals=="function")return typeof t.equals=="function"&&t.equals(e)&&typeof e.equals=="function"&&e.equals(t);switch(a){case"Arguments":case"Array":case"Object":if(typeof t.constructor=="function"&&(function(k){var P=String(k).match(/^function (\w*)/);return P==null?"":P[1]})(t.constructor)==="Promise")return t===e;break;case"Boolean":case"Number":case"String":if(typeof t!=typeof e||!Cf(t.valueOf(),e.valueOf()))return!1;break;case"Date":if(!Cf(t.valueOf(),e.valueOf()))return!1;break;case"Error":return t.name===e.name&&t.message===e.message;case"RegExp":if(t.source!==e.source||t.global!==e.global||t.ignoreCase!==e.ignoreCase||t.multiline!==e.multiline||t.sticky!==e.sticky||t.unicode!==e.unicode)return!1}for(var o=r.length-1;o>=0;){if(r[o]===t)return n[o]===e;o-=1}switch(a){case"Map":return t.size===e.size&&lE(t.entries(),e.entries(),r.concat([t]),n.concat([e]));case"Set":return t.size===e.size&&lE(t.values(),e.values(),r.concat([t]),n.concat([e]));case"Arguments":case"Array":case"Object":case"Boolean":case"Number":case"String":case"Date":case"Error":case"RegExp":case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":case"ArrayBuffer":break;default:return!1}var p=Rp(t);if(p.length!==Rp(e).length)return!1;var c=r.concat([t]),d=n.concat([e]);for(o=p.length-1;o>=0;){var E=p[o];if(!ts(E,e)||!x0(e[E],t[E],c,d))return!1;o-=1}return!0}const Pu=Tr((function(e,r){return x0(e,r,[],[])}));function dy(t,e){return(function(n,a,o){var p,c;if(typeof n.indexOf=="function")switch(typeof a){case"number":if(a===0){for(p=1/a;o=0}function fy(t,e){for(var r=0,n=e.length,a=Array(n);r":uE(p,c)},n=function(o,p){return fy((function(c){return _0(c)+": "+r(o[c])}),p.slice().sort())};switch(Object.prototype.toString.call(t)){case"[object Arguments]":return"(function() { return arguments; }("+fy(r,t).join(", ")+"))";case"[object Array]":return"["+fy(r,t).concat(n(t,SA((function(o){return/^\d+$/.test(o)}),Rp(t)))).join(", ")+"]";case"[object Boolean]":return typeof t=="object"?"new Boolean("+r(t.valueOf())+")":t.toString();case"[object Date]":return"new Date("+(isNaN(t.valueOf())?r(NaN):_0(yA(t)))+")";case"[object Map]":return"new Map("+r(Array.from(t))+")";case"[object Null]":return"null";case"[object Number]":return typeof t=="object"?"new Number("+r(t.valueOf())+")":1/t==-1/0?"-0":t.toString(10);case"[object Set]":return"new Set("+r(Array.from(t).sort())+")";case"[object String]":return typeof t=="object"?"new String("+r(t.valueOf())+")":_0(t);case"[object Undefined]":return"undefined";default:if(typeof t.toString=="function"){var a=t.toString();if(a!=="[object Object]")return a}return"{"+n(t,Rp(t)).join(", ")+"}"}}const Tf=_r((function(e){return uE(e,[])})),O0=Tr((function(e,r){return ja(e+1,(function(){var n=arguments[e];if(n!=null&&S0(n[r]))return n[r].apply(n,Array.prototype.slice.call(arguments,0,e));throw new TypeError(Tf(n)+' does not have a method named "'+r+'"')}))})),pE=O0(1,"split");function xA(t,e){for(var r=e.length-1;r>=0&&t(e[r]);)r-=1;return kf(0,r+1,e)}var _A=(function(){function t(e,r){this.f=e,this.retained=[],this.xf=r}return t.prototype["@@transducer/init"]=Tu,t.prototype["@@transducer/result"]=function(e){return this.retained=null,this.xf["@@transducer/result"](e)},t.prototype["@@transducer/step"]=function(e,r){return this.f(r)?this.retain(e,r):this.flush(e,r)},t.prototype.flush=function(e,r){return e=v0(this.xf,e,this.retained),this.retained=[],this.xf["@@transducer/step"](e,r)},t.prototype.retain=function(e,r){return this.retained.push(r),e},t})();function OA(t){return function(e){return new _A(t,e)}}const kA=Tr(Nu([],OA,xA)),hE=O0(1,"join"),gy=_r((function(e){return ja(e.length,(function(r,n){var a=Array.prototype.slice.call(arguments,0);return a[0]=n,a[1]=r,e.apply(this,a)}))}))(Tr(dy)),AA=E0((function(t,e){return mo(pE(""),kA(gy(t)),hE(""))(e)}));function CA(t,e,r){for(var n=r.next();!n.done;)e=t(e,n.value),n=r.next();return e}function jA(t,e,r,n){return r[n](t,e)}const PA=_c(Pf,jA,CA);var NA=(function(){function t(e,r){this.xf=r,this.f=e}return t.prototype["@@transducer/init"]=Tu,t.prototype["@@transducer/result"]=Nf,t.prototype["@@transducer/step"]=function(e,r){return this.xf["@@transducer/step"](e,this.f(r))},t})();const yy=Tr(Nu(["fantasy-land/map","map"],(function(e){return function(r){return new NA(e,r)}}),(function(e,r){switch(Object.prototype.toString.call(r)){case"[object Function]":return ja(r.length,(function(){return e.call(this,r.apply(this,arguments))}));case"[object Object]":return Pf((function(n,a){return n[a]=e(r[a]),n}),{},Rp(r));default:return fy(e,r)}}))),TA=Tr((function(e,r){return typeof r["fantasy-land/ap"]=="function"?r["fantasy-land/ap"](e):typeof e.ap=="function"?e.ap(r):typeof e=="function"?function(n){return e(n)(r(n))}:PA((function(n,a){return(function(p,c){var d;c=c||[];var E=(p=p||[]).length,x=c.length,k=[];for(d=0;dE!=E>d)return E>d?E:d}var a=n(e,r);if(a!==void 0)return a;var o=n(typeof e,typeof r);if(o!==void 0)return o===typeof e?e:r;var p=Tf(e),c=n(p,Tf(r));return c!==void 0&&c===p?e:r}));var MA=Tr((function(e,r){return yy(Af(e),r)}));const mE=MA,RA=_r((function(e){return ja(Vh(fE,0,mE("length",e)),(function(){for(var r=0,n=e.length;r{var t,e;return/^win/.test((t=(e=globalThis.process)===null||e===void 0?void 0:e.platform)!==null&&t!==void 0?t:"")},C0=t=>{try{const e=new URL(t);return AA(":",e.protocol)}catch{return}},bE=(mo(C0,dE),t=>{if(globalThis.window!==void 0)return!1;const e=C0(t);return Iu(e)||e==="file"||/^[a-zA-Z]$/.test(e)}),j0=t=>{const e=C0(t);return e==="http"||e==="https"},wE=(t,e)=>{const r=[/%23/g,"#",/%24/g,"$",/%26/g,"&",/%2C/g,",",/%40/g,"@"],n=uy(!1,"keepFileProtocol",e),a=uy(A0,"isWindows",e);let o=decodeURI(t);for(let c=0;c{const e=t.indexOf("#");return e!==-1?t.substring(e):"#"},In=t=>{const e=t.indexOf("#");let r=t;return e>=0&&(r=t.substring(0,e)),r},P0=()=>{var t,e;if(globalThis.window!==void 0)return In(globalThis.location.href);const r=(t=(e=globalThis.process)===null||e===void 0?void 0:e.cwd())!==null&&t!==void 0?t:"/",n=py(r);return["/","\\"].includes(n)?r:r+(A0()?"\\":"/")},go=(t,e)=>{const r=new URL(e,new URL(t,"resolve://"));if(r.protocol==="resolve:"){const{pathname:n,search:a,hash:o}=r;return n+a+o}return r.toString()},by=t=>{if(bE(t))return(e=>{const r=[/\?/g,"%3F",/#/g,"%23"];let n=e;A0()&&(n=n.replace(/\\/g,"/")),n=encodeURI(n);for(let a=0;abE(t)?wE(t):decodeURI(t);var JA=Be(8628),Yn=Be.n(JA);const SE=t=>Object.prototype.toString.call(t),N0=t=>ArrayBuffer.isView(t)&&!(t instanceof DataView),xE=Array.isArray,_E=t=>{const e=SE(t);return e==="[object Boolean]"||e==="[object Number]"||e==="[object String]"},WA=Object.getOwnPropertyDescriptor,HA=Object.prototype.propertyIsEnumerable,KA=Object.getOwnPropertySymbols,wy=Object.prototype.hasOwnProperty,OE=t=>t==="__proto__"||t==="constructor"||t==="prototype";function Ey(t,e,r){e==="__proto__"?Object.defineProperty(t,e,{value:r,writable:!0,enumerable:!0,configurable:!0}):t[e]=r}function kE(t,e){if(e!==void 0&&t>e)throw new RangeError(`neotraverse: maximum traversal depth (${e}) exceeded`)}function T0(t){const e=Object.keys(t),r=KA(t);for(let n=0;nnew Ll(t,e);$l.get=(t,e,r)=>new Ll(t,r).get(e),$l.set=(t,e,r,n)=>new Ll(t,n).set(e,r),$l.has=(t,e,r)=>new Ll(t,r).has(e),$l.map=(t,e,r)=>new Ll(t,r).map(e),$l.forEach=(t,e,r)=>new Ll(t,r).forEach(e),$l.reduce=(t,e,r,n)=>new Ll(t,n).reduce(e,r),$l.paths=(t,e)=>new Ll(t,e).paths(),$l.nodes=(t,e)=>new Ll(t,e).nodes(),$l.clone=(t,e)=>new Ll(t,e).clone();const PE="application/json, application/yaml",Sy="https://swagger.io",YA=Object.freeze({url:"/"}),NE=3e3,XA=["properties"],QA=["properties"],ZA=["definitions","parameters","responses","securityDefinitions","components/schemas","components/responses","components/parameters","components/securitySchemes"],eC=["schema/example","items/example"];function TE(t){const e=t[t.length-1],r=t[t.length-2],n=t.join("/");return XA.indexOf(e)>-1&&QA.indexOf(r)===-1||ZA.indexOf(n)>-1||eC.some((a=>n.indexOf(a)>-1))}function I0(t,e){const[r,n]=t.split("#"),a=e??"",o=r??"";let p;if(j0(a))p=go(a,o);else{const c=go(Sy,a),d=go(c,o).replace(Sy,"");p=o.startsWith("/")?d:d.substring(1)}return n?`${p}#${n}`:p}const tC=/^([a-z]+:\/\/|\/\/)/i;class Hh extends xr{}const Oc={},IE=new WeakMap,rC=[t=>t[0]==="paths"&&t[3]==="responses"&&t[5]==="examples",t=>t[0]==="paths"&&t[3]==="responses"&&t[5]==="content"&&t[7]==="example",t=>t[0]==="paths"&&t[3]==="responses"&&t[5]==="content"&&t[7]==="examples"&&t[9]==="value",t=>t[0]==="paths"&&t[3]==="requestBody"&&t[4]==="content"&&t[6]==="example",t=>t[0]==="paths"&&t[3]==="requestBody"&&t[4]==="content"&&t[6]==="examples"&&t[8]==="value",t=>t[0]==="paths"&&t[2]==="parameters"&&t[4]==="example",t=>t[0]==="paths"&&t[3]==="parameters"&&t[5]==="example",t=>t[0]==="paths"&&t[2]==="parameters"&&t[4]==="examples"&&t[6]==="value",t=>t[0]==="paths"&&t[3]==="parameters"&&t[5]==="examples"&&t[7]==="value",t=>t[0]==="paths"&&t[2]==="parameters"&&t[4]==="content"&&t[6]==="example",t=>t[0]==="paths"&&t[2]==="parameters"&&t[4]==="content"&&t[6]==="examples"&&t[8]==="value",t=>t[0]==="paths"&&t[3]==="parameters"&&t[4]==="content"&&t[7]==="example",t=>t[0]==="paths"&&t[3]==="parameters"&&t[5]==="content"&&t[7]==="examples"&&t[9]==="value"],nC={key:"$ref",plugin:(t,e,r,n)=>{const a=n.getInstance(),o=r.slice(0,-1);if(TE(o)||(V=>rC.some((Q=>Q(V))))(o))return;const{baseDoc:p}=n.getContext(r);if(typeof t!="string")return new Hh("$ref: must be a string (JSON-Ref)",{$ref:t,baseDoc:p,fullPath:r});const c=FE(t),d=c[0],E=c[1]||"";let x,k,P;try{x=p||d?DE(d,p):null}catch(V){return M0(V,{pointer:E,$ref:t,basePath:x,fullPath:r})}if((function(Q,J,re,me){let le=IE.get(me);le||(le={},IE.set(me,le));const ve=(function(je){return je.length===0?"":`/${je.map(qE).join("/")}`})(re),be=`${J||""}#${Q}`,ce=ve.replace(/allOf\/\d+\/?/g,""),_e=me.contextTree.get([]).baseDoc;if(J===_e&&F0(ce,Q))return!0;let Ae="";if(re.some((Oe=>(Ae=`${Ae}/${qE(Oe)}`,le[Ae]&&le[Ae].some((je=>F0(je,be)||F0(be,je)))))))return!0;le[ce]=(le[ce]||[]).concat(be)})(E,x,o,n)&&!a.useCircularStructures){const V=I0(t,x);return t===V?null:C.replace(r,V)}if(x==null?(P=D0(E),k=n.get(P),k===void 0&&(k=new Hh(`Could not resolve reference: ${t}`,{pointer:E,$ref:t,baseDoc:p,fullPath:r}))):(k=LE(x,E),k=k.__value!=null?k.__value:k.catch((V=>{throw M0(V,{pointer:E,$ref:t,baseDoc:p,fullPath:r})}))),k instanceof Error)return[C.remove(r),k];const R=I0(t,x),L=C.replace(o,k,{$$ref:R});if(x&&x!==p)return[L,C.context(o,{baseDoc:x})];try{if(!(function(Q,J){const re=[Q];return J.path.reduce(((le,ve)=>(re.push(le[ve]),le[ve])),Q),me(J.value);function me(le){return C.isObject(le)&&(re.indexOf(le)>=0||Object.keys(le).some((ve=>me(le[ve]))))}})(n.state,L)||a.useCircularStructures)return L}catch{return null}}},ME=Object.assign(nC,{docCache:Oc,absoluteify:DE,clearCache:function(e){e!==void 0?delete Oc[e]:Object.keys(Oc).forEach((r=>{delete Oc[r]}))},JSONRefError:Hh,wrapError:M0,getDoc:$E,split:FE,extractFromDoc:LE,fetchJSON:function(e){return fetch(e,{headers:{Accept:PE},loadSpec:!0}).then((r=>r.text())).then((r=>Pe.load(r)))},extract:R0,jsonPointerToArray:D0,unescapeJsonPointerToken:BE}),RE=ME;function DE(t,e){if(!tC.test(t)){if(!e)throw new Hh(`Tried to resolve a relative URL, without having a basePath. path: '${t}' basePath: '${e}'`);return go(e,t)}return t}function M0(t,e){let r;return r=t&&t.response&&t.response.body?`${t.response.body.code} ${t.response.body.message}`:t.message,new Hh(`Could not resolve reference: ${r}`,{...e,cause:t})}function FE(t){return(t+"").split("#")}function LE(t,e){const r=Oc[t];if(r&&!C.isPromise(r))try{const n=R0(e,r);return Object.assign(Promise.resolve(n),{__value:n})}catch(n){return Promise.reject(n)}return $E(t).then((n=>R0(e,n)))}function $E(t){const e=Oc[t];return e?C.isPromise(e)?e:Promise.resolve(e):(Oc[t]=ME.fetchJSON(t).then((r=>(Oc[t]=r,r))),Oc[t])}function R0(t,e){const r=D0(t);if(r.length<1)return e;const n=C.getIn(e,r);if(n===void 0)throw new Hh(`Could not resolve pointer: ${t} does not exist in document`,{pointer:t});return n}function D0(t){if(typeof t!="string")throw new TypeError("Expected a string, got a "+typeof t);return t[0]==="/"&&(t=t.substr(1)),t===""?[]:t.split("/").map(BE)}function BE(t){return typeof t!="string"?t:new URLSearchParams(`=${t.replace(/~1/g,"/").replace(/~0/g,"~")}`).get("")}function qE(t){return new URLSearchParams([["",t.replace(/~/g,"~0").replace(/\//g,"~1")]]).toString().slice(1)}const aC=t=>!t||t==="/"||t==="#";function F0(t,e){if(aC(e))return!0;const r=t.charAt(e.length),n=e.slice(-1);return t.indexOf(e)===0&&(!r||r==="/"||r==="#")&&n!=="#"}const oC={key:"allOf",plugin:(t,e,r,n,a)=>{if(a.meta&&a.meta.$$ref)return;const o=r.slice(0,-1);if(TE(o))return;if(!Array.isArray(t)){const E=new TypeError("allOf must be an array");return E.fullPath=r,E}let p=!1,c=a.value;if(o.forEach((E=>{c&&(c=c[E])})),c={...c},Object.keys(c).length===0)return;delete c.allOf;const d=[];return d.push(n.replace(o,{})),t.forEach(((E,x)=>{if(!n.isObject(E)){if(p)return null;p=!0;const P=new TypeError("Elements in allOf must be objects");return P.fullPath=r,d.push(P)}d.push(n.mergeDeep(o,E));const k=(function(R,L,{specmap:V,getBaseUrlForNodePath:Q=re=>V.getContext([...L,...re]).baseDoc,targetKeys:J=["$ref","$$ref"]}={}){const re=[];return $l(R).forEach((function(){if(Yn()(J).call(J,this.key)&&typeof this.node=="string"){const le=this.path,ve=L.concat(this.path),be=I0(this.node,Q(le));re.push(V.replace(ve,be))}})),re})(E,r.slice(0,-1),{getBaseUrlForNodePath:P=>n.getContext([...r,x,...P]).baseDoc,specmap:n});d.push(...k)})),c.example&&d.push(n.remove([].concat(o,"example"))),d.push(n.mergeDeep(o,c)),c.$$ref||d.push(n.remove([].concat(o,"$$ref"))),d}},iC={key:"parameters",plugin:(t,e,r,n)=>{if(Array.isArray(t)&&t.length){const a=Object.assign([],t),o=r.slice(0,-1),p={...C.getIn(n.spec,o)};for(let c=0;c{const a={...t};for(const o in t)try{a[o].default=n.modelPropertyMacro(a[o])}catch(p){const c=new Error(p);return c.fullPath=r,c}return C.replace(r,a)}};class lC{constructor(e){this.root=L0(e||{})}set(e,r){const n=this.getParent(e,!0);if(!n)return void xy(this.root,r,null);const a=e[e.length-1],{children:o}=n;o[a]?xy(o[a],r,n):o[a]=L0(r,n)}get(e){if((e=e||[]).length<1)return this.root.value;let r,n,a=this.root;for(let o=0;o{if(!n)return n;const{children:o}=n;return!o[a]&&r&&(o[a]=L0(null,n)),o[a]}),this.root)}}function L0(t,e){return xy({children:{}},t,e)}function xy(t,e,r){return t.value=e||{},t.protoValue=r?{...r.protoValue,...t.value}:t.value,Object.keys(t.children).forEach((n=>{const a=t.children[n];t.children[n]=xy(a,a.value,t)})),t}const UE=()=>{};class cC{static getPluginName(e){return e.pluginName}static getPatchesOfType(e,r){return e.filter(r)}constructor(e){Object.assign(this,{spec:"",debugLevel:"info",plugins:[],pluginHistory:{},errors:[],mutations:[],promisedPatches:[],state:{},patches:[],context:{},contextTree:new lC,showDebug:!1,allPatches:[],pluginProp:"specMap",libMethods:Object.assign(Object.create(this),C,{getInstance:()=>this}),allowMetaPatches:!1},e),this.get=this._get.bind(this),this.getContext=this._getContext.bind(this),this.hasRun=this._hasRun.bind(this),this.wrappedPlugins=this.plugins.map(this.wrapPlugin.bind(this)).filter(C.isFunction),this.patches.push(C.add([],this.spec)),this.patches.push(C.context([],this.context)),this.updatePatches(this.patches)}debug(e,...r){this.debugLevel===e&&console.log(...r)}verbose(e,...r){this.debugLevel==="verbose"&&console.log(`[${e}] `,...r)}wrapPlugin(e,r){const{pathDiscriminator:n}=this;let a,o=null;return e[this.pluginProp]?(o=e,a=e[this.pluginProp]):C.isFunction(e)?a=e:C.isObject(e)&&(a=(function(c){const d=(E,x)=>!Array.isArray(E)||E.every(((k,P)=>k===x[P]));return function*(x,k){const P={};for(const[L,V]of x.filter(C.isAdditiveMutation).entries()){if(!(Lthis.getMutationsForPlugin(e).length>0))}nextPromisedPatch(){if(this.promisedPatches.length>0)return Promise.race(this.promisedPatches.map((e=>e.value)))}getPluginHistory(e){const r=this.constructor.getPluginName(e);return this.pluginHistory[r]||[]}getPluginRunCount(e){return this.getPluginHistory(e).length}getPluginHistoryTip(e){const r=this.getPluginHistory(e);return r&&r[r.length-1]||{}}getPluginMutationIndex(e){const r=this.getPluginHistoryTip(e).mutationIndex;return typeof r!="number"?-1:r}updatePluginHistory(e,r){const n=this.constructor.getPluginName(e);this.pluginHistory[n]=this.pluginHistory[n]||[],this.pluginHistory[n].push(r)}updatePatches(e){C.normalizeArray(e).forEach((r=>{if(r instanceof Error)this.errors.push(r);else try{if(!C.isObject(r))return void this.debug("updatePatches","Got a non-object patch",r);if(this.showDebug&&this.allPatches.push(r),C.isPromise(r.value))return this.promisedPatches.push(r),void this.promisedPatchThen(r);if(C.isContextPatch(r))return void this.setContext(r.path,r.value);C.isMutation(r)&&this.updateMutations(r)}catch(n){console.error(n),this.errors.push(n)}}))}updateMutations(e){typeof e.value=="object"&&!Array.isArray(e.value)&&this.allowMetaPatches&&(e.value={...e.value});const r=C.applyPatch(this.state,e,{allowMetaPatches:this.allowMetaPatches});r&&(this.mutations.push(e),this.state=r)}removePromisedPatch(e){const r=this.promisedPatches.indexOf(e);r<0?this.debug("Tried to remove a promisedPatch that isn't there!"):this.promisedPatches.splice(r,1)}promisedPatchThen(e){return e.value=e.value.then((r=>{const n={...e,value:r};this.removePromisedPatch(e),this.updatePatches(n)})).catch((r=>{this.removePromisedPatch(e),this.updatePatches(r)})),e.value}getMutations(e,r){return e=e||0,typeof r!="number"&&(r=this.mutations.length),this.mutations.slice(e,r)}getCurrentMutations(){return this.getMutationsForPlugin(this.getCurrentPlugin())}getMutationsForPlugin(e){const r=this.getPluginMutationIndex(e);return this.getMutations(r+1)}getCurrentPlugin(){return this.currentPlugin}getLib(){return this.libMethods}_get(e){return C.getIn(this.state,e)}_getContext(e){return this.contextTree.get(e)}setContext(e,r){return this.contextTree.set(e,r)}_hasRun(e){return this.getPluginRunCount(this.getCurrentPlugin())>(e||0)}dispatch(){const e=this,r=this.nextPlugin();if(!r){const a=this.nextPromisedPatch();if(a)return a.then((()=>this.dispatch())).catch((()=>this.dispatch()));const o={spec:this.state,errors:this.errors};return this.showDebug&&(o.patches=this.allPatches),Promise.resolve(o)}if(e.pluginCount=e.pluginCount||new WeakMap,e.pluginCount.set(r,(e.pluginCount.get(r)||0)+1),e.pluginCount[r]>100)return Promise.resolve({spec:e.state,errors:e.errors.concat(new Error("We've reached a hard limit of 100 plugin runs"))});if(r!==this.currentPlugin&&this.promisedPatches.length){const a=this.promisedPatches.map((o=>o.value));return Promise.all(a.map((o=>o.then(UE,UE)))).then((()=>this.dispatch()))}return(function(){e.currentPlugin=r;const o=e.getCurrentMutations(),p=e.mutations.length-1;try{if(r.isGenerator)for(const c of r(o,e.getLib()))n(c);else n(r(o,e.getLib()))}catch(c){console.error(c),n([Object.assign(Object.create(c),{plugin:r})])}finally{e.updatePluginHistory(r,{mutationIndex:p})}return e.dispatch()})();function n(a){a&&(a=C.fullyNormalizeArray(a),e.updatePatches(a,r))}}}const Kh={refs:RE,allOf:oC,parameters:iC,properties:sC};function zE(t,e={}){const{requestInterceptor:r,responseInterceptor:n}=e,a=t.withCredentials?"include":"same-origin";return o=>t({url:o,loadSpec:!0,requestInterceptor:r,responseInterceptor:n,headers:{Accept:PE},credentials:a}).then((p=>p.body))}function $0(t,e){return e||typeof navigator>"u"||(e=navigator),e&&e.product==="ReactNative"?!(!t||typeof t!="object"||typeof t.uri!="string"):typeof File<"u"&&t instanceof File||typeof Blob<"u"&&t instanceof Blob||!!ArrayBuffer.isView(t)||t!==null&&typeof t=="object"&&typeof t.pipe=="function"}function VE(t,e){return Array.isArray(t)&&t.some((r=>$0(r,e)))}class B0 extends File{constructor(e,r="",n={}){super([e],r,n),this.data=e}valueOf(){return this.data}toString(){return this.valueOf()}}const uC=t=>":/?#[]@!$&'()*+,;=".indexOf(t)>-1,pC=t=>/^[a-z0-9\-._~]+$/i.test(t);function JE(t,e="reserved"){return[...t].map((r=>{if(pC(r)||uC(r)&&e==="unsafe")return r;const n=new TextEncoder;return Array.from(n.encode(r)).map((a=>`0${a.toString(16).toUpperCase()}`.slice(-2))).map((a=>`%${a}`)).join("")})).join("")}function q0(t){const{value:e}=t;return Array.isArray(e)?(function({key:n,value:a,style:o,explode:p,escape:c}){if(o==="simple")return a.map((d=>yo(d,c))).join(",");if(o==="label")return`.${a.map((d=>yo(d,c))).join(".")}`;if(o==="matrix")return a.map((d=>yo(d,c))).reduce(((d,E)=>!d||p?`${d||""};${n}=${E}`:`${d},${E}`),"");if(o==="form"){const d=p?`&${n}=`:",";return a.map((E=>yo(E,c))).join(d)}if(o==="spaceDelimited"){const d=p?`${n}=`:"";return a.map((E=>yo(E,c))).join(` ${d}`)}if(o==="pipeDelimited"){const d=p?`${n}=`:"";return a.map((E=>yo(E,c))).join(`|${d}`)}})(t):typeof e=="object"?(function({key:n,value:a,style:o,explode:p,escape:c}){const d=Object.keys(a);if(o==="simple")return d.reduce(((E,x)=>{const k=yo(a[x],c);return`${E?`${E},`:""}${x}${p?"=":","}${k}`}),"");if(o==="label")return d.reduce(((E,x)=>{const k=yo(a[x],c);return`${E?`${E}.`:"."}${x}${p?"=":"."}${k}`}),"");if(o==="matrix"&&p)return d.reduce(((E,x)=>`${E?`${E};`:";"}${x}=${yo(a[x],c)}`),"");if(o==="matrix")return d.reduce(((E,x)=>{const k=yo(a[x],c);return`${E?`${E},`:`;${n}=`}${x},${k}`}),"");if(o==="form")return d.reduce(((E,x)=>{const k=yo(a[x],c);return`${E?`${E}${p?"&":","}`:""}${x}${p?"=":","}${k}`}),"")})(t):(function({key:n,value:a,style:o,escape:p}){if(o==="simple")return yo(a,p);if(o==="label")return`.${yo(a,p)}`;if(o==="matrix")return`;${n}=${yo(a,p)}`;if(o==="form"||o==="deepObject")return yo(a,p)})(t)}function yo(t,e=!1){return Array.isArray(t)||t!==null&&typeof t=="object"?t=JSON.stringify(t):typeof t!="number"&&typeof t!="boolean"||(t=String(t)),e&&typeof t=="string"&&t.length>0?JE(t,e):t??""}const hC={form:",",spaceDelimited:"%20",pipeDelimited:"|"},dC={csv:",",ssv:"%20",tsv:"%09",pipes:"|"};function WE(t,e,r=!1){const{collectionFormat:n,allowEmptyValue:a,serializationOption:o,encoding:p}=e,c=typeof e!="object"||Array.isArray(e)?e:e.value,d=r?x=>x.toString():x=>encodeURIComponent(x),E=d(t);if(c===void 0&&a)return[[E,""]];if($0(c)||VE(c))return[[E,c]];if(o)return HE(t,c,r,o);if(p){if([typeof p.style,typeof p.explode,typeof p.allowReserved].some((x=>x!=="undefined"))){const{style:x,explode:k,allowReserved:P}=p;return HE(t,c,r,{style:x,explode:k,allowReserved:P})}if(typeof p.contentType=="string"){if(p.contentType.startsWith("application/json")){const k=d(typeof c=="string"?c:JSON.stringify(c));return[[E,new B0(k,"blob",{type:p.contentType})]]}const x=d(String(c));return[[E,new B0(x,"blob",{type:p.contentType})]]}return typeof c!="object"?[[E,d(c)]]:Array.isArray(c)&&c.every((x=>typeof x!="object"))?[[E,c.map(d).join(",")]]:[[E,d(JSON.stringify(c))]]}return typeof c!="object"?[[E,d(c)]]:Array.isArray(c)?n==="multi"?[[E,c.map(d)]]:[[E,c.map(d).join(dC[n||"csv"])]]:[[E,""]]}function HE(t,e,r,n){const a=n.style||"form",o=n.explode===void 0?a==="form":n.explode,p=!r&&(n&&n.allowReserved?"unsafe":"reserved"),c=E=>yo(E,p),d=r?E=>E:E=>c(E);return typeof e!="object"?[[d(t),c(e)]]:Array.isArray(e)?o?[[d(t),e.map(c)]]:[[d(t),e.map(c).join(hC[a])]]:a==="deepObject"?Object.keys(e).map((E=>[d(`${t}[${E}]`),c(e[E])])):o?Object.keys(e).map((E=>[d(E),c(e[E])])):[[d(t),Object.keys(e).map((E=>[`${d(E)},${c(e[E])}`])).join(",")]]}function KE(t){return((e,{encode:r=!0}={})=>{const n=(p,c,d)=>(Array.isArray(d)?d.reduce(((E,x)=>n(p,c,x)),p):d instanceof Date?p.append(c,d.toISOString()):typeof d=="object"?Object.entries(d).reduce(((E,[x,k])=>n(p,`${c}[${x}]`,k)),p):p.append(c,d),p),a=Object.entries(e).reduce(((p,[c,d])=>n(p,c,d)),new URLSearchParams),o=String(a);return r?o:decodeURIComponent(o)})(Object.keys(t).reduce(((e,r)=>{for(const[n,a]of WE(r,t[r]))e[n]=a instanceof B0?a.valueOf():a;return e}),{}),{encode:!1})}function U0(t={}){const{url:e="",query:r,form:n}=t;if(n){const a=Object.keys(n).some((p=>{const{value:c}=n[p];return $0(c)||VE(c)})),o=t.headers["content-type"]||t.headers["Content-Type"];if(a||/multipart\/form-data/i.test(o)){const p=(function(d){return Object.entries(d).reduce(((E,[x,k])=>{for(const[P,R]of WE(x,k,!0))if(Array.isArray(R))for(const L of R)if(ArrayBuffer.isView(L)){const V=new Blob([L]);E.append(P,V)}else E.append(P,L);else if(ArrayBuffer.isView(R)){const L=new Blob([R]);E.append(P,L)}else E.append(P,R);return E}),new FormData)})(t.form);t.formdata=p,t.body=p}else t.body=KE(n);delete t.form}if(r){const[a,o]=e.split("?");let p="";if(o){const d=new URLSearchParams(o);Object.keys(r).forEach((E=>d.delete(E))),p=String(d)}const c=((...d)=>{const E=d.filter((x=>x)).join("&");return E?`?${E}`:""})(p,KE(r));t.url=a+c,delete t.query}return t}function fC(t={}){return typeof t.entries!="function"?{}:Array.from(t.entries()).reduce(((e,[r,n])=>(e[r]=(function(o){return Yn()(o).call(o,", ")?o.split(", "):o})(n),e)),{})}function GE(t,e,{loadSpec:r=!1}={}){const n={ok:t.ok,url:t.url||e,status:t.status,statusText:t.statusText,headers:fC(t.headers)},a=n.headers["content-type"],o=r||((p="")=>/(json|xml|yaml|text)\b/.test(p))(a);return(o?t.text:t.blob||t.buffer).call(t).then((p=>{if(n.text=p,n.data=p,o)try{const c=(function(E,x){if(x){if(x.indexOf("application/json")===0||x.indexOf("+json")>0)return JSON.parse(E);if(x.indexOf("application/xml")===0||x.indexOf("+xml")>0)return E}return Pe.load(E)})(p,a);n.body=c,n.obj=c}catch(c){n.parseError=c}return n}))}async function Mf(t,e={}){typeof t=="object"&&(t=(e=t).url),e.headers=e.headers||{},(e=U0(e)).headers&&Object.keys(e.headers).forEach((a=>{const o=e.headers[a];typeof o=="string"&&(e.headers[a]=o.replace(/\n+/g," "))})),e.requestInterceptor&&(e=await e.requestInterceptor(e)||e);const r=e.headers["content-type"]||e.headers["Content-Type"];let n;/multipart\/form-data/i.test(r)&&(delete e.headers["content-type"],delete e.headers["Content-Type"]);try{n=await(e.userFetch||fetch)(e.url,e),n=await GE(n,t,e),e.responseInterceptor&&(n=await e.responseInterceptor(n)||n)}catch(a){if(!n)throw a;const o=new Error(n.statusText||`response status is ${n.status}`);throw o.status=n.status,o.statusCode=n.status,o.responseError=a,o}if(!n.ok){const a=new Error(n.statusText||`response status is ${n.status}`);throw a.status=n.status,a.statusCode=n.status,a.response=n,a}return n}const z0=t=>{const{baseDoc:e,url:r}=t,n=e??r??"";return typeof globalThis.document?.baseURI=="string"?String(new URL(n,globalThis.document.baseURI)):n},YE=t=>{const{fetch:e,http:r}=t;return e||r||Mf};async function V0(t){const{spec:e,mode:r,allowMetaPatches:n=!0,pathDiscriminator:a,modelPropertyMacro:o,parameterMacro:p,requestInterceptor:c,responseInterceptor:d,skipNormalization:E=!1,useCircularStructures:x,strategies:k}=t,P=z0(t),R=YE(t),L=k.find((V=>V.match(e)));return(async function(Q){P&&(Kh.refs.docCache[P]=Q),Kh.refs.fetchJSON=zE(R,{requestInterceptor:c,responseInterceptor:d});const J=[Kh.refs];typeof p=="function"&&J.push(Kh.parameters),typeof o=="function"&&J.push(Kh.properties),r!=="strict"&&J.push(Kh.allOf);const re=await(function(le){return new cC(le).dispatch()})({spec:Q,context:{baseDoc:P},plugins:J,allowMetaPatches:n,pathDiscriminator:a,parameterMacro:p,modelPropertyMacro:o,useCircularStructures:x});return E||(re.spec=L.normalize(re.spec)),re})(e)}function J0(t,e){for(var r=e,n=0;nt.replace(/\W/gi,"_"),EC=new Set(["connect","delete","get","head","options","patch","post","put","query","trace"]);function Df(t,e,r="",{v2OperationIdCompatibilityMode:n}={}){return!t||typeof t!="object"?null:(t.operationId||"").replace(/\s/g,"").length?eS(t.operationId):(function(o,p,{v2OperationIdCompatibilityMode:c}={}){if(c){let d=`${p.toLowerCase()}_${o}`.replace(/[\s!@#$%^&*()_+=[{\]};:<>|./?,\\'""-]/g,"_");return d=d||`${o.substring(1)}_${p}`,d.replace(/((_){2,})/g,"_").replace(/^(_)*/g,"").replace(/([_])*$/g,"")}return`${EC.has(p.toLowerCase())?p.toLowerCase():p}${eS(o)}`})(e,r,{v2OperationIdCompatibilityMode:n})}function K0(t){const{spec:e}=t,{paths:r}=e,n={};if(!r||e.$$normalized)return t;for(const p in r){var a;const c=r[p];if(c==null||!Yn()(a=["object","function"]).call(a,typeof c))continue;const d=c.parameters;for(const E in c){var o;const x=c[E];if(x==null||!Yn()(o=["object","function"]).call(o,typeof x))continue;const k=Df(x,p,E);if(k){n[k]?n[k].push(x):n[k]=[x];const P=n[k];if(P.length>1)P.forEach(((R,L)=>{R.__originalOperationId=R.__originalOperationId||R.operationId,R.operationId=`${k}${L+1}`}));else if(x.operationId!==void 0){const R=P[0];R.__originalOperationId=R.__originalOperationId||x.operationId,R.operationId=k}}if(E!=="parameters"){const P=[],R={};for(const L in e)L!=="produces"&&L!=="consumes"&&L!=="security"||(R[L]=e[L],P.push(R));if(d&&(R.parameters=d,P.push(R)),P.length)for(const L of P)for(const V in L)if(Array.isArray(x[V])){if(V==="parameters")for(const Q of L[V])x[V].some((J=>!(!Ls(J)&&!Ls(Q))&&(J===Q||["name","$ref","$$ref"].some((re=>typeof J[re]=="string"&&typeof Q[re]=="string"&&J[re]===Q[re])))))||x[V].push(Q)}else x[V]=L[V]}}}return e.$$normalized=!0,t}const SC={name:"generic",match:()=>!0,normalize(t){const{spec:e}=K0({spec:t});return e},resolve:async t=>V0(t)},G0=SC,tS=t=>{try{const{openapi:e}=t;return typeof e=="string"&&/^3\.0\.(?:[1-9]\d*|0)$/.test(e)}catch{return!1}},Y0=t=>{try{const{openapi:e}=t;return typeof e=="string"&&/^3\.1\.(?:[1-9]\d*|0)$/.test(e)}catch{return!1}},rS=t=>{try{const{openapi:e}=t;return typeof e=="string"&&/^3\.2\.(?:[1-9]\d*|0)$/.test(e)}catch{return!1}},nS=t=>tS(t)||Y0(t)||rS(t),xC={name:"openapi-2",match:t=>(e=>{try{const{swagger:r}=e;return r==="2.0"}catch{return!1}})(t),normalize(t){const{spec:e}=K0({spec:t});return e},resolve:async t=>(async function(r){return V0(r)})(t)},X0=xC,_C={name:"openapi-3-0",match:t=>tS(t),normalize(t){const{spec:e}=K0({spec:t});return e},resolve:async t=>(async function(r){return V0(r)})(t)},Q0=_C;var Ge=Be(34035);function _y(t){return t&&t["@@transducer/reduced"]?t:{"@@transducer/value":t,"@@transducer/reduced":!0}}var OC=(function(){function t(e,r){this.xf=r,this.f=e,this.all=!0}return t.prototype["@@transducer/init"]=Tu,t.prototype["@@transducer/result"]=function(e){return this.all&&(e=this.xf["@@transducer/step"](e,!0)),this.xf["@@transducer/result"](e)},t.prototype["@@transducer/step"]=function(e,r){return this.f(r)||(this.all=!1,e=_y(this.xf["@@transducer/step"](e,!1))),e},t})();function kC(t){return function(e){return new OC(t,e)}}var AC=Tr(Nu(["all"],kC,(function(e,r){for(var n=0;ne.classes.contains("api"))).first}get results(){return this.children.filter((e=>e.classes.contains("result")))}get result(){return this.results.first}get annotations(){return this.children.filter((e=>e.element==="annotation"))}get warnings(){return this.children.filter((e=>e.element==="annotation"&&e.classes.contains("warning")))}get errors(){return this.children.filter((e=>e.element==="annotation"&&e.classes.contains("error")))}get isEmpty(){return this.children.reject((e=>e.element==="annotation")).isEmpty}replaceResult(e){const{result:r}=this;if(Iu(r))return!1;const n=this.content.findIndex((a=>a===r));return n!==-1&&(this.content[n]=e,!0)}}const $s=PC,NC=(t,e)=>typeof e=="object"&&e!==null&&t in e&&typeof e[t]=="function",TC=t=>typeof t=="object"&&t!=null&&"_storedElement"in t&&typeof t._storedElement=="string"&&"_content"in t,IC=(t,e)=>typeof e=="object"&&e!==null&&"primitive"in e&&typeof e.primitive=="function"&&e.primitive()===t,MC=(t,e)=>typeof e=="object"&&e!==null&&"classes"in e&&(Array.isArray(e.classes)||e.classes instanceof Ge.wE)&&e.classes.includes(t),Mu=(t,e)=>typeof e=="object"&&e!==null&&"element"in e&&e.element===t,Qt=t=>t({hasMethod:NC,hasBasicElementProps:TC,primitiveEq:IC,isElementType:Mu,hasClass:MC}),da=Qt((({hasBasicElementProps:t,primitiveEq:e})=>r=>r instanceof Ge.Hg||t(r)&&e(void 0,r))),Ir=Qt((({hasBasicElementProps:t,primitiveEq:e})=>r=>r instanceof Ge.Om||t(r)&&e("string",r))),eb=Qt((({hasBasicElementProps:t,primitiveEq:e})=>r=>r instanceof Ge.kT||t(r)&&e("number",r))),tb=Qt((({hasBasicElementProps:t,primitiveEq:e})=>r=>r instanceof Ge.Os||t(r)&&e("null",r))),Ff=Qt((({hasBasicElementProps:t,primitiveEq:e})=>r=>r instanceof Ge.bd||t(r)&&e("boolean",r))),hn=Qt((({hasBasicElementProps:t,primitiveEq:e,hasMethod:r})=>n=>n instanceof Ge.Sh||t(n)&&e("object",n)&&r("keys",n)&&r("values",n)&&r("items",n))),vo=Qt((({hasBasicElementProps:t,primitiveEq:e,hasMethod:r})=>n=>n instanceof Ge.wE&&!(n instanceof Ge.Sh)||t(n)&&e("array",n)&&r("push",n)&&r("unshift",n)&&r("map",n)&&r("reduce",n))),Bs=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Ge.Pr||t(n)&&e("member",n)&&r(void 0,n))),aS=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Ge.Ft||t(n)&&e("link",n)&&r(void 0,n))),oS=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Ge.sI||t(n)&&e("ref",n)&&r(void 0,n))),RC=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Oy||t(n)&&e("annotation",n)&&r("array",n))),DC=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof ky||t(n)&&e("comment",n)&&r("string",n))),iS=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof $s||t(n)&&e("parseResult",n)&&r("array",n))),$p=t=>Mu("object",t)||Mu("array",t)||Mu("boolean",t)||Mu("number",t)||Mu("string",t)||Mu("null",t)||Mu("member",t),Gh=t=>!!da(t)&&Number.isInteger(t.startPositionRow)&&Number.isInteger(t.startPositionColumn)&&Number.isInteger(t.startIndex)&&Number.isInteger(t.endPositionRow)&&Number.isInteger(t.endPositionColumn)&&Number.isInteger(t.endIndex),FC=(t,e)=>{if(t.length===0)return!0;const r=e.attributes.get("symbols");return!!vo(r)&&Z0(gy(r.toValue()),t)},Yh=(t,e)=>t.length===0||Z0(gy(e.classes.toValue()),t),kc=function(){return!0},rb=function(){return!1},Ay=(t,e,r)=>{const n=t[e];if(n!=null){if(!r&&typeof n=="function")return n;const a=r?n.leave:n.enter;if(typeof a=="function")return a}else{const a=r?t.leave:t.enter;if(a!=null){if(typeof a=="function")return a;const o=a[e];if(typeof o=="function")return o}}return null},on={},Lf=t=>t?.type,sS=t=>typeof Lf(t)=="string",nb=t=>Object.create(Object.getPrototypeOf(t),Object.getOwnPropertyDescriptors(t)),Cy=(t,{visitFnGetter:e=Ay,nodeTypeGetter:r=Lf,breakSymbol:n=on,deleteNodeSymbol:a=null,skipVisitingNodeSymbol:o=!1,exposeEdits:p=!1}={})=>{const c=Symbol("skip"),d=new Array(t.length).fill(c);return{enter(E,x,k,P,R,L){let V=E,Q=!1;const J={...L,replaceWith(re,me){L.replaceWith(re,me),V=re}};for(let re=0;re{const c=Symbol("skip"),d=new Array(t.length).fill(c);return{async enter(E,x,k,P,R,L){let V=E,Q=!1;const J={...L,replaceWith(re,me){L.replaceWith(re,me),V=re}};for(let re=0;re{const R=r||{};let L,V,Q=Array.isArray(t),J=[t],re=-1,me=[],le=t;const ve=[],be=[];do{re+=1;const Ae=re===J.length;let xe;const Oe=Ae&&me.length!==0;if(Ae){if(xe=be.length===0?void 0:ve.pop(),le=V,V=be.pop(),Oe)if(Q){le=le.slice();let G=0;for(const[de,Ne]of me){const Ye=de-G;Ne===o?(le.splice(Ye,1),G+=1):le[Ye]=Ne}}else{le=x(le);for(const[G,de]of me)le[G]=de}re=L.index,J=L.keys,me=L.edits,Q=L.inArray,L=L.prev}else if(V!==o&&V!==void 0){if(xe=Q?re:J[re],le=V[xe],le===o||le===void 0)continue;ve.push(xe)}let je;if(!Array.isArray(le)){var ce;if(!E(le))throw new xr(`Invalid AST Node: ${String(le)}`,{node:le});if(k&&be.includes(le)){typeof P=="function"&&P(le,xe,V,ve,be),ve.pop();continue}const G=c(e,d(le),Ae);if(G){for(const[Ne,Ye]of Object.entries(n))e[Ne]=Ye;const de={replaceWith(Ne,Ye){typeof Ye=="function"?Ye(Ne,le,xe,V,ve,be):V&&(V[xe]=Ne),Ae||(le=Ne)}};je=G.call(e,le,xe,V,ve,be,de)}if(typeof((ce=je)===null||ce===void 0?void 0:ce.then)=="function")throw new xr("Async visitor not supported in sync mode",{visitor:e,visitFn:G});if(je===a)break;if(je===p){if(!Ae){ve.pop();continue}}else if(je!==void 0&&(me.push([xe,je]),!Ae)){if(!E(je)){ve.pop();continue}le=je}}var _e;je===void 0&&Oe&&me.push([xe,le]),!Ae&&(L={inArray:Q,index:re,keys:J,edits:me,prev:L},Q=Array.isArray(le),J=Q?le:(_e=R[d(le)])!==null&&_e!==void 0?_e:[],re=-1,me=[],V!==o&&V!==void 0&&be.push(V),V=le)}while(L!==void 0);return me.length!==0?me[me.length-1][1]:t};ab[Symbol.for("nodejs.util.promisify.custom")]=async(t,e,{keyMap:r=null,state:n={},breakSymbol:a=on,deleteNodeSymbol:o=null,skipVisitingNodeSymbol:p=!1,visitFnGetter:c=Ay,nodeTypeGetter:d=Lf,nodePredicate:E=sS,nodeCloneFn:x=nb,detectCycles:k=!0,detectCyclesCallback:P=null}={})=>{const R=r||{};let L,V,Q=Array.isArray(t),J=[t],re=-1,me=[],le=t;const ve=[],be=[];do{re+=1;const _e=re===J.length;let Ae;const xe=_e&&me.length!==0;if(_e){if(Ae=be.length===0?void 0:ve.pop(),le=V,V=be.pop(),xe)if(Q){le=le.slice();let je=0;for(const[G,de]of me){const Ne=G-je;de===o?(le.splice(Ne,1),je+=1):le[Ne]=de}}else{le=x(le);for(const[je,G]of me)le[je]=G}re=L.index,J=L.keys,me=L.edits,Q=L.inArray,L=L.prev}else if(V!==o&&V!==void 0){if(Ae=Q?re:J[re],le=V[Ae],le===o||le===void 0)continue;ve.push(Ae)}let Oe;if(!Array.isArray(le)){if(!E(le))throw new xr(`Invalid AST Node: ${String(le)}`,{node:le});if(k&&be.includes(le)){typeof P=="function"&&P(le,Ae,V,ve,be),ve.pop();continue}const je=c(e,d(le),_e);if(je){for(const[de,Ne]of Object.entries(n))e[de]=Ne;const G={replaceWith(de,Ne){typeof Ne=="function"?Ne(de,le,Ae,V,ve,be):V&&(V[Ae]=de),_e||(le=de)}};Oe=await je.call(e,le,Ae,V,ve,be,G)}if(Oe===a)break;if(Oe===p){if(!_e){ve.pop();continue}}else if(Oe!==void 0&&(me.push([Ae,Oe]),!_e)){if(!E(Oe)){ve.pop();continue}le=Oe}}var ce;Oe===void 0&&xe&&me.push([Ae,le]),!_e&&(L={inArray:Q,index:re,keys:J,edits:me,prev:L},Q=Array.isArray(le),J=Q?le:(ce=R[d(le)])!==null&&ce!==void 0?ce:[],re=-1,me=[],V!==o&&V!==void 0&&be.push(V),V=le)}while(L!==void 0);return me.length!==0?me[me.length-1][1]:t};const lS=class extends xr{value;constructor(e,r){super(e,r),r!==void 0&&(this.value=r.value)}},LC=class extends lS{},$C=class extends lS{},BC=Tr((function(e,r){return Pf((function(n,a){return n[a]=e(r[a],a,r),n}),{},Rp(r))})),jy=_r((function(e){return e==null}));var qC=Tr((function(e,r){if(e.length===0||jy(r))return!1;for(var n=r,a=0;a{const r=Jh(t,e);return BC((n=>{if(Ls(n)&&$f("$ref",n)&&VC(Lp,"$ref",n)){const a=ro(["$ref"],n),o=cS("#/",a);return ro(o.split("/"),r)}return Ls(n)?Ru(n,r):n}),t)},ob=(t,e)=>(t.startPositionRow=e?.startPositionRow,t.startPositionColumn=e?.startPositionColumn,t.startIndex=e?.startIndex,t.endPositionRow=e?.endPositionRow,t.endPositionColumn=e?.endPositionColumn,t.endIndex=e?.endIndex,t),hr=(t,e={})=>{const{visited:r=new WeakMap}=e,n={...e,visited:r};if(r.has(t))return r.get(t);if(t instanceof Ge.KeyValuePair){const{key:a,value:o}=t,p=da(a)?hr(a,n):a,c=da(o)?hr(o,n):o,d=new Ge.KeyValuePair(p,c);return r.set(t,d),d}if(t instanceof Ge.ot){const a=c=>hr(c,n),o=[...t].map(a),p=new Ge.ot(o);return r.set(t,p),p}if(t instanceof Ge.G6){const a=c=>hr(c,n),o=[...t].map(a),p=new Ge.G6(o);return r.set(t,p),p}if(da(t)){const a=no(t);if(r.set(t,a),t.content)if(da(t.content))a.content=hr(t.content,n);else if(t.content instanceof Ge.KeyValuePair)a.content=hr(t.content,n);else if(Array.isArray(t.content)){const o=p=>hr(p,n);a.content=t.content.map(o)}else a.content=t.content;else a.content=t.content;return a}throw new LC("Value provided to cloneDeep function couldn't be cloned",{value:t})};hr.safe=t=>{try{return hr(t)}catch{return t}};const uS=t=>{const{key:e,value:r}=t;return new Ge.KeyValuePair(e,r)},pS=t=>{const e=new t.constructor;if(e.element=t.element,Gh(t)&&ob(e,t),t.meta.length>0&&(e._meta=hr(t.meta)),t.attributes.length>0&&(e._attributes=hr(t.attributes)),da(t.content)){const r=t.content;e.content=pS(r)}else Array.isArray(t.content)?e.content=[...t.content]:t.content instanceof Ge.KeyValuePair?e.content=uS(t.content):e.content=t.content;return e},no=t=>{if(t instanceof Ge.KeyValuePair)return uS(t);if(t instanceof Ge.ot)return(e=>{const r=[...e];return new Ge.ot(r)})(t);if(t instanceof Ge.G6)return(e=>{const r=[...e];return new Ge.G6(r)})(t);if(da(t))return pS(t);throw new $C("Value provided to cloneShallow function couldn't be cloned",{value:t})};no.safe=t=>{try{return no(t)}catch{return t}};const Xh=t=>hn(t)?"ObjectElement":vo(t)?"ArrayElement":Bs(t)?"MemberElement":Ir(t)?"StringElement":Ff(t)?"BooleanElement":eb(t)?"NumberElement":tb(t)?"NullElement":aS(t)?"LinkElement":oS(t)?"RefElement":void 0,hS=t=>da(t)?no(t):nb(t),dS=mo(Xh,Lp),Bl={ObjectElement:["content"],ArrayElement:["content"],MemberElement:["key","value"],StringElement:[],BooleanElement:[],NumberElement:[],NullElement:[],RefElement:[],LinkElement:[],Annotation:[],Comment:[],ParseResultElement:["content"]};class fS{result;predicate;returnOnTrue;returnOnFalse;constructor({predicate:e=rb,returnOnTrue:r,returnOnFalse:n}={}){this.result=[],this.predicate=e,this.returnOnTrue=r,this.returnOnFalse=n}enter(e){return this.predicate(e)?(this.result.push(e),this.returnOnTrue):this.returnOnFalse}}const Pa=(t,e,{keyMap:r=Bl,...n}={})=>ab(t,e,{keyMap:r,nodeTypeGetter:Xh,nodePredicate:dS,nodeCloneFn:hS,...n});Pa[Symbol.for("nodejs.util.promisify.custom")]=async(t,e,{keyMap:r=Bl,...n}={})=>ab[Symbol.for("nodejs.util.promisify.custom")](t,e,{keyMap:r,nodeTypeGetter:Xh,nodePredicate:dS,nodeCloneFn:hS,...n});const mS=t=>typeof t?.type=="string"?t.type:Xh(t),gS={EphemeralObject:["content"],EphemeralArray:["content"],...Bl},yS=(t,e,{keyMap:r=gS,...n}={})=>Pa(t,e,{keyMap:r,nodeTypeGetter:mS,nodePredicate:kc,detectCycles:!1,deleteNodeSymbol:Symbol.for("delete-node"),skipVisitingNodeSymbol:Symbol.for("skip-visiting-node"),...n});yS[Symbol.for("nodejs.util.promisify.custom")]=async(t,{keyMap:e=gS,...r}={})=>Pa[Symbol.for("nodejs.util.promisify.custom")](t,visitor,{keyMap:e,nodeTypeGetter:mS,nodePredicate:kc,detectCycles:!1,deleteNodeSymbol:Symbol.for("delete-node"),skipVisitingNodeSymbol:Symbol.for("skip-visiting-node"),...r});const KC=class{type="EphemeralArray";content=[];reference=void 0;constructor(e){this.content=e,this.reference=[]}toReference(){return this.reference}toArray(){return this.reference.push(...this.content),this.reference}},GC=class{type="EphemeralObject";content=[];reference=void 0;constructor(e){this.content=e,this.reference={}}toReference(){return this.reference}toObject(){return Object.assign(this.reference,Object.fromEntries(this.content))}};class YC{ObjectElement={enter:e=>{if(this.references.has(e))return this.references.get(e).toReference();const r=new GC(e.content);return this.references.set(e,r),r}};EphemeralObject={leave:e=>e.toObject()};MemberElement={enter:e=>[e.key,e.value]};ArrayElement={enter:e=>{if(this.references.has(e))return this.references.get(e).toReference();const r=new KC(e.content);return this.references.set(e,r),r}};EphemeralArray={leave:e=>e.toArray()};references=new WeakMap;BooleanElement(e){return e.toValue()}NumberElement(e){return e.toValue()}StringElement(e){return e.toValue()}NullElement(){return null}RefElement(e,...r){var n;const a=r[3];return((n=a[a.length-1])===null||n===void 0?void 0:n.type)==="EphemeralObject"?Symbol.for("delete-node"):String(e.toValue())}LinkElement(e){return Ir(e.href)?e.href.toValue():""}}const Et=t=>da(t)?Ir(t)||eb(t)||Ff(t)||tb(t)?t.toValue():yS(t,new YC):t,XC=ha((function(e,r,n){var a,o={};for(a in n=n||{},r=r||{})ts(a,r)&&(o[a]=ts(a,n)?e(a,r[a],n[a]):r[a]);for(a in n)ts(a,n)&&!ts(a,o)&&(o[a]=n[a]);return o})),QC=ha((function t(e,r,n){return XC((function(a,o,p){return my(o)&&my(p)?t(e,o,p):e(a,o,p)}),r,n)})),Py=Tr((function(e,r){return QC((function(n,a,o){return o}),e,r)})),ZC=kf(0,-1),ej=Tr((function(e,r){return e.apply(this,r)})),tj=If(Fp);var rj=_r((function(e){return e!=null&&typeof e["fantasy-land/empty"]=="function"?e["fantasy-land/empty"]():e!=null&&e.constructor!=null&&typeof e.constructor["fantasy-land/empty"]=="function"?e.constructor["fantasy-land/empty"]():e!=null&&typeof e.empty=="function"?e.empty():e!=null&&e.constructor!=null&&typeof e.constructor.empty=="function"?e.constructor.empty():Dl(e)?[]:Of(e)?"":my(e)?{}:oE(e)?(function(){return arguments})():(function(n){var a=Object.prototype.toString.call(n);return a==="[object Uint8ClampedArray]"||a==="[object Int8Array]"||a==="[object Uint8Array]"||a==="[object Int16Array]"||a==="[object Uint16Array]"||a==="[object Int32Array]"||a==="[object Uint32Array]"||a==="[object Float32Array]"||a==="[object Float64Array]"||a==="[object BigInt64Array]"||a==="[object BigUint64Array]"})(e)?e.constructor.from(""):void 0}));const nj=rj,Bf=_r((function(e){return e!=null&&Pu(e,nj(e))})),vS=ja(1,Fp(Array.isArray)?Array.isArray:mo(Fs,Dp("Array"))),aj=Rf(vS,Bf);var oj=ja(3,(function(t,e,r){var n=ro(t,r),a=ro(ZC(t),r);if(!tj(n)&&!aj(t)){var o=tE(n,a);return ej(o,e)}}));const qf=oj;class bS extends Ge.g${constructor(){super(),this.register("annotation",Oy),this.register("comment",ky),this.register("parseResult",$s)}}const ij=new bS,Ac=t=>{const e=new bS;return Ls(t)&&e.use(t),e},wS=ij,ES=()=>({predicates:{...B},namespace:wS}),SS={toolboxCreator:ES,visitorOptions:{nodeTypeGetter:Xh,exposeEdits:!0}},ql=(t,e,r={})=>{if(e.length===0)return t;const n=Py(SS,r),{toolboxCreator:a,visitorOptions:o}=n,p=a(),c=e.map((x=>x(p))),d=Cy(c.map(uy({},"visitor")),{...o});c.forEach(qf(["pre"],[]));const E=Pa(t,d,o);return c.forEach(qf(["post"],[])),E};ql[Symbol.for("nodejs.util.promisify.custom")]=async(t,e,r={})=>{if(e.length===0)return t;const n=Py(SS,r),{toolboxCreator:a,visitorOptions:o}=n,p=a(),c=e.map((P=>P(p))),d=Cy[Symbol.for("nodejs.util.promisify.custom")],E=Pa[Symbol.for("nodejs.util.promisify.custom")],x=d(c.map(uy({},"visitor")),{...o});await Promise.allSettled(c.map(qf(["pre"],[])));const k=await E(t,x,o);return await Promise.allSettled(c.map(qf(["post"],[]))),k};const sj=(t,{Type:e,plugins:r=[]})=>{const n=new e(t);return da(t)&&(t.meta.length>0&&(n.meta=hr(t.meta)),t.attributes.length>0&&(n.attributes=hr(t.attributes))),ql(n,r,{toolboxCreator:ES,visitorOptions:{nodeTypeGetter:Xh}})},qs=t=>(e,r={})=>sj(e,{...r,Type:t});Ge.Sh.refract=qs(Ge.Sh),Ge.wE.refract=qs(Ge.wE),Ge.Om.refract=qs(Ge.Om),Ge.bd.refract=qs(Ge.bd),Ge.Os.refract=qs(Ge.Os),Ge.kT.refract=qs(Ge.kT),Ge.Ft.refract=qs(Ge.Ft),Ge.sI.refract=qs(Ge.sI),Oy.refract=qs(Oy),ky.refract=qs(ky),$s.refract=qs($s);const Ny=(t,e=new WeakMap)=>(Bs(t)?(e.set(t.key,t),Ny(t.key,e),e.set(t.value,t),Ny(t.value,e)):t.children.forEach((r=>{e.set(r,t),Ny(r,e)})),e),lj=class{element;edges;constructor({element:e}){this.element=e}transclude(e,r){var n;if(e===this.element)return r;if(e===r)return this.element;this.edges=(n=this.edges)!==null&&n!==void 0?n:Ny(this.element);const a=this.edges.get(e);return Iu(a)?void 0:(hn(a)?((o,p,c)=>{const d=c.get(o);hn(d)&&(d.content=d.map(((E,x,k)=>k===o?(c.delete(o),c.set(p,d),p):k)))})(e,r,this.edges):vo(a)?((o,p,c)=>{const d=c.get(o);vo(d)&&(d.content=d.map((E=>E===o?(c.delete(o),c.set(p,d),p):E)))})(e,r,this.edges):Bs(a)&&((o,p,c)=>{const d=c.get(o);Bs(d)&&(d.key===o&&(d.key=p,c.delete(o),c.set(p,d)),d.value===o&&(d.value=p,c.delete(o),c.set(p,d)))})(e,r,this.edges),this.element)}},rs=t=>{const e=t.indexOf("#");return(r=>{try{const n=r.startsWith("#")?r.slice(1):r;return decodeURIComponent(n)}catch{return r}})(e===-1?"#":t.substring(e))},ns=function(){const e=Br,r=Ni,n=this,a="parser.js: Parser(): ";n.ast=void 0,n.stats=void 0,n.trace=void 0,n.callbacks=[];let o,p,c,d,E,x,k,P=0,R=0,L=0,V=0,Q=0,J=new function(){this.state=e.ACTIVE,this.phraseLength=0,this.refresh=()=>{this.state=e.ACTIVE,this.phraseLength=0}};n.parse=(ve,be,ce,_e)=>{const Ae=`${a}parse(): `;P=0,R=0,L=0,V=0,Q=0,o=void 0,p=void 0,c=void 0,d=void 0,J.refresh(),E=void 0,x=void 0,k=void 0,d=r.stringToChars(ce),o=ve.rules,p=ve.udts;const xe=be.toLowerCase();let Oe;for(const G in o)if(o.hasOwnProperty(G)&&xe===o[G].lower){Oe=o[G].index;break}if(Oe===void 0)throw new Error(`${Ae}start rule name '${startRule}' not recognized`);(()=>{const G=`${a}initializeCallbacks(): `;let de,Ne;for(E=[],x=[],de=0;de{if(be.phraseLength>ce){let Ae=`${a}opRNM(${ve.name}): callback function error: `;throw Ae+=`sysData.phraseLength: ${be.phraseLength}`,Ae+=` must be <= remaining chars: ${ce}`,new Error(Ae)}switch(be.state){case e.ACTIVE:if(!_e)throw new Error(`${a}opRNM(${ve.name}): callback function return error. ACTIVE state not allowed.`);break;case e.EMPTY:be.phraseLength=0;break;case e.MATCH:be.phraseLength===0&&(be.state=e.EMPTY);break;case e.NOMATCH:be.phraseLength=0;break;default:throw new Error(`${a}opRNM(${ve.name}): callback function return error. Unrecognized return state: ${be.state}`)}},me=(ve,be)=>{let ce,_e,Ae;const xe=c[ve],Oe=p[xe.index];J.UdtIndex=Oe.index,P||(Ae=n.ast&&n.ast.udtDefined(xe.index),Ae&&(_e=o.length+xe.index,ce=n.ast.getLength(),n.ast.down(_e,Oe.name)));const je=d.length-be;x[xe.index](J,d,be,k),((G,de,Ne)=>{if(de.phraseLength>Ne){let Ye=`${a}opUDT(${G.name}): callback function error: `;throw Ye+=`sysData.phraseLength: ${de.phraseLength}`,Ye+=` must be <= remaining chars: ${Ne}`,new Error(Ye)}switch(de.state){case e.ACTIVE:throw new Error(`${a}opUDT(${G.name}) ACTIVE state return not allowed.`);case e.EMPTY:if(!G.empty)throw new Error(`${a}opUDT(${G.name}) may not return EMPTY.`);de.phraseLength=0;break;case e.MATCH:if(de.phraseLength===0){if(!G.empty)throw new Error(`${a}opUDT(${G.name}) may not return EMPTY.`);de.state=e.EMPTY}break;case e.NOMATCH:de.phraseLength=0;break;default:throw new Error(`${a}opUDT(${G.name}): callback function return error. Unrecognized return state: ${de.state}`)}})(Oe,J,je),P||Ae&&(J.state===e.NOMATCH?n.ast.setLength(ce):n.ast.up(_e,Oe.name,be,J.phraseLength))},le=(ve,be)=>{const ce=`${a}opExecute(): `,_e=c[ve];switch(V+=1,R>L&&(L=R),R+=1,J.refresh(),n.trace&&n.trace.down(_e,be),_e.type){case e.ALT:((Ae,xe)=>{const Oe=c[Ae];for(let je=0;je{let Oe,je,G,de;const Ne=c[Ae];n.ast&&(je=n.ast.getLength()),Oe=!0,G=xe,de=0;for(let Ye=0;Ye{let Oe,je,G,de;const Ne=c[Ae];if(Ne.max===0)return J.state=e.EMPTY,void(J.phraseLength=0);for(je=xe,G=0,de=0,n.ast&&(Oe=n.ast.getLength());!(je>=d.length)&&(le(Ae+1,je),J.state!==e.NOMATCH)&&J.state!==e.EMPTY&&(de+=1,G+=J.phraseLength,je+=J.phraseLength,de!==Ne.max););J.state===e.EMPTY||de>=Ne.min?(J.state=G===0?e.EMPTY:e.MATCH,J.phraseLength=G):(J.state=e.NOMATCH,J.phraseLength=0,n.ast&&n.ast.setLength(Oe))})(ve,be);break;case e.RNM:((Ae,xe)=>{let Oe,je,G;const de=c[Ae],Ne=o[de.index],Ye=E[Ne.index];if(P||(je=n.ast&&n.ast.ruleDefined(de.index),je&&(Oe=n.ast.getLength(),n.ast.down(de.index,o[de.index].name))),Ye){const ht=d.length-xe;Ye(J,d,xe,k),re(Ne,J,ht,!0),J.state===e.ACTIVE&&(G=c,c=Ne.opcodes,le(0,xe),c=G,Ye(J,d,xe,k),re(Ne,J,ht,!1))}else G=c,c=Ne.opcodes,le(0,xe),c=G;P||je&&(J.state===e.NOMATCH?n.ast.setLength(Oe):n.ast.up(de.index,Ne.name,xe,J.phraseLength))})(ve,be);break;case e.TRG:((Ae,xe)=>{const Oe=c[Ae];J.state=e.NOMATCH,xe{const Oe=c[Ae],je=Oe.string.length;if(J.state=e.NOMATCH,xe+je<=d.length){for(let G=0;G{let Oe;const je=c[Ae];J.state=e.NOMATCH;const G=je.string.length;if(G!==0){if(xe+G<=d.length){for(let de=0;de=65&&Oe<=90&&(Oe+=32),Oe!==je.string[de])return;J.state=e.MATCH,J.phraseLength=G}}else J.state=e.EMPTY})(ve,be);break;case e.UDT:me(ve,be);break;case e.AND:((Ae,xe)=>{switch(P+=1,le(Ae+1,xe),P-=1,J.phraseLength=0,J.state){case e.EMPTY:case e.MATCH:J.state=e.EMPTY;break;case e.NOMATCH:J.state=e.NOMATCH;break;default:throw new Error(`opAND: invalid state ${J.state}`)}})(ve,be);break;case e.NOT:((Ae,xe)=>{switch(P+=1,le(Ae+1,xe),P-=1,J.phraseLength=0,J.state){case e.EMPTY:case e.MATCH:J.state=e.NOMATCH;break;case e.NOMATCH:J.state=e.EMPTY;break;default:throw new Error(`opNOT: invalid state ${J.state}`)}})(ve,be);break;default:throw new Error(`${ce}unrecognized operator`)}P||be+J.phraseLength>Q&&(Q=be+J.phraseLength),n.stats&&n.stats.collect(_e,J),n.trace&&n.trace.up(_e,J.state,be,J.phraseLength),R-=1}},ib=function(){const e=Br,r=Ni,n=this;let a,o,p,c=0;const d=[],E=[],x=[];function k(P){let R="";for(;P-- >0;)R+=" ";return R}n.callbacks=[],n.init=(P,R,L)=>{let V;E.length=0,x.length=0,c=0,a=P,o=R,p=L;const Q=[];for(V=0;V!!d[P],n.udtDefined=P=>!!d[a.length+P],n.down=(P,R)=>{const L=x.length;return E.push(L),x.push({name:R,thisIndex:L,thatIndex:void 0,state:e.SEM_PRE,callbackIndex:P,phraseIndex:void 0,phraseLength:void 0,stack:E.length}),L},n.up=(P,R,L,V)=>{const Q=x.length,J=E.pop();return x.push({name:R,thisIndex:Q,thatIndex:J,state:e.SEM_POST,callbackIndex:P,phraseIndex:L,phraseLength:V,stack:E.length}),x[J].thatIndex=Q,x[J].phraseIndex=L,x[J].phraseLength=V,Q},n.translate=P=>{let R,L;for(let V=0;V{x.length=P,E.length=P>0?x[P-1].stack:0},n.getLength=()=>x.length,n.toXml=()=>{let P="",R=0;return P+=` +`,P+=` +`,P+=` +`,P+=k(R+2),P+=r.charsToString(p),P+=` +`,x.forEach((L=>{L.state===e.SEM_PRE?(R+=1,P+=k(R),P+=` +`,P+=k(R+2),P+=r.charsToString(p,L.phraseIndex,L.phraseLength),P+=` +`):(P+=k(R),P+=` +`,R-=1)})),P+=` +`,P}},cj=function(){const e=Br,r=Ni,n="parser.js: Trace(): ";let a,o,p,c="",d=0;const E=this,x=P=>{let R="",L=0;if(P>=0)for(;P--;)L+=1,L===5?(R+="|",L=0):R+=".";return R};E.init=(P,R,L)=>{o=P,p=R,a=L};const k=P=>{let R;switch(P.type){case e.ALT:R="ALT";break;case e.CAT:R="CAT";break;case e.REP:R=P.max===1/0?`REP(${P.min},inf)`:`REP(${P.min},${P.max})`;break;case e.RNM:R=`RNM(${o[P.index].name})`;break;case e.TRG:R=`TRG(${P.min},${P.max})`;break;case e.TBS:R=P.string.length>6?`TBS(${r.charsToString(P.string,0,3)}...)`:`TBS(${r.charsToString(P.string,0,6)})`;break;case e.TLS:R=P.string.length>6?`TLS(${r.charsToString(P.string,0,3)}...)`:`TLS(${r.charsToString(P.string,0,6)})`;break;case e.UDT:R=`UDT(${p[P.index].name})`;break;case e.AND:R="AND";break;case e.NOT:R="NOT";break;default:throw new Error(`${n}Trace: opName: unrecognized opcode`)}return R};E.down=(P,R)=>{const L=x(d),V=Math.min(100,a.length-R);let Q=r.charsToString(a,R,V);V{const Q=`${n}trace.up: `;d-=1;const J=x(d);let re,me,le;switch(R){case e.EMPTY:le="|E|",me="''";break;case e.MATCH:le="|M|",re=Math.min(100,V),me=rec},uj=function(){const e=Br;let r,n,a;const o=[],p=[],c=[];this.init=(V,Q)=>{r=V,n=Q,R()},this.collect=(V,Q)=>{L(a,Q.state,Q.phraseLength),L(o[V.type],Q.state,Q.phraseLength),V.type===e.RNM&&L(p[V.index],Q.state,Q.phraseLength),V.type===e.UDT&&L(c[V.index],Q.state,Q.phraseLength)},this.displayStats=()=>{let V="";const Q={match:0,empty:0,nomatch:0,total:0},J=(re,me,le,ve,be)=>(Q.match+=me,Q.empty+=le,Q.nomatch+=ve,Q.total+=be,`${re} | ${d(me)} | ${d(le)} | ${d(ve)} | ${d(be)} | +`);return V+=` OPERATOR STATS +`,V+=` | MATCH | EMPTY | NOMATCH | TOTAL | +`,V+=J(" ALT",o[e.ALT].match,o[e.ALT].empty,o[e.ALT].nomatch,o[e.ALT].total),V+=J(" CAT",o[e.CAT].match,o[e.CAT].empty,o[e.CAT].nomatch,o[e.CAT].total),V+=J(" REP",o[e.REP].match,o[e.REP].empty,o[e.REP].nomatch,o[e.REP].total),V+=J(" RNM",o[e.RNM].match,o[e.RNM].empty,o[e.RNM].nomatch,o[e.RNM].total),V+=J(" TRG",o[e.TRG].match,o[e.TRG].empty,o[e.TRG].nomatch,o[e.TRG].total),V+=J(" TBS",o[e.TBS].match,o[e.TBS].empty,o[e.TBS].nomatch,o[e.TBS].total),V+=J(" TLS",o[e.TLS].match,o[e.TLS].empty,o[e.TLS].nomatch,o[e.TLS].total),V+=J(" UDT",o[e.UDT].match,o[e.UDT].empty,o[e.UDT].nomatch,o[e.UDT].total),V+=J(" AND",o[e.AND].match,o[e.AND].empty,o[e.AND].nomatch,o[e.AND].total),V+=J(" NOT",o[e.NOT].match,o[e.NOT].empty,o[e.NOT].nomatch,o[e.NOT].total),V+=J("TOTAL",Q.match,Q.empty,Q.nomatch,Q.total),V},this.displayHits=V=>{let Q="";const J=(re,me,le,ve,be)=>(a.match+=re,a.empty+=me,a.nomatch+=le,a.total+=ve,`| ${d(re)} | ${d(me)} | ${d(le)} | ${d(ve)} | ${be} +`);typeof V=="string"&&V.toLowerCase()[0]==="a"?(p.sort(E),c.sort(E),Q+=` RULES/UDTS ALPHABETICALLY +`):typeof V=="string"&&V.toLowerCase()[0]==="i"?(p.sort(k),c.sort(k),Q+=` RULES/UDTS BY INDEX +`):(p.sort(x),c.sort(x),Q+=` RULES/UDTS BY HIT COUNT +`),Q+=`| MATCH | EMPTY | NOMATCH | TOTAL | NAME +`;for(let re=0;reV<10?` ${V}`:V<100?` ${V}`:V<1e3?` ${V}`:V<1e4?` ${V}`:V<1e5?` ${V}`:V<1e6?` ${V}`:`${V}`,E=(V,Q)=>V.lowerQ.lower?1:0,x=(V,Q)=>V.totalQ.total?-1:E(V,Q),k=(V,Q)=>V.indexQ.index?1:0,P=function(){this.empty=0,this.match=0,this.nomatch=0,this.total=0},R=()=>{o.length=0,a=new P,o[e.ALT]=new P,o[e.CAT]=new P,o[e.REP]=new P,o[e.RNM]=new P,o[e.TRG]=new P,o[e.TBS]=new P,o[e.TLS]=new P,o[e.UDT]=new P,o[e.AND]=new P,o[e.NOT]=new P,p.length=0;for(let V=0;V0){c.length=0;for(let V=0;V{switch(V.total+=1,Q){case e.EMPTY:V.empty+=1;break;case e.MATCH:V.match+=1;break;case e.NOMATCH:V.nomatch+=1;break;default:throw new Error(`parser.js: Stats(): collect(): incStat(): unrecognized state: ${Q}`)}}},Ni={stringToChars:t=>[...t].map((e=>e.codePointAt(0))),charsToString:(t,e,r)=>{let n=t;for(;!(e===void 0||e<0);){if(r===void 0){n=t.slice(e);break}if(r<=0)return"";n=t.slice(e,e+r);break}return String.fromCodePoint(...n)}},Br={ALT:1,CAT:2,REP:3,RNM:4,TRG:5,TBS:6,TLS:7,UDT:11,AND:12,NOT:13,ACTIVE:100,MATCH:101,EMPTY:102,NOMATCH:103,SEM_PRE:200,SEM_POST:201,SEM_OK:300,idName:t=>{switch(t){case Br.ALT:return"ALT";case Br.CAT:return"CAT";case Br.REP:return"REP";case Br.RNM:return"RNM";case Br.TRG:return"TRG";case Br.TBS:return"TBS";case Br.TLS:return"TLS";case Br.UDT:return"UDT";case Br.AND:return"AND";case Br.NOT:return"NOT";case Br.ACTIVE:return"ACTIVE";case Br.EMPTY:return"EMPTY";case Br.MATCH:return"MATCH";case Br.NOMATCH:return"NOMATCH";case Br.SEM_PRE:return"SEM_PRE";case Br.SEM_POST:return"SEM_POST";case Br.SEM_OK:return"SEM_OK";default:return"UNRECOGNIZED STATE"}}};function Uf(){this.grammarObject="grammarObject",this.rules=[],this.rules[0]={name:"json-pointer",lower:"json-pointer",index:0,isBkr:!1},this.rules[1]={name:"reference-token",lower:"reference-token",index:1,isBkr:!1},this.rules[2]={name:"unescaped",lower:"unescaped",index:2,isBkr:!1},this.rules[3]={name:"escaped",lower:"escaped",index:3,isBkr:!1},this.rules[4]={name:"array-location",lower:"array-location",index:4,isBkr:!1},this.rules[5]={name:"array-index",lower:"array-index",index:5,isBkr:!1},this.rules[6]={name:"array-dash",lower:"array-dash",index:6,isBkr:!1},this.rules[7]={name:"slash",lower:"slash",index:7,isBkr:!1},this.udts=[],this.rules[0].opcodes=[],this.rules[0].opcodes[0]={type:3,min:0,max:1/0},this.rules[0].opcodes[1]={type:2,children:[2,3]},this.rules[0].opcodes[2]={type:4,index:7},this.rules[0].opcodes[3]={type:4,index:1},this.rules[1].opcodes=[],this.rules[1].opcodes[0]={type:3,min:0,max:1/0},this.rules[1].opcodes[1]={type:1,children:[2,3]},this.rules[1].opcodes[2]={type:4,index:2},this.rules[1].opcodes[3]={type:4,index:3},this.rules[2].opcodes=[],this.rules[2].opcodes[0]={type:1,children:[1,2,3]},this.rules[2].opcodes[1]={type:5,min:0,max:46},this.rules[2].opcodes[2]={type:5,min:48,max:125},this.rules[2].opcodes[3]={type:5,min:127,max:1114111},this.rules[3].opcodes=[],this.rules[3].opcodes[0]={type:2,children:[1,2]},this.rules[3].opcodes[1]={type:7,string:[126]},this.rules[3].opcodes[2]={type:1,children:[3,4]},this.rules[3].opcodes[3]={type:7,string:[48]},this.rules[3].opcodes[4]={type:7,string:[49]},this.rules[4].opcodes=[],this.rules[4].opcodes[0]={type:1,children:[1,2]},this.rules[4].opcodes[1]={type:4,index:5},this.rules[4].opcodes[2]={type:4,index:6},this.rules[5].opcodes=[],this.rules[5].opcodes[0]={type:1,children:[1,2]},this.rules[5].opcodes[1]={type:6,string:[48]},this.rules[5].opcodes[2]={type:2,children:[3,4]},this.rules[5].opcodes[3]={type:5,min:49,max:57},this.rules[5].opcodes[4]={type:3,min:0,max:1/0},this.rules[5].opcodes[5]={type:5,min:48,max:57},this.rules[6].opcodes=[],this.rules[6].opcodes[0]={type:7,string:[45]},this.rules[7].opcodes=[],this.rules[7].opcodes[0]={type:7,string:[47]},this.toString=function(){return`; JavaScript Object Notation (JSON) Pointer ABNF syntax +; https://datatracker.ietf.org/doc/html/rfc6901 +json-pointer = *( slash reference-token ) ; MODIFICATION: surrogate text rule used +reference-token = *( unescaped / escaped ) +unescaped = %x00-2E / %x30-7D / %x7F-10FFFF + ; %x2F ('/') and %x7E ('~') are excluded from 'unescaped' +escaped = "~" ( "0" / "1" ) + ; representing '~' and '/', respectively + +; https://datatracker.ietf.org/doc/html/rfc6901#section-4 +array-location = array-index / array-dash +array-index = %x30 / ( %x31-39 *(%x30-39) ) + ; "0", or digits without a leading "0" +array-dash = "-" + +; Surrogate named rules +slash = "/" +`}}class pj extends Error{constructor(e,r=void 0){if(super(e,r),this.name=this.constructor.name,typeof e=="string"&&(this.message=e),typeof Error.captureStackTrace=="function"?Error.captureStackTrace(this,this.constructor):this.stack=new Error(e).stack,r!=null&&typeof r=="object"&&Object.prototype.hasOwnProperty.call(r,"cause")&&!("cause"in this)){const{cause:n}=r;this.cause=n,n instanceof Error&&"stack"in n&&(this.stack=`${this.stack} +CAUSE: ${n.stack}`)}if(r!=null&&typeof r=="object"){const{cause:n,...a}=r;Object.assign(this,a)}}}const Du=pj,xS=class extends Du{},sb=t=>(e,r,n,a,o)=>{if(typeof o!="object"||o===null||Array.isArray(o))throw new xS("parser's user data must be an object");if(e===Br.SEM_PRE){const p={type:t,text:Ni.charsToString(r,n,a),start:n,length:a,children:[]};o.stack.length>0?o.stack[o.stack.length-1].children.push(p):o.root=p,o.stack.push(p)}e===Br.SEM_POST&&o.stack.pop()},hj=class extends ib{constructor(){super(),this.callbacks["json-pointer"]=sb("json-pointer"),this.callbacks["reference-token"]=sb("reference-token"),this.callbacks.slash=sb("text")}getTree(){const e={stack:[],root:null};return this.translate(e),delete e.stack,e}},dj=t=>{if(typeof t!="string")throw new TypeError("Reference token must be a string");return t.replace(/~1/g,"/").replace(/~0/g,"~")},fj=class extends hj{getTree(){const{root:e}=super.getTree();return e.children.filter((({type:r})=>r==="reference-token")).map((({text:r})=>dj(r)))}},mj=class extends Array{toString(){return this.map((e=>`"${String(e)}"`)).join(", ")}},gj=class extends cj{inferExpectations(){const e=this.displayTrace().split(` +`),r=new Set;let n=-1;for(let a=0;an){const p=o.match(/N\|\[TLS\(([^)]+)\)]/);p&&r.add(p[1])}}return new mj(...r)}},yj=new Uf,vj=(t,{translator:e=new fj,stats:r=!1,trace:n=!1}={})=>{if(typeof t!="string")throw new TypeError("JSON Pointer must be a string");try{const a=new ns;e&&(a.ast=e),r&&(a.stats=new uj),n&&(a.trace=new gj);const o=a.parse(yj,"json-pointer",t);return{result:o,tree:o.success&&e?a.ast.getTree():void 0,stats:a.stats,trace:a.trace}}catch(a){throw new xS("Unexpected error during JSON Pointer parsing",{cause:a,jsonPointer:t})}};new Uf,new ns,new Uf,new ns;const bj=new Uf,wj=new ns,Ej=t=>{if(typeof t!="string")return!1;try{return wj.parse(bj,"array-index",t).success}catch{return!1}},Sj=new Uf,xj=new ns,_j=t=>{if(typeof t!="string")return!1;try{return xj.parse(Sj,"array-dash",t).success}catch{return!1}},Oj=t=>{if(typeof t!="string"&&typeof t!="number")throw new TypeError("Reference token must be a string or number");return String(t).replace(/~/g,"~0").replace(/\//g,"~1")},kj=class extends Du{},_S=t=>{if(!Array.isArray(t))throw new TypeError("Reference tokens must be a list of strings or numbers");try{return t.length===0?"":`/${t.map((e=>{if(typeof e!="string"&&typeof e!="number")throw new TypeError("Reference token must be a string or number");return Oj(String(e))})).join("/")}`}catch(e){throw new kj("Unexpected error during JSON Pointer compilation",{cause:e,referenceTokens:t})}},Aj=class{#e;#t;#r;constructor(e,r={}){this.#e=e,this.#e.steps=[],this.#e.failed=!1,this.#e.failedAt=-1,this.#e.message=`JSON Pointer "${r.jsonPointer}" was successfully evaluated against the provided value`,this.#e.context={...r,realm:r.realm.name},this.#t=[],this.#r=r.realm}step({referenceToken:e,input:r,output:n,success:a=!0,reason:o}){const p=this.#t.length;this.#t.push(e);const c={referenceToken:e,referenceTokenPosition:p,input:r,inputType:this.#r.isObject(r)?"object":this.#r.isArray(r)?"array":"unrecognized",output:n,success:a};o&&(c.reason=o),this.#e.steps.push(c),a||(this.#e.failed=!0,this.#e.failedAt=p,this.#e.message=o)}},OS=class{name="";isArray(e){throw new Du("Realm.isArray(node) must be implemented in a subclass")}isObject(e){throw new Du("Realm.isObject(node) must be implemented in a subclass")}sizeOf(e){throw new Du("Realm.sizeOf(node) must be implemented in a subclass")}has(e,r){throw new Du("Realm.has(node) must be implemented in a subclass")}evaluate(e,r){throw new Du("Realm.evaluate(node) must be implemented in a subclass")}},Bp=class extends Du{},Qh=class extends Bp{},Cj=class extends OS{name="json";isArray(e){return Array.isArray(e)}isObject(e){return typeof e=="object"&&e!==null&&!this.isArray(e)}sizeOf(e){return this.isArray(e)?e.length:this.isObject(e)?Object.keys(e).length:0}has(e,r){if(this.isArray(e)){const n=Number(r),a=n>>>0;if(n!==a)throw new Qh(`Invalid array index "${r}": index must be an unsinged 32-bit integer`,{referenceToken:r,currentValue:e,realm:this.name});return a{const{result:p,tree:c,trace:d}=vj(e,{trace:!!o}),E=typeof o=="object"&&o!==null?new Aj(o,{jsonPointer:e,referenceTokens:c,strictArrays:r,strictObjects:n,realm:a,value:t}):null;try{let x;if(!p.success){let k=`Invalid JSON Pointer: "${e}". Syntax error at position ${p.maxMatched}`;throw k+=d?`, expected ${d.inferExpectations()}`:"",new Bp(k,{jsonPointer:e,currentValue:t,realm:a.name})}return c.reduce(((k,P,R)=>{if(a.isArray(k)){if(_j(P)){if(r)throw new Qh(`Invalid array index "-" at position ${R} in "${e}". The "-" token always refers to a nonexistent element during evaluation`,{jsonPointer:e,referenceTokens:c,referenceToken:P,referenceTokenPosition:R,currentValue:k,realm:a.name});return x=a.evaluate(k,String(a.sizeOf(k))),E?.step({referenceToken:P,input:k,output:x}),x}if(!Ej(P))throw new Qh(`Invalid array index "${P}" at position ${R} in "${e}": index MUST be "0", or digits without a leading "0"`,{jsonPointer:e,referenceTokens:c,referenceToken:P,referenceTokenPosition:R,currentValue:k,realm:a.name});const L=Number(P);if(!Number.isSafeInteger(L))throw new Qh(`Invalid array index "${P}" at position ${R} in "${e}": index must be a safe integer`,{jsonPointer:e,referenceTokens:c,referenceToken:P,referenceTokenPosition:R,currentValue:k,realm:a.name});if(!a.has(k,P)&&r)throw new Qh(`Invalid array index "${P}" at position ${R} in "${e}": index not found in array`,{jsonPointer:e,referenceTokens:c,referenceToken:P,referenceTokenPosition:R,currentValue:k,realm:a.name});return x=a.evaluate(k,P),E?.step({referenceToken:P,input:k,output:x}),x}if(a.isObject(k)){if(!a.has(k,P)&&n)throw new kS(`Invalid object key "${P}" at position ${R} in "${e}": key not found in object`,{jsonPointer:e,referenceTokens:c,referenceToken:P,referenceTokenPosition:R,currentValue:k,realm:a.name});return x=a.evaluate(k,P),E?.step({referenceToken:P,input:k,output:x}),x}throw new jj(`Invalid reference token "${P}" at position ${R} in "${e}": cannot be applied to a non-object/non-array value`,{jsonPointer:e,referenceTokens:c,referenceToken:P,referenceTokenPosition:R,currentValue:k,realm:a.name})}),t)}catch(x){throw E?.step({referenceToken:x.referenceToken,input:x.currentValue,success:!1,reason:x.message}),x instanceof Bp?x:new Bp("Unexpected error during JSON Pointer evaluation",{cause:x,jsonPointer:e,referenceTokens:c})}},Nj=class extends OS{name="apidom";isArray(e){return vo(e)}isObject(e){return hn(e)}sizeOf(e){return this.isArray(e)||this.isObject(e)?e.length:0}has(e,r){if(this.isArray(e)){const n=Number(r),a=n>>>0;if(n!==a)throw new Qh(`Invalid array index "${r}": index must be an unsinged 32-bit integer`,{referenceToken:r,currentValue:e,realm:this.name});return aPj(t,e,{...r,realm:new Nj});class Tj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="callback"}}const zf=Tj;class Ij extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="components"}get schemas(){return this.get("schemas")}set schemas(e){this.set("schemas",e)}get responses(){return this.get("responses")}set responses(e){this.set("responses",e)}get parameters(){return this.get("parameters")}set parameters(e){this.set("parameters",e)}get examples(){return this.get("examples")}set examples(e){this.set("examples",e)}get requestBodies(){return this.get("requestBodies")}set requestBodies(e){this.set("requestBodies",e)}get headers(){return this.get("headers")}set headers(e){this.set("headers",e)}get securitySchemes(){return this.get("securitySchemes")}set securitySchemes(e){this.set("securitySchemes",e)}get links(){return this.get("links")}set links(e){this.set("links",e)}get callbacks(){return this.get("callbacks")}set callbacks(e){this.set("callbacks",e)}}const Vf=Ij;class Mj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="contact"}get name(){return this.get("name")}set name(e){this.set("name",e)}get url(){return this.get("url")}set url(e){this.set("url",e)}get email(){return this.get("email")}set email(e){this.set("email",e)}}const Jf=Mj;class Rj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="discriminator"}get propertyName(){return this.get("propertyName")}set propertyName(e){this.set("propertyName",e)}get mapping(){return this.get("mapping")}set mapping(e){this.set("mapping",e)}}const Wf=Rj;class Dj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="encoding"}get contentType(){return this.get("contentType")}set contentType(e){this.set("contentType",e)}get headers(){return this.get("headers")}set headers(e){this.set("headers",e)}get style(){return this.get("style")}set style(e){this.set("style",e)}get explode(){return this.get("explode")}set explode(e){this.set("explode",e)}get allowedReserved(){return this.get("allowedReserved")}set allowedReserved(e){this.set("allowedReserved",e)}}const Ty=Dj;class Fj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="example"}get summary(){return this.get("summary")}set summary(e){this.set("summary",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get value(){return this.get("value")}set value(e){this.set("value",e)}get externalValue(){return this.get("externalValue")}set externalValue(e){this.set("externalValue",e)}}const Hf=Fj;class Lj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="externalDocumentation"}get description(){return this.get("description")}set description(e){this.set("description",e)}get url(){return this.get("url")}set url(e){this.set("url",e)}}const Kf=Lj;class AS extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="header"}get required(){return this.hasKey("required")?this.get("required"):new Ge.bd(!1)}set required(e){this.set("required",e)}get deprecated(){return this.hasKey("deprecated")?this.get("deprecated"):new Ge.bd(!1)}set deprecated(e){this.set("deprecated",e)}get allowEmptyValue(){return this.get("allowEmptyValue")}set allowEmptyValue(e){this.set("allowEmptyValue",e)}get style(){return this.get("style")}set style(e){this.set("style",e)}get explode(){return this.get("explode")}set explode(e){this.set("explode",e)}get allowReserved(){return this.get("allowReserved")}set allowReserved(e){this.set("allowReserved",e)}get schema(){return this.get("schema")}set schema(e){this.set("schema",e)}get example(){return this.get("example")}set example(e){this.set("example",e)}get examples(){return this.get("examples")}set examples(e){this.set("examples",e)}get contentProp(){return this.get("content")}set contentProp(e){this.set("content",e)}}Object.defineProperty(AS.prototype,"description",{get(){return this.get("description")},set(t){this.set("description",t)},enumerable:!0});const Gf=AS;class $j extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="info",this.classes.push("info")}get title(){return this.get("title")}set title(e){this.set("title",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get termsOfService(){return this.get("termsOfService")}set termsOfService(e){this.set("termsOfService",e)}get contact(){return this.get("contact")}set contact(e){this.set("contact",e)}get license(){return this.get("license")}set license(e){this.set("license",e)}get version(){return this.get("version")}set version(e){this.set("version",e)}}const Yf=$j;class Bj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="license"}get name(){return this.get("name")}set name(e){this.set("name",e)}get url(){return this.get("url")}set url(e){this.set("url",e)}}const Xf=Bj;class qj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="link"}get operationRef(){return this.get("operationRef")}set operationRef(e){this.set("operationRef",e)}get operationId(){return this.get("operationId")}set operationId(e){this.set("operationId",e)}get operation(){var e,r;return Ir(this.operationRef)?(e=this.operationRef)===null||e===void 0?void 0:e.meta.get("operation"):Ir(this.operationId)?(r=this.operationId)===null||r===void 0?void 0:r.meta.get("operation"):void 0}set operation(e){this.set("operation",e)}get parameters(){return this.get("parameters")}set parameters(e){this.set("parameters",e)}get requestBody(){return this.get("requestBody")}set requestBody(e){this.set("requestBody",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get server(){return this.get("server")}set server(e){this.set("server",e)}}const Qf=qj;class Uj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="mediaType"}get schema(){return this.get("schema")}set schema(e){this.set("schema",e)}get example(){return this.get("example")}set example(e){this.set("example",e)}get examples(){return this.get("examples")}set examples(e){this.set("examples",e)}get encoding(){return this.get("encoding")}set encoding(e){this.set("encoding",e)}}const Zf=Uj;class zj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="oAuthFlow"}get authorizationUrl(){return this.get("authorizationUrl")}set authorizationUrl(e){this.set("authorizationUrl",e)}get tokenUrl(){return this.get("tokenUrl")}set tokenUrl(e){this.set("tokenUrl",e)}get refreshUrl(){return this.get("refreshUrl")}set refreshUrl(e){this.set("refreshUrl",e)}get scopes(){return this.get("scopes")}set scopes(e){this.set("scopes",e)}}const Iy=zj;class Vj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="oAuthFlows"}get implicit(){return this.get("implicit")}set implicit(e){this.set("implicit",e)}get password(){return this.get("password")}set password(e){this.set("password",e)}get clientCredentials(){return this.get("clientCredentials")}set clientCredentials(e){this.set("clientCredentials",e)}get authorizationCode(){return this.get("authorizationCode")}set authorizationCode(e){this.set("authorizationCode",e)}}const My=Vj;class Jj extends Ge.Om{constructor(e,r,n){super(e,r,n),this.element="openapi",this.classes.push("spec-version"),this.classes.push("version")}}const em=Jj;class Wj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="openApi3_0",this.classes.push("api")}get openapi(){return this.get("openapi")}set openapi(e){this.set("openapi",e)}get info(){return this.get("info")}set info(e){this.set("info",e)}get servers(){return this.get("servers")}set servers(e){this.set("servers",e)}get paths(){return this.get("paths")}set paths(e){this.set("paths",e)}get components(){return this.get("components")}set components(e){this.set("components",e)}get security(){return this.get("security")}set security(e){this.set("security",e)}get tags(){return this.get("tags")}set tags(e){this.set("tags",e)}get externalDocs(){return this.get("externalDocs")}set externalDocs(e){this.set("externalDocs",e)}}const Ry=Wj;class Hj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="operation"}get tags(){return this.get("tags")}set tags(e){this.set("tags",e)}get summary(){return this.get("summary")}set summary(e){this.set("summary",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}set externalDocs(e){this.set("externalDocs",e)}get externalDocs(){return this.get("externalDocs")}get operationId(){return this.get("operationId")}set operationId(e){this.set("operationId",e)}get parameters(){return this.get("parameters")}set parameters(e){this.set("parameters",e)}get requestBody(){return this.get("requestBody")}set requestBody(e){this.set("requestBody",e)}get responses(){return this.get("responses")}set responses(e){this.set("responses",e)}get callbacks(){return this.get("callbacks")}set callbacks(e){this.set("callbacks",e)}get deprecated(){return this.hasKey("deprecated")?this.get("deprecated"):new Ge.bd(!1)}set deprecated(e){this.set("deprecated",e)}get security(){return this.get("security")}set security(e){this.set("security",e)}get servers(){return this.get("severs")}set servers(e){this.set("servers",e)}}const tm=Hj;class CS extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="parameter"}get name(){return this.get("name")}set name(e){this.set("name",e)}get in(){return this.get("in")}set in(e){this.set("in",e)}get required(){return this.hasKey("required")?this.get("required"):new Ge.bd(!1)}set required(e){this.set("required",e)}get deprecated(){return this.hasKey("deprecated")?this.get("deprecated"):new Ge.bd(!1)}set deprecated(e){this.set("deprecated",e)}get allowEmptyValue(){return this.get("allowEmptyValue")}set allowEmptyValue(e){this.set("allowEmptyValue",e)}get style(){return this.get("style")}set style(e){this.set("style",e)}get explode(){return this.get("explode")}set explode(e){this.set("explode",e)}get allowReserved(){return this.get("allowReserved")}set allowReserved(e){this.set("allowReserved",e)}get schema(){return this.get("schema")}set schema(e){this.set("schema",e)}get example(){return this.get("example")}set example(e){this.set("example",e)}get examples(){return this.get("examples")}set examples(e){this.set("examples",e)}get contentProp(){return this.get("content")}set contentProp(e){this.set("content",e)}}Object.defineProperty(CS.prototype,"description",{get(){return this.get("description")},set(t){this.set("description",t)},enumerable:!0});const rm=CS;class Kj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="pathItem"}get $ref(){return this.get("$ref")}set $ref(e){this.set("$ref",e)}get summary(){return this.get("summary")}set summary(e){this.set("summary",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get GET(){return this.get("get")}set GET(e){this.set("GET",e)}get PUT(){return this.get("put")}set PUT(e){this.set("PUT",e)}get POST(){return this.get("post")}set POST(e){this.set("POST",e)}get DELETE(){return this.get("delete")}set DELETE(e){this.set("DELETE",e)}get OPTIONS(){return this.get("options")}set OPTIONS(e){this.set("OPTIONS",e)}get HEAD(){return this.get("head")}set HEAD(e){this.set("HEAD",e)}get PATCH(){return this.get("patch")}set PATCH(e){this.set("PATCH",e)}get TRACE(){return this.get("trace")}set TRACE(e){this.set("TRACE",e)}get servers(){return this.get("servers")}set servers(e){this.set("servers",e)}get parameters(){return this.get("parameters")}set parameters(e){this.set("parameters",e)}}const nm=Kj;class Gj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="paths"}}const am=Gj;class Yj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="reference",this.classes.push("openapi-reference")}get $ref(){return this.get("$ref")}set $ref(e){this.set("$ref",e)}}const om=Yj;class Xj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="requestBody"}get description(){return this.get("description")}set description(e){this.set("description",e)}get contentProp(){return this.get("content")}set contentProp(e){this.set("content",e)}get required(){return this.hasKey("required")?this.get("required"):new Ge.bd(!1)}set required(e){this.set("required",e)}}const im=Xj;class Qj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="response"}get description(){return this.get("description")}set description(e){this.set("description",e)}get headers(){return this.get("headers")}set headers(e){this.set("headers",e)}get contentProp(){return this.get("content")}set contentProp(e){this.set("content",e)}get links(){return this.get("links")}set links(e){this.set("links",e)}}const sm=Qj;class Zj extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="responses"}get default(){return this.get("default")}set default(e){this.set("default",e)}}const lm=Zj,Zr=class extends Mt{};class e3 extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="JSONSchemaDraft4"}get idProp(){return this.get("id")}set idProp(e){this.set("id",e)}get $schema(){return this.get("$schema")}set $schema(e){this.set("$schema",e)}get multipleOf(){return this.get("multipleOf")}set multipleOf(e){this.set("multipleOf",e)}get maximum(){return this.get("maximum")}set maximum(e){this.set("maximum",e)}get exclusiveMaximum(){return this.get("exclusiveMaximum")}set exclusiveMaximum(e){this.set("exclusiveMaximum",e)}get minimum(){return this.get("minimum")}set minimum(e){this.set("minimum",e)}get exclusiveMinimum(){return this.get("exclusiveMinimum")}set exclusiveMinimum(e){this.set("exclusiveMinimum",e)}get maxLength(){return this.get("maxLength")}set maxLength(e){this.set("maxLength",e)}get minLength(){return this.get("minLength")}set minLength(e){this.set("minLength",e)}get pattern(){return this.get("pattern")}set pattern(e){this.set("pattern",e)}get additionalItems(){return this.get("additionalItems")}set additionalItems(e){this.set("additionalItems",e)}get items(){return this.get("items")}set items(e){this.set("items",e)}get maxItems(){return this.get("maxItems")}set maxItems(e){this.set("maxItems",e)}get minItems(){return this.get("minItems")}set minItems(e){this.set("minItems",e)}get uniqueItems(){return this.get("uniqueItems")}set uniqueItems(e){this.set("uniqueItems",e)}get maxProperties(){return this.get("maxProperties")}set maxProperties(e){this.set("maxProperties",e)}get minProperties(){return this.get("minProperties")}set minProperties(e){this.set("minProperties",e)}get required(){return this.get("required")}set required(e){this.set("required",e)}get properties(){return this.get("properties")}set properties(e){this.set("properties",e)}get additionalProperties(){return this.get("additionalProperties")}set additionalProperties(e){this.set("additionalProperties",e)}get patternProperties(){return this.get("patternProperties")}set patternProperties(e){this.set("patternProperties",e)}get dependencies(){return this.get("dependencies")}set dependencies(e){this.set("dependencies",e)}get enum(){return this.get("enum")}set enum(e){this.set("enum",e)}get type(){return this.get("type")}set type(e){this.set("type",e)}get allOf(){return this.get("allOf")}set allOf(e){this.set("allOf",e)}get anyOf(){return this.get("anyOf")}set anyOf(e){this.set("anyOf",e)}get oneOf(){return this.get("oneOf")}set oneOf(e){this.set("oneOf",e)}get not(){return this.get("not")}set not(e){this.set("not",e)}get definitions(){return this.get("definitions")}set definitions(e){this.set("definitions",e)}get title(){return this.get("title")}set title(e){this.set("title",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get default(){return this.get("default")}set default(e){this.set("default",e)}get format(){return this.get("format")}set format(e){this.set("format",e)}get base(){return this.get("base")}set base(e){this.set("base",e)}get links(){return this.get("links")}set links(e){this.set("links",e)}get media(){return this.get("media")}set media(e){this.set("media",e)}get readOnly(){return this.get("readOnly")}set readOnly(e){this.set("readOnly",e)}}const Zh=e3;class t3 extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="JSONReference",this.classes.push("json-reference")}get $ref(){return this.get("$ref")}set $ref(e){this.set("$ref",e)}}const ed=t3;class r3 extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="media"}get binaryEncoding(){return this.get("binaryEncoding")}set binaryEncoding(e){this.set("binaryEncoding",e)}get type(){return this.get("type")}set type(e){this.set("type",e)}}const cm=r3;class n3 extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="linkDescription"}get href(){return this.get("href")}set href(e){this.set("href",e)}get rel(){return this.get("rel")}set rel(e){this.set("rel",e)}get title(){return this.get("title")}set title(e){this.set("title",e)}get targetSchema(){return this.get("targetSchema")}set targetSchema(e){this.set("targetSchema",e)}get mediaType(){return this.get("mediaType")}set mediaType(e){this.set("mediaType",e)}get method(){return this.get("method")}set method(e){this.set("method",e)}get encType(){return this.get("encType")}set encType(e){this.set("encType",e)}get schema(){return this.get("schema")}set schema(e){this.set("schema",e)}}const um=n3,Dy=t=>{const e=t.meta.length>0?hr(t.meta):void 0,r=t.attributes.length>0?hr(t.attributes):void 0;return new t.constructor(void 0,e,r)},Fy=(t,e)=>e.clone&&e.isMergeableElement(t)?td(Dy(t),t,e):t,Ly={clone:!0,isMergeableElement:t=>hn(t)||vo(t),arrayElementMerge:(t,e,r)=>t.concat(e)["fantasy-land/map"]((n=>Fy(n,r))),objectElementMerge:(t,e,r)=>{const n=hn(t)?Dy(t):Dy(e);return hn(t)&&t.forEach(((a,o,p)=>{const c=no(p);c.value=Fy(a,r),n.content.push(c)})),e.forEach(((a,o,p)=>{const c=Et(o);let d;if(hn(t)&&t.hasKey(c)&&r.isMergeableElement(a)){const E=t.get(c);d=no(p),d.value=((x,k)=>{if(typeof k.customMerge!="function")return td;const P=k.customMerge(x,k);return typeof P=="function"?P:td})(o,r)(E,a,r)}else d=no(p),d.value=Fy(a,r);n.remove(c),n.content.push(d)})),n},customMerge:void 0,customMetaMerge:void 0,customAttributesMerge:void 0},td=(t,e,r)=>{var n,a,o;const p={...Ly,...r};p.isMergeableElement=(n=p.isMergeableElement)!==null&&n!==void 0?n:Ly.isMergeableElement,p.arrayElementMerge=(a=p.arrayElementMerge)!==null&&a!==void 0?a:Ly.arrayElementMerge,p.objectElementMerge=(o=p.objectElementMerge)!==null&&o!==void 0?o:Ly.objectElementMerge;const c=vo(e);if(c!==vo(t))return Fy(e,p);const d=c&&typeof p.arrayElementMerge=="function"?p.arrayElementMerge(t,e,p):p.objectElementMerge(t,e,p);return d.meta=(E=>typeof E.customMetaMerge!="function"?x=>hr(x):E.customMetaMerge)(p)(t.meta,e.meta),d.attributes=(E=>typeof E.customAttributesMerge!="function"?x=>hr(x):E.customAttributesMerge)(p)(t.attributes,e.attributes),d};td.all=(t,e)=>{if(!Array.isArray(t))throw new TypeError("First argument of deepmerge should be an array.");return t.length===0?new Ge.Sh:t.reduce(((r,n)=>td(r,n,e)),Dy(t[0]))};const qp=td,jS=class{element;constructor(e){Object.assign(this,e)}copyMetaAndAttributes(e,r){(e.meta.length>0||r.meta.length>0)&&(r.meta=qp(r.meta,e.meta)),Gh(e)&&ob(r,e),(e.attributes.length>0||e.meta.length>0)&&(r.attributes=qp(r.attributes,e.attributes))}},gn=class extends jS{enter(e){return this.element=hr(e),on}},PS=(t,e,r=[])=>{const n=Object.getOwnPropertyDescriptors(e);for(let a of r)delete n[a];Object.defineProperties(t,n)},pm=(t,e=[t])=>{const r=Object.getPrototypeOf(t);return r===null?e:pm(r,[...e,r])},NS=(t,e,r=[])=>{var n;const a=(n=((...c)=>{if(c.length===0)return;let d;const E=c.map((x=>pm(x)));for(;E.every((x=>x.length>0));){const x=E.map((P=>P.pop())),k=x[0];if(!x.every((P=>P===k)))break;d=k}return d})(...t))!==null&&n!==void 0?n:Object.prototype,o=Object.create(a),p=pm(a);for(let c of t){let d=pm(c);for(let E=d.length-1;E>=0;E--){let x=d[E];p.indexOf(x)===-1&&(PS(o,x,["constructor",...r]),p.push(x))}}return o.constructor=e,o},lb=t=>t.filter(((e,r)=>t.indexOf(e)==r)),TS=new WeakMap,a3=t=>TS.get(t),IS=(t,e)=>{var r,n;const a=lb([...Object.getOwnPropertyNames(t),...Object.getOwnPropertyNames(e)]),o={};for(let p of a)o[p]=lb([...(r=t?.[p])!==null&&r!==void 0?r:[],...(n=e?.[p])!==null&&n!==void 0?n:[]]);return o},MS=(t,e)=>{var r,n,a,o;return{property:IS((r=t?.property)!==null&&r!==void 0?r:{},(n=e?.property)!==null&&n!==void 0?n:{}),method:IS((a=t?.method)!==null&&a!==void 0?a:{},(o=e?.method)!==null&&o!==void 0?o:{})}},o3=(t,e)=>{var r,n,a,o,p,c;return{class:lb([...(r=t?.class)!==null&&r!==void 0?r:[],...(n=e?.class)!==null&&n!==void 0?n:[]]),static:MS((a=t?.static)!==null&&a!==void 0?a:{},(o=e?.static)!==null&&o!==void 0?o:{}),instance:MS((p=t?.instance)!==null&&p!==void 0?p:{},(c=e?.instance)!==null&&c!==void 0?c:{})}},i3=new Map,s3=(...t)=>{const e=((...r)=>{var n;const a=new Set,o=new Set([...r]);for(;o.size>0;)for(let p of o){const c=pm(p.prototype).map((E=>E.constructor)),d=[...c,...(n=a3(p))!==null&&n!==void 0?n:[]].filter((E=>!a.has(E)));for(let E of d)o.add(E);a.add(p),o.delete(p)}return[...a]})(...t).map((r=>i3.get(r))).filter((r=>!!r));return e.length==0?{}:e.length==1?e[0]:e.reduce(((r,n)=>o3(r,n)))};function Vt(...t){var e,r,n;const a=t.map((E=>E.prototype));function o(...E){for(const x of t)PS(this,new x(...E))}o.prototype=NS(a,o),Object.setPrototypeOf(o,NS(t,null,["prototype"]));let p=o;{const E=s3(...t);for(let x of(e=E?.class)!==null&&e!==void 0?e:[]){const k=x(p);k&&(p=k)}RS((r=E?.static)!==null&&r!==void 0?r:{},p),RS((n=E?.instance)!==null&&n!==void 0?n:{},p.prototype)}var c,d;return c=p,d=t,TS.set(c,d),p}const RS=(t,e)=>{const r=t.property,n=t.method;if(r)for(let a in r)for(let o of r[a])o(e,a);if(n)for(let a in n)for(let o of n[a])o(e,a,Object.getOwnPropertyDescriptor(e,a))},l3=_r((function(e){return ja(Vh(fE,0,mE("length",e)),(function(){for(var r=0,n=e.length;r{if(Ir(o)&&n.includes(Et(o))&&!this.ignoredFields.includes(Et(o))){const c=this.toRefractedElement([...r,"fixedFields",Et(o)],a),d=new Ge.Pr(hr(o),c);this.copyMetaAndAttributes(p,d),d.classes.push("fixed-field"),this.element.content.push(d)}else this.ignoredFields.includes(Et(o))||this.element.content.push(hr(p))})),this.copyMetaAndAttributes(e,this.element),on}},Na=class{parent;constructor({parent:e}){this.parent=e}},LS=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Zh||t(n)&&e("JSONSchemaDraft4",n)&&r("object",n))),cb=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof ed||t(n)&&e("JSONReference",n)&&r("object",n))),$S=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof cm||t(n)&&e("media",n)&&r("object",n))),p3=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof um||t(n)&&e("linkDescription",n)&&r("object",n)));class h3 extends Vt(Up,Na,gn){constructor(e){super(e),this.element=new Zh,this.specPath=jr(["document","objects","JSONSchema"])}get defaultDialectIdentifier(){return"http://json-schema.org/draft-04/schema#"}ObjectElement(e){return this.handleDialectIdentifier(e),this.handleSchemaIdentifier(e),this.parent=this.element,Up.prototype.ObjectElement.call(this,e)}handleDialectIdentifier(e){if(Iu(this.parent)&&!Ir(e.get("$schema")))this.element.setMetaProperty("inheritedDialectIdentifier",this.defaultDialectIdentifier);else if(LS(this.parent)&&!Ir(e.get("$schema"))){const r=Jh(Et(this.parent.meta.get("inheritedDialectIdentifier")),Et(this.parent.$schema));this.element.setMetaProperty("inheritedDialectIdentifier",r)}}handleSchemaIdentifier(e,r="id"){const n=this.parent!==void 0?hr(this.parent.getMetaProperty("ancestorsSchemaIdentifiers",[])):new Ge.wE,a=Et(e.get(r));$y(a)&&n.push(a),this.element.setMetaProperty("ancestorsSchemaIdentifiers",n)}}const BS=h3,Ul=t=>hn(t)&&t.hasKey("$ref");class d3 extends Vt(Ti,Na,gn){ObjectElement(e){const r=Ul(e)?["document","objects","JSONReference"]:["document","objects","JSONSchema"];return this.element=this.toRefractedElement(r,e),on}ArrayElement(e){return this.element=new Ge.wE,this.element.classes.push("json-schema-items"),e.forEach((r=>{const n=Ul(r)?["document","objects","JSONReference"]:["document","objects","JSONSchema"],a=this.toRefractedElement(n,r);this.element.push(a)})),this.copyMetaAndAttributes(e,this.element),on}}const qS=d3,f3=class extends gn{ArrayElement(e){const r=this.enter(e);return this.element.classes.push("json-schema-required"),r}},m3=class extends Ti{specPath;ignoredFields;fieldPatternPredicate=rb;constructor({specPath:e,ignoredFields:r,fieldPatternPredicate:n,...a}){super({...a}),this.specPath=e,this.ignoredFields=r||[],typeof n=="function"&&(this.fieldPatternPredicate=n)}ObjectElement(e){return e.forEach(((r,n,a)=>{if(!this.ignoredFields.includes(Et(n))&&this.fieldPatternPredicate(Et(n))){const o=this.specPath(r),p=this.toRefractedElement(o,r),c=new Ge.Pr(hr(n),p);this.copyMetaAndAttributes(a,c),c.classes.push("patterned-field"),this.element.content.push(c)}else this.ignoredFields.includes(Et(n))||this.element.content.push(hr(a))})),this.copyMetaAndAttributes(e,this.element),on}},Fu=class extends m3{constructor(e){super(e),this.fieldPatternPredicate=$y}};class g3 extends Vt(Fu,Na,gn){constructor(e){super(e),this.element=new Ge.Sh,this.element.classes.push("json-schema-properties"),this.specPath=r=>Ul(r)?["document","objects","JSONReference"]:["document","objects","JSONSchema"]}}const y3=g3;class v3 extends Vt(Fu,Na,gn){constructor(e){super(e),this.element=new Ge.Sh,this.element.classes.push("json-schema-patternProperties"),this.specPath=r=>Ul(r)?["document","objects","JSONReference"]:["document","objects","JSONSchema"]}}const b3=v3;class w3 extends Vt(Fu,Na,gn){constructor(e){super(e),this.element=new Ge.Sh,this.element.classes.push("json-schema-dependencies"),this.specPath=r=>Ul(r)?["document","objects","JSONReference"]:["document","objects","JSONSchema"]}}const E3=w3,S3=class extends gn{ArrayElement(e){const r=this.enter(e);return this.element.classes.push("json-schema-enum"),r}},x3=class extends gn{StringElement(e){const r=this.enter(e);return this.element.classes.push("json-schema-type"),r}ArrayElement(e){const r=this.enter(e);return this.element.classes.push("json-schema-type"),r}};class _3 extends Vt(Ti,Na,gn){constructor(e){super(e),this.element=new Ge.wE,this.element.classes.push("json-schema-allOf")}ArrayElement(e){return e.forEach((r=>{const n=Ul(r)?["document","objects","JSONReference"]:["document","objects","JSONSchema"],a=this.toRefractedElement(n,r);this.element.push(a)})),this.copyMetaAndAttributes(e,this.element),on}}const O3=_3;class k3 extends Vt(Ti,Na,gn){constructor(e){super(e),this.element=new Ge.wE,this.element.classes.push("json-schema-anyOf")}ArrayElement(e){return e.forEach((r=>{const n=Ul(r)?["document","objects","JSONReference"]:["document","objects","JSONSchema"],a=this.toRefractedElement(n,r);this.element.push(a)})),this.copyMetaAndAttributes(e,this.element),on}}const A3=k3;class C3 extends Vt(Ti,Na,gn){constructor(e){super(e),this.element=new Ge.wE,this.element.classes.push("json-schema-oneOf")}ArrayElement(e){return e.forEach((r=>{const n=Ul(r)?["document","objects","JSONReference"]:["document","objects","JSONSchema"],a=this.toRefractedElement(n,r);this.element.push(a)})),this.copyMetaAndAttributes(e,this.element),on}}const j3=C3;class P3 extends Vt(Fu,Na,gn){constructor(e){super(e),this.element=new Ge.Sh,this.element.classes.push("json-schema-definitions"),this.specPath=r=>Ul(r)?["document","objects","JSONReference"]:["document","objects","JSONSchema"]}}const N3=P3;class T3 extends Vt(Ti,Na,gn){constructor(e){super(e),this.element=new Ge.wE,this.element.classes.push("json-schema-links")}ArrayElement(e){return e.forEach((r=>{const n=this.toRefractedElement(["document","objects","LinkDescription"],r);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),on}}const I3=T3;class M3 extends Vt(Up,gn){constructor(e){super(e),this.element=new ed,this.specPath=jr(["document","objects","JSONReference"])}ObjectElement(e){const r=Up.prototype.ObjectElement.call(this,e);return Ir(this.element.$ref)&&this.element.classes.push("reference-element"),r}}const R3=M3,D3=class extends gn{StringElement(e){const r=this.enter(e);return this.element.classes.push("reference-value"),r}},ub=ha((function(e,r,n){return ja(Math.max(e.length,r.length,n.length),(function(){return e.apply(this,arguments)?r.apply(this,arguments):n.apply(this,arguments)}))})),F3=_r((function(e){return function(r,n){return e(r,n)?-1:e(n,r)?1:0}}));var L3=Tr((function(e,r){return Array.prototype.slice.call(r,0).sort(e)}));const $3=L3;var B3=_r((function(t){return cy(0,t)}));const q3=B3,U3=_r(_y),z3=If(jy),V3=Rf(vS,DS);function J3(t){return(function(r){if(Array.isArray(r))return pb(r)})(t)||(function(r){if(typeof Symbol<"u"&&r[Symbol.iterator]!=null||r["@@iterator"]!=null)return Array.from(r)})(t)||(function(r,n){if(r){if(typeof r=="string")return pb(r,n);var a={}.toString.call(r).slice(8,-1);return a==="Object"&&r.constructor&&(a=r.constructor.name),a==="Map"||a==="Set"?Array.from(r):a==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(a)?pb(r,n):void 0}})(t)||(function(){throw new TypeError(`Invalid attempt to spread non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)})()}function pb(t,e){(e==null||e>t.length)&&(e=t.length);for(var r=0,n=Array(e);re.length}))),q3,Af("length")),H3=E0((function(t,e,r){var n=r.apply(void 0,J3(t));return z3(n)?U3(n):e}));const US=ub(V3,(function(e){var r=W3(e);return ja(r,(function(){for(var n=arguments.length,a=new Array(n),o=0;oub(a,jr(o),vy))),n=US(r)(e);return this.element=this.toRefractedElement(n,e),on}},rd=class extends K3{constructor(e){super(e),this.alternator=[{predicate:Ul,specPath:["document","objects","JSONReference"]},{predicate:kc,specPath:["document","objects","JSONSchema"]}]}};class G3 extends Vt(Up,gn){constructor(e){super(e),this.element=new cm,this.specPath=jr(["document","objects","Media"])}}const Y3=G3;class X3 extends Vt(Up,gn){constructor(e){super(e),this.element=new um,this.specPath=jr(["document","objects","LinkDescription"])}}const zS=X3,bo={visitors:{value:gn,JSONSchemaOrJSONReferenceVisitor:rd,document:{objects:{JSONSchema:{$visitor:BS,fixedFields:{id:{$ref:"#/visitors/value"},$schema:{$ref:"#/visitors/value"},multipleOf:{$ref:"#/visitors/value"},maximum:{$ref:"#/visitors/value"},exclusiveMaximum:{$ref:"#/visitors/value"},minimum:{$ref:"#/visitors/value"},exclusiveMinimum:{$ref:"#/visitors/value"},maxLength:{$ref:"#/visitors/value"},minLength:{$ref:"#/visitors/value"},pattern:{$ref:"#/visitors/value"},additionalItems:rd,items:qS,maxItems:{$ref:"#/visitors/value"},minItems:{$ref:"#/visitors/value"},uniqueItems:{$ref:"#/visitors/value"},maxProperties:{$ref:"#/visitors/value"},minProperties:{$ref:"#/visitors/value"},required:f3,properties:y3,additionalProperties:rd,patternProperties:b3,dependencies:E3,enum:S3,type:x3,allOf:O3,anyOf:A3,oneOf:j3,not:rd,definitions:N3,title:{$ref:"#/visitors/value"},description:{$ref:"#/visitors/value"},default:{$ref:"#/visitors/value"},format:{$ref:"#/visitors/value"},base:{$ref:"#/visitors/value"},links:I3,media:{$ref:"#/visitors/document/objects/Media"},readOnly:{$ref:"#/visitors/value"}}},JSONReference:{$visitor:R3,fixedFields:{$ref:D3}},Media:{$visitor:Y3,fixedFields:{binaryEncoding:{$ref:"#/visitors/value"},type:{$ref:"#/visitors/value"}}},LinkDescription:{$visitor:zS,fixedFields:{href:{$ref:"#/visitors/value"},rel:{$ref:"#/visitors/value"},title:{$ref:"#/visitors/value"},targetSchema:rd,mediaType:{$ref:"#/visitors/value"},method:{$ref:"#/visitors/value"},encType:{$ref:"#/visitors/value"},schema:rd}}}}}},hm=t=>{if(da(t))return`${t.element.charAt(0).toUpperCase()+t.element.slice(1)}Element`},Q3={JSONSchemaDraft4Element:["content"],JSONReferenceElement:["content"],MediaElement:["content"],LinkDescriptionElement:["content"],...Bl},Z3={namespace:t=>{const{base:e}=t;return e.register("jSONSchemaDraft4",Zh),e.register("jSONReference",ed),e.register("media",cm),e.register("linkDescription",um),e}},eP=Z3,tP=()=>{const t=Ac(eP);return{predicates:{...X,isStringElement:Ir},namespace:t}},rP=(t,{specPath:e=["visitors","document","objects","JSONSchema","$visitor"],plugins:r=[],specificationObj:n=bo}={})=>{const a=(0,Ge.e)(t),o=Ru(n),p=new(ro(e,o))({specObj:o});return Pa(a,p),ql(p.element,r,{toolboxCreator:tP,visitorOptions:{keyMap:Q3,nodeTypeGetter:hm}})},By=t=>(e,r={})=>rP(e,{specPath:t,...r});Zh.refract=By(["visitors","document","objects","JSONSchema","$visitor"]),ed.refract=By(["visitors","document","objects","JSONReference","$visitor"]),cm.refract=By(["visitors","document","objects","Media","$visitor"]),um.refract=By(["visitors","document","objects","LinkDescription","$visitor"]);const qy=class extends Zh{constructor(e,r,n){super(e,r,n),this.element="schema",this.classes.push("json-schema-draft-4")}get idProp(){throw new Zr("idProp getter in Schema class is not not supported.")}set idProp(e){throw new Zr("idProp setter in Schema class is not not supported.")}get $schema(){throw new Zr("$schema getter in Schema class is not not supported.")}set $schema(e){throw new Zr("$schema setter in Schema class is not not supported.")}get additionalItems(){return this.get("additionalItems")}set additionalItems(e){this.set("additionalItems",e)}get items(){return this.get("items")}set items(e){this.set("items",e)}get additionalProperties(){return this.get("additionalProperties")}set additionalProperties(e){this.set("additionalProperties",e)}get patternProperties(){throw new Zr("patternProperties getter in Schema class is not not supported.")}set patternProperties(e){throw new Zr("patternProperties setter in Schema class is not not supported.")}get dependencies(){throw new Zr("dependencies getter in Schema class is not not supported.")}set dependencies(e){throw new Zr("dependencies setter in Schema class is not not supported.")}get type(){return this.get("type")}set type(e){this.set("type",e)}get not(){return this.get("not")}set not(e){this.set("not",e)}get definitions(){throw new Zr("definitions getter in Schema class is not not supported.")}set definitions(e){throw new Zr("definitions setter in Schema class is not not supported.")}get base(){throw new Zr("base getter in Schema class is not not supported.")}set base(e){throw new Zr("base setter in Schema class is not not supported.")}get links(){throw new Zr("links getter in Schema class is not not supported.")}set links(e){throw new Zr("links setter in Schema class is not not supported.")}get media(){throw new Zr("media getter in Schema class is not not supported.")}set media(e){throw new Zr("media setter in Schema class is not not supported.")}get nullable(){return this.get("nullable")}set nullable(e){this.set("nullable",e)}get discriminator(){return this.get("discriminator")}set discriminator(e){this.set("discriminator",e)}get writeOnly(){return this.get("writeOnly")}set writeOnly(e){this.set("writeOnly",e)}get xml(){return this.get("xml")}set xml(e){this.set("xml",e)}get externalDocs(){return this.get("externalDocs")}set externalDocs(e){this.set("externalDocs",e)}get example(){return this.get("example")}set example(e){this.set("example",e)}get deprecated(){return this.get("deprecated")}set deprecated(e){this.set("deprecated",e)}};class nP extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="securityRequirement"}}const dm=nP;class aP extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="securityScheme"}get type(){return this.get("type")}set type(e){this.set("type",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get name(){return this.get("name")}set name(e){this.set("name",e)}get in(){return this.get("in")}set in(e){this.set("in",e)}get scheme(){return this.get("scheme")}set scheme(e){this.set("scheme",e)}get bearerFormat(){return this.get("bearerFormat")}set bearerFormat(e){this.set("bearerFormat",e)}get flows(){return this.get("flows")}set flows(e){this.set("flows",e)}get openIdConnectUrl(){return this.get("openIdConnectUrl")}set openIdConnectUrl(e){this.set("openIdConnectUrl",e)}}const fm=aP;class oP extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="server"}get url(){return this.get("url")}set url(e){this.set("url",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get variables(){return this.get("variables")}set variables(e){this.set("variables",e)}}const mm=oP;class iP extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="serverVariable"}get enum(){return this.get("enum")}set enum(e){this.set("enum",e)}get default(){return this.get("default")}set default(e){this.set("default",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}}const gm=iP;class sP extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="tag"}get name(){return this.get("name")}set name(e){this.set("name",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get externalDocs(){return this.get("externalDocs")}set externalDocs(e){this.set("externalDocs",e)}}const Uy=sP;class lP extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="xml"}get name(){return this.get("name")}set name(e){this.set("name",e)}get namespace(){return this.get("namespace")}set namespace(e){this.set("namespace",e)}get prefix(){return this.get("prefix")}set prefix(e){this.set("prefix",e)}get attribute(){return this.get("attribute")}set attribute(e){this.set("attribute",e)}get wrapped(){return this.get("wrapped")}set wrapped(e){this.set("wrapped",e)}}const zy=lP,VS=class{element;constructor(e={}){Object.assign(this,e)}copyMetaAndAttributes(e,r){(e.meta.length>0||r.meta.length>0)&&(r.meta=qp(r.meta,e.meta)),Gh(e)&&ob(r,e),(e.attributes.length>0||e.meta.length>0)&&(r.attributes=qp(r.attributes,e.attributes))}},er=class extends VS{enter(e){return this.element=hr(e),on}},zs=class extends VS{specObj;passingOptionsNames=["specObj","openApiGenericElement","openApiSemanticElement"];openApiGenericElement;openApiSemanticElement;constructor({specObj:e,passingOptionsNames:r,openApiGenericElement:n,openApiSemanticElement:a,...o}){super({...o}),this.specObj=e,this.openApiGenericElement=n,this.openApiSemanticElement=a,Array.isArray(r)&&(this.passingOptionsNames=r)}retrievePassingOptions(){return FS(this.passingOptionsNames,this)}retrieveFixedFields(e){const r=ro(["visitors",...e,"fixedFields"],this.specObj);return typeof r=="object"&&r!==null?Object.keys(r):[]}retrieveVisitor(e){return W0(Fp,["visitors",...e],this.specObj)?ro(["visitors",...e],this.specObj):ro(["visitors",...e,"$visitor"],this.specObj)}retrieveVisitorInstance(e,r={}){const n=this.retrievePassingOptions();return new(this.retrieveVisitor(e))({...n,...r})}toRefractedElement(e,r,n={}){const a=this.retrieveVisitorInstance(e,n);return a instanceof er&&a?.constructor===er?hr(r):(Pa(r,a,n),a.element)}};var cP=(function(){function t(e,r){this.xf=r,this.n=e,this.i=0}return t.prototype["@@transducer/init"]=Tu,t.prototype["@@transducer/result"]=Nf,t.prototype["@@transducer/step"]=function(e,r){this.i+=1;var n=this.n===0?e:this.xf["@@transducer/step"](e,r);return this.n>=0&&this.i>=this.n?_y(n):n},t})();function uP(t){return function(e){return new cP(t,e)}}const pP=Tr(Nu(["take"],uP,(function(e,r){return kf(0,e<0?1/0:e,r)})));var hP=Tr((function(t,e){return Pu(pP(t.length,e),t)}));const dP=hP,Vn=t=>hn(t)&&t.hasKey("$ref"),fP=hn,mP=hn,JS=t=>Ir(t.key)&&dP("x-",Et(t.key)),Jr=class extends zs{specPath;ignoredFields;canSupportSpecificationExtensions=!0;specificationExtensionPredicate=JS;constructor({specPath:e,ignoredFields:r,canSupportSpecificationExtensions:n,specificationExtensionPredicate:a,...o}){super({...o}),this.specPath=e,this.ignoredFields=r||[],typeof n=="boolean"&&(this.canSupportSpecificationExtensions=n),typeof a=="function"&&(this.specificationExtensionPredicate=a)}ObjectElement(e){const r=this.specPath(e),n=this.retrieveFixedFields(r);return e.forEach(((a,o,p)=>{if(Ir(o)&&n.includes(Et(o))&&!this.ignoredFields.includes(Et(o))){const c=this.toRefractedElement([...r,"fixedFields",Et(o)],a),d=new Ge.Pr(hr(o),c);this.copyMetaAndAttributes(p,d),d.classes.push("fixed-field"),this.element.content.push(d)}else if(this.canSupportSpecificationExtensions&&this.specificationExtensionPredicate(p)){const c=this.toRefractedElement(["document","extension"],p);this.element.content.push(c)}else this.ignoredFields.includes(Et(o))||this.element.content.push(hr(p))})),this.copyMetaAndAttributes(e,this.element),on}};class gP extends Vt(Jr,er){constructor(e){super(e),this.element=new Ry,this.specPath=jr(["document","objects","OpenApi"]),this.canSupportSpecificationExtensions=!0}ObjectElement(e){return Jr.prototype.ObjectElement.call(this,e)}}const yP=gP;class vP extends Vt(zs,er){StringElement(e){const r=new em(Et(e));return this.copyMetaAndAttributes(e,r),this.element=r,on}}const bP=vP,wP=class extends zs{MemberElement(e){return this.element=hr(e),this.element.classes.push("specification-extension"),on}};class EP extends Vt(Jr,er){constructor(e){super(e),this.element=new Yf,this.specPath=jr(["document","objects","Info"]),this.canSupportSpecificationExtensions=!0}}const SP=EP,xP=class extends er{StringElement(e){const r=super.enter(e);return this.element.classes.push("api-version"),this.element.classes.push("version"),r}};class _P extends Vt(Jr,er){constructor(e){super(e),this.element=new Jf,this.specPath=jr(["document","objects","Contact"]),this.canSupportSpecificationExtensions=!0}}const OP=_P;class kP extends Vt(Jr,er){constructor(e){super(e),this.element=new Xf,this.specPath=jr(["document","objects","License"]),this.canSupportSpecificationExtensions=!0}}const AP=kP;class CP extends Vt(Jr,er){constructor(e){super(e),this.element=new Qf,this.specPath=jr(["document","objects","Link"]),this.canSupportSpecificationExtensions=!0}ObjectElement(e){const r=Jr.prototype.ObjectElement.call(this,e);return(Ir(this.element.operationId)||Ir(this.element.operationRef))&&this.element.classes.push("reference-element"),r}}const jP=CP,PP=class extends er{StringElement(e){const r=super.enter(e);return this.element.classes.push("reference-value"),r}},NP=class extends er{StringElement(e){const r=super.enter(e);return this.element.classes.push("reference-value"),r}},nd=class extends zs{specPath;ignoredFields;fieldPatternPredicate=rb;canSupportSpecificationExtensions=!1;specificationExtensionPredicate=JS;constructor({specPath:e,ignoredFields:r,fieldPatternPredicate:n,canSupportSpecificationExtensions:a,specificationExtensionPredicate:o,...p}){super({...p}),this.specPath=e,this.ignoredFields=r||[],typeof n=="function"&&(this.fieldPatternPredicate=n),typeof a=="boolean"&&(this.canSupportSpecificationExtensions=a),typeof o=="function"&&(this.specificationExtensionPredicate=o)}ObjectElement(e){return e.forEach(((r,n,a)=>{if(this.canSupportSpecificationExtensions&&this.specificationExtensionPredicate(a)){const o=this.toRefractedElement(["document","extension"],a);this.element.content.push(o)}else if(!this.ignoredFields.includes(Et(n))&&this.fieldPatternPredicate(Et(n))){const o=this.specPath(r),p=this.toRefractedElement(o,r),c=new Ge.Pr(hr(n),p);this.copyMetaAndAttributes(a,c),c.classes.push("patterned-field"),this.element.content.push(c)}else this.ignoredFields.includes(Et(n))||this.element.content.push(hr(a))})),this.copyMetaAndAttributes(e,this.element),on}},Ur=class extends nd{constructor(e){super(e),this.fieldPatternPredicate=$y}};class hb extends Ge.Sh{static primaryClass="link-parameters";constructor(e,r,n){super(e,r,n),this.classes.push(hb.primaryClass)}}const TP=hb;class IP extends Vt(Ur,er){constructor(e){super(e),this.element=new TP,this.specPath=jr(["value"])}}const MP=IP;class RP extends Vt(Jr,er){constructor(e){super(e),this.element=new mm,this.specPath=jr(["document","objects","Server"]),this.canSupportSpecificationExtensions=!0}}const DP=RP,FP=class extends er{StringElement(e){const r=super.enter(e);return this.element.classes.push("server-url"),r}};class db extends Ge.wE{static primaryClass="servers";constructor(e,r,n){super(e,r,n),this.classes.push(db.primaryClass)}}const WS=db;class LP extends Vt(zs,er){constructor(e){super(e),this.element=new WS}ArrayElement(e){return e.forEach((r=>{const n=fP(r)?["document","objects","Server"]:["value"],a=this.toRefractedElement(n,r);this.element.push(a)})),this.copyMetaAndAttributes(e,this.element),on}}const fb=LP;class $P extends Vt(Jr,er){constructor(e){super(e),this.element=new gm,this.specPath=jr(["document","objects","ServerVariable"]),this.canSupportSpecificationExtensions=!0}}const BP=$P;class mb extends Ge.Sh{static primaryClass="server-variables";constructor(e,r,n){super(e,r,n),this.classes.push(mb.primaryClass)}}const qP=mb;class UP extends Vt(Ur,er){constructor(e){super(e),this.element=new qP,this.specPath=jr(["document","objects","ServerVariable"])}}const zP=UP;class VP extends Vt(Jr,er){constructor(e){super(e),this.element=new Zf,this.specPath=jr(["document","objects","MediaType"]),this.canSupportSpecificationExtensions=!0}}const JP=VP,zl=class extends zs{alternator;constructor({alternator:e,...r}){super({...r}),this.alternator=e||[]}enter(e){const r=this.alternator.map((({predicate:a,specPath:o})=>ub(a,jr(o),vy))),n=US(r)(e);return this.element=this.toRefractedElement(n,e),on}},WP=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof zf||t(n)&&e("callback",n)&&r("object",n))),HP=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Vf||t(n)&&e("components",n)&&r("object",n))),KP=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Jf||t(n)&&e("contact",n)&&r("object",n))),GP=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Hf||t(n)&&e("example",n)&&r("object",n))),YP=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Kf||t(n)&&e("externalDocumentation",n)&&r("object",n))),ad=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Gf||t(n)&&e("header",n)&&r("object",n))),XP=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Yf||t(n)&&e("info",n)&&r("object",n))),QP=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Xf||t(n)&&e("license",n)&&r("object",n))),ZP=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Qf||t(n)&&e("link",n)&&r("object",n))),eN=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof em||t(n)&&e("openapi",n)&&r("string",n))),tN=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r,hasClass:n})=>a=>a instanceof Ry||t(a)&&e("openApi3_0",a)&&r("object",a)&&n("api",a))),HS=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof tm||t(n)&&e("operation",n)&&r("object",n))),rN=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof rm||t(n)&&e("parameter",n)&&r("object",n))),gb=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof nm||t(n)&&e("pathItem",n)&&r("object",n))),nN=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof am||t(n)&&e("paths",n)&&r("object",n))),Mn=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof om||t(n)&&e("reference",n)&&r("object",n))),aN=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof im||t(n)&&e("requestBody",n)&&r("object",n))),Vy=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof sm||t(n)&&e("response",n)&&r("object",n))),oN=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof lm||t(n)&&e("responses",n)&&r("object",n))),iN=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof qy||t(n)&&e("schema",n)&&r("object",n))),sN=t=>Ff(t)&&t.classes.includes("boolean-json-schema"),lN=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof dm||t(n)&&e("securityRequirement",n)&&r("object",n))),cN=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof fm||t(n)&&e("securityScheme",n)&&r("object",n))),uN=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof mm||t(n)&&e("server",n)&&r("object",n))),pN=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof gm||t(n)&&e("serverVariable",n)&&r("object",n))),Jy=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Zf||t(n)&&e("mediaType",n)&&r("object",n))),KS=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r,hasClass:n})=>a=>a instanceof WS||t(a)&&e("array",a)&&r("array",a)&&n("servers",a))),hN=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Wf||t(n)&&e("discriminator",n)&&r("object",n)));class dN extends Vt(zl,er){constructor(e){super(e),this.alternator=[{predicate:Vn,specPath:["document","objects","Reference"]},{predicate:kc,specPath:["document","objects","Schema"]}]}ObjectElement(e){const r=zl.prototype.enter.call(this,e);return Mn(this.element)&&this.element.setMetaProperty("referenced-element","schema"),r}}const fN=dN;class mN extends Vt(Ur,er){constructor(e){super(e),this.element=new Ge.Sh,this.element.classes.push("examples"),this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","Example"],this.canSupportSpecificationExtensions=!0}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","example")})),r}}const yb=mN;class vb extends Ge.Sh{static primaryClass="media-type-examples";constructor(e,r,n){super(e,r,n),this.classes.push(vb.primaryClass),this.classes.push("examples")}}const gN=vb,yN=class extends yb{constructor(e){super(e),this.element=new gN}};class bb extends Ge.Sh{static primaryClass="media-type-encoding";constructor(e,r,n){super(e,r,n),this.classes.push(bb.primaryClass)}}const vN=bb;class bN extends Vt(Ur,er){constructor(e){super(e),this.element=new vN,this.specPath=jr(["document","objects","Encoding"])}}const wN=bN;class EN extends Vt(Ur,er){constructor(e){super(e),this.element=new dm,this.specPath=jr(["value"])}}const SN=EN;class wb extends Ge.wE{static primaryClass="security";constructor(e,r,n){super(e,r,n),this.classes.push(wb.primaryClass)}}const xN=wb;class _N extends Vt(zs,er){constructor(e){super(e),this.element=new xN}ArrayElement(e){return e.forEach((r=>{if(hn(r)){const n=this.toRefractedElement(["document","objects","SecurityRequirement"],r);this.element.push(n)}else this.element.push(hr(r))})),this.copyMetaAndAttributes(e,this.element),on}}const ON=_N;class kN extends Vt(Jr,er){constructor(e){super(e),this.element=new Vf,this.specPath=jr(["document","objects","Components"]),this.canSupportSpecificationExtensions=!0}}const AN=kN;class CN extends Vt(Jr,er){constructor(e){super(e),this.element=new Uy,this.specPath=jr(["document","objects","Tag"]),this.canSupportSpecificationExtensions=!0}}const jN=CN;class PN extends Vt(Jr,er){constructor(e){super(e),this.element=new om,this.specPath=jr(["document","objects","Reference"]),this.canSupportSpecificationExtensions=!1}ObjectElement(e){const r=Jr.prototype.ObjectElement.call(this,e);return Ir(this.element.$ref)&&this.element.classes.push("reference-element"),r}}const NN=PN,TN=class extends er{StringElement(e){const r=super.enter(e);return this.element.classes.push("reference-value"),r}};class IN extends Vt(Jr,er){constructor(e){super(e),this.element=new rm,this.specPath=jr(["document","objects","Parameter"]),this.canSupportSpecificationExtensions=!0}ObjectElement(e){const r=Jr.prototype.ObjectElement.call(this,e);return hn(this.element.contentProp)&&this.element.contentProp.filter(Jy).forEach(((n,a)=>{n.setMetaProperty("media-type",Et(a))})),r}}const MN=IN;class RN extends Vt(zl,er){constructor(e){super(e),this.alternator=[{predicate:Vn,specPath:["document","objects","Reference"]},{predicate:kc,specPath:["document","objects","Schema"]}]}ObjectElement(e){const r=zl.prototype.enter.call(this,e);return Mn(this.element)&&this.element.setMetaProperty("referenced-element","schema"),r}}const DN=RN;class FN extends Vt(Jr,er){constructor(e){super(e),this.element=new Gf,this.specPath=jr(["document","objects","Header"]),this.canSupportSpecificationExtensions=!0}}const LN=FN;class $N extends Vt(zl,er){constructor(e){super(e),this.alternator=[{predicate:Vn,specPath:["document","objects","Reference"]},{predicate:kc,specPath:["document","objects","Schema"]}]}ObjectElement(e){const r=zl.prototype.enter.call(this,e);return Mn(this.element)&&this.element.setMetaProperty("referenced-element","schema"),r}}const BN=$N;class Eb extends Ge.Sh{static primaryClass="header-examples";constructor(e,r,n){super(e,r,n),this.classes.push(Eb.primaryClass),this.classes.push("examples")}}const qN=Eb,UN=class extends yb{constructor(e){super(e),this.element=new qN}};class zN extends Vt(Ur,er){constructor(e){super(e),this.element=new Ge.Sh,this.element.classes.push("content"),this.specPath=jr(["document","objects","MediaType"])}}const Wy=zN;class Sb extends Ge.Sh{static primaryClass="header-content";constructor(e,r,n){super(e,r,n),this.classes.push(Sb.primaryClass),this.classes.push("content")}}const VN=Sb,JN=class extends Wy{constructor(e){super(e),this.element=new VN}};class WN extends Vt(Jr,er){constructor(e){super(e),this.element=new qy,this.specPath=jr(["document","objects","Schema"]),this.canSupportSpecificationExtensions=!0}}const HN=WN,GS=bo.visitors.document.objects.JSONSchema.fixedFields.allOf,KN=class extends GS{ArrayElement(e){const r=GS.prototype.ArrayElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","schema")})),r}},YS=bo.visitors.document.objects.JSONSchema.fixedFields.anyOf,GN=class extends YS{ArrayElement(e){const r=YS.prototype.ArrayElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","schema")})),r}},XS=bo.visitors.document.objects.JSONSchema.fixedFields.oneOf,YN=class extends XS{ArrayElement(e){const r=XS.prototype.ArrayElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","schema")})),r}},QS=bo.visitors.document.objects.JSONSchema.fixedFields.items,XN=class extends QS{ObjectElement(e){const r=QS.prototype.ObjectElement.call(this,e);return Mn(this.element)&&this.element.setMetaProperty("referenced-element","schema"),r}ArrayElement(e){return this.enter(e)}},ZS=bo.visitors.document.objects.JSONSchema.fixedFields.properties,QN=class extends ZS{ObjectElement(e){const r=ZS.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","schema")})),r}},ZN=bo.visitors.document.objects.JSONSchema.fixedFields.type,eT=class extends ZN{ArrayElement(e){return this.enter(e)}},e2=bo.visitors.JSONSchemaOrJSONReferenceVisitor,t2=class extends e2{ObjectElement(e){const r=e2.prototype.enter.call(this,e);return Mn(this.element)&&this.element.setMetaProperty("referenced-element","schema"),r}};class tT extends Vt(Jr,er){constructor(e){super(e),this.element=new Wf,this.specPath=jr(["document","objects","Discriminator"]),this.canSupportSpecificationExtensions=!1}}const rT=tT;class xb extends Ge.Sh{static primaryClass="discriminator-mapping";constructor(e,r,n){super(e,r,n),this.classes.push(xb.primaryClass)}}const nT=xb;class aT extends Vt(Ur,er){constructor(e){super(e),this.element=new nT,this.specPath=jr(["value"])}}const oT=aT;class iT extends Vt(Jr,er){constructor(e){super(e),this.element=new zy,this.specPath=jr(["document","objects","XML"]),this.canSupportSpecificationExtensions=!0}}const sT=iT;class _b extends Ge.Sh{static primaryClass="parameter-examples";constructor(e,r,n){super(e,r,n),this.classes.push(_b.primaryClass),this.classes.push("examples")}}const lT=_b,cT=class extends yb{constructor(e){super(e),this.element=new lT}};class Ob extends Ge.Sh{static primaryClass="parameter-content";constructor(e,r,n){super(e,r,n),this.classes.push(Ob.primaryClass),this.classes.push("content")}}const uT=Ob,pT=class extends Wy{constructor(e){super(e),this.element=new uT}};class kb extends Ge.Sh{static primaryClass="components-schemas";constructor(e,r,n){super(e,r,n),this.classes.push(kb.primaryClass)}}const r2=kb;class hT extends Vt(Ur,er){constructor(e){super(e),this.element=new r2,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","Schema"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","schema")})),r}}const dT=hT;class Ab extends Ge.Sh{static primaryClass="components-responses";constructor(e,r,n){super(e,r,n),this.classes.push(Ab.primaryClass)}}const fT=Ab;class mT extends Vt(Ur,er){constructor(e){super(e),this.element=new fT,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","Response"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","response")})),this.element.filter(Vy).forEach(((n,a)=>{n.setMetaProperty("http-status-code",Et(a))})),r}}const gT=mT;class Cb extends Ge.Sh{static primaryClass="components-parameters";constructor(e,r,n){super(e,r,n),this.classes.push(Cb.primaryClass),this.classes.push("parameters")}}const yT=Cb;class vT extends Vt(Ur,er){constructor(e){super(e),this.element=new yT,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","Parameter"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","parameter")})),r}}const bT=vT;class jb extends Ge.Sh{static primaryClass="components-examples";constructor(e,r,n){super(e,r,n),this.classes.push(jb.primaryClass),this.classes.push("examples")}}const wT=jb;class ET extends Vt(Ur,er){constructor(e){super(e),this.element=new wT,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","Example"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","example")})),r}}const ST=ET;class Pb extends Ge.Sh{static primaryClass="components-request-bodies";constructor(e,r,n){super(e,r,n),this.classes.push(Pb.primaryClass)}}const xT=Pb;class _T extends Vt(Ur,er){constructor(e){super(e),this.element=new xT,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","RequestBody"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","requestBody")})),r}}const OT=_T;class Nb extends Ge.Sh{static primaryClass="components-headers";constructor(e,r,n){super(e,r,n),this.classes.push(Nb.primaryClass)}}const kT=Nb;class AT extends Vt(Ur,er){constructor(e){super(e),this.element=new kT,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","Header"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","header")})),this.element.filter(ad).forEach(((n,a)=>{n.setMetaProperty("header-name",Et(a))})),r}}const CT=AT;class Tb extends Ge.Sh{static primaryClass="components-security-schemes";constructor(e,r,n){super(e,r,n),this.classes.push(Tb.primaryClass)}}const jT=Tb;class PT extends Vt(Ur,er){constructor(e){super(e),this.element=new jT,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","SecurityScheme"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","securityScheme")})),r}}const NT=PT;class Ib extends Ge.Sh{static primaryClass="components-links";constructor(e,r,n){super(e,r,n),this.classes.push(Ib.primaryClass)}}const TT=Ib;class IT extends Vt(Ur,er){constructor(e){super(e),this.element=new TT,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","Link"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","link")})),r}}const MT=IT;class Mb extends Ge.Sh{static primaryClass="components-callbacks";constructor(e,r,n){super(e,r,n),this.classes.push(Mb.primaryClass)}}const RT=Mb;class DT extends Vt(Ur,er){constructor(e){super(e),this.element=new RT,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","Callback"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","callback")})),r}}const FT=DT;class LT extends Vt(Jr,er){constructor(e){super(e),this.element=new Hf,this.specPath=jr(["document","objects","Example"]),this.canSupportSpecificationExtensions=!0}ObjectElement(e){const r=Jr.prototype.ObjectElement.call(this,e);return Ir(this.element.externalValue)&&this.element.classes.push("reference-element"),r}}const $T=LT,BT=class extends er{StringElement(e){const r=super.enter(e);return this.element.classes.push("reference-value"),r}};class qT extends Vt(Jr,er){constructor(e){super(e),this.element=new Kf,this.specPath=jr(["document","objects","ExternalDocumentation"]),this.canSupportSpecificationExtensions=!0}}const UT=qT;class zT extends Vt(Jr,er){constructor(e){super(e),this.element=new Ty,this.specPath=jr(["document","objects","Encoding"]),this.canSupportSpecificationExtensions=!0}ObjectElement(e){const r=Jr.prototype.ObjectElement.call(this,e);return hn(this.element.headers)&&this.element.headers.filter(ad).forEach(((n,a)=>{n.setMetaProperty("header-name",Et(a))})),r}}const VT=zT;class Rb extends Ge.Sh{static primaryClass="encoding-headers";constructor(e,r,n){super(e,r,n),this.classes.push(Rb.primaryClass)}}const JT=Rb;class WT extends Vt(Ur,er){constructor(e){super(e),this.element=new JT,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","Header"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","header")})),this.element.forEach(((n,a)=>{if(!ad(n))return;const o=Et(a);n.setMetaProperty("headerName",o)})),r}}const HT=WT;class KT extends Vt(nd,er){constructor(e){super(e),this.element=new am,this.specPath=jr(["document","objects","PathItem"]),this.canSupportSpecificationExtensions=!0,this.fieldPatternPredicate=kc}ObjectElement(e){const r=nd.prototype.ObjectElement.call(this,e);return this.element.filter(gb).forEach(((n,a)=>{a.classes.push("openapi-path-template"),a.classes.push("path-template"),n.setMetaProperty("path",hr(a))})),r}}const GT=KT;class YT extends Vt(Jr,er){constructor(e){super(e),this.element=new im,this.specPath=jr(["document","objects","RequestBody"])}ObjectElement(e){const r=Jr.prototype.ObjectElement.call(this,e);return hn(this.element.contentProp)&&this.element.contentProp.filter(Jy).forEach(((n,a)=>{n.setMetaProperty("media-type",Et(a))})),r}}const XT=YT;class Db extends Ge.Sh{static primaryClass="request-body-content";constructor(e,r,n){super(e,r,n),this.classes.push(Db.primaryClass),this.classes.push("content")}}const QT=Db,ZT=class extends Wy{constructor(e){super(e),this.element=new QT}};class e6 extends Vt(nd,er){constructor(e){super(e),this.element=new zf,this.specPath=jr(["document","objects","PathItem"]),this.canSupportSpecificationExtensions=!0,this.fieldPatternPredicate=r=>/{(?[^}]{1,2083})}/.test(String(r))}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(gb).forEach(((n,a)=>{n.setMetaProperty("runtime-expression",Et(a))})),r}}const t6=e6;class r6 extends Vt(Jr,er){constructor(e){super(e),this.element=new sm,this.specPath=jr(["document","objects","Response"])}ObjectElement(e){const r=Jr.prototype.ObjectElement.call(this,e);return hn(this.element.contentProp)&&this.element.contentProp.filter(Jy).forEach(((n,a)=>{n.setMetaProperty("media-type",Et(a))})),hn(this.element.headers)&&this.element.headers.filter(ad).forEach(((n,a)=>{n.setMetaProperty("header-name",Et(a))})),r}}const n6=r6;class Fb extends Ge.Sh{static primaryClass="response-headers";constructor(e,r,n){super(e,r,n),this.classes.push(Fb.primaryClass)}}const a6=Fb;class o6 extends Vt(Ur,er){constructor(e){super(e),this.element=new a6,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","Header"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","header")})),this.element.forEach(((n,a)=>{if(!ad(n))return;const o=Et(a);n.setMetaProperty("header-name",o)})),r}}const i6=o6;class Lb extends Ge.Sh{static primaryClass="response-content";constructor(e,r,n){super(e,r,n),this.classes.push(Lb.primaryClass),this.classes.push("content")}}const s6=Lb,l6=class extends Wy{constructor(e){super(e),this.element=new s6}};class $b extends Ge.Sh{static primaryClass="response-links";constructor(e,r,n){super(e,r,n),this.classes.push($b.primaryClass)}}const c6=$b;class u6 extends Vt(Ur,er){constructor(e){super(e),this.element=new c6,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","Link"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","link")})),r}}const p6=u6;function n2(t){return Object.prototype.toString.call(t)==="[object Number]"}var h6=Tr((function(e,r){if(!n2(e)||!n2(r))throw new TypeError("Both arguments to range must be numbers");for(var n=Array(eVn(r)?["document","objects","Reference"]:["document","objects","Response"],this.fieldPatternPredicate=r=>new RegExp(`^(1XX|2XX|3XX|4XX|5XX|${d6(100,600).join("|")})$`).test(String(r))}ObjectElement(e){const r=o2.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","response")})),this.element.filter(Vy).forEach(((n,a)=>{const o=hr(a);this.fieldPatternPredicate(Et(o))&&n.setMetaProperty("http-status-code",o)})),r}}const b6=v6;class w6 extends Vt(zl,er){constructor(e){super(e),this.alternator=[{predicate:Vn,specPath:["document","objects","Reference"]},{predicate:kc,specPath:["document","objects","Response"]}]}ObjectElement(e){const r=zl.prototype.enter.call(this,e);return Mn(this.element)?this.element.setMetaProperty("referenced-element","response"):Vy(this.element)&&this.element.setMetaProperty("http-status-code","default"),r}}const E6=w6;class S6 extends Vt(Jr,er){constructor(e){super(e),this.element=new tm,this.specPath=jr(["document","objects","Operation"])}}const x6=S6;class Bb extends Ge.wE{static primaryClass="operation-tags";constructor(e,r,n){super(e,r,n),this.classes.push(Bb.primaryClass)}}const _6=Bb,O6=class extends er{constructor(e){super(e),this.element=new _6}ArrayElement(e){return this.element=this.element.concat(hr(e)),on}};class qb extends Ge.wE{static primaryClass="operation-parameters";constructor(e,r,n){super(e,r,n),this.classes.push(qb.primaryClass),this.classes.push("parameters")}}const i2=qb;class k6 extends Vt(zs,er){constructor(e){super(e),this.element=new Ge.wE,this.element.classes.push("parameters")}ArrayElement(e){return e.forEach((r=>{const n=Vn(r)?["document","objects","Reference"]:["document","objects","Parameter"],a=this.toRefractedElement(n,r);Mn(a)&&a.setMetaProperty("referenced-element","parameter"),this.element.push(a)})),this.copyMetaAndAttributes(e,this.element),on}}const s2=k6,A6=class extends s2{constructor(e){super(e),this.element=new i2}},C6=class extends zl{constructor(e){super(e),this.alternator=[{predicate:Vn,specPath:["document","objects","Reference"]},{predicate:kc,specPath:["document","objects","RequestBody"]}]}ObjectElement(e){const r=zl.prototype.enter.call(this,e);return Mn(this.element)&&this.element.setMetaProperty("referenced-element","requestBody"),r}};class Ub extends Ge.Sh{static primaryClass="operation-callbacks";constructor(e,r,n){super(e,r,n),this.classes.push(Ub.primaryClass)}}const j6=Ub;class P6 extends Vt(Ur,er){specPath;constructor(e){super(e),this.element=new j6,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","Callback"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Mn).forEach((n=>{n.setMetaProperty("referenced-element","callback")})),r}}const N6=P6;class zb extends Ge.wE{static primaryClass="operation-security";constructor(e,r,n){super(e,r,n),this.classes.push(zb.primaryClass),this.classes.push("security")}}const l2=zb;class T6 extends Vt(zs,er){constructor(e){super(e),this.element=new l2}ArrayElement(e){return e.forEach((r=>{const n=hn(r)?["document","objects","SecurityRequirement"]:["value"],a=this.toRefractedElement(n,r);this.element.push(a)})),this.copyMetaAndAttributes(e,this.element),on}}const I6=T6;class Vb extends Ge.wE{static primaryClass="operation-servers";constructor(e,r,n){super(e,r,n),this.classes.push(Vb.primaryClass),this.classes.push("servers")}}const M6=Vb,R6=class extends fb{constructor(e){super(e),this.element=new M6}};class D6 extends Vt(Jr,er){constructor(e){super(e),this.element=new nm,this.specPath=jr(["document","objects","PathItem"])}ObjectElement(e){const r=Jr.prototype.ObjectElement.call(this,e);return this.element.filter(HS).forEach(((n,a)=>{const o=hr(a);o.content=Et(o).toUpperCase(),n.setMetaProperty("http-method",o)})),Ir(this.element.$ref)&&this.element.classes.push("reference-element"),r}}const F6=D6,L6=class extends er{StringElement(e){const r=super.enter(e);return this.element.classes.push("reference-value"),r}};class Jb extends Ge.wE{static primaryClass="path-item-servers";constructor(e,r,n){super(e,r,n),this.classes.push(Jb.primaryClass),this.classes.push("servers")}}const $6=Jb,B6=class extends fb{constructor(e){super(e),this.element=new $6}};class Wb extends Ge.wE{static primaryClass="path-item-parameters";constructor(e,r,n){super(e,r,n),this.classes.push(Wb.primaryClass),this.classes.push("parameters")}}const q6=Wb,U6=class extends s2{constructor(e){super(e),this.element=new q6}};class z6 extends Vt(Jr,er){constructor(e){super(e),this.element=new fm,this.specPath=jr(["document","objects","SecurityScheme"]),this.canSupportSpecificationExtensions=!0}}const V6=z6;class J6 extends Vt(Jr,er){constructor(e){super(e),this.element=new My,this.specPath=jr(["document","objects","OAuthFlows"]),this.canSupportSpecificationExtensions=!0}}const W6=J6;class H6 extends Vt(Jr,er){constructor(e){super(e),this.element=new Iy,this.specPath=jr(["document","objects","OAuthFlow"]),this.canSupportSpecificationExtensions=!0}}const K6=H6;class Hb extends Ge.Sh{static primaryClass="oauth-flow-scopes";constructor(e,r,n){super(e,r,n),this.classes.push(Hb.primaryClass)}}const G6=Hb;class Y6 extends Vt(Ur,er){constructor(e){super(e),this.element=new G6,this.specPath=jr(["value"])}}const X6=Y6;class Kb extends Ge.wE{static primaryClass="tags";constructor(e,r,n){super(e,r,n),this.classes.push(Kb.primaryClass)}}const Q6=Kb;class Z6 extends Vt(zs,er){constructor(e){super(e),this.element=new Q6}ArrayElement(e){return e.forEach((r=>{const n=mP(r)?["document","objects","Tag"]:["value"],a=this.toRefractedElement(n,r);this.element.push(a)})),this.copyMetaAndAttributes(e,this.element),on}}const e4=Z6,{fixedFields:Ba}=bo.visitors.document.objects.JSONSchema,pt={visitors:{value:er,document:{objects:{OpenApi:{$visitor:yP,fixedFields:{openapi:bP,info:{$ref:"#/visitors/document/objects/Info"},servers:fb,paths:{$ref:"#/visitors/document/objects/Paths"},components:{$ref:"#/visitors/document/objects/Components"},security:ON,tags:e4,externalDocs:{$ref:"#/visitors/document/objects/ExternalDocumentation"}}},Info:{$visitor:SP,fixedFields:{title:{$ref:"#/visitors/value"},description:{$ref:"#/visitors/value"},termsOfService:{$ref:"#/visitors/value"},contact:{$ref:"#/visitors/document/objects/Contact"},license:{$ref:"#/visitors/document/objects/License"},version:xP}},Contact:{$visitor:OP,fixedFields:{name:{$ref:"#/visitors/value"},url:{$ref:"#/visitors/value"},email:{$ref:"#/visitors/value"}}},License:{$visitor:AP,fixedFields:{name:{$ref:"#/visitors/value"},url:{$ref:"#/visitors/value"}}},Server:{$visitor:DP,fixedFields:{url:FP,description:{$ref:"#/visitors/value"},variables:zP}},ServerVariable:{$visitor:BP,fixedFields:{enum:{$ref:"#/visitors/value"},default:{$ref:"#/visitors/value"},description:{$ref:"#/visitors/value"}}},Components:{$visitor:AN,fixedFields:{schemas:dT,responses:gT,parameters:bT,examples:ST,requestBodies:OT,headers:CT,securitySchemes:NT,links:MT,callbacks:FT}},Paths:{$visitor:GT},PathItem:{$visitor:F6,fixedFields:{$ref:L6,summary:{$ref:"#/visitors/value"},description:{$ref:"#/visitors/value"},get:{$ref:"#/visitors/document/objects/Operation"},put:{$ref:"#/visitors/document/objects/Operation"},post:{$ref:"#/visitors/document/objects/Operation"},delete:{$ref:"#/visitors/document/objects/Operation"},options:{$ref:"#/visitors/document/objects/Operation"},head:{$ref:"#/visitors/document/objects/Operation"},patch:{$ref:"#/visitors/document/objects/Operation"},trace:{$ref:"#/visitors/document/objects/Operation"},servers:B6,parameters:U6}},Operation:{$visitor:x6,fixedFields:{tags:O6,summary:{$ref:"#/visitors/value"},description:{$ref:"#/visitors/value"},externalDocs:{$ref:"#/visitors/document/objects/ExternalDocumentation"},operationId:{$ref:"#/visitors/value"},parameters:A6,requestBody:C6,responses:{$ref:"#/visitors/document/objects/Responses"},callbacks:N6,deprecated:{$ref:"#/visitors/value"},security:I6,servers:R6}},ExternalDocumentation:{$visitor:UT,fixedFields:{description:{$ref:"#/visitors/value"},url:{$ref:"#/visitors/value"}}},Parameter:{$visitor:MN,fixedFields:{name:{$ref:"#/visitors/value"},in:{$ref:"#/visitors/value"},description:{$ref:"#/visitors/value"},required:{$ref:"#/visitors/value"},deprecated:{$ref:"#/visitors/value"},allowEmptyValue:{$ref:"#/visitors/value"},style:{$ref:"#/visitors/value"},explode:{$ref:"#/visitors/value"},allowReserved:{$ref:"#/visitors/value"},schema:DN,example:{$ref:"#/visitors/value"},examples:cT,content:pT}},RequestBody:{$visitor:XT,fixedFields:{description:{$ref:"#/visitors/value"},content:ZT,required:{$ref:"#/visitors/value"}}},MediaType:{$visitor:JP,fixedFields:{schema:fN,example:{$ref:"#/visitors/value"},examples:yN,encoding:wN}},Encoding:{$visitor:VT,fixedFields:{contentType:{$ref:"#/visitors/value"},headers:HT,style:{$ref:"#/visitors/value"},explode:{$ref:"#/visitors/value"},allowReserved:{$ref:"#/visitors/value"}}},Responses:{$visitor:b6,fixedFields:{default:E6}},Response:{$visitor:n6,fixedFields:{description:{$ref:"#/visitors/value"},headers:i6,content:l6,links:p6}},Callback:{$visitor:t6},Example:{$visitor:$T,fixedFields:{summary:{$ref:"#/visitors/value"},description:{$ref:"#/visitors/value"},value:{$ref:"#/visitors/value"},externalValue:BT}},Link:{$visitor:jP,fixedFields:{operationRef:PP,operationId:NP,parameters:MP,requestBody:{$ref:"#/visitors/value"},description:{$ref:"#/visitors/value"},server:{$ref:"#/visitors/document/objects/Server"}}},Header:{$visitor:LN,fixedFields:{description:{$ref:"#/visitors/value"},required:{$ref:"#/visitors/value"},deprecated:{$ref:"#/visitors/value"},allowEmptyValue:{$ref:"#/visitors/value"},style:{$ref:"#/visitors/value"},explode:{$ref:"#/visitors/value"},allowReserved:{$ref:"#/visitors/value"},schema:BN,example:{$ref:"#/visitors/value"},examples:UN,content:JN}},Tag:{$visitor:jN,fixedFields:{name:{$ref:"#/visitors/value"},description:{$ref:"#/visitors/value"},externalDocs:{$ref:"#/visitors/document/objects/ExternalDocumentation"}}},Reference:{$visitor:NN,fixedFields:{$ref:TN}},JSONSchema:{$ref:"#/visitors/document/objects/Schema"},JSONReference:{$ref:"#/visitors/document/objects/Reference"},Schema:{$visitor:HN,fixedFields:{title:Ba.title,multipleOf:Ba.multipleOf,maximum:Ba.maximum,exclusiveMaximum:Ba.exclusiveMaximum,minimum:Ba.minimum,exclusiveMinimum:Ba.exclusiveMinimum,maxLength:Ba.maxLength,minLength:Ba.minLength,pattern:Ba.pattern,maxItems:Ba.maxItems,minItems:Ba.minItems,uniqueItems:Ba.uniqueItems,maxProperties:Ba.maxProperties,minProperties:Ba.minProperties,required:Ba.required,enum:Ba.enum,type:eT,allOf:KN,anyOf:GN,oneOf:YN,not:t2,items:XN,properties:QN,additionalProperties:t2,description:Ba.description,format:Ba.format,default:Ba.default,nullable:{$ref:"#/visitors/value"},discriminator:{$ref:"#/visitors/document/objects/Discriminator"},writeOnly:{$ref:"#/visitors/value"},xml:{$ref:"#/visitors/document/objects/XML"},externalDocs:{$ref:"#/visitors/document/objects/ExternalDocumentation"},example:{$ref:"#/visitors/value"},deprecated:{$ref:"#/visitors/value"}}},Discriminator:{$visitor:rT,fixedFields:{propertyName:{$ref:"#/visitors/value"},mapping:oT}},XML:{$visitor:sT,fixedFields:{name:{$ref:"#/visitors/value"},namespace:{$ref:"#/visitors/value"},prefix:{$ref:"#/visitors/value"},attribute:{$ref:"#/visitors/value"},wrapped:{$ref:"#/visitors/value"}}},SecurityScheme:{$visitor:V6,fixedFields:{type:{$ref:"#/visitors/value"},description:{$ref:"#/visitors/value"},name:{$ref:"#/visitors/value"},in:{$ref:"#/visitors/value"},scheme:{$ref:"#/visitors/value"},bearerFormat:{$ref:"#/visitors/value"},flows:{$ref:"#/visitors/document/objects/OAuthFlows"},openIdConnectUrl:{$ref:"#/visitors/value"}}},OAuthFlows:{$visitor:W6,fixedFields:{implicit:{$ref:"#/visitors/document/objects/OAuthFlow"},password:{$ref:"#/visitors/document/objects/OAuthFlow"},clientCredentials:{$ref:"#/visitors/document/objects/OAuthFlow"},authorizationCode:{$ref:"#/visitors/document/objects/OAuthFlow"}}},OAuthFlow:{$visitor:K6,fixedFields:{authorizationUrl:{$ref:"#/visitors/value"},tokenUrl:{$ref:"#/visitors/value"},refreshUrl:{$ref:"#/visitors/value"},scopes:X6}},SecurityRequirement:{$visitor:SN}},extension:{$visitor:wP}}}},t4=t=>{if(da(t))return`${t.element.charAt(0).toUpperCase()+t.element.slice(1)}Element`},r4={CallbackElement:["content"],ComponentsElement:["content"],ContactElement:["content"],DiscriminatorElement:["content"],Encoding:["content"],Example:["content"],ExternalDocumentationElement:["content"],HeaderElement:["content"],InfoElement:["content"],LicenseElement:["content"],MediaTypeElement:["content"],OAuthFlowElement:["content"],OAuthFlowsElement:["content"],OpenApi3_0Element:["content"],OperationElement:["content"],ParameterElement:["content"],PathItemElement:["content"],PathsElement:["content"],ReferenceElement:["content"],RequestBodyElement:["content"],ResponseElement:["content"],ResponsesElement:["content"],SchemaElement:["content"],SecurityRequirementElement:["content"],SecuritySchemeElement:["content"],ServerElement:["content"],ServerVariableElement:["content"],TagElement:["content"],...Bl},n4={namespace:t=>{const{base:e}=t;return e.register("callback",zf),e.register("components",Vf),e.register("contact",Jf),e.register("discriminator",Wf),e.register("encoding",Ty),e.register("example",Hf),e.register("externalDocumentation",Kf),e.register("header",Gf),e.register("info",Yf),e.register("license",Xf),e.register("link",Qf),e.register("mediaType",Zf),e.register("oAuthFlow",Iy),e.register("oAuthFlows",My),e.register("openapi",em),e.register("openApi3_0",Ry),e.register("operation",tm),e.register("parameter",rm),e.register("pathItem",nm),e.register("paths",am),e.register("reference",om),e.register("requestBody",im),e.register("response",sm),e.register("responses",lm),e.register("schema",qy),e.register("securityRequirement",dm),e.register("securityScheme",fm),e.register("server",mm),e.register("serverVariable",gm),e.register("tag",Uy),e.register("xml",zy),e}},a4=n4,o4=()=>{const t=Ac(a4);return{predicates:{...te,isElement:da,isStringElement:Ir,isArrayElement:vo,isObjectElement:hn,isMemberElement:Bs,includesClasses:Yh,hasElementSourceMap:Gh},namespace:t}},i4=(t,{specPath:e=["visitors","document","objects","OpenApi","$visitor"],plugins:r=[]}={})=>{const n=(0,Ge.e)(t),a=Ru(pt),o=new(ro(e,a))({specObj:a});return Pa(n,o),ql(o.element,r,{toolboxCreator:o4,visitorOptions:{keyMap:r4,nodeTypeGetter:t4}})},On=t=>(e,r={})=>i4(e,{specPath:t,...r});zf.refract=On(["visitors","document","objects","Callback","$visitor"]),Vf.refract=On(["visitors","document","objects","Components","$visitor"]),Jf.refract=On(["visitors","document","objects","Contact","$visitor"]),Hf.refract=On(["visitors","document","objects","Example","$visitor"]),Wf.refract=On(["visitors","document","objects","Discriminator","$visitor"]),Ty.refract=On(["visitors","document","objects","Encoding","$visitor"]),Kf.refract=On(["visitors","document","objects","ExternalDocumentation","$visitor"]),Gf.refract=On(["visitors","document","objects","Header","$visitor"]),Yf.refract=On(["visitors","document","objects","Info","$visitor"]),Xf.refract=On(["visitors","document","objects","License","$visitor"]),Qf.refract=On(["visitors","document","objects","Link","$visitor"]),Zf.refract=On(["visitors","document","objects","MediaType","$visitor"]),Iy.refract=On(["visitors","document","objects","OAuthFlow","$visitor"]),My.refract=On(["visitors","document","objects","OAuthFlows","$visitor"]),em.refract=On(["visitors","document","objects","OpenApi","fixedFields","openapi"]),Ry.refract=On(["visitors","document","objects","OpenApi","$visitor"]),tm.refract=On(["visitors","document","objects","Operation","$visitor"]),rm.refract=On(["visitors","document","objects","Parameter","$visitor"]),nm.refract=On(["visitors","document","objects","PathItem","$visitor"]),am.refract=On(["visitors","document","objects","Paths","$visitor"]),om.refract=On(["visitors","document","objects","Reference","$visitor"]),im.refract=On(["visitors","document","objects","RequestBody","$visitor"]),sm.refract=On(["visitors","document","objects","Response","$visitor"]),lm.refract=On(["visitors","document","objects","Responses","$visitor"]),qy.refract=On(["visitors","document","objects","Schema","$visitor"]),dm.refract=On(["visitors","document","objects","SecurityRequirement","$visitor"]),fm.refract=On(["visitors","document","objects","SecurityScheme","$visitor"]),mm.refract=On(["visitors","document","objects","Server","$visitor"]),gm.refract=On(["visitors","document","objects","ServerVariable","$visitor"]),Uy.refract=On(["visitors","document","objects","Tag","$visitor"]),zy.refract=On(["visitors","document","objects","XML","$visitor"]);const Hy=class extends zf{},Ky=class extends Vf{get pathItems(){return this.get("pathItems")}set pathItems(e){this.set("pathItems",e)}},Gy=class extends Jf{},Gb=class extends Wf{},Yb=class extends Ty{},Yy=class extends Hf{},Xy=class extends Kf{},Qy=class extends Gf{get schema(){return this.get("schema")}set schema(e){this.set("schema",e)}},Zy=class extends Yf{get license(){return this.get("license")}set license(e){this.set("license",e)}get summary(){return this.get("summary")}set summary(e){this.set("summary",e)}};class Xb extends Ge.Om{static default=new Xb("https://spec.openapis.org/oas/3.1/dialect/base");constructor(e,r,n){super(e,r,n),this.element="jsonSchemaDialect"}}const ym=Xb,ev=class extends Xf{get identifier(){return this.get("identifier")}set identifier(e){this.set("identifier",e)}},tv=class extends Qf{},rv=class extends Zf{get schema(){return this.get("schema")}set schema(e){this.set("schema",e)}},Qb=class extends Iy{},Zb=class extends My{},e1=class extends em{};class s4 extends Ge.Sh{constructor(e,r,n){super(e,r,n),this.element="openApi3_1",this.classes.push("api")}get openapi(){return this.get("openapi")}set openapi(e){this.set("openapi",e)}get info(){return this.get("info")}set info(e){this.set("info",e)}get jsonSchemaDialect(){return this.get("jsonSchemaDialect")}set jsonSchemaDialect(e){this.set("jsonSchemaDialect",e)}get servers(){return this.get("servers")}set servers(e){this.set("servers",e)}get paths(){return this.get("paths")}set paths(e){this.set("paths",e)}get components(){return this.get("components")}set components(e){this.set("components",e)}get security(){return this.get("security")}set security(e){this.set("security",e)}get tags(){return this.get("tags")}set tags(e){this.set("tags",e)}get externalDocs(){return this.get("externalDocs")}set externalDocs(e){this.set("externalDocs",e)}get webhooks(){return this.get("webhooks")}set webhooks(e){this.set("webhooks",e)}}const Lu=s4,vm=class extends tm{get requestBody(){return this.get("requestBody")}set requestBody(e){this.set("requestBody",e)}},nv=class extends rm{get schema(){return this.get("schema")}set schema(e){this.set("schema",e)}},$u=class extends nm{get GET(){return this.get("get")}set GET(e){this.set("GET",e)}get PUT(){return this.get("put")}set PUT(e){this.set("PUT",e)}get POST(){return this.get("post")}set POST(e){this.set("POST",e)}get DELETE(){return this.get("delete")}set DELETE(e){this.set("DELETE",e)}get OPTIONS(){return this.get("options")}set OPTIONS(e){this.set("OPTIONS",e)}get HEAD(){return this.get("head")}set HEAD(e){this.set("HEAD",e)}get PATCH(){return this.get("patch")}set PATCH(e){this.set("PATCH",e)}get TRACE(){return this.get("trace")}set TRACE(e){this.set("TRACE",e)}},av=class extends am{};class t1 extends om{}Object.defineProperty(t1.prototype,"description",{get(){return this.get("description")},set(t){this.set("description",t)},enumerable:!0}),Object.defineProperty(t1.prototype,"summary",{get(){return this.get("summary")},set(t){this.set("summary",t)},enumerable:!0});const od=t1,ov=class extends im{},iv=class extends sm{},sv=class extends lm{},bm=class extends Zh{constructor(e,r,n){super(e,r,n),this.element="JSONSchemaDraft6"}get idProp(){throw new Zr("id keyword from Core vocabulary has been renamed to $id.")}set idProp(e){throw new Zr("id keyword from Core vocabulary has been renamed to $id.")}get $id(){return this.get("$id")}set $id(e){this.set("$id",e)}get exclusiveMaximum(){return this.get("exclusiveMaximum")}set exclusiveMaximum(e){this.set("exclusiveMaximum",e)}get exclusiveMinimum(){return this.get("exclusiveMinimum")}set exclusiveMinimum(e){this.set("exclusiveMinimum",e)}get containsProp(){return this.get("contains")}set containsProp(e){this.set("contains",e)}get items(){return this.get("items")}set items(e){this.set("items",e)}get propertyNames(){return this.get("propertyNames")}set propertyNames(e){this.set("propertyNames",e)}get const(){return this.get("const")}set const(e){this.set("const",e)}get not(){return this.get("not")}set not(e){this.set("not",e)}get examples(){return this.get("examples")}set examples(e){this.set("examples",e)}},wm=class extends um{get hrefSchema(){return this.get("hrefSchema")}set hrefSchema(e){this.set("hrefSchema",e)}get targetSchema(){return this.get("targetSchema")}set targetSchema(e){this.set("targetSchema",e)}get schema(){throw new Zr("schema keyword from Hyper-Schema vocabulary has been renamed to submissionSchema.")}set schema(e){throw new Zr("schema keyword from Hyper-Schema vocabulary has been renamed to submissionSchema.")}get submissionSchema(){return this.get("submissionSchema")}set submissionSchema(e){this.set("submissionSchema",e)}get method(){throw new Zr("method keyword from Hyper-Schema vocabulary has been removed.")}set method(e){throw new Zr("method keyword from Hyper-Schema vocabulary has been removed.")}get encType(){throw new Zr("encType keyword from Hyper-Schema vocabulary has been renamed to submissionEncType.")}set encType(e){throw new Zr("encType keyword from Hyper-Schema vocabulary has been renamed to submissionEncType.")}get submissionEncType(){return this.get("submissionEncType")}set submissionEncType(e){this.set("submissionEncType",e)}};var l4=ha((function t(e,r,n){if(e.length===0)return r;var a=e[0];if(e.length>1){var o=!jy(n)&&ts(a,n)&&typeof n[a]=="object"?n[a]:Wh(e[1])?[]:{};r=t(Array.prototype.slice.call(e,1),r,o)}return(function(c,d,E){if(Wh(c)&&Dl(E)){var x=[].concat(E);return x[c]=d,x}var k={};for(var P in E)k[P]=E[P];return k[c]=d,k})(a,r,n)}));const Kt=l4;var c4=ha((function(e,r,n){var a=Array.prototype.slice.call(n,0);return a.splice(e,r),a}));const u4=c4;var p4=ha((function(e,r,n){return Kt([e],r,n)}));const h4=p4;var d4=Tr((function t(e,r){if(r==null)return r;switch(e.length){case 0:return r;case 1:return(function(p,c){if(c==null)return c;if(Wh(p)&&Dl(c))return u4(p,1,c);var d={};for(var E in c)d[E]=c[E];return delete d[p],d})(e[0],r);default:var n=e[0],a=Array.prototype.slice.call(e,1);return r[n]==null?(function(p,c){if(Wh(p)&&Dl(c))return[].concat(c);var d={};for(var E in c)d[E]=c[E];return d})(n,r):h4(n,t(a,r[n]),r)}}));const Ii=d4,c2=class extends BS{constructor(e){super(e),this.element=new bm}get defaultDialectIdentifier(){return"http://json-schema.org/draft-06/schema#"}BooleanElement(e){const r=this.enter(e);return this.element.classes.push("boolean-json-schema"),r}handleSchemaIdentifier(e,r="$id"){return super.handleSchemaIdentifier(e,r)}},f4=class extends qS{BooleanElement(e){return this.element=this.toRefractedElement(["document","objects","JSONSchema"],e),on}},m4=class extends gn{ArrayElement(e){const r=this.enter(e);return this.element.classes.push("json-schema-examples"),r}},u2=class extends zS{constructor(e){super(e),this.element=new wm}},wo=mo(Kt(["visitors","document","objects","JSONSchema","$visitor"],c2),Ii(["visitors","document","objects","JSONSchema","fixedFields","id"]),Kt(["visitors","document","objects","JSONSchema","fixedFields","$id"],bo.visitors.value),Kt(["visitors","document","objects","JSONSchema","fixedFields","contains"],bo.visitors.JSONSchemaOrJSONReferenceVisitor),Kt(["visitors","document","objects","JSONSchema","fixedFields","items"],f4),Kt(["visitors","document","objects","JSONSchema","fixedFields","propertyNames"],bo.visitors.JSONSchemaOrJSONReferenceVisitor),Kt(["visitors","document","objects","JSONSchema","fixedFields","const"],bo.visitors.value),Kt(["visitors","document","objects","JSONSchema","fixedFields","examples"],m4),Kt(["visitors","document","objects","LinkDescription","$visitor"],u2),Kt(["visitors","document","objects","LinkDescription","fixedFields","hrefSchema"],bo.visitors.JSONSchemaOrJSONReferenceVisitor),Ii(["visitors","document","objects","LinkDescription","fixedFields","schema"]),Kt(["visitors","document","objects","LinkDescription","fixedFields","submissionSchema"],bo.visitors.JSONSchemaOrJSONReferenceVisitor),Ii(["visitors","document","objects","LinkDescription","fixedFields","method"]),Ii(["visitors","document","objects","LinkDescription","fixedFields","encType"]),Kt(["visitors","document","objects","LinkDescription","fixedFields","submissionEncType"],bo.visitors.value))(bo),g4={JSONSchemaDraft6Element:["content"],JSONReferenceElement:["content"],MediaElement:["content"],LinkDescriptionElement:["content"],...Bl},y4=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof bm||t(n)&&e("JSONSchemaDraft6",n)&&r("object",n))),v4=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof wm||t(n)&&e("linkDescription",n)&&r("object",n))),b4={namespace:t=>{const{base:e}=t;return e.register("jSONSchemaDraft6",bm),e.register("jSONReference",ed),e.register("media",cm),e.register("linkDescription",wm),e}},w4=b4,E4=()=>{const t=Ac(w4);return{predicates:{...ie,isStringElement:Ir},namespace:t}},S4=(t,{specPath:e=["visitors","document","objects","JSONSchema","$visitor"],plugins:r=[],specificationObj:n=wo}={})=>{const a=(0,Ge.e)(t),o=Ru(n),p=new(ro(e,o))({specObj:o});return Pa(a,p),ql(p.element,r,{toolboxCreator:E4,visitorOptions:{keyMap:g4,nodeTypeGetter:hm}})},p2=t=>(e,r={})=>S4(e,{specPath:t,...r});bm.refract=p2(["visitors","document","objects","JSONSchema","$visitor"]),wm.refract=p2(["visitors","document","objects","LinkDescription","$visitor"]);const Em=class extends bm{constructor(e,r,n){super(e,r,n),this.element="JSONSchemaDraft7"}get $comment(){return this.get("$comment")}set $comment(e){this.set("$comment",e)}get items(){return this.get("items")}set items(e){this.set("items",e)}get if(){return this.get("if")}set if(e){this.set("if",e)}get then(){return this.get("then")}set then(e){this.set("then",e)}get else(){return this.get("else")}set else(e){this.set("else",e)}get not(){return this.get("not")}set not(e){this.set("not",e)}get contentEncoding(){return this.get("contentEncoding")}set contentEncoding(e){this.set("contentEncoding",e)}get contentMediaType(){return this.get("contentMediaType")}set contentMediaType(e){this.set("contentMediaType",e)}get media(){throw new Zr('media keyword from Hyper-Schema vocabulary has been moved to validation vocabulary as "contentMediaType" / "contentEncoding"')}set media(e){throw new Zr('media keyword from Hyper-Schema vocabulary has been moved to validation vocabulary as "contentMediaType" / "contentEncoding"')}get writeOnly(){return this.get("writeOnly")}set writeOnly(e){this.set("writeOnly",e)}},Sm=class extends wm{get anchor(){return this.get("anchor")}set anchor(e){this.set("anchor",e)}get anchorPointer(){return this.get("anchorPointer")}set anchorPointer(e){this.set("anchorPointer",e)}get templatePointers(){return this.get("templatePointers")}set templatePointers(e){this.set("templatePointers",e)}get templateRequired(){return this.get("templateRequired")}set templateRequired(e){this.set("templateRequired",e)}get targetSchema(){return this.get("targetSchema")}set targetSchema(e){this.set("targetSchema",e)}get mediaType(){throw new Zr("mediaType keyword from Hyper-Schema vocabulary has been renamed to targetMediaType.")}set mediaType(e){throw new Zr("mediaType keyword from Hyper-Schema vocabulary has been renamed to targetMediaType.")}get targetMediaType(){return this.get("targetMediaType")}set targetMediaType(e){this.set("targetMediaType",e)}get targetHints(){return this.get("targetHints")}set targetHints(e){this.set("targetHints",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get $comment(){return this.get("$comment")}set $comment(e){this.set("$comment",e)}get hrefSchema(){return this.get("hrefSchema")}set hrefSchema(e){this.set("hrefSchema",e)}get headerSchema(){return this.get("headerSchema")}set headerSchema(e){this.set("headerSchema",e)}get submissionSchema(){return this.get("submissionSchema")}set submissionSchema(e){this.set("submissionSchema",e)}get submissionEncType(){throw new Zr("submissionEncType keyword from Hyper-Schema vocabulary has been renamed to submissionMediaType.")}set submissionEncType(e){throw new Zr("submissionEncType keyword from Hyper-Schema vocabulary has been renamed to submissionMediaType.")}get submissionMediaType(){return this.get("submissionMediaType")}set submissionMediaType(e){this.set("submissionMediaType",e)}},h2=class extends c2{constructor(e){super(e),this.element=new Em}get defaultDialectIdentifier(){return"http://json-schema.org/draft-07/schema#"}},d2=class extends u2{constructor(e){super(e),this.element=new Sm}},Bu=mo(Kt(["visitors","document","objects","JSONSchema","$visitor"],h2),Kt(["visitors","document","objects","JSONSchema","fixedFields","$comment"],wo.visitors.value),Kt(["visitors","document","objects","JSONSchema","fixedFields","if"],wo.visitors.JSONSchemaOrJSONReferenceVisitor),Kt(["visitors","document","objects","JSONSchema","fixedFields","then"],wo.visitors.JSONSchemaOrJSONReferenceVisitor),Kt(["visitors","document","objects","JSONSchema","fixedFields","else"],wo.visitors.JSONSchemaOrJSONReferenceVisitor),Ii(["visitors","document","objects","JSONSchema","fixedFields","media"]),Kt(["visitors","document","objects","JSONSchema","fixedFields","contentEncoding"],wo.visitors.value),Kt(["visitors","document","objects","JSONSchema","fixedFields","contentMediaType"],wo.visitors.value),Kt(["visitors","document","objects","JSONSchema","fixedFields","writeOnly"],wo.visitors.value),Kt(["visitors","document","objects","LinkDescription","$visitor"],d2),Kt(["visitors","document","objects","LinkDescription","fixedFields","anchor"],wo.visitors.value),Kt(["visitors","document","objects","LinkDescription","fixedFields","anchorPointer"],wo.visitors.value),Ii(["visitors","document","objects","LinkDescription","fixedFields","mediaType"]),Kt(["visitors","document","objects","LinkDescription","fixedFields","targetMediaType"],wo.visitors.value),Kt(["visitors","document","objects","LinkDescription","fixedFields","targetHints"],wo.visitors.value),Kt(["visitors","document","objects","LinkDescription","fixedFields","description"],wo.visitors.value),Kt(["visitors","document","objects","LinkDescription","fixedFields","$comment"],wo.visitors.value),Kt(["visitors","document","objects","LinkDescription","fixedFields","headerSchema"],wo.visitors.JSONSchemaOrJSONReferenceVisitor),Ii(["visitors","document","objects","LinkDescription","fixedFields","submissionEncType"]),Kt(["visitors","document","objects","LinkDescription","fixedFields","submissionMediaType"],wo.visitors.value))(wo),x4={JSONSchemaDraft7Element:["content"],JSONReferenceElement:["content"],LinkDescriptionElement:["content"],...Bl},_4=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Em||t(n)&&e("JSONSchemaDraft7",n)&&r("object",n))),O4=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Sm||t(n)&&e("linkDescription",n)&&r("object",n))),k4={namespace:t=>{const{base:e}=t;return e.register("jSONSchemaDraft7",Em),e.register("jSONReference",ed),e.register("linkDescription",Sm),e}},A4=k4,C4=()=>{const t=Ac(A4);return{predicates:{...se,isStringElement:Ir},namespace:t}},j4=(t,{specPath:e=["visitors","document","objects","JSONSchema","$visitor"],plugins:r=[],specificationObj:n=Bu}={})=>{const a=(0,Ge.e)(t),o=Ru(n),p=new(ro(e,o))({specObj:o});return Pa(a,p),ql(p.element,r,{toolboxCreator:C4,visitorOptions:{keyMap:x4,nodeTypeGetter:hm}})},f2=t=>(e,r={})=>j4(e,{specPath:t,...r});Em.refract=f2(["visitors","document","objects","JSONSchema","$visitor"]),Sm.refract=f2(["visitors","document","objects","LinkDescription","$visitor"]);const xm=class extends Em{constructor(e,r,n){super(e,r,n),this.element="JSONSchema201909"}get $vocabulary(){return this.get("$vocabulary")}set $vocabulary(e){this.set("$vocabulary",e)}get $anchor(){return this.get("$anchor")}set $anchor(e){this.set("$anchor",e)}get $recursiveAnchor(){return this.get("$recursiveAnchor")}set $recursiveAnchor(e){this.set("$recursiveAnchor",e)}get $recursiveRef(){return this.get("$recursiveRef")}set $recursiveRef(e){this.set("$recursiveRef",e)}get $ref(){return this.get("$ref")}set $ref(e){this.set("$ref",e)}get $defs(){return this.get("$defs")}set $defs(e){this.set("$defs",e)}get definitions(){throw new Zr("definitions keyword from Validation vocabulary has been renamed to $defs.")}set definitions(e){throw new Zr("definitions keyword from Validation vocabulary has been renamed to $defs.")}get not(){return this.get("not")}set not(e){this.set("not",e)}get if(){return this.get("if")}set if(e){this.set("if",e)}get then(){return this.get("then")}set then(e){this.set("then",e)}get else(){return this.get("else")}set else(e){this.set("else",e)}get dependentSchemas(){return this.get("dependentSchemas")}set dependentSchemas(e){this.set("dependentSchemas",e)}get dependencies(){throw new Zr("dependencies keyword from Validation vocabulary has been renamed to dependentSchemas.")}set dependencies(e){throw new Zr("dependencies keyword from Validation vocabulary has been renamed to dependentSchemas.")}get items(){return this.get("items")}set items(e){this.set("items",e)}get containsProp(){return this.get("contains")}set containsProp(e){this.set("contains",e)}get additionalProperties(){return this.get("additionalProperties")}set additionalProperties(e){this.set("additionalProperties",e)}get additionalItems(){return this.get("additionalItems")}set additionalItems(e){this.set("additionalItems",e)}get propertyNames(){return this.get("propertyNames")}set propertyNames(e){this.set("propertyNames",e)}get unevaluatedItems(){return this.get("unevaluatedItems")}set unevaluatedItems(e){this.set("unevaluatedItems",e)}get unevaluatedProperties(){return this.get("unevaluatedProperties")}set unevaluatedProperties(e){this.set("unevaluatedProperties",e)}get maxContains(){return this.get("maxContains")}set maxContains(e){this.set("maxContains",e)}get minContains(){return this.get("minContains")}set minContains(e){this.set("minContains",e)}get dependentRequired(){return this.get("dependentRequired")}set dependentRequired(e){this.set("dependentRequired",e)}get deprecated(){return this.get("deprecated")}set deprecated(e){this.set("deprecated",e)}get contentSchema(){return this.get("contentSchema")}set contentSchema(e){this.set("contentSchema",e)}},_m=class extends Sm{get targetSchema(){return this.get("targetSchema")}set targetSchema(e){this.set("targetSchema",e)}get hrefSchema(){return this.get("hrefSchema")}set hrefSchema(e){this.set("hrefSchema",e)}get headerSchema(){return this.get("headerSchema")}set headerSchema(e){this.set("headerSchema",e)}get submissionSchema(){return this.get("submissionSchema")}set submissionSchema(e){this.set("submissionSchema",e)}},Eo=class extends h2{constructor(e){super(e),this.element=new xm}get defaultDialectIdentifier(){return"https://json-schema.org/draft/2019-09/schema"}ObjectElement(e){this.handleDialectIdentifier(e),this.handleSchemaIdentifier(e),this.parent=this.element;const r=Up.prototype.ObjectElement.call(this,e);return Ir(this.element.$ref)&&(this.element.classes.push("reference-element"),this.element.setMetaProperty("referenced-element","schema")),r}},P4=class extends gn{ObjectElement(e){const r=super.enter(e);return this.element.classes.push("json-schema-$vocabulary"),r}},N4=class extends gn{StringElement(e){const r=super.enter(e);return this.element.classes.push("reference-value"),r}};class T4 extends Vt(Fu,Na,gn){constructor(e){super(e),this.element=new Ge.Sh,this.element.classes.push("json-schema-$defs"),this.specPath=jr(["document","objects","JSONSchema"])}}const m2=T4;class I4 extends Vt(Ti,Na,gn){constructor(e){super(e),this.element=new Ge.wE,this.element.classes.push("json-schema-allOf")}ArrayElement(e){return e.forEach((r=>{const n=this.toRefractedElement(["document","objects","JSONSchema"],r);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),on}}const g2=I4;class M4 extends Vt(Ti,Na,gn){constructor(e){super(e),this.element=new Ge.wE,this.element.classes.push("json-schema-anyOf")}ArrayElement(e){return e.forEach((r=>{const n=this.toRefractedElement(["document","objects","JSONSchema"],r);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),on}}const y2=M4;class R4 extends Vt(Ti,Na,gn){constructor(e){super(e),this.element=new Ge.wE,this.element.classes.push("json-schema-oneOf")}ArrayElement(e){return e.forEach((r=>{const n=this.toRefractedElement(["document","objects","JSONSchema"],r);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),on}}const v2=R4;class D4 extends Vt(Fu,Na,gn){constructor(e){super(e),this.element=new Ge.Sh,this.element.classes.push("json-schema-dependentSchemas"),this.specPath=jr(["document","objects","JSONSchema"])}}const b2=D4;class F4 extends Vt(Ti,Na,gn){ObjectElement(e){return this.element=this.toRefractedElement(["document","objects","JSONSchema"],e),on}ArrayElement(e){return this.element=new Ge.wE,this.element.classes.push("json-schema-items"),e.forEach((r=>{const n=this.toRefractedElement(["document","objects","JSONSchema"],r);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),on}BooleanElement(e){return this.element=this.toRefractedElement(["document","objects","JSONSchema"],e),on}}const L4=F4;class $4 extends Vt(Fu,Na,gn){constructor(e){super(e),this.element=new Ge.Sh,this.element.classes.push("json-schema-properties"),this.specPath=jr(["document","objects","JSONSchema"])}}const w2=$4;class B4 extends Vt(Fu,Na,gn){constructor(e){super(e),this.element=new Ge.Sh,this.element.classes.push("json-schema-patternProperties"),this.specPath=jr(["document","objects","JSONSchema"])}}const E2=B4,q4=class extends gn{ObjectElement(e){const r=super.enter(e);return this.element.classes.push("json-schema-dependentRequired"),r}},S2=class extends d2{constructor(e){super(e),this.element=new _m}},lv=mo(Kt(["visitors","document","objects","JSONSchema","$visitor"],Eo),Kt(["visitors","document","objects","JSONSchema","fixedFields","$vocabulary"],P4),Kt(["visitors","document","objects","JSONSchema","fixedFields","$anchor"],Bu.visitors.value),Kt(["visitors","document","objects","JSONSchema","fixedFields","$recursiveAnchor"],Bu.visitors.value),Kt(["visitors","document","objects","JSONSchema","fixedFields","$recursiveRef"],Bu.visitors.value),Ii(["visitors","document","objects","JSONReference","$visitor"]),Kt(["visitors","document","objects","JSONSchema","fixedFields","$ref"],N4),Ii(["visitors","document","objects","JSONSchema","fixedFields","definitions"]),Kt(["visitors","document","objects","JSONSchema","fixedFields","$defs"],m2),Kt(["visitors","document","objects","JSONSchema","fixedFields","allOf"],g2),Kt(["visitors","document","objects","JSONSchema","fixedFields","anyOf"],y2),Kt(["visitors","document","objects","JSONSchema","fixedFields","oneOf"],v2),Kt(["visitors","document","objects","JSONSchema","fixedFields","not"],Eo),Kt(["visitors","document","objects","JSONSchema","fixedFields","if"],Eo),Kt(["visitors","document","objects","JSONSchema","fixedFields","then"],Eo),Kt(["visitors","document","objects","JSONSchema","fixedFields","else"],Eo),Ii(["visitors","document","objects","JSONSchema","fixedFields","dependencies"]),Kt(["visitors","document","objects","JSONSchema","fixedFields","dependentSchemas"],b2),Kt(["visitors","document","objects","JSONSchema","fixedFields","items"],L4),Kt(["visitors","document","objects","JSONSchema","fixedFields","contains"],Eo),Kt(["visitors","document","objects","JSONSchema","fixedFields","properties"],w2),Kt(["visitors","document","objects","JSONSchema","fixedFields","patternProperties"],E2),Kt(["visitors","document","objects","JSONSchema","fixedFields","additionalProperties"],Eo),Kt(["visitors","document","objects","JSONSchema","fixedFields","additionalItems"],Eo),Kt(["visitors","document","objects","JSONSchema","fixedFields","propertyNames"],Eo),Kt(["visitors","document","objects","JSONSchema","fixedFields","unevaluatedItems"],Eo),Kt(["visitors","document","objects","JSONSchema","fixedFields","unevaluatedProperties"],Eo),Kt(["visitors","document","objects","JSONSchema","fixedFields","maxContains"],Bu.visitors.value),Kt(["visitors","document","objects","JSONSchema","fixedFields","minContains"],Bu.visitors.value),Kt(["visitors","document","objects","JSONSchema","fixedFields","dependentRequired"],q4),Kt(["visitors","document","objects","JSONSchema","fixedFields","deprecated"],Bu.visitors.value),Kt(["visitors","document","objects","JSONSchema","fixedFields","contentSchema"],Eo),Kt(["visitors","document","objects","LinkDescription","$visitor"],S2),Kt(["visitors","document","objects","LinkDescription","fixedFields","targetSchema"],Eo),Kt(["visitors","document","objects","LinkDescription","fixedFields","hrefSchema"],Eo),Kt(["visitors","document","objects","LinkDescription","fixedFields","headerSchema"],Eo),Kt(["visitors","document","objects","LinkDescription","fixedFields","submissionSchema"],Eo))(Bu),U4={JSONSchema201909Element:["content"],LinkDescriptionElement:["content"],...Bl},z4=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof xm||t(n)&&e("JSONSchema201909",n)&&r("object",n))),V4=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof _m||t(n)&&e("linkDescription",n)&&r("object",n))),J4={namespace:t=>{const{base:e}=t;return e.register("jSONSchema201909",xm),e.register("linkDescription",_m),e}},W4=J4,H4=()=>{const t=Ac(W4);return{predicates:{...Te,isStringElement:Ir},namespace:t}},K4=(t,{specPath:e=["visitors","document","objects","JSONSchema","$visitor"],plugins:r=[],specificationObj:n=lv}={})=>{const a=(0,Ge.e)(t),o=Ru(n),p=new(ro(e,o))({specObj:o});return Pa(a,p),ql(p.element,r,{toolboxCreator:H4,visitorOptions:{keyMap:U4,nodeTypeGetter:hm}})},x2=t=>(e,r={})=>K4(e,{specPath:t,...r});xm.refract=x2(["visitors","document","objects","JSONSchema","$visitor"]),_m.refract=x2(["visitors","document","objects","LinkDescription","$visitor"]);const Om=class extends xm{constructor(e,r,n){super(e,r,n),this.element="JSONSchema202012"}get $dynamicAnchor(){return this.get("$dynamicAnchor")}set $dynamicAnchor(e){this.set("$dynamicAnchor",e)}get $recursiveAnchor(){throw new Zr("$recursiveAnchor keyword from Core vocabulary has been renamed to $dynamicAnchor.")}set $recursiveAnchor(e){throw new Zr("$recursiveAnchor keyword from Core vocabulary has been renamed to $dynamicAnchor.")}get $dynamicRef(){return this.get("$dynamicRef")}set $dynamicRef(e){this.set("$dynamicRef",e)}get $recursiveRef(){throw new Zr("$recursiveRef keyword from Core vocabulary has been renamed to $dynamicRef.")}set $recursiveRef(e){throw new Zr("$recursiveRef keyword from Core vocabulary has been renamed to $dynamicRef.")}get prefixItems(){return this.get("prefixItems")}set prefixItems(e){this.set("prefixItems",e)}},cv=class extends _m{get targetSchema(){return this.get("targetSchema")}set targetSchema(e){this.set("targetSchema",e)}get hrefSchema(){return this.get("hrefSchema")}set hrefSchema(e){this.set("hrefSchema",e)}get headerSchema(){return this.get("headerSchema")}set headerSchema(e){this.set("headerSchema",e)}get submissionSchema(){return this.get("submissionSchema")}set submissionSchema(e){this.set("submissionSchema",e)}},qa=class extends Eo{constructor(e){super(e),this.element=new Om}get defaultDialectIdentifier(){return"https://json-schema.org/draft/2020-12/schema"}};class G4 extends Vt(Ti,Na,gn){constructor(e){super(e),this.element=new Ge.wE,this.element.classes.push("json-schema-prefixItems")}ArrayElement(e){return e.forEach((r=>{const n=this.toRefractedElement(["document","objects","JSONSchema"],r);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),on}}const _2=G4,Y4=class extends S2{constructor(e){super(e),this.element=new cv}},O2=mo(Kt(["visitors","document","objects","JSONSchema","$visitor"],qa),Ii(["visitors","document","objects","JSONSchema","fixedFields","$recursiveAnchor"]),Kt(["visitors","document","objects","JSONSchema","fixedFields","$dynamicAnchor"],lv.visitors.value),Ii(["visitors","document","objects","JSONSchema","fixedFields","$recursiveRef"]),Kt(["visitors","document","objects","JSONSchema","fixedFields","$dynamicRef"],lv.visitors.value),Kt(["visitors","document","objects","JSONSchema","fixedFields","not"],qa),Kt(["visitors","document","objects","JSONSchema","fixedFields","if"],qa),Kt(["visitors","document","objects","JSONSchema","fixedFields","then"],qa),Kt(["visitors","document","objects","JSONSchema","fixedFields","else"],qa),Kt(["visitors","document","objects","JSONSchema","fixedFields","prefixItems"],_2),Kt(["visitors","document","objects","JSONSchema","fixedFields","items"],qa),Kt(["visitors","document","objects","JSONSchema","fixedFields","contains"],qa),Kt(["visitors","document","objects","JSONSchema","fixedFields","additionalProperties"],qa),Ii(["visitors","document","objects","JSONSchema","fixedFields","additionalItems"]),Kt(["visitors","document","objects","JSONSchema","fixedFields","propertyNames"],qa),Kt(["visitors","document","objects","JSONSchema","fixedFields","unevaluatedItems"],qa),Kt(["visitors","document","objects","JSONSchema","fixedFields","unevaluatedProperties"],qa),Kt(["visitors","document","objects","JSONSchema","fixedFields","contentSchema"],qa),Kt(["visitors","document","objects","LinkDescription","$visitor"],Y4),Kt(["visitors","document","objects","LinkDescription","fixedFields","targetSchema"],qa),Kt(["visitors","document","objects","LinkDescription","fixedFields","hrefSchema"],qa),Kt(["visitors","document","objects","LinkDescription","fixedFields","headerSchema"],qa),Kt(["visitors","document","objects","LinkDescription","fixedFields","submissionSchema"],qa))(lv),X4={JSONSchema202012Element:["content"],LinkDescriptionElement:["content"],...Bl},Q4=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Om||t(n)&&e("JSONSchema202012",n)&&r("object",n))),Z4=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof cv||t(n)&&e("linkDescription",n)&&r("object",n))),eI={namespace:t=>{const{base:e}=t;return e.register("jSONSchema202012",Om),e.register("linkDescription",cv),e}},tI=eI,rI=()=>{const t=Ac(tI);return{predicates:{...he,isStringElement:Ir},namespace:t}},nI=(t,{specPath:e=["visitors","document","objects","JSONSchema","$visitor"],plugins:r=[],specificationObj:n=O2}={})=>{const a=(0,Ge.e)(t),o=Ru(n),p=new(ro(e,o))({specObj:o});return Pa(a,p),ql(p.element,r,{toolboxCreator:rI,visitorOptions:{keyMap:X4,nodeTypeGetter:hm}})},k2=t=>(e,r={})=>nI(e,{specPath:t,...r});Om.refract=k2(["visitors","document","objects","JSONSchema","$visitor"]),cv.refract=k2(["visitors","document","objects","LinkDescription","$visitor"]);const qu=class extends Om{constructor(e,r,n){super(e,r,n),this.element="schema"}get discriminator(){return this.get("discriminator")}set discriminator(e){this.set("discriminator",e)}get xml(){return this.get("xml")}set xml(e){this.set("xml",e)}get externalDocs(){return this.get("externalDocs")}set externalDocs(e){this.set("externalDocs",e)}get example(){return this.get("example")}set example(e){this.set("example",e)}},uv=class extends dm{},pv=class extends fm{},hv=class extends mm{},dv=class extends gm{},r1=class extends Uy{},n1=class extends zy{};class aI extends Vt(Jr,er){constructor(e){super(e),this.element=new Lu,this.specPath=jr(["document","objects","OpenApi"]),this.canSupportSpecificationExtensions=!0,this.openApiSemanticElement=this.element}ObjectElement(e){return this.openApiGenericElement=e,Jr.prototype.ObjectElement.call(this,e)}}const oI=aI,iI=pt.visitors.document.objects.Info.$visitor,sI=class extends iI{constructor(e){super(e),this.element=new Zy}},lI=pt.visitors.document.objects.Contact.$visitor,cI=class extends lI{constructor(e){super(e),this.element=new Gy}},uI=pt.visitors.document.objects.License.$visitor,pI=class extends uI{constructor(e){super(e),this.element=new ev}},hI=pt.visitors.document.objects.Link.$visitor,dI=class extends hI{constructor(e){super(e),this.element=new tv}};class fI extends Vt(zs,er){StringElement(e){const r=new ym(Et(e));return this.copyMetaAndAttributes(e,r),this.element=r,on}}const mI=fI,gI=pt.visitors.document.objects.Server.$visitor,yI=class extends gI{constructor(e){super(e),this.element=new hv}},vI=pt.visitors.document.objects.ServerVariable.$visitor,bI=class extends vI{constructor(e){super(e),this.element=new dv}},wI=pt.visitors.document.objects.MediaType.$visitor,EI=class extends wI{constructor(e){super(e),this.element=new rv}},SI=pt.visitors.document.objects.SecurityRequirement.$visitor,xI=class extends SI{constructor(e){super(e),this.element=new uv}},_I=pt.visitors.document.objects.Components.$visitor,OI=class extends _I{constructor(e){super(e),this.element=new Ky}},kI=pt.visitors.document.objects.Tag.$visitor,AI=class extends kI{constructor(e){super(e),this.element=new r1}},CI=pt.visitors.document.objects.Reference.$visitor,jI=class extends CI{constructor(e){super(e),this.element=new od}},PI=pt.visitors.document.objects.Parameter.$visitor,NI=class extends PI{constructor(e){super(e),this.element=new nv}},TI=pt.visitors.document.objects.Header.$visitor,II=class extends TI{constructor(e){super(e),this.element=new Qy}},MI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Hy||t(n)&&e("callback",n)&&r("object",n))),RI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Ky||t(n)&&e("components",n)&&r("object",n))),DI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Gy||t(n)&&e("contact",n)&&r("object",n))),FI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Yy||t(n)&&e("example",n)&&r("object",n))),LI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Xy||t(n)&&e("externalDocumentation",n)&&r("object",n))),$I=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Qy||t(n)&&e("header",n)&&r("object",n))),BI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof Zy||t(n)&&e("info",n)&&r("object",n))),A2=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof ym||t(n)&&e("jsonSchemaDialect",n)&&r("string",n))),qI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof ev||t(n)&&e("license",n)&&r("object",n))),UI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof tv||t(n)&&e("link",n)&&r("object",n))),zI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof e1||t(n)&&e("openapi",n)&&r("string",n))),C2=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r,hasClass:n})=>a=>a instanceof Lu||t(a)&&e("openApi3_1",a)&&r("object",a)&&n("api",a))),j2=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof vm||t(n)&&e("operation",n)&&r("object",n))),VI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof nv||t(n)&&e("parameter",n)&&r("object",n))),zp=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof $u||t(n)&&e("pathItem",n)&&r("object",n))),JI=t=>{if(!zp(t)||!Ir(t.$ref))return!1;const e=Et(t.$ref);return typeof e=="string"&&e.length>0&&!e.startsWith("#")},WI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof av||t(n)&&e("paths",n)&&r("object",n))),Vp=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof od||t(n)&&e("reference",n)&&r("object",n))),HI=t=>{if(!Vp(t)||!Ir(t.$ref))return!1;const e=Et(t.$ref);return typeof e=="string"&&e.length>0&&!e.startsWith("#")},KI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof ov||t(n)&&e("requestBody",n)&&r("object",n))),GI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof iv||t(n)&&e("response",n)&&r("object",n))),YI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof sv||t(n)&&e("responses",n)&&r("object",n))),Vs=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof qu||t(n)&&e("schema",n)&&r("object",n))),a1=t=>Ff(t)&&t.classes.includes("boolean-json-schema"),XI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof uv||t(n)&&e("securityRequirement",n)&&r("object",n))),QI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof pv||t(n)&&e("securityScheme",n)&&r("object",n))),ZI=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof hv||t(n)&&e("server",n)&&r("object",n))),e5=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof dv||t(n)&&e("serverVariable",n)&&r("object",n))),t5=Qt((({hasBasicElementProps:t,isElementType:e,primitiveEq:r})=>n=>n instanceof rv||t(n)&&e("mediaType",n)&&r("object",n)));class r5 extends Vt(Jr,Na,er){constructor(e){super(e),this.element=new qu,this.specPath=jr(["document","objects","Schema"]),this.canSupportSpecificationExtensions=!0,this.jsonSchemaDefaultDialect=ym.default,this.passingOptionsNames.push("parent")}ObjectElement(e){this.handleDialectIdentifier(e),this.handleSchemaIdentifier(e),this.parent=this.element;const r=Jr.prototype.ObjectElement.call(this,e);return Ir(this.element.$ref)&&(this.element.classes.push("reference-element"),this.element.setMetaProperty("referenced-element","schema")),r}BooleanElement(e){return qa.prototype.BooleanElement.call(this,e)}get defaultDialectIdentifier(){let e;return e=this.openApiSemanticElement!==void 0&&A2(this.openApiSemanticElement.jsonSchemaDialect)?Et(this.openApiSemanticElement.jsonSchemaDialect):this.openApiGenericElement!==void 0&&Ir(this.openApiGenericElement.get("jsonSchemaDialect"))?Et(this.openApiGenericElement.get("jsonSchemaDialect")):Et(this.jsonSchemaDefaultDialect),e}handleDialectIdentifier(e){return qa.prototype.handleDialectIdentifier.call(this,e)}handleSchemaIdentifier(e){return qa.prototype.handleSchemaIdentifier.call(this,e)}}const n5=r5,a5=class extends m2{constructor(e){super(e),this.passingOptionsNames.push("parent")}},o5=class extends g2{constructor(e){super(e),this.passingOptionsNames.push("parent")}},i5=class extends y2{constructor(e){super(e),this.passingOptionsNames.push("parent")}},s5=class extends v2{constructor(e){super(e),this.passingOptionsNames.push("parent")}},l5=class extends b2{constructor(e){super(e),this.passingOptionsNames.push("parent")}},c5=class extends _2{constructor(e){super(e),this.passingOptionsNames.push("parent")}},u5=class extends w2{constructor(e){super(e),this.passingOptionsNames.push("parent")}},p5=class extends E2{constructor(e){super(e),this.passingOptionsNames.push("parent")}},h5=pt.visitors.document.objects.Discriminator.$visitor,d5=class extends h5{constructor(e){super(e),this.element=new Gb,this.canSupportSpecificationExtensions=!0}},f5=pt.visitors.document.objects.XML.$visitor,m5=class extends f5{constructor(e){super(e),this.element=new n1}};class g5 extends Vt(Ur,er){constructor(e){super(e),this.element=new r2,this.specPath=jr(["document","objects","Schema"])}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Vs).forEach(((n,a)=>{n.setMetaProperty("schemaName",Et(a))})),r}}const y5=g5;class o1 extends Ge.Sh{static primaryClass="components-path-items";constructor(e,r,n){super(e,r,n),this.classes.push(o1.primaryClass)}}const v5=o1;class b5 extends Vt(Ur,er){constructor(e){super(e),this.element=new v5,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","PathItem"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Vp).forEach((n=>{n.setMetaProperty("referenced-element","pathItem")})),r}}const w5=b5,E5=pt.visitors.document.objects.Example.$visitor,S5=class extends E5{constructor(e){super(e),this.element=new Yy}},x5=pt.visitors.document.objects.ExternalDocumentation.$visitor,_5=class extends x5{constructor(e){super(e),this.element=new Xy}},O5=pt.visitors.document.objects.Encoding.$visitor,k5=class extends O5{constructor(e){super(e),this.element=new Yb}},A5=pt.visitors.document.objects.Paths.$visitor,C5=class extends A5{constructor(e){super(e),this.element=new av}},j5=pt.visitors.document.objects.RequestBody.$visitor,P5=class extends j5{constructor(e){super(e),this.element=new ov}},P2=pt.visitors.document.objects.Callback.$visitor,N5=class extends P2{constructor(e){super(e),this.element=new Hy,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","PathItem"]}ObjectElement(e){const r=P2.prototype.ObjectElement.call(this,e);return this.element.filter(Vp).forEach((n=>{n.setMetaProperty("referenced-element","pathItem")})),r}},T5=pt.visitors.document.objects.Response.$visitor,I5=class extends T5{constructor(e){super(e),this.element=new iv}},M5=pt.visitors.document.objects.Responses.$visitor,R5=class extends M5{constructor(e){super(e),this.element=new sv}},D5=pt.visitors.document.objects.Operation.$visitor,F5=class extends D5{constructor(e){super(e),this.element=new vm}},L5=pt.visitors.document.objects.PathItem.$visitor,$5=class extends L5{constructor(e){super(e),this.element=new $u}},B5=pt.visitors.document.objects.SecurityScheme.$visitor,q5=class extends B5{constructor(e){super(e),this.element=new pv}},U5=pt.visitors.document.objects.OAuthFlows.$visitor,z5=class extends U5{constructor(e){super(e),this.element=new Zb}},V5=pt.visitors.document.objects.OAuthFlow.$visitor,J5=class extends V5{constructor(e){super(e),this.element=new Qb}};class i1 extends Ge.Sh{static primaryClass="webhooks";constructor(e,r,n){super(e,r,n),this.classes.push(i1.primaryClass)}}const W5=i1;class H5 extends Vt(Ur,er){constructor(e){super(e),this.element=new W5,this.specPath=r=>Vn(r)?["document","objects","Reference"]:["document","objects","PathItem"]}ObjectElement(e){const r=Ur.prototype.ObjectElement.call(this,e);return this.element.filter(Vp).forEach((n=>{n.setMetaProperty("referenced-element","pathItem")})),this.element.filter(zp).forEach(((n,a)=>{n.setMetaProperty("webhook-name",Et(a))})),r}}const K5=H5,{JSONSchema:G5,LinkDescription:Y5}=O2.visitors.document.objects,X5={visitors:{value:pt.visitors.value,document:{objects:{OpenApi:{$visitor:oI,fixedFields:{openapi:pt.visitors.document.objects.OpenApi.fixedFields.openapi,info:{$ref:"#/visitors/document/objects/Info"},jsonSchemaDialect:mI,servers:pt.visitors.document.objects.OpenApi.fixedFields.servers,paths:{$ref:"#/visitors/document/objects/Paths"},webhooks:K5,components:{$ref:"#/visitors/document/objects/Components"},security:pt.visitors.document.objects.OpenApi.fixedFields.security,tags:pt.visitors.document.objects.OpenApi.fixedFields.tags,externalDocs:{$ref:"#/visitors/document/objects/ExternalDocumentation"}}},Info:{$visitor:sI,fixedFields:{title:pt.visitors.document.objects.Info.fixedFields.title,description:pt.visitors.document.objects.Info.fixedFields.description,summary:{$ref:"#/visitors/value"},termsOfService:pt.visitors.document.objects.Info.fixedFields.termsOfService,contact:{$ref:"#/visitors/document/objects/Contact"},license:{$ref:"#/visitors/document/objects/License"},version:pt.visitors.document.objects.Info.fixedFields.version}},Contact:{$visitor:cI,fixedFields:{name:pt.visitors.document.objects.Contact.fixedFields.name,url:pt.visitors.document.objects.Contact.fixedFields.url,email:pt.visitors.document.objects.Contact.fixedFields.email}},License:{$visitor:pI,fixedFields:{name:pt.visitors.document.objects.License.fixedFields.name,identifier:{$ref:"#/visitors/value"},url:pt.visitors.document.objects.License.fixedFields.url}},Server:{$visitor:yI,fixedFields:{url:pt.visitors.document.objects.Server.fixedFields.url,description:pt.visitors.document.objects.Server.fixedFields.description,variables:pt.visitors.document.objects.Server.fixedFields.variables}},ServerVariable:{$visitor:bI,fixedFields:{enum:pt.visitors.document.objects.ServerVariable.fixedFields.enum,default:pt.visitors.document.objects.ServerVariable.fixedFields.default,description:pt.visitors.document.objects.ServerVariable.fixedFields.description}},Components:{$visitor:OI,fixedFields:{schemas:y5,responses:pt.visitors.document.objects.Components.fixedFields.responses,parameters:pt.visitors.document.objects.Components.fixedFields.parameters,examples:pt.visitors.document.objects.Components.fixedFields.examples,requestBodies:pt.visitors.document.objects.Components.fixedFields.requestBodies,headers:pt.visitors.document.objects.Components.fixedFields.headers,securitySchemes:pt.visitors.document.objects.Components.fixedFields.securitySchemes,links:pt.visitors.document.objects.Components.fixedFields.links,callbacks:pt.visitors.document.objects.Components.fixedFields.callbacks,pathItems:w5}},Paths:{$visitor:C5},PathItem:{$visitor:$5,fixedFields:{$ref:pt.visitors.document.objects.PathItem.fixedFields.$ref,summary:pt.visitors.document.objects.PathItem.fixedFields.summary,description:pt.visitors.document.objects.PathItem.fixedFields.description,get:{$ref:"#/visitors/document/objects/Operation"},put:{$ref:"#/visitors/document/objects/Operation"},post:{$ref:"#/visitors/document/objects/Operation"},delete:{$ref:"#/visitors/document/objects/Operation"},options:{$ref:"#/visitors/document/objects/Operation"},head:{$ref:"#/visitors/document/objects/Operation"},patch:{$ref:"#/visitors/document/objects/Operation"},trace:{$ref:"#/visitors/document/objects/Operation"},servers:pt.visitors.document.objects.PathItem.fixedFields.servers,parameters:pt.visitors.document.objects.PathItem.fixedFields.parameters}},Operation:{$visitor:F5,fixedFields:{tags:pt.visitors.document.objects.Operation.fixedFields.tags,summary:pt.visitors.document.objects.Operation.fixedFields.summary,description:pt.visitors.document.objects.Operation.fixedFields.description,externalDocs:{$ref:"#/visitors/document/objects/ExternalDocumentation"},operationId:pt.visitors.document.objects.Operation.fixedFields.operationId,parameters:pt.visitors.document.objects.Operation.fixedFields.parameters,requestBody:pt.visitors.document.objects.Operation.fixedFields.requestBody,responses:{$ref:"#/visitors/document/objects/Responses"},callbacks:pt.visitors.document.objects.Operation.fixedFields.callbacks,deprecated:pt.visitors.document.objects.Operation.fixedFields.deprecated,security:pt.visitors.document.objects.Operation.fixedFields.security,servers:pt.visitors.document.objects.Operation.fixedFields.servers}},ExternalDocumentation:{$visitor:_5,fixedFields:{description:pt.visitors.document.objects.ExternalDocumentation.fixedFields.description,url:pt.visitors.document.objects.ExternalDocumentation.fixedFields.url}},Parameter:{$visitor:NI,fixedFields:{name:pt.visitors.document.objects.Parameter.fixedFields.name,in:pt.visitors.document.objects.Parameter.fixedFields.in,description:pt.visitors.document.objects.Parameter.fixedFields.description,required:pt.visitors.document.objects.Parameter.fixedFields.required,deprecated:pt.visitors.document.objects.Parameter.fixedFields.deprecated,allowEmptyValue:pt.visitors.document.objects.Parameter.fixedFields.allowEmptyValue,style:pt.visitors.document.objects.Parameter.fixedFields.style,explode:pt.visitors.document.objects.Parameter.fixedFields.explode,allowReserved:pt.visitors.document.objects.Parameter.fixedFields.allowReserved,schema:{$ref:"#/visitors/document/objects/Schema"},example:pt.visitors.document.objects.Parameter.fixedFields.example,examples:pt.visitors.document.objects.Parameter.fixedFields.examples,content:pt.visitors.document.objects.Parameter.fixedFields.content}},RequestBody:{$visitor:P5,fixedFields:{description:pt.visitors.document.objects.RequestBody.fixedFields.description,content:pt.visitors.document.objects.RequestBody.fixedFields.content,required:pt.visitors.document.objects.RequestBody.fixedFields.required}},MediaType:{$visitor:EI,fixedFields:{schema:{$ref:"#/visitors/document/objects/Schema"},example:pt.visitors.document.objects.MediaType.fixedFields.example,examples:pt.visitors.document.objects.MediaType.fixedFields.examples,encoding:pt.visitors.document.objects.MediaType.fixedFields.encoding}},Encoding:{$visitor:k5,fixedFields:{contentType:pt.visitors.document.objects.Encoding.fixedFields.contentType,headers:pt.visitors.document.objects.Encoding.fixedFields.headers,style:pt.visitors.document.objects.Encoding.fixedFields.style,explode:pt.visitors.document.objects.Encoding.fixedFields.explode,allowReserved:pt.visitors.document.objects.Encoding.fixedFields.allowReserved}},Responses:{$visitor:R5,fixedFields:{default:pt.visitors.document.objects.Responses.fixedFields.default}},Response:{$visitor:I5,fixedFields:{description:pt.visitors.document.objects.Response.fixedFields.description,headers:pt.visitors.document.objects.Response.fixedFields.headers,content:pt.visitors.document.objects.Response.fixedFields.content,links:pt.visitors.document.objects.Response.fixedFields.links}},Callback:{$visitor:N5},Example:{$visitor:S5,fixedFields:{summary:pt.visitors.document.objects.Example.fixedFields.summary,description:pt.visitors.document.objects.Example.fixedFields.description,value:pt.visitors.document.objects.Example.fixedFields.value,externalValue:pt.visitors.document.objects.Example.fixedFields.externalValue}},Link:{$visitor:dI,fixedFields:{operationRef:pt.visitors.document.objects.Link.fixedFields.operationRef,operationId:pt.visitors.document.objects.Link.fixedFields.operationId,parameters:pt.visitors.document.objects.Link.fixedFields.parameters,requestBody:pt.visitors.document.objects.Link.fixedFields.requestBody,description:pt.visitors.document.objects.Link.fixedFields.description,server:{$ref:"#/visitors/document/objects/Server"}}},Header:{$visitor:II,fixedFields:{description:pt.visitors.document.objects.Header.fixedFields.description,required:pt.visitors.document.objects.Header.fixedFields.required,deprecated:pt.visitors.document.objects.Header.fixedFields.deprecated,allowEmptyValue:pt.visitors.document.objects.Header.fixedFields.allowEmptyValue,style:pt.visitors.document.objects.Header.fixedFields.style,explode:pt.visitors.document.objects.Header.fixedFields.explode,allowReserved:pt.visitors.document.objects.Header.fixedFields.allowReserved,schema:{$ref:"#/visitors/document/objects/Schema"},example:pt.visitors.document.objects.Header.fixedFields.example,examples:pt.visitors.document.objects.Header.fixedFields.examples,content:pt.visitors.document.objects.Header.fixedFields.content}},Tag:{$visitor:AI,fixedFields:{name:pt.visitors.document.objects.Tag.fixedFields.name,description:pt.visitors.document.objects.Tag.fixedFields.description,externalDocs:{$ref:"#/visitors/document/objects/ExternalDocumentation"}}},Reference:{$visitor:jI,fixedFields:{$ref:pt.visitors.document.objects.Reference.fixedFields.$ref,summary:{$ref:"#/visitors/value"},description:{$ref:"#/visitors/value"}}},JSONSchema:{$ref:"#/visitors/document/objects/Schema"},LinkDescription:{...Y5},Schema:{$visitor:n5,fixedFields:{...G5.fixedFields,$defs:a5,allOf:o5,anyOf:i5,oneOf:s5,not:{$ref:"#/visitors/document/objects/Schema"},if:{$ref:"#/visitors/document/objects/Schema"},then:{$ref:"#/visitors/document/objects/Schema"},else:{$ref:"#/visitors/document/objects/Schema"},dependentSchemas:l5,prefixItems:c5,items:{$ref:"#/visitors/document/objects/Schema"},contains:{$ref:"#/visitors/document/objects/Schema"},properties:u5,patternProperties:p5,additionalProperties:{$ref:"#/visitors/document/objects/Schema"},propertyNames:{$ref:"#/visitors/document/objects/Schema"},unevaluatedItems:{$ref:"#/visitors/document/objects/Schema"},unevaluatedProperties:{$ref:"#/visitors/document/objects/Schema"},contentSchema:{$ref:"#/visitors/document/objects/Schema"},discriminator:{$ref:"#/visitors/document/objects/Discriminator"},xml:{$ref:"#/visitors/document/objects/XML"},externalDocs:{$ref:"#/visitors/document/objects/ExternalDocumentation"},example:{$ref:"#/visitors/value"}}},Discriminator:{$visitor:d5,fixedFields:{propertyName:pt.visitors.document.objects.Discriminator.fixedFields.propertyName,mapping:pt.visitors.document.objects.Discriminator.fixedFields.mapping}},XML:{$visitor:m5,fixedFields:{name:pt.visitors.document.objects.XML.fixedFields.name,namespace:pt.visitors.document.objects.XML.fixedFields.namespace,prefix:pt.visitors.document.objects.XML.fixedFields.prefix,attribute:pt.visitors.document.objects.XML.fixedFields.attribute,wrapped:pt.visitors.document.objects.XML.fixedFields.wrapped}},SecurityScheme:{$visitor:q5,fixedFields:{type:pt.visitors.document.objects.SecurityScheme.fixedFields.type,description:pt.visitors.document.objects.SecurityScheme.fixedFields.description,name:pt.visitors.document.objects.SecurityScheme.fixedFields.name,in:pt.visitors.document.objects.SecurityScheme.fixedFields.in,scheme:pt.visitors.document.objects.SecurityScheme.fixedFields.scheme,bearerFormat:pt.visitors.document.objects.SecurityScheme.fixedFields.bearerFormat,flows:{$ref:"#/visitors/document/objects/OAuthFlows"},openIdConnectUrl:pt.visitors.document.objects.SecurityScheme.fixedFields.openIdConnectUrl}},OAuthFlows:{$visitor:z5,fixedFields:{implicit:{$ref:"#/visitors/document/objects/OAuthFlow"},password:{$ref:"#/visitors/document/objects/OAuthFlow"},clientCredentials:{$ref:"#/visitors/document/objects/OAuthFlow"},authorizationCode:{$ref:"#/visitors/document/objects/OAuthFlow"}}},OAuthFlow:{$visitor:J5,fixedFields:{authorizationUrl:pt.visitors.document.objects.OAuthFlow.fixedFields.authorizationUrl,tokenUrl:pt.visitors.document.objects.OAuthFlow.fixedFields.tokenUrl,refreshUrl:pt.visitors.document.objects.OAuthFlow.fixedFields.refreshUrl,scopes:pt.visitors.document.objects.OAuthFlow.fixedFields.scopes}},SecurityRequirement:{$visitor:xI}},extension:{$visitor:pt.visitors.document.extension.$visitor}}}},as=t=>{if(da(t))return`${t.element.charAt(0).toUpperCase()+t.element.slice(1)}Element`},Js={CallbackElement:["content"],ComponentsElement:["content"],ContactElement:["content"],DiscriminatorElement:["content"],Encoding:["content"],Example:["content"],ExternalDocumentationElement:["content"],HeaderElement:["content"],InfoElement:["content"],LicenseElement:["content"],MediaTypeElement:["content"],OAuthFlowElement:["content"],OAuthFlowsElement:["content"],OpenApi3_1Element:["content"],OperationElement:["content"],ParameterElement:["content"],PathItemElement:["content"],PathsElement:["content"],ReferenceElement:["content"],RequestBodyElement:["content"],ResponseElement:["content"],ResponsesElement:["content"],SchemaElement:["content"],SecurityRequirementElement:["content"],SecuritySchemeElement:["content"],ServerElement:["content"],ServerVariableElement:["content"],TagElement:["content"],...Bl},Q5={namespace:t=>{const{base:e}=t;return e.register("callback",Hy),e.register("components",Ky),e.register("contact",Gy),e.register("discriminator",Gb),e.register("encoding",Yb),e.register("example",Yy),e.register("externalDocumentation",Xy),e.register("header",Qy),e.register("info",Zy),e.register("jsonSchemaDialect",ym),e.register("license",ev),e.register("link",tv),e.register("mediaType",rv),e.register("oAuthFlow",Qb),e.register("oAuthFlows",Zb),e.register("openapi",e1),e.register("openApi3_1",Lu),e.register("operation",vm),e.register("parameter",nv),e.register("pathItem",$u),e.register("paths",av),e.register("reference",od),e.register("requestBody",ov),e.register("response",iv),e.register("responses",sv),e.register("schema",qu),e.register("securityRequirement",uv),e.register("securityScheme",pv),e.register("server",hv),e.register("serverVariable",dv),e.register("tag",r1),e.register("xml",n1),e}},s1=Q5,Z5=t=>{const e=t.reduce(((r,n,a)=>{if(Bs(n)){const o=String(Et(n.key));r.push(o)}else if(vo(t[a-2])){const o=String(t[a-2].content.indexOf(n));r.push(o)}return r}),[]);return _S(e)},N2=()=>{const t=Ac(s1);return{predicates:{...ge,isElement:da,isStringElement:Ir,isArrayElement:vo,isObjectElement:hn,isMemberElement:Bs,isServersElement:KS,includesClasses:Yh,hasElementSourceMap:Gh},ancestorLineageToJSONPointer:Z5,namespace:t}},e8=(t,{specPath:e=["visitors","document","objects","OpenApi","$visitor"],plugins:r=[]}={})=>{const n=(0,Ge.e)(t),a=Ru(X5),o=new(ro(e,a))({specObj:a});return Pa(n,o),ql(o.element,r,{toolboxCreator:N2,visitorOptions:{keyMap:Js,nodeTypeGetter:as}})},wn=t=>(e,r={})=>e8(e,{specPath:t,...r});Hy.refract=wn(["visitors","document","objects","Callback","$visitor"]),Ky.refract=wn(["visitors","document","objects","Components","$visitor"]),Gy.refract=wn(["visitors","document","objects","Contact","$visitor"]),Yy.refract=wn(["visitors","document","objects","Example","$visitor"]),Gb.refract=wn(["visitors","document","objects","Discriminator","$visitor"]),Yb.refract=wn(["visitors","document","objects","Encoding","$visitor"]),Xy.refract=wn(["visitors","document","objects","ExternalDocumentation","$visitor"]),Qy.refract=wn(["visitors","document","objects","Header","$visitor"]),Zy.refract=wn(["visitors","document","objects","Info","$visitor"]),ym.refract=wn(["visitors","document","objects","OpenApi","fixedFields","jsonSchemaDialect"]),ev.refract=wn(["visitors","document","objects","License","$visitor"]),tv.refract=wn(["visitors","document","objects","Link","$visitor"]),rv.refract=wn(["visitors","document","objects","MediaType","$visitor"]),Qb.refract=wn(["visitors","document","objects","OAuthFlow","$visitor"]),Zb.refract=wn(["visitors","document","objects","OAuthFlows","$visitor"]),e1.refract=wn(["visitors","document","objects","OpenApi","fixedFields","openapi"]),Lu.refract=wn(["visitors","document","objects","OpenApi","$visitor"]),vm.refract=wn(["visitors","document","objects","Operation","$visitor"]),nv.refract=wn(["visitors","document","objects","Parameter","$visitor"]),$u.refract=wn(["visitors","document","objects","PathItem","$visitor"]),av.refract=wn(["visitors","document","objects","Paths","$visitor"]),od.refract=wn(["visitors","document","objects","Reference","$visitor"]),ov.refract=wn(["visitors","document","objects","RequestBody","$visitor"]),iv.refract=wn(["visitors","document","objects","Response","$visitor"]),sv.refract=wn(["visitors","document","objects","Responses","$visitor"]),qu.refract=wn(["visitors","document","objects","Schema","$visitor"]),uv.refract=wn(["visitors","document","objects","SecurityRequirement","$visitor"]),pv.refract=wn(["visitors","document","objects","SecurityScheme","$visitor"]),hv.refract=wn(["visitors","document","objects","Server","$visitor"]),dv.refract=wn(["visitors","document","objects","ServerVariable","$visitor"]),r1.refract=wn(["visitors","document","objects","Tag","$visitor"]),n1.refract=wn(["visitors","document","objects","XML","$visitor"]);const l1=class extends Zr{},t8=class extends Array{unknownMediaType="application/octet-stream";filterByFormat(){throw new l1("filterByFormat method in MediaTypes class is not yet implemented.")}findBy(){throw new l1("findBy method in MediaTypes class is not yet implemented.")}latest(){throw new l1("latest method in MediaTypes class is not yet implemented.")}};class c1 extends t8{filterByFormat(e="generic"){const r=e==="generic"?"openapi;version":e;return this.filter((n=>n.includes(r)))}findBy(e="3.1.0",r="generic"){const n=r==="generic"?`vnd.oai.openapi;version=${e}`:`vnd.oai.openapi+${r};version=${e}`;return this.find((a=>a.includes(n)))||this.unknownMediaType}latest(e="generic"){return py(this.filterByFormat(e))}}const id=new c1("application/vnd.oai.openapi;version=3.1.0","application/vnd.oai.openapi+json;version=3.1.0","application/vnd.oai.openapi+yaml;version=3.1.0"),Cc=class{uri;depth;value;refSet;errors;constructor({uri:e,depth:r=0,refSet:n,value:a}){this.uri=e,this.value=a,this.depth=r,this.refSet=n,this.errors=[]}},sd=class{rootRef;refs;circular;constructor({refs:e=[],circular:r=!1}={}){this.refs=[],this.circular=r,e.forEach(this.add.bind(this))}get size(){return this.refs.length}add(e){return this.has(e)||(this.refs.push(e),this.rootRef=this.rootRef===void 0?e:this.rootRef,e.refSet=this),this}merge(e){for(const r of e.values())this.add(r);return this}has(e){const r=Lp(e)?e:e.uri;return dE(this.find((n=>n.uri===r)))}find(e){return this.refs.find(e)}*values(){yield*this.refs}clean(){this.refs.forEach((e=>{e.refSet=void 0})),this.rootRef=void 0,this.refs.length=0}};function r8(t){return t}const T2=_r(r8),I2={parse:{mediaType:"text/plain",parsers:[],parserOpts:{}},resolve:{baseURI:"",resolvers:[],resolverOpts:{},strategies:[],strategyOpts:{},internal:!0,external:!0,maxDepth:1/0},dereference:{strategies:[],strategyOpts:{},refSet:null,maxDepth:1/0,circular:"ignore",circularReplacer:T2,immutable:!0,dereferenceOpts:{}},bundle:{strategies:[],refSet:null,maxDepth:1/0}},n8=Tr((function(e,r){return function(n){return function(a){return yy((function(o){return r(o,a)}),n(e(a)))}}}));var M2=function(t){return{value:t,map:function(e){return M2(e(t))}}},a8=ha((function(e,r,n){return e((function(a){return M2(r(a))}))(n).value}));const o8=a8,i8=Pu(""),s8=n8(ro(["resolve","baseURI"]),Kt(["resolve","baseURI"])),l8=t=>i8(t)?P0():t,R2=(t,e)=>{const r=Py(t,e);return o8(s8,l8,r)},ld=class{uri;mediaType;data;parseResult;constructor({uri:e,mediaType:r="text/plain",data:n,parseResult:a}){this.uri=e,this.mediaType=r,this.data=n,this.parseResult=a}get extension(){return Lp(this.uri)?(e=>{const r=e.lastIndexOf(".");return r>=0?e.substring(r).toLowerCase():""})(this.uri):""}toString(){return typeof this.data=="string"?this.data:this.data instanceof ArrayBuffer||["ArrayBuffer"].includes(Fs(this.data))||ArrayBuffer.isView(this.data)?new TextDecoder("utf-8").decode(this.data):String(this.data)}},c8=class extends Mt{plugin;constructor(e,r){super(e,{cause:r.cause}),this.plugin=r.plugin}},u1=async(t,e,r)=>{const n=await Promise.all(r.map(qf([t],e)));return r.filter(((a,o)=>n[o]))},p1=async(t,e,r)=>{let n;for(const a of r)try{const o=await a[t].call(a,...e);return{plugin:a,result:o}}catch(o){n=new c8("Error while running plugin",{cause:o,plugin:a})}return Promise.reject(n)},h1=class extends Mt{},D2=class extends h1{},u8=async(t,e)=>{let r=t,n=!1;if(!iS(t)){const p=no(t);p.classes.push("result"),r=new $s([p]),n=!0}const a=new ld({uri:e.resolve.baseURI,parseResult:r,mediaType:e.parse.mediaType}),o=await u1("canDereference",[a,e],e.dereference.strategies);if(Bf(o))throw new D2(a.uri);try{const{result:p}=await p1("dereference",[a,e],o);return n?p.get(0):p}catch(p){throw new h1(`Error while dereferencing file "${a.uri}"`,{cause:p})}},d1=class extends Mt{},jc=class extends d1{},km=class{name;allowEmpty;sourceMap;fileExtensions;mediaTypes;constructor({name:e,allowEmpty:r=!0,sourceMap:n=!1,fileExtensions:a=[],mediaTypes:o=[]}){this.name=e,this.allowEmpty=r,this.sourceMap=n,this.fileExtensions=a,this.mediaTypes=o}},p8=class extends km{constructor(e){super({...e??{},name:"binary"})}canParse(e){return this.fileExtensions.length===0||this.fileExtensions.includes(e.extension)}parse(e){try{const r=unescape(encodeURIComponent(e.toString())),n=btoa(r),a=new $s;if(n.length!==0){const o=new Ge.Om(n);o.classes.push("result"),a.push(o)}return a}catch(r){throw new jc(`Error parsing "${e.uri}"`,{cause:r})}}},h8=class{name;constructor({name:e}){this.name=e}},d8=class extends h8{constructor(e){super({...e??{},name:"openapi-3-1"})}canResolve(e,r){const n=r.dereference.strategies.find((a=>a.name==="openapi-3-1"));return n!==void 0&&n.canDereference(e,r)}async resolve(e,r){const n=r.dereference.strategies.find((p=>p.name==="openapi-3-1"));if(n===void 0)throw new D2('"openapi-3-1" dereference strategy is not available.');const a=new sd,o=R2(r,{resolve:{internal:!1},dereference:{refSet:a}});return await n.dereference(e,o),a}},f8=class{name;constructor({name:e}){this.name=e}},m8=class extends f8{timeout;redirects;withCredentials;constructor(e){const{name:r="http-resolver",timeout:n=5e3,redirects:a=5,withCredentials:o=!1}=e??{};super({name:r}),this.timeout=n,this.redirects=a,this.withCredentials=o}canRead(e){return j0(e.uri)}},fv=class extends Mt{},F2=class extends fv{},{AbortController:g8,AbortSignal:y8}=globalThis;globalThis.AbortController===void 0&&(globalThis.AbortController=g8),globalThis.AbortSignal===void 0&&(globalThis.AbortSignal=y8);const v8=class extends m8{swaggerHTTPClient=Mf;swaggerHTTPClientConfig;constructor({swaggerHTTPClient:e=Mf,swaggerHTTPClientConfig:r={},...n}={}){super({...n,name:"http-swagger-client"}),this.swaggerHTTPClient=e,this.swaggerHTTPClientConfig=r}getHttpClient(){return this.swaggerHTTPClient}async read(e){const r=this.getHttpClient(),n=new AbortController,{signal:a}=n,o=setTimeout((()=>{n.abort()}),this.timeout),p=this.getHttpClient().withCredentials||this.withCredentials?"include":"same-origin",c=this.redirects===0?"error":"follow",d=this.redirects>0?this.redirects:void 0;try{return(await r({url:e.uri,signal:a,userFetch:async(E,x)=>{let k=await fetch(E,x);try{k.headers.delete("Content-Type")}catch{k=new Response(k.body,{...k,headers:new Headers(k.headers)}),k.headers.delete("Content-Type")}return k},credentials:p,redirect:c,follow:d,...this.swaggerHTTPClientConfig})).text.arrayBuffer()}catch(E){throw new F2(`Error downloading "${e.uri}"`,{cause:E})}finally{clearTimeout(o)}}},L2=(t,e=wS)=>{if(Lp(t))try{return e.fromRefract(JSON.parse(t))}catch{}return Ls(t)&&$f("element",t)?e.fromRefract(t):e.toElement(t)},b8=class extends km{constructor(e={}){super({name:"json-swagger-client",mediaTypes:["application/json"],...e})}async canParse(e){var r,n;const a=this.fileExtensions.length===0||Yn()(r=this.fileExtensions).call(r,e.extension),o=Yn()(n=this.mediaTypes).call(n,e.mediaType);if(!a)return!1;if(o)return!0;if(!o)try{return JSON.parse(e.toString()),!0}catch{return!1}return!1}async parse(e){if(this.sourceMap)throw new jc("json-swagger-client parser plugin doesn't support sourceMaps option");const r=new $s,n=e.toString();if(this.allowEmpty&&n.trim()==="")return r;try{const a=L2(JSON.parse(n));return a.classes.push("result"),r.push(a),r}catch(a){throw new jc(`Error parsing "${e.uri}"`,{cause:a})}}},w8=class extends km{constructor(e={}){super({name:"yaml-1-2-swagger-client",mediaTypes:["text/yaml","application/yaml"],...e})}async canParse(e){var r,n;const a=this.fileExtensions.length===0||Yn()(r=this.fileExtensions).call(r,e.extension),o=Yn()(n=this.mediaTypes).call(n,e.mediaType);if(!a)return!1;if(o)return!0;if(!o)try{return Pe.load(e.toString(),{schema:Rt}),!0}catch{return!1}return!1}async parse(e){if(this.sourceMap)throw new jc("yaml-1-2-swagger-client parser plugin doesn't support sourceMaps option");const r=new $s,n=e.toString();try{const a=Pe.load(n,{schema:Rt});if(this.allowEmpty&&a===void 0)return r;const o=L2(a);return o.classes.push("result"),r.push(o),r}catch(a){throw new jc(`Error parsing "${e.uri}"`,{cause:a})}}},E8=class extends km{detectionRegExp=/"openapi"\s*:\s*"(?3\.1\.(?:[1-9]\d*|0))"/;constructor(e={}){super({name:"openapi-json-3-1-swagger-client",mediaTypes:new c1(...id.filterByFormat("generic"),...id.filterByFormat("json")),...e})}async canParse(e){var r,n;const a=this.fileExtensions.length===0||Yn()(r=this.fileExtensions).call(r,e.extension),o=Yn()(n=this.mediaTypes).call(n,e.mediaType);if(!a)return!1;if(o)return!0;if(!o)try{const p=e.toString();return JSON.parse(p),this.detectionRegExp.test(p)}catch{return!1}return!1}async parse(e){if(this.sourceMap)throw new jc("openapi-json-3-1-swagger-client parser plugin doesn't support sourceMaps option");const r=new $s,n=e.toString();if(this.allowEmpty&&n.trim()==="")return r;try{const a=JSON.parse(n),o=Lu.refract(a,this.refractorOpts);return o.classes.push("result"),r.push(o),r}catch(a){throw new jc(`Error parsing "${e.uri}"`,{cause:a})}}},S8=class extends km{detectionRegExp=/(?^(["']?)openapi\2\s*:\s*(["']?)(?3\.1\.(?:[1-9]\d*|0))\3(?:\s+|$))|(?"openapi"\s*:\s*"(?3\.1\.(?:[1-9]\d*|0))")/m;constructor(e={}){super({name:"openapi-yaml-3-1-swagger-client",mediaTypes:new c1(...id.filterByFormat("generic"),...id.filterByFormat("yaml")),...e})}async canParse(e){var r,n;const a=this.fileExtensions.length===0||Yn()(r=this.fileExtensions).call(r,e.extension),o=Yn()(n=this.mediaTypes).call(n,e.mediaType);if(!a)return!1;if(o)return!0;if(!o)try{const p=e.toString();return Pe.load(p),this.detectionRegExp.test(p)}catch{return!1}return!1}async parse(e){if(this.sourceMap)throw new jc("openapi-yaml-3-1-swagger-client parser plugin doesn't support sourceMaps option");const r=new $s,n=e.toString();try{const a=Pe.load(n,{schema:Rt});if(this.allowEmpty&&a===void 0)return r;const o=Lu.refract(a,this.refractorOpts);return o.classes.push("result"),r.push(o),r}catch(a){throw new jc(`Error parsing "${e.uri}"`,{cause:a})}}},$2=ha((function(e,r,n){return Pu(e,Af(r,n))})),x8=class{name;constructor({name:e}){this.name=e}},_8=Tr((function(e,r){return Z0(cE(e),r)}));var O8=Be(8068);const k8=class extends xr{value;constructor(e,r){super(e,r),r!==void 0&&(this.value=r.value)}};class f1{uuid;identityMap;constructor({length:e=6}={}){this.uuid=new O8({length:e}),this.identityMap=new WeakMap}identify(e){if(!da(e))throw new k8("Cannot not identify the element. `element` is neither structurally compatible nor a subclass of an Element class.",{value:e});if(e.meta.hasKey("id")&&Ir(e.meta.get("id"))&&!e.meta.get("id").equals(""))return e.id;if(this.identityMap.has(e))return this.identityMap.get(e);const r=new Ge.Om(this.generateId());return this.identityMap.set(e,r),r}forget(e){return!!this.identityMap.has(e)&&(this.identityMap.delete(e),!0)}generateId(){return this.uuid.randomUUID()}}new f1;const B2=ha((function(e,r,n){return Jh(e,J0(r,n))})),q2=(t,e)=>{const r=new fS({predicate:t,returnOnTrue:on});return Pa(e,r),B2(void 0,[0],r.result)},U2=class extends Mt{},A8=class extends U2{},C8=class extends U2{constructor(e){super(`Invalid JSON Schema $anchor "${e}".`)}},mv=t=>/^[A-Za-z_][A-Za-z_0-9.-]*$/.test(t),cd=t=>{const e=EE(t);return cS("#",e)},m1=(t,e)=>{const r=(a=>{if(!mv(a))throw new C8(a);return a})(t),n=q2((a=>Vs(a)&&Et(a.$anchor)===r),e);if(Iu(n))throw new A8(`Evaluation failed on token: "${r}"`);return n},j8=(t,e)=>{const r=new fS({predicate:t});return Pa(e,r),new Ge.G6(r.result)},P8=class extends Mt{},g1=class extends P8{},z2=(t,e)=>{if(e.$ref===void 0)return;const r=EE(Et(e.$ref)),n=Et(e.meta.get("ancestorsSchemaIdentifiers"));return`${Vh(((o,p)=>go(o,by(In(p)))),t,[...n,Et(e.$ref)])}${r==="#"?"":r}`},Am=t=>{if(Am.cache.has(t))return Am.cache.get(t);const e=qu.refract(t);return Am.cache.set(t,e),e};Am.cache=new WeakMap;const qo=t=>$p(t)?Am(t):t,gv=(t,e)=>{const{cache:r}=gv,n=In(t),a=p=>Vs(p)&&p.$id!==void 0;if(!r.has(e)){const p=j8(a,e);r.set(e,Array.from(p))}const o=r.get(e).find((p=>((d,E)=>{if(E.$id===void 0)return;const x=Et(E.meta.get("ancestorsSchemaIdentifiers"));return Vh(((k,P)=>go(k,by(In(P)))),d,x)})(n,p)===n));if(Iu(o))throw new g1(`Evaluation failed on URI: "${t}"`);return mv(cd(t))?m1(cd(t),o):Us(o,rs(t))};gv.cache=new WeakMap;const ud=class extends h1{},N8=class extends fv{},V2=class extends F2{},J2="apidom-file-cache",W2=async({cacheKey:t,result:e,error:r,cacheTTL:n})=>{if(n!==0)try{const a={cachedResult:e,cachedError:r,timestamp:Date.now()},o=await caches.open(J2),p=new Response(JSON.stringify(a),{headers:{"Content-Type":"application/json"}});await o.put(t,p)}catch{console.error("CacheStorage is not available")}},T8=async(t,e)=>{const{cacheTTL:r=0}=e.resolve.resolverOpts,n=`read_${t.uri}`,{cachedResult:a,cachedError:o}=await(async({cacheKey:k,cacheTTL:P})=>{if(P===0)return{cachedResult:null,cachedError:null};try{const R=await caches.open(J2),L=await R.match(k);if(L){const{cachedResult:V,cachedError:Q,timestamp:J}=await L.json();if(Date.now()-J{const P=Object.create(k);return Object.assign(P,e.resolve.resolverOpts)})),c=await u1("canRead",[t,e],p);if(Bf(c))throw new V2(t.uri);try{const{result:k}=await p1("read",[t],c),P=new ld({...t,data:k}).toString();return await W2({cacheKey:n,result:P,error:null,cacheTTL:r}),k}catch(k){var d,E,x;throw await W2({cacheKey:n,result:null,error:(d=(E=k==null||(x=k.cause)===null||x===void 0?void 0:x.cause)!==null&&E!==void 0?E:k?.cause)!==null&&d!==void 0?d:k,cacheTTL:r}),new fv(`Error while reading file "${t.uri}"`,{cause:k})}},I8=async(t,e)=>{const r=new ld({uri:by(In(t)),mediaType:e.parse.mediaType}),n=await T8(r,e);return(async(a,o)=>{const p=o.parse.parsers.map((d=>{const E=Object.create(d);return Object.assign(E,o.parse.parserOpts)})),c=await u1("canParse",[a,o],p);if(Bf(c))throw new V2(a.uri);try{const{plugin:d,result:E}=await p1("parse",[a,o],c);return!d.allowEmpty&&E.isEmpty?Promise.reject(new d1(`Error while parsing file "${a.uri}". File is empty.`)):E}catch(d){throw new d1(`Error while parsing file "${a.uri}"`,{cause:d})}})(new ld({...r,data:n}),e)};class y1 extends Array{includesCycle(e){return this.filter((r=>r.has(e))).length>1}includes(e,r){return e instanceof Set?super.includes(e,r):this.some((n=>n.has(e)))}findItem(e){for(const r of this)for(const n of r)if(da(n)&&e(n))return n}}const yv=Pa[Symbol.for("nodejs.util.promisify.custom")],So=new f1,os=(t,e,r,n)=>{Bs(n)?n.value=t:Array.isArray(n)&&(n[r]=t)};class pd{indirections;namespace;reference;options;ancestors;refractCache;allOfDiscriminatorMapping;errorContext;errorPropagationCache=new Map;pendingPropagations=[];constructor({reference:e,namespace:r,options:n,indirections:a=[],ancestors:o=new y1,refractCache:p=new Map,allOfDiscriminatorMapping:c=new Map,errorContext:d=[],errorPropagationCache:E=new Map,pendingPropagations:x=[]}){this.indirections=a,this.namespace=r,this.reference=e,this.options=n,this.ancestors=new y1(...o),this.refractCache=p,this.allOfDiscriminatorMapping=c,this.errorContext=d,this.errorPropagationCache=E,this.pendingPropagations=x}popErrorContext(e){if(this.errorContext[this.errorContext.length-1]===e)this.errorContext.pop();else for(let r=this.errorContext.length-1;r>=0;r-=1)if(this.errorContext[r]===e){this.errorContext.splice(r,1);break}}runPropagationPass(){let e=!1;return this.pendingPropagations.forEach((({referencingElement:r,referencedElement:n})=>{const a=this.errorPropagationCache.get(n);Array.isArray(a)&&a.length>0&&a.forEach((o=>{const p=this.errorPropagationCache.get(r);var c;p!=null&&p.includes(o)||((c=this.options.dereference.dereferenceOpts)===null||c===void 0||c.errors.push({error:o,refEl:r}),p?p.push(o):this.errorPropagationCache.set(r,[o]),e=!0)}))})),e}propagateErrors(){for(;this.runPropagationPass(););this.pendingPropagations.length=0}handleDereferenceError(e,r,n){var a;if((a=this.options.dereference.dereferenceOpts)===null||a===void 0||!a.continueOnError)throw e;{var o,p,c;const{directAncestors:x,isExternalReference:k=!1}=n??{},P=(o=(p=this.options.dereference.dereferenceOpts)===null||p===void 0?void 0:p.skipNestedExternal)!==null&&o!==void 0&&o,R=k&&P;if(x&&!R){for(const V of x){const Q=this.errorPropagationCache.get(V);Q?Q.push(e):this.errorPropagationCache.set(V,[e])}const L=[...x].find((V=>hn(V)&&V.getMetaProperty("ref-referencing-element")));var d;L&&((d=this.options.dereference.dereferenceOpts)===null||d===void 0||d.errors.push({error:e,refEl:L.getMetaProperty("ref-referencing-element")}))}if((c=this.options.dereference.dereferenceOpts)===null||c===void 0||c.errors.push({error:e,refEl:r}),!R){const L=new Set(this.errorContext);L.delete(r),this.popErrorContext(r);for(const V of L){var E;(E=this.options.dereference.dereferenceOpts)===null||E===void 0||E.errors.push({error:e,refEl:V})}}}}getNestedVisitorOptions(e){var r,n;return{...this.options,resolve:{...this.options.resolve,external:((r=this.options.dereference)===null||r===void 0||(r=r.dereferenceOpts)===null||r===void 0||!r.skipNestedExternal||!Et(e.get("$ref")).startsWith("#"))&&this.options.resolve.external},dereference:{...this.options.dereference,dereferenceOpts:{...(n=this.options.dereference)===null||n===void 0?void 0:n.dereferenceOpts,continueOnError:!1}}}}toBaseURI(e){return go(this.reference.uri,by(In(e)))}async toReference(e){if(this.reference.depth>=this.options.resolve.maxDepth)throw new N8(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`);const r=this.toBaseURI(e),{refSet:n}=this.reference;if(n.has(r))return n.find($2(r,"uri"));const a=await I8(Fl(r),{...this.options,parse:{...this.options.parse,mediaType:"text/plain"}}),o=new Cc({uri:r,value:hr(a),depth:this.reference.depth+1});if(n.add(o),this.options.dereference.immutable){const p=new Cc({uri:`immutable://${r}`,value:a,depth:this.reference.depth+1});n.add(p)}return o}toAncestorLineage(e){const r=new Set(e.filter(da));return[new y1(...this.ancestors,r),r]}OpenApi3_1Element={leave:(e,r,n,a,o,p)=>{var c,d;if((c=this.options.dereference.dereferenceOpts)!==null&&c!==void 0&&c.continueOnError&&this.propagateErrors(),(d=this.options.dereference.strategyOpts["openapi-3-1"])===null||d===void 0||!d.dereferenceDiscriminatorMapping)return;const E=no(e);return E.setMetaProperty("allOfDiscriminatorMapping",Object.fromEntries(this.allOfDiscriminatorMapping)),p.replaceWith(E,os),n?void 0:E}};async ReferenceElement(e,r,n,a,o,p){var c,d;if(this.indirections.includes(e))return!1;const[E,x]=this.toAncestorLineage([...o,n]),k=this.toBaseURI(Et(e.$ref)),P=In(this.reference.uri)===k,R=!P;if(!this.options.resolve.internal&&P||!this.options.resolve.external&&R)return!1;let L;try{L=await this.toReference(Et(e.$ref))}catch(ce){return this.handleDereferenceError(ce,e,{directAncestors:x,isExternalReference:R})}const V=go(k,Et(e.$ref));this.indirections.push(e);const Q=rs(V);let J;try{J=Us(L.value.result,Q)}catch(ce){return this.indirections.pop(),this.handleDereferenceError(ce,e,{directAncestors:x,isExternalReference:R})}if(J.id=So.identify(J),$p(J)){const ce=Et(e.meta.get("referenced-element")),_e=`${ce}-${Et(So.identify(J))}`;this.refractCache.has(_e)?J=this.refractCache.get(_e):Vn(J)?(J=od.refract(J),J.setMetaProperty("referenced-element",ce),this.refractCache.set(_e,J)):(J=this.namespace.getElementClass(ce).refract(J),this.refractCache.set(_e,J))}if(e===J){const ce=new Mt("Recursive Reference Object detected");return this.indirections.pop(),this.handleDereferenceError(ce,e,{directAncestors:x,isExternalReference:R})}if(this.indirections.length>this.options.dereference.maxDepth){const ce=new ud(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);return this.indirections.pop(),this.handleDereferenceError(ce,e,{directAncestors:x,isExternalReference:R})}if(E.includes(J)){if(L.refSet.circular=!0,this.options.dereference.circular==="error"){const ce=new Mt("Circular reference detected");return this.indirections.pop(),this.handleDereferenceError(ce,e,{directAncestors:x,isExternalReference:R})}if(this.options.dereference.circular==="replace"){var re,me;const ce=new Ge.sI(J.id,{type:"reference",uri:L.uri,$ref:Et(e.$ref)}),_e=((re=(me=this.options.dereference.strategyOpts["openapi-3-1"])===null||me===void 0?void 0:me.circularReplacer)!==null&&re!==void 0?re:this.options.dereference.circularReplacer)(ce);return p.replaceWith(_e,os),!n&&_e}}const le=In(L.refSet.rootRef.uri)!==L.uri,ve=["error","replace"].includes(this.options.dereference.circular);if((R||le||Vp(J)||ve)&&!E.includesCycle(J)){x.add(e),this.errorContext.push(e);const ce=new pd({reference:L,namespace:this.namespace,indirections:[...this.indirections],options:this.getNestedVisitorOptions(e),refractCache:this.refractCache,ancestors:E,allOfDiscriminatorMapping:this.allOfDiscriminatorMapping,errorContext:this.errorContext,errorPropagationCache:this.errorPropagationCache,pendingPropagations:this.pendingPropagations});try{J=await yv(J,ce,{keyMap:Js,nodeTypeGetter:as})}catch(_e){return this.indirections.pop(),this.handleDereferenceError(_e,e,{directAncestors:x,isExternalReference:R})}x.delete(e),this.popErrorContext(e)}(c=this.options.dereference.dereferenceOpts)!==null&&c!==void 0&&c.continueOnError&&this.pendingPropagations.push({referencingElement:e,referencedElement:J}),this.indirections.pop();const be=no(J);return be.setMetaProperty("id",So.generateId()),be.setMetaProperty("ref-fields",{$ref:Et(e.$ref),description:Et(e.description),summary:Et(e.summary)}),be.setMetaProperty("ref-origin",L.uri),be.setMetaProperty("ref-referencing-element-id",hr(So.identify(e))),(d=this.options.dereference.dereferenceOpts)!==null&&d!==void 0&&d.continueOnError&&be.setMetaProperty("ref-referencing-element",e),hn(J)&&hn(be)&&(e.hasKey("description")&&"description"in J&&(be.remove("description"),be.set("description",e.get("description"))),e.hasKey("summary")&&"summary"in J&&(be.remove("summary"),be.set("summary",e.get("summary")))),p.replaceWith(be,os),!n&&be}async PathItemElement(e,r,n,a,o,p){var c;if(!Ir(e.$ref))return;if(this.indirections.includes(e))return!1;const[d,E]=this.toAncestorLineage([...o,n]),x=this.toBaseURI(Et(e.$ref)),k=In(this.reference.uri)===x,P=!k;if(!this.options.resolve.internal&&k||!this.options.resolve.external&&P)return;let R;try{R=await this.toReference(Et(e.$ref))}catch(be){return this.handleDereferenceError(be,e,{directAncestors:E,isExternalReference:P})}const L=go(x,Et(e.$ref));this.indirections.push(e);const V=rs(L);let Q;try{Q=Us(R.value.result,V)}catch(be){return this.indirections.pop(),this.handleDereferenceError(be,e,{directAncestors:E,isExternalReference:P})}if(Q.id=So.identify(Q),$p(Q)){const be=`path-item-${Et(So.identify(Q))}`;this.refractCache.has(be)?Q=this.refractCache.get(be):(Q=$u.refract(Q),this.refractCache.set(be,Q))}if(e===Q){const be=new Mt("Recursive Path Item Object reference detected");return this.indirections.pop(),this.handleDereferenceError(be,e,{directAncestors:E,isExternalReference:P})}if(this.indirections.length>this.options.dereference.maxDepth){const be=new ud(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);return this.indirections.pop(),this.handleDereferenceError(be,e,{directAncestors:E,isExternalReference:P})}if(d.includes(Q)){if(R.refSet.circular=!0,this.options.dereference.circular==="error"){const be=new Mt("Circular reference detected");return this.indirections.pop(),this.handleDereferenceError(be,e,{directAncestors:E,isExternalReference:P})}if(this.options.dereference.circular==="replace"){var J,re;const be=new Ge.sI(Q.id,{type:"path-item",uri:R.uri,$ref:Et(e.$ref)}),ce=((J=(re=this.options.dereference.strategyOpts["openapi-3-1"])===null||re===void 0?void 0:re.circularReplacer)!==null&&J!==void 0?J:this.options.dereference.circularReplacer)(be);return p.replaceWith(ce,os),!n&&ce}}const me=In(R.refSet.rootRef.uri)!==R.uri,le=["error","replace"].includes(this.options.dereference.circular);if((P||me||zp(Q)&&Ir(Q.$ref)||le)&&!d.includesCycle(Q)){E.add(e),this.errorContext.push(e);const be=new pd({reference:R,namespace:this.namespace,indirections:[...this.indirections],options:this.getNestedVisitorOptions(e),refractCache:this.refractCache,ancestors:d,allOfDiscriminatorMapping:this.allOfDiscriminatorMapping,errorContext:this.errorContext,errorPropagationCache:this.errorPropagationCache,pendingPropagations:this.pendingPropagations});try{Q=await yv(Q,be,{keyMap:Js,nodeTypeGetter:as})}catch(ce){return this.indirections.pop(),this.handleDereferenceError(ce,e,{directAncestors:E,isExternalReference:P})}E.delete(e),this.popErrorContext(e)}if((c=this.options.dereference.dereferenceOpts)!==null&&c!==void 0&&c.continueOnError&&this.pendingPropagations.push({referencingElement:e,referencedElement:Q}),this.indirections.pop(),zp(Q)){var ve;const be=new $u([...Q.content],hr(Q.meta),hr(Q.attributes));be.setMetaProperty("id",So.generateId()),e.forEach(((ce,_e,Ae)=>{be.remove(Et(_e)),be.content.push(Ae)})),be.remove("$ref"),be.setMetaProperty("ref-fields",{$ref:Et(e.$ref)}),be.setMetaProperty("ref-origin",R.uri),be.setMetaProperty("ref-referencing-element-id",hr(So.identify(e))),(ve=this.options.dereference.dereferenceOpts)!==null&&ve!==void 0&&ve.continueOnError&&be.setMetaProperty("ref-referencing-element",e),Q=be}return p.replaceWith(Q,os),n?void 0:Q}async LinkElement(e,r,n,a,o,p){if(!Ir(e.operationRef)&&!Ir(e.operationId))return;if(Ir(e.operationRef)&&Ir(e.operationId)){const x=new Mt("LinkElement operationRef and operationId fields are mutually exclusive.");return this.handleDereferenceError(x,e)}let c;if(Ir(e.operationRef)){var d;const x=rs(Et(e.operationRef)),k=this.toBaseURI(Et(e.operationRef)),P=In(this.reference.uri)===k,R=!P;if(!this.options.resolve.internal&&P||!this.options.resolve.external&&R)return;let L;try{L=await this.toReference(Et(e.operationRef)),c=Us(L.value.result,x)}catch(Q){return this.handleDereferenceError(Q,e,{isExternalReference:R})}if($p(c)){const Q=`operation-${Et(So.identify(c))}`;this.refractCache.has(Q)?c=this.refractCache.get(Q):(c=vm.refract(c),this.refractCache.set(Q,c))}c=no(c),c.setMetaProperty("ref-origin",L.uri);const V=no(e);return(d=V.operationRef)===null||d===void 0||d.meta.set("operation",c),p.replaceWith(V,os),n?void 0:V}if(Ir(e.operationId)){var E;const x=Et(e.operationId);let k;try{k=await this.toReference(Fl(this.reference.uri))}catch(R){return this.handleDereferenceError(R,e)}if(c=q2((R=>j2(R)&&da(R.operationId)&&R.operationId.equals(x)),k.value.result),Iu(c)){const R=new Mt(`OperationElement(operationId=${x}) not found.`);return this.handleDereferenceError(R,e)}const P=no(e);return(E=P.operationId)===null||E===void 0||E.meta.set("operation",c),p.replaceWith(P,os),n?void 0:P}}async ExampleElement(e,r,n,a,o,p){if(!Ir(e.externalValue))return;if(e.hasKey("value")&&Ir(e.externalValue)){const R=new Mt("ExampleElement value and externalValue fields are mutually exclusive.");return this.handleDereferenceError(R,e)}const c=this.toBaseURI(Et(e.externalValue)),d=In(this.reference.uri)===c,E=!d;if(!this.options.resolve.internal&&d||!this.options.resolve.external&&E)return;let x;try{x=await this.toReference(Et(e.externalValue))}catch(R){return this.handleDereferenceError(R,e,{isExternalReference:E})}const k=no(x.value.result);k.setMetaProperty("ref-origin",x.uri);const P=no(e);return P.value=k,p.replaceWith(P,os),n?void 0:P}async MemberElement(e,r,n,a,o,p){var c;const d=o[o.length-1];if(!hn(d)||!d.classes.contains("discriminator-mapping"))return;if((c=this.options.dereference.strategyOpts["openapi-3-1"])===null||c===void 0||!c.dereferenceDiscriminatorMapping||!Ir(e.key)||!Ir(e.value)||this.indirections.includes(e))return!1;this.indirections.push(e);const[E,x]=this.toAncestorLineage([...o,n]),k=[...x].findLast(Vs),P=hr(k.getMetaProperty("ancestorsSchemaIdentifiers")),R=Et(e.value),L=/^[a-zA-Z0-9\\.\\-_]+$/.test(R)?`#/components/schemas/${R}`:R,V=new qu({$ref:L});V.setMetaProperty("ancestorsSchemaIdentifiers",P),x.add(V);const Q=new pd({reference:this.reference,namespace:this.namespace,indirections:[...this.indirections],options:this.getNestedVisitorOptions(V),refractCache:this.refractCache,ancestors:E,allOfDiscriminatorMapping:this.allOfDiscriminatorMapping});let J;try{J=await yv(V,Q,{keyMap:Js,nodeTypeGetter:as})}catch(me){const le=!L.startsWith("#");return this.handleDereferenceError(me,V,{directAncestors:x,isExternalReference:le})}x.delete(V),this.indirections.pop();const re=no(e);return re.value.setMetaProperty("ref-schema",J),p.replaceWith(re,os),n?void 0:re}async SchemaElement(e,r,n,a,o,p){var c;if(!Ir(e.$ref))return;if(this.indirections.includes(e))return!1;const[d,E]=this.toAncestorLineage([...o,n]);let x;try{x=await this.toReference(Fl(this.reference.uri))}catch(je){return this.handleDereferenceError(je,e,{directAncestors:E})}let{uri:k}=x;const P=z2(k,e),R=In(P),L=new ld({uri:R}),V=_8((je=>je.canRead(L)),this.options.resolve.resolvers),Q=!V;let J,re=In(this.reference.uri)===P,me=!re;this.indirections.push(e);try{if(V||Q){k=this.toBaseURI(P);const je=P,G=qo(x.value.result);if(J=gv(je,G),J=qo(J),J.id=So.identify(J),!this.options.resolve.internal&&re||!this.options.resolve.external&&me)return}else{if(k=this.toBaseURI(P),re=In(this.reference.uri)===k,me=!re,!this.options.resolve.internal&&re||!this.options.resolve.external&&me)return;x=await this.toReference(Fl(P));const je=rs(P),G=qo(x.value.result);J=Us(G,je),J=qo(J),J.id=So.identify(J)}}catch(je){if(!(Q&&je instanceof g1))return this.indirections.pop(),this.handleDereferenceError(je,e,{directAncestors:E,isExternalReference:me});if(mv(cd(P))){if(re=In(this.reference.uri)===k,me=!re,!this.options.resolve.internal&&re||!this.options.resolve.external&&me)return;try{x=await this.toReference(Fl(P))}catch(Ne){return this.indirections.pop(),this.handleDereferenceError(Ne,e,{directAncestors:E,isExternalReference:me})}const G=cd(P),de=qo(x.value.result);try{J=m1(G,de)}catch(Ne){return this.indirections.pop(),this.handleDereferenceError(Ne,e,{directAncestors:E,isExternalReference:me})}J=qo(J),J.id=So.identify(J)}else{if(k=this.toBaseURI(P),re=In(this.reference.uri)===k,me=!re,!this.options.resolve.internal&&re||!this.options.resolve.external&&me)return;try{x=await this.toReference(Fl(P))}catch(Ne){return this.indirections.pop(),this.handleDereferenceError(Ne,e,{directAncestors:E,isExternalReference:me})}const G=rs(P),de=qo(x.value.result);try{J=Us(de,G)}catch(Ne){return this.indirections.pop(),this.handleDereferenceError(Ne,e,{directAncestors:E,isExternalReference:me})}J=qo(J),J.id=So.identify(J)}}if(e===J){const je=new Mt("Recursive Schema Object reference detected");return this.indirections.pop(),this.handleDereferenceError(je,e,{directAncestors:E,isExternalReference:me})}if(this.indirections.length>this.options.dereference.maxDepth){const je=new ud(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);return this.indirections.pop(),this.handleDereferenceError(je,e,{directAncestors:E,isExternalReference:me})}if(d.includes(J)){if(x.refSet.circular=!0,this.options.dereference.circular==="error"){const je=new Mt("Circular reference detected");return this.indirections.pop(),this.handleDereferenceError(je,e,{directAncestors:E,isExternalReference:me})}if(this.options.dereference.circular==="replace"){var le,ve;const je=new Ge.sI(J.id,{type:"json-schema",uri:x.uri,$ref:Et(e.$ref)}),G=((le=(ve=this.options.dereference.strategyOpts["openapi-3-1"])===null||ve===void 0?void 0:ve.circularReplacer)!==null&&le!==void 0?le:this.options.dereference.circularReplacer)(je);return p.replaceWith(G,os),!n&&G}}const be=In(x.refSet.rootRef.uri)!==x.uri,ce=["error","replace"].includes(this.options.dereference.circular);if((me||be||Vs(J)&&Ir(J.$ref)||ce)&&!d.includesCycle(J)){E.add(e),this.errorContext.push(e);const je=new pd({reference:x,namespace:this.namespace,indirections:[...this.indirections],options:this.getNestedVisitorOptions(e),refractCache:this.refractCache,ancestors:d,allOfDiscriminatorMapping:this.allOfDiscriminatorMapping,errorContext:this.errorContext,errorPropagationCache:this.errorPropagationCache,pendingPropagations:this.pendingPropagations});try{J=await yv(J,je,{keyMap:Js,nodeTypeGetter:as})}catch(G){return this.indirections.pop(),this.handleDereferenceError(G,e,{directAncestors:E,isExternalReference:me})}E.delete(e),this.popErrorContext(e)}if((c=this.options.dereference.dereferenceOpts)!==null&&c!==void 0&&c.continueOnError&&this.pendingPropagations.push({referencingElement:e,referencedElement:J}),this.indirections.pop(),a1(J)){const je=hr(J);return je.setMetaProperty("id",So.generateId()),je.setMetaProperty("ref-fields",{$ref:Et(e.$ref),$refBaseURI:P}),je.setMetaProperty("ref-origin",x.uri),je.setMetaProperty("ref-referencing-element-id",hr(So.identify(e))),p.replaceWith(je,os),!n&&je}if(Vs(J)){var _e,Ae;const je=new qu([...J.content],hr(J.meta),hr(J.attributes));if(je.setMetaProperty("id",So.generateId()),e.forEach(((G,de,Ne)=>{je.remove(Et(de)),je.content.push(Ne)})),je.remove("$ref"),je.setMetaProperty("ref-fields",{$ref:Et(e.$ref),$refBaseURI:P}),je.setMetaProperty("ref-origin",x.uri),je.setMetaProperty("ref-referencing-element-id",hr(So.identify(e))),(_e=this.options.dereference.dereferenceOpts)!==null&&_e!==void 0&&_e.continueOnError&&je.setMetaProperty("ref-referencing-element",e),(Ae=this.options.dereference.strategyOpts["openapi-3-1"])!==null&&Ae!==void 0&&Ae.dereferenceDiscriminatorMapping){var xe;const G=o[o.length-1],de=[...E].findLast(Vs),Ne=de?.getMetaProperty("schemaName"),Ye=Et(je.getMetaProperty("schemaName"));if(Ye&&Ne&&G!=null&&(xe=G.classes)!==null&&xe!==void 0&&xe.contains("json-schema-allOf")){var Oe;const ht=(Oe=this.allOfDiscriminatorMapping.get(Ye))!==null&&Oe!==void 0?Oe:[];ht.push(de),this.allOfDiscriminatorMapping.set(Ye,ht)}}J=je}return p.replaceWith(J,os),n?void 0:J}}const H2=pd,M8=Pa[Symbol.for("nodejs.util.promisify.custom")],R8=class extends x8{constructor(e){super({...e??{},name:"openapi-3-1"})}canDereference(e){var r;return e.mediaType!=="text/plain"?id.includes(e.mediaType):C2((r=e.parseResult)===null||r===void 0?void 0:r.result)}async dereference(e,r){var n;const a=Ac(s1),o=(n=r.dereference.refSet)!==null&&n!==void 0?n:new sd,p=new sd;let c,d=o;o.has(e.uri)?c=o.find($2(e.uri,"uri")):(c=new Cc({uri:e.uri,value:e.parseResult}),o.add(c)),r.dereference.immutable&&(o.refs.map((k=>new Cc({...k,value:hr(k.value)}))).forEach((k=>p.add(k))),c=p.find((k=>k.uri===e.uri)),d=p);const E=new H2({reference:c,namespace:a,options:r}),x=await M8(d.rootRef.value,E,{keyMap:Js,nodeTypeGetter:as});return r.dereference.immutable&&p.refs.filter((k=>k.uri.startsWith("immutable://"))).map((k=>new Cc({...k,uri:k.uri.replace(/^immutable:\/\//,"")}))).forEach((k=>o.add(k))),r.dereference.refSet===null&&o.clean(),p.clean(),x}},Ws=t=>{const e=(r=>r.slice(2))(t);return e.reduce(((r,n,a)=>{if(Bs(n)){const o=String(Et(n.key));r.push(o)}else if(vo(e[a-2])){const o=e[a-2].content.indexOf(n);r.push(o)}return r}),[])},D8=class{modelPropertyMacro;options;SchemaElement={leave:(e,r,n,a,o)=>{e.properties!==void 0&&hn(e.properties)&&e.properties.forEach((p=>{if(hn(p))try{const c=this.modelPropertyMacro(Et(p));p.set("default",c)}catch(c){const d=new Error(c,{cause:c});d.fullPath=[...Ws([...o,n,e]),"properties"],this.options.dereference.dereferenceOpts?.errors?.push?.(d)}}))}};constructor({modelPropertyMacro:e,options:r}){this.modelPropertyMacro=e,this.options=r}};var F8=(function(){function t(e,r){this.xf=r,this.pred=e,this.items=[]}return t.prototype["@@transducer/init"]=Tu,t.prototype["@@transducer/result"]=Nf,t.prototype["@@transducer/step"]=function(e,r){return hy(this.pred,r,this.items)?e:(this.items.push(r),this.xf["@@transducer/step"](e,r))},t})();function L8(t){return function(e){return new F8(t,e)}}var $8=Tr(Nu([],L8,(function(t,e){for(var r,n=0,a=e.length,o=[];nEt(d)==="enum"?(E,x)=>{if(Yh(["json-schema-enum"],E)&&Yh(["json-schema-enum"],x)){const k=(R,L)=>!(vo(R)||vo(L)||hn(R)||hn(L))&&R.equals(Et(L)),P=no(E);return P.content=K2(k)([...E.content,...x.content]),P}return qp(E,x)}:qp});if(e.hasKey("$$ref")||c.remove("$$ref"),e.hasKey("example")){const d=c.getMember("example");d&&(d.value=e.get("example"))}if(e.hasKey("examples")){const d=c.getMember("examples");d&&(d.value=e.get("examples"))}e.content=c.content}}}};constructor({options:e}){this.options=e}},q8=class{parameterMacro;options;#e;OperationElement={enter:e=>{this.#e=e},leave:()=>{this.#e=void 0}};ParameterElement={leave:(e,r,n,a,o)=>{const p=this.#e?Et(this.#e):null,c=Et(e);try{const d=this.parameterMacro(p,c);e.set("default",d)}catch(d){const E=new Error(d,{cause:d});E.fullPath=Ws([...o,n]),this.options.dereference.dereferenceOpts?.errors?.push?.(E)}}};constructor({parameterMacro:e,options:r}){this.parameterMacro=e,this.options=r}},vv=t=>{if(t.cause==null)return t;let{cause:e}=t;for(;e.cause!=null;)e=e.cause;return e},U8=class extends xr{},{wrapError:v1}=RE,b1=Pa[Symbol.for("nodejs.util.promisify.custom")],is=new f1,Jp=(t,e,r,n)=>{Bs(n)?n.value=t:Array.isArray(n)&&(n[r]=t)};class Cm extends H2{useCircularStructures;allowMetaPatches;basePath;constructor({allowMetaPatches:e=!0,useCircularStructures:r=!1,basePath:n=null,...a}){super(a),this.allowMetaPatches=e,this.useCircularStructures=r,this.basePath=n}async ReferenceElement(e,r,n,a,o,p){try{var c,d;if(Yn()(c=this.indirections).call(c,e))return!1;const[E,x]=this.toAncestorLineage([...o,n]),k=this.toBaseURI(Et(e.$ref)),P=In(this.reference.uri)===k,R=!P;if(!this.options.resolve.internal&&P||!this.options.resolve.external&&R)return!1;const L=await this.toReference(Et(e.$ref)),V=go(k,Et(e.$ref));this.indirections.push(e);const Q=rs(V);let J=Us(L.value.result,Q);if(J.id=is.identify(J),$p(J)){const ve=Et(e.meta.get("referenced-element")),be=`${ve}-${Et(is.identify(J))}`;this.refractCache.has(be)?J=this.refractCache.get(be):Vn(J)?(J=od.refract(J),J.setMetaProperty("referenced-element",ve),this.refractCache.set(be,J)):(J=this.namespace.getElementClass(ve).refract(J),this.refractCache.set(be,J))}if(e===J)throw new Mt("Recursive Reference Object detected");if(this.indirections.length>this.options.dereference.maxDepth)throw new ud(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);if(Yn()(E).call(E,J)){if(L.refSet.circular=!0,this.options.dereference.circular==="error")throw new Mt("Circular reference detected");if(this.options.dereference.circular==="replace"){const ve=new Ge.sI(J.id,{type:"reference",uri:L.uri,$ref:Et(e.$ref),baseURI:V,referencingElement:e}),be=(this.options.dereference.strategyOpts["openapi-3-1"]?.circularReplacer??this.options.dereference.circularReplacer)(ve);return p.replaceWith(ve,Jp),!n&&be}}const re=In(L.refSet.rootRef.uri)!==L.uri,me=Yn()(d=["error","replace"]).call(d,this.options.dereference.circular);if((R||re||Vp(J)||me)&&!E.includesCycle(J)){x.add(e);const ve=new Cm({reference:L,namespace:this.namespace,indirections:[...this.indirections],options:this.options,refractCache:this.refractCache,ancestors:E,allowMetaPatches:this.allowMetaPatches,useCircularStructures:this.useCircularStructures,basePath:this.basePath??[...Ws([...o,n,e]),"$ref"]});J=await b1(J,ve,{keyMap:Js,nodeTypeGetter:as}),x.delete(e)}this.indirections.pop();const le=no(J);if(le.setMetaProperty("ref-fields",{$ref:Et(e.$ref),description:Et(e.description),summary:Et(e.summary)}),le.setMetaProperty("ref-origin",L.uri),le.setMetaProperty("ref-referencing-element-id",hr(is.identify(e))),hn(J)&&(e.hasKey("description")&&"description"in J&&(le.remove("description"),le.set("description",e.get("description"))),e.hasKey("summary")&&"summary"in J&&(le.remove("summary"),le.set("summary",e.get("summary")))),this.allowMetaPatches&&hn(le)&&!le.hasKey("$$ref")){const ve=go(k,V);le.set("$$ref",ve)}return p.replaceWith(le,Jp),!n&&le}catch(E){const x=vv(E),k=v1(x,{baseDoc:this.reference.uri,$ref:Et(e.$ref),pointer:rs(Et(e.$ref)),fullPath:this.basePath??[...Ws([...o,n,e]),"$ref"]});return void this.options.dereference.dereferenceOpts?.errors?.push?.(k)}}async PathItemElement(e,r,n,a,o,p){try{var c,d;if(!Ir(e.$ref))return;if(Yn()(c=this.indirections).call(c,e)||Yh(["cycle"],e.$ref))return!1;const[E,x]=this.toAncestorLineage([...o,n]),k=this.toBaseURI(Et(e.$ref)),P=In(this.reference.uri)===k,R=!P;if(!this.options.resolve.internal&&P||!this.options.resolve.external&&R)return;const L=await this.toReference(Et(e.$ref)),V=go(k,Et(e.$ref));this.indirections.push(e);const Q=rs(V);let J=Us(L.value.result,Q);if(J.id=is.identify(J),$p(J)){const le=`path-item-${Et(is.identify(J))}`;this.refractCache.has(le)?J=this.refractCache.get(le):(J=$u.refract(J),this.refractCache.set(le,J))}if(e===J)throw new Mt("Recursive Path Item Object reference detected");if(this.indirections.length>this.options.dereference.maxDepth)throw new ud(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);if(Yn()(E).call(E,J)){if(L.refSet.circular=!0,this.options.dereference.circular==="error")throw new Mt("Circular reference detected");if(this.options.dereference.circular==="replace"){const le=new Ge.sI(J.id,{type:"path-item",uri:L.uri,$ref:Et(e.$ref),baseURI:V,referencingElement:e}),ve=(this.options.dereference.strategyOpts["openapi-3-1"]?.circularReplacer??this.options.dereference.circularReplacer)(le);return p.replaceWith(le,Jp),!n&&ve}}const re=In(L.refSet.rootRef.uri)!==L.uri,me=Yn()(d=["error","replace"]).call(d,this.options.dereference.circular);if((R||re||zp(J)&&Ir(J.$ref)||me)&&!E.includesCycle(J)){x.add(e);const le=new Cm({reference:L,namespace:this.namespace,indirections:[...this.indirections],options:this.options,ancestors:E,allowMetaPatches:this.allowMetaPatches,useCircularStructures:this.useCircularStructures,basePath:this.basePath??[...Ws([...o,n,e]),"$ref"]});J=await b1(J,le,{keyMap:Js,nodeTypeGetter:as}),x.delete(e)}if(this.indirections.pop(),zp(J)){const le=new $u([...J.content],hr(J.meta),hr(J.attributes));if(e.forEach(((ve,be,ce)=>{le.remove(Et(be)),le.content.push(ce)})),le.remove("$ref"),le.setMetaProperty("ref-fields",{$ref:Et(e.$ref)}),le.setMetaProperty("ref-origin",L.uri),le.setMetaProperty("ref-referencing-element-id",hr(is.identify(e))),this.allowMetaPatches&&le.get("$$ref")===void 0){const ve=go(k,V);le.set("$$ref",ve)}J=le}return p.replaceWith(J,Jp),n?void 0:J}catch(E){const x=vv(E),k=v1(x,{baseDoc:this.reference.uri,$ref:Et(e.$ref),pointer:rs(Et(e.$ref)),fullPath:this.basePath??[...Ws([...o,n,e]),"$ref"]});return void this.options.dereference.dereferenceOpts?.errors?.push?.(k)}}async SchemaElement(e,r,n,a,o,p){try{var c,d;if(!Ir(e.$ref))return;if(Yn()(c=this.indirections).call(c,e))return!1;const[E,x]=this.toAncestorLineage([...o,n]);let k=await this.toReference(Fl(this.reference.uri)),{uri:P}=k;const R=z2(P,e),L=In(R),V=new ld({uri:L}),Q=!this.options.resolve.resolvers.some((ce=>ce.canRead(V))),J=!Q;let re,me=In(this.reference.uri)===R,le=!me;this.indirections.push(e);try{if(Q||J){P=this.toBaseURI(R);const ce=R,_e=qo(k.value.result);if(re=gv(ce,_e),re=qo(re),re.id=is.identify(re),!this.options.resolve.internal&&me||!this.options.resolve.external&&le)return}else{if(P=this.toBaseURI(R),me=In(this.reference.uri)===P,le=!me,!this.options.resolve.internal&&me||!this.options.resolve.external&&le)return;k=await this.toReference(Fl(R));const ce=rs(R),_e=qo(k.value.result);re=Us(_e,ce),re=qo(re),re.id=is.identify(re)}}catch(ce){if(!(J&&ce instanceof g1))throw ce;if(mv(cd(R))){if(me=In(this.reference.uri)===P,le=!me,!this.options.resolve.internal&&me||!this.options.resolve.external&&le)return;k=await this.toReference(Fl(R));const _e=cd(R),Ae=qo(k.value.result);re=m1(_e,Ae),re=qo(re),re.id=is.identify(re)}else{if(P=this.toBaseURI(Et(R)),me=In(this.reference.uri)===P,le=!me,!this.options.resolve.internal&&me||!this.options.resolve.external&&le)return;k=await this.toReference(Fl(R));const _e=rs(R),Ae=qo(k.value.result);re=Us(Ae,_e),re=qo(re),re.id=is.identify(re)}}if(e===re)throw new Mt("Recursive Schema Object reference detected");if(this.indirections.length>this.options.dereference.maxDepth)throw new ud(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);if(Yn()(E).call(E,re)){if(k.refSet.circular=!0,this.options.dereference.circular==="error")throw new Mt("Circular reference detected");if(this.options.dereference.circular==="replace"){const ce=new Ge.sI(re.id,{type:"json-schema",uri:k.uri,$ref:Et(e.$ref),baseURI:go(P,R),referencingElement:e}),_e=(this.options.dereference.strategyOpts["openapi-3-1"]?.circularReplacer??this.options.dereference.circularReplacer)(ce);return p.replaceWith(_e,Jp),!n&&_e}}const ve=In(k.refSet.rootRef.uri)!==k.uri,be=Yn()(d=["error","replace"]).call(d,this.options.dereference.circular);if((le||ve||Vs(re)&&Ir(re.$ref)||be)&&!E.includesCycle(re)){x.add(e);const ce=new Cm({reference:k,namespace:this.namespace,indirections:[...this.indirections],options:this.options,useCircularStructures:this.useCircularStructures,allowMetaPatches:this.allowMetaPatches,ancestors:E,basePath:this.basePath??[...Ws([...o,n,e]),"$ref"]});re=await b1(re,ce,{keyMap:Js,nodeTypeGetter:as}),x.delete(e)}if(this.indirections.pop(),a1(re)){const ce=hr(re);return ce.setMetaProperty("ref-fields",{$ref:Et(e.$ref)}),ce.setMetaProperty("ref-origin",k.uri),ce.setMetaProperty("ref-referencing-element-id",hr(is.identify(e))),p.replaceWith(ce,Jp),!n&&ce}if(Vs(re)){const ce=new qu([...re.content],hr(re.meta),hr(re.attributes));if(e.forEach(((_e,Ae,xe)=>{ce.remove(Et(Ae)),ce.content.push(xe)})),ce.remove("$ref"),ce.setMetaProperty("ref-fields",{$ref:Et(e.$ref)}),ce.setMetaProperty("ref-origin",k.uri),ce.setMetaProperty("ref-referencing-element-id",hr(is.identify(e))),this.allowMetaPatches&&ce.get("$$ref")===void 0){const _e=go(P,R);ce.set("$$ref",_e)}re=ce}return p.replaceWith(re,Jp),n?void 0:re}catch(E){const x=vv(E),k=new U8(`Could not resolve reference: ${x.message}`,{baseDoc:this.reference.uri,$ref:Et(e.$ref),fullPath:this.basePath??[...Ws([...o,n,e]),"$ref"],cause:x});return void this.options.dereference.dereferenceOpts?.errors?.push?.(k)}}async LinkElement(){}async ExampleElement(e,r,n,a,o,p){try{return await super.ExampleElement(e,r,n,a,o,p)}catch(c){const d=vv(c),E=v1(d,{baseDoc:this.reference.uri,externalValue:Et(e.externalValue),fullPath:this.basePath??[...Ws([...o,n,e]),"externalValue"]});return void this.options.dereference.dereferenceOpts?.errors?.push?.(E)}}}const z8=Cm,V8=Cy[Symbol.for("nodejs.util.promisify.custom")],J8=class{constructor({parameterMacro:e,modelPropertyMacro:r,mode:n,options:a,...o}){const p=[];p.push(new z8({...o,options:a})),typeof r=="function"&&p.push(new D8({modelPropertyMacro:r,options:a})),n!=="strict"&&p.push(new B8({options:a})),typeof e=="function"&&p.push(new q8({parameterMacro:e,options:a}));const c=V8(p,{nodeTypeGetter:as});Object.assign(this,c)}},W8=Pa[Symbol.for("nodejs.util.promisify.custom")],H8=class extends R8{allowMetaPatches;parameterMacro;modelPropertyMacro;mode;ancestors;constructor({allowMetaPatches:e=!1,parameterMacro:r=null,modelPropertyMacro:n=null,mode:a="non-strict",ancestors:o=[],...p}={}){super({...p}),this.name="openapi-3-1-swagger-client",this.allowMetaPatches=e,this.parameterMacro=r,this.modelPropertyMacro=n,this.mode=a,this.ancestors=[...o]}async dereference(e,r){const n=Ac(s1),a=r.dereference.refSet??new sd,o=new sd;let p,c=a;a.has(e.uri)?p=a.find((x=>x.uri===e.uri)):(p=new Cc({uri:e.uri,value:e.parseResult}),a.add(p)),r.dereference.immutable&&(a.refs.map((x=>new Cc({...x,value:hr(x.value)}))).forEach((x=>o.add(x))),p=o.find((x=>x.uri===e.uri)),c=o);const d=new J8({reference:p,namespace:n,options:r,allowMetaPatches:this.allowMetaPatches,ancestors:this.ancestors,modelPropertyMacro:this.modelPropertyMacro,mode:this.mode,parameterMacro:this.parameterMacro}),E=await W8(c.rootRef.value,d,{keyMap:Js,nodeTypeGetter:as});return r.dereference.immutable&&o.refs.filter((x=>x.uri.startsWith("immutable://"))).map((x=>new Cc({...x,uri:x.uri.replace(/^immutable:\/\//,"")}))).forEach((x=>a.add(x))),r.dereference.refSet===null&&a.clean(),o.clean(),E}},K8=t=>{const e=Et(t.meta.get("baseURI")),r=t.meta.get("referencingElement");return new Ge.Sh({$ref:e},hr(r.meta),hr(r.attributes))},w1=async t=>{const{spec:e,timeout:r,redirects:n,requestInterceptor:a,responseInterceptor:o,pathDiscriminator:p=[],allowMetaPatches:c=!1,useCircularStructures:d=!1,skipNormalization:E=!1,parameterMacro:x=null,modelPropertyMacro:k=null,mode:P="non-strict",strategies:R}=t;try{const{cache:L}=w1,V=R.find((Ne=>Ne.match(e))),Q=j0(P0())?P0():Sy,J=z0(t),re=go(Q,J);let me;L.has(e)?me=L.get(e):(me=Lu.refract(e),me.classes.push("result"),L.set(e,me));const le=new $s([me]),ve=_S(p),be=ve===""?"":`#${ve}`,ce=Us(me,ve),_e=new Cc({uri:re,value:le}),Ae=new sd({refs:[_e]});ve!==""&&(Ae.rootRef=void 0);const xe=[new Set([ce])],Oe=[],je=await(async(Ne,Ye={})=>{const ht=R2(I2,Ye);return u8(Ne,ht)})(ce,{resolve:{baseURI:`${re}${be}`,resolvers:[new v8({timeout:r||1e4,redirects:n||10})],resolverOpts:{swaggerHTTPClientConfig:{requestInterceptor:a,responseInterceptor:o}},strategies:[new d8]},parse:{mediaType:id.latest(),parsers:[new E8({allowEmpty:!1,sourceMap:!1}),new S8({allowEmpty:!1,sourceMap:!1}),new b8({allowEmpty:!1,sourceMap:!1}),new w8({allowEmpty:!1,sourceMap:!1}),new p8({allowEmpty:!1,sourceMap:!1})]},dereference:{maxDepth:100,strategies:[new H8({allowMetaPatches:c,useCircularStructures:d,parameterMacro:x,modelPropertyMacro:k,mode:P,ancestors:xe})],refSet:Ae,dereferenceOpts:{errors:Oe},immutable:!1,circular:d?"ignore":"replace",circularReplacer:d?I2.dereference.circularReplacer:K8}}),G=((Ne,Ye,ht)=>new lj({element:ht}).transclude(Ne,Ye))(ce,je,me),de=E?G:V.normalize(G);return{spec:Et(de),errors:Oe}}catch(L){if(L instanceof Bp)return{spec:e,errors:[]};throw L}};w1.cache=new WeakMap;const G2=w1;function Y2(t,e,r){if(r||(r=new G8),(function(o){var p=typeof o;return o==null||p!="object"&&p!="function"})(t))return t;var n=function(o){var p=r.get(t);if(p)return p;for(var c in r.set(t,o),t)Object.prototype.hasOwnProperty.call(t,c)&&(o[c]=t[c]);return o};switch(Fs(t)){case"Object":return n(Object.create(Object.getPrototypeOf(t)));case"Array":return n(Array(t.length));case"Date":return new Date(t.valueOf());case"RegExp":return(function(o){return new RegExp(o.source,o.flags?o.flags:(o.global?"g":"")+(o.ignoreCase?"i":"")+(o.multiline?"m":"")+(o.sticky?"y":"")+(o.unicode?"u":"")+(o.dotAll?"s":""))})(t);case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":case"BigInt64Array":case"BigUint64Array":return t.slice();default:return t}}var G8=(function(){function t(){this.map={},this.length=0}return t.prototype.set=function(e,r){var n=this.hash(e),a=this.map[n];a||(this.map[n]=a=[]),a.push([e,r]),this.length+=1},t.prototype.hash=function(e){var r=[];for(var n in e)r.push(Object.prototype.toString.call(e[n]));return r.join()},t.prototype.get=function(e){if(this.length<=180){for(var r in this.map)for(var n=this.map[r],a=0;at.replace(/\s/g,""),S1=t=>t.replace(/\W/gi,"_"),eM=(t,e,r)=>{const n=E1(t);return n.length>0?S1(n):((a,o)=>`${S1(E1(o.toLowerCase()))}${S1(E1(a))}`)(e,r)},tM=({storageField:t="x-normalized",operationIdNormalizer:e=eM}={})=>r=>{const{predicates:n,ancestorLineageToJSONPointer:a,namespace:o}=r,p=[],c=[],d=[];let E;return{visitor:{OpenApi3_1Element:{enter(x){E=new jm(x,t,"operation-ids")},leave(){const x=Z8((k=>Et(k.operationId)),c);Object.entries(x).forEach((([k,P])=>{Array.isArray(P)&&(P.length<=1||P.forEach(((R,L)=>{const V=`${k}${L+1}`;R.operationId=new o.elements.String(V)})))})),d.forEach((k=>{if(k.operationId===void 0)return;const P=String(Et(k.operationId)),R=c.find((L=>Et(L.meta.get("originalOperationId"))===P));R!==void 0&&(k.operationId=hr.safe(R.operationId),k.meta.set("originalOperationId",P),k.set("__originalOperationId",P))})),c.length=0,d.length=0,E=void 0}},PathItemElement:{enter(x){const k=Jh("path",Et(x.meta.get("path")));p.push(k)},leave(){p.pop()}},OperationElement:{enter(x,k,P,R,L){if(x.operationId===void 0)return;const V=a([...L,P,x]);if(E.includes(V))return;const Q=String(Et(x.operationId)),J=py(p),re=Jh("method",Et(x.meta.get("http-method"))),me=e(Q,J,re);Q!==me&&(x.operationId=new o.elements.String(me),x.set("__originalOperationId",Q),x.meta.set("originalOperationId",Q),c.push(x),E.append(V))}},LinkElement:{leave(x){n.isLinkElement(x)&&x.operationId!==void 0&&d.push(x)}}}}},rM=({storageField:t="x-normalized"}={})=>e=>{const{predicates:r,ancestorLineageToJSONPointer:n}=e,a=(c,d)=>!!r.isParameterElement(c)&&!!r.isParameterElement(d)&&!!r.isStringElement(c.name)&&!!r.isStringElement(c.in)&&!!r.isStringElement(d.name)&&!!r.isStringElement(d.in)&&Et(c.name)===Et(d.name)&&Et(c.in)===Et(d.in),o=[];let p;return{visitor:{OpenApi3_1Element:{enter(c){p=new jm(c,t,"parameters")},leave(){p=void 0}},PathItemElement:{enter(c,d,E,x,k){if(k.some(r.isComponentsElement))return;const{parameters:P}=c;r.isArrayElement(P)?o.push([...P.content]):o.push([])},leave(){o.pop()}},OperationElement:{leave(c,d,E,x,k){const P=py(o);if(!Array.isArray(P)||P.length===0)return;const R=n([...k,E,c]);if(p.includes(R))return;const L=B2([],["parameters","content"],c),V=K2(a,[...L,...P]);c.parameters=new i2(V),p.append(R)}}}}},nM=({storageField:t="x-normalized"}={})=>e=>{const{predicates:r,ancestorLineageToJSONPointer:n}=e;let a,o;return{visitor:{OpenApi3_1Element:{enter(p){o=new jm(p,t,"security-requirements"),r.isArrayElement(p.security)&&(a=p.security)},leave(){o=void 0,a=void 0}},OperationElement:{leave(p,c,d,E,x){if(x.some(r.isComponentsElement))return;const k=n([...x,d,p]);if(!o.includes(k)){var P;p.security===void 0&&a!==void 0&&(p.security=new l2((P=a)===null||P===void 0?void 0:P.content),o.append(k))}}}}}},aM=({storageField:t="x-normalized"}={})=>e=>{const{predicates:r,ancestorLineageToJSONPointer:n}=e;let a;return{visitor:{OpenApi3_1Element:{enter(o){a=new jm(o,t,"parameter-examples")},leave(){a=void 0}},ParameterElement:{leave(o,p,c,d,E){var x,k;if(E.some(r.isComponentsElement)||o.schema===void 0||!r.isSchemaElement(o.schema)||((x=o.schema)===null||x===void 0?void 0:x.example)===void 0&&((k=o.schema)===null||k===void 0?void 0:k.examples)===void 0)return;const P=n([...E,c,o]);if(!a.includes(P)){if(o.examples!==void 0&&r.isObjectElement(o.examples)){const R=o.examples.map((L=>hr.safe(L.value)));return o.schema.examples!==void 0&&(o.schema.set("examples",R),a.append(P)),void(o.schema.example!==void 0&&(o.schema.set("example",R[0]),a.append(P)))}o.example!==void 0&&(o.schema.examples!==void 0&&(o.schema.set("examples",[hr(o.example)]),a.append(P)),o.schema.example!==void 0&&(o.schema.set("example",hr(o.example)),a.append(P)))}}}}}},oM=({storageField:t="x-normalized"}={})=>e=>{const{predicates:r,ancestorLineageToJSONPointer:n}=e;let a;return{visitor:{OpenApi3_1Element:{enter(o){a=new jm(o,t,"header-examples")},leave(){a=void 0}},HeaderElement:{leave(o,p,c,d,E){var x,k;if(E.some(r.isComponentsElement)||o.schema===void 0||!r.isSchemaElement(o.schema)||((x=o.schema)===null||x===void 0?void 0:x.example)===void 0&&((k=o.schema)===null||k===void 0?void 0:k.examples)===void 0)return;const P=n([...E,c,o]);if(!a.includes(P)){if(o.examples!==void 0&&r.isObjectElement(o.examples)){const R=o.examples.map((L=>hr.safe(L.value)));return o.schema.examples!==void 0&&(o.schema.set("examples",R),a.append(P)),void(o.schema.example!==void 0&&(o.schema.set("example",R[0]),a.append(P)))}o.example!==void 0&&(o.schema.examples!==void 0&&(o.schema.set("examples",[hr(o.example)]),a.append(P)),o.schema.example!==void 0&&(o.schema.set("example",hr(o.example)),a.append(P)))}}}}}},X2=t=>{if(!hn(t))return t;const e=[tM({operationIdNormalizer:(r,n,a)=>Df({operationId:r},n,a,{v2OperationIdCompatibilityMode:!1})}),rM(),nM(),aM(),oM()];return ql(t,e,{toolboxCreator:N2,visitorOptions:{keyMap:Js,nodeTypeGetter:as}})},iM={name:"openapi-3-1-apidom",match:t=>Y0(t),normalize(t){if(!da(t)&&Ls(t)&&!t.$$normalized){const r=(e=X2,n=>{const a=Lu.refract(n);a.classes.push("result");const o=e(a),p=Et(o);return G2.cache.set(p,o),Et(o)})(t);return r.$$normalized=!0,r}var e;return da(t)?X2(t):t},resolve:async t=>G2(t)},Q2=iM,Z2=t=>async e=>(async r=>{const{spec:n,requestInterceptor:a,responseInterceptor:o}=r,p=z0(r),c=YE(r),d=n||await zE(c,{requestInterceptor:a,responseInterceptor:o})(p),E={...r,spec:d};return r.strategies.find((x=>x.match(d))).resolve(E)})({...t,...e}),sM=Z2({strategies:[Q0,X0,G0]}),lM=(t,e,r,n,a)=>{if(t===Br.SEM_PRE){if(Array.isArray(a)===!1)throw new Error("parser's user data must be an array");a.push(["server-url-template",Ni.charsToString(e,r,n)])}return Br.SEM_OK},cM=(t,e,r,n,a)=>{if(t===Br.SEM_PRE){if(Array.isArray(a)===!1)throw new Error("parser's user data must be an array");a.push(["server-variable",Ni.charsToString(e,r,n)])}return Br.SEM_OK},uM=(t,e,r,n,a)=>{if(t===Br.SEM_PRE){if(Array.isArray(a)===!1)throw new Error("parser's user data must be an array");a.push(["server-variable-name",Ni.charsToString(e,r,n)])}return Br.SEM_OK},pM=(t,e,r,n,a)=>{if(t===Br.SEM_PRE){if(Array.isArray(a)===!1)throw new Error("parser's user data must be an array");a.push(["literals",Ni.charsToString(e,r,n)])}return Br.SEM_OK},hM=new function(){this.grammarObject="grammarObject",this.rules=[],this.rules[0]={name:"server-url-template",lower:"server-url-template",index:0,isBkr:!1},this.rules[1]={name:"server-variable",lower:"server-variable",index:1,isBkr:!1},this.rules[2]={name:"server-variable-name",lower:"server-variable-name",index:2,isBkr:!1},this.rules[3]={name:"literals",lower:"literals",index:3,isBkr:!1},this.rules[4]={name:"DIGIT",lower:"digit",index:4,isBkr:!1},this.rules[5]={name:"HEXDIG",lower:"hexdig",index:5,isBkr:!1},this.rules[6]={name:"pct-encoded",lower:"pct-encoded",index:6,isBkr:!1},this.rules[7]={name:"ucschar",lower:"ucschar",index:7,isBkr:!1},this.rules[8]={name:"iprivate",lower:"iprivate",index:8,isBkr:!1},this.udts=[],this.rules[0].opcodes=[],this.rules[0].opcodes[0]={type:3,min:1,max:1/0},this.rules[0].opcodes[1]={type:1,children:[2,3]},this.rules[0].opcodes[2]={type:4,index:3},this.rules[0].opcodes[3]={type:4,index:1},this.rules[1].opcodes=[],this.rules[1].opcodes[0]={type:2,children:[1,2,3]},this.rules[1].opcodes[1]={type:7,string:[123]},this.rules[1].opcodes[2]={type:4,index:2},this.rules[1].opcodes[3]={type:7,string:[125]},this.rules[2].opcodes=[],this.rules[2].opcodes[0]={type:3,min:1,max:1/0},this.rules[2].opcodes[1]={type:1,children:[2,3,4]},this.rules[2].opcodes[2]={type:5,min:0,max:122},this.rules[2].opcodes[3]={type:6,string:[124]},this.rules[2].opcodes[4]={type:5,min:126,max:1114111},this.rules[3].opcodes=[],this.rules[3].opcodes[0]={type:3,min:1,max:1/0},this.rules[3].opcodes[1]={type:1,children:[2,3,4,5,6,7,8,9,10,11,12,13]},this.rules[3].opcodes[2]={type:6,string:[33]},this.rules[3].opcodes[3]={type:5,min:35,max:36},this.rules[3].opcodes[4]={type:5,min:38,max:59},this.rules[3].opcodes[5]={type:6,string:[61]},this.rules[3].opcodes[6]={type:5,min:63,max:91},this.rules[3].opcodes[7]={type:6,string:[93]},this.rules[3].opcodes[8]={type:6,string:[95]},this.rules[3].opcodes[9]={type:5,min:97,max:122},this.rules[3].opcodes[10]={type:6,string:[126]},this.rules[3].opcodes[11]={type:4,index:7},this.rules[3].opcodes[12]={type:4,index:8},this.rules[3].opcodes[13]={type:4,index:6},this.rules[4].opcodes=[],this.rules[4].opcodes[0]={type:5,min:48,max:57},this.rules[5].opcodes=[],this.rules[5].opcodes[0]={type:1,children:[1,2,3,4,5,6,7]},this.rules[5].opcodes[1]={type:4,index:4},this.rules[5].opcodes[2]={type:7,string:[97]},this.rules[5].opcodes[3]={type:7,string:[98]},this.rules[5].opcodes[4]={type:7,string:[99]},this.rules[5].opcodes[5]={type:7,string:[100]},this.rules[5].opcodes[6]={type:7,string:[101]},this.rules[5].opcodes[7]={type:7,string:[102]},this.rules[6].opcodes=[],this.rules[6].opcodes[0]={type:2,children:[1,2,3]},this.rules[6].opcodes[1]={type:7,string:[37]},this.rules[6].opcodes[2]={type:4,index:5},this.rules[6].opcodes[3]={type:4,index:5},this.rules[7].opcodes=[],this.rules[7].opcodes[0]={type:1,children:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]},this.rules[7].opcodes[1]={type:5,min:160,max:55295},this.rules[7].opcodes[2]={type:5,min:63744,max:64975},this.rules[7].opcodes[3]={type:5,min:65008,max:65519},this.rules[7].opcodes[4]={type:5,min:65536,max:131069},this.rules[7].opcodes[5]={type:5,min:131072,max:196605},this.rules[7].opcodes[6]={type:5,min:196608,max:262141},this.rules[7].opcodes[7]={type:5,min:262144,max:327677},this.rules[7].opcodes[8]={type:5,min:327680,max:393213},this.rules[7].opcodes[9]={type:5,min:393216,max:458749},this.rules[7].opcodes[10]={type:5,min:458752,max:524285},this.rules[7].opcodes[11]={type:5,min:524288,max:589821},this.rules[7].opcodes[12]={type:5,min:589824,max:655357},this.rules[7].opcodes[13]={type:5,min:655360,max:720893},this.rules[7].opcodes[14]={type:5,min:720896,max:786429},this.rules[7].opcodes[15]={type:5,min:786432,max:851965},this.rules[7].opcodes[16]={type:5,min:851968,max:917501},this.rules[7].opcodes[17]={type:5,min:921600,max:983037},this.rules[8].opcodes=[],this.rules[8].opcodes[0]={type:1,children:[1,2,3]},this.rules[8].opcodes[1]={type:5,min:57344,max:63743},this.rules[8].opcodes[2]={type:5,min:983040,max:1048573},this.rules[8].opcodes[3]={type:5,min:1048576,max:1114109},this.toString=function(){return`; OpenAPI Server URL templating ABNF syntax +server-url-template = 1*( literals / server-variable ) ; variant of https://www.rfc-editor.org/rfc/rfc6570#section-2 +server-variable = "{" server-variable-name "}" +server-variable-name = 1*( %x00-7A / %x7C / %x7E-10FFFF ) ; every UTF8 character except { and } (from OpenAPI) + +; https://www.rfc-editor.org/rfc/rfc6570#section-2.1 +; https://www.rfc-editor.org/errata/eid6937 +literals = 1*( %x21 / %x23-24 / %x26-3B / %x3D / %x3F-5B + / %x5D / %x5F / %x61-7A / %x7E / ucschar / iprivate + / pct-encoded) + ; any Unicode character except: CTL, SP, + ; DQUOTE, "%" (aside from pct-encoded), + ; "<", ">", "\\", "^", "\`", "{", "|", "}" + +; https://www.rfc-editor.org/rfc/rfc6570#section-1.5 +DIGIT = %x30-39 ; 0-9 +HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" ; case-insensitive + +pct-encoded = "%" HEXDIG HEXDIG + +ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF + / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD + / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD + / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD + / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD + / %xD0000-DFFFD / %xE1000-EFFFD + +iprivate = %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD +`}},ex=t=>{const e=new ns;return e.ast=new ib,e.ast.callbacks["server-url-template"]=lM,e.ast.callbacks["server-variable"]=cM,e.ast.callbacks["server-variable-name"]=uM,e.ast.callbacks.literals=pM,{result:e.parse(hM,"server-url-template",t),ast:e.ast}},dM=(t,{strict:e=!1}={})=>{try{const r=ex(t);if(!r.result.success)return!1;const n=[];r.ast.translate(n);const a=n.some((([o])=>o==="server-variable"));if(!e&&!a)try{return new URL(t,"https://vladimirgorej.com"),!0}catch{return!1}return!e||a}catch{return!1}},fM=t=>(e=>{try{return typeof e=="string"&&decodeURIComponent(e)!==e}catch{return!1}})(t)?t:encodeURIComponent(t).replace(/%5B/g,"[").replace(/%5D/g,"]"),mM=["literals","server-variable-name"],gM=(t,e,r={})=>{const n={encoder:fM,...r},a=ex(t);if(!a.result.success)return t;const o=[];return a.ast.translate(o),o.filter((([c])=>mM.includes(c))).map((([c,d])=>c==="server-variable-name"?Object.hasOwn(e,d)?n.encoder(e[d],d):`{${d}}`:d)).join("")};function tx(){this.grammarObject="grammarObject",this.rules=[],this.rules[0]={name:"path-template",lower:"path-template",index:0,isBkr:!1},this.rules[1]={name:"path-segment",lower:"path-segment",index:1,isBkr:!1},this.rules[2]={name:"slash",lower:"slash",index:2,isBkr:!1},this.rules[3]={name:"path-literal",lower:"path-literal",index:3,isBkr:!1},this.rules[4]={name:"template-expression",lower:"template-expression",index:4,isBkr:!1},this.rules[5]={name:"template-expression-param-name",lower:"template-expression-param-name",index:5,isBkr:!1},this.rules[6]={name:"pchar",lower:"pchar",index:6,isBkr:!1},this.rules[7]={name:"unreserved",lower:"unreserved",index:7,isBkr:!1},this.rules[8]={name:"pct-encoded",lower:"pct-encoded",index:8,isBkr:!1},this.rules[9]={name:"sub-delims",lower:"sub-delims",index:9,isBkr:!1},this.rules[10]={name:"ALPHA",lower:"alpha",index:10,isBkr:!1},this.rules[11]={name:"DIGIT",lower:"digit",index:11,isBkr:!1},this.rules[12]={name:"HEXDIG",lower:"hexdig",index:12,isBkr:!1},this.udts=[],this.rules[0].opcodes=[],this.rules[0].opcodes[0]={type:2,children:[1,2,6]},this.rules[0].opcodes[1]={type:4,index:2},this.rules[0].opcodes[2]={type:3,min:0,max:1/0},this.rules[0].opcodes[3]={type:2,children:[4,5]},this.rules[0].opcodes[4]={type:4,index:1},this.rules[0].opcodes[5]={type:4,index:2},this.rules[0].opcodes[6]={type:3,min:0,max:1},this.rules[0].opcodes[7]={type:4,index:1},this.rules[1].opcodes=[],this.rules[1].opcodes[0]={type:3,min:1,max:1/0},this.rules[1].opcodes[1]={type:1,children:[2,3]},this.rules[1].opcodes[2]={type:4,index:3},this.rules[1].opcodes[3]={type:4,index:4},this.rules[2].opcodes=[],this.rules[2].opcodes[0]={type:7,string:[47]},this.rules[3].opcodes=[],this.rules[3].opcodes[0]={type:3,min:1,max:1/0},this.rules[3].opcodes[1]={type:4,index:6},this.rules[4].opcodes=[],this.rules[4].opcodes[0]={type:2,children:[1,2,3]},this.rules[4].opcodes[1]={type:7,string:[123]},this.rules[4].opcodes[2]={type:4,index:5},this.rules[4].opcodes[3]={type:7,string:[125]},this.rules[5].opcodes=[],this.rules[5].opcodes[0]={type:3,min:1,max:1/0},this.rules[5].opcodes[1]={type:1,children:[2,3,4]},this.rules[5].opcodes[2]={type:5,min:0,max:122},this.rules[5].opcodes[3]={type:6,string:[124]},this.rules[5].opcodes[4]={type:5,min:126,max:1114111},this.rules[6].opcodes=[],this.rules[6].opcodes[0]={type:1,children:[1,2,3,4,5]},this.rules[6].opcodes[1]={type:4,index:7},this.rules[6].opcodes[2]={type:4,index:8},this.rules[6].opcodes[3]={type:4,index:9},this.rules[6].opcodes[4]={type:7,string:[58]},this.rules[6].opcodes[5]={type:7,string:[64]},this.rules[7].opcodes=[],this.rules[7].opcodes[0]={type:1,children:[1,2,3,4,5,6]},this.rules[7].opcodes[1]={type:4,index:10},this.rules[7].opcodes[2]={type:4,index:11},this.rules[7].opcodes[3]={type:7,string:[45]},this.rules[7].opcodes[4]={type:7,string:[46]},this.rules[7].opcodes[5]={type:7,string:[95]},this.rules[7].opcodes[6]={type:7,string:[126]},this.rules[8].opcodes=[],this.rules[8].opcodes[0]={type:2,children:[1,2,3]},this.rules[8].opcodes[1]={type:7,string:[37]},this.rules[8].opcodes[2]={type:4,index:12},this.rules[8].opcodes[3]={type:4,index:12},this.rules[9].opcodes=[],this.rules[9].opcodes[0]={type:1,children:[1,2,3,4,5,6,7,8,9,10,11]},this.rules[9].opcodes[1]={type:7,string:[33]},this.rules[9].opcodes[2]={type:7,string:[36]},this.rules[9].opcodes[3]={type:7,string:[38]},this.rules[9].opcodes[4]={type:7,string:[39]},this.rules[9].opcodes[5]={type:7,string:[40]},this.rules[9].opcodes[6]={type:7,string:[41]},this.rules[9].opcodes[7]={type:7,string:[42]},this.rules[9].opcodes[8]={type:7,string:[43]},this.rules[9].opcodes[9]={type:7,string:[44]},this.rules[9].opcodes[10]={type:7,string:[59]},this.rules[9].opcodes[11]={type:7,string:[61]},this.rules[10].opcodes=[],this.rules[10].opcodes[0]={type:1,children:[1,2]},this.rules[10].opcodes[1]={type:5,min:65,max:90},this.rules[10].opcodes[2]={type:5,min:97,max:122},this.rules[11].opcodes=[],this.rules[11].opcodes[0]={type:5,min:48,max:57},this.rules[12].opcodes=[],this.rules[12].opcodes[0]={type:1,children:[1,2,3,4,5,6,7]},this.rules[12].opcodes[1]={type:4,index:11},this.rules[12].opcodes[2]={type:7,string:[97]},this.rules[12].opcodes[3]={type:7,string:[98]},this.rules[12].opcodes[4]={type:7,string:[99]},this.rules[12].opcodes[5]={type:7,string:[100]},this.rules[12].opcodes[6]={type:7,string:[101]},this.rules[12].opcodes[7]={type:7,string:[102]},this.toString=function(){return`; OpenAPI Path Templating ABNF syntax +; variant of https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 +path-template = slash *( path-segment slash ) [ path-segment ] +path-segment = 1*( path-literal / template-expression ) +slash = "/" +path-literal = 1*pchar +template-expression = "{" template-expression-param-name "}" +template-expression-param-name = 1*( %x00-7A / %x7C / %x7E-10FFFF ) ; every UTF8 character except { and } (from OpenAPI) + +; https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 +pchar = unreserved / pct-encoded / sub-delims / ":" / "@" +unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + ; https://datatracker.ietf.org/doc/html/rfc3986#section-2.3 +pct-encoded = "%" HEXDIG HEXDIG + ; https://datatracker.ietf.org/doc/html/rfc3986#section-2.1 +sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + / "*" / "+" / "," / ";" / "=" + ; https://datatracker.ietf.org/doc/html/rfc3986#section-2.2 + +; https://datatracker.ietf.org/doc/html/rfc5234#appendix-B.1 +ALPHA = %x41-5A / %x61-7A ; A-Z / a-z +DIGIT = %x30-39 ; 0-9 +HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" +`}}const yM=(t,e,r,n,a)=>(t===Br.SEM_PRE?a.push(["slash",Ni.charsToString(e,r,n)]):Br.SEM_POST,Br.SEM_OK),vM=(t,e,r,n,a)=>{if(t===Br.SEM_PRE){if(Array.isArray(a)===!1)throw new Error("parser's user data must be an array");a.push(["path-template",Ni.charsToString(e,r,n)])}return Br.SEM_OK},bM=(t,e,r,n,a)=>(t===Br.SEM_PRE?a.push(["path-literal",Ni.charsToString(e,r,n)]):Br.SEM_POST,Br.SEM_OK),wM=(t,e,r,n,a)=>(t===Br.SEM_PRE?a.push(["template-expression",Ni.charsToString(e,r,n)]):Br.SEM_POST,Br.SEM_OK),EM=(t,e,r,n,a)=>(t===Br.SEM_PRE?a.push(["template-expression-param-name",Ni.charsToString(e,r,n)]):Br.SEM_POST,Br.SEM_OK),SM=new tx,xM=t=>{const e=new ns;return e.ast=new ib,e.ast.callbacks["path-template"]=vM,e.ast.callbacks.slash=yM,e.ast.callbacks["path-literal"]=bM,e.ast.callbacks["template-expression"]=wM,e.ast.callbacks["template-expression-param-name"]=EM,{result:e.parse(SM,"path-template",t),ast:e.ast}},_M=t=>(e=>{try{return typeof e=="string"&&decodeURIComponent(e)!==e}catch{return!1}})(t)?t:encodeURIComponent(t).replace(/%5B/g,"[").replace(/%5D/g,"]"),OM=["slash","path-literal","template-expression-param-name"],x1=(t,e,r={})=>{const n={encoder:_M,...r},a=xM(t);if(!a.result.success)return t;const o=[];return a.ast.translate(o),o.filter((([c])=>OM.includes(c))).map((([c,d])=>c==="template-expression-param-name"?Object.prototype.hasOwnProperty.call(e,d)?n.encoder(e[d],d):`{${d}}`:d)).join("")};new tx,new ns;const kM={body:function({req:e,value:r}){r!==void 0&&(e.body=r)},header:function({req:e,parameter:r,value:n}){e.headers=e.headers||{},n!==void 0&&(e.headers[r.name]=n)},query:function({req:e,value:r,parameter:n}){if(e.query=e.query||{},r===!1&&n.type==="boolean"&&(r="false"),r===0&&["number","integer"].indexOf(n.type)>-1&&(r="0"),r)e.query[n.name]={collectionFormat:n.collectionFormat,value:r};else if(n.allowEmptyValue&&r!==void 0){const a=n.name;e.query[a]=e.query[a]||{},e.query[a].allowEmptyValue=!0}},path:function({req:e,value:r,parameter:n,baseURL:a}){if(r!==void 0){const o=e.url.replace(a,""),p=x1(o,{[n.name]:r});e.url=a+p}},formData:function({req:e,value:r,parameter:n}){if(r===!1&&n.type==="boolean"&&(r="false"),r===0&&["number","integer"].indexOf(n.type)>-1&&(r="0"),r)e.form=e.form||{},e.form[n.name]={collectionFormat:n.collectionFormat,value:r};else if(n.allowEmptyValue&&r!==void 0){e.form=e.form||{};const a=n.name;e.form[a]=e.form[a]||{},e.form[a].allowEmptyValue=!0}}};function bv(t,e){return Yn()(e).call(e,"application/json")?typeof t=="string"?t:(Array.isArray(t)&&(t=t.map((r=>{try{return JSON.parse(r)}catch{return r}}))),JSON.stringify(t)):String(t)}function Pm(){this.grammarObject="grammarObject",this.rules=[],this.rules[0]={name:"lenient-cookie-string",lower:"lenient-cookie-string",index:0,isBkr:!1},this.rules[1]={name:"lenient-cookie-entry",lower:"lenient-cookie-entry",index:1,isBkr:!1},this.rules[2]={name:"lenient-cookie-pair",lower:"lenient-cookie-pair",index:2,isBkr:!1},this.rules[3]={name:"lenient-cookie-pair-invalid",lower:"lenient-cookie-pair-invalid",index:3,isBkr:!1},this.rules[4]={name:"lenient-cookie-name",lower:"lenient-cookie-name",index:4,isBkr:!1},this.rules[5]={name:"lenient-cookie-value",lower:"lenient-cookie-value",index:5,isBkr:!1},this.rules[6]={name:"lenient-quoted-value",lower:"lenient-quoted-value",index:6,isBkr:!1},this.rules[7]={name:"lenient-quoted-char",lower:"lenient-quoted-char",index:7,isBkr:!1},this.rules[8]={name:"lenient-cookie-octet",lower:"lenient-cookie-octet",index:8,isBkr:!1},this.rules[9]={name:"cookie-string",lower:"cookie-string",index:9,isBkr:!1},this.rules[10]={name:"cookie-pair",lower:"cookie-pair",index:10,isBkr:!1},this.rules[11]={name:"cookie-name",lower:"cookie-name",index:11,isBkr:!1},this.rules[12]={name:"cookie-value",lower:"cookie-value",index:12,isBkr:!1},this.rules[13]={name:"cookie-octet",lower:"cookie-octet",index:13,isBkr:!1},this.rules[14]={name:"OWS",lower:"ows",index:14,isBkr:!1},this.rules[15]={name:"token",lower:"token",index:15,isBkr:!1},this.rules[16]={name:"tchar",lower:"tchar",index:16,isBkr:!1},this.rules[17]={name:"CHAR",lower:"char",index:17,isBkr:!1},this.rules[18]={name:"CTL",lower:"ctl",index:18,isBkr:!1},this.rules[19]={name:"separators",lower:"separators",index:19,isBkr:!1},this.rules[20]={name:"SP",lower:"sp",index:20,isBkr:!1},this.rules[21]={name:"HT",lower:"ht",index:21,isBkr:!1},this.rules[22]={name:"ALPHA",lower:"alpha",index:22,isBkr:!1},this.rules[23]={name:"DIGIT",lower:"digit",index:23,isBkr:!1},this.rules[24]={name:"DQUOTE",lower:"dquote",index:24,isBkr:!1},this.rules[25]={name:"WSP",lower:"wsp",index:25,isBkr:!1},this.rules[26]={name:"HTAB",lower:"htab",index:26,isBkr:!1},this.rules[27]={name:"CRLF",lower:"crlf",index:27,isBkr:!1},this.rules[28]={name:"CR",lower:"cr",index:28,isBkr:!1},this.rules[29]={name:"LF",lower:"lf",index:29,isBkr:!1},this.udts=[],this.rules[0].opcodes=[],this.rules[0].opcodes[0]={type:2,children:[1,2]},this.rules[0].opcodes[1]={type:4,index:1},this.rules[0].opcodes[2]={type:3,min:0,max:1/0},this.rules[0].opcodes[3]={type:2,children:[4,5,6]},this.rules[0].opcodes[4]={type:7,string:[59]},this.rules[0].opcodes[5]={type:4,index:14},this.rules[0].opcodes[6]={type:4,index:1},this.rules[1].opcodes=[],this.rules[1].opcodes[0]={type:1,children:[1,2]},this.rules[1].opcodes[1]={type:4,index:2},this.rules[1].opcodes[2]={type:4,index:3},this.rules[2].opcodes=[],this.rules[2].opcodes[0]={type:2,children:[1,2,3,4,5,6,7]},this.rules[2].opcodes[1]={type:4,index:14},this.rules[2].opcodes[2]={type:4,index:4},this.rules[2].opcodes[3]={type:4,index:14},this.rules[2].opcodes[4]={type:7,string:[61]},this.rules[2].opcodes[5]={type:4,index:14},this.rules[2].opcodes[6]={type:4,index:5},this.rules[2].opcodes[7]={type:4,index:14},this.rules[3].opcodes=[],this.rules[3].opcodes[0]={type:2,children:[1,2,4]},this.rules[3].opcodes[1]={type:4,index:14},this.rules[3].opcodes[2]={type:3,min:1,max:1/0},this.rules[3].opcodes[3]={type:4,index:16},this.rules[3].opcodes[4]={type:4,index:14},this.rules[4].opcodes=[],this.rules[4].opcodes[0]={type:3,min:1,max:1/0},this.rules[4].opcodes[1]={type:1,children:[2,3,4]},this.rules[4].opcodes[2]={type:5,min:33,max:58},this.rules[4].opcodes[3]={type:6,string:[60]},this.rules[4].opcodes[4]={type:5,min:62,max:126},this.rules[5].opcodes=[],this.rules[5].opcodes[0]={type:1,children:[1,6]},this.rules[5].opcodes[1]={type:2,children:[2,3]},this.rules[5].opcodes[2]={type:4,index:6},this.rules[5].opcodes[3]={type:3,min:0,max:1},this.rules[5].opcodes[4]={type:3,min:0,max:1/0},this.rules[5].opcodes[5]={type:4,index:8},this.rules[5].opcodes[6]={type:3,min:0,max:1/0},this.rules[5].opcodes[7]={type:4,index:8},this.rules[6].opcodes=[],this.rules[6].opcodes[0]={type:2,children:[1,2,4]},this.rules[6].opcodes[1]={type:4,index:24},this.rules[6].opcodes[2]={type:3,min:0,max:1/0},this.rules[6].opcodes[3]={type:4,index:7},this.rules[6].opcodes[4]={type:4,index:24},this.rules[7].opcodes=[],this.rules[7].opcodes[0]={type:1,children:[1,2]},this.rules[7].opcodes[1]={type:5,min:32,max:33},this.rules[7].opcodes[2]={type:5,min:35,max:126},this.rules[8].opcodes=[],this.rules[8].opcodes[0]={type:1,children:[1,2,3]},this.rules[8].opcodes[1]={type:5,min:33,max:43},this.rules[8].opcodes[2]={type:5,min:45,max:58},this.rules[8].opcodes[3]={type:5,min:60,max:126},this.rules[9].opcodes=[],this.rules[9].opcodes[0]={type:2,children:[1,2]},this.rules[9].opcodes[1]={type:4,index:10},this.rules[9].opcodes[2]={type:3,min:0,max:1/0},this.rules[9].opcodes[3]={type:2,children:[4,5,6]},this.rules[9].opcodes[4]={type:7,string:[59]},this.rules[9].opcodes[5]={type:4,index:20},this.rules[9].opcodes[6]={type:4,index:10},this.rules[10].opcodes=[],this.rules[10].opcodes[0]={type:2,children:[1,2,3]},this.rules[10].opcodes[1]={type:4,index:11},this.rules[10].opcodes[2]={type:7,string:[61]},this.rules[10].opcodes[3]={type:4,index:12},this.rules[11].opcodes=[],this.rules[11].opcodes[0]={type:4,index:15},this.rules[12].opcodes=[],this.rules[12].opcodes[0]={type:1,children:[1,6]},this.rules[12].opcodes[1]={type:2,children:[2,3,5]},this.rules[12].opcodes[2]={type:4,index:24},this.rules[12].opcodes[3]={type:3,min:0,max:1/0},this.rules[12].opcodes[4]={type:4,index:13},this.rules[12].opcodes[5]={type:4,index:24},this.rules[12].opcodes[6]={type:3,min:0,max:1/0},this.rules[12].opcodes[7]={type:4,index:13},this.rules[13].opcodes=[],this.rules[13].opcodes[0]={type:1,children:[1,2,3,4,5]},this.rules[13].opcodes[1]={type:6,string:[33]},this.rules[13].opcodes[2]={type:5,min:35,max:43},this.rules[13].opcodes[3]={type:5,min:45,max:58},this.rules[13].opcodes[4]={type:5,min:60,max:91},this.rules[13].opcodes[5]={type:5,min:93,max:126},this.rules[14].opcodes=[],this.rules[14].opcodes[0]={type:3,min:0,max:1/0},this.rules[14].opcodes[1]={type:2,children:[2,4]},this.rules[14].opcodes[2]={type:3,min:0,max:1},this.rules[14].opcodes[3]={type:4,index:27},this.rules[14].opcodes[4]={type:4,index:25},this.rules[15].opcodes=[],this.rules[15].opcodes[0]={type:3,min:1,max:1/0},this.rules[15].opcodes[1]={type:4,index:16},this.rules[16].opcodes=[],this.rules[16].opcodes[0]={type:1,children:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]},this.rules[16].opcodes[1]={type:7,string:[33]},this.rules[16].opcodes[2]={type:7,string:[35]},this.rules[16].opcodes[3]={type:7,string:[36]},this.rules[16].opcodes[4]={type:7,string:[37]},this.rules[16].opcodes[5]={type:7,string:[38]},this.rules[16].opcodes[6]={type:7,string:[39]},this.rules[16].opcodes[7]={type:7,string:[42]},this.rules[16].opcodes[8]={type:7,string:[43]},this.rules[16].opcodes[9]={type:7,string:[45]},this.rules[16].opcodes[10]={type:7,string:[46]},this.rules[16].opcodes[11]={type:7,string:[94]},this.rules[16].opcodes[12]={type:7,string:[95]},this.rules[16].opcodes[13]={type:7,string:[96]},this.rules[16].opcodes[14]={type:7,string:[124]},this.rules[16].opcodes[15]={type:7,string:[126]},this.rules[16].opcodes[16]={type:4,index:23},this.rules[16].opcodes[17]={type:4,index:22},this.rules[17].opcodes=[],this.rules[17].opcodes[0]={type:5,min:1,max:127},this.rules[18].opcodes=[],this.rules[18].opcodes[0]={type:1,children:[1,2]},this.rules[18].opcodes[1]={type:5,min:0,max:31},this.rules[18].opcodes[2]={type:6,string:[127]},this.rules[19].opcodes=[],this.rules[19].opcodes[0]={type:1,children:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]},this.rules[19].opcodes[1]={type:7,string:[40]},this.rules[19].opcodes[2]={type:7,string:[41]},this.rules[19].opcodes[3]={type:7,string:[60]},this.rules[19].opcodes[4]={type:7,string:[62]},this.rules[19].opcodes[5]={type:7,string:[64]},this.rules[19].opcodes[6]={type:7,string:[44]},this.rules[19].opcodes[7]={type:7,string:[59]},this.rules[19].opcodes[8]={type:7,string:[58]},this.rules[19].opcodes[9]={type:7,string:[92]},this.rules[19].opcodes[10]={type:6,string:[34]},this.rules[19].opcodes[11]={type:7,string:[47]},this.rules[19].opcodes[12]={type:7,string:[91]},this.rules[19].opcodes[13]={type:7,string:[93]},this.rules[19].opcodes[14]={type:7,string:[63]},this.rules[19].opcodes[15]={type:7,string:[61]},this.rules[19].opcodes[16]={type:7,string:[123]},this.rules[19].opcodes[17]={type:7,string:[125]},this.rules[19].opcodes[18]={type:4,index:20},this.rules[19].opcodes[19]={type:4,index:21},this.rules[20].opcodes=[],this.rules[20].opcodes[0]={type:6,string:[32]},this.rules[21].opcodes=[],this.rules[21].opcodes[0]={type:6,string:[9]},this.rules[22].opcodes=[],this.rules[22].opcodes[0]={type:1,children:[1,2]},this.rules[22].opcodes[1]={type:5,min:65,max:90},this.rules[22].opcodes[2]={type:5,min:97,max:122},this.rules[23].opcodes=[],this.rules[23].opcodes[0]={type:5,min:48,max:57},this.rules[24].opcodes=[],this.rules[24].opcodes[0]={type:6,string:[34]},this.rules[25].opcodes=[],this.rules[25].opcodes[0]={type:1,children:[1,2]},this.rules[25].opcodes[1]={type:4,index:20},this.rules[25].opcodes[2]={type:4,index:26},this.rules[26].opcodes=[],this.rules[26].opcodes[0]={type:6,string:[9]},this.rules[27].opcodes=[],this.rules[27].opcodes[0]={type:2,children:[1,2]},this.rules[27].opcodes[1]={type:4,index:28},this.rules[27].opcodes[2]={type:4,index:29},this.rules[28].opcodes=[],this.rules[28].opcodes[0]={type:6,string:[13]},this.rules[29].opcodes=[],this.rules[29].opcodes[0]={type:6,string:[10]},this.toString=function(){return`; Lenient version of https://datatracker.ietf.org/doc/html/rfc6265#section-4.2.1 +lenient-cookie-string = lenient-cookie-entry *( ";" OWS lenient-cookie-entry ) +lenient-cookie-entry = lenient-cookie-pair / lenient-cookie-pair-invalid +lenient-cookie-pair = OWS lenient-cookie-name OWS "=" OWS lenient-cookie-value OWS +lenient-cookie-pair-invalid = OWS 1*tchar OWS ; Allow for standalone entries like "fizz" to be ignored +lenient-cookie-name = 1*( %x21-3A / %x3C / %x3E-7E ) ; Allow all printable US-ASCII except "=" +lenient-cookie-value = lenient-quoted-value [ *lenient-cookie-octet ] / *lenient-cookie-octet +lenient-quoted-value = DQUOTE *( lenient-quoted-char ) DQUOTE +lenient-quoted-char = %x20-21 / %x23-7E ; Allow all printable US-ASCII except DQUOTE +lenient-cookie-octet = %x21-2B / %x2D-3A / %x3C-7E + ; Allow all printable characters except CTLs, semicolon and SP + +; https://datatracker.ietf.org/doc/html/rfc6265#section-4.2.1 +cookie-string = cookie-pair *( ";" SP cookie-pair ) + +; https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.1 +; https://www.rfc-editor.org/errata/eid5518 +cookie-pair = cookie-name "=" cookie-value +cookie-name = token +cookie-value = ( DQUOTE *cookie-octet DQUOTE ) / *cookie-octet + ; https://www.rfc-editor.org/errata/eid8242 +cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E + ; US-ASCII characters excluding CTLs, + ; whitespace, DQUOTE, comma, semicolon, + ; and backslash + +; https://datatracker.ietf.org/doc/html/rfc6265#section-2.2 +OWS = *( [ CRLF ] WSP ) ; "optional" whitespace + +; https://datatracker.ietf.org/doc/html/rfc9110#section-5.6.2 +token = 1*(tchar) +tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" + / "+" / "-" / "." / "^" / "_" / "\`" / "|" / "~" + / DIGIT / ALPHA + ; any VCHAR, except delimiters + +; https://datatracker.ietf.org/doc/html/rfc2616#section-2.2 +CHAR = %x01-7F ; any US-ASCII character (octets 0 - 127) +CTL = %x00-1F / %x7F ; any US-ASCII control character +separators = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\\" / %x22 / "/" / "[" / "]" / "?" / "=" / "{" / "}" / SP / HT +SP = %x20 ; US-ASCII SP, space (32) +HT = %x09 ; US-ASCII HT, horizontal-tab (9) + +; https://datatracker.ietf.org/doc/html/rfc5234#appendix-B.1 +ALPHA = %x41-5A / %x61-7A ; A-Z / a-z +DIGIT = %x30-39 ; 0-9 +DQUOTE = %x22 ; " (Double Quote) +WSP = SP / HTAB ; white space +HTAB = %x09 ; horizontal tab +CRLF = CR LF ; Internet standard newline +CR = %x0D ; carriage return +LF = %x0A ; linefeed +`}}new Pm;const AM=t=>{if(typeof t!="string"||[...t].length!==1)throw new TypeError("Input must be a single character string.");const e=t.codePointAt(0);return e<=127?`%${e.toString(16).toUpperCase().padStart(2,"0")}`:encodeURIComponent(t)},wv=t=>t.length>=2&&t.startsWith('"')&&t.endsWith('"'),rx=t=>wv(t)?t.slice(1,-1):t,nx=t=>`"${t}"`,ax=t=>t,CM=new ns,jM=new Pm,_1=(t,{strict:e=!0,quoted:r=null}={})=>{try{const n=e?"cookie-value":"lenient-cookie-value",a=CM.parse(jM,n,t);return typeof r=="boolean"?a.success&&r===wv(t):a.success}catch{return!1}},ox=t=>{const e=new TextEncoder().encode(t).reduce(((r,n)=>r+String.fromCharCode(n)),"");return btoa(e)},PM=(t,e=ox)=>{const r=String(t);if(_1(r))return r;const n=wv(r),a=e(n?rx(r):r);return n?nx(a):a},NM=t=>(e=>e.replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,""))(ox(t)),TM=t=>PM(t,NM),IM=new ns,MM=new Pm,ix=(t,{strict:e=!0}={})=>{try{const r=e?"cookie-name":"lenient-cookie-name";return IM.parse(MM,r,t).success}catch{return!1}},RM=t=>{if(!ix(t))throw new TypeError(`Invalid cookie name: ${t}`)},sx=t=>{if(!_1(t))throw new TypeError(`Invalid cookie value: ${t}`)},lx={encoders:{name:ax,value:TM},validators:{name:RM,value:sx}},DM=(t,e,r={})=>{const n={...r,encoders:{...lx.encoders,...r.encoders},validators:{...lx.validators,...r.validators}},a=n.encoders.name(t),o=n.encoders.value(e);return n.validators.name(a),n.validators.value(o),`${a}=${o}`},FM=(t,e={})=>(Array.isArray(t)?t:typeof t=="object"&&t!==null?Object.entries(t):[]).map((([r,n])=>DM(r,n,e))).join("; "),LM=new ns,$M=new Pm,BM=t=>{const e=String(t);if(_1(e))return e;const r=wv(e),n=r?rx(e):e;let a="";for(const o of n)a+=LM.parse($M,"cookie-octet",o).success?o:AM(o);return r?nx(a):a};new ns,new Pm;const qM=t=>{if(!ix(t,{strict:!1}))throw new TypeError(`Invalid cookie name: ${t}`)},UM=t=>BM(t).replace(/[=&]/gu,(e=>e==="="?"%3D":"%26")),O1=(t,e={})=>FM(t,Py({encoders:{name:ax,value:UM},validators:{name:qM,value:sx}},e));function zM({req:t,value:e,parameter:r,baseURL:n}){const{name:a,style:o,explode:p,content:c}=r;if(e===void 0)return;const d=t.url.replace(n,"");let E;if(c){const x=Object.keys(c)[0];E=x1(d,{[a]:e},{encoder:k=>JE(bv(k,x))})}else E=x1(d,{[a]:e},{encoder:x=>q0({key:r.name,value:x,style:o||"simple",explode:p??!1,escape:"reserved"})});t.url=n+E}function VM({req:t,value:e,parameter:r}){if(t.query=t.query||{},e!==void 0&&r.content){const n=bv(e,Object.keys(r.content)[0]);if(n)t.query[r.name]=n;else if(r.allowEmptyValue){const a=r.name;t.query[a]=t.query[a]||{},t.query[a].allowEmptyValue=!0}}else if(e===!1&&(e="false"),e===0&&(e="0"),e){const{style:n,explode:a,allowReserved:o}=r;t.query[r.name]={value:e,serializationOption:{style:n,explode:a,allowReserved:o}}}else if(r.allowEmptyValue&&e!==void 0){const n=r.name;t.query[n]=t.query[n]||{},t.query[n].allowEmptyValue=!0}}const JM=["accept","authorization","content-type"];function WM({req:t,parameter:e,value:r}){if(t.headers=t.headers||{},!(JM.indexOf(e.name.toLowerCase())>-1))if(r!==void 0&&e.content){const n=Object.keys(e.content)[0];t.headers[e.name]=bv(r,n)}else r===void 0||Array.isArray(r)&&r.length===0||(t.headers[e.name]=q0({key:e.name,value:r,style:e.style||"simple",explode:e.explode!==void 0&&e.explode,escape:!1}))}function HM({req:t,parameter:e,value:r}){const{name:n}=e;if(t.headers=t.headers||{},r!==void 0&&e.content){const a=bv(r,Object.keys(e.content)[0]);t.headers.Cookie=O1({[n]:a})}else if(r!==void 0&&(!Array.isArray(r)||r.length!==0)){const a=q0({key:e.name,value:r,escape:!1,style:e.style||"form",explode:e.explode??!1}),o=Array.isArray(r)&&e.explode?`${n}=${a}`:a;t.headers.Cookie=O1({[n]:o})}}const KM=typeof globalThis<"u"?globalThis:typeof self<"u"?self:window,{btoa:GM}=KM,cx=GM;function YM(t,e){const{operation:r,requestBody:n,securities:a,spec:o,attachContentTypeForEmptyPayload:p}=t;let{requestContentType:c}=t;e=(function({request:P,securities:R={},operation:L={},spec:V}){const Q={...P},{authorized:J={}}=R,re=L.security||V.security||[],me=J&&!!Object.keys(J).length,le=V?.components?.securitySchemes||{};return Q.headers=Q.headers||{},Q.query=Q.query||{},!Object.keys(R).length||!me||!re||Array.isArray(L.security)&&!L.security.length?P:(re.forEach((ve=>{Object.keys(ve).forEach((be=>{const ce=J[be],_e=le[be];if(!ce)return;const Ae=ce.value||ce,{type:xe}=_e;if(ce){if(xe==="apiKey")_e.in==="query"&&(Q.query[_e.name]=Ae),_e.in==="header"&&(Q.headers[_e.name]=Ae),_e.in==="cookie"&&(Q.cookies[_e.name]=Ae);else if(xe==="http"){if(/^basic$/i.test(_e.scheme)){const Oe=Ae.username||"",je=Ae.password||"",G=cx(`${Oe}:${je}`);Q.headers.Authorization=`Basic ${G}`}/^bearer$/i.test(_e.scheme)&&(Q.headers.Authorization=`Bearer ${Ae}`)}else if(xe==="oauth2"||xe==="openIdConnect"){const Oe=ce.token||{},je=Oe[_e["x-tokenName"]||"access_token"];let G=Oe.token_type;G&&G.toLowerCase()!=="bearer"||(G="Bearer"),Q.headers.Authorization=`${G} ${je}`}}}))})),Q)})({request:e,securities:a,operation:r,spec:o});const d=r.requestBody||{},E=Object.keys(d.content||{}),x=c&&E.indexOf(c)>-1;if(n||p){if(c&&x)e.headers["Content-Type"]=c;else if(!c){const k=E[0];k&&(e.headers["Content-Type"]=k,c=k)}}else c&&x&&(e.headers["Content-Type"]=c);if(!t.responseContentType&&r.responses){const k=Object.entries(r.responses).filter((([P,R])=>{const L=parseInt(P,10);return L>=200&&L<300&&Ls(R.content)})).reduce(((P,[,R])=>P.concat(Object.keys(R.content))),[]);k.length>0&&(e.headers.accept=k.join(", "))}if(n)if(c){if(E.indexOf(c)>-1)if(c==="application/x-www-form-urlencoded"||c==="multipart/form-data")if(typeof n=="object"){const k=d.content[c]?.encoding??{};e.form={},Object.keys(n).forEach((P=>{let R;try{R=JSON.parse(n[P])}catch{R=n[P]}e.form[P]={value:R,encoding:k[P]||{}}}))}else if(typeof n=="string"){const k=d.content[c]?.encoding??{};try{e.form={};const P=JSON.parse(n);Object.entries(P).forEach((([R,L])=>{e.form[R]={value:L,encoding:k[R]||{}}}))}catch{e.form=n}}else e.form=n;else e.body=n}else e.body=n;return e}function XM(t,e){const{spec:r,operation:n,securities:a,requestContentType:o,responseContentType:p,attachContentTypeForEmptyPayload:c}=t;if(e=(function({request:E,securities:x={},operation:k={},spec:P}){const R={...E},{authorized:L={},specSecurity:V=[]}=x,Q=k.security||V,J=L&&!!Object.keys(L).length,re=P.securityDefinitions;return R.headers=R.headers||{},R.query=R.query||{},!Object.keys(x).length||!J||!Q||Array.isArray(k.security)&&!k.security.length?E:(Q.forEach((me=>{Object.keys(me).forEach((le=>{const ve=L[le];if(!ve)return;const{token:be}=ve,ce=ve.value||ve,_e=re[le],{type:Ae}=_e,xe=_e["x-tokenName"]||"access_token",Oe=be&&be[xe];let je=be&&be.token_type;if(ve)if(Ae==="apiKey"){const G=_e.in==="query"?"query":"headers";R[G]=R[G]||{},R[G][_e.name]=ce}else if(Ae==="basic")if(ce.header)R.headers.authorization=ce.header;else{const G=ce.username||"",de=ce.password||"";ce.base64=cx(`${G}:${de}`),R.headers.authorization=`Basic ${ce.base64}`}else Ae==="oauth2"&&Oe&&(je=je&&je.toLowerCase()!=="bearer"?je:"Bearer",R.headers.authorization=`${je} ${Oe}`)}))})),R)})({request:e,securities:a,operation:n,spec:r}),e.body||e.form||c)o?e.headers["Content-Type"]=o:Array.isArray(n.consumes)?[e.headers["Content-Type"]]=n.consumes:Array.isArray(r.consumes)?[e.headers["Content-Type"]]=r.consumes:n.parameters&&n.parameters.filter((d=>d.type==="file")).length?e.headers["Content-Type"]="multipart/form-data":n.parameters&&n.parameters.filter((d=>d.in==="formData")).length&&(e.headers["Content-Type"]="application/x-www-form-urlencoded");else if(o){const d=n.parameters&&n.parameters.filter((x=>x.in==="body")).length>0,E=n.parameters&&n.parameters.filter((x=>x.in==="formData")).length>0;(d||E)&&(e.headers["Content-Type"]=o)}return!p&&Array.isArray(n.produces)&&n.produces.length>0&&(e.headers.accept=n.produces.join(", ")),e}function QM(t,e){return(function(n,a,o){if(!n||typeof n!="object"||!n.paths||typeof n.paths!="object")return null;const{paths:p}=n,c=rS(n);for(const d in p){for(const x in p[d]){if(x.toUpperCase()==="PARAMETERS"||x==="additionalOperations")continue;const k=p[d][x];if(!k||typeof k!="object")continue;const P={spec:n,pathName:d,method:x.toUpperCase(),operation:k};if(a(P))return P}const{additionalOperations:E}=p[d];if(c&&E&&typeof E=="object")for(const x of Object.keys(E)){const k=E[x];if(!k||typeof k!="object")continue;const P={spec:n,pathName:d,method:x,operation:k};if(a(P))return P}}})(t,e)||null}function ux(t,e){return`${e.toLowerCase()}-${t}`}const px=t=>Array.isArray(t)?t:[],ZM=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/,Nm=(t,{recurse:e=!0,depth:r=1}={})=>{var n,a;if(Ls(t)){if(t.type==="object"||t.type==="array"||Array.isArray(t.type)&&(Yn()(n=t.type).call(n,"object")||Yn()(a=t.type).call(a,"array")))return t;if(!(r>NE)&&e){const o=Array.isArray(t.oneOf)?t.oneOf.find((c=>Nm(c,{recurse:e,depth:r+1}))):void 0;if(o)return o;const p=Array.isArray(t.anyOf)?t.anyOf.find((c=>Nm(c,{recurse:e,depth:r+1}))):void 0;if(p)return p}}},hx=({value:t,silentFail:e=!1})=>{try{const r=JSON.parse(t);if(Ls(r)||Array.isArray(r))return r;if(!e)throw new Error("Expected JSON serialized object or array")}catch{if(!e)throw new Error("Could not parse parameter value string as JSON Object or JSON Array")}return t},k1=t=>{try{return new URL(t)}catch{const e=new URL(t,Sy),r=String(t).startsWith("/")?e.pathname:e.pathname.substring(1);return{hash:e.hash,host:"",hostname:"",href:"",origin:"",password:"",pathname:r,port:"",protocol:"",search:e.search,searchParams:e.searchParams}}};class eR extends xr{}const tR={buildRequest:dx};function rR({http:t,fetch:e,spec:r,operationId:n,pathName:a,method:o,parameters:p,securities:c,...d}){const E=t||e||Mf;a&&o&&!n&&(n=ux(a,o));const x=tR.buildRequest({spec:r,operationId:n,parameters:p,securities:c,http:E,...d});return x.body&&(Ls(x.body)||Array.isArray(x.body))&&(x.body=JSON.stringify(x.body)),E(x)}function dx(t){const{spec:e,operationId:r,responseContentType:n,scheme:a,requestInterceptor:o,responseInterceptor:p,contextUrl:c,userFetch:d,server:E,serverVariables:x,http:k,signal:P,serverVariableEncoder:R}=t;let{parameters:L,parameterBuilders:V,baseURL:Q}=t;const J=nS(e);V||(V=J?ke:kM);let re={url:"",credentials:k&&k.withCredentials?"include":"same-origin",headers:{},cookies:{}};P&&(re.signal=P),o&&(re.requestInterceptor=o),p&&(re.responseInterceptor=p),d&&(re.userFetch=d);const me=(function(Oe,je){return Oe&&Oe.paths?QM(Oe,(({pathName:G,method:de,operation:Ne})=>{if(!Ne||typeof Ne!="object")return!1;const Ye=Ne.operationId;return[Df(Ne,G,de),ux(G,de),Ye].some((ht=>ht&&ht===je))})):null})(e,r);if(!me)throw new eR(`Operation ${r} not found`);const{operation:le={},method:ve,pathName:be}=me;if(!(xe=>typeof xe=="string"&&ZM.test(xe))(ve))throw new Error("Invalid HTTP method");if(Q=Q??(function(Oe){return nS(Oe.spec)?(function({spec:de,pathName:Ne,method:Ye,operation:ht,server:Ct,contextUrl:_t,serverVariables:qt={},serverVariableEncoder:It}){let Ot,et=[],Dt="";const or=ht?.servers??de?.paths?.[Ne]?.[(Ye||"").toLowerCase()]?.servers,Ut=de?.paths?.[Ne]?.servers,pr=de?.servers;if(et=C1(or)?or:C1(Ut)?Ut:C1(pr)?pr:[YA],Ct&&(Ot=et.find((W=>W.url===Ct)),Ot&&(Dt=Ct)),Dt||([Ot]=et,Dt=Ot.url),dM(Dt,{strict:!0})){const W=Object.entries({...Ot.variables}).reduce((($e,[Je,Ft])=>($e[Je]=Ft.default,$e)),{});Dt=gM(Dt,{...W,...qt},{encoder:typeof It=="function"?It:T2})}return(function($e="",Je=""){const Ft=k1($e&&Je?go(Je,$e):$e),it=k1(Je),bt=A1(Ft.protocol)||A1(it.protocol),ut=Ft.host||it.host,Xt=Ft.pathname;let zt;return zt=bt&&ut?`${bt}://${ut+Xt}`:Xt,zt[zt.length-1]==="/"?zt.slice(0,-1):zt})(Dt,_t)})(Oe):(function({spec:de,scheme:Ne,contextUrl:Ye=""}){const ht=k1(Ye),Ct=Array.isArray(de.schemes)?de.schemes[0]:null,_t=Ne||Ct||A1(ht.protocol)||"http",qt=de.host||ht.host||"",It=de.basePath||"";let Ot;return Ot=qt?`${_t}://${qt+It}`:It,Ot[Ot.length-1]==="/"?Ot.slice(0,-1):Ot})(Oe)})({spec:e,scheme:a,contextUrl:c,server:E,serverVariables:x,pathName:be,method:ve,operation:le,serverVariableEncoder:R}),re.url+=Q,!r)return delete re.cookies,re;re.url+=be,re.method=ve,L=L||{};const ce=e.paths[be]||{};n&&(re.headers.accept=n);const _e=(xe=>{const Oe={};xe.forEach((G=>{Oe[G.in]||(Oe[G.in]={}),Oe[G.in][G.name]=G}));const je=[];return Object.keys(Oe).forEach((G=>{Object.keys(Oe[G]).forEach((de=>{je.push(Oe[G][de])}))})),je})([].concat(px(le.parameters)).concat(px(ce.parameters)));_e.forEach((xe=>{const Oe=V[xe.in];let je;if(xe.in==="body"&&xe.schema&&xe.schema.properties&&(je=L),je=xe&&xe.name&&L[xe.name],je===void 0?je=xe&&xe.name&&L[`${xe.in}.${xe.name}`]:((G,de)=>de.filter((Ne=>Ne.name===G)))(xe.name,_e).length>1&&console.warn(`Parameter '${xe.name}' is ambiguous because the defined spec has more than one parameter with the name: '${xe.name}' and the passed-in parameter values did not define an 'in' value.`),je!==null){if(xe.default!==void 0&&je===void 0&&(je=xe.default),je===void 0&&xe.required&&!xe.allowEmptyValue)throw new Error(`Required parameter ${xe.name} is not provided`);J&&typeof je=="string"&&($f("type",xe.schema)&&typeof xe.schema.type=="string"&&Nm(xe.schema,{recurse:!1})?je=hx({value:je,silentFail:!1}):($f("type",xe.schema)&&Array.isArray(xe.schema.type)&&Nm(xe.schema,{recurse:!1})||!$f("type",xe.schema)&&Nm(xe.schema,{recurse:!0}))&&(je=hx({value:je,silentFail:!0}))),Oe&&Oe({req:re,parameter:xe,value:je,operation:le,spec:e,baseURL:Q})}}));const Ae={...t,operation:le};if(re=J?YM(Ae,re):XM(Ae,re),re.cookies&&Object.keys(re.cookies).length>0){const xe=O1(re.cookies);$y(re.headers.Cookie)?re.headers.Cookie+=`; ${xe}`:re.headers.Cookie=xe}return re.cookies&&delete re.cookies,U0(re)}const A1=t=>t?t.replace(/\W/g,""):null,C1=t=>Array.isArray(t)&&t.length>0,nR=t=>async(e,r,n={})=>(async(a,o,p={})=>{const{returnEntireTree:c,baseDoc:d,requestInterceptor:E,responseInterceptor:x,parameterMacro:k,modelPropertyMacro:P,useCircularStructures:R,strategies:L}=p,V={spec:a,pathDiscriminator:o,baseDoc:d,requestInterceptor:E,responseInterceptor:x,parameterMacro:k,modelPropertyMacro:P,useCircularStructures:R,strategies:L},Q=L.find((re=>re.match(a))).normalize(a),J=await sM({spec:Q,...V,allowMetaPatches:!0,skipNormalization:!Y0(a)});return!c&&Array.isArray(o)&&o.length&&(J.spec=o.reduce(((re,me)=>re?.[me]),J.spec)||null),J})(e,r,{...t,...n}),aR=((t,e)=>(...r)=>{t(...r);const n=e.getConfigs().withCredentials;e.fn.fetch.withCredentials=n});function fx({configs:t,getConfigs:e}){return{fn:{fetch:(r=Mf,n=t.preFetch,a=t.postFetch,a=a||(o=>o),n=n||(o=>o),o=>(typeof o=="string"&&(o={url:o}),o=U0(o),o=n(o),a(r(o)))),buildRequest:dx,execute:rR,resolve:Z2({strategies:[Q2,Q0,X0,G0]}),resolveSubtree:async(o,p,c={})=>{const d=e(),E={modelPropertyMacro:d.modelPropertyMacro,parameterMacro:d.parameterMacro,requestInterceptor:d.requestInterceptor,responseInterceptor:d.responseInterceptor,strategies:[Q2,Q0,X0,G0]};return nR(E)(o,p,c)},serializeRes:GE,opId:Df},statePlugins:{configs:{wrapActions:{loaded:aR}}}};var r,n,a}function mx(){return{fn:{shallowEqualKeys:fi,sanitizeUrl:Dr}}}var gx=Be(40961),oR=(Be(78418),u.version.startsWith("19")),iR=Symbol.for(oR?"react.transitional.element":"react.element"),sR=Symbol.for("react.portal"),lR=Symbol.for("react.fragment"),cR=Symbol.for("react.strict_mode"),uR=Symbol.for("react.profiler"),pR=Symbol.for("react.consumer"),hR=Symbol.for("react.context"),yx=Symbol.for("react.forward_ref"),dR=Symbol.for("react.suspense"),fR=Symbol.for("react.suspense_list"),j1=Symbol.for("react.memo"),mR=Symbol.for("react.lazy"),gR=yx,yR=j1;function vR(t){if(typeof t=="object"&&t!==null){const{$$typeof:e}=t;switch(e){case iR:switch(t=t.type){case lR:case uR:case cR:case dR:case fR:return t;default:switch(t=t&&t.$$typeof){case hR:case yx:case mR:case j1:case pR:return t;default:return e}}case sR:return e}}}function bR(t,e,r,n,{areStatesEqual:a,areOwnPropsEqual:o,areStatePropsEqual:p}){let c,d,E,x,k,P=!1;function R(L,V){const Q=!o(V,d),J=!a(L,c,V,d);return c=L,d=V,Q&&J?(function(){return E=t(c,d),e.dependsOnOwnProps&&(x=e(n,d)),k=r(E,x,d),k})():Q?(function(){return t.dependsOnOwnProps&&(E=t(c,d)),e.dependsOnOwnProps&&(x=e(n,d)),k=r(E,x,d),k})():J?(function(){const me=t(c,d),le=!p(me,E);return E=me,le&&(k=r(E,x,d)),k})():k}return function(V,Q){return P?R(V,Q):(function(re,me){return c=re,d=me,E=t(c,d),x=e(n,d),k=r(E,x,d),P=!0,k})(V,Q)}}function P1(t){return function(r){const n=t(r);function a(){return n}return a.dependsOnOwnProps=!1,a}}function vx(t){return t.dependsOnOwnProps?!!t.dependsOnOwnProps:t.length!==1}function bx(t,e){return function(n,{displayName:a}){const o=function(c,d){return o.dependsOnOwnProps?o.mapToProps(c,d):o.mapToProps(c,void 0)};return o.dependsOnOwnProps=!0,o.mapToProps=function(c,d){o.mapToProps=t,o.dependsOnOwnProps=vx(t);let E=o(c,d);return typeof E=="function"&&(o.mapToProps=E,o.dependsOnOwnProps=vx(E),E=o(c,d)),E},o}}function N1(t,e){return(r,n)=>{throw new Error(`Invalid value of type ${typeof t} for ${e} argument when connecting component ${n.wrappedComponentName}.`)}}function wR(t,e,r){return{...r,...t,...e}}function ER(t){t()}var wx={notify(){},get:()=>[]};function Ex(t,e){let r,n=wx,a=0,o=!1;function p(){E.onStateChange&&E.onStateChange()}function c(){a++,r||(r=e?e.addNestedSub(p):t.subscribe(p),n=(function(){let k=null,P=null;return{clear(){k=null,P=null},notify(){ER((()=>{let R=k;for(;R;)R.callback(),R=R.next}))},get(){const R=[];let L=k;for(;L;)R.push(L),L=L.next;return R},subscribe(R){let L=!0;const V=P={callback:R,next:null,prev:P};return V.prev?V.prev.next=V:k=V,function(){L&&k!==null&&(L=!1,V.next?V.next.prev=V.prev:P=V.prev,V.prev?V.prev.next=V.next:k=V.next)}}}})())}function d(){a--,r&&a===0&&(r(),r=void 0,n.clear(),n=wx)}const E={addNestedSub:function(k){c();const P=n.subscribe(k);let R=!1;return()=>{R||(R=!0,P(),d())}},notifyNestedSubs:function(){n.notify()},handleChangeWrapper:p,isSubscribed:function(){return o},trySubscribe:function(){o||(o=!0,c())},tryUnsubscribe:function(){o&&(o=!1,d())},getListeners:()=>n};return E}var SR=!(typeof window>"u"||window.document===void 0||window.document.createElement===void 0),xR=typeof navigator<"u"&&navigator.product==="ReactNative",Ev=SR||xR?u.useLayoutEffect:u.useEffect;function Sx(t,e){return t===e?t!==0||e!==0||1/t==1/e:t!=t&&e!=e}function T1(t,e){if(Sx(t,e))return!0;if(typeof t!="object"||t===null||typeof e!="object"||e===null)return!1;const r=Object.keys(t),n=Object.keys(e);if(r.length!==n.length)return!1;for(let a=0;a({})))})(t),P=(function(Q){return Q&&typeof Q=="object"?P1((J=>(function(me,le){const ve={};for(const be in me){const ce=me[be];typeof ce=="function"&&(ve[be]=(..._e)=>le(ce(..._e)))}return ve})(Q,J))):Q?typeof Q=="function"?bx(Q):N1(Q,"mapDispatchToProps"):P1((J=>({dispatch:J})))})(e),R=(function(Q){return Q?typeof Q=="function"?(function(re){return function(le,{displayName:ve,areMergedPropsEqual:be}){let ce,_e=!1;return function(xe,Oe,je){const G=re(xe,Oe,je);return _e?be(G,ce)||(ce=G):(_e=!0,ce=G),ce}}})(Q):N1(Q,"mergeProps"):()=>wR})(r),L=!!t;return V=>{const Q=V.displayName||V.name||"Component",J=`Connect(${Q})`,re={shouldHandleStateChanges:L,displayName:J,wrappedComponentName:Q,WrappedComponent:V,initMapStateToProps:k,initMapDispatchToProps:P,initMergeProps:R,areStatesEqual:a,areStatePropsEqual:p,areOwnPropsEqual:o,areMergedPropsEqual:c};function me(ve){const[be,ce,_e]=u.useMemo((()=>{const{reactReduxForwardedRef:$e,...Je}=ve;return[ve.context,$e,Je]}),[ve]),Ae=u.useMemo((()=>x),[be,x]),xe=u.useContext(Ae),Oe=!!ve.store&&!!ve.store.getState&&!!ve.store.dispatch,je=!!xe&&!!xe.store,G=Oe?ve.store:xe.store,de=je?xe.getServerState:G.getState,Ne=u.useMemo((()=>(function(Je,{initMapStateToProps:Ft,initMapDispatchToProps:it,initMergeProps:bt,...ut}){return bR(Ft(Je,ut),it(Je,ut),bt(Je,ut),Je,ut)})(G.dispatch,re)),[G]),[Ye,ht]=u.useMemo((()=>{if(!L)return MR;const $e=Ex(G,Oe?void 0:xe.subscription),Je=$e.notifyNestedSubs.bind($e);return[$e,Je]}),[G,Oe,xe]),Ct=u.useMemo((()=>Oe?xe:{...xe,subscription:Ye}),[Oe,xe,Ye]),_t=u.useRef(void 0),qt=u.useRef(_e),It=u.useRef(void 0),Ot=u.useRef(!1),et=u.useRef(!1),Dt=u.useRef(void 0);Ev((()=>(et.current=!0,()=>{et.current=!1})),[]);const or=u.useMemo((()=>()=>It.current&&_e===qt.current?It.current:Ne(G.getState(),_e)),[G,_e]),Ut=u.useMemo((()=>$e=>Ye?(function(Ft,it,bt,ut,Xt,zt,tr,Er,dr,qr,en){if(!Ft)return()=>{};let kn=!1,ln=null;const jt=()=>{if(kn||!Er.current)return;const We=it.getState();let Kr,Lr;try{Kr=ut(We,Xt.current)}catch(zr){Lr=zr,ln=zr}Lr||(ln=null),Kr===zt.current?tr.current||qr():(zt.current=Kr,dr.current=Kr,tr.current=!0,en())};return bt.onStateChange=jt,bt.trySubscribe(),jt(),()=>{if(kn=!0,bt.tryUnsubscribe(),bt.onStateChange=null,ln)throw ln}})(L,G,Ye,Ne,qt,_t,Ot,et,It,ht,$e):()=>{}),[Ye]);let pr;(function(Je,Ft,it){Ev((()=>Je(...Ft)),it)})(RR,[qt,_t,Ot,_e,It,ht]);try{pr=u.useSyncExternalStore(Ut,or,de?()=>Ne(de(),_e):or)}catch($e){throw Dt.current&&($e.message+=` +The error may be correlated with this previous error: +${Dt.current.stack} + +`),$e}Ev((()=>{Dt.current=void 0,It.current=void 0,_t.current=pr}));const W=u.useMemo((()=>u.createElement(V,{...pr,ref:ce})),[ce,V,pr]);return u.useMemo((()=>L?u.createElement(Ae.Provider,{value:Ct},W):W),[Ae,W,Ct])}const le=u.memo(me);if(le.WrappedComponent=V,le.displayName=me.displayName=J,d){const ve=u.forwardRef((function(ce,_e){return u.createElement(le,{...ce,reactReduxForwardedRef:_e})}));return ve.displayName=J,ve.WrappedComponent=V,I1(ve,V)}return I1(le,V)}}var LR=FR,$R=function(e){const{children:r,context:n,serverState:a,store:o}=e,p=u.useMemo((()=>{const E=Ex(o);return{store:o,subscription:E,getServerState:a?()=>a:void 0}}),[o,a]),c=u.useMemo((()=>o.getState()),[o]);Ev((()=>{const{subscription:E}=p;return E.onStateChange=E.notifyNestedSubs,E.trySubscribe(),c!==o.getState()&&E.notifyNestedSubs(),()=>{E.tryUnsubscribe(),E.onStateChange=void 0}}),[p,c]);const d=n||Ax;return u.createElement(d.Provider,{value:p},r)},BR=Be(83488),Cx=Be.n(BR);const qR=t=>e=>{const{fn:r}=t();class n extends u.Component{render(){return u.createElement(e,Yr()({},t(),this.props,this.context))}}return n.displayName=`WithSystem(${r.getDisplayName(e)})`,n},UR=(t,e)=>r=>{const{fn:n}=t();class a extends u.Component{render(){return u.createElement($R,{store:e},u.createElement(r,Yr()({},this.props,this.context)))}}return a.displayName=`WithRoot(${n.getDisplayName(r)})`,a},jx=(t,e,r)=>Wt(r?UR(t,r):Cx(),LR(((n,a)=>{const o={...a,...t()};return(e.prototype?.mapStateToProps||(c=>({state:c})))(n,o)})),qR(t))(e),Px=(t,e,r,n)=>{for(const a in e){const o=e[a];typeof o=="function"&&o(r[a],n[a],t())}},zR=(t,e,r)=>(n,a)=>{const{fn:o}=t(),p=r(n,"root");class c extends u.Component{constructor(E,x){super(E,x),Px(t,a,E,{})}UNSAFE_componentWillReceiveProps(E){Px(t,a,E,this.props)}render(){const E=Ha()(this.props,a?Object.keys(a):[]);return u.createElement(p,E)}}return c.displayName=`WithMappedContainer(${o.getDisplayName(p)})`,c},VR=(t,e,r,n)=>a=>{const o=r(t,e,n)("App","root"),{createRoot:p}=gx;p(a).render(u.createElement(o,null))},M1=(t,e,r)=>(n,a,o={})=>{if(typeof n!="string")throw new TypeError("Need a string, to fetch a component. Was given a "+typeof n);const p=r(n);return p?a?a==="root"?jx(t,p,e()):jx(t,p):p:(o.failSilently||t().log.warn("Could not find component:",n),null)},JR=t=>t.displayName||t.name||"Component",Nx=({getComponents:t,getStore:e,getSystem:r})=>{const n=(a=M1(r,e,t),fs(a,((...p)=>JSON.stringify(p))));var a;const o=(p=>gu(p,((...c)=>c)))(zR(r,0,n));return{rootInjects:{getComponent:n,makeMappedContainer:o,render:VR(r,e,M1,t)},fn:{getDisplayName:JR}}},Tx=({React:t,getSystem:e,getStore:r,getComponents:n})=>{const a={},o=parseInt(t?.version,10);return o>=16&&o<18&&(a.render=((p,c,d,E)=>x=>{const k=d(p,c,E)("App","root");gx.render(u.createElement(k,null),x)})(e,r,M1,n)),{rootInjects:a}};function Ix(t){let{fn:e}=t;const r={download:a=>({errActions:o,specSelectors:p,specActions:c,getConfigs:d})=>{let{fetch:E}=e;const x=d();function k(P){if(P instanceof Error||P.status>=400)return c.updateLoadingStatus("failed"),o.newThrownErr(Object.assign(new Error((P.message||P.statusText)+" "+a),{source:"fetch"})),void(!P.status&&P instanceof Error&&(function(){try{let L;if("URL"in Qe?L=new URL(a):(L=document.createElement("a"),L.href=a),L.protocol!=="https:"&&Qe.location.protocol==="https:"){const V=Object.assign(new Error(`Possible mixed-content issue? The page was loaded over https:// but a ${L.protocol}// URL was specified. Check that you are not attempting to load mixed content.`),{source:"fetch"});return void o.newThrownErr(V)}if(L.origin!==Qe.location.origin){const V=Object.assign(new Error(`Possible cross-origin (CORS) issue? The URL origin (${L.origin}) does not match the page (${Qe.location.origin}). Check the server returns the correct 'Access-Control-Allow-*' headers.`),{source:"fetch"});o.newThrownErr(V)}}catch{return}})());c.updateLoadingStatus("success"),c.updateSpec(P.text),p.url()!==a&&c.updateUrl(a)}a=a||p.url(),c.updateLoadingStatus("loading"),o.clear({source:"fetch"}),E({url:a,loadSpec:!0,requestInterceptor:x.requestInterceptor||(P=>P),responseInterceptor:x.responseInterceptor||(P=>P),credentials:"same-origin",headers:{Accept:"application/json,*/*"}}).then(k,k)},updateLoadingStatus:a=>{let o=[null,"loading","failed","success","failedConfig"];return o.indexOf(a)===-1&&console.error(`Error: ${a} is not one of ${JSON.stringify(o)}`),{type:"spec_update_loading_status",payload:a}}};let n={loadingStatus:yr((a=>a||(0,H.Map)()),(a=>a.get("loadingStatus")||null))};return{statePlugins:{spec:{actions:r,reducers:{spec_update_loading_status:(a,o)=>typeof o.payload=="string"?a.set("loadingStatus",o.payload):a},selectors:n}}}}function R1(t,e){(e==null||e>t.length)&&(e=t.length);for(var r=0,n=Array(e);r1&&arguments[1]!==void 0?arguments[1]:{},r=arguments.length>2?arguments[2]:void 0;return(function(a){if(a.length===0||a.length===1)return a;var o=a.join(".");return L1[o]||(L1[o]=(function(c){var d=c.length;return d===0||d===1?c:d===2?[c[0],c[1],"".concat(c[0],".").concat(c[1]),"".concat(c[1],".").concat(c[0])]:d===3?[c[0],c[1],c[2],"".concat(c[0],".").concat(c[1]),"".concat(c[0],".").concat(c[2]),"".concat(c[1],".").concat(c[0]),"".concat(c[1],".").concat(c[2]),"".concat(c[2],".").concat(c[0]),"".concat(c[2],".").concat(c[1]),"".concat(c[0],".").concat(c[1],".").concat(c[2]),"".concat(c[0],".").concat(c[2],".").concat(c[1]),"".concat(c[1],".").concat(c[0],".").concat(c[2]),"".concat(c[1],".").concat(c[2],".").concat(c[0]),"".concat(c[2],".").concat(c[0],".").concat(c[1]),"".concat(c[2],".").concat(c[1],".").concat(c[0])]:d>=4?[c[0],c[1],c[2],c[3],"".concat(c[0],".").concat(c[1]),"".concat(c[0],".").concat(c[2]),"".concat(c[0],".").concat(c[3]),"".concat(c[1],".").concat(c[0]),"".concat(c[1],".").concat(c[2]),"".concat(c[1],".").concat(c[3]),"".concat(c[2],".").concat(c[0]),"".concat(c[2],".").concat(c[1]),"".concat(c[2],".").concat(c[3]),"".concat(c[3],".").concat(c[0]),"".concat(c[3],".").concat(c[1]),"".concat(c[3],".").concat(c[2]),"".concat(c[0],".").concat(c[1],".").concat(c[2]),"".concat(c[0],".").concat(c[1],".").concat(c[3]),"".concat(c[0],".").concat(c[2],".").concat(c[1]),"".concat(c[0],".").concat(c[2],".").concat(c[3]),"".concat(c[0],".").concat(c[3],".").concat(c[1]),"".concat(c[0],".").concat(c[3],".").concat(c[2]),"".concat(c[1],".").concat(c[0],".").concat(c[2]),"".concat(c[1],".").concat(c[0],".").concat(c[3]),"".concat(c[1],".").concat(c[2],".").concat(c[0]),"".concat(c[1],".").concat(c[2],".").concat(c[3]),"".concat(c[1],".").concat(c[3],".").concat(c[0]),"".concat(c[1],".").concat(c[3],".").concat(c[2]),"".concat(c[2],".").concat(c[0],".").concat(c[1]),"".concat(c[2],".").concat(c[0],".").concat(c[3]),"".concat(c[2],".").concat(c[1],".").concat(c[0]),"".concat(c[2],".").concat(c[1],".").concat(c[3]),"".concat(c[2],".").concat(c[3],".").concat(c[0]),"".concat(c[2],".").concat(c[3],".").concat(c[1]),"".concat(c[3],".").concat(c[0],".").concat(c[1]),"".concat(c[3],".").concat(c[0],".").concat(c[2]),"".concat(c[3],".").concat(c[1],".").concat(c[0]),"".concat(c[3],".").concat(c[1],".").concat(c[2]),"".concat(c[3],".").concat(c[2],".").concat(c[0]),"".concat(c[3],".").concat(c[2],".").concat(c[1]),"".concat(c[0],".").concat(c[1],".").concat(c[2],".").concat(c[3]),"".concat(c[0],".").concat(c[1],".").concat(c[3],".").concat(c[2]),"".concat(c[0],".").concat(c[2],".").concat(c[1],".").concat(c[3]),"".concat(c[0],".").concat(c[2],".").concat(c[3],".").concat(c[1]),"".concat(c[0],".").concat(c[3],".").concat(c[1],".").concat(c[2]),"".concat(c[0],".").concat(c[3],".").concat(c[2],".").concat(c[1]),"".concat(c[1],".").concat(c[0],".").concat(c[2],".").concat(c[3]),"".concat(c[1],".").concat(c[0],".").concat(c[3],".").concat(c[2]),"".concat(c[1],".").concat(c[2],".").concat(c[0],".").concat(c[3]),"".concat(c[1],".").concat(c[2],".").concat(c[3],".").concat(c[0]),"".concat(c[1],".").concat(c[3],".").concat(c[0],".").concat(c[2]),"".concat(c[1],".").concat(c[3],".").concat(c[2],".").concat(c[0]),"".concat(c[2],".").concat(c[0],".").concat(c[1],".").concat(c[3]),"".concat(c[2],".").concat(c[0],".").concat(c[3],".").concat(c[1]),"".concat(c[2],".").concat(c[1],".").concat(c[0],".").concat(c[3]),"".concat(c[2],".").concat(c[1],".").concat(c[3],".").concat(c[0]),"".concat(c[2],".").concat(c[3],".").concat(c[0],".").concat(c[1]),"".concat(c[2],".").concat(c[3],".").concat(c[1],".").concat(c[0]),"".concat(c[3],".").concat(c[0],".").concat(c[1],".").concat(c[2]),"".concat(c[3],".").concat(c[0],".").concat(c[2],".").concat(c[1]),"".concat(c[3],".").concat(c[1],".").concat(c[0],".").concat(c[2]),"".concat(c[3],".").concat(c[1],".").concat(c[2],".").concat(c[0]),"".concat(c[3],".").concat(c[2],".").concat(c[0],".").concat(c[1]),"".concat(c[3],".").concat(c[2],".").concat(c[1],".").concat(c[0])]:void 0})(a)),L1[o]})(t.filter((function(n){return n!=="token"}))).reduce((function(n,a){return hd(hd({},n),r[a])}),e)}function Dx(t){return t.join(" ")}function Fx(t){var e=t.node,r=t.stylesheet,n=t.style,a=n===void 0?{}:n,o=t.useInlineStyles,p=t.key,c=e.properties,d=e.type,E=e.tagName,x=e.value;if(d==="text")return x;if(E){var k,P=(function(re,me){var le=0;return function(ve){return le+=1,ve.map((function(be,ce){return Fx({node:be,stylesheet:re,useInlineStyles:me,key:"code-segment-".concat(le,"-").concat(ce)})}))}})(r,o);if(o){var R=Object.keys(r).reduce((function(J,re){return re.split(".").forEach((function(me){J.includes(me)||J.push(me)})),J}),[]),L=c.className&&c.className.includes("token")?["token"]:[],V=c.className&&L.concat(c.className.filter((function(J){return!R.includes(J)})));k=hd(hd({},c),{},{className:Dx(V)||void 0,style:HR(c.className,Object.assign({},c.style,a),r)})}else k=hd(hd({},c),{},{className:Dx(c.className)});var Q=P(e.children);return u.createElement(E,F1({key:p},k),Q)}}var KR=["language","children","style","customStyle","codeTagProps","useInlineStyles","showLineNumbers","showInlineLineNumbers","startingLineNumber","lineNumberContainerStyle","lineNumberStyle","wrapLines","wrapLongLines","lineProps","renderer","PreTag","CodeTag","code","astGenerator"];function Lx(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(a){return Object.getOwnPropertyDescriptor(t,a).enumerable}))),r.push.apply(r,n)}return r}function Wp(t){for(var e=1;e1&&arguments[1]!==void 0?arguments[1]:[],r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:[];t.length===void 0&&(t=[t]);for(var n=0;n2&&arguments[2]!==void 0?arguments[2]:[];return e||le.length>0?(function(be,ce){return Sv({children:be,lineNumber:ce,lineNumberStyle:c,largestLineNumber:p,showInlineLineNumbers:a,lineProps:r,className:arguments.length>2&&arguments[2]!==void 0?arguments[2]:[],showLineNumbers:n,wrapLongLines:d,wrapLines:e})})(re,me,le):(function(be,ce){if(n&&ce&&a){var _e=Bx(c,ce,p);be.unshift($x(ce,_e))}return be})(re,me)}for(var V=function(){var me=x[R],le=me.children[0].value,ve=(function(_e){return _e.match(GR)})(le);if(ve){var be=le.split(` +`);be.forEach((function(ce,_e){var Ae=n&&k.length+o,xe={type:"text",value:"".concat(ce,` +`)};if(_e===0){var Oe=L(x.slice(P+1,R).concat(Sv({children:[xe],className:me.properties.className})),Ae);k.push(Oe)}else if(_e===be.length-1){var je=x[R+1]&&x[R+1].children&&x[R+1].children[0],G={type:"text",value:"".concat(ce)};if(je){var de=Sv({children:[G],className:me.properties.className});x.splice(R+1,0,de)}else{var Ne=L([G],Ae,me.properties.className);k.push(Ne)}}else{var Ye=L([xe],Ae,me.properties.className);k.push(Ye)}})),P=R}R++};R{Pc.registerLanguage("json",t9),Pc.registerLanguage("js",Jx),Pc.registerLanguage("xml",n9),Pc.registerLanguage("yaml",s9),Pc.registerLanguage("http",c9),Pc.registerLanguage("bash",o9),Pc.registerLanguage("powershell",p9),Pc.registerLanguage("javascript",Jx)},Wx={hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#333",color:"white"},"hljs-name":{fontWeight:"bold"},"hljs-strong":{fontWeight:"bold"},"hljs-code":{fontStyle:"italic",color:"#888"},"hljs-emphasis":{fontStyle:"italic"},"hljs-tag":{color:"#62c8f3"},"hljs-variable":{color:"#ade5fc"},"hljs-template-variable":{color:"#ade5fc"},"hljs-selector-id":{color:"#ade5fc"},"hljs-selector-class":{color:"#ade5fc"},"hljs-string":{color:"#a2fca2"},"hljs-bullet":{color:"#d36363"},"hljs-type":{color:"#ffa"},"hljs-title":{color:"#ffa"},"hljs-section":{color:"#ffa"},"hljs-attribute":{color:"#ffa"},"hljs-quote":{color:"#ffa"},"hljs-built_in":{color:"#ffa"},"hljs-builtin-name":{color:"#ffa"},"hljs-number":{color:"#d36363"},"hljs-symbol":{color:"#d36363"},"hljs-keyword":{color:"#fcc28c"},"hljs-selector-tag":{color:"#fcc28c"},"hljs-literal":{color:"#fcc28c"},"hljs-comment":{color:"#888"},"hljs-deletion":{color:"#333",backgroundColor:"#fc9b9b"},"hljs-regexp":{color:"#c6b4f0"},"hljs-link":{color:"#c6b4f0"},"hljs-meta":{color:"#fc9b9b"},"hljs-addition":{backgroundColor:"#a2fca2",color:"#333"}},d9={agate:Wx,arta:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#222",color:"#aaa"},"hljs-subst":{color:"#aaa"},"hljs-section":{color:"#fff",fontWeight:"bold"},"hljs-comment":{color:"#444"},"hljs-quote":{color:"#444"},"hljs-meta":{color:"#444"},"hljs-string":{color:"#ffcc33"},"hljs-symbol":{color:"#ffcc33"},"hljs-bullet":{color:"#ffcc33"},"hljs-regexp":{color:"#ffcc33"},"hljs-number":{color:"#00cc66"},"hljs-addition":{color:"#00cc66"},"hljs-built_in":{color:"#32aaee"},"hljs-builtin-name":{color:"#32aaee"},"hljs-literal":{color:"#32aaee"},"hljs-type":{color:"#32aaee"},"hljs-template-variable":{color:"#32aaee"},"hljs-attribute":{color:"#32aaee"},"hljs-link":{color:"#32aaee"},"hljs-keyword":{color:"#6644aa"},"hljs-selector-tag":{color:"#6644aa"},"hljs-name":{color:"#6644aa"},"hljs-selector-id":{color:"#6644aa"},"hljs-selector-class":{color:"#6644aa"},"hljs-title":{color:"#bb1166"},"hljs-variable":{color:"#bb1166"},"hljs-deletion":{color:"#bb1166"},"hljs-template-tag":{color:"#bb1166"},"hljs-doctag":{fontWeight:"bold"},"hljs-strong":{fontWeight:"bold"},"hljs-emphasis":{fontStyle:"italic"}},monokai:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#272822",color:"#ddd"},"hljs-tag":{color:"#f92672"},"hljs-keyword":{color:"#f92672",fontWeight:"bold"},"hljs-selector-tag":{color:"#f92672",fontWeight:"bold"},"hljs-literal":{color:"#f92672",fontWeight:"bold"},"hljs-strong":{color:"#f92672"},"hljs-name":{color:"#f92672"},"hljs-code":{color:"#66d9ef"},"hljs-class .hljs-title":{color:"white"},"hljs-attribute":{color:"#bf79db"},"hljs-symbol":{color:"#bf79db"},"hljs-regexp":{color:"#bf79db"},"hljs-link":{color:"#bf79db"},"hljs-string":{color:"#a6e22e"},"hljs-bullet":{color:"#a6e22e"},"hljs-subst":{color:"#a6e22e"},"hljs-title":{color:"#a6e22e",fontWeight:"bold"},"hljs-section":{color:"#a6e22e",fontWeight:"bold"},"hljs-emphasis":{color:"#a6e22e"},"hljs-type":{color:"#a6e22e",fontWeight:"bold"},"hljs-built_in":{color:"#a6e22e"},"hljs-builtin-name":{color:"#a6e22e"},"hljs-selector-attr":{color:"#a6e22e"},"hljs-selector-pseudo":{color:"#a6e22e"},"hljs-addition":{color:"#a6e22e"},"hljs-variable":{color:"#a6e22e"},"hljs-template-tag":{color:"#a6e22e"},"hljs-template-variable":{color:"#a6e22e"},"hljs-comment":{color:"#75715e"},"hljs-quote":{color:"#75715e"},"hljs-deletion":{color:"#75715e"},"hljs-meta":{color:"#75715e"},"hljs-doctag":{fontWeight:"bold"},"hljs-selector-id":{fontWeight:"bold"}},nord:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#2E3440",color:"#D8DEE9"},"hljs-subst":{color:"#D8DEE9"},"hljs-selector-tag":{color:"#81A1C1"},"hljs-selector-id":{color:"#8FBCBB",fontWeight:"bold"},"hljs-selector-class":{color:"#8FBCBB"},"hljs-selector-attr":{color:"#8FBCBB"},"hljs-selector-pseudo":{color:"#88C0D0"},"hljs-addition":{backgroundColor:"rgba(163, 190, 140, 0.5)"},"hljs-deletion":{backgroundColor:"rgba(191, 97, 106, 0.5)"},"hljs-built_in":{color:"#8FBCBB"},"hljs-type":{color:"#8FBCBB"},"hljs-class":{color:"#8FBCBB"},"hljs-function":{color:"#88C0D0"},"hljs-function > .hljs-title":{color:"#88C0D0"},"hljs-keyword":{color:"#81A1C1"},"hljs-literal":{color:"#81A1C1"},"hljs-symbol":{color:"#81A1C1"},"hljs-number":{color:"#B48EAD"},"hljs-regexp":{color:"#EBCB8B"},"hljs-string":{color:"#A3BE8C"},"hljs-title":{color:"#8FBCBB"},"hljs-params":{color:"#D8DEE9"},"hljs-bullet":{color:"#81A1C1"},"hljs-code":{color:"#8FBCBB"},"hljs-emphasis":{fontStyle:"italic"},"hljs-formula":{color:"#8FBCBB"},"hljs-strong":{fontWeight:"bold"},"hljs-link:hover":{textDecoration:"underline"},"hljs-quote":{color:"#4C566A"},"hljs-comment":{color:"#4C566A"},"hljs-doctag":{color:"#8FBCBB"},"hljs-meta":{color:"#5E81AC"},"hljs-meta-keyword":{color:"#5E81AC"},"hljs-meta-string":{color:"#A3BE8C"},"hljs-attr":{color:"#8FBCBB"},"hljs-attribute":{color:"#D8DEE9"},"hljs-builtin-name":{color:"#81A1C1"},"hljs-name":{color:"#81A1C1"},"hljs-section":{color:"#88C0D0"},"hljs-tag":{color:"#81A1C1"},"hljs-variable":{color:"#D8DEE9"},"hljs-template-variable":{color:"#D8DEE9"},"hljs-template-tag":{color:"#5E81AC"},"abnf .hljs-attribute":{color:"#88C0D0"},"abnf .hljs-symbol":{color:"#EBCB8B"},"apache .hljs-attribute":{color:"#88C0D0"},"apache .hljs-section":{color:"#81A1C1"},"arduino .hljs-built_in":{color:"#88C0D0"},"aspectj .hljs-meta":{color:"#D08770"},"aspectj > .hljs-title":{color:"#88C0D0"},"bnf .hljs-attribute":{color:"#8FBCBB"},"clojure .hljs-name":{color:"#88C0D0"},"clojure .hljs-symbol":{color:"#EBCB8B"},"coq .hljs-built_in":{color:"#88C0D0"},"cpp .hljs-meta-string":{color:"#8FBCBB"},"css .hljs-built_in":{color:"#88C0D0"},"css .hljs-keyword":{color:"#D08770"},"diff .hljs-meta":{color:"#8FBCBB"},"ebnf .hljs-attribute":{color:"#8FBCBB"},"glsl .hljs-built_in":{color:"#88C0D0"},"groovy .hljs-meta:not(:first-child)":{color:"#D08770"},"haxe .hljs-meta":{color:"#D08770"},"java .hljs-meta":{color:"#D08770"},"ldif .hljs-attribute":{color:"#8FBCBB"},"lisp .hljs-name":{color:"#88C0D0"},"lua .hljs-built_in":{color:"#88C0D0"},"moonscript .hljs-built_in":{color:"#88C0D0"},"nginx .hljs-attribute":{color:"#88C0D0"},"nginx .hljs-section":{color:"#5E81AC"},"pf .hljs-built_in":{color:"#88C0D0"},"processing .hljs-built_in":{color:"#88C0D0"},"scss .hljs-keyword":{color:"#81A1C1"},"stylus .hljs-keyword":{color:"#81A1C1"},"swift .hljs-meta":{color:"#D08770"},"vim .hljs-built_in":{color:"#88C0D0",fontStyle:"italic"},"yaml .hljs-meta":{color:"#D08770"}},obsidian:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#282b2e",color:"#e0e2e4"},"hljs-keyword":{color:"#93c763",fontWeight:"bold"},"hljs-selector-tag":{color:"#93c763",fontWeight:"bold"},"hljs-literal":{color:"#93c763",fontWeight:"bold"},"hljs-selector-id":{color:"#93c763"},"hljs-number":{color:"#ffcd22"},"hljs-attribute":{color:"#668bb0"},"hljs-code":{color:"white"},"hljs-class .hljs-title":{color:"white"},"hljs-section":{color:"white",fontWeight:"bold"},"hljs-regexp":{color:"#d39745"},"hljs-link":{color:"#d39745"},"hljs-meta":{color:"#557182"},"hljs-tag":{color:"#8cbbad"},"hljs-name":{color:"#8cbbad",fontWeight:"bold"},"hljs-bullet":{color:"#8cbbad"},"hljs-subst":{color:"#8cbbad"},"hljs-emphasis":{color:"#8cbbad"},"hljs-type":{color:"#8cbbad",fontWeight:"bold"},"hljs-built_in":{color:"#8cbbad"},"hljs-selector-attr":{color:"#8cbbad"},"hljs-selector-pseudo":{color:"#8cbbad"},"hljs-addition":{color:"#8cbbad"},"hljs-variable":{color:"#8cbbad"},"hljs-template-tag":{color:"#8cbbad"},"hljs-template-variable":{color:"#8cbbad"},"hljs-string":{color:"#ec7600"},"hljs-symbol":{color:"#ec7600"},"hljs-comment":{color:"#818e96"},"hljs-quote":{color:"#818e96"},"hljs-deletion":{color:"#818e96"},"hljs-selector-class":{color:"#A082BD"},"hljs-doctag":{fontWeight:"bold"},"hljs-title":{fontWeight:"bold"},"hljs-strong":{fontWeight:"bold"}},"tomorrow-night":{"hljs-comment":{color:"#969896"},"hljs-quote":{color:"#969896"},"hljs-variable":{color:"#cc6666"},"hljs-template-variable":{color:"#cc6666"},"hljs-tag":{color:"#cc6666"},"hljs-name":{color:"#cc6666"},"hljs-selector-id":{color:"#cc6666"},"hljs-selector-class":{color:"#cc6666"},"hljs-regexp":{color:"#cc6666"},"hljs-deletion":{color:"#cc6666"},"hljs-number":{color:"#de935f"},"hljs-built_in":{color:"#de935f"},"hljs-builtin-name":{color:"#de935f"},"hljs-literal":{color:"#de935f"},"hljs-type":{color:"#de935f"},"hljs-params":{color:"#de935f"},"hljs-meta":{color:"#de935f"},"hljs-link":{color:"#de935f"},"hljs-attribute":{color:"#f0c674"},"hljs-string":{color:"#b5bd68"},"hljs-symbol":{color:"#b5bd68"},"hljs-bullet":{color:"#b5bd68"},"hljs-addition":{color:"#b5bd68"},"hljs-title":{color:"#81a2be"},"hljs-section":{color:"#81a2be"},"hljs-keyword":{color:"#b294bb"},"hljs-selector-tag":{color:"#b294bb"},hljs:{display:"block",overflowX:"auto",background:"#1d1f21",color:"#c5c8c6",padding:"0.5em"},"hljs-emphasis":{fontStyle:"italic"},"hljs-strong":{fontWeight:"bold"}},idea:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",color:"#000",background:"#fff"},"hljs-subst":{fontWeight:"normal",color:"#000"},"hljs-title":{fontWeight:"normal",color:"#000"},"hljs-comment":{color:"#808080",fontStyle:"italic"},"hljs-quote":{color:"#808080",fontStyle:"italic"},"hljs-meta":{color:"#808000"},"hljs-tag":{background:"#efefef"},"hljs-section":{fontWeight:"bold",color:"#000080"},"hljs-name":{fontWeight:"bold",color:"#000080"},"hljs-literal":{fontWeight:"bold",color:"#000080"},"hljs-keyword":{fontWeight:"bold",color:"#000080"},"hljs-selector-tag":{fontWeight:"bold",color:"#000080"},"hljs-type":{fontWeight:"bold",color:"#000080"},"hljs-selector-id":{fontWeight:"bold",color:"#000080"},"hljs-selector-class":{fontWeight:"bold",color:"#000080"},"hljs-attribute":{fontWeight:"bold",color:"#0000ff"},"hljs-number":{fontWeight:"normal",color:"#0000ff"},"hljs-regexp":{fontWeight:"normal",color:"#0000ff"},"hljs-link":{fontWeight:"normal",color:"#0000ff"},"hljs-string":{color:"#008000",fontWeight:"bold"},"hljs-symbol":{color:"#000",background:"#d0eded",fontStyle:"italic"},"hljs-bullet":{color:"#000",background:"#d0eded",fontStyle:"italic"},"hljs-formula":{color:"#000",background:"#d0eded",fontStyle:"italic"},"hljs-doctag":{textDecoration:"underline"},"hljs-variable":{color:"#660e7a"},"hljs-template-variable":{color:"#660e7a"},"hljs-addition":{background:"#baeeba"},"hljs-deletion":{background:"#ffc8bd"},"hljs-emphasis":{fontStyle:"italic"},"hljs-strong":{fontWeight:"bold"}}},f9=Wx,m9=({language:t,className:e="",getConfigs:r,syntaxHighlighting:n={},children:a=""})=>{const o=r().syntaxHighlight.theme,{styles:p,defaultStyle:c}=n,d=p?.[o]??c;return u.createElement(Pc,{language:t,className:e,style:d},a)};var g9=Be(5419),y9=Be.n(g9);const xv="Copy code to clipboard",v9=({fileName:t="response.txt",className:e,downloadable:r,getComponent:n,canCopy:a,language:o,children:p})=>{const c=(0,u.useRef)(null),d=n("SyntaxHighlighter",!0),E=x=>{const{target:k,deltaY:P}=x,{scrollHeight:R,offsetHeight:L,scrollTop:V}=k;R>L&&(V===0&&P<0||L+V>=R&&P>0)&&x.preventDefault()};return(0,u.useEffect)((()=>{const x=Array.from(c.current.childNodes).filter((k=>!!k.nodeType&&k.classList.contains("microlight")));return x.forEach((k=>k.addEventListener("mousewheel",E,{passive:!1}))),()=>{x.forEach((k=>k.removeEventListener("mousewheel",E)))}}),[p,e,o]),u.createElement("div",{className:"highlight-code",ref:c},a&&u.createElement("div",{className:"copy-to-clipboard",title:xv,"aria-label":xv},u.createElement(cu.CopyToClipboard,{text:p},u.createElement("button",{"aria-label":xv,title:xv,type:"button"}))),r?u.createElement("button",{className:"download-contents",onClick:()=>{y9()(p,t)}},"Download"):null,u.createElement(d,{language:o,className:Xr()(e,"microlight"),renderPlainText:({children:x,PlainTextViewer:k})=>u.createElement(k,{className:e},x)},p))},b9=({className:t="",children:e})=>u.createElement("pre",{className:Xr()("microlight",t)},e),w9=(t,e)=>({renderPlainText:r,children:n,...a})=>{const o=e.getConfigs().syntaxHighlight.activated,p=e.getComponent("PlainTextViewer");return o||typeof r!="function"?o?u.createElement(t,a,n):u.createElement(p,null,n):r({children:n,PlainTextViewer:p})},E9=()=>({afterLoad:h9,rootInjects:{syntaxHighlighting:{styles:d9,defaultStyle:f9}},components:{SyntaxHighlighter:m9,HighlightCode:v9,PlainTextViewer:b9}}),S9=()=>({wrapComponents:{SyntaxHighlighter:w9}}),Hx=()=>[E9,S9],x9=()=>{const{GIT_DIRTY:t,GIT_COMMIT:e,PACKAGE_VERSION:r,BUILD_TIME:n}={PACKAGE_VERSION:"5.32.14",GIT_COMMIT:"gfefb1df1",GIT_DIRTY:!0,BUILD_TIME:"Tue, 18 Aug 2026 12:11:43 GMT"};Qe.versions=Qe.versions||{},Qe.versions.swaggerUI={version:r,gitRevision:e,gitDirty:t,buildTimestamp:n}},Kx=()=>({afterLoad:x9});var _9=Be(47248),O9=Be.n(_9);const Gx=console.error,k9=t=>e=>{const{getComponent:r,fn:n}=t(),a=r("ErrorBoundary"),o=n.getDisplayName(e);class p extends u.Component{render(){return u.createElement(a,{targetName:o,getComponent:r,fn:n},u.createElement(e,Yr()({},this.props,this.context)))}}var c;return p.displayName=`WithErrorBoundary(${o})`,(c=e).prototype&&c.prototype.isReactComponent&&(p.prototype.mapStateToProps=e.prototype.mapStateToProps),p},Yx=({name:t})=>u.createElement("div",{className:"fallback"},"😱 ",u.createElement("i",null,"Could not render ",t==="t"?"this component":t,", see the console."));class A9 extends u.Component{static defaultProps={targetName:"this component",getComponent:()=>Yx,fn:{componentDidCatch:Gx},children:null};static getDerivedStateFromError(e){return{hasError:!0,error:e}}constructor(...e){super(...e),this.state={hasError:!1,error:null}}componentDidCatch(e,r){this.props.fn.componentDidCatch(e,r)}render(){const{getComponent:e,targetName:r,children:n}=this.props;if(this.state.hasError){const a=e("Fallback");return u.createElement(a,{name:r})}return n}}const C9=A9,Xx=({componentList:t=[],fullOverride:e=!1}={})=>({getSystem:r})=>{const n=e?t:["App","BaseLayout","VersionPragmaFilter","InfoContainer","ServersContainer","SchemesContainer","AuthorizeBtnContainer","FilterContainer","Operations","OperationContainer","parameters","responses","OperationServers","Models","ModelWrapper",...t],a=O9()(n,Array(n.length).fill(((o,{fn:p})=>p.withErrorBoundary(o))));return{fn:{componentDidCatch:Gx,withErrorBoundary:k9(r)},components:{ErrorBoundary:C9,Fallback:Yx},wrapComponents:a}};class j9 extends u.Component{getLayout(){const{getComponent:e,layoutSelectors:r}=this.props,n=r.current();return e(n,!0)||(()=>u.createElement("h1",null,' No layout defined for "',n,'" '))}render(){const e=this.getLayout();return u.createElement(e,null)}}const P9=j9;class N9 extends u.Component{close=()=>{let{authActions:e}=this.props;e.showDefinitions(!1)};handleEscKeydown=e=>{e.key==="Escape"&&this.close()};componentDidMount(){document.addEventListener("keydown",this.handleEscKeydown)}componentWillUnmount(){document.removeEventListener("keydown",this.handleEscKeydown)}render(){let{authSelectors:e,authActions:r,getComponent:n,errSelectors:a,specSelectors:o,fn:{AST:p={}}}=this.props,c=e.shownDefinitions();const d=n("auths"),E=n("CloseIcon");return u.createElement("div",{className:"dialog-ux"},u.createElement("div",{className:"backdrop-ux",onClick:this.close}),u.createElement("div",{className:"modal-ux"},u.createElement("div",{className:"modal-dialog-ux"},u.createElement("div",{className:"modal-ux-inner"},u.createElement("div",{className:"modal-ux-header"},u.createElement("h3",null,"Available authorizations"),u.createElement("button",{type:"button",className:"close-modal",onClick:this.close},u.createElement(E,null))),u.createElement("div",{className:"modal-ux-content"},c.valueSeq().map(((x,k)=>u.createElement(d,{key:k,AST:p,definitions:x,getComponent:n,errSelectors:a,authSelectors:e,authActions:r,specSelectors:o}))))))))}}class T9 extends u.Component{render(){let{isAuthorized:e,showPopup:r,onClick:n,getComponent:a}=this.props;const o=a("authorizationPopup",!0),p=a("LockAuthIcon",!0),c=a("UnlockAuthIcon",!0);return u.createElement("div",{className:"auth-wrapper"},u.createElement("button",{className:e?"btn authorize locked":"btn authorize unlocked",onClick:n},u.createElement("span",null,"Authorize"),e?u.createElement(p,null):u.createElement(c,null)),r&&u.createElement(o,null))}}class I9 extends u.Component{render(){const{authActions:e,authSelectors:r,specSelectors:n,getComponent:a}=this.props,o=n.securityDefinitions(),p=r.definitionsToAuthorize(),c=a("authorizeBtn");return o?u.createElement(c,{onClick:()=>e.showDefinitions(p),isAuthorized:!!r.authorized().size,showPopup:!!r.shownDefinitions(),getComponent:a}):null}}class M9 extends u.Component{onClick=e=>{e.stopPropagation();let{onClick:r}=this.props;r&&r()};render(){let{isAuthorized:e,getComponent:r}=this.props;const n=r("LockAuthOperationIcon",!0),a=r("UnlockAuthOperationIcon",!0);return u.createElement("button",{className:"authorization__btn","aria-label":e?"authorization button locked":"authorization button unlocked",onClick:this.onClick},e?u.createElement(n,{className:"locked"}):u.createElement(a,{className:"unlocked"}))}}class R9 extends u.Component{constructor(e,r){super(e,r),this.state={}}onAuthChange=e=>{let{name:r}=e;this.setState({[r]:e})};submitAuth=e=>{e.preventDefault();let{authActions:r}=this.props;r.authorizeWithPersistOption(this.state)};logoutClick=e=>{e.preventDefault();let{authActions:r,definitions:n}=this.props,a=n.keySeq().toArray();this.setState(a.reduce(((o,p)=>(o[p]="",o)),{})),r.logoutWithPersistOption(a)};close=e=>{e.preventDefault();let{authActions:r}=this.props;r.showDefinitions(!1)};render(){let{definitions:e,getComponent:r,authSelectors:n,errSelectors:a}=this.props;const o=r("AuthItem"),p=r("oauth2",!0),c=r("Button");let d=n.authorized(),E=e.filter(((P,R)=>!!d.get(R))),x=e.filter((P=>P.get("type")!=="oauth2")),k=e.filter((P=>P.get("type")==="oauth2"));return u.createElement("div",{className:"auth-container"},!!x.size&&u.createElement("form",{onSubmit:this.submitAuth},x.map(((P,R)=>u.createElement(o,{key:R,schema:P,name:R,getComponent:r,onAuthChange:this.onAuthChange,authorized:d,errSelectors:a,authSelectors:n}))).valueSeq().toArray(),u.createElement("div",{className:"auth-btn-wrapper"},x.size===E.size?u.createElement(c,{className:"btn modal-btn auth",onClick:this.logoutClick,"aria-label":"Remove authorization"},"Logout"):u.createElement(c,{type:"submit",className:"btn modal-btn auth authorize","aria-label":"Apply credentials"},"Authorize"),u.createElement(c,{className:"btn modal-btn auth btn-done",onClick:this.close},"Close"))),k&&k.size?u.createElement("div",null,u.createElement("div",{className:"scope-def"},u.createElement("p",null,"Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes."),u.createElement("p",null,"API requires the following scopes. Select which ones you want to grant to Swagger UI.")),e.filter((P=>P.get("type")==="oauth2")).map(((P,R)=>u.createElement("div",{key:R},u.createElement(p,{authorized:d,schema:P,name:R})))).valueSeq().toArray()):null)}}class D9 extends u.Component{render(){let{schema:e,name:r,getComponent:n,onAuthChange:a,authorized:o,errSelectors:p,authSelectors:c}=this.props;const d=n("apiKeyAuth"),E=n("basicAuth");let x;const k=e.get("type");switch(k){case"apiKey":x=u.createElement(d,{key:r,schema:e,name:r,errSelectors:p,authorized:o,getComponent:n,onChange:a,authSelectors:c});break;case"basic":x=u.createElement(E,{key:r,schema:e,name:r,errSelectors:p,authorized:o,getComponent:n,onChange:a,authSelectors:c});break;default:x=u.createElement("div",{key:r},"Unknown security definition type ",k)}return u.createElement("div",{key:`${r}-jump`},x)}}class F9 extends u.Component{render(){let{error:e}=this.props,r=e.get("level"),n=e.get("message"),a=e.get("source");return u.createElement("div",{className:"errors"},u.createElement("b",null,a," ",r),u.createElement("span",null,n))}}class L9 extends u.Component{constructor(e,r){super(e,r);let{name:n,schema:a}=this.props,o=this.getValue();this.state={name:n,schema:a,value:o}}getValue(){let{name:e,authorized:r}=this.props;return r&&r.getIn([e,"value"])}onChange=e=>{let{onChange:r}=this.props,n=e.target.value,a=Object.assign({},this.state,{value:n});this.setState(a),r(a)};render(){let{schema:e,getComponent:r,errSelectors:n,name:a,authSelectors:o}=this.props;const p=r("Input"),c=r("Row"),d=r("Col"),E=r("authError"),x=r("Markdown",!0),k=r("JumpToPath",!0),P=o.selectAuthPath(a);let R=this.getValue(),L=n.allErrors().filter((V=>V.get("authId")===a));return u.createElement("div",null,u.createElement("h4",null,u.createElement("code",null,a||e.get("name"))," (apiKey)",u.createElement(k,{path:P})),R&&u.createElement("h6",null,"Authorized"),u.createElement(c,null,u.createElement(x,{source:e.get("description")})),u.createElement(c,null,u.createElement("p",null,"Name: ",u.createElement("code",null,e.get("name")))),u.createElement(c,null,u.createElement("p",null,"In: ",u.createElement("code",null,e.get("in")))),u.createElement(c,null,u.createElement("label",{htmlFor:"api_key_value"},"Value:"),R?u.createElement("code",null," ****** "):u.createElement(d,null,u.createElement(p,{id:"api_key_value",type:"text",onChange:this.onChange,autoFocus:!0}))),L.valueSeq().map(((V,Q)=>u.createElement(E,{error:V,key:Q}))))}}class $9 extends u.Component{constructor(e,r){super(e,r);let{schema:n,name:a}=this.props,o=this.getValue().username;this.state={name:a,schema:n,value:o?{username:o}:{}}}getValue(){let{authorized:e,name:r}=this.props;return e&&e.getIn([r,"value"])||{}}onChange=e=>{let{onChange:r}=this.props,{value:n,name:a}=e.target,o=this.state.value;o[a]=n,this.setState({value:o}),r(this.state)};render(){let{schema:e,getComponent:r,name:n,errSelectors:a,authSelectors:o}=this.props;const p=r("Input"),c=r("Row"),d=r("Col"),E=r("authError"),x=r("JumpToPath",!0),k=r("Markdown",!0),P=o.selectAuthPath(n);let R=this.getValue().username,L=a.allErrors().filter((V=>V.get("authId")===n));return u.createElement("div",null,u.createElement("h4",null,"Basic authorization",u.createElement(x,{path:P})),R&&u.createElement("h6",null,"Authorized"),u.createElement(c,null,u.createElement(k,{source:e.get("description")})),u.createElement(c,null,u.createElement("label",{htmlFor:"auth_username"},"Username:"),R?u.createElement("code",null," ",R," "):u.createElement(d,null,u.createElement(p,{id:"auth_username",type:"text",required:"required",name:"username",onChange:this.onChange,autoFocus:!0}))),u.createElement(c,null,u.createElement("label",{htmlFor:"auth_password"},"Password:"),R?u.createElement("code",null," ****** "):u.createElement(d,null,u.createElement(p,{id:"auth_password",autoComplete:"new-password",name:"password",type:"password",onChange:this.onChange}))),L.valueSeq().map(((V,Q)=>u.createElement(E,{error:V,key:Q}))))}}function B9(t){const{example:e,showValue:r,getComponent:n}=t,a=n("Markdown",!0),o=n("HighlightCode",!0);return e&&H.Map.isMap(e)?u.createElement("div",{className:"example"},e.get("description")?u.createElement("section",{className:"example__section"},u.createElement("div",{className:"example__section-header"},"Example Description"),u.createElement("p",null,u.createElement(a,{source:e.get("description")}))):null,r&&e.has("value")?u.createElement("section",{className:"example__section"},u.createElement("div",{className:"example__section-header"},"Example Value"),u.createElement(o,null,Gr(e.get("value")))):null):null}class q9 extends u.PureComponent{static defaultProps={examples:(0,H.Map)({}),onSelect:(...e)=>console.log("DEBUG: ExamplesSelect was not given an onSelect callback",...e),currentExampleKey:null,showLabels:!0};_onSelect=(e,{isSyntheticChange:r=!1}={})=>{typeof this.props.onSelect=="function"&&this.props.onSelect(e,{isSyntheticChange:r})};_onDomSelect=e=>{if(typeof this.props.onSelect=="function"){const r=e.target.selectedOptions[0].getAttribute("value");this._onSelect(r,{isSyntheticChange:!1})}};getCurrentExample=()=>{const{examples:e,currentExampleKey:r}=this.props,n=e.get(r),a=e.keySeq().first(),o=e.get(a);return n||o||(0,H.Map)({})};componentDidMount(){const{onSelect:e,examples:r}=this.props;if(typeof e=="function"){const n=r.first(),a=r.keyOf(n);this._onSelect(a,{isSyntheticChange:!0})}}UNSAFE_componentWillReceiveProps(e){const{currentExampleKey:r,examples:n}=e;if(n!==this.props.examples&&!n.has(r)){const a=n.first(),o=n.keyOf(a);this._onSelect(o,{isSyntheticChange:!0})}}render(){const{examples:e,currentExampleKey:r,isValueModified:n,isModifiedValueAvailable:a,showLabels:o}=this.props;return u.createElement("div",{className:"examples-select"},o?u.createElement("span",{className:"examples-select__section-label"},"Examples: "):null,u.createElement("select",{className:"examples-select-element",onChange:this._onDomSelect,value:a&&n?"__MODIFIED__VALUE__":r||""},a?u.createElement("option",{value:"__MODIFIED__VALUE__"},"[Modified value]"):null,e.map(((p,c)=>u.createElement("option",{key:c,value:c},H.Map.isMap(p)&&p.get("summary")||c))).valueSeq()))}}const $1=t=>H.List.isList(t)?t:Gr(t);class U9 extends u.PureComponent{static defaultProps={userHasEditedBody:!1,examples:(0,H.Map)({}),currentNamespace:"__DEFAULT__NAMESPACE__",setRetainRequestBodyValueFlag:()=>{},onSelect:(...e)=>console.log("ExamplesSelectValueRetainer: no `onSelect` function was provided",...e),updateValue:(...e)=>console.log("ExamplesSelectValueRetainer: no `updateValue` function was provided",...e)};constructor(e){super(e);const r=this._getCurrentExampleValue();this.state={[e.currentNamespace]:(0,H.Map)({lastUserEditedValue:this.props.currentUserInputValue,lastDownstreamValue:r,isModifiedValueSelected:this.props.userHasEditedBody||this.props.currentUserInputValue!==r})}}componentWillUnmount(){this.props.setRetainRequestBodyValueFlag(!1)}_getStateForCurrentNamespace=()=>{const{currentNamespace:e}=this.props;return(this.state[e]||(0,H.Map)()).toObject()};_setStateForCurrentNamespace=e=>{const{currentNamespace:r}=this.props;return this._setStateForNamespace(r,e)};_setStateForNamespace=(e,r)=>{const n=(this.state[e]||(0,H.Map)()).merge(r);return this.setState({[e]:n})};_isCurrentUserInputSameAsExampleValue=()=>{const{currentUserInputValue:e}=this.props;return this._getCurrentExampleValue()===e};_getValueForExample=(e,r)=>{const{examples:n}=r||this.props;return $1((n||(0,H.Map)({})).getIn([e,"value"]))};_getCurrentExampleValue=e=>{const{currentKey:r}=e||this.props;return this._getValueForExample(r,e||this.props)};_onExamplesSelect=(e,{isSyntheticChange:r}={},...n)=>{const{onSelect:a,updateValue:o,currentUserInputValue:p,userHasEditedBody:c}=this.props,{lastUserEditedValue:d}=this._getStateForCurrentNamespace(),E=this._getValueForExample(e);if(e==="__MODIFIED__VALUE__")return o($1(d)),this._setStateForCurrentNamespace({isModifiedValueSelected:!0});typeof a=="function"&&a(e,{isSyntheticChange:r},...n),this._setStateForCurrentNamespace({lastDownstreamValue:E,isModifiedValueSelected:r&&c||!!p&&p!==E}),r||typeof o=="function"&&o($1(E))};UNSAFE_componentWillReceiveProps(e){const{currentUserInputValue:r,examples:n,onSelect:a,userHasEditedBody:o}=e,{lastUserEditedValue:p,lastDownstreamValue:c}=this._getStateForCurrentNamespace(),d=this._getValueForExample(e.currentKey,e),E=n.filter((x=>H.Map.isMap(x)&&(x.get("value")===r||Gr(x.get("value"))===r)));if(E.size){let x;x=E.has(e.currentKey)?e.currentKey:E.keySeq().first(),a(x,{isSyntheticChange:!0})}else r!==this.props.currentUserInputValue&&r!==p&&r!==c&&(this.props.setRetainRequestBodyValueFlag(!0),this._setStateForNamespace(e.currentNamespace,{lastUserEditedValue:e.currentUserInputValue,isModifiedValueSelected:o||r!==d}))}render(){const{currentUserInputValue:e,examples:r,currentKey:n,getComponent:a,userHasEditedBody:o}=this.props,{lastDownstreamValue:p,lastUserEditedValue:c,isModifiedValueSelected:d}=this._getStateForCurrentNamespace(),E=a("ExamplesSelect");return u.createElement(E,{examples:r,currentExampleKey:n,onSelect:this._onExamplesSelect,isModifiedValueAvailable:!!c&&c!==p,isValueModified:e!==void 0&&d&&e!==this._getCurrentExampleValue()||o})}}function z9({auth:t,authActions:e,errActions:r,configs:n,authConfigs:a={},currentServer:o}){let{schema:p,scopes:c,name:d,clientId:E}=t,x=p.get("flow"),k=[];switch(x){case"password":return void e.authorizePassword(t);case"application":case"clientCredentials":case"client_credentials":return void e.authorizeApplication(t);case"accessCode":case"authorizationCode":case"authorization_code":k.push("response_type=code");break;case"implicit":k.push("response_type=token")}typeof E=="string"&&k.push("client_id="+encodeURIComponent(E));let P=n.oauth2RedirectUrl;if(P===void 0)return void r.newAuthErr({authId:d,source:"validation",level:"error",message:"oauth2RedirectUrl configuration is not passed. Oauth2 authorization cannot be performed."});k.push("redirect_uri="+encodeURIComponent(P));let R=[];if(Array.isArray(c)?R=c:H.default.List.isList(c)&&(R=c.toArray()),R.length>0){let le=a.scopeSeparator||" ";k.push("scope="+encodeURIComponent(R.join(le)))}let L=ko(new Date);if(k.push("state="+encodeURIComponent(L)),a.realm!==void 0&&k.push("realm="+encodeURIComponent(a.realm)),(x==="authorizationCode"||x==="authorization_code"||x==="accessCode")&&a.usePkceWithAuthorizationCodeGrant){const le=(function(){return Ja(Fn()(32).toString("base64"))})(),ve=(function(ce){return Ja(Vo()("sha256").update(ce).digest("base64"))})(le);k.push("code_challenge="+ve),k.push("code_challenge_method=S256"),t.codeVerifier=le}let{additionalQueryStringParams:V}=a;for(let le in V)V[le]!==void 0&&k.push([le,V[le]].map(encodeURIComponent).join("="));const Q=p.get("authorizationUrl");let J;J=o?Ao()(Dr(Q),o,!0).toString():Dr(Q);let re,me=[J,k.join("&")].join(typeof Q!="string"||Q.includes("?")?"&":"?");re=x==="implicit"?e.preAuthorizeImplicit:a.useBasicAuthenticationWithAccessCodeGrant?e.authorizeAccessCodeWithBasicAuthentication:e.authorizeAccessCodeWithFormParams,e.authPopup(me,{auth:t,state:L,redirectUrl:P,callback:re,errCb:r.newAuthErr})}class V9 extends u.Component{constructor(e,r){super(e,r);let{name:n,schema:a,authorized:o,authSelectors:p}=this.props,c=o&&o.get(n),d=p.getConfigs()||{},E=c&&c.get("username")||"",x=c&&c.get("clientId")||d.clientId||"",k=c&&c.get("clientSecret")||d.clientSecret||"",P=c&&c.get("passwordType")||"basic",R=c&&c.get("scopes")||d.scopes||[];typeof R=="string"&&(R=R.split(d.scopeSeparator||" ")),this.state={appName:d.appName,name:n,schema:a,scopes:R,clientId:x,clientSecret:k,username:E,password:"",passwordType:P}}close=e=>{e.preventDefault();let{authActions:r}=this.props;r.showDefinitions(!1)};authorize=()=>{let{authActions:e,errActions:r,getConfigs:n,authSelectors:a,oas3Selectors:o}=this.props,p=n(),c=a.getConfigs();r.clear({authId:name,type:"auth",source:"auth"}),z9({auth:this.state,currentServer:o.serverEffectiveValue(o.selectedServer()),authActions:e,errActions:r,configs:p,authConfigs:c})};onScopeChange=e=>{let{target:r}=e,{checked:n}=r,a=r.dataset.value;if(n&&this.state.scopes.indexOf(a)===-1){let o=this.state.scopes.concat([a]);this.setState({scopes:o})}else!n&&this.state.scopes.indexOf(a)>-1&&this.setState({scopes:this.state.scopes.filter((o=>o!==a))})};onInputChange=e=>{let{target:{dataset:{name:r},value:n}}=e,a={[r]:n};this.setState(a)};selectScopes=e=>{e.target.dataset.all?this.setState({scopes:Array.from((this.props.schema.get("allowedScopes")||this.props.schema.get("scopes")).keys())}):this.setState({scopes:[]})};logout=e=>{e.preventDefault();let{authActions:r,errActions:n,name:a}=this.props;n.clear({authId:a,type:"auth",source:"auth"}),r.logoutWithPersistOption([a])};render(){let{schema:e,getComponent:r,authSelectors:n,errSelectors:a,name:o,specSelectors:p}=this.props;const c=r("Input"),d=r("Row"),E=r("Col"),x=r("Button"),k=r("authError"),P=r("JumpToPath",!0),R=r("Markdown",!0),L=r("InitializedInput"),{isOAS3:V}=p;let Q=V()?e.get("openIdConnectUrl"):null;const J="implicit",re="password",me=V()?Q?"authorization_code":"authorizationCode":"accessCode",le=V()?Q?"client_credentials":"clientCredentials":"application",ve=n.selectAuthPath(o);let be=!!(n.getConfigs()||{}).usePkceWithAuthorizationCodeGrant,ce=e.get("flow"),_e=ce===me&&be?ce+" with PKCE":ce,Ae=e.get("allowedScopes")||e.get("scopes"),xe=!!n.authorized().get(o),Oe=a.allErrors().filter((de=>de.get("authId")===o)),je=!Oe.filter((de=>de.get("source")==="validation")).size,G=e.get("description");return u.createElement("div",null,u.createElement("h4",null,o," (OAuth2, ",_e,") ",u.createElement(P,{path:ve})),this.state.appName?u.createElement("h5",null,"Application: ",this.state.appName," "):null,G&&u.createElement(R,{source:e.get("description")}),xe&&u.createElement("h6",null,"Authorized"),Q&&u.createElement("p",null,"OpenID Connect URL: ",u.createElement("code",null,Q)),(ce===J||ce===me)&&u.createElement("p",null,"Authorization URL: ",u.createElement("code",null,e.get("authorizationUrl"))),(ce===re||ce===me||ce===le)&&u.createElement("p",null,"Token URL:",u.createElement("code",null," ",e.get("tokenUrl"))),u.createElement("p",{className:"flow"},"Flow: ",u.createElement("code",null,_e)),ce!==re?null:u.createElement(d,null,u.createElement(d,null,u.createElement("label",{htmlFor:"oauth_username"},"username:"),xe?u.createElement("code",null," ",this.state.username," "):u.createElement(E,{tablet:10,desktop:10},u.createElement("input",{id:"oauth_username",type:"text","data-name":"username",onChange:this.onInputChange,autoFocus:!0}))),u.createElement(d,null,u.createElement("label",{htmlFor:"oauth_password"},"password:"),xe?u.createElement("code",null," ****** "):u.createElement(E,{tablet:10,desktop:10},u.createElement("input",{id:"oauth_password",type:"password","data-name":"password",onChange:this.onInputChange}))),u.createElement(d,null,u.createElement("label",{htmlFor:"password_type"},"Client credentials location:"),xe?u.createElement("code",null," ",this.state.passwordType," "):u.createElement(E,{tablet:10,desktop:10},u.createElement("select",{id:"password_type","data-name":"passwordType",onChange:this.onInputChange},u.createElement("option",{value:"basic"},"Authorization header"),u.createElement("option",{value:"request-body"},"Request body"))))),(ce===le||ce===J||ce===me||ce===re)&&(!xe||xe&&this.state.clientId)&&u.createElement(d,null,u.createElement("label",{htmlFor:`client_id_${ce}`},"client_id:"),xe?u.createElement("code",null," ****** "):u.createElement(E,{tablet:10,desktop:10},u.createElement(L,{id:`client_id_${ce}`,type:"text",required:ce===re,initialValue:this.state.clientId,"data-name":"clientId",onChange:this.onInputChange}))),(ce===le||ce===me||ce===re)&&u.createElement(d,null,u.createElement("label",{htmlFor:`client_secret_${ce}`},"client_secret:"),xe?u.createElement("code",null," ****** "):u.createElement(E,{tablet:10,desktop:10},u.createElement(L,{id:`client_secret_${ce}`,initialValue:this.state.clientSecret,type:"password","data-name":"clientSecret",onChange:this.onInputChange}))),!xe&&Ae&&Ae.size?u.createElement("div",{className:"scopes"},u.createElement("h2",null,"Scopes:",u.createElement("a",{onClick:this.selectScopes,"data-all":!0},"select all"),u.createElement("a",{onClick:this.selectScopes},"select none")),Ae.map(((de,Ne)=>u.createElement(d,{key:Ne},u.createElement("div",{className:"checkbox"},u.createElement(c,{"data-value":Ne,id:`${Ne}-${ce}-checkbox-${this.state.name}`,disabled:xe,checked:this.state.scopes.includes(Ne),type:"checkbox",onChange:this.onScopeChange}),u.createElement("label",{htmlFor:`${Ne}-${ce}-checkbox-${this.state.name}`},u.createElement("span",{className:"item"}),u.createElement("div",{className:"text"},u.createElement("p",{className:"name"},Ne),u.createElement("p",{className:"description"},de))))))).valueSeq().toArray()):null,Oe.valueSeq().map(((de,Ne)=>u.createElement(k,{error:de,key:Ne}))),u.createElement("div",{className:"auth-btn-wrapper"},je&&(xe?u.createElement(x,{className:"btn modal-btn auth authorize",onClick:this.logout,"aria-label":"Remove authorization"},"Logout"):u.createElement(x,{className:"btn modal-btn auth authorize",onClick:this.authorize,"aria-label":"Apply given OAuth2 credentials"},"Authorize")),u.createElement(x,{className:"btn modal-btn auth btn-done",onClick:this.close},"Close")))}}class J9 extends u.Component{onClick=()=>{let{specActions:e,path:r,method:n}=this.props;e.clearResponse(r,n),e.clearRequest(r,n)};render(){return u.createElement("button",{className:"btn btn-clear opblock-control__btn",onClick:this.onClick},"Clear")}}const W9=({headers:t})=>u.createElement("div",null,u.createElement("h5",null,"Response headers"),u.createElement("pre",{className:"microlight"},t)),H9=({duration:t})=>u.createElement("div",null,u.createElement("h5",null,"Request duration"),u.createElement("pre",{className:"microlight"},t," ms"));class K9 extends u.Component{shouldComponentUpdate(e){return this.props.response!==e.response||this.props.path!==e.path||this.props.method!==e.method||this.props.displayRequestDuration!==e.displayRequestDuration}render(){const{response:e,getComponent:r,getConfigs:n,displayRequestDuration:a,specSelectors:o,path:p,method:c}=this.props,{showMutatedRequest:d,requestSnippetsEnabled:E}=n(),x=d?o.mutatedRequestFor(p,c):o.requestFor(p,c),k=e.get("status"),P=x.get("url"),R=e.get("headers").toJS(),L=e.get("notDocumented"),V=e.get("error"),Q=e.get("text"),J=e.get("duration"),re=Object.keys(R),me=R["content-type"]||R["Content-Type"],le=r("responseBody"),ve=re.map((xe=>{var Oe=Array.isArray(R[xe])?R[xe].join():R[xe];return u.createElement("span",{className:"headerline",key:xe}," ",xe,": ",Oe," ")})),be=ve.length!==0,ce=r("Markdown",!0),_e=r("RequestSnippets",!0),Ae=r("curl",!0);return u.createElement("div",null,x&&E?u.createElement(_e,{request:x}):u.createElement(Ae,{request:x}),P&&u.createElement("div",null,u.createElement("div",{className:"request-url"},u.createElement("h4",null,"Request URL"),u.createElement("pre",{className:"microlight"},P))),u.createElement("h4",null,"Server response"),u.createElement("table",{className:"responses-table live-responses-table"},u.createElement("thead",null,u.createElement("tr",{className:"responses-header"},u.createElement("td",{className:"col_header response-col_status"},"Code"),u.createElement("td",{className:"col_header response-col_description"},"Details"))),u.createElement("tbody",null,u.createElement("tr",{className:"response"},u.createElement("td",{className:"response-col_status"},k,L?u.createElement("div",{className:"response-undocumented"},u.createElement("i",null," Undocumented ")):null),u.createElement("td",{className:"response-col_description"},V?u.createElement(ce,{source:`${e.get("name")!==""?`${e.get("name")}: `:""}${e.get("message")}`}):null,Q?u.createElement(le,{content:Q,contentType:me,url:P,headers:R,getConfigs:n,getComponent:r}):null,be?u.createElement(W9,{headers:ve}):null,a&&J?u.createElement(H9,{duration:J}):null)))))}}class Qx extends u.Component{constructor(e,r){super(e,r);let{getConfigs:n}=e,{validatorUrl:a}=n();this.state={url:this.getDefinitionUrl(),validatorUrl:a===void 0?"https://validator.swagger.io/validator":a}}getDefinitionUrl=()=>{let{specSelectors:e}=this.props;return new(Ao())(e.url(),Qe.location).toString()};UNSAFE_componentWillReceiveProps(e){let{getConfigs:r}=e,{validatorUrl:n}=r();this.setState({url:this.getDefinitionUrl(),validatorUrl:n===void 0?"https://validator.swagger.io/validator":n})}render(){let{getConfigs:e}=this.props,{spec:r}=e(),n=Dr(this.state.validatorUrl);return typeof r=="object"&&Object.keys(r).length?null:this.state.url&&ta(this.state.validatorUrl)&&ta(this.state.url)?u.createElement("span",{className:"float-right"},u.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:`${n}/debug?url=${encodeURIComponent(this.state.url)}`},u.createElement(G9,{src:`${n}?url=${encodeURIComponent(this.state.url)}`,alt:"Online validator badge"}))):null}}class G9 extends u.Component{constructor(e){super(e),this.state={loaded:!1,error:!1}}componentDidMount(){const e=new Image;e.onload=()=>{this.setState({loaded:!0})},e.onerror=()=>{this.setState({error:!0})},e.src=this.props.src}UNSAFE_componentWillReceiveProps(e){if(e.src!==this.props.src){const r=new Image;r.onload=()=>{this.setState({loaded:!0})},r.onerror=()=>{this.setState({error:!0})},r.src=e.src}}render(){return this.state.error?u.createElement("img",{alt:"Error"}):this.state.loaded?u.createElement("img",{src:this.props.src,alt:this.props.alt}):null}}class Y9 extends u.Component{render(){let{specSelectors:e}=this.props;const r=e.taggedOperations();return r.size===0?u.createElement("h3",null," No operations defined in spec!"):u.createElement("div",null,r.map(this.renderOperationTag).valueSeq().toArray(),r.size<1?u.createElement("h3",null," No operations defined in spec! "):null)}renderOperationTag=(e,r)=>{const{specSelectors:n,getComponent:a,oas3Selectors:o,layoutSelectors:p,layoutActions:c,getConfigs:d}=this.props,E=n.validOperationMethods(),x=a("OperationContainer",!0),k=a("OperationTag"),P=e.get("operations");return u.createElement(k,{key:"operation-"+r,tagObj:e,tag:r,oas3Selectors:o,layoutSelectors:p,layoutActions:c,getConfigs:d,getComponent:a,specUrl:n.url()},u.createElement("div",{className:"operation-tag-content"},P.map((R=>{const L=R.get("path"),V=R.get("method"),Q=R.get("specPath");return E.indexOf(V)===-1?null:u.createElement(x,{key:`${L}-${V}`,specPath:Q,op:R,path:L,method:V,tag:r})})).toArray()))}}class X9 extends u.Component{static defaultProps={tagObj:H.default.fromJS({}),tag:""};render(){const{tagObj:e,tag:r,children:n,oas3Selectors:a,layoutSelectors:o,layoutActions:p,getConfigs:c,getComponent:d,specUrl:E}=this.props;let{docExpansion:x,deepLinking:k}=c();const P=d("Collapse"),R=d("Markdown",!0),L=d("DeepLink"),V=d("Link"),Q=d("ArrowUpIcon"),J=d("ArrowDownIcon");let re,me=e.getIn(["tagDetails","description"],null),le=e.getIn(["tagDetails","externalDocs","description"]),ve=e.getIn(["tagDetails","externalDocs","url"]);re=ia(a)&&ia(a.selectedServer)?ba(ve,E,{selectedServer:a.selectedServer()}):ve;let be=["operations-tag",r],ce=o.isShown(be,x==="full"||x==="list");return u.createElement("div",{className:ce?"opblock-tag-section is-open":"opblock-tag-section"},u.createElement("h3",{onClick:()=>p.show(be,!ce),className:me?"opblock-tag":"opblock-tag no-desc",id:be.map((_e=>ms(_e))).join("-"),"data-tag":r,"data-is-open":ce},u.createElement(L,{enabled:k,isShown:ce,path:Jo(r),text:r}),me?u.createElement("small",null,u.createElement(R,{source:me})):u.createElement("small",null),re?u.createElement("div",{className:"info__externaldocs"},u.createElement("small",null,u.createElement(V,{href:Dr(re),onClick:_e=>_e.stopPropagation(),target:"_blank"},le||re))):null,u.createElement("button",{"aria-expanded":ce,className:"expand-operation",title:ce?"Collapse operation":"Expand operation",onClick:()=>p.show(be,!ce)},ce?u.createElement(Q,{className:"arrow"}):u.createElement(J,{className:"arrow"}))),u.createElement(P,{isOpened:ce},n))}}class Q9 extends u.PureComponent{static defaultProps={operation:null,response:null,request:null,specPath:(0,H.List)(),summary:""};render(){let{specPath:e,response:r,request:n,toggleShown:a,onTryoutClick:o,onResetClick:p,onCancelClick:c,onExecute:d,fn:E,getComponent:x,getConfigs:k,specActions:P,specSelectors:R,authActions:L,authSelectors:V,oas3Actions:Q,oas3Selectors:J}=this.props,re=this.props.operation,{deprecated:me,isShown:le,path:ve,method:be,op:ce,tag:_e,operationId:Ae,allowTryItOut:xe,displayRequestDuration:Oe,tryItOutEnabled:je,executeInProgress:G}=re.toObject();const de=ce.get("description"),Ne=ce.get("externalDocs")?.toJS(),Ye=ce.get("schemes")?.toJS(),ht=Ne?ba(Ne.url,R.url(),{selectedServer:J.selectedServer()}):"";let Ct=re.getIn(["op"]),_t=Ct.get("responses"),qt=(function(dr,qr){if(!H.default.Iterable.isIterable(dr))return H.default.List();let en=dr.getIn(Array.isArray(qr)?qr:[qr]);return H.default.List.isList(en)?en:H.default.List()})(Ct,["parameters"]),It=R.operationScheme(ve,be),Ot=["operations",_e,Ae],et=gr(Ct);const Dt=x("responses",!0),or=x("parameters",!0),Ut=x("execute"),pr=x("clear"),W=x("Collapse"),$e=x("Markdown",!0),Je=x("schemes"),Ft=x("OperationServers"),it=x("OperationExt"),bt=x("OperationSummary"),ut=x("Link"),{showExtensions:Xt}=k();if(_t&&r&&r.size>0){let Er=!_t.get(String(r.get("status")))&&!_t.get("default");r=r.set("notDocumented",Er)}let zt=[ve,be];const tr=R.validationErrors([ve,be]);return u.createElement("div",{className:me?"opblock opblock-deprecated":le?`opblock opblock-${be} is-open`:`opblock opblock-${be}`,id:ms(Ot.join("-"))},u.createElement(bt,{operationProps:re,isShown:le,toggleShown:a,getComponent:x,authActions:L,authSelectors:V,specPath:e}),u.createElement(W,{isOpened:le},u.createElement("div",{className:"opblock-body"},Ct&&Ct.size||Ct===null?null:u.createElement(yh,{height:"32px",width:"32px",className:"opblock-loading-animation"}),me&&u.createElement("h4",{className:"opblock-title_normal"}," Warning: Deprecated"),de&&u.createElement("div",{className:"opblock-description-wrapper"},u.createElement("div",{className:"opblock-description"},u.createElement($e,{source:de}))),ht?u.createElement("div",{className:"opblock-external-docs-wrapper"},u.createElement("h4",{className:"opblock-title_normal"},"Find more details"),u.createElement("div",{className:"opblock-external-docs"},Ne.description&&u.createElement("span",{className:"opblock-external-docs__description"},u.createElement($e,{source:Ne.description})),u.createElement(ut,{target:"_blank",className:"opblock-external-docs__link",href:Dr(ht)},ht))):null,Ct&&Ct.size?u.createElement(or,{parameters:qt,specPath:e.push("parameters"),operation:Ct,onChangeKey:zt,onTryoutClick:o,onResetClick:p,onCancelClick:c,tryItOutEnabled:je,allowTryItOut:xe,fn:E,getComponent:x,specActions:P,specSelectors:R,pathMethod:[ve,be],getConfigs:k,oas3Actions:Q,oas3Selectors:J}):null,je?u.createElement(Ft,{getComponent:x,path:ve,method:be,operationServers:Ct.get("servers"),pathServers:R.paths().getIn([ve,"servers"]),getSelectedServer:J.selectedServer,setSelectedServer:Q.setSelectedServer,setServerVariableValue:Q.setServerVariableValue,getServerVariable:J.serverVariableValue,getEffectiveServerValue:J.serverEffectiveValue}):null,je&&xe&&Ye&&Ye.size?u.createElement("div",{className:"opblock-schemes"},u.createElement(Je,{schemes:Ye,path:ve,method:be,specActions:P,currentScheme:It})):null,!je||!xe||tr.length<=0?null:u.createElement("div",{className:"validation-errors errors-wrapper"},"Please correct the following validation errors and try again.",u.createElement("ul",null,tr.map(((Er,dr)=>u.createElement("li",{key:dr}," ",Er," "))))),u.createElement("div",{className:je&&r&&xe?"btn-group":"execute-wrapper"},je&&xe?u.createElement(Ut,{operation:Ct,specActions:P,specSelectors:R,oas3Selectors:J,oas3Actions:Q,path:ve,method:be,onExecute:d,disabled:G}):null,je&&r&&xe?u.createElement(pr,{specActions:P,path:ve,method:be}):null),G?u.createElement("div",{className:"loading-container"},u.createElement("div",{className:"loading"})):null,_t?u.createElement(Dt,{responses:_t,request:n,tryItOutResponse:r,getComponent:x,getConfigs:k,specSelectors:R,oas3Actions:Q,oas3Selectors:J,specActions:P,produces:R.producesOptionsFor([ve,be]),producesValue:R.currentProducesFor([ve,be]),specPath:e.push("responses"),path:ve,method:be,displayRequestDuration:Oe,fn:E}):null,Xt&&et.size?u.createElement(it,{extensions:et,getComponent:x}):null)))}}class Z9 extends u.PureComponent{constructor(e,r){super(e,r);const{tryItOutEnabled:n}=e.getConfigs();this.state={tryItOutEnabled:n,executeInProgress:!1}}static defaultProps={showSummary:!0,response:null,allowTryItOut:!0,displayOperationId:!1,displayRequestDuration:!1};mapStateToProps(e,r){const{op:n,layoutSelectors:a,getConfigs:o}=r,{docExpansion:p,deepLinking:c,displayOperationId:d,displayRequestDuration:E,supportedSubmitMethods:x}=o(),k=a.showSummary(),P=n.getIn(["operation","__originalOperationId"])||n.getIn(["operation","operationId"])||Df(n.get("operation"),r.path,r.method)||n.get("id"),R=["operations",r.tag,P],L=x.indexOf(r.method)>=0&&(r.allowTryItOut===void 0?r.specSelectors.allowTryItOutFor(r.path,r.method):r.allowTryItOut),V=n.getIn(["operation","security"])||r.specSelectors.security();return{operationId:P,isDeepLinkingEnabled:c,showSummary:k,displayOperationId:d,displayRequestDuration:E,allowTryItOut:L,security:V,isAuthorized:r.authSelectors.isAuthorized(V),isShown:a.isShown(R,p==="full"),jumpToKey:`paths.${r.path}.${r.method}`,response:r.specSelectors.responseFor(r.path,r.method),request:r.specSelectors.requestFor(r.path,r.method)}}componentDidMount(){const{isShown:e}=this.props,r=this.getResolvedSubtree();e&&r===void 0&&this.requestResolvedSubtree()}componentDidUpdate(e){const{response:r,isShown:n}=this.props,a=this.getResolvedSubtree();r!==e.response&&this.setState({executeInProgress:!1}),n&&a===void 0&&this.requestResolvedSubtree()}toggleShown=()=>{let{layoutActions:e,tag:r,operationId:n,isShown:a}=this.props;const o=this.getResolvedSubtree();a||o!==void 0||this.requestResolvedSubtree(),e.show(["operations",r,n],!a)};onCancelClick=()=>{this.setState({tryItOutEnabled:!this.state.tryItOutEnabled})};onTryoutClick=()=>{this.setState({tryItOutEnabled:!this.state.tryItOutEnabled})};onResetClick=e=>{const r=this.props.oas3Selectors.selectDefaultRequestBodyValue(...e),n=this.props.oas3Selectors.requestContentType(...e);if(n==="application/x-www-form-urlencoded"||n==="multipart/form-data"){const a=JSON.parse(r);Object.entries(a).forEach((([o,p])=>{Array.isArray(p)?a[o]=a[o].map((c=>typeof c=="object"?JSON.stringify(c,null,2):c)):typeof p=="object"&&(a[o]=JSON.stringify(a[o],null,2))})),this.props.oas3Actions.setRequestBodyValue({value:(0,H.fromJS)(a),pathMethod:e})}else this.props.oas3Actions.setRequestBodyValue({value:r,pathMethod:e})};onExecute=()=>{this.setState({executeInProgress:!0})};getResolvedSubtree=()=>{const{specSelectors:e,path:r,method:n,specPath:a}=this.props;return a?e.specResolvedSubtree(a.toJS()):e.specResolvedSubtree(["paths",r,n])};requestResolvedSubtree=()=>{const{specActions:e,path:r,method:n,specPath:a}=this.props;return a?e.requestResolvedSubtree(a.toJS()):e.requestResolvedSubtree(["paths",r,n])};render(){let{op:e,tag:r,path:n,method:a,security:o,isAuthorized:p,operationId:c,showSummary:d,isShown:E,jumpToKey:x,allowTryItOut:k,response:P,request:R,displayOperationId:L,displayRequestDuration:V,isDeepLinkingEnabled:Q,specPath:J,specSelectors:re,specActions:me,getComponent:le,getConfigs:ve,layoutSelectors:be,layoutActions:ce,authActions:_e,authSelectors:Ae,oas3Actions:xe,oas3Selectors:Oe,fn:je}=this.props;const G=le("operation"),de=this.getResolvedSubtree()||(0,H.Map)(),Ne=(0,H.fromJS)({op:de,tag:r,path:n,summary:e.getIn(["operation","summary"])||"",deprecated:de.get("deprecated")||e.getIn(["operation","deprecated"])||!1,method:a,security:o,isAuthorized:p,operationId:c,originalOperationId:de.getIn(["operation","__originalOperationId"]),showSummary:d,isShown:E,jumpToKey:x,allowTryItOut:k,request:R,displayOperationId:L,displayRequestDuration:V,isDeepLinkingEnabled:Q,executeInProgress:this.state.executeInProgress,tryItOutEnabled:this.state.tryItOutEnabled});return u.createElement(G,{operation:Ne,response:P,request:R,isShown:E,toggleShown:this.toggleShown,onTryoutClick:this.onTryoutClick,onResetClick:this.onResetClick,onCancelClick:this.onCancelClick,onExecute:this.onExecute,specPath:J,specActions:me,specSelectors:re,oas3Actions:xe,oas3Selectors:Oe,layoutActions:ce,layoutSelectors:be,authActions:_e,authSelectors:Ae,getComponent:le,getConfigs:ve,fn:je})}}var e7=Be(13222),t7=Be.n(e7);class r7 extends u.PureComponent{static defaultProps={operationProps:null,specPath:(0,H.List)(),summary:""};render(){let{isShown:e,toggleShown:r,getComponent:n,authActions:a,authSelectors:o,operationProps:p,specPath:c}=this.props,{summary:d,isAuthorized:E,method:x,op:k,showSummary:P,path:R,operationId:L,originalOperationId:V,displayOperationId:Q}=p.toObject();const J=k.get("summary"),re=p.get("security"),me=n("authorizeOperationBtn",!0),le=n("OperationSummaryMethod"),ve=n("OperationSummaryPath"),be=n("JumpToPath",!0),ce=n("CopyToClipboardBtn",!0),_e=n("ArrowUpIcon"),Ae=n("ArrowDownIcon"),xe=re&&!!re.count(),Oe=xe&&re.size===1&&re.first().isEmpty(),je=!xe||Oe;return u.createElement("div",{className:`opblock-summary opblock-summary-${x}`},u.createElement("button",{"aria-expanded":e,className:"opblock-summary-control",onClick:r},u.createElement(le,{method:x}),u.createElement("div",{className:"opblock-summary-path-description-wrapper"},u.createElement(ve,{getComponent:n,operationProps:p,specPath:c}),P?u.createElement("div",{className:"opblock-summary-description"},t7()(J||d)):null),Q&&(V||L)?u.createElement("span",{className:"opblock-summary-operation-id"},V||L):null),u.createElement(ce,{textToCopy:`${c.get(1)}`}),je?null:u.createElement(me,{isAuthorized:E,onClick:()=>{const G=o.definitionsForRequirements(re);a.showDefinitions(G)}}),u.createElement(be,{path:c}),u.createElement("button",{"aria-label":`${x} ${R.replace(/\//g,"​/")}`,className:"opblock-control-arrow","aria-expanded":e,tabIndex:"-1",onClick:r},e?u.createElement(_e,{className:"arrow"}):u.createElement(Ae,{className:"arrow"})))}}class n7 extends u.PureComponent{static defaultProps={operationProps:null};render(){let{method:e}=this.props;return u.createElement("span",{className:"opblock-summary-method"},e.toUpperCase())}}class a7 extends u.PureComponent{render(){let{getComponent:e,operationProps:r}=this.props,{deprecated:n,isShown:a,path:o,tag:p,operationId:c,isDeepLinkingEnabled:d}=r.toObject();const E=o.split(/(?=\/)/g);for(let k=1;k{let r=e("OperationExtRow");return u.createElement("div",{className:"opblock-section"},u.createElement("div",{className:"opblock-section-header"},u.createElement("h4",null,"Extensions")),u.createElement("div",{className:"table-container"},u.createElement("table",null,u.createElement("thead",null,u.createElement("tr",null,u.createElement("td",{className:"col_header"},"Field"),u.createElement("td",{className:"col_header"},"Value"))),u.createElement("tbody",null,t.entrySeq().map((([n,a])=>u.createElement(r,{key:`${n}-${a}`,xKey:n,xVal:a})))))))},i7=({xKey:t,xVal:e})=>{const r=e?e.toJS?e.toJS():e:null;return u.createElement("tr",null,u.createElement("td",null,t),u.createElement("td",null,JSON.stringify(r)))};function B1(t,e="_"){return t.replace(/[^\w-]/g,e)}class q1 extends u.Component{static defaultProps={tryItOutResponse:null,produces:(0,H.fromJS)(["application/json"]),displayRequestDuration:!1};onChangeProducesWrapper=e=>this.props.specActions.changeProducesValue([this.props.path,this.props.method],e);onResponseContentTypeChange=({controlsAcceptHeader:e,value:r})=>{const{oas3Actions:n,path:a,method:o}=this.props;e&&n.setResponseContentType({value:r,path:a,method:o})};render(){let{responses:e,tryItOutResponse:r,getComponent:n,getConfigs:a,specSelectors:o,fn:p,producesValue:c,displayRequestDuration:d,specPath:E,path:x,method:k,oas3Selectors:P,oas3Actions:R}=this.props,L=(function(_e){let Ae=_e.keySeq();return Ae.contains(tn)?tn:Ae.filter((xe=>(xe+"")[0]==="2")).sort().first()})(e);const V=n("contentType"),Q=n("liveResponse"),J=n("response");let re=this.props.produces&&this.props.produces.size?this.props.produces:q1.defaultProps.produces;const me=o.isOAS3()?(function(_e){if(!H.default.OrderedMap.isOrderedMap(_e)||!_e.size)return null;const Ae=_e.find(((je,G)=>G.startsWith("2")&&Object.keys(je.get("content")||{}).length>0)),xe=_e.get("default")||H.default.OrderedMap(),Oe=(xe.get("content")||H.default.OrderedMap()).keySeq().toJS().length?xe:null;return Ae||Oe})(e):null,le=e.filter(((ce,_e)=>!Yt(_e))),ve=B1(`${k}${x}_responses`),be=`${ve}_select`;return le&&le.size?u.createElement("div",{className:"responses-wrapper"},u.createElement("div",{className:"opblock-section-header"},u.createElement("h4",null,"Responses"),o.isOAS3()?null:u.createElement("label",{htmlFor:be},u.createElement("span",null,"Response content type"),u.createElement(V,{value:c,ariaControls:ve,ariaLabel:"Response content type",className:"execute-content-type",contentTypes:re,controlId:be,onChange:this.onChangeProducesWrapper}))),u.createElement("div",{className:"responses-inner"},r?u.createElement("div",null,u.createElement(Q,{response:r,getComponent:n,getConfigs:a,specSelectors:o,path:this.props.path,method:this.props.method,displayRequestDuration:d}),u.createElement("h4",null,"Responses")):null,u.createElement("table",{"aria-live":"polite",className:"responses-table",id:ve,role:"region"},u.createElement("thead",null,u.createElement("tr",{className:"responses-header"},u.createElement("td",{className:"col_header response-col_status"},"Code"),u.createElement("td",{className:"col_header response-col_description"},"Description"),o.isOAS3()?u.createElement("td",{className:"col col_header response-col_links"},"Links"):null)),u.createElement("tbody",null,le.entrySeq().map((([ce,_e])=>{let Ae=r&&r.get("status")==ce?"response_current":"";return u.createElement(J,{key:ce,path:x,method:k,specPath:E.push(ce),isDefault:L===ce,fn:p,className:Ae,code:ce,response:_e,specSelectors:o,controlsAcceptHeader:_e===me,onContentTypeChange:this.onResponseContentTypeChange,contentType:c,getConfigs:a,activeExamplesKey:P.activeExamplesMember(x,k,"responses",ce),oas3Actions:R,getComponent:n})})).toArray())))):null}}function _v(t){return(function(n){try{return!!JSON.parse(n)}catch{return null}})(t)?"json":null}class s7 extends u.Component{constructor(e,r){super(e,r),this.state={responseContentType:""}}static defaultProps={response:(0,H.fromJS)({}),onContentTypeChange:()=>{}};_onContentTypeChange=e=>{const{onContentTypeChange:r,controlsAcceptHeader:n}=this.props;this.setState({responseContentType:e}),r({value:e,controlsAcceptHeader:n})};getTargetExamplesKey=()=>{const{response:e,contentType:r,activeExamplesKey:n}=this.props,a=this.state.responseContentType||r,o=e.getIn(["content",a],(0,H.Map)({})).get("examples",null).keySeq().first();return n||o};render(){let{path:e,method:r,code:n,response:a,className:o,specPath:p,fn:c,getComponent:d,getConfigs:E,specSelectors:x,contentType:k,controlsAcceptHeader:P,oas3Actions:R}=this.props,{inferSchema:L,getSampleSchema:V}=c,Q=x.isOAS3();const{showExtensions:J}=E();let re=J?gr(a):null,me=a.get("headers"),le=a.get("links");const ve=d("ResponseExtension"),be=d("headers"),ce=d("HighlightCode",!0),_e=d("modelExample"),Ae=d("Markdown",!0),xe=d("operationLink"),Oe=d("contentType"),je=d("ExamplesSelect"),G=d("Example");var de,Ne;const Ye=this.state.responseContentType||k,ht=a.getIn(["content",Ye],(0,H.Map)({})),Ct=ht.get("examples",null);if(Q){const Dt=ht.get("schema");de=Dt?L(Dt.toJS()):null,Ne=Dt?p.push("content",this.state.responseContentType,"schema"):p}else de=a.get("schema"),Ne=a.has("schema")?p.push("schema"):p;let _t,qt,It=!1,Ot={includeReadOnly:!0};if(Q)if(qt=ht.get("schema")?.toJS(),H.Map.isMap(Ct)&&!Ct.isEmpty()){const Dt=this.getTargetExamplesKey(),or=Ut=>H.Map.isMap(Ut)?Ut.get("value"):void 0;_t=or(Ct.get(Dt,(0,H.Map)({}))),_t===void 0&&(_t=or(Ct.values().next().value)),It=!0}else ht.get("example")!==void 0&&(_t=ht.get("example"),It=!0);else{qt=de,Ot={...Ot,includeWriteOnly:!0};const Dt=a.getIn(["examples",Ye]);Dt&&(_t=Dt,It=!0)}const et=((Dt,or)=>{if(Dt==null)return null;const Ut=_v(Dt)?"json":null;return u.createElement("div",null,u.createElement(or,{className:"example",language:Ut},Gr(Dt)))})(V(qt,Ye,Ot,It?_t:void 0),ce);return u.createElement("tr",{className:"response "+(o||""),"data-code":n},u.createElement("td",{className:"response-col_status"},n),u.createElement("td",{className:"response-col_description"},u.createElement("div",{className:"response-col_description__inner"},u.createElement(Ae,{source:a.get("description")})),J&&re.size?re.entrySeq().map((([Dt,or])=>u.createElement(ve,{key:`${Dt}-${or}`,xKey:Dt,xVal:or}))):null,Q&&a.get("content")?u.createElement("section",{className:"response-controls"},u.createElement("div",{className:Xr()("response-control-media-type",{"response-control-media-type--accept-controller":P})},u.createElement("small",{className:"response-control-media-type__title"},"Media type"),u.createElement(Oe,{value:this.state.responseContentType,contentTypes:a.get("content")?a.get("content").keySeq():(0,H.Seq)(),onChange:this._onContentTypeChange,ariaLabel:"Media Type"}),P?u.createElement("small",{className:"response-control-media-type__accept-message"},"Controls ",u.createElement("code",null,"Accept")," header."):null),H.Map.isMap(Ct)&&!Ct.isEmpty()?u.createElement("div",{className:"response-control-examples"},u.createElement("small",{className:"response-control-examples__title"},"Examples"),u.createElement(je,{examples:Ct,currentExampleKey:this.getTargetExamplesKey(),onSelect:Dt=>R.setActiveExamplesMember({name:Dt,pathMethod:[e,r],contextType:"responses",contextName:n}),showLabels:!1})):null):null,et||de?u.createElement(_e,{specPath:Ne,getComponent:d,getConfigs:E,specSelectors:x,schema:nn(de),example:et,includeReadOnly:!0}):null,Q&&Ct?u.createElement(G,{example:Ct.get(this.getTargetExamplesKey(),(0,H.Map)({})),getComponent:d,getConfigs:E,omitValue:!0}):null,me?u.createElement(be,{headers:me,getComponent:d}):null),Q?u.createElement("td",{className:"response-col_links"},le?le.toSeq().entrySeq().map((([Dt,or])=>u.createElement(xe,{key:Dt,name:Dt,link:or,getComponent:d}))):u.createElement("i",null,"No links")):null)}}const l7=({xKey:t,xVal:e})=>u.createElement("div",{className:"response__extension"},t,": ",String(e));var c7=Be(26657),u7=Be.n(c7),p7=Be(80218),Zx=Be.n(p7);class h7 extends u.PureComponent{state={parsedContent:null};updateParsedContent=e=>{const{content:r}=this.props;if(e!==r)if(r&&r instanceof Blob){var n=new FileReader;n.onload=()=>{this.setState({parsedContent:n.result})},n.readAsText(r)}else this.setState({parsedContent:r.toString()})};componentDidMount(){this.updateParsedContent(null)}componentDidUpdate(e){this.updateParsedContent(e.content)}render(){let{content:e,contentType:r,url:n,headers:a={},getComponent:o}=this.props;const{parsedContent:p}=this.state,c=o("HighlightCode",!0),d="response_"+new Date().getTime();let E,x;if(n=n||"",(/^application\/octet-stream/i.test(r)||a["Content-Disposition"]&&/attachment/i.test(a["Content-Disposition"])||a["content-disposition"]&&/attachment/i.test(a["content-disposition"])||a["Content-Description"]&&/File Transfer/i.test(a["Content-Description"])||a["content-description"]&&/File Transfer/i.test(a["content-description"]))&&(e.size>0||e.length>0))if("Blob"in window){let k=r||"text/html",P=e instanceof Blob?e:new Blob([e],{type:k}),R=window.URL.createObjectURL(P),L=[k,n.substr(n.lastIndexOf("/")+1),R].join(":"),V=a["content-disposition"]||a["Content-Disposition"];if(V!==void 0){let Q=(function(re){let me;if([/filename\*=[^']+'\w*'"([^"]+)";?/i,/filename\*=[^']+'\w*'([^;]+);?/i,/filename="([^;]*);?"/i,/filename=([^;]*);?/i].some((le=>(me=le.exec(re),me!==null))),me!==null&&me.length>1)try{return decodeURIComponent(me[1])}catch(le){console.error(le)}return null})(V);Q!==null&&(L=Q)}x=Qe.navigator&&Qe.navigator.msSaveOrOpenBlob?u.createElement("div",null,u.createElement("a",{href:R,onClick:()=>Qe.navigator.msSaveOrOpenBlob(P,L)},"Download file")):u.createElement("div",null,u.createElement("a",{href:R,download:L},"Download file"))}else x=u.createElement("pre",{className:"microlight"},"Download headers detected but your browser does not support downloading binary via XHR (Blob).");else if(/json/i.test(r)){let k=null;_v(e)&&(k="json");try{E=JSON.stringify(JSON.parse(e),null," ")}catch{E=`can't parse JSON. Raw result: + +`+e}x=u.createElement(c,{language:k,downloadable:!0,fileName:`${d}.json`,canCopy:!0},E)}else/xml/i.test(r)?(E=u7()(e,{textNodesOnSameLine:!0,indentor:" "}),x=u.createElement(c,{downloadable:!0,fileName:`${d}.xml`,canCopy:!0},E)):x=Zx()(r)==="text/html"||/text\/plain/.test(r)?u.createElement(c,{downloadable:!0,fileName:`${d}.html`,canCopy:!0},e):Zx()(r)==="text/csv"||/text\/csv/.test(r)?u.createElement(c,{downloadable:!0,fileName:`${d}.csv`,canCopy:!0},e):/^image\//i.test(r)?r.includes("svg")?u.createElement("div",null," ",e," "):u.createElement("img",{src:window.URL.createObjectURL(e)}):/^audio\//i.test(r)?u.createElement("pre",{className:"microlight"},u.createElement("audio",{controls:!0,key:n},u.createElement("source",{src:n,type:r}))):typeof e=="string"?u.createElement(c,{downloadable:!0,fileName:`${d}.txt`,canCopy:!0},e):e.size>0?p?u.createElement("div",null,u.createElement("p",{className:"i"},"Unrecognized response type; displaying content as text."),u.createElement(c,{downloadable:!0,fileName:`${d}.txt`,canCopy:!0},p)):u.createElement("p",{className:"i"},"Unrecognized response type; unable to display."):null;return x?u.createElement("div",null,u.createElement("h5",null,"Response body"),x):null}}class d7 extends u.Component{constructor(e){super(e),this.state={callbackVisible:!1,parametersVisible:!0}}static defaultProps={onTryoutClick:Function.prototype,onCancelClick:Function.prototype,tryItOutEnabled:!1,allowTryItOut:!0,onChangeKey:[],specPath:[]};onChange=(e,r,n)=>{let{specActions:{changeParamByIdentity:a},onChangeKey:o}=this.props;a(o,e,r,n)};onChangeConsumesWrapper=e=>{let{specActions:{changeConsumesValue:r},onChangeKey:n}=this.props;r(n,e)};toggleTab=e=>e==="parameters"?this.setState({parametersVisible:!0,callbackVisible:!1}):e==="callbacks"?this.setState({callbackVisible:!0,parametersVisible:!1}):void 0;onChangeMediaType=({value:e,pathMethod:r})=>{let{specActions:n,oas3Selectors:a,oas3Actions:o}=this.props;const p=a.hasUserEditedBody(...r),c=a.shouldRetainRequestBodyValue(...r);o.setRequestContentType({value:e,pathMethod:r}),o.initRequestBodyValidateError({pathMethod:r}),p||(c||o.setRequestBodyValue({value:void 0,pathMethod:r}),n.clearResponse(...r),n.clearRequest(...r),n.clearValidateParams(r))};render(){let{onTryoutClick:e,onResetClick:r,parameters:n,allowTryItOut:a,tryItOutEnabled:o,specPath:p,fn:c,getComponent:d,getConfigs:E,specSelectors:x,specActions:k,pathMethod:P,oas3Actions:R,oas3Selectors:L,operation:V}=this.props;const Q=d("parameterRow"),J=d("TryItOutButton"),re=d("contentType"),me=d("Callbacks",!0),le=d("RequestBody",!0),ve=o&&a,be=x.isOAS3(),ce=`${B1(`${P[1]}${P[0]}_requests`)}_select`,_e=V.get("requestBody"),Ae=Object.values(n.reduce(((xe,Oe)=>{if(H.Map.isMap(Oe)){const je=Oe.get("in");xe[je]??=[],xe[je].push(Oe)}return xe}),{})).reduce(((xe,Oe)=>xe.concat(Oe)),[]);return u.createElement("div",{className:"opblock-section"},u.createElement("div",{className:"opblock-section-header"},be?u.createElement("div",{className:"tab-header"},u.createElement("div",{onClick:()=>this.toggleTab("parameters"),className:`tab-item ${this.state.parametersVisible&&"active"}`},u.createElement("h4",{className:"opblock-title"},u.createElement("span",null,"Parameters"))),V.get("callbacks")?u.createElement("div",{onClick:()=>this.toggleTab("callbacks"),className:`tab-item ${this.state.callbackVisible&&"active"}`},u.createElement("h4",{className:"opblock-title"},u.createElement("span",null,"Callbacks"))):null):u.createElement("div",{className:"tab-header"},u.createElement("h4",{className:"opblock-title"},"Parameters")),a?u.createElement(J,{isOAS3:x.isOAS3(),hasUserEditedBody:L.hasUserEditedBody(...P),enabled:o,onCancelClick:this.props.onCancelClick,onTryoutClick:e,onResetClick:()=>r(P)}):null),this.state.parametersVisible?u.createElement("div",{className:"parameters-container"},Ae.length?u.createElement("div",{className:"table-container"},u.createElement("table",{className:"parameters"},u.createElement("thead",null,u.createElement("tr",null,u.createElement("th",{className:"col_header parameters-col_name"},"Name"),u.createElement("th",{className:"col_header parameters-col_description"},"Description"))),u.createElement("tbody",null,Ae.map(((xe,Oe)=>u.createElement(Q,{fn:c,specPath:p.push(Oe.toString()),getComponent:d,getConfigs:E,rawParam:xe,param:x.parameterWithMetaByIdentity(P,xe),key:`${xe.get("in")}.${xe.get("name")}`,onChange:this.onChange,onChangeConsumes:this.onChangeConsumesWrapper,specSelectors:x,specActions:k,oas3Actions:R,oas3Selectors:L,pathMethod:P,isExecute:ve})))))):u.createElement("div",{className:"opblock-description-wrapper"},u.createElement("p",null,"No parameters"))):null,this.state.callbackVisible?u.createElement("div",{className:"callbacks-container opblock-description-wrapper"},u.createElement(me,{callbacks:(0,H.Map)(V.get("callbacks")),specPath:p.slice(0,-1).push("callbacks")})):null,be&&_e&&this.state.parametersVisible&&u.createElement("div",{className:"opblock-section opblock-section-request-body"},u.createElement("div",{className:"opblock-section-header"},u.createElement("h4",{className:`opblock-title parameter__name ${_e.get("required")&&"required"}`},"Request body"),u.createElement("label",{id:ce},u.createElement(re,{value:L.requestContentType(...P),contentTypes:_e.get("content",(0,H.List)()).keySeq(),onChange:xe=>{this.onChangeMediaType({value:xe,pathMethod:P})},className:"body-param-content-type",ariaLabel:"Request content type",controlId:ce}))),u.createElement("div",{className:"opblock-description-wrapper"},u.createElement(le,{setRetainRequestBodyValueFlag:xe=>R.setRetainRequestBodyValueFlag({value:xe,pathMethod:P}),userHasEditedBody:L.hasUserEditedBody(...P),specPath:p.slice(0,-1).push("requestBody"),requestBody:_e,requestBodyValue:L.requestBodyValue(...P),requestBodyInclusionSetting:L.requestBodyInclusionSetting(...P),requestBodyErrors:L.requestBodyErrors(...P),isExecute:ve,getConfigs:E,activeExamplesKey:L.activeExamplesMember(...P,"requestBody","requestBody"),updateActiveExamplesKey:xe=>{this.props.oas3Actions.setActiveExamplesMember({name:xe,pathMethod:this.props.pathMethod,contextType:"requestBody",contextName:"requestBody"})},onChange:(xe,Oe)=>{if(Oe){const je=L.requestBodyValue(...P),G=H.Map.isMap(je)?je:(0,H.Map)();return R.setRequestBodyValue({pathMethod:P,value:G.setIn(Oe,xe)})}R.setRequestBodyValue({value:xe,pathMethod:P})},onChangeIncludeEmpty:(xe,Oe)=>{R.setRequestBodyInclusion({pathMethod:P,value:Oe,name:xe})},contentType:L.requestContentType(...P)}))))}}const f7=({xKey:t,xVal:e})=>u.createElement("div",{className:"parameter__extension"},t,": ",String(e)),m7={onChange:()=>{},isIncludedOptions:{}};class g7 extends u.Component{static defaultProps=m7;componentDidMount(){const{isIncludedOptions:e,onChange:r}=this.props,{shouldDispatchInit:n,defaultValue:a}=e;n&&r(a)}onCheckboxChange=e=>{const{onChange:r}=this.props;r(e.target.checked)};render(){let{isIncluded:e,isDisabled:r}=this.props;return u.createElement("div",null,u.createElement("label",{htmlFor:"include_empty_value",className:Xr()("parameter__empty_value_toggle",{disabled:r})},u.createElement("input",{id:"include_empty_value",type:"checkbox",disabled:r,checked:!r&&e,onChange:this.onCheckboxChange}),"Send empty value"))}}const U1=t=>{if(H.List.isList(t))return t;if(typeof t!="string")return null;try{const e=JSON.parse(t);return Array.isArray(e)?(0,H.List)(e):null}catch{return null}};class y7 extends u.Component{constructor(e,r){super(e,r),this.setDefaultValue()}UNSAFE_componentWillReceiveProps(e){let r,{specSelectors:n,pathMethod:a,rawParam:o}=e,p=n.isOAS3(),c=n.parameterWithMetaByIdentity(a,o)||new H.Map;if(c=c.isEmpty()?o:c,p){let{schema:x}=sr(c,{isOAS3:p});r=x?x.get("enum"):void 0}else r=c?c.get("enum"):void 0;let d,E=c?c.get("value"):void 0;E!==void 0?d=E:o.get("required")&&r&&r.size&&(d=r.first()),d!==void 0&&d!==E&&this.onChangeWrapper((function(k){return typeof k=="number"?k.toString():k})(d)),this.setDefaultValue()}onChangeWrapper=(e,r=!1)=>{let n,{onChange:a,rawParam:o}=this.props;return n=e===""||e&&e.size===0?null:e,a(o,n,r)};_onExampleSelect=e=>{this.props.oas3Actions.setActiveExamplesMember({name:e,pathMethod:this.props.pathMethod,contextType:"parameters",contextName:this.getParamKey()})};onChangeIncludeEmpty=e=>{let{specActions:r,param:n,pathMethod:a}=this.props;const o=n.get("name"),p=n.get("in");return r.updateEmptyParamInclusion(a,o,p,e)};setDefaultValue=()=>{let{specSelectors:e,pathMethod:r,rawParam:n,oas3Selectors:a,fn:o}=this.props;const p=e.isOAS3(),c=e.parameterWithMetaByIdentity(r,n)||(0,H.Map)();let{schema:d}=sr(c,{isOAS3:p});const E=c.get("content",(0,H.Map)()).keySeq().first(),x=d?o.getSampleSchema(d.toJS(),E,{includeWriteOnly:!0}):null;if(c&&c.get("value")===void 0&&c.get("in")!=="body"){let k;if(e.isSwagger2())k=c.get("x-example")!==void 0?c.get("x-example"):c.getIn(["schema","example"])!==void 0?c.getIn(["schema","example"]):d&&d.getIn(["default"]);else if(p){d=this.composeJsonSchema(d);const V=a.activeExamplesMember(...r,"parameters",this.getParamKey());k=c.getIn(["examples",V,"value"])!==void 0?c.getIn(["examples",V,"value"]):c.getIn(["content",E,"example"])!==void 0?c.getIn(["content",E,"example"]):c.get("example")!==void 0?c.get("example"):(d&&d.get("example"))!==void 0?d&&d.get("example"):(d&&d.get("default"))!==void 0?d&&d.get("default"):c.get("default")}k===void 0||H.List.isList(k)||(k=Gr(k));const P=o.getSchemaObjectType(d),R=o.getSchemaObjectType(d?.get("items")),L=H.List.isList(d?.get("type"));if(p&&P==="array"&&!L&&k!==void 0){const V=U1(k);return void this.onChangeWrapper(V)}if(k!==void 0)this.onChangeWrapper(k);else if(P==="object"&&x&&!c.get("examples"))this.onChangeWrapper(H.List.isList(x)?x:Gr(x));else if(P==="array"&&R==="object"&&x&&!c.get("examples")){const V=U1(x);this.onChangeWrapper(V)}}};onExampleValueUpdate=e=>{const{specSelectors:r,pathMethod:n,rawParam:a,fn:o}=this.props,p=r.parameterWithMetaByIdentity(n,a)||(0,H.Map)();let{schema:c}=sr(p,{isOAS3:!0});c&&(c=this.composeJsonSchema(c));const d=o.getSchemaObjectType(c),E=H.List.isList(c?.get("type"));if(d!=="array"||E)this.onChangeWrapper(e);else{const x=U1(e);this.onChangeWrapper(x)}};getParamKey(){const{param:e}=this.props;return e?`${e.get("name")}-${e.get("in")}`:null}composeJsonSchema(e){const{fn:r}=this.props,n=e.get("oneOf")?.get(0)?.toJS(),a=e.get("anyOf")?.get(0)?.toJS();return(0,H.fromJS)(r.mergeJsonSchema(e.toJS(),n??a??{}))}render(){let{param:e,rawParam:r,getComponent:n,getConfigs:a,isExecute:o,fn:p,onChangeConsumes:c,specSelectors:d,pathMethod:E,specPath:x,oas3Selectors:k}=this.props,P=d.isOAS3();const{showExtensions:R,showCommonExtensions:L}=a();if(e||(e=r),!r)return null;const V=n("JsonSchemaForm"),Q=n("ParamBody");let J=e.get("in"),re=J!=="body"?null:u.createElement(Q,{getComponent:n,getConfigs:a,fn:p,param:e,consumes:d.consumesOptionsFor(E),consumesValue:d.contentTypeValues(E).get("requestContentType"),onChange:this.onChangeWrapper,onChangeConsumes:c,isExecute:o,specSelectors:d,pathMethod:E});const me=n("modelExample"),le=n("Markdown",!0),ve=n("ParameterExt"),be=n("ParameterIncludeEmpty"),ce=n("ExamplesSelectValueRetainer"),_e=n("Example");let{schema:Ae}=sr(e,{isOAS3:P}),xe=d.parameterWithMetaByIdentity(E,r)||(0,H.Map)();const Oe=xe.get("content",(0,H.Map)()).keySeq().first();P&&(Ae=this.composeJsonSchema(Ae));let je=Ae?Ae.get("format"):null,G=J==="formData",de="FormData"in Qe,Ne=e.get("required");const Ye=p.getSchemaObjectType(Ae),ht=p.getSchemaObjectType(Ae?.get("items")),Ct=p.getSchemaObjectTypeLabel(Ae),_t=!re&&Ye==="object",qt=!re&&ht==="object";let It,Ot,et,Dt,or=xe?xe.get("value"):"",Ut=L?mr(Ae):null,pr=R?gr(e):null,W=!1;e!==void 0&&Ae&&(It=Ae.get("items")),It!==void 0?(Ot=It.get("enum"),et=It.get("default")):Ae&&(Ot=Ae.get("enum")),Ot&&Ot.size&&Ot.size>0&&(W=!0),e!==void 0&&(Ae&&(et=Ae.get("default")),et===void 0&&(et=e.get("default")),Dt=e.get("example"),Dt===void 0&&(Dt=e.get("x-example")));const $e=re?null:u.createElement(V,{fn:p,getComponent:n,value:or,required:Ne,disabled:!o,description:e.get("name"),onChange:this.onChangeWrapper,errors:xe.get("errors"),schema:Ae});return u.createElement("tr",{"data-param-name":e.get("name"),"data-param-in":e.get("in")},u.createElement("td",{className:"parameters-col_name"},u.createElement("div",{className:Ne?"parameter__name required":"parameter__name"},e.get("name"),Ne?u.createElement("span",null," *"):null),u.createElement("div",{className:"parameter__type"},Ct,je&&u.createElement("span",{className:"prop-format"},"($",je,")")),u.createElement("div",{className:"parameter__deprecated"},P&&e.get("deprecated")?"deprecated":null),u.createElement("div",{className:"parameter__in"},"(",e.get("in"),")")),u.createElement("td",{className:"parameters-col_description"},e.get("description")?u.createElement(le,{source:e.get("description")}):null,!re&&o||!W?null:u.createElement(le,{className:"parameter__enum",source:"Available values : "+Ot.map((function(Je){return Je})).toArray().map(String).join(", ")}),!re&&o||et===void 0?null:u.createElement(le,{className:"parameter__default",source:"Default value : "+et}),!re&&o||Dt===void 0?null:u.createElement(le,{source:"Example : "+Dt}),G&&!de&&u.createElement("div",null,"Error: your browser does not support FormData"),P&&e.get("examples")?u.createElement("section",{className:"parameter-controls"},u.createElement(ce,{examples:e.get("examples"),onSelect:this._onExampleSelect,updateValue:this.onExampleValueUpdate,getComponent:n,defaultToFirstExample:!0,currentKey:k.activeExamplesMember(...E,"parameters",this.getParamKey()),currentUserInputValue:or})):null,_t||qt?u.createElement(me,{getComponent:n,specPath:Oe?x.push("content",Oe,"schema"):x.push("schema"),getConfigs:a,isExecute:o,specSelectors:d,schema:Ae,example:$e}):$e,re&&Ae?u.createElement(me,{getComponent:n,specPath:x.push("schema"),getConfigs:a,isExecute:o,specSelectors:d,schema:Ae,example:re,includeWriteOnly:!0}):null,!re&&o&&e.get("allowEmptyValue")?u.createElement(be,{onChange:this.onChangeIncludeEmpty,isIncluded:d.parameterInclusionSettingFor(E,e.get("name"),e.get("in")),isDisabled:!Wa(or)}):null,P&&e.get("examples")?u.createElement(_e,{example:e.getIn(["examples",k.activeExamplesMember(...E,"parameters",this.getParamKey())]),getComponent:n,getConfigs:a}):null,L&&Ut.size?Ut.entrySeq().map((([Je,Ft])=>u.createElement(ve,{key:`${Je}-${Ft}`,xKey:Je,xVal:Ft}))):null,R&&pr.size?pr.entrySeq().map((([Je,Ft])=>u.createElement(ve,{key:`${Je}-${Ft}`,xKey:Je,xVal:Ft}))):null))}}class v7 extends u.Component{handleValidateParameters=()=>{let{specSelectors:e,specActions:r,path:n,method:a}=this.props;return r.validateParams([n,a]),e.validateBeforeExecute([n,a])};handleValidateRequestBody=()=>{let{path:e,method:r,specSelectors:n,oas3Selectors:a,oas3Actions:o}=this.props,p={missingBodyValue:!1,missingRequiredKeys:[]};o.clearRequestBodyValidateError({path:e,method:r});let c=n.getOAS3RequiredRequestBodyContentType([e,r]),d=a.requestBodyValue(e,r),E=a.validateBeforeExecute([e,r]),x=a.requestContentType(e,r);if(!E)return p.missingBodyValue=!0,o.setRequestBodyValidateError({path:e,method:r,validationErrors:p}),!1;if(!c)return!0;let k=a.validateShallowRequired({oas3RequiredRequestBodyContentType:c,oas3RequestContentType:x,oas3RequestBodyValue:d});return!k||k.length<1||(k.forEach((P=>{p.missingRequiredKeys.push(P)})),o.setRequestBodyValidateError({path:e,method:r,validationErrors:p}),!1)};handleValidationResultPass=()=>{let{specActions:e,operation:r,path:n,method:a}=this.props;this.props.onExecute&&this.props.onExecute(),e.execute({operation:r,path:n,method:a})};handleValidationResultFail=()=>{let{specActions:e,path:r,method:n}=this.props;e.clearValidateParams([r,n]),setTimeout((()=>{e.validateParams([r,n])}),40)};handleValidationResult=e=>{e?this.handleValidationResultPass():this.handleValidationResultFail()};onClick=()=>{let e=this.handleValidateParameters(),r=this.handleValidateRequestBody(),n=e&&r;this.handleValidationResult(n)};onChangeProducesWrapper=e=>this.props.specActions.changeProducesValue([this.props.path,this.props.method],e);render(){const{disabled:e}=this.props;return u.createElement("button",{className:"btn execute opblock-control__btn",onClick:this.onClick,disabled:e},"Execute")}}class b7 extends u.Component{render(){let{headers:e,getComponent:r}=this.props;const n=r("Property"),a=r("Markdown",!0);return e&&e.size?u.createElement("div",{className:"headers-wrapper"},u.createElement("h4",{className:"headers__title"},"Headers:"),u.createElement("table",{className:"headers"},u.createElement("thead",null,u.createElement("tr",{className:"header-row"},u.createElement("th",{className:"header-col"},"Name"),u.createElement("th",{className:"header-col"},"Description"),u.createElement("th",{className:"header-col"},"Type"))),u.createElement("tbody",null,e.entrySeq().map((([o,p])=>{if(!H.default.Map.isMap(p))return null;const c=p.get("description"),d=p.getIn(["schema"])?p.getIn(["schema","type"]):p.getIn(["type"]),E=p.getIn(["schema","example"]);return u.createElement("tr",{key:o},u.createElement("td",{className:"header-col"},o),u.createElement("td",{className:"header-col"},c?u.createElement(a,{source:c}):null),u.createElement("td",{className:"header-col"},d," ",E?u.createElement(n,{propKey:"Example",propVal:E,propClass:"header-example"}):null))})).toArray()))):null}}class w7 extends u.Component{render(){let{editorActions:e,errSelectors:r,layoutSelectors:n,layoutActions:a,getComponent:o}=this.props;const p=o("Collapse");if(e&&e.jumpToLine)var c=e.jumpToLine;let d=r.allErrors().filter((k=>k.get("type")==="thrown"||k.get("level")==="error"));if(!d||d.count()<1)return null;let E=n.isShown(["errorPane"],!0),x=d.sortBy((k=>k.get("line")));return u.createElement("pre",{className:"errors-wrapper"},u.createElement("hgroup",{className:"error"},u.createElement("h4",{className:"errors__title"},"Errors"),u.createElement("button",{className:"btn errors__clear-btn",onClick:()=>a.show(["errorPane"],!E)},E?"Hide":"Show")),u.createElement(p,{isOpened:E,animated:!0},u.createElement("div",{className:"errors"},x.map(((k,P)=>{let R=k.get("type");return R==="thrown"||R==="auth"?u.createElement(E7,{key:P,error:k.get("error")||k,jumpToLine:c}):R==="spec"?u.createElement(S7,{key:P,error:k,jumpToLine:c}):void 0})))))}}const E7=({error:t,jumpToLine:e})=>{if(!t)return null;let r=t.get("line");return u.createElement("div",{className:"error-wrapper"},t?u.createElement("div",null,u.createElement("h4",null,t.get("source")&&t.get("level")?e_(t.get("source"))+" "+t.get("level"):"",t.get("path")?u.createElement("small",null," at ",t.get("path")):null),u.createElement("span",{className:"message thrown"},t.get("message")),u.createElement("div",{className:"error-line"},r&&e?u.createElement("a",{onClick:e.bind(null,r)},"Jump to line ",r):null)):null)},S7=({error:t,jumpToLine:e=null})=>{let r=null;return t.get("path")?r=H.List.isList(t.get("path"))?u.createElement("small",null,"at ",t.get("path").join(".")):u.createElement("small",null,"at ",t.get("path")):t.get("line")&&!e&&(r=u.createElement("small",null,"on line ",t.get("line"))),u.createElement("div",{className:"error-wrapper"},t?u.createElement("div",null,u.createElement("h4",null,e_(t.get("source"))+" "+t.get("level")," ",r),u.createElement("span",{className:"message"},t.get("message")),u.createElement("div",{className:"error-line"},e?u.createElement("a",{onClick:e.bind(null,t.get("line"))},"Jump to line ",t.get("line")):null)):null)};function e_(t){return(t||"").split(" ").map((e=>e[0].toUpperCase()+e.slice(1))).join(" ")}const x7=()=>{};class _7 extends u.Component{static defaultProps={onChange:x7,value:null,contentTypes:(0,H.fromJS)(["application/json"])};componentDidMount(){const{contentTypes:e,onChange:r}=this.props;e&&e.size&&r(e.first())}componentDidUpdate(){const{contentTypes:e,value:r,onChange:n}=this.props;e&&e.size&&(e.includes(r)||n(e.first()))}onChangeWrapper=e=>this.props.onChange(e.target.value);render(){let{ariaControls:e,ariaLabel:r,className:n,contentTypes:a,controlId:o,value:p}=this.props;return a&&a.size?u.createElement("div",{className:"content-type-wrapper "+(n||"")},u.createElement("select",{"aria-controls":e,"aria-label":r,className:"content-type",id:o,onChange:this.onChangeWrapper,value:p||""},a.map((c=>u.createElement("option",{key:c,value:c},c))).toArray())):null}}function Im(...t){return t.filter((e=>!!e)).join(" ").trim()}class O7 extends u.Component{render(){let{fullscreen:e,full:r,...n}=this.props;if(e)return u.createElement("section",n);let a="swagger-container"+(r?"-full":"");return u.createElement("section",Yr()({},n,{className:Im(n.className,a)}))}}const z1={mobile:"",tablet:"-tablet",desktop:"-desktop",large:"-hd"};class k7 extends u.Component{render(){const{hide:e,keepContents:r,mobile:n,tablet:a,desktop:o,large:p,...c}=this.props;if(e&&!r)return u.createElement("span",null);let d=[];for(let x in z1){if(!Object.prototype.hasOwnProperty.call(z1,x))continue;let k=z1[x];if(x in this.props){let P=this.props[x];if(P<1){d.push("none"+k);continue}d.push("block"+k),d.push("col-"+P+k)}}e&&d.push("hidden");let E=Im(c.className,...d);return u.createElement("section",Yr()({},c,{className:E}))}}class A7 extends u.Component{render(){return u.createElement("div",Yr()({},this.props,{className:Im(this.props.className,"wrapper")}))}}class C7 extends u.Component{static defaultProps={className:""};render(){return u.createElement("button",Yr()({},this.props,{className:Im(this.props.className,"button")}))}}const j7=t=>u.createElement("textarea",t),P7=t=>u.createElement("input",t);class N7 extends u.Component{static defaultProps={multiple:!1,allowEmptyValue:!0};constructor(e,r){let n;super(e,r),n=e.value?e.value:e.multiple?[""]:"",this.state={value:n}}onChange=e=>{let r,{onChange:n,multiple:a}=this.props,o=[].slice.call(e.target.options);r=a?o.filter((function(p){return p.selected})).map((function(p){return p.value})):e.target.value,this.setState({value:r}),n&&n(r)};UNSAFE_componentWillReceiveProps(e){e.value!==this.props.value&&this.setState({value:e.value})}render(){let{allowedValues:e,multiple:r,allowEmptyValue:n,disabled:a}=this.props,o=this.state.value?.toJS?.()||this.state.value;return u.createElement("select",{className:this.props.className,multiple:r,value:o,onChange:this.onChange,disabled:a},n?u.createElement("option",{value:""},"--"):null,e.map((function(p,c){return u.createElement("option",{key:c,value:String(p)},String(p))})))}}class t_ extends u.Component{render(){return u.createElement("a",Yr()({},this.props,{rel:"noopener noreferrer",className:Im(this.props.className,"link")}))}}const r_=({children:t})=>u.createElement("div",{className:"no-margin"}," ",t," ");class T7 extends u.Component{static defaultProps={isOpened:!1,animated:!1};renderNotAnimated(){return this.props.isOpened?u.createElement(r_,null,this.props.children):u.createElement("noscript",null)}render(){let{animated:e,isOpened:r,children:n}=this.props;return e?(n=r?n:null,u.createElement(r_,null,n)):this.renderNotAnimated()}}class I7 extends u.Component{constructor(...e){super(...e),this.setTagShown=this._setTagShown.bind(this)}_setTagShown(e,r){this.props.layoutActions.show(e,r)}showOp(e,r){let{layoutActions:n}=this.props;n.show(e,r)}render(){let{specSelectors:e,layoutSelectors:r,layoutActions:n,getComponent:a}=this.props,o=e.taggedOperations();const p=a("Collapse");return u.createElement("div",null,u.createElement("h4",{className:"overview-title"},"Overview"),o.map(((c,d)=>{let E=c.get("operations"),x=["overview-tags",d],k=r.isShown(x,!0);return u.createElement("div",{key:"overview-"+d},u.createElement("h4",{onClick:()=>n.show(x,!k),className:"link overview-tag"}," ",k?"-":"+",d),u.createElement(p,{isOpened:k,animated:!0},E.map((P=>{let{path:R,method:L,id:V}=P.toObject(),Q="operations",J=V,re=r.isShown([Q,J]);return u.createElement(M7,{key:V,path:R,method:L,id:R+"-"+L,shown:re,showOpId:J,showOpIdPrefix:Q,href:`#operation-${J}`,onClick:n.show})})).toArray()))})).valueSeq().toArray(),o.size<1&&u.createElement("h3",null," No operations defined in spec! "))}}class M7 extends u.Component{constructor(e){super(e),this.onClick=this._onClick.bind(this)}_onClick(){let{showOpId:e,showOpIdPrefix:r,onClick:n,shown:a}=this.props;n([r,e],!a)}render(){let{id:e,method:r,shown:n,href:a}=this.props;return u.createElement(t_,{href:a,onClick:this.onClick,className:"block opblock-link "+(n?"shown":"")},u.createElement("div",null,u.createElement("small",{className:`bold-label-${r}`},r.toUpperCase()),u.createElement("span",{className:"bold-label"},e)))}}class R7 extends u.Component{componentDidMount(){this.props.initialValue&&(this.inputRef.value=this.props.initialValue)}render(){const{value:e,defaultValue:r,initialValue:n,...a}=this.props;return u.createElement("input",Yr()({},a,{ref:o=>this.inputRef=o}))}}class D7 extends u.Component{render(){const{host:e,basePath:r}=this.props;return u.createElement("pre",{className:"base-url"},"[ Base URL: ",e,r," ]")}}class F7 extends u.PureComponent{render(){const{url:e,getComponent:r}=this.props,n=r("Link");return u.createElement(n,{target:"_blank",href:Dr(e)},u.createElement("span",{className:"url"}," ",e))}}class L7 extends u.Component{render(){const{info:e,url:r,host:n,basePath:a,getComponent:o,externalDocs:p,selectedServer:c,url:d}=this.props,E=e.get("version"),x=e.get("description"),k=e.get("title"),P=ba(e.get("termsOfService"),d,{selectedServer:c}),R=e.get("contact"),L=e.get("license"),V=ba(p&&p.get("url"),d,{selectedServer:c}),Q=p&&p.get("description"),J=o("Markdown",!0),re=o("Link"),me=o("VersionStamp"),le=o("OpenAPIVersion"),ve=o("InfoUrl"),be=o("InfoBasePath"),ce=o("License"),_e=o("Contact");return u.createElement("div",{className:"info"},u.createElement("hgroup",{className:"main"},u.createElement("h1",{className:"title"},k,u.createElement("span",null,E&&u.createElement(me,{version:E}),u.createElement(le,{oasVersion:"2.0"}))),n||a?u.createElement(be,{host:n,basePath:a}):null,r&&u.createElement(ve,{getComponent:o,url:r})),u.createElement("div",{className:"description"},u.createElement(J,{source:x})),P&&u.createElement("div",{className:"info__tos"},u.createElement(re,{target:"_blank",href:Dr(P)},"Terms of service")),R?.size>0&&u.createElement(_e,{getComponent:o,data:R,selectedServer:c,url:r}),L?.size>0&&u.createElement(ce,{getComponent:o,license:L,selectedServer:c,url:r}),V?u.createElement(re,{className:"info__extdocs",target:"_blank",href:Dr(V)},Q||V):null)}}const $7=L7;class B7 extends u.Component{render(){const{specSelectors:e,getComponent:r,oas3Selectors:n}=this.props,a=e.info(),o=e.url(),p=e.basePath(),c=e.host(),d=e.externalDocs(),E=n.selectedServer(),x=r("info");return u.createElement("div",null,a&&a.count()?u.createElement(x,{info:a,url:o,host:c,basePath:p,externalDocs:d,getComponent:r,selectedServer:E}):null)}}class q7 extends u.Component{render(){const{data:e,getComponent:r,selectedServer:n,url:a}=this.props,o=e.get("name","the developer"),p=ba(e.get("url"),a,{selectedServer:n}),c=e.get("email"),d=r("Link");return u.createElement("div",{className:"info__contact"},p&&u.createElement("div",null,u.createElement(d,{href:Dr(p),target:"_blank"},o," - Website")),c&&u.createElement(d,{href:Dr(`mailto:${c}`)},p?`Send email to ${o}`:`Contact ${o}`))}}const U7=q7;class z7 extends u.Component{render(){const{license:e,getComponent:r,selectedServer:n,url:a}=this.props,o=e.get("name","License"),p=ba(e.get("url"),a,{selectedServer:n}),c=r("Link");return u.createElement("div",{className:"info__license"},p?u.createElement("div",{className:"info__license__url"},u.createElement(c,{target:"_blank",href:Dr(p)},o)):u.createElement("span",null,o))}}const V7=z7;class J7 extends u.Component{render(){return null}}const Ov="Copy path to clipboard";class W7 extends u.Component{render(){let{getComponent:e}=this.props;const r=e("CopyIcon");return u.createElement("div",{className:"view-line-link copy-to-clipboard",title:Ov,"aria-label":Ov},u.createElement(cu.CopyToClipboard,{text:this.props.textToCopy},u.createElement("button",{"aria-label":Ov,title:Ov,type:"button"},u.createElement(r,null))))}}class H7 extends u.Component{render(){return u.createElement("div",{className:"footer"})}}class K7 extends u.Component{onFilterChange=e=>{const{target:{value:r}}=e;this.props.layoutActions.updateFilter(r)};render(){const{specSelectors:e,layoutSelectors:r,getComponent:n}=this.props,a=n("Col"),o=e.loadingStatus()==="loading",p=e.loadingStatus()==="failed",c=r.currentFilter(),d=["operation-filter-input"];return p&&d.push("failed"),o&&d.push("loading"),u.createElement("div",null,c===!1?null:u.createElement("div",{className:"filter-container"},u.createElement(a,{className:"filter wrapper",mobile:12},u.createElement("input",{className:d.join(" "),placeholder:"Filter by tag",type:"text",onChange:this.onFilterChange,value:typeof c=="string"?c:"",disabled:o}))))}}const V1=Function.prototype;class J1 extends u.PureComponent{static defaultProp={consumes:(0,H.fromJS)(["application/json"]),param:(0,H.fromJS)({}),onChange:V1,onChangeConsumes:V1};constructor(e,r){super(e,r),this.state={isEditBox:!1,value:""}}componentDidMount(){this.updateValues.call(this,this.props)}UNSAFE_componentWillReceiveProps(e){this.updateValues.call(this,e)}updateValues=e=>{let{param:r,isExecute:n,consumesValue:a=""}=e,o=/xml/i.test(a),p=/json/i.test(a),c=o?r.get("value_xml"):r.get("value");if(c!==void 0){let d=!c&&p?"{}":c;this.setState({value:d}),this.onChange(d,{isXml:o,isEditBox:n})}else o?this.onChange(this.sample("xml"),{isXml:o,isEditBox:n}):this.onChange(this.sample(),{isEditBox:n})};sample=e=>{let{param:r,fn:n}=this.props,a=n.inferSchema(r.toJS());return n.getSampleSchema(a,e,{includeWriteOnly:!0})};onChange=(e,{isEditBox:r,isXml:n})=>{this.setState({value:e,isEditBox:r}),this._onChange(e,n)};_onChange=(e,r)=>{(this.props.onChange||V1)(e,r)};handleOnChange=e=>{const{consumesValue:r}=this.props,n=/xml/i.test(r),a=e.target.value;this.onChange(a,{isXml:n,isEditBox:this.state.isEditBox})};toggleIsEditBox=()=>this.setState((e=>({isEditBox:!e.isEditBox})));render(){let{onChangeConsumes:e,param:r,isExecute:n,specSelectors:a,pathMethod:o,getComponent:p}=this.props;const c=p("Button"),d=p("TextArea"),E=p("HighlightCode",!0),x=p("contentType");let k=(a?a.parameterWithMetaByIdentity(o,r):r).get("errors",(0,H.List)()),P=a.contentTypeValues(o).get("requestContentType"),R=this.props.consumes&&this.props.consumes.size?this.props.consumes:J1.defaultProp.consumes,{value:L,isEditBox:V}=this.state,Q=null;_v(L)&&(Q="json");const J=`${B1(`${o[1]}${o[0]}_parameters`)}_select`;return u.createElement("div",{className:"body-param","data-param-name":r.get("name"),"data-param-in":r.get("in")},V&&n?u.createElement(d,{className:"body-param__text"+(k.count()?" invalid":""),value:L,onChange:this.handleOnChange}):L&&u.createElement(E,{className:"body-param__example",language:Q},L),u.createElement("div",{className:"body-param-options"},n?u.createElement("div",{className:"body-param-edit"},u.createElement(c,{className:V?"btn cancel body-param__example-edit":"btn edit body-param__example-edit",onClick:this.toggleIsEditBox},V?"Cancel":"Edit")):null,u.createElement("label",{htmlFor:J},u.createElement("span",null,"Parameter content type"),u.createElement(x,{value:P,contentTypes:R,onChange:e,className:"body-param-content-type",ariaLabel:"Parameter content type",controlId:J}))))}}const kv="Copy cURL command to clipboard";class G7 extends u.Component{render(){const{request:e,getComponent:r}=this.props,n=mp(e),a=r("SyntaxHighlighter",!0);return u.createElement("div",{className:"curl-command"},u.createElement("h4",null,"Curl"),u.createElement("div",{className:"copy-to-clipboard",title:kv,"aria-label":kv},u.createElement(cu.CopyToClipboard,{text:n},u.createElement("button",{"aria-label":kv,title:kv,type:"button"}))),u.createElement("div",null,u.createElement(a,{language:"bash",className:"curl microlight",renderPlainText:({children:o,PlainTextViewer:p})=>u.createElement(p,{className:"curl"},o)},n)))}}const Y7=({propKey:t,propVal:e,propClass:r})=>u.createElement("span",{className:r},u.createElement("br",null),t,": ",Gr(e));class X7 extends u.Component{static defaultProps={onTryoutClick:Function.prototype,onCancelClick:Function.prototype,onResetClick:Function.prototype,enabled:!1,hasUserEditedBody:!1,isOAS3:!1};render(){const{onTryoutClick:e,onCancelClick:r,onResetClick:n,enabled:a,hasUserEditedBody:o,isOAS3:p}=this.props,c=p&&o;return u.createElement("div",{className:c?"try-out btn-group":"try-out"},a?u.createElement("button",{className:"btn try-out__btn cancel",onClick:r},"Cancel"):u.createElement("button",{className:"btn try-out__btn",onClick:e},"Try it out "),c&&u.createElement("button",{className:"btn try-out__btn reset",onClick:n},"Reset"))}}class Q7 extends u.PureComponent{static defaultProps={alsoShow:null,children:null,bypass:!1};render(){const{bypass:e,isSwagger2:r,isOAS3:n,alsoShow:a}=this.props;return e?u.createElement("div",null,this.props.children):r&&n?u.createElement("div",{className:"version-pragma"},a,u.createElement("div",{className:"version-pragma__message version-pragma__message--ambiguous"},u.createElement("div",null,u.createElement("h3",null,"Unable to render this definition"),u.createElement("p",null,u.createElement("code",null,"swagger")," and ",u.createElement("code",null,"openapi")," fields cannot be present in the same Swagger or OpenAPI definition. Please remove one of the fields."),u.createElement("p",null,"Supported version fields are ",u.createElement("code",null,"swagger: ",'"2.0"')," and those that match ",u.createElement("code",null,"openapi: 3.0.n")," (for example, ",u.createElement("code",null,"openapi: 3.0.4"),").")))):r||n?u.createElement("div",null,this.props.children):u.createElement("div",{className:"version-pragma"},a,u.createElement("div",{className:"version-pragma__message version-pragma__message--missing"},u.createElement("div",null,u.createElement("h3",null,"Unable to render this definition"),u.createElement("p",null,"The provided definition does not specify a valid version field."),u.createElement("p",null,"Please indicate a valid Swagger or OpenAPI version field. Supported version fields are ",u.createElement("code",null,"swagger: ",'"2.0"')," and those that match ",u.createElement("code",null,"openapi: 3.0.n")," (for example, ",u.createElement("code",null,"openapi: 3.0.4"),")."))))}}const Z7=({version:t})=>u.createElement("small",null,u.createElement("pre",{className:"version"}," ",t," ")),eD=({oasVersion:t})=>u.createElement("small",{className:"version-stamp"},u.createElement("pre",{className:"version"},"OAS ",t)),tD=({enabled:t,path:e,text:r})=>u.createElement("a",{className:"nostyle",onClick:t?n=>n.preventDefault():null,href:t?`#/${e}`:null},u.createElement("span",null,r)),rD=()=>u.createElement("div",null,u.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",xmlnsXlink:"http://www.w3.org/1999/xlink",className:"svg-assets"},u.createElement("defs",null,u.createElement("symbol",{viewBox:"0 0 20 20",id:"unlocked"},u.createElement("path",{d:"M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"})),u.createElement("symbol",{viewBox:"0 0 20 20",id:"locked"},u.createElement("path",{d:"M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"})),u.createElement("symbol",{viewBox:"0 0 20 20",id:"close"},u.createElement("path",{d:"M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"})),u.createElement("symbol",{viewBox:"0 0 20 20",id:"large-arrow"},u.createElement("path",{d:"M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"})),u.createElement("symbol",{viewBox:"0 0 20 20",id:"large-arrow-down"},u.createElement("path",{d:"M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"})),u.createElement("symbol",{viewBox:"0 0 20 20",id:"large-arrow-up"},u.createElement("path",{d:"M 17.418 14.908 C 17.69 15.176 18.127 15.176 18.397 14.908 C 18.667 14.64 18.668 14.207 18.397 13.939 L 10.489 6.109 C 10.219 5.841 9.782 5.841 9.51 6.109 L 1.602 13.939 C 1.332 14.207 1.332 14.64 1.602 14.908 C 1.873 15.176 2.311 15.176 2.581 14.908 L 10 7.767 L 17.418 14.908 Z"})),u.createElement("symbol",{viewBox:"0 0 24 24",id:"jump-to"},u.createElement("path",{d:"M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"})),u.createElement("symbol",{viewBox:"0 0 24 24",id:"expand"},u.createElement("path",{d:"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"})),u.createElement("symbol",{viewBox:"0 0 15 16",id:"copy"},u.createElement("g",{transform:"translate(2, -1)"},u.createElement("path",{fill:"#ffffff",fillRule:"evenodd",d:"M2 13h4v1H2v-1zm5-6H2v1h5V7zm2 3V8l-3 3 3 3v-2h5v-2H9zM4.5 9H2v1h2.5V9zM2 12h2.5v-1H2v1zm9 1h1v2c-.02.28-.11.52-.3.7-.19.18-.42.28-.7.3H1c-.55 0-1-.45-1-1V4c0-.55.45-1 1-1h3c0-1.11.89-2 2-2 1.11 0 2 .89 2 2h3c.55 0 1 .45 1 1v5h-1V6H1v9h10v-2zM2 5h8c0-.55-.45-1-1-1H8c-.55 0-1-.45-1-1s-.45-1-1-1-1 .45-1 1-.45 1-1 1H3c-.55 0-1 .45-1 1z"}))))));var W1;function n_(t){return(W1=W1||document.createElement("textarea")).innerHTML="&"+t+";",W1.value}var nD=Object.prototype.hasOwnProperty;function aD(t,e){return!!t&&nD.call(t,e)}function a_(t){return[].slice.call(arguments,1).forEach((function(e){if(e){if(typeof e!="object")throw new TypeError(e+"must be object");Object.keys(e).forEach((function(r){t[r]=e[r]}))}})),t}var oD=/\\([\\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g;function Mm(t){return t.indexOf("\\")<0?t:t.replace(oD,"$1")}function o_(t){return!(t>=55296&&t<=57343)&&!(t>=64976&&t<=65007)&&!!(65535&~t&&(65535&t)!=65534)&&!(t>=0&&t<=8)&&t!==11&&!(t>=14&&t<=31)&&!(t>=127&&t<=159)&&!(t>1114111)}function H1(t){if(t>65535){var e=55296+((t-=65536)>>10),r=56320+(1023&t);return String.fromCharCode(e,r)}return String.fromCharCode(t)}var iD=/&([a-z#][a-z0-9]{1,31});/gi,sD=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i;function lD(t,e){var r=0,n=n_(e);return e!==n?n:e.charCodeAt(0)===35&&sD.test(e)&&o_(r=e[1].toLowerCase()==="x"?parseInt(e.slice(2),16):parseInt(e.slice(1),10))?H1(r):t}function Hp(t){return t.indexOf("&")<0?t:t.replace(iD,lD)}var cD=/[&<>"]/,uD=/[&<>"]/g,pD={"&":"&","<":"<",">":">",'"':"""};function hD(t){return pD[t]}function Mi(t){return cD.test(t)?t.replace(uD,hD):t}var fr={};function i_(t,e){return++e>=t.length-2?e:t[e].type==="paragraph_open"&&t[e].tight&&t[e+1].type==="inline"&&t[e+1].content.length===0&&t[e+2].type==="paragraph_close"&&t[e+2].tight?i_(t,e+2):e}fr.blockquote_open=function(){return`
+`},fr.blockquote_close=function(t,e){return"
"+Kp(t,e)},fr.code=function(t,e){return t[e].block?"
"+Mi(t[e].content)+"
"+Kp(t,e):""+Mi(t[e].content)+""},fr.fence=function(t,e,r,n,a){var o,p,c=t[e],d="",E=r.langPrefix;if(c.params){if(p=(o=c.params.split(/\s+/g)).join(" "),aD(a.rules.fence_custom,o[0]))return a.rules.fence_custom[o[0]](t,e,r,n,a);d=' class="'+E+Mi(Hp(Mm(p)))+'"'}return"
"+Kp(t,e)},fr.fence_custom={},fr.heading_open=function(t,e){return""},fr.heading_close=function(t,e){return" +`},fr.hr=function(t,e,r){return(r.xhtmlOut?"
":"
")+Kp(t,e)},fr.bullet_list_open=function(){return`
    +`},fr.bullet_list_close=function(t,e){return"
"+Kp(t,e)},fr.list_item_open=function(){return"
  • "},fr.list_item_close=function(){return`
  • +`},fr.ordered_list_open=function(t,e){var r=t[e];return"1?' start="'+r.order+'"':"")+`> +`},fr.ordered_list_close=function(t,e){return""+Kp(t,e)},fr.paragraph_open=function(t,e){return t[e].tight?"":"

    "},fr.paragraph_close=function(t,e){var r=!(t[e].tight&&e&&t[e-1].type==="inline"&&!t[e-1].content);return(t[e].tight?"":"

    ")+(r?Kp(t,e):"")},fr.link_open=function(t,e,r){var n=t[e].title?' title="'+Mi(Hp(t[e].title))+'"':"",a=r.linkTarget?' target="'+r.linkTarget+'"':"";return'"},fr.link_close=function(){return""},fr.image=function(t,e,r){var n=' src="'+Mi(t[e].src)+'"',a=t[e].title?' title="'+Mi(Hp(t[e].title))+'"':"";return""},fr.table_open=function(){return` +`},fr.table_close=function(){return`
    +`},fr.thead_open=function(){return` +`},fr.thead_close=function(){return` +`},fr.tbody_open=function(){return` +`},fr.tbody_close=function(){return` +`},fr.tr_open=function(){return""},fr.tr_close=function(){return` +`},fr.th_open=function(t,e){var r=t[e];return""},fr.th_close=function(){return""},fr.td_open=function(t,e){var r=t[e];return""},fr.td_close=function(){return""},fr.strong_open=function(){return""},fr.strong_close=function(){return""},fr.em_open=function(){return""},fr.em_close=function(){return""},fr.del_open=function(){return""},fr.del_close=function(){return""},fr.ins_open=function(){return""},fr.ins_close=function(){return""},fr.mark_open=function(){return""},fr.mark_close=function(){return""},fr.sub=function(t,e){return""+Mi(t[e].content)+""},fr.sup=function(t,e){return""+Mi(t[e].content)+""},fr.hardbreak=function(t,e,r){return r.xhtmlOut?`
    +`:`
    +`},fr.softbreak=function(t,e,r){return r.breaks?r.xhtmlOut?`
    +`:`
    +`:` +`},fr.text=function(t,e){return Mi(t[e].content)},fr.htmlblock=function(t,e){return t[e].content},fr.htmltag=function(t,e){return t[e].content},fr.abbr_open=function(t,e){return''},fr.abbr_close=function(){return""},fr.footnote_ref=function(t,e){var r=Number(t[e].id+1).toString(),n="fnref"+r;return t[e].subId>0&&(n+=":"+t[e].subId),'['+r+"]"},fr.footnote_block_open=function(t,e,r){return(r.xhtmlOut?`
    +`:`
    +`)+`
    +
      +`},fr.footnote_block_close=function(){return`
    +
    +`},fr.footnote_open=function(t,e){return'
  • '},fr.footnote_close=function(){return`
  • +`},fr.footnote_anchor=function(t,e){var r="fnref"+Number(t[e].id+1).toString();return t[e].subId>0&&(r+=":"+t[e].subId),' '},fr.dl_open=function(){return`
    +`},fr.dt_open=function(){return"
    "},fr.dd_open=function(){return"
    "},fr.dl_close=function(){return`
    +`},fr.dt_close=function(){return` +`},fr.dd_close=function(){return` +`};var Kp=fr.getBreak=function(e,r){return(r=i_(e,r))1||r===41&&--n<0)break;e++}return o!==e&&(a=Mm(t.src.slice(o,e)),!!t.parser.validateLink(a)&&(t.linkContent=a,t.pos=e,!0))}function l_(t,e){var r,n=e,a=t.posMax,o=t.src.charCodeAt(e);if(o!==34&&o!==39&&o!==40)return!1;for(e++,o===40&&(o=41);e=t.length)&&!wD.test(t[e])}function dd(t,e,r){return t.substr(0,e)+r+t.substr(e+1)}var X1=[["block",function(e){e.inlineMode?e.tokens.push({type:"inline",content:e.src.replace(/\n/g," ").trim(),level:0,lines:[0,1],children:[]}):e.block.parse(e.src,e.options,e.env,e.tokens)}],["abbr",function(e){var r,n,a,o,p=e.tokens;if(!e.inlineMode){for(r=1,n=p.length-1;r0?c[r].count:1,a=0;a=0;r--)if((p=o[r]).type==="text"){for(E=0,c=p.content,k.lastIndex=0,x=p.level,d=[];P=k.exec(c);)k.lastIndex>E&&d.push({type:"text",content:c.slice(E,P.index+P[1].length),level:x}),d.push({type:"abbr_open",title:e.env.abbreviations[":"+P[2]],level:x++}),d.push({type:"text",content:P[2],level:x}),d.push({type:"abbr_close",level:--x}),E=k.lastIndex-P[3].length;d.length&&(E=0;p--)if(e.tokens[p].type==="inline")for(r=(o=e.tokens[p].children).length-1;r>=0;r--)(n=o[r]).type==="text"&&(a=vD(a=n.content),mD.test(a)&&(a=a.replace(/\+-/g,"±").replace(/\.{2,}/g,"…").replace(/([?!])…/g,"$1..").replace(/([?!]){4,}/g,"$1$1$1").replace(/,{2,}/g,",").replace(/(^|[^-])---([^-]|$)/gm,"$1—$2").replace(/(^|\s)--(\s|$)/gm,"$1–$2").replace(/(^|[^-\s])--([^-\s]|$)/gm,"$1–$2")),n.content=a)}}],["smartquotes",function(e){var r,n,a,o,p,c,d,E,x,k,P,R,L,V,Q,J,re;if(e.options.typographer){for(re=[],Q=e.tokens.length-1;Q>=0;Q--)if(e.tokens[Q].type==="inline"){for(J=e.tokens[Q].children,re.length=0,r=0;r=0&&!(re[L].level<=d);L--);re.length=L+1,p=0,c=(a=n.content).length;e:for(;p=0&&(k=re[L],!(re[L].level=(a=t.eMarks[e])||(r=t.src.charCodeAt(n++))!==42&&r!==45&&r!==43||n=a||(r=t.src.charCodeAt(n++))<48||r>57)return-1;for(;;){if(n>=a)return-1;if(!((r=t.src.charCodeAt(n++))>=48&&r<=57)){if(r===41||r===46)break;return-1}}return n=this.eMarks[e]},Yp.prototype.skipEmptyLines=function(e){for(var r=this.lineMax;en;)if(r!==this.src.charCodeAt(--e))return e+1;return e},Yp.prototype.getLines=function(e,r,n,a){var o,p,c,d,E,x=e;if(e>=r)return"";if(x+1===r)return p=this.bMarks[x]+Math.min(this.tShift[x],n),c=a?this.eMarks[x]+1:this.eMarks[x],this.src.slice(p,c);for(d=new Array(r-e),o=0;xn&&(E=n),E<0&&(E=0),p=this.bMarks[x]+E,c=x+1]/,SD=/^<\/([a-zA-Z]{1,15})[\s>]/;function Q1(t,e){var r=t.bMarks[e]+t.blkIndent,n=t.eMarks[e];return t.src.substr(r,n-r)}function Av(t,e){var r,n,a=t.bMarks[e]+t.tShift[e],o=t.eMarks[e];return a>=o||(n=t.src.charCodeAt(a++))!==126&&n!==58||a===(r=t.skipSpaces(a))||r>=o?-1:r}var Cv=[["code",function(e,r,n){var a,o;if(e.tShift[r]-e.blkIndent<4)return!1;for(o=a=r+1;a=4))break;o=++a}return e.line=a,e.tokens.push({type:"code",content:e.getLines(r,o,4+e.blkIndent,!0),block:!0,lines:[r,e.line],level:e.level}),!0}],["fences",function(e,r,n,a){var o,p,c,d,E,x=!1,k=e.bMarks[r]+e.tShift[r],P=e.eMarks[r];if(k+3>P||(o=e.src.charCodeAt(k))!==126&&o!==96||(E=k,(p=(k=e.skipChars(k,o))-E)<3)||(c=e.src.slice(k,P).trim()).indexOf("`")>=0)return!1;if(a)return!0;for(d=r;!(++d>=n)&&!((k=E=e.bMarks[d]+e.tShift[d])<(P=e.eMarks[d])&&e.tShift[d]=4||(k=e.skipChars(k,o))-EJ||e.src.charCodeAt(Q++)!==62||e.level>=e.options.maxNesting)return!1;if(a)return!0;for(e.src.charCodeAt(Q)===32&&Q++,E=e.blkIndent,e.blkIndent=0,d=[e.bMarks[r]],e.bMarks[r]=Q,p=(Q=Q=J,c=[e.tShift[r]],e.tShift[r]=Q-e.bMarks[r],P=e.parser.ruler.getRules("blockquote"),o=r+1;o=(J=e.eMarks[o]));o++)if(e.src.charCodeAt(Q++)!==62){if(p)break;for(V=!1,R=0,L=P.length;R=J,c.push(e.tShift[o]),e.tShift[o]=Q-e.bMarks[o];for(x=e.parentType,e.parentType="blockquote",e.tokens.push({type:"blockquote_open",lines:k=[r,0],level:e.level++}),e.parser.tokenize(e,r,o),e.tokens.push({type:"blockquote_close",level:--e.level}),e.parentType=x,k[1]=e.line,R=0;RE||(o=e.src.charCodeAt(d++))!==42&&o!==45&&o!==95)return!1;for(p=1;d=0)Q=!0;else{if(!((P=f_(e,r))>=0))return!1;Q=!1}if(e.level>=e.options.maxNesting)return!1;if(V=e.src.charCodeAt(P-1),a)return!0;for(re=e.tokens.length,Q?(k=e.bMarks[r]+e.tShift[r],L=Number(e.src.substr(k,P-k-1)),e.tokens.push({type:"ordered_list_open",order:L,lines:le=[r,0],level:e.level++})):e.tokens.push({type:"bullet_list_open",lines:le=[r,0],level:e.level++}),o=r,me=!1,be=e.parser.ruler.getRules("list");!(!(o=e.eMarks[o]?1:J-P)>4&&(R=1),R<1&&(R=1),p=P-e.bMarks[o]+R,e.tokens.push({type:"list_item_open",lines:ve=[r,0],level:e.level++}),d=e.blkIndent,E=e.tight,c=e.tShift[r],x=e.parentType,e.tShift[r]=J-e.bMarks[r],e.blkIndent=p,e.tight=!0,e.parentType="list",e.parser.tokenize(e,r,n,!0),e.tight&&!me||(xe=!1),me=e.line-r>1&&e.isEmpty(e.line-1),e.blkIndent=d,e.tShift[r]=c,e.tight=E,e.parentType=x,e.tokens.push({type:"list_item_close",level:--e.level}),o=r=e.line,ve[1]=o,J=e.bMarks[r],o>=n)||e.isEmpty(o)||e.tShift[o]k||e.src.charCodeAt(x)!==91||e.src.charCodeAt(x+1)!==94||e.level>=e.options.maxNesting)return!1;for(d=x+2;d=k||e.src.charCodeAt(++d)!==58)&&(a||(d++,e.env.footnotes||(e.env.footnotes={}),e.env.footnotes.refs||(e.env.footnotes.refs={}),E=e.src.slice(x+2,d-2),e.env.footnotes.refs[":"+E]=-1,e.tokens.push({type:"footnote_reference_open",label:E,level:e.level++}),o=e.bMarks[r],p=e.tShift[r],c=e.parentType,e.tShift[r]=e.skipSpaces(d)-d,e.bMarks[r]=d,e.blkIndent+=4,e.parentType="footnote",e.tShift[r]=E||(o=e.src.charCodeAt(d))!==35||d>=E)return!1;for(p=1,o=e.src.charCodeAt(++d);o===35&&d6||dd&&e.src.charCodeAt(c-1)===32&&(E=c),e.line=r+1,e.tokens.push({type:"heading_open",hLevel:p,lines:[r,e.line],level:e.level}),d=n)&&!(e.tShift[c]3)&&!((o=e.bMarks[c]+e.tShift[c])>=(p=e.eMarks[c]))&&((a=e.src.charCodeAt(o))===45||a===61)&&(o=e.skipChars(o,a),!((o=e.skipSpaces(o))3||d+2>=E||e.src.charCodeAt(d)!==60)return!1;if((o=e.src.charCodeAt(d+1))===33||o===63){if(a)return!0}else{if(o!==47&&!(function(P){var R=32|P;return R>=97&&R<=122})(o))return!1;if(o===47){if(!(p=e.src.slice(d,E).match(SD)))return!1}else if(!(p=e.src.slice(d,E).match(ED)))return!1;if(g_[p[1].toLowerCase()]!==!0)return!1;if(a)return!0}for(c=r+1;cn||(E=r+1,e.tShift[E]=e.eMarks[E]||(o=e.src.charCodeAt(c))!==124&&o!==45&&o!==58||(p=Q1(e,r+1),!/^[-:| ]+$/.test(p))||(x=p.split("|"))<=2)return!1;for(P=[],d=0;d=0;if(k=r+1,e.isEmpty(k)&&++k>n||e.tShift[k]=e.options.maxNesting)return!1;x=e.tokens.length,e.tokens.push({type:"dl_open",lines:E=[r,0],level:e.level++}),c=r,p=k;e:for(;;){for(re=!0,J=!1,e.tokens.push({type:"dt_open",lines:[c,c],level:e.level++}),e.tokens.push({type:"inline",content:e.getLines(c,c+1,e.blkIndent,!1).trim(),level:e.level+1,lines:[c,c],children:[]}),e.tokens.push({type:"dt_close",level:--e.level});;){if(e.tokens.push({type:"dd_open",lines:d=[k,0],level:e.level++}),Q=e.tight,R=e.ddIndent,P=e.blkIndent,V=e.tShift[p],L=e.parentType,e.blkIndent=e.ddIndent=e.tShift[p]+2,e.tShift[p]=o-e.bMarks[p],e.tight=!0,e.parentType="deflist",e.parser.tokenize(e,p,n,!0),e.tight&&!J||(re=!1),J=e.line-p>1&&e.isEmpty(e.line-1),e.tShift[p]=V,e.tight=Q,e.parentType=L,e.blkIndent=P,e.ddIndent=R,e.tokens.push({type:"dd_close",level:--e.level}),d[1]=k=e.line,k>=n||e.tShift[k]=n||(c=k,e.isEmpty(c))||e.tShift[c]=n||(e.isEmpty(p)&&p++,p>=n)||e.tShift[p]3)){for(o=!1,p=0,c=d.length;p=r))&&!(t.tShift[p]=0&&(t=t.replace(xD,(function(c,d){var E;return t.charCodeAt(d)===10?(o=d+1,p=0,c):(E=" ".slice((d-o-p)%4),p=d-o+1,E)}))),a=new Yp(t,this,e,r,n),this.tokenize(a,a.line,a.lineMax)};for(var ew=[],y_=0;y_<256;y_++)ew.push(0);function v_(t){return t>=48&&t<=57||t>=65&&t<=90||t>=97&&t<=122}function b_(t,e){var r,n,a,o=e,p=!0,c=!0,d=t.posMax,E=t.src.charCodeAt(e);for(r=e>0?t.src.charCodeAt(e-1):-1;o=d&&(p=!1),(a=o-e)>=4?p=c=!1:((n=o?@[]^_`{|}~-".split("").forEach((function(t){ew[t.charCodeAt(0)]=1}));var AD=/\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g,CD=/\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g,jD=["coap","doi","javascript","aaa","aaas","about","acap","cap","cid","crid","data","dav","dict","dns","file","ftp","geo","go","gopher","h323","http","https","iax","icap","im","imap","info","ipp","iris","iris.beep","iris.xpc","iris.xpcs","iris.lwz","ldap","mailto","mid","msrp","msrps","mtqp","mupdate","news","nfs","ni","nih","nntp","opaquelocktoken","pop","pres","rtsp","service","session","shttp","sieve","sip","sips","sms","snmp","soap.beep","soap.beeps","tag","tel","telnet","tftp","thismessage","tn3270","tip","tv","urn","vemmi","ws","wss","xcon","xcon-userid","xmlrpc.beep","xmlrpc.beeps","xmpp","z39.50r","z39.50s","adiumxtra","afp","afs","aim","apt","attachment","aw","beshare","bitcoin","bolo","callto","chrome","chrome-extension","com-eventbrite-attendee","content","cvs","dlna-playsingle","dlna-playcontainer","dtn","dvb","ed2k","facetime","feed","finger","fish","gg","git","gizmoproject","gtalk","hcp","icon","ipn","irc","irc6","ircs","itms","jar","jms","keyparc","lastfm","ldaps","magnet","maps","market","message","mms","ms-help","msnim","mumble","mvn","notes","oid","palm","paparazzi","platform","proxy","psyc","query","res","resource","rmi","rsync","rtmp","secondlife","sftp","sgn","skype","smb","soldat","spotify","ssh","steam","svn","teamspeak","things","udp","unreal","ut2004","ventrilo","view-source","webcal","wtai","wyciwyg","xfire","xri","ymsgr"],PD=/^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/,ND=/^<([a-zA-Z.\-]{1,25}):([^<>\x00-\x20]*)>/;function jv(t,e){return t=t.source,e=e||"",function r(n,a){return n?(a=a.source||a,t=t.replace(n,a),r):new RegExp(t,e)}}var TD=jv(/(?:unquoted|single_quoted|double_quoted)/)("unquoted",/[^"'=<>`\x00-\x20]+/)("single_quoted",/'[^']*'/)("double_quoted",/"[^"]*"/)(),ID=jv(/(?:\s+attr_name(?:\s*=\s*attr_value)?)/)("attr_name",/[a-zA-Z_:][a-zA-Z0-9:._-]*/)("attr_value",TD)(),MD=jv(/<[A-Za-z][A-Za-z0-9]*attribute*\s*\/?>/)("attribute",ID)(),RD=jv(/^(?:open_tag|close_tag|comment|processing|declaration|cdata)/)("open_tag",MD)("close_tag",/<\/[A-Za-z][A-Za-z0-9]*\s*>/)("comment",/|/)("processing",/<[?].*?[?]>/)("declaration",/]*>/)("cdata",//)(),DD=/^&#((?:x[a-f0-9]{1,8}|[0-9]{1,8}));/i,FD=/^&([a-z][a-z0-9]{1,31});/i,tw=[["text",function(e,r){for(var n=e.pos;n=0&&e.pending.charCodeAt(n)===32)if(n>=1&&e.pending.charCodeAt(n-1)===32){for(var p=n-2;p>=0;p--)if(e.pending.charCodeAt(p)!==32){e.pending=e.pending.substring(0,p+1);break}e.push({type:"hardbreak",level:e.level})}else e.pending=e.pending.slice(0,-1),e.push({type:"softbreak",level:e.level});else e.push({type:"softbreak",level:e.level});for(o++;o=d||e.src.charCodeAt(E+1)!==126||e.level>=e.options.maxNesting||(p=E>0?e.src.charCodeAt(E-1):-1,c=e.src.charCodeAt(E+2),p===126)||c===126||c===32||c===10)return!1;for(a=E+2;aE+3)return e.pos+=a-E,r||(e.pending+=e.src.slice(E,a)),!0;for(e.pos=E+2,o=1;e.pos+1=d||e.src.charCodeAt(E+1)!==43||e.level>=e.options.maxNesting||(p=E>0?e.src.charCodeAt(E-1):-1,c=e.src.charCodeAt(E+2),p===43)||c===43||c===32||c===10)return!1;for(a=E+2;a=d||e.src.charCodeAt(E+1)!==61||e.level>=e.options.maxNesting||(p=E>0?e.src.charCodeAt(E-1):-1,c=e.src.charCodeAt(E+2),p===61)||c===61||c===32||c===10)return!1;for(a=E+2;a=e.options.maxNesting)return!1;for(e.pos=k+n,d=[n];e.pos=o||e.level>=e.options.maxNesting)return!1;for(e.pos=p+1;e.pos=o||e.level>=e.options.maxNesting)return!1;for(e.pos=p+1;e.pos=e.options.maxNesting||(n=L+1,(a=Rm(e,L))<0))return!1;if((d=a+1)=R)return!1;for(L=d,s_(e,d)?(p=e.linkContent,d=e.pos):p="",L=d;d=R||e.src.charCodeAt(d)!==41)return e.pos=P,!1;d++}else{if(e.linkLevel>0)return!1;for(;d=0?o=e.src.slice(L,d++):d=L-1),o||(o===void 0&&(d=a+1),o=e.src.slice(n,a)),!(E=e.env.references[c_(o)]))return e.pos=P,!1;p=E.href,c=E.title}return r||(e.pos=n,e.posMax=a,k?e.push({type:"image",src:p,title:c,alt:e.src.substr(n,a-n),level:e.level}):(e.push({type:"link_open",href:p,title:c,level:e.level++}),e.linkLevel++,e.parser.tokenize(e),e.linkLevel--,e.push({type:"link_close",level:--e.level}))),e.pos=d,e.posMax=R,!0}],["footnote_inline",function(e,r){var n,a,o,p,c=e.posMax,d=e.pos;return!(d+2>=c)&&e.src.charCodeAt(d)===94&&e.src.charCodeAt(d+1)===91&&!(e.level>=e.options.maxNesting)&&(n=d+2,!((a=Rm(e,d+1))<0)&&(r||(e.env.footnotes||(e.env.footnotes={}),e.env.footnotes.list||(e.env.footnotes.list=[]),o=e.env.footnotes.list.length,e.pos=n,e.posMax=a,e.push({type:"footnote_ref",id:o,level:e.level}),e.linkLevel++,p=e.tokens.length,e.parser.tokenize(e),e.env.footnotes.list[o]={tokens:e.tokens.splice(p)},e.linkLevel--),e.pos=a+1,e.posMax=c,!0))}],["footnote_ref",function(e,r){var n,a,o,p,c=e.posMax,d=e.pos;if(d+3>c||!e.env.footnotes||!e.env.footnotes.refs||e.src.charCodeAt(d)!==91||e.src.charCodeAt(d+1)!==94||e.level>=e.options.maxNesting)return!1;for(a=d+2;a=c)&&(a++,n=e.src.slice(d+2,a-1),e.env.footnotes.refs[":"+n]!==void 0&&(r||(e.env.footnotes.list||(e.env.footnotes.list=[]),e.env.footnotes.refs[":"+n]<0?(o=e.env.footnotes.list.length,e.env.footnotes.list[o]={label:n,count:0},e.env.footnotes.refs[":"+n]=o):o=e.env.footnotes.refs[":"+n],p=e.env.footnotes.list[o].count,e.env.footnotes.list[o].count++,e.push({type:"footnote_ref",id:o,subId:p,level:e.level})),e.pos=a,e.posMax=c,!0))}],["autolink",function(e,r){var n,a,o,p,c,d=e.pos;return e.src.charCodeAt(d)===60&&!((n=e.src.slice(d)).indexOf(">")<0)&&((a=n.match(ND))?!(jD.indexOf(a[1].toLowerCase())<0)&&(c=G1(p=a[0].slice(1,-1)),!!e.parser.validateLink(p)&&(r||(e.push({type:"link_open",href:c,level:e.level}),e.push({type:"text",content:p,level:e.level+1}),e.push({type:"link_close",level:e.level})),e.pos+=a[0].length,!0)):!!(o=n.match(PD))&&(c=G1("mailto:"+(p=o[0].slice(1,-1))),!!e.parser.validateLink(c)&&(r||(e.push({type:"link_open",href:c,level:e.level}),e.push({type:"text",content:p,level:e.level+1}),e.push({type:"link_close",level:e.level})),e.pos+=o[0].length,!0)))}],["htmltag",function(e,r){var n,a,o,p=e.pos;return!!e.options.html&&(o=e.posMax,!(e.src.charCodeAt(p)!==60||p+2>=o)&&!((n=e.src.charCodeAt(p+1))!==33&&n!==63&&n!==47&&!(function(d){var E=32|d;return E>=97&&E<=122})(n))&&!!(a=e.src.slice(p).match(RD))&&(r||e.push({type:"htmltag",content:e.src.slice(p,p+a[0].length),level:e.level}),e.pos+=a[0].length,!0))}],["entity",function(e,r){var n,a,o=e.pos,p=e.posMax;if(e.src.charCodeAt(o)!==38)return!1;if(o+10)t.pos=r;else{for(e=0;e=o)break}else t.pending+=t.src[t.pos++]}t.pending&&t.pushPending()},Pv.prototype.parse=function(t,e,r,n){var a=new Gp(t,this,e,r,n);this.tokenize(a)};var $D={default:{options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkTarget:"",typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:20},components:{core:{rules:["block","inline","references","replacements","smartquotes","references","abbr2","footnote_tail"]},block:{rules:["blockquote","code","fences","footnote","heading","hr","htmlblock","lheading","list","paragraph","table"]},inline:{rules:["autolink","backticks","del","emphasis","entity","escape","footnote_ref","htmltag","links","newline","text"]}}},full:{options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkTarget:"",typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:20},components:{core:{},block:{},inline:{}}},commonmark:{options:{html:!0,xhtmlOut:!0,breaks:!1,langPrefix:"language-",linkTarget:"",typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:20},components:{core:{rules:["block","inline","references","abbr2"]},block:{rules:["blockquote","code","fences","heading","hr","htmlblock","lheading","list","paragraph"]},inline:{rules:["autolink","backticks","emphasis","entity","escape","htmltag","links","newline","text"]}}}};function w_(t,e,r){this.src=e,this.env=r,this.options=t.options,this.tokens=[],this.inlineMode=!1,this.inline=t.inline,this.block=t.block,this.renderer=t.renderer,this.typographer=t.typographer}function Nc(t,e){typeof t!="string"&&(e=t,t="default"),e&&e.linkify!=null&&console.warn(`linkify option is removed. Use linkify plugin instead: + +import Remarkable from 'remarkable'; +import linkify from 'remarkable/linkify'; +new Remarkable().use(linkify) +`),this.inline=new Pv,this.block=new Z1,this.core=new d_,this.renderer=new K1,this.ruler=new Ri,this.options={},this.configure($D[t]),this.set(e||{})}Nc.prototype.set=function(t){a_(this.options,t)},Nc.prototype.configure=function(t){var e=this;if(!t)throw new Error("Wrong `remarkable` preset, check name/content");t.options&&e.set(t.options),t.components&&Object.keys(t.components).forEach((function(r){t.components[r].rules&&e[r].ruler.enable(t.components[r].rules,!0)}))},Nc.prototype.use=function(t,e){return t(this,e),this},Nc.prototype.parse=function(t,e){var r=new w_(this,t,e);return this.core.process(r),r.tokens},Nc.prototype.render=function(t,e){return e=e||{},this.renderer.render(this.parse(t,e),this.options,e)},Nc.prototype.parseInline=function(t,e){var r=new w_(this,t,e);return r.inlineMode=!0,this.core.process(r),r.tokens},Nc.prototype.renderInline=function(t,e){return e=e||{},this.renderer.render(this.parseInline(t,e),this.options,e)};function E_(t,e){if(Array.prototype.indexOf)return t.indexOf(e);for(var r=0,n=t.length;r=0;r--)e(t[r])===!0&&t.splice(r,1)}function rw(t){throw new Error("Unhandled case for value: '".concat(t,"'"))}var nw=(function(){function t(e){e===void 0&&(e={}),this.tagName="",this.attrs={},this.innerHTML="",this.whitespaceRegex=/\s+/,this.tagName=e.tagName||"",this.attrs=e.attrs||{},this.innerHTML=e.innerHtml||e.innerHTML||""}return t.prototype.setTagName=function(e){return this.tagName=e,this},t.prototype.getTagName=function(){return this.tagName||""},t.prototype.setAttr=function(e,r){return this.getAttrs()[e]=r,this},t.prototype.getAttr=function(e){return this.getAttrs()[e]},t.prototype.setAttrs=function(e){return Object.assign(this.getAttrs(),e),this},t.prototype.getAttrs=function(){return this.attrs||(this.attrs={})},t.prototype.setClass=function(e){return this.setAttr("class",e)},t.prototype.addClass=function(e){for(var r,n=this.getClass(),a=this.whitespaceRegex,o=n?n.split(a):[],p=e.split(a);r=p.shift();)E_(o,r)===-1&&o.push(r);return this.getAttrs().class=o.join(" "),this},t.prototype.removeClass=function(e){for(var r,n=this.getClass(),a=this.whitespaceRegex,o=n?n.split(a):[],p=e.split(a);o.length&&(r=p.shift());){var c=E_(o,r);c!==-1&&o.splice(c,1)}return this.getAttrs().class=o.join(" "),this},t.prototype.getClass=function(){return this.getAttrs().class||""},t.prototype.hasClass=function(e){return(" "+this.getClass()+" ").indexOf(" "+e+" ")!==-1},t.prototype.setInnerHTML=function(e){return this.innerHTML=e,this},t.prototype.setInnerHtml=function(e){return this.setInnerHTML(e)},t.prototype.getInnerHTML=function(){return this.innerHTML||""},t.prototype.getInnerHtml=function(){return this.getInnerHTML()},t.prototype.toAnchorString=function(){var e=this.getTagName(),r=this.buildAttrsStr();return["<",e,r=r?" "+r:"",">",this.getInnerHtml(),""].join("")},t.prototype.buildAttrsStr=function(){if(!this.attrs)return"";var e=this.getAttrs(),r=[];for(var n in e)e.hasOwnProperty(n)&&r.push(n+'="'+e[n]+'"');return r.join(" ")},t})(),S_=(function(){function t(e){e===void 0&&(e={}),this.newWindow=!1,this.truncate={},this.className="",this.newWindow=e.newWindow||!1,this.truncate=e.truncate||{},this.className=e.className||""}return t.prototype.build=function(e){return new nw({tagName:"a",attrs:this.createAttrs(e),innerHtml:this.processAnchorText(e.getAnchorText())})},t.prototype.createAttrs=function(e){var r={href:e.getAnchorHref()},n=this.createCssClass(e);return n&&(r.class=n),this.newWindow&&(r.target="_blank",r.rel="noopener noreferrer"),this.truncate&&this.truncate.length&&this.truncate.length=R)return L.host.length==c?(L.host.substr(0,c-x)+d).substr(0,R+E):P(Q,R).substr(0,R+E);var J="";if(L.path&&(J+="/"+L.path),L.query&&(J+="?"+L.query),J){if((Q+J).length>=R)return(Q+J).length==c?(Q+J).substr(0,c):(Q+P(J,R-Q.length)).substr(0,R+E);Q+=J}if(L.fragment){var re="#"+L.fragment;if((Q+re).length>=R)return(Q+re).length==c?(Q+re).substr(0,c):(Q+P(re,R-Q.length)).substr(0,R+E);Q+=re}if(L.scheme&&L.host){var me=L.scheme+"://";if((Q+me).length0&&(le=Q.substr(-1*Math.floor(R/2))),(Q.substr(0,Math.ceil(R/2))+d+le).substr(0,R+E)})(e,n):a==="middle"?(function(p,c,d){if(p.length<=c)return p;var E,x;d==null?(d="…",E=8,x=3):(E=d.length,x=d.length);var k=c-x,P="";return k>0&&(P=p.substr(-1*Math.floor(k/2))),(p.substr(0,Math.ceil(k/2))+d+P).substr(0,k+E)})(e,n):(function(p,c,d){return(function(x,k,P){var R;return x.length>k&&(P==null?(P="…",R=3):R=P.length,x=x.substring(0,k-R)+P),x})(p,c,d)})(e,n)},t})(),fd=(function(){function t(e){this.__jsduckDummyDocProp=null,this.matchedText="",this.offset=0,this.tagBuilder=e.tagBuilder,this.matchedText=e.matchedText,this.offset=e.offset}return t.prototype.getMatchedText=function(){return this.matchedText},t.prototype.setOffset=function(e){this.offset=e},t.prototype.getOffset=function(){return this.offset},t.prototype.getCssClassSuffixes=function(){return[this.getType()]},t.prototype.buildTag=function(){return this.tagBuilder.build(this)},t})(),aw=function(t,e){return aw=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&(r[a]=n[a])},aw(t,e)};function Vl(t,e){if(typeof e!="function"&&e!==null)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");function r(){this.constructor=t}aw(t,e),t.prototype=e===null?Object.create(e):(r.prototype=e.prototype,new r)}var fa=function(){return fa=Object.assign||function(e){for(var r,n=1,a=arguments.length;n-1},t.isValidUriScheme=function(e){var r=e.match(this.uriSchemeRegex),n=r&&r[0].toLowerCase();return n!=="javascript:"&&n!=="vbscript:"},t.urlMatchDoesNotHaveProtocolOrDot=function(e,r){return!(!e||r&&this.hasFullProtocolRegex.test(r)||e.indexOf(".")!==-1)},t.urlMatchDoesNotHaveAtLeastOneWordChar=function(e,r){return!(!e||!r)&&!this.hasFullProtocolRegex.test(r)&&!this.hasWordCharAfterProtocolRegex.test(e)},t.hasFullProtocolRegex=/^[A-Za-z][-.+A-Za-z0-9]*:\/\//,t.uriSchemeRegex=/^[A-Za-z][-.+A-Za-z0-9]*:/,t.hasWordCharAfterProtocolRegex=new RegExp(":[^\\s]*?["+j_+"]"),t.ipRegex=/[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?(:[0-9]*)?\/?$/,t})(),GD=(x_=new RegExp("[/?#](?:["+ma+"\\-+&@#/%=~_()|'$*\\[\\]{}?!:,.;^✓]*["+ma+"\\-+&@#/%=~_()|'$*\\[\\]{}✓])?"),new RegExp(["(?:","(",/(?:[A-Za-z][-.+A-Za-z0-9]{0,63}:(?![A-Za-z][-.+A-Za-z0-9]{0,63}:\/\/)(?!\d+\/?)(?:\/\/)?)/.source,sw(2),")","|","(","(//)?",/(?:www\.)/.source,sw(6),")","|","(","(//)?",sw(10)+"\\.",T_.source,"(?![-"+zD+"])",")",")","(?::[0-9]+)?","(?:"+x_.source+")?"].join(""),"gi")),YD=new RegExp("["+ma+"]"),M_=(function(t){function e(r){var n=t.call(this,r)||this;return n.stripPrefix={scheme:!0,www:!0},n.stripTrailingSlash=!0,n.decodePercentEncoding=!0,n.matcherRegex=GD,n.wordCharRegExp=YD,n.stripPrefix=r.stripPrefix,n.stripTrailingSlash=r.stripTrailingSlash,n.decodePercentEncoding=r.decodePercentEncoding,n}return Vl(e,t),e.prototype.parseMatches=function(r){for(var n,a=this.matcherRegex,o=this.stripPrefix,p=this.stripTrailingSlash,c=this.decodePercentEncoding,d=this.tagBuilder,E=[],x=function(){var P=n[0],R=n[1],L=n[4],V=n[5],Q=n[9],J=n.index,re=V||Q,me=r.charAt(J-1);if(!KD.isValid(P,R)||J>0&&me==="@"||J>0&&re&&k.wordCharRegExp.test(me))return"continue";if(/\?$/.test(P)&&(P=P.substr(0,P.length-1)),k.matchHasUnbalancedClosingParen(P))P=P.substr(0,P.length-1);else{var le=k.matchHasInvalidCharAfterTld(P,R);le>-1&&(P=P.substr(0,le))}var ve=["http://","https://"].find((function(Ae){return!!R&&R.indexOf(Ae)!==-1}));if(ve){var be=P.indexOf(ve);P=P.substr(be),R=R.substr(be),J+=be}var ce=R?"scheme":L?"www":"tld",_e=!!R;E.push(new C_({tagBuilder:d,matchedText:P,offset:J,urlMatchType:ce,url:P,protocolUrlMatch:_e,protocolRelativeMatch:!!re,stripPrefix:o,stripTrailingSlash:p,decodePercentEncoding:c}))},k=this;(n=a.exec(r))!==null;)x();return E},e.prototype.matchHasUnbalancedClosingParen=function(r){var n,a=r.charAt(r.length-1);if(a===")")n="(";else if(a==="]")n="[";else{if(a!=="}")return!1;n="{"}for(var o=0,p=0,c=r.length-1;p-1&&c-d<=140){var Q=r.slice(d,c),J=new O_({tagBuilder:n,matchedText:Q,offset:d,serviceName:a,hashtag:Q.slice(1)});o.push(J)}}},e})(md),XD=["twitter","facebook","instagram","tiktok"],QD=new RegExp("".concat(/(?:(?:(?:(\+)?\d{1,3}[-\040.]?)?\(?\d{3}\)?[-\040.]?\d{3}[-\040.]?\d{4})|(?:(\+)(?:9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)[-\040.]?(?:\d[-\040.]?){6,12}\d+))([,;]+[0-9]+#?)*/.source,"|").concat(/(0([1-9]{1}-?[1-9]\d{3}|[1-9]{2}-?\d{3}|[1-9]{2}\d{1}-?\d{2}|[1-9]{2}\d{2}-?\d{1})-?\d{4}|0[789]0-?\d{4}-?\d{4}|050-?\d{4}-?\d{4})/.source),"g"),F_=(function(t){function e(){var r=t!==null&&t.apply(this,arguments)||this;return r.matcherRegex=QD,r}return Vl(e,t),e.prototype.parseMatches=function(r){for(var n,a=this.matcherRegex,o=this.tagBuilder,p=[];(n=a.exec(r))!==null;){var c=n[0],d=c.replace(/[^0-9,;#]/g,""),E=!(!n[1]&&!n[2]),x=n.index==0?"":r.substr(n.index-1,1),k=r.substr(n.index+c.length,1),P=!x.match(/\d/)&&!k.match(/\d/);this.testMatch(n[3])&&this.testMatch(c)&&P&&p.push(new A_({tagBuilder:o,matchedText:c,offset:n.index,number:d,plusSign:E}))}return p},e.prototype.testMatch=function(r){return qD.test(r)},e})(md),ZD=new RegExp("@[_".concat(ma,"]{1,50}(?![_").concat(ma,"])"),"g"),eF=new RegExp("@[_.".concat(ma,"]{1,30}(?![_").concat(ma,"])"),"g"),tF=new RegExp("@[-_.".concat(ma,"]{1,50}(?![-_").concat(ma,"])"),"g"),rF=new RegExp("@[_.".concat(ma,"]{1,23}[_").concat(ma,"](?![_").concat(ma,"])"),"g"),nF=new RegExp("[^"+ma+"]"),L_=(function(t){function e(r){var n=t.call(this,r)||this;return n.serviceName="twitter",n.matcherRegexes={twitter:ZD,instagram:eF,soundcloud:tF,tiktok:rF},n.nonWordCharRegex=nF,n.serviceName=r.serviceName,n}return Vl(e,t),e.prototype.parseMatches=function(r){var n,a=this.serviceName,o=this.matcherRegexes[this.serviceName],p=this.nonWordCharRegex,c=this.tagBuilder,d=[];if(!o)return d;for(;(n=o.exec(r))!==null;){var E=n.index,x=r.charAt(E-1);if(E===0||p.test(x)){var k=n[0].replace(/\.+$/g,""),P=k.slice(1);d.push(new k_({tagBuilder:c,matchedText:k,offset:E,serviceName:a,mention:P}))}}return d},e})(md);function aF(t,e){for(var r=e.onOpenTag,n=e.onCloseTag,a=e.onText,o=e.onComment,p=e.onDoctype,c=new Jl,d=0,E=t.length,x=0,k=0,P=c;d"?(P=new Jl(fa(fa({},P),{name:Ot()})),It()):ow.test(et)||BD.test(et)||et===":"||_t()}function J(et){et===">"?_t():ow.test(et)?x=3:_t()}function re(et){Qp.test(et)||(et==="/"?x=12:et===">"?It():et==="<"?qt():et==="="||iw.test(et)||UD.test(et)?_t():x=5)}function me(et){Qp.test(et)?x=6:et==="/"?x=12:et==="="?x=7:et===">"?It():et==="<"?qt():iw.test(et)&&_t()}function le(et){Qp.test(et)||(et==="/"?x=12:et==="="?x=7:et===">"?It():et==="<"?qt():iw.test(et)?_t():x=5)}function ve(et){Qp.test(et)||(et==='"'?x=8:et==="'"?x=9:/[>=`]/.test(et)?_t():et==="<"?qt():x=10)}function be(et){et==='"'&&(x=11)}function ce(et){et==="'"&&(x=11)}function _e(et){Qp.test(et)?x=4:et===">"?It():et==="<"&&qt()}function Ae(et){Qp.test(et)?x=4:et==="/"?x=12:et===">"?It():et==="<"?qt():(x=4,(function(){d--})())}function xe(et){et===">"?(P=new Jl(fa(fa({},P),{isClosing:!0})),It()):x=4}function Oe(et){t.substr(d,2)==="--"?(d+=2,P=new Jl(fa(fa({},P),{type:"comment"})),x=14):t.substr(d,7).toUpperCase()==="DOCTYPE"?(d+=7,P=new Jl(fa(fa({},P),{type:"doctype"})),x=20):_t()}function je(et){et==="-"?x=15:et===">"?_t():x=16}function G(et){et==="-"?x=18:et===">"?_t():x=16}function de(et){et==="-"&&(x=17)}function Ne(et){x=et==="-"?18:16}function Ye(et){et===">"?It():et==="!"?x=19:et==="-"||(x=16)}function ht(et){et==="-"?x=17:et===">"?It():x=16}function Ct(et){et===">"?It():et==="<"&&qt()}function _t(){x=0,P=c}function qt(){x=1,P=new Jl({idx:d})}function It(){var et=t.slice(k,P.idx);et&&a(et,k),P.type==="comment"?o(P.idx):P.type==="doctype"?p(P.idx):(P.isOpening&&r(P.name,P.idx),P.isClosing&&n(P.name,P.idx)),_t(),k=d+1}function Ot(){var et=P.idx+(P.isClosing?2:1);return t.slice(et,d).toLowerCase()}k=0&&a++},onText:function(p,c){if(a===0){var d=(function(k,P){if(!P.global)throw new Error("`splitRegex` must have the 'g' flag set");for(var R,L=[],V=0;R=P.exec(k);)L.push(k.substring(V,R.index)),L.push(R[0]),V=R.index+R[0].length;return L.push(k.substring(V)),L})(p,/( | |<|<|>|>|"|"|')/gi),E=c;d.forEach((function(x,k){if(k%2==0){var P=r.parseText(x,E);o.push.apply(o,P)}E+=x.length}))}},onCloseTag:function(p){n.indexOf(p)>=0&&(a=Math.max(a-1,0))},onComment:function(p){},onDoctype:function(p){}}),o=this.compactMatches(o),o=this.removeUnwantedMatches(o)},t.prototype.compactMatches=function(e){e.sort((function(d,E){return d.getOffset()-E.getOffset()}));for(var r=0;ro?r:r+1;e.splice(c,1);continue}if(e[r+1].getOffset()/g,">"));for(var r=this.parse(e),n=[],a=0,o=0,p=r.length;o\s]/i.test(t)}function cF(t){return/^<\/a\s*>/i.test(t)}function uF(){var t=[],e=new iF({stripPrefix:!1,url:!0,email:!0,replaceFn:function(r){switch(r.getType()){case"url":t.push({text:r.matchedText,url:r.getUrl()});break;case"email":t.push({text:r.matchedText,url:"mailto:"+r.getEmail().replace(/^mailto:/i,"")})}return!1}});return{links:t,autolinker:e}}function pF(t){var e,r,n,a,o,p,c,d,E,x,k,P,R,L=t.tokens,V=null;for(r=0,n=L.length;r=0;e--)if((o=a[e]).type!=="link_close"){if(o.type==="htmltag"&&(lF(o.content)&&k>0&&k--,cF(o.content)&&k++),!(k>0)&&o.type==="text"&&sF.test(o.content)){if(V||(P=(V=uF()).links,R=V.autolinker),p=o.content,P.length=0,R.link(p),!P.length)continue;for(c=[],x=o.level,d=0;dt.length)&&(e=t.length);for(var r=0,n=Array(e);r2?n-2:0),o=2;o1?r-1:0),a=1;a"u"?null:Ta(BigInt.prototype.toString),H_=typeof Symbol>"u"?null:Ta(Symbol.prototype.toString),ao=Ta(Object.prototype.hasOwnProperty),Mv=Ta(Object.prototype.toString),oo=Ta(RegExp.prototype.test),Zp=(function(e){return function(){for(var r=arguments.length,n=new Array(r),a=0;a1?r-1:0),a=1;a2&&arguments[2]!==void 0?arguments[2]:Fm;if(q_&&q_(t,null),!Uu(e))return t;let n=e.length;for(;n--;){let a=e[n];if(typeof a=="string"){const o=r(a);o!==a&&(fF(e)||(e[n]=o),a=o)}t[a]=!0}return t}function SF(t){for(let e=0;e/g),AF=_o(/\${[\w\W]*/g),CF=_o(/^data-[\-\w.\u00B7-\uFFFF]+$/),jF=_o(/^aria-[\-\w]+$/),Q_=_o(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),PF=_o(/^(?:\w+script|data):/i),NF=_o(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),TF=_o(/^html$/i),IF=_o(/^[a-z][.\w]*(-[.\w]+)+$/i),Z_=_o(/<[/\w!]/g),eO=_o(/<[/\w]/g),MF=_o(/<\/no(script|embed|frames)/i),RF=_o(/\/>/i),bd=1,DF=3,tO=7,rO=8,FF=9,LF=11,$F=function(){return typeof window>"u"?null:window},zu=function(e,r,n,a){return ao(e,r)&&Uu(e[r])?dn(a.base?Uo(a.base):{},e[r],a.transform):n};var mw=(function t(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:$F();const r=rr=>t(rr);if(r.version="3.4.13",r.removed=[],!e||!e.document||e.document.nodeType!==FF||!e.Element)return r.isSupported=!1,r;let n=e.document;const a=n,o=a.currentScript;e.DocumentFragment;const p=e.HTMLTemplateElement,c=e.Node,d=e.Element,E=e.NodeFilter;e.NamedNodeMap===void 0&&(e.NamedNodeMap||e.MozNamedAttrMap),e.HTMLFormElement;const k=e.DOMParser,P=e.trustedTypes,R=d.prototype,L=Hs(R,"cloneNode"),V=Hs(R,"remove"),Q=Hs(R,"nextSibling"),J=Hs(R,"childNodes"),re=Hs(R,"parentNode"),me=Hs(R,"shadowRoot"),le=Hs(R,"attributes"),ve=c&&c.prototype?Hs(c.prototype,"nodeType"):null,be=c&&c.prototype?Hs(c.prototype,"nodeName"):null,ce=c&&c.prototype?Hs(c.prototype,"ownerDocument"):null;if(typeof p=="function"){const rr=n.createElement("template");rr.content&&rr.content.ownerDocument&&(n=rr.content.ownerDocument)}let _e,Ae,xe="",Oe=!1,je=0;const G=function(){if(je>0)throw Zp('A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.')},de=function(Ie){G(),je++;try{return _e.createHTML(Ie)}finally{je--}},Ne=function(){return Oe||(Ae=(function(dt,Ht){if(typeof dt!="object"||typeof dt.createPolicy!="function")return null;let ir=null;const Or="data-tt-policy-suffix";Ht&&Ht.hasAttribute(Or)&&(ir=Ht.getAttribute(Or));const Pr="dompurify"+(ir?"#"+ir:"");try{return dt.createPolicy(Pr,{createHTML:$r=>$r,createScriptURL:$r=>$r})}catch{return console.warn("TrustedTypes policy "+Pr+" could not be created."),null}})(P,o),Oe=!0),Ae},Ye=n,ht=Ye.implementation,Ct=Ye.createNodeIterator,_t=Ye.createDocumentFragment,qt=Ye.getElementsByTagName,It=a.importNode;let Ot={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]};r.isSupported=typeof B_=="function"&&typeof re=="function"&&ht&&ht.createHTMLDocument!==void 0;const et=OF,Dt=kF,or=AF,Ut=CF,pr=jF,W=PF,$e=NF,Je=IF;let Ft=Q_,it=null;const bt=dn({},[...K_,...pw,...hw,...dw,...G_]);let ut=null;const Xt=dn({},[...Y_,...fw,...X_,...Rv]);let zt=Object.seal(gd(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),tr=null,Er=null;const dr=Object.seal(gd(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let qr=!0,en=!0,kn=!1,ln=!0,jt=!1,We=!0,Kr=!1,Lr=!1,zr=null,yn=null,En=!1,Pn=!1,Ua=!1,Dn=!1,Di=!0,vn=!1;const cn="user-content-";let hi=!0,Hl=!1,Xs={},Zo=null;const so=dn({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","selectedcontent","style","svg","template","thead","title","video","xmp"]);let Bk=null;const qk=dn({},["audio","video","img","source","image","track"]);let qw=null;const Uk=dn({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Xv="http://www.w3.org/1998/Math/MathML",Qv="http://www.w3.org/2000/svg",Kl="http://www.w3.org/1999/xhtml";let _d=Kl,Uw=!1,zw=null;const wU=dn({},[Xv,Qv,Kl],uw),zk=xo(["mi","mo","mn","ms","mtext"]);let Vw=dn({},zk);const Vk=xo(["annotation-xml"]);let Jw=dn({},Vk);const EU=dn({},["title","style","font","a","script"]);let Jm=null;const SU=["application/xhtml+xml","text/html"];let oa=null,Od=null;const xU=n.createElement("form"),Jk=function(Ie){return Ie instanceof RegExp||Ie instanceof Function},Ww=function(){let Ie=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};if(Od&&Od===Ie)return;Ie&&typeof Ie=="object"||(Ie={}),Ie=Uo(Ie),Jm=SU.indexOf(Ie.PARSER_MEDIA_TYPE)===-1?"text/html":Ie.PARSER_MEDIA_TYPE,oa=Jm==="application/xhtml+xml"?uw:Fm,it=zu(Ie,"ALLOWED_TAGS",bt,{transform:oa}),ut=zu(Ie,"ALLOWED_ATTR",Xt,{transform:oa}),zw=zu(Ie,"ALLOWED_NAMESPACES",wU,{transform:uw}),qw=zu(Ie,"ADD_URI_SAFE_ATTR",Uk,{transform:oa,base:Uk}),Bk=zu(Ie,"ADD_DATA_URI_TAGS",qk,{transform:oa,base:qk}),Zo=zu(Ie,"FORBID_CONTENTS",so,{transform:oa}),tr=zu(Ie,"FORBID_TAGS",Uo({}),{transform:oa}),Er=zu(Ie,"FORBID_ATTR",Uo({}),{transform:oa}),Xs=!!ao(Ie,"USE_PROFILES")&&(Ie.USE_PROFILES&&typeof Ie.USE_PROFILES=="object"?Uo(Ie.USE_PROFILES):Ie.USE_PROFILES),qr=Ie.ALLOW_ARIA_ATTR!==!1,en=Ie.ALLOW_DATA_ATTR!==!1,kn=Ie.ALLOW_UNKNOWN_PROTOCOLS||!1,ln=Ie.ALLOW_SELF_CLOSE_IN_ATTR!==!1,jt=Ie.SAFE_FOR_TEMPLATES||!1,We=Ie.SAFE_FOR_XML!==!1,Kr=Ie.WHOLE_DOCUMENT||!1,Pn=Ie.RETURN_DOM||!1,Ua=Ie.RETURN_DOM_FRAGMENT||!1,Dn=Ie.RETURN_TRUSTED_TYPE||!1,En=Ie.FORCE_BODY||!1,Di=Ie.SANITIZE_DOM!==!1,vn=Ie.SANITIZE_NAMED_PROPS||!1,hi=Ie.KEEP_CONTENT!==!1,Hl=Ie.IN_PLACE||!1,Ft=(function(ir){try{return oo(ir,""),!0}catch{return!1}})(Ie.ALLOWED_URI_REGEXP)?Ie.ALLOWED_URI_REGEXP:Q_,_d=typeof Ie.NAMESPACE=="string"?Ie.NAMESPACE:Kl,Vw=ao(Ie,"MATHML_TEXT_INTEGRATION_POINTS")&&Ie.MATHML_TEXT_INTEGRATION_POINTS&&typeof Ie.MATHML_TEXT_INTEGRATION_POINTS=="object"?Uo(Ie.MATHML_TEXT_INTEGRATION_POINTS):dn({},zk),Jw=ao(Ie,"HTML_INTEGRATION_POINTS")&&Ie.HTML_INTEGRATION_POINTS&&typeof Ie.HTML_INTEGRATION_POINTS=="object"?Uo(Ie.HTML_INTEGRATION_POINTS):dn({},Vk);const dt=ao(Ie,"CUSTOM_ELEMENT_HANDLING")&&Ie.CUSTOM_ELEMENT_HANDLING&&typeof Ie.CUSTOM_ELEMENT_HANDLING=="object"?Uo(Ie.CUSTOM_ELEMENT_HANDLING):gd(null);if(zt=gd(null),ao(dt,"tagNameCheck")&&Jk(dt.tagNameCheck)&&(zt.tagNameCheck=dt.tagNameCheck),ao(dt,"attributeNameCheck")&&Jk(dt.attributeNameCheck)&&(zt.attributeNameCheck=dt.attributeNameCheck),ao(dt,"allowCustomizedBuiltInElements")&&typeof dt.allowCustomizedBuiltInElements=="boolean"&&(zt.allowCustomizedBuiltInElements=dt.allowCustomizedBuiltInElements),_o(zt),jt&&(en=!1),Ua&&(Pn=!0),Xs&&(it=dn({},G_),ut=gd(null),Xs.html===!0&&(dn(it,K_),dn(ut,Y_)),Xs.svg===!0&&(dn(it,pw),dn(ut,fw),dn(ut,Rv)),Xs.svgFilters===!0&&(dn(it,hw),dn(ut,fw),dn(ut,Rv)),Xs.mathMl===!0&&(dn(it,dw),dn(ut,X_),dn(ut,Rv))),dr.tagCheck=null,dr.attributeCheck=null,ao(Ie,"ADD_TAGS")&&(typeof Ie.ADD_TAGS=="function"?dr.tagCheck=Ie.ADD_TAGS:Uu(Ie.ADD_TAGS)&&(it===bt&&(it=Uo(it)),dn(it,Ie.ADD_TAGS,oa))),ao(Ie,"ADD_ATTR")&&(typeof Ie.ADD_ATTR=="function"?dr.attributeCheck=Ie.ADD_ATTR:Uu(Ie.ADD_ATTR)&&(ut===Xt&&(ut=Uo(ut)),dn(ut,Ie.ADD_ATTR,oa))),ao(Ie,"ADD_URI_SAFE_ATTR")&&Uu(Ie.ADD_URI_SAFE_ATTR)&&dn(qw,Ie.ADD_URI_SAFE_ATTR,oa),ao(Ie,"FORBID_CONTENTS")&&Uu(Ie.FORBID_CONTENTS)&&(Zo===so&&(Zo=Uo(Zo)),dn(Zo,Ie.FORBID_CONTENTS,oa)),ao(Ie,"ADD_FORBID_CONTENTS")&&Uu(Ie.ADD_FORBID_CONTENTS)&&(Zo===so&&(Zo=Uo(Zo)),dn(Zo,Ie.ADD_FORBID_CONTENTS,oa)),hi&&(it["#text"]=!0),Kr&&dn(it,["html","head","body"]),it.table&&(dn(it,["tbody"]),delete tr.tbody),Ie.TRUSTED_TYPES_POLICY){if(typeof Ie.TRUSTED_TYPES_POLICY.createHTML!="function")throw Zp('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if(typeof Ie.TRUSTED_TYPES_POLICY.createScriptURL!="function")throw Zp('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');const Ht=_e;_e=Ie.TRUSTED_TYPES_POLICY;try{xe=de("")}catch(ir){throw _e=Ht,ir}}else Ie.TRUSTED_TYPES_POLICY===null?(_e=void 0,xe=""):(_e===void 0&&(_e=Ne()),_e&&typeof xe=="string"&&(xe=de("")));xo&&xo(Ie),Od=Ie},Wk=dn({},[...pw,...hw,...xF]),Hk=dn({},[...dw,..._F]),_U=function(Ie){let dt=re(Ie);dt&&dt.tagName||(dt={namespaceURI:_d,tagName:"template"});const Ht=Fm(Ie.tagName),ir=Fm(dt.tagName);return!!zw[Ie.namespaceURI]&&(Ie.namespaceURI===Qv?(function(Pr,$r,ga){return $r.namespaceURI===Kl?Pr==="svg":$r.namespaceURI===Xv?Pr==="svg"&&(ga==="annotation-xml"||Vw[ga]):!!Wk[Pr]})(Ht,dt,ir):Ie.namespaceURI===Xv?(function(Pr,$r,ga){return $r.namespaceURI===Kl?Pr==="math":$r.namespaceURI===Qv?Pr==="math"&&Jw[ga]:!!Hk[Pr]})(Ht,dt,ir):Ie.namespaceURI===Kl?(function(Pr,$r,ga){return!($r.namespaceURI===Qv&&!Jw[ga])&&!($r.namespaceURI===Xv&&!Vw[ga])&&!Hk[Pr]&&(EU[Pr]||!Wk[Pr])})(Ht,dt,ir):!(Jm!=="application/xhtml+xml"||!zw[Ie.namespaceURI]))},Ju=function(Ie){vd(r.removed,{element:Ie});try{re(Ie).removeChild(Ie)}catch{if(V(Ie),!re(Ie))throw Zp("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place")}},Zv=function(Ie){Wm(Ie);const dt=J(Ie);if(dt){const ir=[];yd(dt,(Or=>{vd(ir,Or)})),yd(ir,(Or=>{try{V(Or)}catch{}}))}const Ht=le(Ie);if(Ht)for(let ir=Ht.length-1;ir>=0;--ir){const Or=Ht[ir],Pr=Or&&Or.name;if(typeof Pr=="string")try{Ie.removeAttribute(Pr)}catch{}}},kd=function(Ie,dt){try{vd(r.removed,{attribute:dt.getAttributeNode(Ie),from:dt})}catch{vd(r.removed,{attribute:null,from:dt})}if(dt.removeAttribute(Ie),Ie==="is")if(Pn||Ua)try{Ju(dt)}catch{}else try{dt.setAttribute(Ie,"")}catch{}},OU=function(Ie){const dt=le(Ie);if(dt)for(let Ht=dt.length-1;Ht>=0;--Ht){const ir=dt[Ht],Or=ir&&ir.name;if(typeof Or=="string"&&!ut[oa(Or)])try{Ie.removeAttribute(Or)}catch{}}},Wm=function(Ie){const dt=[Ie];for(;dt.length>0;){const Ht=dt.pop();(ve?ve(Ht):Ht.nodeType)===bd&&OU(Ht);const ir=J(Ht);if(ir)for(let Or=ir.length-1;Or>=0;--Or)dt.push(ir[Or])}},Kk=function(Ie){let dt=null,Ht=null;if(En)Ie=""+Ie;else{const Pr=V_(Ie,/^[\r\n\t ]+/);Ht=Pr&&Pr[0]}Jm==="application/xhtml+xml"&&_d===Kl&&(Ie=''+Ie+"");const ir=_e?de(Ie):Ie;if(_d===Kl)try{dt=new k().parseFromString(ir,Jm)}catch{}if(!dt||!dt.documentElement){dt=ht.createDocument(_d,"template",null);try{dt.documentElement.innerHTML=Uw?xe:ir}catch{}}const Or=dt.body||dt.documentElement;return Ie&&Ht&&Or.insertBefore(n.createTextNode(Ht),Or.childNodes[0]||null),_d===Kl?qt.call(dt,Kr?"html":"body")[0]:Kr?dt.documentElement:Or},Gk=function(Ie){const dt=ce?ce(Ie):Ie.ownerDocument;return Ct.call(dt||Ie,Ie,E.SHOW_ELEMENT|E.SHOW_COMMENT|E.SHOW_TEXT|E.SHOW_PROCESSING_INSTRUCTION|E.SHOW_CDATA_SECTION,null)},e0=function(Ie){return Ie=Lm(Ie,et," "),Ie=Lm(Ie,Dt," "),Ie=Lm(Ie,or," ")},Hw=function(Ie){var dt;Ie.normalize();const Ht=ce?ce(Ie):Ie.ownerDocument,ir=Ct.call(Ht||Ie,Ie,E.SHOW_TEXT|E.SHOW_COMMENT|E.SHOW_CDATA_SECTION|E.SHOW_PROCESSING_INSTRUCTION,null);let Or=ir.nextNode();for(;Or;)Or.data=e0(Or.data),Or=ir.nextNode();const Pr=(dt=Ie.querySelectorAll)===null||dt===void 0?void 0:dt.call(Ie,"template");Pr&&yd(Pr,($r=>{Ad($r.content)&&Hw($r.content)}))},t0=function(Ie){const dt=be?be(Ie):null;return typeof dt=="string"&&oa(dt)==="form"&&(typeof Ie.nodeName!="string"||typeof Ie.textContent!="string"||typeof Ie.removeChild!="function"||Ie.attributes!==le(Ie)||typeof Ie.removeAttribute!="function"||typeof Ie.setAttribute!="function"||typeof Ie.namespaceURI!="string"||typeof Ie.insertBefore!="function"||typeof Ie.hasChildNodes!="function"||Ie.nodeType!==ve(Ie)||Ie.childNodes!==J(Ie))},Ad=function(Ie){if(!ve||typeof Ie!="object"||Ie===null)return!1;try{return ve(Ie)===LF}catch{return!1}},Hm=function(Ie){if(!ve||typeof Ie!="object"||Ie===null)return!1;try{return typeof ve(Ie)=="number"}catch{return!1}};function Gl(rr,Ie,dt){rr.length!==0&&yd(rr,(Ht=>{Ht.call(r,Ie,dt,Od)}))}const Yk=function(Ie,dt,Ht,ir){return Ie.length===0?dt:dt===Ht||dt===ir?Uo(dt):dt},Xk=function(Ie,dt){if(Gl(Ot.beforeSanitizeElements,Ie,null),Ie!==dt&&re(Ie)===null)return Hl&&Wm(Ie),!0;if(t0(Ie))return Ju(Ie),!0;const Ht=oa(be?be(Ie):Ie.nodeName);if(it=Yk(Ot.uponSanitizeElement,it,bt,zr),Gl(Ot.uponSanitizeElement,Ie,{tagName:Ht,allowedTags:it}),Ie!==dt&&re(Ie)===null)return Hl&&Wm(Ie),!0;if((function(Or,Pr){return!!(We&&Or.hasChildNodes()&&!Hm(Or.firstElementChild)&&oo(Z_,Or.textContent)&&oo(Z_,Or.innerHTML))||!(!We||Or.namespaceURI!==Kl||Pr!=="style"||!Hm(Or.firstElementChild))||Or.nodeType===tO||!(!We||Or.nodeType!==rO||!oo(eO,Or.data))})(Ie,Ht))return Ju(Ie),!0;if(tr[Ht]||!(dr.tagCheck instanceof Function&&dr.tagCheck(Ht))&&!it[Ht]){const ir=(function(Pr,$r,ga){if(!tr[$r]&&Zk($r)&&(zt.tagNameCheck instanceof RegExp&&oo(zt.tagNameCheck,$r)||zt.tagNameCheck instanceof Function&&zt.tagNameCheck($r)))return!1;if(hi&&!Zo[$r]){const ei=re(Pr),Sn=J(Pr);if(Sn&&ei)for(let Fi=Sn.length-1;Fi>=0;--Fi){const Qn=Pr===ga?L(Sn[Fi],!0):Sn[Fi];ei.insertBefore(Qn,Q(Pr))}}return Ju(Pr),!0})(Ie,Ht,dt);return ir===!1&&Gl(Ot.afterSanitizeElements,Ie,null),ir}if((ve?ve(Ie):Ie.nodeType)===bd&&!_U(Ie)||(Ht==="noscript"||Ht==="noembed"||Ht==="noframes")&&oo(MF,Ie.innerHTML))return Ju(Ie),!0;if(jt&&Ie.nodeType===DF){const ir=e0(Ie.textContent);Ie.textContent!==ir&&(vd(r.removed,{element:Ie.cloneNode()}),Ie.textContent=ir)}return Gl(Ot.afterSanitizeElements,Ie,null),!1},Qk=function(Ie,dt,Ht){if(Er[dt]||We&&dt==="patchsrc"||We&&dt==="for"&&Ie!=="label"&&Ie!=="output"||Di&&(dt==="id"||dt==="name")&&(Ht in n||Ht in xU))return!1;const ir=ut[dt]||dr.attributeCheck instanceof Function&&dr.attributeCheck(dt,Ie);if(!(en&&oo(Ut,dt))){if(!(qr&&oo(pr,dt))){if(ir){if(!qw[dt]){if(!oo(Ft,Lm(Ht,$e,""))){if((dt!=="src"&&dt!=="xlink:href"&&dt!=="href"||Ie==="script"||J_(Ht,"data:")!==0||!Bk[Ie])&&!(kn&&!oo(W,Lm(Ht,$e,"")))){if(Ht)return!1}}}}else if(!(Zk(Ie)&&(zt.tagNameCheck instanceof RegExp&&oo(zt.tagNameCheck,Ie)||zt.tagNameCheck instanceof Function&&zt.tagNameCheck(Ie))&&(zt.attributeNameCheck instanceof RegExp&&oo(zt.attributeNameCheck,dt)||zt.attributeNameCheck instanceof Function&&zt.attributeNameCheck(dt,Ie))||dt==="is"&&zt.allowCustomizedBuiltInElements&&(zt.tagNameCheck instanceof RegExp&&oo(zt.tagNameCheck,Ht)||zt.tagNameCheck instanceof Function&&zt.tagNameCheck(Ht))))return!1}}return!0},kU=dn({},["annotation-xml","color-profile","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","missing-glyph"]),Zk=function(Ie){return!kU[Fm(Ie)]&&oo(Je,Ie)},AU=function(Ie,dt,Ht,ir){if(_e&&typeof P=="object"&&typeof P.getAttributeType=="function"&&!Ht)switch(P.getAttributeType(Ie,dt)){case"TrustedHTML":return de(ir);case"TrustedScriptURL":return(function(Pr){G(),je++;try{return _e.createScriptURL(Pr)}finally{je--}})(ir)}return ir},CU=function(Ie,dt,Ht,ir){try{Ht?Ie.setAttributeNS(Ht,dt,ir):Ie.setAttribute(dt,ir),t0(Ie)?Ju(Ie):z_(r.removed)}catch{kd(dt,Ie)}},eA=function(Ie){Gl(Ot.beforeSanitizeAttributes,Ie,null);const dt=Ie.attributes;if(!dt||t0(Ie))return;ut=Yk(Ot.uponSanitizeAttribute,ut,Xt,yn);const Ht={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:ut,forceKeepAttr:void 0};let ir=dt.length;const Or=oa(Ie.nodeName);for(;ir--;){const Pr=dt[ir],$r=Pr.name,ga=Pr.namespaceURI,ei=Pr.value,Sn=oa($r),Fi=ei;let Qn=$r==="value"?Fi:bF(Fi);Ht.attrName=Sn,Ht.attrValue=Qn,Ht.keepAttr=!0,Ht.forceKeepAttr=void 0,Gl(Ot.uponSanitizeAttribute,Ie,Ht),Qn=Ht.attrValue,!vn||Sn!=="id"&&Sn!=="name"||J_(Qn,cn)===0||(kd($r,Ie),Qn=cn+Qn),We&&oo(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i,Qn)||Sn==="attributename"&&V_(Qn,"href")?kd($r,Ie):Ht.forceKeepAttr||(Ht.keepAttr&&(ln||!oo(RF,Qn))?(jt&&(Qn=e0(Qn)),Qk(Or,Sn,Qn)?(Qn=AU(Or,Sn,ga,Qn),Qn!==Fi&&CU(Ie,$r,ga,Qn)):kd($r,Ie)):kd($r,Ie))}Gl(Ot.afterSanitizeAttributes,Ie,null)},r0=function(Ie){let dt=null;const Ht=Gk(Ie);for(Gl(Ot.beforeSanitizeShadowDOM,Ie,null);dt=Ht.nextNode();)if(Gl(Ot.uponSanitizeShadowNode,dt,null),Xk(dt,Ie),eA(dt),Ad(dt.content)&&r0(dt.content),(ve?ve(dt):dt.nodeType)===bd){const ir=me(dt);Ad(ir)&&(Kw(ir),r0(ir))}Gl(Ot.afterSanitizeShadowDOM,Ie,null)},Kw=function(Ie){const dt=[{node:Ie,shadow:null}];for(;dt.length>0;){const Ht=dt.pop();if(Ht.shadow){r0(Ht.shadow);continue}const ir=Ht.node,Or=(ve?ve(ir):ir.nodeType)===bd,Pr=J(ir);if(Pr)for(let $r=Pr.length-1;$r>=0;--$r)dt.push({node:Pr[$r],shadow:null});if(Or){const $r=be?be(ir):null;if(typeof $r=="string"&&oa($r)==="template"){const ga=ir.content;Ad(ga)&&dt.push({node:ga,shadow:null})}}if(Or){const $r=me(ir);Ad($r)&&dt.push({node:null,shadow:$r},{node:$r,shadow:null})}}};return r.sanitize=function(rr){let Ie=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},dt=null,Ht=null,ir=null,Or=null;if(Uw=!rr,Uw&&(rr=""),typeof rr!="string"&&!Hm(rr)&&typeof(rr=(function(Sn){switch(typeof Sn){case"string":return Sn;case"number":return wF(Sn);case"boolean":return EF(Sn);case"bigint":return W_?W_(Sn):"0";case"symbol":return H_?H_(Sn):"Symbol()";case"undefined":default:return Mv(Sn);case"function":case"object":{if(Sn===null)return Mv(Sn);const Fi=Sn,Qn=Hs(Fi,"toString");if(typeof Qn=="function"){const ps=Qn(Fi);return typeof ps=="string"?ps:Mv(ps)}return Mv(Sn)}}})(rr))!="string")throw Zp("dirty is not a string, aborting");if(!r.isSupported)return rr;Lr?(it=zr,ut=yn):Ww(Ie),(Ot.uponSanitizeElement.length>0||Ot.uponSanitizeAttribute.length>0)&&(it=Uo(it)),Ot.uponSanitizeAttribute.length>0&&(ut=Uo(ut)),r.removed=[];const Pr=Hl&&typeof rr!="string"&&Hm(rr);if(Pr){(function(Fi){if(!We)return;const Qn=[Fi];for(;Qn.length>0;){const ps=Qn.pop(),Gw=ve?ve(ps):ps.nodeType;if(Gw===tO||Gw===rO&&oo(eO,ps.data)){try{V(ps)}catch{}continue}if(Gw===bd){const Qs=ps,tA=oa(be?be(ps):ps.nodeName);try{Qs.hasAttribute&&Qs.hasAttribute("patchsrc")&&Qs.removeAttribute("patchsrc"),Qs.hasAttribute&&Qs.hasAttribute("for")&&tA!=="label"&&tA!=="output"&&Qs.removeAttribute("for")}catch{}}const Yw=J(ps);if(Yw)for(let Qs=Yw.length-1;Qs>=0;--Qs)Qn.push(Yw[Qs])}})(rr);const ei=be?be(rr):rr.nodeName;if(typeof ei=="string"){const Sn=oa(ei);if(!it[Sn]||tr[Sn])throw Zv(rr),Zp("root node is forbidden and cannot be sanitized in-place")}if(t0(rr))throw Zv(rr),Zp("root node is clobbered and cannot be sanitized in-place");try{Kw(rr)}catch(Sn){throw Zv(rr),Sn}}else if(Hm(rr))dt=Kk(""),Ht=dt.ownerDocument.importNode(rr,!0),Ht.nodeType===bd&&Ht.nodeName==="BODY"||Ht.nodeName==="HTML"?dt=Ht:dt.appendChild(Ht),Kw(Ht);else{if(!Pn&&!jt&&!Kr&&rr.indexOf("<")===-1)return _e&&Dn?de(rr):rr;if(dt=Kk(rr),!dt)return Pn?null:Dn?xe:""}dt&&En&&Ju(dt.firstChild);const $r=Pr?rr:dt;try{const ei=Gk($r);for(;ir=ei.nextNode();)Xk(ir,$r),eA(ir),Ad(ir.content)&&r0(ir.content)}catch(ei){throw Pr&&(Zv(rr),yd(r.removed,(Sn=>{Sn.element&&Wm(Sn.element)}))),ei}if(Pr)return yd(r.removed,(ei=>{ei.element&&Wm(ei.element)})),jt&&Hw(rr),rr;if(Pn){if(jt&&Hw(dt),Ua)for(Or=_t.call(dt.ownerDocument);dt.firstChild;)Or.appendChild(dt.firstChild);else Or=dt;return(ut.shadowroot||ut.shadowrootmode)&&(Or=It.call(a,Or,!0)),Or}let ga=Kr?dt.outerHTML:dt.innerHTML;return Kr&&it["!doctype"]&&dt.ownerDocument&&dt.ownerDocument.doctype&&dt.ownerDocument.doctype.name&&oo(TF,dt.ownerDocument.doctype.name)&&(ga=" +`+ga),jt&&(ga=e0(ga)),_e&&Dn?de(ga):ga},r.setConfig=function(){Ww(arguments.length>0&&arguments[0]!==void 0?arguments[0]:{}),Lr=!0,zr=it,yn=ut},r.clearConfig=function(){Od=null,Lr=!1,zr=null,yn=null,_e=Ae,xe=""},r.isValidAttribute=function(rr,Ie,dt){Od||Ww({});const Ht=oa(rr),ir=oa(Ie);return Qk(Ht,ir,dt)},r.addHook=function(rr,Ie){typeof Ie=="function"&&ao(Ot,rr)&&vd(Ot[rr],Ie)},r.removeHook=function(rr,Ie){if(ao(Ot,rr)){if(Ie!==void 0){const dt=yF(Ot[rr],Ie);return dt===-1?void 0:vF(Ot[rr],dt,1)[0]}return z_(Ot[rr])}},r.removeHooks=function(rr){ao(Ot,rr)&&(Ot[rr]=[])},r.removeAllHooks=function(){Ot={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}},r})();mw.addHook&&mw.addHook("beforeSanitizeElements",(function(t){return t.href&&t.setAttribute("rel","noopener noreferrer"),t}));const BF=function({source:e,className:r="",getConfigs:n=()=>({useUnsafeMarkdown:!1})}){if(typeof e!="string")return null;const a=new Nc({html:!0,typographer:!0,breaks:!0,linkTarget:"_blank"}).use(hF);a.core.ruler.disable(["replacements","smartquotes"]);const{useUnsafeMarkdown:o}=n(),p=a.render(e),c=$m(p,{useUnsafeMarkdown:o});return e&&p&&c?u.createElement("div",{className:Xr()(r,"markdown"),dangerouslySetInnerHTML:{__html:c}}):null};function $m(t,{useUnsafeMarkdown:e=!1}={}){const r=e,n=e?[]:["style","class"];return e&&!$m.hasWarnedAboutDeprecation&&(console.warn("useUnsafeMarkdown display configuration parameter is deprecated since >3.26.0 and will be removed in v4.0.0."),$m.hasWarnedAboutDeprecation=!0),mw.sanitize(t,{ADD_ATTR:["target"],FORBID_TAGS:["style","form"],ALLOW_DATA_ATTR:r,FORBID_ATTR:n})}$m.hasWarnedAboutDeprecation=!1;class qF extends u.Component{render(){const{errSelectors:e,specSelectors:r,getComponent:n}=this.props,a=n("SvgAssets"),o=n("InfoContainer",!0),p=n("VersionPragmaFilter"),c=n("operations",!0),d=n("Models",!0),E=n("Webhooks",!0),x=n("Row"),k=n("Col"),P=n("errors",!0),R=n("ServersContainer",!0),L=n("SchemesContainer",!0),V=n("AuthorizeBtnContainer",!0),Q=n("FilterContainer",!0),J=r.isSwagger2(),re=r.isOAS3(),me=r.isOAS31(),le=r.isOAS32(),ve=!r.specStr(),be=r.loadingStatus();let ce=null;if(be==="loading"&&(ce=u.createElement("div",{className:"info"},u.createElement("div",{className:"loading-container"},u.createElement("div",{className:"loading"})))),be==="failed"&&(ce=u.createElement("div",{className:"info"},u.createElement("div",{className:"loading-container"},u.createElement("h4",{className:"title"},"Failed to load API definition."),u.createElement(P,null)))),be==="failedConfig"){const G=e.lastError(),de=G?G.get("message"):"";ce=u.createElement("div",{className:"info failed-config"},u.createElement("div",{className:"loading-container"},u.createElement("h4",{className:"title"},"Failed to load remote configuration."),u.createElement("p",null,de)))}if(!ce&&ve&&(ce=u.createElement("h4",null,"No API definition provided.")),ce)return u.createElement("div",{className:"swagger-ui"},u.createElement("div",{className:"loading-container"},ce));const _e=r.servers(),Ae=r.schemes(),xe=_e&&_e.size,Oe=Ae&&Ae.size,je=!!r.securityDefinitions();return u.createElement("div",{className:"swagger-ui"},u.createElement(a,null),u.createElement(p,{isSwagger2:J,isOAS3:re,isOAS31:me,isOAS32:le,alsoShow:u.createElement(P,null)},u.createElement(P,null),u.createElement(x,{className:"information-container"},u.createElement(k,{mobile:12},u.createElement(o,null))),xe||Oe||je?u.createElement("div",{className:"scheme-container"},u.createElement(k,{className:"schemes wrapper",mobile:12},xe||Oe?u.createElement("div",{className:"schemes-server-container"},xe?u.createElement(R,null):null,Oe?u.createElement(L,null):null):null,je?u.createElement(V,null):null)):null,u.createElement(Q,null),u.createElement("main",{id:"operations",tabIndex:"-1",style:{outline:"none"}},u.createElement(x,null,u.createElement(k,{mobile:12,desktop:12},u.createElement(c,null))),me&&u.createElement(x,{className:"webhooks-container"},u.createElement(k,{mobile:12,desktop:12},u.createElement(E,null))),u.createElement(x,null,u.createElement(k,{mobile:12,desktop:12},u.createElement(d,null))))))}}const UF=()=>({components:{App:P9,authorizationPopup:N9,authorizeBtn:T9,AuthorizeBtnContainer:I9,authorizeOperationBtn:M9,auths:R9,AuthItem:D9,authError:F9,oauth2:V9,apiKeyAuth:L9,basicAuth:$9,clear:J9,liveResponse:K9,InitializedInput:R7,info:$7,InfoContainer:B7,InfoUrl:F7,InfoBasePath:D7,Contact:U7,License:V7,JumpToPath:J7,CopyToClipboardBtn:W7,onlineValidatorBadge:Qx,operations:Y9,operation:Q9,OperationSummary:r7,OperationSummaryMethod:n7,OperationSummaryPath:a7,responses:q1,response:s7,ResponseExtension:l7,responseBody:h7,parameters:d7,parameterRow:y7,execute:v7,headers:b7,errors:w7,contentType:_7,overview:I7,footer:H7,FilterContainer:K7,ParamBody:J1,curl:G7,Property:Y7,TryItOutButton:X7,Markdown:BF,BaseLayout:qF,VersionPragmaFilter:Q7,VersionStamp:Z7,OperationExt:o7,OperationExtRow:i7,ParameterExt:f7,ParameterIncludeEmpty:g7,OperationTag:X9,OperationContainer:Z9,OpenAPIVersion:eD,DeepLink:tD,SvgAssets:rD,Example:B9,ExamplesSelect:q9,ExamplesSelectValueRetainer:U9}}),zF=()=>({components:{...Ve}}),nO=()=>[Zl,Nd,mx,uh,Nx,Tx,ey,sh,pp,Fd,Yd,Ah,UF,zF,fx,Ss,Ix,up,Id,Ld,Ol,Hx,Kx,Xx()],VF=(0,H.Map)();function Bm(t){return(e,r)=>(...n)=>{if(r.getSystem().specSelectors.isOAS3()){const a=t(...n);return typeof a=="function"?a(r):a}return e(...n)}}const qm=Bm(vu()(null)),JF=Bm(((t,e)=>r=>r.getSystem().specSelectors.findSchema(e))),WF=Bm((()=>t=>{const e=t.getSystem().specSelectors.specJson().getIn(["components","schemas"]);return H.Map.isMap(e)?e:VF})),HF=Bm((()=>t=>t.getSystem().specSelectors.specJson().hasIn(["servers",0]))),KF=Bm(yr(li,(t=>t.getIn(["components","securitySchemes"])||null))),GF=(t,e)=>(r,...n)=>e.specSelectors.isOAS3()?e.oas3Selectors.validOperationMethods():t(...n),YF=qm,XF=qm,QF=qm,ZF=qm,eL=qm,tL=(function(e){return(r,n)=>(...a)=>{if(n.getSystem().specSelectors.isOAS3()){let o=n.getState().getIn(["spec","resolvedSubtrees","components","securitySchemes"]);return e(n,o,...a)}return r(...a)}})(yr((t=>t),(({specSelectors:t})=>t.securityDefinitions()),((t,e)=>{let r=(0,H.List)();return e&&e.entrySeq().forEach((([n,a])=>{const o=a?.get("type");if(o==="oauth2"&&a.get("flows").entrySeq().forEach((([p,c])=>{let d=(0,H.fromJS)({flow:p,authorizationUrl:c.get("authorizationUrl"),tokenUrl:c.get("tokenUrl"),scopes:c.get("scopes"),type:a.get("type"),description:a.get("description")});r=r.push(new H.Map({[n]:d.filter((E=>E!==void 0))}))})),o!=="http"&&o!=="apiKey"||(r=r.push(new H.Map({[n]:a}))),o==="openIdConnect"&&a.get("openIdConnectData")){let p=a.get("openIdConnectData");(p.get("grant_types_supported")||["authorization_code","implicit"]).forEach((c=>{let d=p.get("scopes_supported")&&p.get("scopes_supported").reduce(((x,k)=>x.set(k,"")),new H.Map),E=(0,H.fromJS)({flow:c,authorizationUrl:p.get("authorization_endpoint"),tokenUrl:p.get("token_endpoint"),scopes:d,type:"oauth2",openIdConnectUrl:a.get("openIdConnectUrl")});r=r.push(new H.Map({[n]:E.filter((x=>x!==void 0))}))}))}})),r})));function Um(t){return(e,r)=>n=>typeof r.specSelectors?.isOAS3=="function"?r.specSelectors.isOAS3()?u.createElement(t,Yr()({},n,r,{Ori:e})):u.createElement(e,n):(console.warn("OAS3 wrapper: couldn't get spec"),null)}const rL=(0,H.Map)(),nL=()=>t=>(function(r){const n=r.get("swagger");return typeof n=="string"&&n==="2.0"})(t.getSystem().specSelectors.specJson()),aL=()=>t=>(function(r){const n=r.get("openapi");return typeof n=="string"&&/^3\.0\.(?:[1-9]\d*|0)$/.test(n)})(t.getSystem().specSelectors.specJson()),oL=()=>t=>t.getSystem().specSelectors.isOAS30();function aO(t){return(e,...r)=>n=>{if(n.specSelectors.isOAS3()){const a=t(e,...r);return typeof a=="function"?a(n):a}return null}}const iL=aO((()=>t=>t.specSelectors.specJson().get("servers",rL))),sL=(t,e)=>{const r=t.getIn(["resolvedSubtrees","components","schemas",e],null),n=t.getIn(["json","components","schemas",e],null);return r||n||null},lL=aO(((t,{callbacks:e,specPath:r})=>n=>{const a=n.specSelectors.validOperationMethods();return H.Map.isMap(e)?e.reduce(((o,p,c)=>{if(!H.Map.isMap(p))return o;const d=p.reduce(((E,x,k)=>{if(!H.Map.isMap(x))return E;const P=x.entrySeq().filter((([R])=>a.includes(R))).map((([R,L])=>({operation:(0,H.Map)({operation:L}),method:R,path:k,callbackName:c,specPath:r.concat([c,k,R])})));return E.concat(P)}),(0,H.List)());return o.concat(d)}),(0,H.List)()).groupBy((o=>o.callbackName)).map((o=>o.toArray())).toObject():{}})),cL=({callbacks:t,specPath:e,specSelectors:r,getComponent:n})=>{const a=r.callbacksOperations({callbacks:t,specPath:e}),o=Object.keys(a),p=n("OperationContainer",!0);return o.length===0?u.createElement("span",null,"No callbacks"):u.createElement("div",null,o.map((c=>u.createElement("div",{key:`${c}`},u.createElement("h2",null,c),a[c].map((d=>u.createElement(p,{key:`${c}-${d.path}-${d.method}`,op:d.operation,tag:"callbacks",method:d.method,path:d.path,specPath:d.specPath,allowTryItOut:!1})))))))},Dv=(t,e,r,n)=>{const a=t.getIn(["content",e])??(0,H.OrderedMap)(),o=a.get("schema",(0,H.OrderedMap)()),p=a.get("examples")!==void 0,c=a.get("example"),d=p?a.getIn(["examples",r,"value"]):c;return Gr(n.getSampleSchema(o,e,{includeWriteOnly:!0},d))},uL=({userHasEditedBody:t,requestBody:e,requestBodyValue:r,requestBodyInclusionSetting:n,requestBodyErrors:a,getComponent:o,getConfigs:p,specSelectors:c,fn:d,contentType:E,isExecute:x,specPath:k,onChange:P,onChangeIncludeEmpty:R,activeExamplesKey:L,updateActiveExamplesKey:V,setRetainRequestBodyValueFlag:Q})=>{const J=qt=>{P(qt.target.files[0])},re=qt=>{let It={key:qt,shouldDispatchInit:!1,defaultValue:!0};return n.get(qt,"no value")==="no value"&&(It.shouldDispatchInit=!0),It},me=o("Markdown",!0),le=o("modelExample"),ve=o("RequestBodyEditor"),be=o("HighlightCode",!0),ce=o("ExamplesSelectValueRetainer"),_e=o("Example"),Ae=o("ParameterIncludeEmpty"),{showCommonExtensions:xe}=p(),Oe=e?.get("description")??null,je=e?.get("content")??new H.OrderedMap;E=E||je.keySeq().first()||"";const G=je.get(E)??(0,H.OrderedMap)(),de=G.get("schema",(0,H.OrderedMap)()),Ne=G.get("examples",null),Ye=Ne?.map(((qt,It)=>{const Ot=qt?.get("value",null);return Ot&&(qt=qt.set("value",Dv(e,E,It,d),Ot)),qt}));if(a=H.List.isList(a)?a:(0,H.List)(),d.isFileUploadIntended(G?.get("schema"),E)){const qt=o("Input");return x?u.createElement(qt,{type:"file",onChange:J}):u.createElement("i",null,"Example values are not available for ",u.createElement("code",null,E)," media types.")}if(!G.size)return null;if(d.hasSchemaType(G.get("schema"),"object")&&(E==="application/x-www-form-urlencoded"||E.indexOf("multipart/")===0)&&de.get("properties",(0,H.OrderedMap)()).size>0){const qt=o("JsonSchemaForm"),It=o("ParameterExt"),Ot=de.get("properties",(0,H.OrderedMap)());return r=H.Map.isMap(r)?r:(0,H.OrderedMap)(),u.createElement("div",{className:"table-container"},Oe&&u.createElement(me,{source:Oe}),u.createElement("table",null,u.createElement("tbody",null,H.Map.isMap(Ot)&&Ot.entrySeq().map((([et,Dt])=>{if(Dt.get("readOnly"))return;const or=Dt.get("oneOf")?.get(0)?.toJS(),Ut=Dt.get("anyOf")?.get(0)?.toJS();Dt=(0,H.fromJS)(d.mergeJsonSchema(Dt.toJS(),or??Ut??{}));let pr=xe?mr(Dt):null;const W=de.get("required",(0,H.List)()).includes(et),$e=d.getSchemaObjectType(Dt),Je=d.getSchemaObjectTypeLabel(Dt),Ft=d.getSchemaObjectType(Dt?.get("items")),it=Dt.get("format"),bt=Dt.get("description"),ut=r.getIn([et,"value"]),Xt=r.getIn([et,"errors"])||a,zt=n.get(et)||!1;let tr=d.getSampleSchema(Dt,!1,{includeWriteOnly:!0});tr===!1&&(tr="false"),tr===0&&(tr="0"),typeof tr!="string"&&$e==="object"&&(tr=Gr(tr)),typeof tr=="string"&&$e==="array"&&(tr=JSON.parse(tr));const Er=d.isFileUploadIntended(Dt),dr=u.createElement(qt,{fn:d,dispatchInitialValue:!Er,schema:Dt,description:et,getComponent:o,value:ut===void 0?tr:ut,required:W,errors:Xt,onChange:qr=>{P(qr,[et])}});return u.createElement("tr",{key:et,className:"parameters","data-property-name":et},u.createElement("td",{className:"parameters-col_name"},u.createElement("div",{className:W?"parameter__name required":"parameter__name"},et,W?u.createElement("span",null," *"):null),u.createElement("div",{className:"parameter__type"},Je,it&&u.createElement("span",{className:"prop-format"},"($",it,")"),xe&&pr.size?pr.entrySeq().map((([qr,en])=>u.createElement(It,{key:`${qr}-${en}`,xKey:qr,xVal:en}))):null),u.createElement("div",{className:"parameter__deprecated"},Dt.get("deprecated")?"deprecated":null)),u.createElement("td",{className:"parameters-col_description"},u.createElement(me,{source:bt}),x?u.createElement("div",null,$e==="object"||Ft==="object"?u.createElement(le,{getComponent:o,specPath:k.push("schema"),getConfigs:p,isExecute:x,specSelectors:c,schema:Dt,example:dr}):dr,W?null:u.createElement(Ae,{onChange:qr=>R(et,qr),isIncluded:zt,isIncludedOptions:re(et),isDisabled:Array.isArray(ut)?ut.length!==0:!Wa(ut)})):null))})))))}const ht=Dv(e,E,L,d);let Ct=null;_v(ht)&&(Ct="json");const _t=x?u.createElement(ve,{value:r,errors:a,defaultValue:ht,onChange:P,getComponent:o}):u.createElement(be,{className:"body-param__example",language:Ct},Gr(r)||ht);return u.createElement("div",null,Oe&&u.createElement(me,{source:Oe}),Ye?u.createElement(ce,{userHasEditedBody:t,examples:Ye,currentKey:L,currentUserInputValue:r,onSelect:qt=>{V(qt)},updateValue:P,defaultToFirstExample:!0,getComponent:o,setRetainRequestBodyValueFlag:Q}):null,u.createElement(le,{getComponent:o,getConfigs:p,specSelectors:c,expandDepth:1,isExecute:x,schema:G.get("schema"),specPath:k.push("content",E,"schema"),example:_t,includeWriteOnly:!0}),Ye?u.createElement(_e,{example:Ye.get(L),getComponent:o,getConfigs:p}):null)};class pL extends u.Component{render(){const{link:e,name:r,getComponent:n}=this.props,a=n("Markdown",!0);let o=e.get("operationId")||e.get("operationRef"),p=e.get("parameters")&&e.get("parameters").toJS(),c=e.get("description");return u.createElement("div",{className:"operation-link"},u.createElement("div",{className:"description"},u.createElement("b",null,u.createElement("code",null,r)),c?u.createElement(a,{source:c}):null),u.createElement("pre",null,"Operation `",o,"`",u.createElement("br",null),u.createElement("br",null),"Parameters ",(function(E,x){return typeof x!="string"?"":x.split(` +`).map(((k,P)=>P>0?Array(E+1).join(" ")+k:k)).join(` +`)})(0,JSON.stringify(p,null,2))||"{}",u.createElement("br",null)))}}const hL=pL,dL=({servers:t,currentServer:e,setSelectedServer:r,setServerVariableValue:n,getServerVariable:a,getEffectiveServerValue:o})=>{const p=(t.find((x=>x.get("url")===e))||(0,H.OrderedMap)()).get("variables")||(0,H.OrderedMap)(),c=p.size!==0;(0,u.useEffect)((()=>{e||r(t.first()?.get("url"))}),[]),(0,u.useEffect)((()=>{const x=t.find((k=>k.get("url")===e));if(!x)return void r(t.first().get("url"));(x.get("variables")||(0,H.OrderedMap)()).map(((k,P)=>{n({server:e,key:P,val:k.get("default")||""})}))}),[e,t]);const d=(0,u.useCallback)((x=>{r(x.target.value)}),[r]),E=(0,u.useCallback)((x=>{const k=x.target.getAttribute("data-variable"),P=x.target.value;n({server:e,key:k,val:P})}),[n,e]);return u.createElement("div",{className:"servers"},u.createElement("label",{htmlFor:"servers"},u.createElement("select",{onChange:d,value:e,id:"servers"},t.valueSeq().map((x=>u.createElement("option",{value:x.get("url"),key:x.get("url")},x.get("url"),x.get("description")&&` - ${x.get("description")}`))).toArray())),c&&u.createElement("div",null,u.createElement("div",{className:"computed-url"},"Computed URL:",u.createElement("code",null,o(e))),u.createElement("h4",null,"Server variables"),u.createElement("table",null,u.createElement("tbody",null,p.entrySeq().map((([x,k])=>u.createElement("tr",{key:x},u.createElement("td",null,x),u.createElement("td",null,k.get("enum")?u.createElement("select",{"data-variable":x,onChange:E},k.get("enum").map((P=>u.createElement("option",{selected:P===a(e,x),key:P,value:P},P)))):u.createElement("input",{type:"text",value:a(e,x)||"",onChange:E,"data-variable":x})))))))))};class fL extends u.Component{render(){const{specSelectors:e,oas3Selectors:r,oas3Actions:n,getComponent:a}=this.props,o=e.servers(),p=a("Servers");return o&&o.size?u.createElement("div",null,u.createElement("span",{className:"servers-title"},"Servers"),u.createElement(p,{servers:o,currentServer:r.selectedServer(),setSelectedServer:n.setSelectedServer,setServerVariableValue:n.setServerVariableValue,getServerVariable:r.serverVariableValue,getEffectiveServerValue:r.serverEffectiveValue})):null}}const mL=Function.prototype;class gL extends u.PureComponent{static defaultProps={onChange:mL,userHasEditedBody:!1};constructor(e,r){super(e,r),this.state={value:Gr(e.value)||e.defaultValue},e.onChange(e.value)}applyDefaultValue=e=>{const{onChange:r,defaultValue:n}=e||this.props;return this.setState({value:n}),r(n)};onChange=e=>{this.props.onChange(Gr(e))};onDomChange=e=>{const r=e.target.value;this.setState({value:r},(()=>this.onChange(r)))};UNSAFE_componentWillReceiveProps(e){this.props.value!==e.value&&e.value!==this.state.value&&this.setState({value:Gr(e.value)}),!e.value&&e.defaultValue&&this.state.value&&this.applyDefaultValue(e)}render(){let{getComponent:e,errors:r}=this.props,{value:n}=this.state,a=r.size>0;const o=e("TextArea");return u.createElement("div",{className:"body-param"},u.createElement(o,{className:Xr()("body-param__text",{invalid:a}),title:r.size?r.join(", "):"",value:n,onChange:this.onDomChange}))}}class yL extends u.Component{constructor(e,r){super(e,r);let{name:n,schema:a}=this.props,o=this.getValue();this.state={name:n,schema:a,value:o}}getValue(){let{name:e,authorized:r}=this.props;return r&&r.getIn([e,"value"])}onChange=e=>{let{onChange:r}=this.props,{value:n,name:a}=e.target,o=Object.assign({},this.state.value);a?o[a]=n:o=n,this.setState({value:o},(()=>r(this.state)))};render(){let{schema:e,getComponent:r,errSelectors:n,name:a,authSelectors:o}=this.props;const p=r("Input"),c=r("Row"),d=r("Col"),E=r("authError"),x=r("Markdown",!0),k=r("JumpToPath",!0),P=(e.get("scheme")||"").toLowerCase(),R=o.selectAuthPath(a);let L=this.getValue(),V=n.allErrors().filter((Q=>Q.get("authId")===a));if(P==="basic"){let Q=L?L.get("username"):null;return u.createElement("div",null,u.createElement("h4",null,u.createElement("code",null,a),"  (http, Basic)",u.createElement(k,{path:R})),Q&&u.createElement("h6",null,"Authorized"),u.createElement(c,null,u.createElement(x,{source:e.get("description")})),u.createElement(c,null,u.createElement("label",{htmlFor:"auth-basic-username"},"Username:"),Q?u.createElement("code",null," ",Q," "):u.createElement(d,null,u.createElement(p,{id:"auth-basic-username",type:"text",required:"required",name:"username","aria-label":"auth-basic-username",onChange:this.onChange,autoFocus:!0}))),u.createElement(c,null,u.createElement("label",{htmlFor:"auth-basic-password"},"Password:"),Q?u.createElement("code",null," ****** "):u.createElement(d,null,u.createElement(p,{id:"auth-basic-password",autoComplete:"new-password",name:"password",type:"password","aria-label":"auth-basic-password",onChange:this.onChange}))),V.valueSeq().map(((J,re)=>u.createElement(E,{error:J,key:re}))))}return P==="bearer"?u.createElement("div",null,u.createElement("h4",null,u.createElement("code",null,a),"  (http, Bearer)",u.createElement(k,{path:R})),L&&u.createElement("h6",null,"Authorized"),u.createElement(c,null,u.createElement(x,{source:e.get("description")})),u.createElement(c,null,u.createElement("label",{htmlFor:"auth-bearer-value"},"Value:"),L?u.createElement("code",null," ****** "):u.createElement(d,null,u.createElement(p,{id:"auth-bearer-value",type:"text","aria-label":"auth-bearer-value",onChange:this.onChange,autoFocus:!0}))),V.valueSeq().map(((Q,J)=>u.createElement(E,{error:Q,key:J})))):u.createElement("div",null,u.createElement("em",null,u.createElement("b",null,a)," HTTP authentication: unsupported scheme ",`'${P}'`))}}class vL extends u.Component{setSelectedServer=e=>{const{path:r,method:n}=this.props;return this.forceUpdate(),this.props.setSelectedServer(e,`${r}:${n}`)};setServerVariableValue=e=>{const{path:r,method:n}=this.props;return this.forceUpdate(),this.props.setServerVariableValue({...e,namespace:`${r}:${n}`})};getSelectedServer=()=>{const{path:e,method:r}=this.props;return this.props.getSelectedServer(`${e}:${r}`)};getServerVariable=(e,r)=>{const{path:n,method:a}=this.props;return this.props.getServerVariable({namespace:`${n}:${a}`,server:e},r)};getEffectiveServerValue=e=>{const{path:r,method:n}=this.props;return this.props.getEffectiveServerValue({server:e,namespace:`${r}:${n}`})};render(){const{operationServers:e,pathServers:r,getComponent:n}=this.props;if(!e&&!r)return null;const a=n("Servers"),o=e||r,p=e?"operation":"path";return u.createElement("div",{className:"opblock-section operation-servers"},u.createElement("div",{className:"opblock-section-header"},u.createElement("div",{className:"tab-header"},u.createElement("h4",{className:"opblock-title"},"Servers"))),u.createElement("div",{className:"opblock-description-wrapper"},u.createElement("h4",{className:"message"},"These ",p,"-level options override the global server options."),u.createElement(a,{servers:o,currentServer:this.getSelectedServer(),setSelectedServer:this.setSelectedServer,setServerVariableValue:this.setServerVariableValue,getServerVariable:this.getServerVariable,getEffectiveServerValue:this.getEffectiveServerValue})))}}const bL={Callbacks:cL,HttpAuth:yL,RequestBody:uL,Servers:dL,ServersContainer:fL,RequestBodyEditor:gL,OperationServers:vL,operationLink:hL},gw=new Nc("commonmark");gw.block.ruler.enable(["table"]),gw.set({linkTarget:"_blank"});const wL=Um((({source:t,className:e="",getConfigs:r=()=>({useUnsafeMarkdown:!1})})=>{if(typeof t!="string")return null;if(t){const{useUnsafeMarkdown:n}=r(),a=$m(gw.render(t),{useUnsafeMarkdown:n});let o;return typeof a=="string"&&(o=a.trim()),u.createElement("div",{dangerouslySetInnerHTML:{__html:o},className:Xr()(e,"renderedMarkdown")})}return null})),EL=Um((({Ori:t,...e})=>{const{schema:r,getComponent:n,errSelectors:a,authorized:o,onAuthChange:p,name:c,authSelectors:d}=e,E=n("HttpAuth");return r.get("type")==="http"?u.createElement(E,{key:c,schema:r,name:c,errSelectors:a,authorized:o,getComponent:n,onChange:p,authSelectors:d}):u.createElement(t,e)})),SL=Um(Qx);class xL extends u.Component{render(){let{getConfigs:e,schema:r,Ori:n}=this.props,a=["model-box"],o=null;return r.get("deprecated")===!0&&(a.push("deprecated"),o=u.createElement("span",{className:"model-deprecated-warning"},"Deprecated:")),u.createElement("div",{className:a.join(" ")},o,u.createElement(n,Yr()({},this.props,{getConfigs:e,depth:1,expandDepth:this.props.expandDepth||0})))}}const _L=Um(xL),OL=Um((({Ori:t,...e})=>{const{schema:r,getComponent:n,errors:a,onChange:o,fn:p}=e,c=p.isFileUploadIntended(r),d=n("Input");return c?u.createElement(d,{type:"file",className:a.length?"invalid":"",title:a.length?a:"",onChange:E=>{o(E.target.files[0])},disabled:t.isDisabled}):u.createElement(t,e)})),kL={Markdown:wL,AuthItem:EL,OpenAPIVersion:(function(e){return(r,n)=>a=>typeof n.specSelectors?.isOAS30=="function"?n.specSelectors.isOAS30()?u.createElement(e,Yr()({},a,n,{Ori:r})):u.createElement(r,a):(console.warn("OAS30 wrapper: couldn't get spec"),null)})((t=>{const{Ori:e}=t;return u.createElement(e,{oasVersion:"3.0"})})),JsonSchema_string:OL,model:_L,onlineValidatorBadge:SL},yw="oas3_set_servers",vw="oas3_set_request_body_value",bw="oas3_set_request_body_retain_flag",ww="oas3_set_request_body_inclusion",Ew="oas3_set_active_examples_member",Sw="oas3_set_request_content_type",xw="oas3_set_response_content_type",_w="oas3_set_server_variable_value",Ow="oas3_set_request_body_validate_error",Fv="oas3_clear_request_body_validate_error",kw="oas3_clear_request_body_value";function AL(t,e){return{type:yw,payload:{selectedServerUrl:t,namespace:e}}}function CL({value:t,pathMethod:e}){return{type:vw,payload:{value:t,pathMethod:e}}}const jL=({value:t,pathMethod:e})=>({type:bw,payload:{value:t,pathMethod:e}});function PL({value:t,pathMethod:e,name:r}){return{type:ww,payload:{value:t,pathMethod:e,name:r}}}function NL({name:t,pathMethod:e,contextType:r,contextName:n}){return{type:Ew,payload:{name:t,pathMethod:e,contextType:r,contextName:n}}}function TL({value:t,pathMethod:e}){return{type:Sw,payload:{value:t,pathMethod:e}}}function IL({value:t,path:e,method:r}){return{type:xw,payload:{value:t,path:e,method:r}}}function ML({server:t,namespace:e,key:r,val:n}){return{type:_w,payload:{server:t,namespace:e,key:r,val:n}}}const RL=({path:t,method:e,validationErrors:r})=>({type:Ow,payload:{path:t,method:e,validationErrors:r}}),DL=({path:t,method:e})=>({type:Fv,payload:{path:t,method:e}}),FL=({pathMethod:t})=>({type:Fv,payload:{path:t[0],method:t[1]}}),LL=({pathMethod:t})=>({type:kw,payload:{pathMethod:t}});var $L=Be(60680),BL=Be.n($L);const ss=t=>(e,...r)=>n=>{if(n.getSystem().specSelectors.isOAS3()){const a=t(e,...r);return typeof a=="function"?a(n):a}return null},qL=ss(((t,e)=>{const r=e?[e,"selectedServer"]:["selectedServer"];return t.getIn(r)||""})),UL=ss(((t,e,r)=>t.getIn(["requestData",e,r,"bodyValue"])||null)),zL=ss(((t,e,r)=>t.getIn(["requestData",e,r,"retainBodyValue"])||!1)),VL=(t,e,r)=>n=>{const{oas3Selectors:a,specSelectors:o,fn:p}=n.getSystem();if(o.isOAS3()){const c=a.requestContentType(e,r);if(c)return Dv(o.specResolvedSubtree(["paths",e,r,"requestBody"]),c,a.activeExamplesMember(e,r,"requestBody","requestBody"),p)}return null},JL=ss(((t,e,r)=>n=>{const{oas3Selectors:a,specSelectors:o,fn:p}=n;let c=!1;const d=a.requestContentType(e,r);let E=a.requestBodyValue(e,r);const x=o.specResolvedSubtree(["paths",e,r,"requestBody"]);if(!x)return!1;if(H.Map.isMap(E)&&(E=Gr(E.mapEntries((k=>H.Map.isMap(k[1])?[k[0],k[1].get("value")]:k)).toJS())),H.List.isList(E)&&(E=Gr(E)),d){const k=Dv(x,d,a.activeExamplesMember(e,r,"requestBody","requestBody"),p);c=!!E&&E!==k}return c})),WL=ss(((t,e,r)=>t.getIn(["requestData",e,r,"bodyInclusion"])||(0,H.Map)())),HL=ss(((t,e,r)=>t.getIn(["requestData",e,r,"errors"])||null)),KL=ss(((t,e,r,n,a)=>t.getIn(["examples",e,r,n,a,"activeExample"])||null)),GL=ss(((t,e,r)=>t.getIn(["requestData",e,r,"requestContentType"])||null)),YL=ss(((t,e,r)=>t.getIn(["requestData",e,r,"responseContentType"])||null)),XL=ss(((t,e,r)=>{let n;if(typeof e!="string"){const{server:a,namespace:o}=e;n=o?[o,"serverVariableValues",a,r]:["serverVariableValues",a,r]}else n=["serverVariableValues",e,r];return t.getIn(n)||null})),QL=ss(((t,e)=>{let r;if(typeof e!="string"){const{server:n,namespace:a}=e;r=a?[a,"serverVariableValues",n]:["serverVariableValues",n]}else r=["serverVariableValues",e];return t.getIn(r)||(0,H.OrderedMap)()})),ZL=ss(((t,e)=>{var r,n;if(typeof e!="string"){const{server:o,namespace:p}=e;n=o,r=p?t.getIn([p,"serverVariableValues",n]):t.getIn(["serverVariableValues",n])}else n=e,r=t.getIn(["serverVariableValues",n]);r=r||(0,H.OrderedMap)();let a=n;return r.map(((o,p)=>{a=a.replace(new RegExp(`{${BL()(p)}}`,"g"),o)})),a})),e$=(function(e){return(...r)=>n=>{const a=n.getSystem().specSelectors.specJson();let o=[...r][1]||[];return!a.getIn(["paths",...o,"requestBody","required"])||e(...r)}})(((t,e)=>((r,n)=>(n=n||[],!!r.getIn(["requestData",...n,"bodyValue"])))(t,e))),t$=(t,{oas3RequiredRequestBodyContentType:e,oas3RequestContentType:r,oas3RequestBodyValue:n})=>{let a=[];if(!H.Map.isMap(n))return a;let o=[];return Object.keys(e.requestContentType).forEach((p=>{p===r&&e.requestContentType[p].forEach((c=>{o.indexOf(c)<0&&o.push(c)}))})),o.forEach((p=>{n.getIn([p,"value"])||a.push(p)})),a},r$=vu()(["get","put","post","delete","options","head","patch","trace"]),n$={[yw]:(t,{payload:{selectedServerUrl:e,namespace:r}})=>{const n=r?[r,"selectedServer"]:["selectedServer"];return t.setIn(n,e)},[vw]:(t,{payload:{value:e,pathMethod:r}})=>{let[n,a]=r;if(!H.Map.isMap(e))return t.setIn(["requestData",n,a,"bodyValue"],e);let o=t.getIn(["requestData",n,a,"bodyValue"])||(0,H.Map)();H.Map.isMap(o)||(o=(0,H.Map)());let p=o;const[...c]=e.keys();return c.forEach((d=>{let E=e.getIn([d]);p.has(d)&&H.Map.isMap(E)||(p=p.setIn([d,"value"],E))})),t.setIn(["requestData",n,a,"bodyValue"],p)},[bw]:(t,{payload:{value:e,pathMethod:r}})=>{let[n,a]=r;return t.setIn(["requestData",n,a,"retainBodyValue"],e)},[ww]:(t,{payload:{value:e,pathMethod:r,name:n}})=>{let[a,o]=r;return t.setIn(["requestData",a,o,"bodyInclusion",n],e)},[Ew]:(t,{payload:{name:e,pathMethod:r,contextType:n,contextName:a}})=>{let[o,p]=r;return t.setIn(["examples",o,p,n,a,"activeExample"],e)},[Sw]:(t,{payload:{value:e,pathMethod:r}})=>{let[n,a]=r;return t.setIn(["requestData",n,a,"requestContentType"],e)},[xw]:(t,{payload:{value:e,path:r,method:n}})=>t.setIn(["requestData",r,n,"responseContentType"],e),[_w]:(t,{payload:{server:e,namespace:r,key:n,val:a}})=>{const o=r?[r,"serverVariableValues",e,n]:["serverVariableValues",e,n];return t.setIn(o,a)},[Ow]:(t,{payload:{path:e,method:r,validationErrors:n}})=>{let a=[];if(a.push("Required field is not provided"),n.missingBodyValue)return t.setIn(["requestData",e,r,"errors"],(0,H.fromJS)(a));if(n.missingRequiredKeys&&n.missingRequiredKeys.length>0){const{missingRequiredKeys:o}=n;return t.updateIn(["requestData",e,r,"bodyValue"],(0,H.fromJS)({}),(p=>o.reduce(((c,d)=>c.setIn([d,"errors"],(0,H.fromJS)(a))),p)))}return console.warn("unexpected result: SET_REQUEST_BODY_VALIDATE_ERROR"),t},[Fv]:(t,{payload:{path:e,method:r}})=>{const n=t.getIn(["requestData",e,r,"bodyValue"]);if(!H.Map.isMap(n))return t.setIn(["requestData",e,r,"errors"],(0,H.fromJS)([]));const[...a]=n.keys();return a?t.updateIn(["requestData",e,r,"bodyValue"],(0,H.fromJS)({}),(o=>a.reduce(((p,c)=>p.setIn([c,"errors"],(0,H.fromJS)([]))),o))):t},[kw]:(t,{payload:{pathMethod:e}})=>{let[r,n]=e;const a=t.getIn(["requestData",r,n,"bodyValue"]);return a?H.Map.isMap(a)?t.setIn(["requestData",r,n,"bodyValue"],(0,H.Map)()):t.setIn(["requestData",r,n,"bodyValue"],""):t}};function Aw({getSystem:t}){const e=(r=>(n,a=null)=>{const{getConfigs:o,fn:p}=r(),{fileUploadMediaTypes:c}=o();if(typeof a=="string"&&c.some((x=>a.startsWith(x))))return!0;const d=H.Map.isMap(n);if(!d&&!Ai()(n))return!1;const E=d?n.get("format"):n.format;return p.hasSchemaType(n,"string")&&["binary","byte"].includes(E)})(t);return{components:bL,wrapComponents:kL,statePlugins:{spec:{wrapSelectors:He,selectors:nt},auth:{wrapSelectors:qe},oas3:{actions:{...gt},reducers:n$,selectors:{...Re}}},fn:{isFileUploadIntended:e,isFileUploadIntendedOAS30:e}}}const a$=({specSelectors:t,getComponent:e})=>{const r=t.selectWebhooksOperations();if(!r)return null;const n=Object.keys(r),a=e("OperationContainer",!0);return n.length===0?null:u.createElement("div",{className:"webhooks"},u.createElement("h2",null,"Webhooks"),n.map((o=>u.createElement("div",{key:`${o}-webhook`},r[o].map((p=>u.createElement(a,{key:`${o}-${p.method}-webhook`,op:p.operation,tag:"webhooks",method:p.method,path:o,specPath:(0,H.List)(p.specPath),allowTryItOut:!1})))))))},o$=({getComponent:t,specSelectors:e})=>{const r=e.selectLicenseNameField(),n=e.selectLicenseUrl(),a=t("Link");return u.createElement("div",{className:"info__license"},n?u.createElement("div",{className:"info__license__url"},u.createElement(a,{target:"_blank",href:Dr(n)},r)):u.createElement("span",null,r))},i$=({getComponent:t,specSelectors:e})=>{const r=e.selectContactNameField(),n=e.selectContactUrl(),a=e.selectContactEmailField(),o=t("Link");return u.createElement("div",{className:"info__contact"},n&&u.createElement("div",null,u.createElement(o,{href:Dr(n),target:"_blank"},r," - Website")),a&&u.createElement(o,{href:Dr(`mailto:${a}`)},n?`Send email to ${r}`:`Contact ${r}`))},s$=({getComponent:t,specSelectors:e})=>{const r=e.version(),n=e.url(),a=e.basePath(),o=e.host(),p=e.selectInfoSummaryField(),c=e.selectInfoDescriptionField(),d=e.selectInfoTitleField(),E=e.selectInfoTermsOfServiceUrl(),x=e.selectExternalDocsUrl(),k=e.selectExternalDocsDescriptionField(),P=e.contact(),R=e.license(),L=t("Markdown",!0),V=t("Link"),Q=t("VersionStamp"),J=t("OpenAPIVersion"),re=t("InfoUrl"),me=t("InfoBasePath"),le=t("License",!0),ve=t("Contact",!0),be=t("JsonSchemaDialect",!0);return u.createElement("div",{className:"info"},u.createElement("hgroup",{className:"main"},u.createElement("h1",{className:"title"},d,u.createElement("span",null,r&&u.createElement(Q,{version:r}),u.createElement(J,{oasVersion:"3.1"}))),(o||a)&&u.createElement(me,{host:o,basePath:a}),n&&u.createElement(re,{getComponent:t,url:n})),p&&u.createElement("p",{className:"info__summary"},p),u.createElement("div",{className:"info__description description"},u.createElement(L,{source:c})),E&&u.createElement("div",{className:"info__tos"},u.createElement(V,{target:"_blank",href:Dr(E)},"Terms of service")),P.size>0&&u.createElement(ve,null),R.size>0&&u.createElement(le,null),x&&u.createElement(V,{className:"info__extdocs",target:"_blank",href:Dr(x)},k||x),u.createElement(be,null))},l$=({getComponent:t,specSelectors:e})=>{const r=e.selectJsonSchemaDialectField(),n=e.selectJsonSchemaDialectDefault(),a=t("Link");return u.createElement(u.Fragment,null,r&&r===n&&u.createElement("p",{className:"info__jsonschemadialect"},"JSON Schema dialect:"," ",u.createElement(a,{target:"_blank",href:Dr(r)},r)),r&&r!==n&&u.createElement("div",{className:"error-wrapper"},u.createElement("div",{className:"no-margin"},u.createElement("div",{className:"errors"},u.createElement("div",{className:"errors-wrapper"},u.createElement("h4",{className:"center"},"Warning"),u.createElement("p",{className:"message"},u.createElement("strong",null,"OpenAPI.jsonSchemaDialect")," field contains a value different from the default value of"," ",u.createElement(a,{target:"_blank",href:n},n),". Values different from the default one are currently not supported. Please either omit the field or provide it with the default value."))))))},c$=({bypass:t,isSwagger2:e,isOAS3:r,isOAS31:n,alsoShow:a,children:o})=>t?u.createElement("div",null,o):e&&(r||n)?u.createElement("div",{className:"version-pragma"},a,u.createElement("div",{className:"version-pragma__message version-pragma__message--ambiguous"},u.createElement("div",null,u.createElement("h3",null,"Unable to render this definition"),u.createElement("p",null,u.createElement("code",null,"swagger")," and ",u.createElement("code",null,"openapi")," fields cannot be present in the same Swagger or OpenAPI definition. Please remove one of the fields."),u.createElement("p",null,"Supported version fields are ",u.createElement("code",null,'swagger: "2.0"'),","," ",u.createElement("code",null,"openapi: 3.0.x"),", or ",u.createElement("code",null,"openapi: 3.1.x")," (for example, ",u.createElement("code",null,"openapi: 3.1.0"),").")))):e||r||n?u.createElement("div",null,o):u.createElement("div",{className:"version-pragma"},a,u.createElement("div",{className:"version-pragma__message version-pragma__message--missing"},u.createElement("div",null,u.createElement("h3",null,"Unable to render this definition"),u.createElement("p",null,"The provided definition does not specify a valid version field."),u.createElement("p",null,"Please indicate a valid Swagger or OpenAPI version field. Supported version fields are ",u.createElement("code",null,'swagger: "2.0"'),","," ",u.createElement("code",null,"openapi: 3.0.x"),", or ",u.createElement("code",null,"openapi: 3.1.x")," (for example, ",u.createElement("code",null,"openapi: 3.1.0"),").")))),u$=t=>typeof t=="string"&&t.includes("#/components/schemas/")?(e=>{const r=e.replace(/~1/g,"/").replace(/~0/g,"~");try{return decodeURIComponent(r)}catch{return r}})(t.replace(/^.*#\/components\/schemas\//,"")):null,p$=(0,u.forwardRef)((({schema:t,getComponent:e,onToggle:r=()=>{},specPath:n},a)=>{const o=e("JSONSchema202012"),p=u$(t.get("$$ref")),c=(0,u.useCallback)(((d,E)=>{r(p,E)}),[p,r]);return u.createElement(o,{name:p,schema:t.toJS(),ref:a,onExpand:c,identifier:n.toJS().join("_")})})),h$=p$,d$=({specActions:t,specSelectors:e,layoutSelectors:r,layoutActions:n,getComponent:a,getConfigs:o,fn:p})=>{const c=e.selectSchemas(),d=Object.keys(c).length>0,E=["components","schemas"],{docExpansion:x,defaultModelsExpandDepth:k}=o(),P=k>0&&x!=="none",R=r.isShown(E,P),L=a("Collapse"),V=a("JSONSchema202012"),Q=a("ArrowUpIcon"),J=a("ArrowDownIcon"),{getTitle:re}=p.jsonSchema202012.useFn();(0,u.useEffect)((()=>{const ce=Object.entries(c).some((([xe])=>r.isShown([...E,xe],!1))),_e=R&&(k>1||ce),Ae=e.specResolvedSubtree(E)!=null;_e&&!Ae&&t.requestResolvedSubtree(E)}),[R,k]);const me=(0,u.useCallback)((()=>{n.show(E,!R)}),[R]),le=(0,u.useCallback)((ce=>{ce!==null&&n.readyToScroll(E,ce)}),[]),ve=ce=>_e=>{_e!==null&&n.readyToScroll([...E,ce],_e)},be=ce=>(_e,Ae)=>{const xe=[...E,ce];Ae?(e.specResolvedSubtree(xe)!=null||t.requestResolvedSubtree([...E,ce]),n.show(xe,!0)):n.show(xe,!1)};return!d||k<0?null:u.createElement("section",{className:Xr()("models",{"is-open":R}),ref:le},u.createElement("h4",null,u.createElement("button",{"aria-expanded":R,className:"models-control",onClick:me},u.createElement("span",null,"Schemas"),R?u.createElement(Q,null):u.createElement(J,null))),u.createElement(L,{isOpened:R},Object.entries(c).map((([ce,_e])=>{const Ae=re(_e,{lookup:"basic"})||ce;return u.createElement(V,{key:ce,ref:ve(ce),schema:_e,name:Ae,onExpand:be(ce)})}))))},f$=({schema:t,getComponent:e,name:r,authSelectors:n})=>{const a=e("JumpToPath",!0),o=n.selectAuthPath(r);return u.createElement("div",null,u.createElement("h4",null,r," (mutualTLS) ",u.createElement(a,{path:o})),u.createElement("p",null,"Mutual TLS is required by this API/Operation. Certificates are managed via your Operating System and/or your browser."),u.createElement("p",null,t.get("description")))};class m$ extends u.Component{constructor(e,r){super(e,r),this.state={}}onAuthChange=e=>{let{name:r}=e;this.setState({[r]:e})};submitAuth=e=>{e.preventDefault();let{authActions:r}=this.props;r.authorizeWithPersistOption(this.state)};logoutClick=e=>{e.preventDefault();let{authActions:r,definitions:n}=this.props,a=n.keySeq().toArray();this.setState(a.reduce(((o,p)=>(o[p]="",o)),{})),r.logoutWithPersistOption(a)};close=e=>{e.preventDefault();let{authActions:r}=this.props;r.showDefinitions(!1)};render(){let{definitions:e,getComponent:r,authSelectors:n,errSelectors:a}=this.props;const o=r("AuthItem"),p=r("oauth2",!0),c=r("Button"),d=n.authorized(),E=e.filter(((R,L)=>!!d.get(L))),x=e.filter((R=>R.get("type")!=="oauth2"&&R.get("type")!=="mutualTLS")),k=e.filter((R=>R.get("type")==="oauth2")),P=e.filter((R=>R.get("type")==="mutualTLS"));return u.createElement("div",{className:"auth-container"},x.size>0&&u.createElement("form",{onSubmit:this.submitAuth},x.map(((R,L)=>u.createElement(o,{key:L,schema:R,name:L,getComponent:r,onAuthChange:this.onAuthChange,authorized:d,errSelectors:a,authSelectors:n}))).valueSeq().toArray(),u.createElement("div",{className:"auth-btn-wrapper"},x.size===E.size?u.createElement(c,{className:"btn modal-btn auth",onClick:this.logoutClick,"aria-label":"Remove authorization"},"Logout"):u.createElement(c,{type:"submit",className:"btn modal-btn auth authorize","aria-label":"Apply credentials"},"Authorize"),u.createElement(c,{className:"btn modal-btn auth btn-done",onClick:this.close},"Close"))),k.size>0?u.createElement("div",null,u.createElement("div",{className:"scope-def"},u.createElement("p",null,"Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes."),u.createElement("p",null,"API requires the following scopes. Select which ones you want to grant to Swagger UI.")),e.filter((R=>R.get("type")==="oauth2")).map(((R,L)=>u.createElement("div",{key:L},u.createElement(p,{authorized:d,schema:R,name:L})))).valueSeq().toArray()):null,P.size>0&&u.createElement("div",null,P.map(((R,L)=>u.createElement(o,{key:L,schema:R,name:L,getComponent:r,onAuthChange:this.onAuthChange,authorized:d,errSelectors:a,authSelectors:n}))).valueSeq().toArray()))}}const g$=m$,oO=t=>{const e=t.get("openapi");return typeof e=="string"&&/^3\.1\.(?:[1-9]\d*|0)$/.test(e)},iO=t=>(e,...r)=>n=>{if(n.getSystem().specSelectors.isOAS31()){const a=t(e,...r);return typeof a=="function"?a(n):a}return null},sO=t=>(e,r)=>(n,...a)=>{if(r.getSystem().specSelectors.isOAS31()){const o=t(n,...a);return typeof o=="function"?o(e,r):o}return e(...a)},lO=t=>(e,...r)=>n=>{const a=t(e,n,...r);return typeof a=="function"?a(n):a},Wl=t=>(e,r)=>n=>r.specSelectors.isOAS31()?u.createElement(t,Yr()({},n,{originalComponent:e,getSystem:r.getSystem})):u.createElement(e,n),Cw=(t,e)=>{const{fn:r,specSelectors:n}=e;return Object.fromEntries(Object.entries(t).map((([a,o])=>{const p=r[a];return[a,(...c)=>n.isOAS31()?o(...c):typeof p=="function"?p(...c):void 0]})))},y$=Wl((({getSystem:t})=>{const e=t().getComponent("OAS31License",!0);return u.createElement(e,null)})),v$=Wl((({getSystem:t})=>{const e=t().getComponent("OAS31Contact",!0);return u.createElement(e,null)})),b$=Wl((({getSystem:t})=>{const e=t().getComponent("OAS31Info",!0);return u.createElement(e,null)})),w$=(t,{includeReadOnly:e,includeWriteOnly:r})=>{if(!t?.properties)return{};const n=Object.entries(t.properties).filter((([,a])=>(a?.readOnly!==!0||e)&&(a?.writeOnly!==!0||r)));return Object.fromEntries(n)},cO=t=>{if(typeof t!="function")return null;const e=t();return()=>[...e,"discriminator","xml","externalDocs","example","$$ref"]},Tc=Wl((({getSystem:t,...e})=>{const r=t(),{getComponent:n,fn:a,getConfigs:o}=r,p=o(),c=window?.location?.pathname??"/";if(Tc.ModelWithJSONSchemaContext&&Tc.pathname===c)return u.createElement(Tc.ModelWithJSONSchemaContext,e);const d=n("OAS31Model"),E=n("withJSONSchema202012SystemContext");return Tc.ModelWithJSONSchemaContext=E(d,{config:{default$schema:"https://spec.openapis.org/oas/3.1/dialect/base",defaultExpandedLevels:p.defaultModelExpandDepth,includeReadOnly:e.includeReadOnly,includeWriteOnly:e.includeWriteOnly},fn:{getProperties:a.jsonSchema202012.getProperties,isExpandable:a.jsonSchema202012.isExpandable,getSchemaKeywords:cO(a.jsonSchema202012.getSchemaKeywords)}}),Tc.pathname=c,u.createElement(Tc.ModelWithJSONSchemaContext,e)}));Tc.ModelWithJSONSchemaContext=null,Tc.pathname=null;const E$=Tc,Ic=Wl((({getSystem:t})=>{const{getComponent:e,fn:r,getConfigs:n}=t(),a=n(),o=window?.location?.pathname??"/";if(Ic.ModelsWithJSONSchemaContext&&Ic.pathname===o)return u.createElement(Ic.ModelsWithJSONSchemaContext,null);const p=e("OAS31Models",!0),c=e("withJSONSchema202012SystemContext");return Ic.ModelsWithJSONSchemaContext=c(p,{config:{default$schema:"https://spec.openapis.org/oas/3.1/dialect/base",defaultExpandedLevels:a.defaultModelsExpandDepth-1,includeReadOnly:!0,includeWriteOnly:!0},fn:{getProperties:r.jsonSchema202012.getProperties,isExpandable:r.jsonSchema202012.isExpandable,getSchemaKeywords:cO(r.jsonSchema202012.getSchemaKeywords)}}),Ic.pathname=o,u.createElement(Ic.ModelsWithJSONSchemaContext,null)}));Ic.ModelsWithJSONSchemaContext=null,Ic.pathname=null;const S$=Ic,x$=(t,e)=>r=>{const n=e.specSelectors.isOAS31(),a=e.getComponent("OAS31VersionPragmaFilter");return u.createElement(a,Yr()({isOAS31:n},r))},_$=Wl((({originalComponent:t,...e})=>{const{getComponent:r,schema:n,name:a}=e,o=r("MutualTLSAuth",!0);return n.get("type")==="mutualTLS"?u.createElement(o,{schema:n,name:a}):u.createElement(t,e)})),O$=_$,k$=Wl((({getSystem:t,...e})=>{const r=t().getComponent("OAS31Auths",!0);return u.createElement(r,e)})),jw=(0,H.Map)(),A$=yr(((t,e)=>e.specSelectors.specJson()),oO),C$=()=>t=>{const e=t.specSelectors.specJson().get("webhooks");return H.Map.isMap(e)?e:jw},j$=yr([(t,e)=>e.specSelectors.webhooks(),(t,e)=>e.specSelectors.validOperationMethods(),(t,e)=>e.specSelectors.specResolvedSubtree(["webhooks"])],((t,e)=>t.reduce(((r,n,a)=>{if(!H.Map.isMap(n))return r;const o=n.entrySeq().filter((([p])=>e.includes(p))).map((([p,c])=>({operation:(0,H.Map)({operation:c}),method:p,path:a,specPath:["webhooks",a,p]})));return r.concat(o)}),(0,H.List)()).groupBy((r=>r.path)).map((r=>r.toArray())).toObject())),P$=()=>t=>{const e=t.specSelectors.info().get("license");return H.Map.isMap(e)?e:jw},N$=()=>t=>t.specSelectors.license().get("name","License"),T$=()=>t=>t.specSelectors.license().get("url"),I$=yr([(t,e)=>e.specSelectors.url(),(t,e)=>e.oas3Selectors.selectedServer(),(t,e)=>e.specSelectors.selectLicenseUrlField()],((t,e,r)=>{if(r)return ba(r,t,{selectedServer:e})})),M$=()=>t=>t.specSelectors.license().get("identifier"),R$=()=>t=>{const e=t.specSelectors.info().get("contact");return H.Map.isMap(e)?e:jw},D$=()=>t=>t.specSelectors.contact().get("name","the developer"),F$=()=>t=>t.specSelectors.contact().get("email"),L$=()=>t=>t.specSelectors.contact().get("url"),$$=yr([(t,e)=>e.specSelectors.url(),(t,e)=>e.oas3Selectors.selectedServer(),(t,e)=>e.specSelectors.selectContactUrlField()],((t,e,r)=>{if(r)return ba(r,t,{selectedServer:e})})),B$=()=>t=>t.specSelectors.info().get("title"),q$=()=>t=>t.specSelectors.info().get("summary"),U$=()=>t=>t.specSelectors.info().get("description"),z$=()=>t=>t.specSelectors.info().get("termsOfService"),V$=yr([(t,e)=>e.specSelectors.url(),(t,e)=>e.oas3Selectors.selectedServer(),(t,e)=>e.specSelectors.selectInfoTermsOfServiceField()],((t,e,r)=>{if(r)return ba(r,t,{selectedServer:e})})),J$=()=>t=>t.specSelectors.externalDocs().get("description"),W$=()=>t=>t.specSelectors.externalDocs().get("url"),H$=yr([(t,e)=>e.specSelectors.url(),(t,e)=>e.oas3Selectors.selectedServer(),(t,e)=>e.specSelectors.selectExternalDocsUrlField()],((t,e,r)=>{if(r)return ba(r,t,{selectedServer:e})})),K$=()=>t=>t.specSelectors.specJson().get("jsonSchemaDialect"),G$=()=>"https://spec.openapis.org/oas/3.1/dialect/base",Y$=yr(((t,e)=>e.specSelectors.definitions()),((t,e)=>e.specSelectors.specResolvedSubtree(["components","schemas"])),((t,e)=>H.Map.isMap(t)?H.Map.isMap(e)?Object.entries(t.toJS()).reduce(((r,[n,a])=>{const o=e.get(n);return r[n]=o?.toJS()||a,r}),{}):t.toJS():{})),X$=(t,e)=>(r,...n)=>e.specSelectors.isOAS31()||t(...n),Q$=sO((()=>(t,e)=>e.oas31Selectors.selectLicenseUrl())),Z$=sO((()=>(t,e)=>{const r=e.specSelectors.securityDefinitions();let n=t();return r&&r.entrySeq().forEach((([a,o])=>{o?.get("type")==="mutualTLS"&&(n=n.push(new H.Map({[a]:o})))})),n})),eB=yr([(t,e)=>e.specSelectors.url(),(t,e)=>e.oas3Selectors.selectedServer(),(t,e)=>e.specSelectors.selectLicenseUrlField(),(t,e)=>e.specSelectors.selectLicenseIdentifierField()],((t,e,r,n)=>r?ba(r,t,{selectedServer:e}):n?`https://spdx.org/licenses/${n}.html`:void 0)),tB=({schema:t,getSystem:e})=>{const{fn:r,getComponent:n}=e(),{hasKeyword:a}=r.jsonSchema202012.useFn(),o=n("JSONSchema202012JSONViewer");return a(t,"example")?u.createElement(o,{name:"Example",value:t.example,className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--example"}):null},rB=({schema:t,getSystem:e})=>{const r=t?.xml||{},{fn:n,getComponent:a,getConfigs:o}=e(),{showExtensions:p}=o(),{useComponent:c,useIsExpanded:d,usePath:E,useLevel:x}=n.jsonSchema202012,{path:k}=E("xml"),{isExpanded:P,setExpanded:R,setCollapsed:L}=d("xml"),[V,Q]=x(),J=p?gr(r):[],re=!!(r.name||r.namespace||r.prefix||J.length>0),me=c("Accordion"),le=c("ExpandDeepButton"),ve=a("OpenAPI31Extensions"),be=a("JSONSchema202012PathContext")(),ce=a("JSONSchema202012LevelContext")(),_e=(0,u.useCallback)((()=>{P?L():R()}),[P,R,L]),Ae=(0,u.useCallback)(((xe,Oe)=>{Oe?R({deep:!0}):L({deep:!0})}),[R,L]);return Object.keys(r).length===0?null:u.createElement(be.Provider,{value:k},u.createElement(ce.Provider,{value:Q},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--xml","data-json-schema-level":V},re?u.createElement(u.Fragment,null,u.createElement(me,{expanded:P,onChange:_e},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"XML")),u.createElement(le,{expanded:P,onClick:Ae})):u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"XML"),r.attribute===!0&&u.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},"attribute"),r.wrapped===!0&&u.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},"wrapped"),u.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),u.createElement("ul",{className:Xr()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!P})},P&&u.createElement(u.Fragment,null,r.name&&u.createElement("li",{className:"json-schema-2020-12-property"},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"name"),u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},r.name))),r.namespace&&u.createElement("li",{className:"json-schema-2020-12-property"},u.createElement("div",{className:"json-schema-2020-12-keyword"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"namespace"),u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},r.namespace))),r.prefix&&u.createElement("li",{className:"json-schema-2020-12-property"},u.createElement("div",{className:"json-schema-2020-12-keyword"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"prefix"),u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},r.prefix)))),J.length>0&&u.createElement(ve,{openAPISpecObj:r,openAPIExtensions:J,getSystem:e})))))},nB=({discriminator:t})=>{const e=t?.mapping||{};return Object.keys(e).length===0?null:Object.entries(e).map((([r,n])=>u.createElement("div",{key:`${r}-${n}`,className:"json-schema-2020-12-keyword"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},r),u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},n))))},aB=({schema:t,getSystem:e})=>{const r=t?.discriminator||{},{fn:n,getComponent:a,getConfigs:o}=e(),{showExtensions:p}=o(),{useComponent:c,useIsExpanded:d,usePath:E,useLevel:x}=n.jsonSchema202012,k="discriminator",{path:P}=E(k),{isExpanded:R,setExpanded:L,setCollapsed:V}=d(k),[Q,J]=x(),re=p?gr(r):[],me=!!(r.mapping||re.length>0),le=c("Accordion"),ve=c("ExpandDeepButton"),be=a("OpenAPI31Extensions"),ce=a("JSONSchema202012PathContext")(),_e=a("JSONSchema202012LevelContext")(),Ae=(0,u.useCallback)((()=>{R?V():L()}),[R,L,V]),xe=(0,u.useCallback)(((Oe,je)=>{je?L({deep:!0}):V({deep:!0})}),[L,V]);return Object.keys(r).length===0?null:u.createElement(ce.Provider,{value:P},u.createElement(_e.Provider,{value:J},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--discriminator","data-json-schema-level":Q},me?u.createElement(u.Fragment,null,u.createElement(le,{expanded:R,onChange:Ae},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"Discriminator")),u.createElement(ve,{expanded:R,onClick:xe})):u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"Discriminator"),r.propertyName&&u.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},r.propertyName),u.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),u.createElement("ul",{className:Xr()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!R})},R&&u.createElement("li",{className:"json-schema-2020-12-property"},u.createElement(nB,{discriminator:r})),re.length>0&&u.createElement(be,{openAPISpecObj:r,openAPIExtensions:re,getSystem:e})))))},oB=({openAPISpecObj:t,getSystem:e,openAPIExtensions:r})=>{const{fn:n}=e(),{useComponent:a}=n.jsonSchema202012,o=a("JSONViewer");return r.map((p=>u.createElement(o,{key:p,name:p,value:t[p],className:"json-schema-2020-12-json-viewer-extension-keyword"})))},iB=({schema:t,getSystem:e})=>{const r=t?.externalDocs||{},{fn:n,getComponent:a,getConfigs:o}=e(),{showExtensions:p}=o(),{useComponent:c,useIsExpanded:d,usePath:E,useLevel:x}=n.jsonSchema202012,k="externalDocs",{path:P}=E(k),{isExpanded:R,setExpanded:L,setCollapsed:V}=d(k),[Q,J]=x(),re=p?gr(r):[],me=!!(r.description||r.url||re.length>0),le=c("Accordion"),ve=c("ExpandDeepButton"),be=a("JSONSchema202012KeywordDescription"),ce=a("Link"),_e=a("OpenAPI31Extensions"),Ae=a("JSONSchema202012PathContext")(),xe=a("JSONSchema202012LevelContext")(),Oe=(0,u.useCallback)((()=>{R?V():L()}),[R,L,V]),je=(0,u.useCallback)(((G,de)=>{de?L({deep:!0}):V({deep:!0})}),[L,V]);return Object.keys(r).length===0?null:u.createElement(Ae.Provider,{value:P},u.createElement(xe.Provider,{value:J},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--externalDocs","data-json-schema-level":Q},me?u.createElement(u.Fragment,null,u.createElement(le,{expanded:R,onChange:Oe},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"External documentation")),u.createElement(ve,{expanded:R,onClick:je})):u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"External documentation"),u.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),u.createElement("ul",{className:Xr()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!R})},R&&u.createElement(u.Fragment,null,r.description&&u.createElement("li",{className:"json-schema-2020-12-property"},u.createElement(be,{schema:r,getSystem:e})),r.url&&u.createElement("li",{className:"json-schema-2020-12-property"},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"url"),u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},u.createElement(ce,{target:"_blank",href:Dr(r.url)},r.url))))),re.length>0&&u.createElement(_e,{openAPISpecObj:r,openAPIExtensions:re,getSystem:e})))))},sB=({schema:t,getSystem:e})=>{if(!t?.description)return null;const{getComponent:r}=e(),n=r("Markdown");return u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--description"},u.createElement("div",{className:"json-schema-2020-12-core-keyword__value json-schema-2020-12-core-keyword__value--secondary"},u.createElement(n,{source:t.description})))},lB=Wl(sB),cB=Wl((({schema:t,getSystem:e,originalComponent:r})=>{const{getComponent:n}=e(),a=n("JSONSchema202012KeywordDiscriminator"),o=n("JSONSchema202012KeywordXml"),p=n("JSONSchema202012KeywordExample"),c=n("JSONSchema202012KeywordExternalDocs");return u.createElement(u.Fragment,null,u.createElement(r,{schema:t}),u.createElement(a,{schema:t,getSystem:e}),u.createElement(o,{schema:t,getSystem:e}),u.createElement(c,{schema:t,getSystem:e}),u.createElement(p,{schema:t,getSystem:e}))})),uB=cB,pB=({schema:t,getSystem:e})=>{const{fn:r,getComponent:n}=e(),{useComponent:a,usePath:o}=r.jsonSchema202012,{getDependentRequired:p,getProperties:c}=r.jsonSchema202012.useFn(),d=r.jsonSchema202012.useConfig(),E=Array.isArray(t?.required)?t.required:[],{path:x}=o("properties"),k=a("JSONSchema"),P=n("JSONSchema202012PathContext")(),R=c(t,d);return Object.keys(R).length===0?null:u.createElement(P.Provider,{value:x},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--properties"},u.createElement("ul",null,Object.entries(R).map((([L,V])=>{const Q=E.includes(L),J=p(L,t);return u.createElement("li",{key:L,className:Xr()("json-schema-2020-12-property",{"json-schema-2020-12-property--required":Q})},u.createElement(k,{name:L,schema:V,dependentRequired:J}))})))))},hB=Wl(pB),dB=function({fn:e,getSystem:r}){if(e.jsonSchema202012){const o=((p,c)=>{const{fn:d}=c();if(typeof p!="function")return null;const{hasKeyword:E}=d.jsonSchema202012;return x=>p(x)||E(x,"example")||x?.xml||x?.discriminator||x?.externalDocs})(e.jsonSchema202012.isExpandable,r);Object.assign(this.fn.jsonSchema202012,{isExpandable:o,getProperties:w$})}if(typeof e.sampleFromSchema=="function"&&e.jsonSchema202012){const o=Cw({sampleFromSchema:e.jsonSchema202012.sampleFromSchema,sampleFromSchemaGeneric:e.jsonSchema202012.sampleFromSchemaGeneric,createXMLExample:e.jsonSchema202012.createXMLExample,memoizedSampleFromSchema:e.jsonSchema202012.memoizedSampleFromSchema,memoizedCreateXMLExample:e.jsonSchema202012.memoizedCreateXMLExample,getJsonSampleSchema:e.jsonSchema202012.getJsonSampleSchema,getYamlSampleSchema:e.jsonSchema202012.getYamlSampleSchema,getXmlSampleSchema:e.jsonSchema202012.getXmlSampleSchema,getSampleSchema:e.jsonSchema202012.getSampleSchema,mergeJsonSchema:e.jsonSchema202012.mergeJsonSchema,getSchemaObjectTypeLabel:p=>e.jsonSchema202012.getType(Mr(p)),getSchemaObjectType:p=>e.jsonSchema202012.foldType(Mr(p)?.type)},r());Object.assign(this.fn,o)}const n=(o=>(p,c=null)=>{const{fn:d}=o();if(d.isFileUploadIntendedOAS30(p,c))return!0;const E=H.Map.isMap(p);if(!E&&!Ai()(p))return!1;const x=E?p.get("contentMediaType"):p.contentMediaType,k=E?p.get("contentEncoding"):p.contentEncoding;return typeof x=="string"&&x!==""||typeof k=="string"&&k!==""})(r),{isFileUploadIntended:a}=Cw({isFileUploadIntended:n},r());if(this.fn.isFileUploadIntended=a,this.fn.isFileUploadIntendedOAS31=n,e.jsonSchema202012){const{hasSchemaType:o}=Cw({hasSchemaType:e.jsonSchema202012.hasSchemaType},r());this.fn.hasSchemaType=o}},fB=({fn:t})=>{const e=t.createSystemSelector||lO,r=t.createOnlyOAS31Selector||iO;return{afterLoad:dB,fn:{isOAS31:oO,createSystemSelector:lO,createOnlyOAS31Selector:iO},components:{Webhooks:a$,JsonSchemaDialect:l$,MutualTLSAuth:f$,OAS31Info:s$,OAS31License:o$,OAS31Contact:i$,OAS31VersionPragmaFilter:c$,OAS31Model:h$,OAS31Models:d$,OAS31Auths:g$,JSONSchema202012KeywordExample:tB,JSONSchema202012KeywordXml:rB,JSONSchema202012KeywordDiscriminator:aB,JSONSchema202012KeywordExternalDocs:iB,OpenAPI31Extensions:oB},wrapComponents:{InfoContainer:b$,License:y$,Contact:v$,VersionPragmaFilter:x$,Model:E$,Models:S$,AuthItem:O$,auths:k$,JSONSchema202012KeywordDescription:lB,JSONSchema202012KeywordExamples:uB,JSONSchema202012KeywordProperties:hB},statePlugins:{auth:{wrapSelectors:{definitionsToAuthorize:Z$}},spec:{selectors:{isOAS31:e(A$),license:P$,selectLicenseNameField:N$,selectLicenseUrlField:T$,selectLicenseIdentifierField:r(M$),selectLicenseUrl:e(I$),contact:R$,selectContactNameField:D$,selectContactEmailField:F$,selectContactUrlField:L$,selectContactUrl:e($$),selectInfoTitleField:B$,selectInfoSummaryField:r(q$),selectInfoDescriptionField:U$,selectInfoTermsOfServiceField:z$,selectInfoTermsOfServiceUrl:e(V$),selectExternalDocsDescriptionField:J$,selectExternalDocsUrlField:W$,selectExternalDocsUrl:e(H$),webhooks:r(C$),selectWebhooksOperations:r(e(j$)),selectJsonSchemaDialectField:K$,selectJsonSchemaDialectDefault:G$,selectSchemas:e(Y$)},wrapSelectors:{isOAS3:X$,selectLicenseUrl:Q$}},oas31:{selectors:{selectLicenseUrl:r(e(eB))}}}}},mB=({bypass:t,isSwagger2:e,isOAS3:r,isOAS31:n,isOAS32:a,alsoShow:o,children:p})=>t?u.createElement("div",null,p):e&&(r||n||a)?u.createElement("div",{className:"version-pragma"},o,u.createElement("div",{className:"version-pragma__message version-pragma__message--ambiguous"},u.createElement("div",null,u.createElement("h3",null,"Unable to render this definition"),u.createElement("p",null,u.createElement("code",null,"swagger")," and ",u.createElement("code",null,"openapi")," fields cannot be present in the same Swagger or OpenAPI definition. Please remove one of the fields."),u.createElement("p",null,"Supported version fields are ",u.createElement("code",null,'swagger: "2.0"')," ","and ",u.createElement("code",null,"openapi: 3.0.x"),", ",u.createElement("code",null,"openapi: 3.1.x"),", or"," ",u.createElement("code",null,"openapi: 3.2.x")," (for example,"," ",u.createElement("code",null,"openapi: 3.2.0"),").")))):e||r||n||a?u.createElement("div",null,p):u.createElement("div",{className:"version-pragma"},o,u.createElement("div",{className:"version-pragma__message version-pragma__message--missing"},u.createElement("div",null,u.createElement("h3",null,"Unable to render this definition"),u.createElement("p",null,"The provided definition does not specify a valid version field."),u.createElement("p",null,"Please indicate a valid Swagger or OpenAPI version field. Supported version fields are ",u.createElement("code",null,'swagger: "2.0"')," ","and ",u.createElement("code",null,"openapi: 3.0.x"),", ",u.createElement("code",null,"openapi: 3.1.x"),", or"," ",u.createElement("code",null,"openapi: 3.2.x")," (for example,"," ",u.createElement("code",null,"openapi: 3.2.0"),").")))),uO=t=>{const e=t.get("openapi");return typeof e=="string"&&/^3\.2\.(?:[1-9]\d*|0)$/.test(e)},gB=t=>(e,...r)=>n=>{if(n.getSystem().specSelectors.isOAS32()){const a=t(e,...r);return typeof a=="function"?a(n):a}return null},pO=t=>(e,...r)=>n=>{const a=t(e,n,...r);return typeof a=="function"?a(n):a},Mc=t=>(e,r)=>n=>r.specSelectors.isOAS32()?u.createElement(t,Yr()({},n,{originalComponent:e,getSystem:r.getSystem})):u.createElement(e,n),Pw=(t,e)=>{const{fn:r,specSelectors:n}=e;return Object.fromEntries(Object.entries(t).map((([a,o])=>{const p=r[a];return[a,(...c)=>n.isOAS32()?o(...c):typeof p=="function"?p(...c):void 0]})))},yB=Mc((t=>{const{getSystem:e}=t,r=e().getComponent("OAS31Contact",!0);return u.createElement(r,t)})),vB=Mc((({getSystem:t})=>{const e=t().getComponent("OAS31Info",!0);return u.createElement(e,null)})),bB=Mc((t=>{const{getSystem:e}=t,r=e().getComponent("OAS31License",!0);return u.createElement(r,t)})),hO=t=>{if(typeof t!="function")return null;const e=t();return()=>[...e,"discriminator","xml","externalDocs","example","$$ref"]},Rc=Mc((({getSystem:t,...e})=>{const r=t(),{getComponent:n,fn:a,getConfigs:o}=r,p=o(),c=window?.location?.pathname??"/";if(Rc.ModelWithJSONSchemaContext&&Rc.pathname===c)return u.createElement(Rc.ModelWithJSONSchemaContext,e);const d=n("OAS31Model"),E=n("withJSONSchema202012SystemContext");return Rc.ModelWithJSONSchemaContext=E(d,{config:{default$schema:"https://spec.openapis.org/oas/3.2/schema/2025-09-17",defaultExpandedLevels:p.defaultModelExpandDepth,includeReadOnly:e.includeReadOnly,includeWriteOnly:e.includeWriteOnly},fn:{getProperties:a.jsonSchema202012.getProperties,isExpandable:a.jsonSchema202012.isExpandable,getSchemaKeywords:hO(a.jsonSchema202012.getSchemaKeywords)}}),Rc.pathname=c,u.createElement(Rc.ModelWithJSONSchemaContext,e)}));Rc.ModelWithJSONSchemaContext=null,Rc.pathname=null;const wB=Rc,Dc=Mc((({getSystem:t})=>{const{getComponent:e,fn:r,getConfigs:n}=t(),a=n(),o=window?.location?.pathname??"/";if(Dc.ModelsWithJSONSchemaContext&&Dc.pathname===o)return u.createElement(Dc.ModelsWithJSONSchemaContext,null);const p=e("OAS31Models",!0),c=e("withJSONSchema202012SystemContext");return Dc.ModelsWithJSONSchemaContext=c(p,{config:{default$schema:"https://spec.openapis.org/oas/3.2/schema/2025-09-17",defaultExpandedLevels:a.defaultModelsExpandDepth-1,includeReadOnly:!0,includeWriteOnly:!0},fn:{getProperties:r.jsonSchema202012.getProperties,isExpandable:r.jsonSchema202012.isExpandable,getSchemaKeywords:hO(r.jsonSchema202012.getSchemaKeywords)}}),Dc.pathname=o,u.createElement(Dc.ModelsWithJSONSchemaContext,null)}));Dc.ModelsWithJSONSchemaContext=null,Dc.pathname=null;const EB=Dc,SB=Mc((t=>{const{originalComponent:e}=t;return u.createElement(e,{oasVersion:"3.2"})})),xB=(t,e)=>r=>{const n=e.specSelectors.isOAS32(),a=e.getComponent("OAS32VersionPragmaFilter");return u.createElement(a,Yr()({isOAS32:n},r))},_B=({schema:t,getSystem:e})=>{if(!t?.description)return null;const{getComponent:r}=e(),n=r("Markdown");return u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--description"},u.createElement("div",{className:"json-schema-2020-12-core-keyword__value json-schema-2020-12-core-keyword__value--secondary"},u.createElement(n,{source:t.description})))},OB=Mc(_B),kB=Mc((({schema:t,getSystem:e,originalComponent:r})=>{const{getComponent:n}=e(),a=n("JSONSchema202012KeywordDiscriminator"),o=n("JSONSchema202012KeywordXml"),p=n("JSONSchema202012KeywordExample"),c=n("JSONSchema202012KeywordExternalDocs");return u.createElement(u.Fragment,null,u.createElement(r,{schema:t}),u.createElement(a,{schema:t,getSystem:e}),u.createElement(o,{schema:t,getSystem:e}),u.createElement(c,{schema:t,getSystem:e}),u.createElement(p,{schema:t,getSystem:e}))})),AB=kB,CB=({schema:t,getSystem:e})=>{const{fn:r,getComponent:n}=e(),{useComponent:a,usePath:o}=r.jsonSchema202012,{getDependentRequired:p,getProperties:c}=r.jsonSchema202012.useFn(),d=r.jsonSchema202012.useConfig(),E=Array.isArray(t?.required)?t.required:[],{path:x}=o("properties"),k=a("JSONSchema"),P=n("JSONSchema202012PathContext")(),R=c(t,d);return Object.keys(R).length===0?null:u.createElement(P.Provider,{value:x},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--properties"},u.createElement("ul",null,Object.entries(R).map((([L,V])=>{const Q=E.includes(L),J=p(L,t);return u.createElement("li",{key:L,className:Xr()("json-schema-2020-12-property",{"json-schema-2020-12-property--required":Q})},u.createElement(k,{name:L,schema:V,dependentRequired:J}))})))))},jB=Mc(CB),PB=vu()(["get","put","post","delete","options","head","patch","trace","query"]),NB=(t,e)=>(r,...n)=>e.specSelectors.isOAS32()||t(...n),TB=(dO=()=>(t,e)=>e.oas32Selectors.validOperationMethods(),(t,e)=>(r,...n)=>{if(e.getSystem().specSelectors.isOAS32()){const a=dO(r,...n);return typeof a=="function"?a(t,e):a}return t(...n)});var dO;const fO=(0,H.Map)(),IB=(t,e)=>()=>{const r=e.specSelectors.specJson();return uO(r)},MB=()=>t=>{const e=t.specSelectors.info().get("license");return H.Map.isMap(e)?e:fO},RB=()=>t=>t.specSelectors.license().get("name","License"),DB=()=>t=>t.specSelectors.license().get("url"),FB=yr([(t,e)=>e.specSelectors.url(),(t,e)=>e.oas3Selectors.selectedServer(),(t,e)=>e.specSelectors.selectLicenseUrlField()],((t,e,r)=>{if(r)return ba(r,t,{selectedServer:e})})),LB=()=>t=>t.specSelectors.license().get("identifier"),$B=()=>t=>{const e=t.specSelectors.info().get("contact");return H.Map.isMap(e)?e:fO},BB=()=>t=>t.specSelectors.contact().get("name","the developer"),qB=()=>t=>t.specSelectors.contact().get("email"),UB=()=>t=>t.specSelectors.contact().get("url"),zB=yr([(t,e)=>e.specSelectors.url(),(t,e)=>e.oas3Selectors.selectedServer(),(t,e)=>e.specSelectors.selectContactUrlField()],((t,e,r)=>{if(r)return ba(r,t,{selectedServer:e})})),VB=()=>t=>t.specSelectors.info().get("summary"),JB=function({fn:e,getSystem:r}){if(typeof e.sampleFromSchema=="function"&&e.jsonSchema202012){const o=Pw({sampleFromSchema:e.jsonSchema202012.sampleFromSchema,sampleFromSchemaGeneric:e.jsonSchema202012.sampleFromSchemaGeneric,createXMLExample:e.jsonSchema202012.createXMLExample,memoizedSampleFromSchema:e.jsonSchema202012.memoizedSampleFromSchema,memoizedCreateXMLExample:e.jsonSchema202012.memoizedCreateXMLExample,getJsonSampleSchema:e.jsonSchema202012.getJsonSampleSchema,getYamlSampleSchema:e.jsonSchema202012.getYamlSampleSchema,getXmlSampleSchema:e.jsonSchema202012.getXmlSampleSchema,getSampleSchema:e.jsonSchema202012.getSampleSchema,mergeJsonSchema:e.jsonSchema202012.mergeJsonSchema,getSchemaObjectTypeLabel:p=>e.jsonSchema202012.getType(Mr(p)),getSchemaObjectType:p=>e.jsonSchema202012.foldType(Mr(p)?.type)},r());Object.assign(this.fn,o)}const n=(o=>(p,c=null)=>{const{fn:d}=o();if(d.isFileUploadIntendedOAS30(p,c))return!0;const E=H.Map.isMap(p);if(!E&&!Ai()(p))return!1;const x=E?p.get("contentMediaType"):p.contentMediaType,k=E?p.get("contentEncoding"):p.contentEncoding;return typeof x=="string"&&x!==""||typeof k=="string"&&k!==""})(r),{isFileUploadIntended:a}=Pw({isFileUploadIntended:n},r());if(this.fn.isFileUploadIntended=a,this.fn.isFileUploadIntendedOAS32=n,e.jsonSchema202012){const{hasSchemaType:o}=Pw({hasSchemaType:e.jsonSchema202012.hasSchemaType},r());this.fn.hasSchemaType=o}},WB=({fn:t})=>{const e=t.createSystemSelector||pO;return{afterLoad:JB,fn:{isOAS32:uO,createSystemSelector:pO,createOnlyOAS32Selector:gB},components:{OAS32VersionPragmaFilter:mB},wrapComponents:{Contact:yB,InfoContainer:vB,License:bB,Model:wB,Models:EB,OpenAPIVersion:SB,VersionPragmaFilter:xB,JSONSchema202012KeywordDescription:OB,JSONSchema202012KeywordExamples:AB,JSONSchema202012KeywordProperties:jB},statePlugins:{spec:{selectors:{isOAS32:e(IB),selectInfoSummaryField:VB,license:MB,selectLicenseNameField:RB,selectLicenseUrlField:DB,selectLicenseIdentifierField:LB,selectLicenseUrl:e(FB),contact:$B,selectContactNameField:BB,selectContactEmailField:qB,selectContactUrlField:UB,selectContactUrl:e(zB)},wrapSelectors:{isOAS3:NB,validOperationMethods:TB}},oas32:{selectors:{validOperationMethods:PB}}}}},HB=va().object,KB=va().bool,eh=(va().oneOfType([HB,KB]),(0,u.createContext)(null));eh.displayName="JSONSchemaContext";const ls=(0,u.createContext)(0);ls.displayName="JSONSchemaLevelContext";const Nw=(0,u.createContext)(new Set),pi=(0,u.createContext)([]);class Ks{static Collapsed="collapsed";static Expanded="expanded";static DeeplyExpanded="deeply-expanded"}const Tw=()=>{const{config:t}=(0,u.useContext)(eh);return t},Jt=t=>{const{components:e}=(0,u.useContext)(eh);return e[t]||null},Rn=(t=void 0)=>{const{fn:e}=(0,u.useContext)(eh);return t!==void 0?e[t]:e},mO=()=>{const[,t]=(0,u.useState)(null),{state:e}=(0,u.useContext)(eh);return{state:e,setState:r=>{r(e),t({})}}},cs=()=>{const t=(0,u.useContext)(ls);return[t,t+1]},Qo=t=>{const e=(0,u.useContext)(pi),{setState:r}=mO(),n=typeof t=="string"?[...e,t]:e;return{path:n,pathMutator:(a,o={deep:!1})=>{const p=n.toString(),c=E=>{E.paths[p]=a,a===Ks.Collapsed&&Object.keys(E.paths).forEach((x=>{x.startsWith(p)&&E.paths[x]===Ks.DeeplyExpanded&&(E.paths[x]=Ks.Expanded)}))},d=E=>{Object.keys(E.paths).forEach((x=>{x.startsWith(p)&&(E.paths[x]=a)}))};o.deep?r(d):r(c)}}},Gs=t=>{const[e]=cs(),{defaultExpandedLevels:r}=Tw(),{path:n,pathMutator:a}=Qo(t),{path:o}=Qo(),{state:p}=mO(),c=p.paths[n.toString()],d=p.paths[o.toString()]??p.paths[o.slice(0,-1).toString()],E=c??(r-e>0?Ks.Expanded:Ks.Collapsed),x=E!==Ks.Collapsed;return(0,u.useEffect)((()=>{a(d===Ks.DeeplyExpanded?Ks.DeeplyExpanded:E)}),[d]),{isExpanded:x,setExpanded:(0,u.useCallback)(((k={deep:!1})=>{a(k.deep?Ks.DeeplyExpanded:Ks.Expanded)}),[]),setCollapsed:(0,u.useCallback)(((k={deep:!1})=>{a(Ks.Collapsed,k)}),[])}},gO=(t=void 0)=>{if(t===void 0)return(0,u.useContext)(Nw);const e=(0,u.useContext)(Nw);return new Set([...e,t])},GB=(0,u.forwardRef)((({schema:t,name:e="",dependentRequired:r=[],onExpand:n=()=>{},identifier:a=""},o)=>{const p=Rn(),c=a||t?.$id||e,{path:d}=Qo(c),{isExpanded:E,setExpanded:x,setCollapsed:k}=Gs(c),[P,R]=cs(),L=(()=>{const[Lr]=cs();return Lr>0})(),V=p.isExpandable(t)||r.length>0,Q=(Lr=>gO().has(Lr))(t),J=gO(t),re=p.stringifyConstraints(t),me=Jt("Accordion"),le=Jt("Keyword$schema"),ve=Jt("Keyword$vocabulary"),be=Jt("Keyword$id"),ce=Jt("Keyword$anchor"),_e=Jt("Keyword$dynamicAnchor"),Ae=Jt("Keyword$ref"),xe=Jt("Keyword$dynamicRef"),Oe=Jt("Keyword$defs"),je=Jt("Keyword$comment"),G=Jt("KeywordAllOf"),de=Jt("KeywordAnyOf"),Ne=Jt("KeywordOneOf"),Ye=Jt("KeywordNot"),ht=Jt("KeywordIf"),Ct=Jt("KeywordThen"),_t=Jt("KeywordElse"),qt=Jt("KeywordDependentSchemas"),It=Jt("KeywordPrefixItems"),Ot=Jt("KeywordItems"),et=Jt("KeywordContains"),Dt=Jt("KeywordProperties"),or=Jt("KeywordPatternProperties"),Ut=Jt("KeywordAdditionalProperties"),pr=Jt("KeywordPropertyNames"),W=Jt("KeywordUnevaluatedItems"),$e=Jt("KeywordUnevaluatedProperties"),Je=Jt("KeywordType"),Ft=Jt("KeywordEnum"),it=Jt("KeywordConst"),bt=Jt("KeywordConstraint"),ut=Jt("KeywordDependentRequired"),Xt=Jt("KeywordContentSchema"),zt=Jt("KeywordTitle"),tr=Jt("KeywordDescription"),Er=Jt("KeywordDefault"),dr=Jt("KeywordDeprecated"),qr=Jt("KeywordReadOnly"),en=Jt("KeywordWriteOnly"),kn=Jt("KeywordExamples"),ln=Jt("ExtensionKeywords"),jt=Jt("ExpandDeepButton"),We=(0,u.useCallback)(((Lr,zr)=>{zr?x():k(),n(Lr,zr,!1)}),[n,x,k]),Kr=(0,u.useCallback)(((Lr,zr)=>{zr?x({deep:!0}):k({deep:!0}),n(Lr,zr,!0)}),[n,x,k]);return u.createElement(pi.Provider,{value:d},u.createElement(ls.Provider,{value:R},u.createElement(Nw.Provider,{value:J},u.createElement("article",{ref:o,"data-json-schema-level":P,className:Xr()("json-schema-2020-12",{"json-schema-2020-12--embedded":L,"json-schema-2020-12--circular":Q})},u.createElement("div",{className:"json-schema-2020-12-head"},V&&!Q?u.createElement(u.Fragment,null,u.createElement(me,{expanded:E,onChange:We},u.createElement(zt,{title:e,schema:t})),u.createElement(jt,{expanded:E,onClick:Kr})):u.createElement(zt,{title:e,schema:t}),u.createElement(dr,{schema:t}),u.createElement(qr,{schema:t}),u.createElement(en,{schema:t}),u.createElement(Je,{schema:t,isCircular:Q}),re.length>0&&re.map((Lr=>u.createElement(bt,{key:`${Lr.scope}-${Lr.value}`,constraint:Lr})))),u.createElement("div",{className:Xr()("json-schema-2020-12-body",{"json-schema-2020-12-body--collapsed":!E})},E&&u.createElement(u.Fragment,null,u.createElement(tr,{schema:t}),!Q&&V&&u.createElement(u.Fragment,null,u.createElement(Dt,{schema:t}),u.createElement(or,{schema:t}),u.createElement(Ut,{schema:t}),u.createElement($e,{schema:t}),u.createElement(pr,{schema:t}),u.createElement(G,{schema:t}),u.createElement(de,{schema:t}),u.createElement(Ne,{schema:t}),u.createElement(Ye,{schema:t}),u.createElement(ht,{schema:t}),u.createElement(Ct,{schema:t}),u.createElement(_t,{schema:t}),u.createElement(qt,{schema:t}),u.createElement(It,{schema:t}),u.createElement(Ot,{schema:t}),u.createElement(W,{schema:t}),u.createElement(et,{schema:t}),u.createElement(Xt,{schema:t})),u.createElement(Ft,{schema:t}),u.createElement(it,{schema:t}),u.createElement(ut,{schema:t,dependentRequired:r}),u.createElement(Er,{schema:t}),u.createElement(kn,{schema:t}),u.createElement(le,{schema:t}),u.createElement(ve,{schema:t}),u.createElement(be,{schema:t}),u.createElement(ce,{schema:t}),u.createElement(_e,{schema:t}),u.createElement(Ae,{schema:t}),!Q&&V&&u.createElement(Oe,{schema:t}),u.createElement(xe,{schema:t}),u.createElement(je,{schema:t}),u.createElement(ln,{schema:t})))))))})),yO=GB,vO=({schema:t})=>t?.$schema?u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$schema"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$schema"),u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$schema)):null,bO=({schema:t})=>{const e="$vocabulary",{path:r}=Qo(e),{isExpanded:n,setExpanded:a,setCollapsed:o}=Gs(e),p=Jt("Accordion"),c=(0,u.useCallback)((()=>{n?o():a()}),[n,a,o]);return t?.$vocabulary?typeof t.$vocabulary!="object"?null:u.createElement(pi.Provider,{value:r},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$vocabulary"},u.createElement(p,{expanded:n,onChange:c},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$vocabulary")),u.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),u.createElement("ul",null,n&&Object.entries(t.$vocabulary).map((([d,E])=>u.createElement("li",{key:d,className:Xr()("json-schema-2020-12-$vocabulary-uri",{"json-schema-2020-12-$vocabulary-uri--disabled":!E})},u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},d))))))):null},wO=({schema:t})=>t?.$id?u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$id"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$id"),u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$id)):null,EO=({schema:t})=>t?.$anchor?u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$anchor"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$anchor"),u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$anchor)):null,SO=({schema:t})=>t?.$dynamicAnchor?u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$dynamicAnchor"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$dynamicAnchor"),u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$dynamicAnchor)):null,xO=({schema:t})=>t?.$ref?u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$ref"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$ref"),u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$ref)):null,_O=({schema:t})=>t?.$dynamicRef?u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$dynamicRef"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$dynamicRef"),u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$dynamicRef)):null,OO=({schema:t})=>{const e=t?.$defs||{},r="$defs",{path:n}=Qo(r),{isExpanded:a,setExpanded:o,setCollapsed:p}=Gs(r),[c,d]=cs(),E=Jt("Accordion"),x=Jt("ExpandDeepButton"),k=Jt("JSONSchema"),P=(0,u.useCallback)((()=>{a?p():o()}),[a,o,p]),R=(0,u.useCallback)(((L,V)=>{V?o({deep:!0}):p({deep:!0})}),[o,p]);return Object.keys(e).length===0?null:u.createElement(pi.Provider,{value:n},u.createElement(ls.Provider,{value:d},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$defs","data-json-schema-level":c},u.createElement(E,{expanded:a,onChange:P},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$defs")),u.createElement(x,{expanded:a,onClick:R}),u.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),u.createElement("ul",{className:Xr()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!a})},a&&u.createElement(u.Fragment,null,Object.entries(e).map((([L,V])=>u.createElement("li",{key:L,className:"json-schema-2020-12-property"},u.createElement(k,{name:L,schema:V})))))))))},kO=({schema:t})=>t?.$comment?u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$comment"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$comment"),u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$comment)):null,AO=({schema:t})=>{const e=t?.allOf||[],r=Rn(),n="allOf",{path:a}=Qo(n),{isExpanded:o,setExpanded:p,setCollapsed:c}=Gs(n),[d,E]=cs(),x=Jt("Accordion"),k=Jt("ExpandDeepButton"),P=Jt("JSONSchema"),R=Jt("KeywordType"),L=(0,u.useCallback)((()=>{o?c():p()}),[o,p,c]),V=(0,u.useCallback)(((Q,J)=>{J?p({deep:!0}):c({deep:!0})}),[p,c]);return Array.isArray(e)&&e.length!==0?u.createElement(pi.Provider,{value:a},u.createElement(ls.Provider,{value:E},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--allOf","data-json-schema-level":d},u.createElement(x,{expanded:o,onChange:L},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"All of")),u.createElement(k,{expanded:o,onClick:V}),u.createElement(R,{schema:{allOf:e}}),u.createElement("ul",{className:Xr()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!o})},o&&u.createElement(u.Fragment,null,e.map(((Q,J)=>u.createElement("li",{key:`#${J}`,className:"json-schema-2020-12-property"},u.createElement(P,{name:`#${J} ${r.getTitle(Q)}`,schema:Q}))))))))):null},CO=({schema:t})=>{const e=t?.anyOf||[],r=Rn(),n="anyOf",{path:a}=Qo(n),{isExpanded:o,setExpanded:p,setCollapsed:c}=Gs(n),[d,E]=cs(),x=Jt("Accordion"),k=Jt("ExpandDeepButton"),P=Jt("JSONSchema"),R=Jt("KeywordType"),L=(0,u.useCallback)((()=>{o?c():p()}),[o,p,c]),V=(0,u.useCallback)(((Q,J)=>{J?p({deep:!0}):c({deep:!0})}),[p,c]);return Array.isArray(e)&&e.length!==0?u.createElement(pi.Provider,{value:a},u.createElement(ls.Provider,{value:E},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--anyOf","data-json-schema-level":d},u.createElement(x,{expanded:o,onChange:L},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Any of")),u.createElement(k,{expanded:o,onClick:V}),u.createElement(R,{schema:{anyOf:e}}),u.createElement("ul",{className:Xr()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!o})},o&&u.createElement(u.Fragment,null,e.map(((Q,J)=>u.createElement("li",{key:`#${J}`,className:"json-schema-2020-12-property"},u.createElement(P,{name:`#${J} ${r.getTitle(Q)}`,schema:Q}))))))))):null},jO=({schema:t})=>{const e=t?.oneOf||[],r=Rn(),n="oneOf",{path:a}=Qo(n),{isExpanded:o,setExpanded:p,setCollapsed:c}=Gs(n),[d,E]=cs(),x=Jt("Accordion"),k=Jt("ExpandDeepButton"),P=Jt("JSONSchema"),R=Jt("KeywordType"),L=(0,u.useCallback)((()=>{o?c():p()}),[o,p,c]),V=(0,u.useCallback)(((Q,J)=>{J?p({deep:!0}):c({deep:!0})}),[p,c]);return Array.isArray(e)&&e.length!==0?u.createElement(pi.Provider,{value:a},u.createElement(ls.Provider,{value:E},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--oneOf","data-json-schema-level":d},u.createElement(x,{expanded:o,onChange:L},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"One of")),u.createElement(k,{expanded:o,onClick:V}),u.createElement(R,{schema:{oneOf:e}}),u.createElement("ul",{className:Xr()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!o})},o&&u.createElement(u.Fragment,null,e.map(((Q,J)=>u.createElement("li",{key:`#${J}`,className:"json-schema-2020-12-property"},u.createElement(P,{name:`#${J} ${r.getTitle(Q)}`,schema:Q}))))))))):null},PO=({schema:t})=>{const e=Rn(),r=Jt("JSONSchema");if(!e.hasKeyword(t,"not"))return null;const n=u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Not");return u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--not"},u.createElement(r,{name:n,schema:t.not,identifier:"not"}))},NO=({schema:t})=>{const e=Rn(),r=Jt("JSONSchema");if(!e.hasKeyword(t,"if"))return null;const n=u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"If");return u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--if"},u.createElement(r,{name:n,schema:t.if,identifier:"if"}))},TO=({schema:t})=>{const e=Rn(),r=Jt("JSONSchema");if(!e.hasKeyword(t,"then"))return null;const n=u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Then");return u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--then"},u.createElement(r,{name:n,schema:t.then,identifier:"then"}))},IO=({schema:t})=>{const e=Rn(),r=Jt("JSONSchema");if(!e.hasKeyword(t,"else"))return null;const n=u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Else");return u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--if"},u.createElement(r,{name:n,schema:t.else,identifier:"else"}))},MO=({schema:t})=>{const e=t?.dependentSchemas||[],r="dependentSchemas",{path:n}=Qo(r),{isExpanded:a,setExpanded:o,setCollapsed:p}=Gs(r),[c,d]=cs(),E=Jt("Accordion"),x=Jt("ExpandDeepButton"),k=Jt("JSONSchema"),P=(0,u.useCallback)((()=>{a?p():o()}),[a,o,p]),R=(0,u.useCallback)(((L,V)=>{V?o({deep:!0}):p({deep:!0})}),[o,p]);return typeof e!="object"||Object.keys(e).length===0?null:u.createElement(pi.Provider,{value:n},u.createElement(ls.Provider,{value:d},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--dependentSchemas","data-json-schema-level":c},u.createElement(E,{expanded:a,onChange:P},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Dependent schemas")),u.createElement(x,{expanded:a,onClick:R}),u.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),u.createElement("ul",{className:Xr()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!a})},a&&u.createElement(u.Fragment,null,Object.entries(e).map((([L,V])=>u.createElement("li",{key:L,className:"json-schema-2020-12-property"},u.createElement(k,{name:L,schema:V})))))))))},RO=({schema:t})=>{const e=t?.prefixItems||[],r=Rn(),n="prefixItems",{path:a}=Qo(n),{isExpanded:o,setExpanded:p,setCollapsed:c}=Gs(n),[d,E]=cs(),x=Jt("Accordion"),k=Jt("ExpandDeepButton"),P=Jt("JSONSchema"),R=Jt("KeywordType"),L=(0,u.useCallback)((()=>{o?c():p()}),[o,p,c]),V=(0,u.useCallback)(((Q,J)=>{J?p({deep:!0}):c({deep:!0})}),[p,c]);return Array.isArray(e)&&e.length!==0?u.createElement(pi.Provider,{value:a},u.createElement(ls.Provider,{value:E},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--prefixItems","data-json-schema-level":d},u.createElement(x,{expanded:o,onChange:L},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Prefix items")),u.createElement(k,{expanded:o,onClick:V}),u.createElement(R,{schema:{prefixItems:e}}),u.createElement("ul",{className:Xr()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!o})},o&&u.createElement(u.Fragment,null,e.map(((Q,J)=>u.createElement("li",{key:`#${J}`,className:"json-schema-2020-12-property"},u.createElement(P,{name:`#${J} ${r.getTitle(Q)}`,schema:Q}))))))))):null},DO=({schema:t})=>{const e=Rn(),r=Jt("JSONSchema");if(!e.hasKeyword(t,"items"))return null;const n=u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Items");return u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--items"},u.createElement(r,{name:n,schema:t.items,identifier:"items"}))},FO=({schema:t})=>{const e=Rn(),r=Jt("JSONSchema");if(!e.hasKeyword(t,"contains"))return null;const n=u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Contains");return u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--contains"},u.createElement(r,{name:n,schema:t.contains,identifier:"contains"}))},LO=({schema:t})=>{const e=Rn(),r=t?.properties||{},n=Array.isArray(t?.required)?t.required:[],a=Jt("JSONSchema"),{path:o}=Qo("properties");return Object.keys(r).length===0?null:u.createElement(pi.Provider,{value:o},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--properties"},u.createElement("ul",null,Object.entries(r).map((([p,c])=>{const d=n.includes(p),E=e.getDependentRequired(p,t);return u.createElement("li",{key:p,className:Xr()("json-schema-2020-12-property",{"json-schema-2020-12-property--required":d})},u.createElement(a,{name:p,schema:c,dependentRequired:E}))})))))},$O=({schema:t})=>{const e=t?.patternProperties||{},r=Jt("JSONSchema"),{path:n}=Qo("patternProperties");return Object.keys(e).length===0?null:u.createElement(pi.Provider,{value:n},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--patternProperties"},u.createElement("ul",null,Object.entries(e).map((([a,o])=>u.createElement("li",{key:a,className:"json-schema-2020-12-property"},u.createElement(r,{name:a,schema:o})))))))},BO=({schema:t})=>{const e=Rn(),r=Jt("JSONSchema");if(!e.hasKeyword(t,"additionalProperties"))return null;const n=u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Additional properties");return u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--additionalProperties"},t.additionalProperties===!0?u.createElement(u.Fragment,null,n,u.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"allowed")):t.additionalProperties===!1?u.createElement(u.Fragment,null,n,u.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"forbidden")):u.createElement(r,{name:n,schema:t.additionalProperties,identifier:"additionalProperties"}))},qO=({schema:t})=>{const e=Rn(),r=Jt("JSONSchema"),n=u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Property names");return e.hasKeyword(t,"propertyNames")?u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--propertyNames"},u.createElement(r,{name:n,schema:t.propertyNames,identifier:"propertyNames"})):null},UO=({schema:t})=>{const e=Rn(),r=Jt("JSONSchema");if(!e.hasKeyword(t,"unevaluatedItems"))return null;const n=u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Unevaluated items");return u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--unevaluatedItems"},u.createElement(r,{name:n,schema:t.unevaluatedItems,identifier:"unevaluatedItems"}))},zO=({schema:t})=>{const e=Rn(),r=Jt("JSONSchema");if(!e.hasKeyword(t,"unevaluatedProperties"))return null;const n=u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Unevaluated properties");return u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--unevaluatedProperties"},u.createElement(r,{name:n,schema:t.unevaluatedProperties,identifier:"unevaluatedProperties"}))},VO=({schema:t,isCircular:e=!1})=>{const r=Rn().getType(t),n=e?" [circular]":"";return u.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},`${r}${n}`)},JO=({schema:t})=>{const e=Jt("JSONViewer");return Array.isArray(t?.enum)?u.createElement(e,{name:"Enum",value:t.enum,className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--enum"}):null},WO=({schema:t})=>{const e=Rn(),r=Jt("JSONViewer");return e.hasKeyword(t,"const")?u.createElement(r,{name:"Const",value:t.const,className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--const"}):null},HO=t=>typeof t=="string"?`${t.charAt(0).toUpperCase()}${t.slice(1)}`:t,KO=t=>(e,{lookup:r="extended"}={})=>{const n=t();if(e?.title!=null)return n.upperFirst(String(e.title));if(r==="extended"){if(e?.$anchor!=null)return n.upperFirst(String(e.$anchor));if(e?.$id!=null)return String(e.$id)}return""},GO=t=>{const e=(r,n=new WeakSet)=>{const a=t();if(r==null)return"any";if(a.isBooleanJSONSchema(r))return r?"any":"never";if(typeof r!="object"||n.has(r))return"any";n.add(r);const{type:o,prefixItems:p,items:c}=r,d=()=>{if(Array.isArray(p)){const k=p.map((R=>e(R,n))),P=c?e(c,n):"any";return`array<[${k.join(", ")}], ${P}>`}return c?`array<${e(c,n)}>`:"array"};if(r.not&&e(r.not)==="any")return"never";const E=(k,P)=>Array.isArray(r[k])?`(${r[k].map((R=>e(R,n))).join(P)})`:null,x=[Array.isArray(o)?o.map((k=>k==="array"?d():k)).join(" | "):o==="array"?d():["null","boolean","object","array","number","integer","string"].includes(o)?o:(()=>{if(Object.hasOwn(r,"prefixItems")||Object.hasOwn(r,"items")||Object.hasOwn(r,"contains"))return d();if(Object.hasOwn(r,"properties")||Object.hasOwn(r,"additionalProperties")||Object.hasOwn(r,"patternProperties"))return"object";if(["int32","int64"].includes(r.format))return"integer";if(["float","double"].includes(r.format))return"number";if(Object.hasOwn(r,"minimum")||Object.hasOwn(r,"maximum")||Object.hasOwn(r,"exclusiveMinimum")||Object.hasOwn(r,"exclusiveMaximum")||Object.hasOwn(r,"multipleOf"))return"number | integer";if(Object.hasOwn(r,"pattern")||Object.hasOwn(r,"format")||Object.hasOwn(r,"minLength")||Object.hasOwn(r,"maxLength")||Object.hasOwn(r,"contentEncoding")||Object.hasOwn(r,"contentMediaType"))return"string";if(r.const!==void 0){if(r.const===null)return"null";if(typeof r.const=="boolean")return"boolean";if(typeof r.const=="number")return Number.isInteger(r.const)?"integer":"number";if(typeof r.const=="string")return"string";if(Array.isArray(r.const))return"array";if(typeof r.const=="object")return"object"}return null})(),E("oneOf"," | "),E("anyOf"," | "),E("allOf"," & ")].filter(Boolean).join(" | ");return n.delete(r),x||"any"};return e},YO=t=>typeof t=="boolean",XO=(t,e)=>t!==null&&typeof t=="object"&&Object.hasOwn(t,e),QO=t=>e=>{const r=t();return e?.$schema||e?.$vocabulary||e?.$id||e?.$anchor||e?.$dynamicAnchor||e?.$ref||e?.$dynamicRef||e?.$defs||e?.$comment||e?.allOf||e?.anyOf||e?.oneOf||r.hasKeyword(e,"not")||r.hasKeyword(e,"if")||r.hasKeyword(e,"then")||r.hasKeyword(e,"else")||e?.dependentSchemas||e?.prefixItems||r.hasKeyword(e,"items")||r.hasKeyword(e,"contains")||e?.properties||e?.patternProperties||r.hasKeyword(e,"additionalProperties")||r.hasKeyword(e,"propertyNames")||r.hasKeyword(e,"unevaluatedItems")||r.hasKeyword(e,"unevaluatedProperties")||e?.description||e?.enum||r.hasKeyword(e,"const")||r.hasKeyword(e,"contentSchema")||r.hasKeyword(e,"default")||e?.examples||r.getExtensionKeywords(e).length>0},ZO=t=>t===null||["number","bigint","boolean"].includes(typeof t)?String(t):Array.isArray(t)?`[${t.map(ZO).join(", ")}]`:JSON.stringify(t),Lv=(t,e,r)=>{const n=typeof e=="number",a=typeof r=="number";return n&&a?e===r?`${e} ${t}`:`[${e}, ${r}] ${t}`:n?`≥ ${e} ${t}`:a?`≤ ${r} ${t}`:null},YB=t=>{const e=[],r=(d=>{if(typeof d?.multipleOf!="number"||d.multipleOf<=0||d.multipleOf===1)return null;const{multipleOf:E}=d;if(Number.isInteger(E))return`multiple of ${E}`;const x=10**E.toString().split(".")[1].length;return`multiple of ${E*x}/${x}`})(t);r!==null&&e.push({scope:"number",value:r});const n=(d=>{const E=d?.minimum,x=d?.maximum,k=d?.exclusiveMinimum,P=d?.exclusiveMaximum,R=typeof E=="number",L=typeof x=="number",V=typeof k=="number",Q=typeof P=="number",J=V&&(!R||EP);return(R||V)&&(L||Q)?`${J?"(":"["}${J?k:E}, ${re?P:x}${re?")":"]"}`:R||V?`${J?">":"≥"} ${J?k:E}`:L||Q?`${re?"<":"≤"} ${re?P:x}`:null})(t);n!==null&&e.push({scope:"number",value:n}),t?.format&&e.push({scope:"string",value:t.format});const a=Lv("characters",t?.minLength,t?.maxLength);a!==null&&e.push({scope:"string",value:a}),t?.pattern&&e.push({scope:"string",value:`matches ${t?.pattern}`}),t?.contentMediaType&&e.push({scope:"string",value:`media type: ${t.contentMediaType}`}),t?.contentEncoding&&e.push({scope:"string",value:`encoding: ${t.contentEncoding}`});const o=Lv(t?.uniqueItems?"unique items":"items",t?.minItems,t?.maxItems);o!==null&&e.push({scope:"array",value:o}),t?.uniqueItems&&!o&&e.push({scope:"array",value:"unique"});const p=Lv("contained items",t?.minContains,t?.maxContains);p!==null&&e.push({scope:"array",value:p});const c=Lv("properties",t?.minProperties,t?.maxProperties);return c!==null&&e.push({scope:"object",value:c}),e},XB=(t,e)=>e?.dependentRequired?Array.from(Object.entries(e.dependentRequired).reduce(((r,[n,a])=>(Array.isArray(a)&&a.includes(t)&&r.add(n),r)),new Set)):[],$v=t=>typeof t=="object"&&t!==null&&!Array.isArray(t)&&(Object.getPrototypeOf(t)===null||Object.getPrototypeOf(t)===Object.prototype),ek=()=>["$schema","$vocabulary","$id","$anchor","$dynamicAnchor","$dynamicRef","$ref","$defs","$comment","allOf","anyOf","oneOf","not","if","then","else","dependentSchemas","prefixItems","items","contains","properties","patternProperties","additionalProperties","propertyNames","unevaluatedItems","unevaluatedProperties","type","enum","const","multipleOf","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","maxItems","minItems","uniqueItems","maxContains","minContains","maxProperties","minProperties","required","dependentRequired","title","description","default","deprecated","readOnly","writeOnly","examples","format","contentEncoding","contentMediaType","contentSchema"],tk=t=>e=>{const r=t().getSchemaKeywords();return $v(e)?((n,a)=>{const o=new Set(a);return n.filter((p=>!o.has(p)))})(Object.keys(e),r):[]},QB=(t,e)=>{const r=H.Map.isMap(t);if(!r&&!$v(t))return!1;const n=o=>e===o||Array.isArray(e)&&e.includes(o),a=r?t.get("type"):t.type;return H.List.isList(a)||Array.isArray(a)?a.some((o=>n(o))):n(a)},ZB=({constraint:t})=>$v(t)&&typeof t.scope=="string"&&typeof t.value=="string"?u.createElement("span",{className:`json-schema-2020-12__constraint json-schema-2020-12__constraint--${t.scope}`},t.value):null,rk=u.memo(ZB),nk=({dependentRequired:t})=>Array.isArray(t)&&t.length!==0?u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--dependentRequired"},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Required when defined"),u.createElement("ul",null,t.map((e=>u.createElement("li",{key:e},u.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--warning"},e)))))):null,ak=({schema:t})=>{const e=Rn(),r=Jt("JSONSchema");if(!e.hasKeyword(t,"contentSchema"))return null;const n=u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Content schema");return u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--contentSchema"},u.createElement(r,{name:n,schema:t.contentSchema,identifier:"contentSchema"}))},ok=({title:t="",schema:e})=>{const r=Rn(),n=t||r.getTitle(e);return n?u.createElement("strong",{className:"json-schema-2020-12__title"},n):null},ik=({schema:t})=>t?.description?u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--description"},u.createElement("div",{className:"json-schema-2020-12-core-keyword__value json-schema-2020-12-core-keyword__value--secondary"},t.description)):null,sk=({schema:t})=>{const e=Rn(),r=Jt("JSONViewer");return e.hasKeyword(t,"default")?u.createElement(r,{name:"Default",value:t.default,className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--default"}):null},lk=({schema:t})=>t?.deprecated!==!0?null:u.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--warning"},"deprecated"),ck=({schema:t})=>t?.readOnly!==!0?null:u.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},"read-only"),uk=({schema:t})=>t?.writeOnly!==!0?null:u.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},"write-only"),pk=({schema:t})=>{const e=t?.examples||[],r=Jt("JSONViewer");return Array.isArray(e)&&e.length!==0?u.createElement(r,{name:"Examples",value:t.examples,className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--examples"}):null},hk=({schema:t})=>{const e=Rn(),r="ExtensionKeywords",{path:n}=Qo(r),{isExpanded:a,setExpanded:o,setCollapsed:p}=Gs(r),[c,d]=cs(),E=Jt("Accordion"),x=Jt("ExpandDeepButton"),k=Jt("JSONViewer"),{showExtensionKeywords:P}=Tw(),R=e.getExtensionKeywords(t),L=(0,u.useCallback)((()=>{a?p():o()}),[a,o,p]),V=(0,u.useCallback)(((Q,J)=>{J?o({deep:!0}):p({deep:!0})}),[o,p]);return P&&R.length!==0?u.createElement(pi.Provider,{value:n},u.createElement(ls.Provider,{value:d},u.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--extension-keywords","data-json-schema-level":c},u.createElement(E,{expanded:a,onChange:L},u.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--extension"},"Extension Keywords")),u.createElement(x,{expanded:a,onClick:V}),u.createElement("ul",{className:Xr()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!a})},a&&u.createElement(u.Fragment,null,R.map((Q=>u.createElement(k,{key:Q,name:Q,value:t[Q],className:"json-schema-2020-12-json-viewer-extension-keyword"})))))))):null},Iw=({name:t,value:e,className:r})=>{const n=Rn(),{path:a}=Qo(t),{isExpanded:o,setExpanded:p,setCollapsed:c}=Gs(t),[d,E]=cs(),x=Jt("Accordion"),k=Jt("ExpandDeepButton"),P=typeof e=="string"||typeof e=="number"||typeof e=="bigint"||typeof e=="boolean"||typeof e=="symbol"||e==null,R=(Q=>$v(Q)&&Object.keys(Q).length===0)(e)||(Q=>Array.isArray(Q)&&Q.length===0)(e),L=(0,u.useCallback)((()=>{o?c():p()}),[o,p,c]),V=(0,u.useCallback)(((Q,J)=>{J?p({deep:!0}):c({deep:!0})}),[p,c]);return P?u.createElement("div",{className:Xr()("json-schema-2020-12-json-viewer",r)},u.createElement("span",{className:"json-schema-2020-12-json-viewer__name json-schema-2020-12-json-viewer__name--secondary"},t),u.createElement("span",{className:"json-schema-2020-12-json-viewer__value json-schema-2020-12-json-viewer__value--secondary"},n.stringify(e))):R?u.createElement("div",{className:Xr()("json-schema-2020-12-json-viewer",r)},u.createElement("span",{className:"json-schema-2020-12-json-viewer__name json-schema-2020-12-json-viewer__name--secondary"},t),u.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},Array.isArray(e)?"empty array":"empty object")):u.createElement(pi.Provider,{value:a},u.createElement(ls.Provider,{value:E},u.createElement("div",{className:Xr()("json-schema-2020-12-json-viewer",r),"data-json-schema-level":d},u.createElement(x,{expanded:o,onChange:L},u.createElement("span",{className:"json-schema-2020-12-json-viewer__name json-schema-2020-12-json-viewer__name--secondary"},t)),u.createElement(k,{expanded:o,onClick:V}),u.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},Array.isArray(e)?"array":"object"),u.createElement("ul",{className:Xr()("json-schema-2020-12-json-viewer__children",{"json-schema-2020-12-json-viewer__children--collapsed":!o})},o&&u.createElement(u.Fragment,null,Array.isArray(e)?e.map(((Q,J)=>u.createElement("li",{key:`#${J}`,className:"json-schema-2020-12-property"},u.createElement(Iw,{name:`#${J}`,value:Q,className:r})))):Object.entries(e).map((([Q,J])=>u.createElement("li",{key:Q,className:"json-schema-2020-12-property"},u.createElement(Iw,{name:Q,value:J,className:r})))))))))},dk=Iw,fk=({expanded:t=!1,children:e,onChange:r})=>{const n=Jt("ChevronRightIcon"),a=(0,u.useCallback)((o=>{r(o,!t)}),[t,r]);return u.createElement("button",{type:"button",className:"json-schema-2020-12-accordion",onClick:a},u.createElement("div",{className:"json-schema-2020-12-accordion__children"},e),u.createElement("span",{className:Xr()("json-schema-2020-12-accordion__icon",{"json-schema-2020-12-accordion__icon--expanded":t,"json-schema-2020-12-accordion__icon--collapsed":!t})},u.createElement(n,null)))},mk=({expanded:t,onClick:e})=>{const r=(0,u.useCallback)((n=>{e(n,!t)}),[t,e]);return u.createElement("button",{type:"button",className:"json-schema-2020-12-expand-deep-button",onClick:r},t?"Collapse all":"Expand all")},gk=()=>u.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24"},u.createElement("path",{fill:"currentColor",d:"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"})),yk=(t,e={})=>{const r={components:{JSONSchema:yO,Keyword$schema:vO,Keyword$vocabulary:bO,Keyword$id:wO,Keyword$anchor:EO,Keyword$dynamicAnchor:SO,Keyword$ref:xO,Keyword$dynamicRef:_O,Keyword$defs:OO,Keyword$comment:kO,KeywordAllOf:AO,KeywordAnyOf:CO,KeywordOneOf:jO,KeywordNot:PO,KeywordIf:NO,KeywordThen:TO,KeywordElse:IO,KeywordDependentSchemas:MO,KeywordPrefixItems:RO,KeywordItems:DO,KeywordContains:FO,KeywordProperties:LO,KeywordPatternProperties:$O,KeywordAdditionalProperties:BO,KeywordPropertyNames:qO,KeywordUnevaluatedItems:UO,KeywordUnevaluatedProperties:zO,KeywordType:VO,KeywordEnum:JO,KeywordConst:WO,KeywordConstraint:rk,KeywordDependentRequired:nk,KeywordContentSchema:ak,KeywordTitle:ok,KeywordDescription:ik,KeywordDefault:sk,KeywordDeprecated:lk,KeywordReadOnly:ck,KeywordWriteOnly:uk,KeywordExamples:pk,ExtensionKeywords:hk,JSONViewer:dk,Accordion:fk,ExpandDeepButton:mk,ChevronRightIcon:gk,...e.components},config:{default$schema:"https://json-schema.org/draft/2020-12/schema",defaultExpandedLevels:0,showExtensionKeywords:!0,...e.config},fn:{upperFirst:HO,getTitle:KO(Rn),getType:GO(Rn),isBooleanJSONSchema:YO,hasKeyword:XO,isExpandable:QO(Rn),stringify:ZO,stringifyConstraints:YB,getDependentRequired:XB,getSchemaKeywords:ek,getExtensionKeywords:tk(Rn),...e.fn},state:{paths:{}}},n=a=>u.createElement(eh.Provider,{value:r},u.createElement(t,a));return n.contexts={JSONSchemaContext:eh},n.displayName=t.displayName,n},eq=({getSystem:t})=>(e,r={})=>{const{getComponent:n,getConfigs:a}=t(),o=a(),p=n("JSONSchema202012"),c=n("JSONSchema202012Keyword$schema"),d=n("JSONSchema202012Keyword$vocabulary"),E=n("JSONSchema202012Keyword$id"),x=n("JSONSchema202012Keyword$anchor"),k=n("JSONSchema202012Keyword$dynamicAnchor"),P=n("JSONSchema202012Keyword$ref"),R=n("JSONSchema202012Keyword$dynamicRef"),L=n("JSONSchema202012Keyword$defs"),V=n("JSONSchema202012Keyword$comment"),Q=n("JSONSchema202012KeywordAllOf"),J=n("JSONSchema202012KeywordAnyOf"),re=n("JSONSchema202012KeywordOneOf"),me=n("JSONSchema202012KeywordNot"),le=n("JSONSchema202012KeywordIf"),ve=n("JSONSchema202012KeywordThen"),be=n("JSONSchema202012KeywordElse"),ce=n("JSONSchema202012KeywordDependentSchemas"),_e=n("JSONSchema202012KeywordPrefixItems"),Ae=n("JSONSchema202012KeywordItems"),xe=n("JSONSchema202012KeywordContains"),Oe=n("JSONSchema202012KeywordProperties"),je=n("JSONSchema202012KeywordPatternProperties"),G=n("JSONSchema202012KeywordAdditionalProperties"),de=n("JSONSchema202012KeywordPropertyNames"),Ne=n("JSONSchema202012KeywordUnevaluatedItems"),Ye=n("JSONSchema202012KeywordUnevaluatedProperties"),ht=n("JSONSchema202012KeywordType"),Ct=n("JSONSchema202012KeywordEnum"),_t=n("JSONSchema202012KeywordConst"),qt=n("JSONSchema202012KeywordConstraint"),It=n("JSONSchema202012KeywordDependentRequired"),Ot=n("JSONSchema202012KeywordContentSchema"),et=n("JSONSchema202012KeywordTitle"),Dt=n("JSONSchema202012KeywordDescription"),or=n("JSONSchema202012KeywordDefault"),Ut=n("JSONSchema202012KeywordDeprecated"),pr=n("JSONSchema202012KeywordReadOnly"),W=n("JSONSchema202012KeywordWriteOnly"),$e=n("JSONSchema202012KeywordExamples"),Je=n("JSONSchema202012ExtensionKeywords"),Ft=n("JSONSchema202012JSONViewer"),it=n("JSONSchema202012Accordion"),bt=n("JSONSchema202012ExpandDeepButton"),ut=n("JSONSchema202012ChevronRightIcon");return yk(e,{components:{JSONSchema:p,Keyword$schema:c,Keyword$vocabulary:d,Keyword$id:E,Keyword$anchor:x,Keyword$dynamicAnchor:k,Keyword$ref:P,Keyword$dynamicRef:R,Keyword$defs:L,Keyword$comment:V,KeywordAllOf:Q,KeywordAnyOf:J,KeywordOneOf:re,KeywordNot:me,KeywordIf:le,KeywordThen:ve,KeywordElse:be,KeywordDependentSchemas:ce,KeywordPrefixItems:_e,KeywordItems:Ae,KeywordContains:xe,KeywordProperties:Oe,KeywordPatternProperties:je,KeywordAdditionalProperties:G,KeywordPropertyNames:de,KeywordUnevaluatedItems:Ne,KeywordUnevaluatedProperties:Ye,KeywordType:ht,KeywordEnum:Ct,KeywordConst:_t,KeywordConstraint:qt,KeywordDependentRequired:It,KeywordContentSchema:Ot,KeywordTitle:et,KeywordDescription:Dt,KeywordDefault:or,KeywordDeprecated:Ut,KeywordReadOnly:pr,KeywordWriteOnly:W,KeywordExamples:$e,ExtensionKeywords:Je,JSONViewer:Ft,Accordion:it,ExpandDeepButton:bt,ChevronRightIcon:ut,...r.components},config:{showExtensionKeywords:o.showExtensions,...r.config},fn:{...r.fn}})},vk=({getSystem:t,fn:e})=>{const r=()=>({upperFirst:e.upperFirst,...e.jsonSchema202012});return{components:{JSONSchema202012:yO,JSONSchema202012Keyword$schema:vO,JSONSchema202012Keyword$vocabulary:bO,JSONSchema202012Keyword$id:wO,JSONSchema202012Keyword$anchor:EO,JSONSchema202012Keyword$dynamicAnchor:SO,JSONSchema202012Keyword$ref:xO,JSONSchema202012Keyword$dynamicRef:_O,JSONSchema202012Keyword$defs:OO,JSONSchema202012Keyword$comment:kO,JSONSchema202012KeywordAllOf:AO,JSONSchema202012KeywordAnyOf:CO,JSONSchema202012KeywordOneOf:jO,JSONSchema202012KeywordNot:PO,JSONSchema202012KeywordIf:NO,JSONSchema202012KeywordThen:TO,JSONSchema202012KeywordElse:IO,JSONSchema202012KeywordDependentSchemas:MO,JSONSchema202012KeywordPrefixItems:RO,JSONSchema202012KeywordItems:DO,JSONSchema202012KeywordContains:FO,JSONSchema202012KeywordProperties:LO,JSONSchema202012KeywordPatternProperties:$O,JSONSchema202012KeywordAdditionalProperties:BO,JSONSchema202012KeywordPropertyNames:qO,JSONSchema202012KeywordUnevaluatedItems:UO,JSONSchema202012KeywordUnevaluatedProperties:zO,JSONSchema202012KeywordType:VO,JSONSchema202012KeywordEnum:JO,JSONSchema202012KeywordConst:WO,JSONSchema202012KeywordConstraint:rk,JSONSchema202012KeywordDependentRequired:nk,JSONSchema202012KeywordContentSchema:ak,JSONSchema202012KeywordTitle:ok,JSONSchema202012KeywordDescription:ik,JSONSchema202012KeywordDefault:sk,JSONSchema202012KeywordDeprecated:lk,JSONSchema202012KeywordReadOnly:ck,JSONSchema202012KeywordWriteOnly:uk,JSONSchema202012KeywordExamples:pk,JSONSchema202012ExtensionKeywords:hk,JSONSchema202012JSONViewer:dk,JSONSchema202012Accordion:fk,JSONSchema202012ExpandDeepButton:mk,JSONSchema202012ChevronRightIcon:gk,withJSONSchema202012Context:yk,withJSONSchema202012SystemContext:eq(t()),JSONSchema202012PathContext:()=>pi,JSONSchema202012LevelContext:()=>ls},fn:{upperFirst:HO,jsonSchema202012:{getTitle:KO(r),getType:GO(r),isExpandable:QO(r),isBooleanJSONSchema:YO,hasKeyword:XO,useFn:Rn,useConfig:Tw,useComponent:Jt,useIsExpanded:Gs,usePath:Qo,useLevel:cs,getSchemaKeywords:ek,getExtensionKeywords:tk(r),hasSchemaType:QB}}}},tq=(t,{sample:e=[]}={})=>((r,n={})=>{const{minItems:a,maxItems:o,uniqueItems:p}=n,{contains:c,minContains:d,maxContains:E}=n;let x=[...r];if(c!=null&&typeof c=="object"&&Number.isInteger(d)&&d>1){const k=x.at(0);for(let P=1;P0&&(x=r.slice(0,o)),Number.isInteger(a)&&a>0)for(let k=0;x.length{throw new Error("Not implemented")},Bv=t=>Fn()(t),qv=t=>t.at(0),th=t=>typeof t=="boolean",Ys=t=>Ai()(t),Fc=t=>th(t)||Ys(t),Uv=class{data={};register(e,r){this.data[e]=r}unregister(e){e===void 0?this.data={}:delete this.data[e]}get(e){return this.data[e]}},bk=()=>0,wk=()=>0,nq=()=>.1,aq=()=>.1,oq=()=>"user@example.com",iq=()=>"실례@example.com",sq=()=>"example.com",lq=()=>"실례.com",cq=()=>"198.51.100.42",uq=()=>"2001:0db8:5b96:0000:0000:426f:8e17:642a",pq=()=>"https://example.com/",hq=()=>"path/index.html",dq=()=>"https://실례.com/",fq=()=>"path/실례.html",mq=()=>"3fa85f64-5717-4562-b3fc-2c963f66afa6",gq=()=>"https://example.com/dictionary/{term:1}/{term}",yq=()=>"/a/b/c",vq=()=>"1/0",bq=()=>new Date().toISOString(),wq=()=>new Date().toISOString().substring(0,10),Eq=()=>new Date().toISOString().substring(11),Sq=()=>"P3D",xq=()=>"********",_q=()=>"^[a-z]+$",zv=new class extends Uv{#e={int32:bk,int64:wk,float:nq,double:aq,email:oq,"idn-email":iq,hostname:sq,"idn-hostname":lq,ipv4:cq,ipv6:uq,uri:pq,"uri-reference":hq,iri:dq,"iri-reference":fq,uuid:mq,"uri-template":gq,"json-pointer":yq,"relative-json-pointer":vq,"date-time":bq,date:wq,time:Eq,duration:Sq,password:xq,regex:_q};data={...this.#e};get defaults(){return{...this.#e}}},Ek=(t,e)=>typeof e=="function"?zv.register(t,e):e===null?zv.unregister(t):zv.get(t);Ek.getDefaults=()=>zv.defaults;const Vv=Ek;var Oq=Be(48287).Buffer;const kq=t=>Oq.from(t).toString("ascii");var Aq=Be(48287).Buffer;const Cq=t=>Aq.from(t).toString("utf8");var jq=Be(48287).Buffer;const Pq=t=>jq.from(t).toString("binary"),Nq=t=>{let e="";for(let r=0;r=33&&n<=60||n>=62&&n<=126||n===9||n===32)e+=t.charAt(r);else if(n===13||n===10)e+=`\r +`;else if(n>126){const a=unescape(encodeURIComponent(t.charAt(r)));for(let o=0;oTq.from(t).toString("hex");var Mq=Be(48287).Buffer;const Rq=t=>{const e=Mq.from(t).toString("utf8"),r="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";let n=0,a="",o=0,p=0;for(let c=0;c=5;)a+=r.charAt(o>>>p-5&31),p-=5;p>0&&(a+=r.charAt(o<<5-p&31),n=(8-8*e.length%5)%5);for(let c=0;cDq.from(t).toString("base64");var Lq=Be(48287).Buffer;const $q=t=>Lq.from(t).toString("base64url"),Jv=new class extends Uv{#e={"7bit":kq,"8bit":Cq,binary:Pq,"quoted-printable":Nq,base16:Iq,base32:Rq,base64:Fq,base64url:$q};data={...this.#e};get defaults(){return{...this.#e}}},Sk=(t,e)=>typeof e=="function"?Jv.register(t,e):e===null?Jv.unregister(t):Jv.get(t);Sk.getDefaults=()=>Jv.defaults;const xk=Sk,Bq={"text/plain":()=>"string","text/css":()=>".selector { border: 1px solid red }","text/csv":()=>"value1,value2,value3","text/html":()=>"

    content

    ","text/calendar":()=>"BEGIN:VCALENDAR","text/javascript":()=>"console.dir('Hello world!');","text/xml":()=>'John Doe',"text/*":()=>"string"},qq={"image/*":()=>Bv(25).toString("binary")},Uq={"audio/*":()=>Bv(25).toString("binary")},zq={"video/*":()=>Bv(25).toString("binary")},Vq={"application/json":()=>'{"key":"value"}',"application/ld+json":()=>'{"name": "John Doe"}',"application/x-httpd-php":()=>"Hello World!

    '; ?>","application/rtf":()=>String.raw`{\rtf1\adeflang1025\ansi\ansicpg1252\uc1`,"application/x-sh":()=>'echo "Hello World!"',"application/xhtml+xml":()=>"

    content

    ","application/*":()=>Bv(25).toString("binary")},wd=new class extends Uv{#e={...Bq,...qq,...Uq,...zq,...Vq};data={...this.#e};get defaults(){return{...this.#e}}},_k=(t,e)=>{if(typeof e=="function")return wd.register(t,e);if(e===null)return wd.unregister(t);const r=t.split(";").at(0),n=`${r.split("/").at(0)}/*`;return wd.get(t)||wd.get(r)||wd.get(n)};_k.getDefaults=()=>wd.defaults;const Ok=_k,Mw=(t,e={})=>{const{maxLength:r,minLength:n}=e;let a=t;if(Number.isInteger(r)&&r>0&&(a=a.slice(0,r)),Number.isInteger(n)&&n>0){let o=0;for(;a.length{const{contentEncoding:r,contentMediaType:n,contentSchema:a}=t,{pattern:o,format:p}=t,c=xk(r)||Cx();let d;return d=typeof o=="string"?Mw((E=>{try{const x=new RegExp("(?<=(?{const{format:x}=E,k=Vv(x);return typeof k=="function"?k(E):"string"})(t):Fc(a)&&typeof n=="string"&&e!==void 0?Array.isArray(e)||typeof e=="object"?JSON.stringify(e):Mw(String(e),t):typeof n=="string"?(E=>{const{contentMediaType:x}=E,k=Ok(x);return typeof k=="function"?k(E):"string"})(t):Mw("string",t),c(d)},kk=(t,e={})=>{const{minimum:r,maximum:n,exclusiveMinimum:a,exclusiveMaximum:o}=e,{multipleOf:p}=e,c=Number.isInteger(t)?1:Number.EPSILON;let d=typeof r=="number"?r:null,E=typeof n=="number"?n:null,x=t;if(typeof a=="number"&&(d=d!==null?Math.max(d,a+c):a+c),typeof o=="number"&&(E=E!==null?Math.min(E,o-c):o-c),x=d>E&&t||d||E||x,typeof p=="number"&&p>0){const k=x%p;x=k===0?x:x+p-k}return x},Wq=t=>{const{format:e}=t;let r;return r=typeof e=="string"?(n=>{const{format:a}=n,o=Vv(a);return typeof o=="function"?o(n):0})(t):0,kk(r,t)},Hq=t=>{const{format:e}=t;let r;return r=typeof e=="string"?(n=>{const{format:a}=n,o=Vv(a);if(typeof o=="function")return o(n);switch(a){case"int32":return bk();case"int64":return wk()}return 0})(t):0,kk(r,t)},Kq=t=>typeof t.default!="boolean"||t.default,Wv=new Proxy({array:tq,object:rq,string:Jq,number:Wq,integer:Hq,boolean:Kq,null:()=>null},{get:(t,e)=>typeof e=="string"&&Object.hasOwn(t,e)?t[e]:()=>`Unknown Type: ${e}`}),Ak=["array","object","number","integer","string","boolean","null"],zm=t=>{if(!Ys(t))return!1;const{examples:e,example:r,default:n}=t;return!!(Array.isArray(e)&&e.length>=1)||n!==void 0||r!==void 0},Rw=t=>{if(!Ys(t))return null;const{examples:e,example:r,default:n}=t;return Array.isArray(e)&&e.length>=1?e.at(0):n!==void 0?n:r!==void 0?r:void 0},Hv={array:["items","prefixItems","contains","maxContains","minContains","maxItems","minItems","uniqueItems","unevaluatedItems"],object:["properties","additionalProperties","patternProperties","propertyNames","minProperties","maxProperties","required","dependentSchemas","dependentRequired","unevaluatedProperties"],string:["pattern","format","minLength","maxLength","contentEncoding","contentMediaType","contentSchema"],integer:["minimum","maximum","exclusiveMinimum","exclusiveMaximum","multipleOf"]};Hv.number=Hv.integer;const Dw="string",Ck=t=>t===void 0?null:t===null?"null":Array.isArray(t)?"array":Number.isInteger(t)?"integer":typeof t,Kv=t=>{if(Array.isArray(t)&&t.length>=1){if(t.includes("array"))return"array";if(t.includes("object"))return"object";{const e=t.filter((n=>n!=="null")),r=qv(e.length>0?e:t);if(Ak.includes(r))return r}}return Ak.includes(t)?t:null},Fw=(t,e=new WeakSet)=>{if(!Ys(t)||e.has(t))return Dw;e.add(t);let{type:r,const:n}=t;if(r=Kv(r),typeof r!="string"){const a=Object.keys(Hv);e:for(let o=0;o{if(Array.isArray(t[E])){const x=t[E].map((k=>Fw(k,e)));return Kv(x)}return null},o=a("allOf"),p=a("anyOf"),c=a("oneOf"),d=t.not?Fw(t.not,e):null;(o||p||c||d)&&(r=Kv([o,p,c,d].filter(Boolean)))}if(typeof r!="string"&&zm(t)){const a=Rw(t),o=Ck(a);r=typeof o=="string"?o:r}return e.delete(t),r||Dw},jk=t=>Fw(t),Lw=t=>th(t)?(e=>e===!1?{not:{}}:{})(t):Ys(t)?t:{},Vm=(t,e,r={})=>{if(th(t)&&t===!0)return!0;if(th(t)&&t===!1)return!1;if(th(e)&&e===!0)return!0;if(th(e)&&e===!1)return!1;if(!Fc(t))return e;if(!Fc(e))return t;const n={...e,...t};if(e.type&&t.type&&Array.isArray(e.type)&&typeof e.type=="string"){const a=fn(e.type).concat(t.type);n.type=Array.from(new Set(a))}if(Array.isArray(e.required)&&Array.isArray(t.required)&&(n.required=[...new Set([...t.required,...e.required])]),e.properties&&t.properties){const a=new Set([...Object.keys(e.properties),...Object.keys(t.properties)]);n.properties={};for(const o of a){const p=e.properties[o]||{},c=t.properties[o]||{};p.readOnly&&!r.includeReadOnly||p.writeOnly&&!r.includeWriteOnly?n.required=(n.required||[]).filter((d=>d!==o)):n.properties[o]=Vm(c,p,r)}}return Fc(e.items)&&Fc(t.items)&&(n.items=Vm(t.items,e.items,r)),Fc(e.contains)&&Fc(t.contains)&&(n.contains=Vm(t.contains,e.contains,r)),Fc(e.contentSchema)&&Fc(t.contentSchema)&&(n.contentSchema=Vm(t.contentSchema,e.contentSchema,r)),n},Ed=Vm,io=(t,e={},r=void 0,n=!1)=>{if(t==null&&r===void 0)return;typeof t?.toJS=="function"&&(t=t.toJS()),t=Lw(t);let a=r!==void 0||zm(t);const o=!a&&Array.isArray(t.oneOf)&&t.oneOf.length>0,p=!a&&Array.isArray(t.anyOf)&&t.anyOf.length>0;if(!a&&(o||p)){const Oe=Lw(qv(o?t.oneOf:t.anyOf));!(t=Ed(t,Oe,e)).xml&&Oe.xml&&(t.xml=Oe.xml),zm(t)&&zm(Oe)&&(a=!0)}const c={};let{xml:d,properties:E,additionalProperties:x,items:k,contains:P}=t||{},R=jk(t),{includeReadOnly:L,includeWriteOnly:V}=e;d=d||{};let Q,{name:J,prefix:re,namespace:me}=d,le={};Object.hasOwn(t,"type")||(t.type=R),n&&(J=J||"notagname",Q=(re?`${re}:`:"")+J,me)&&(c[re?`xmlns:${re}`:"xmlns"]=me),n&&(le[Q]=[]);const ve=Nn(E);let be,ce=0;const _e=()=>Number.isInteger(t.maxProperties)&&t.maxProperties>0&&ce>=t.maxProperties,Ae=Oe=>!(Number.isInteger(t.maxProperties)&&t.maxProperties>0)||!_e()&&(!(je=>!Array.isArray(t.required)||t.required.length===0||!t.required.includes(je))(Oe)||t.maxProperties-ce-(()=>{if(!Array.isArray(t.required)||t.required.length===0)return 0;let je=0;return n?t.required.forEach((G=>je+=le[G]===void 0?0:1)):t.required.forEach((G=>{je+=le[Q]?.find((de=>de[G]!==void 0))===void 0?0:1})),t.required.length-je})()>0);if(be=n?(Oe,je=void 0)=>{if(t&&ve[Oe]){if(ve[Oe].xml=ve[Oe].xml||{},ve[Oe].xml.attribute){const de=Array.isArray(ve[Oe].enum)?qv(ve[Oe].enum):void 0;if(zm(ve[Oe]))c[ve[Oe].xml.name||Oe]=Rw(ve[Oe]);else if(de!==void 0)c[ve[Oe].xml.name||Oe]=de;else{const Ne=Lw(ve[Oe]),Ye=jk(Ne),ht=ve[Oe].xml.name||Oe;if(Ye==="array"){const Ct=io(ve[Oe],e,je,!1);c[ht]=Ct.map((_t=>Ai()(_t)?"UnknownTypeObject":Array.isArray(_t)?"UnknownTypeArray":_t)).join(" ")}else c[ht]=Ye==="object"?"UnknownTypeObject":Wv[Ye](Ne)}return}ve[Oe].xml.name=ve[Oe].xml.name||Oe}else ve[Oe]||x===!1||(ve[Oe]={xml:{name:Oe}});let G=io(ve[Oe],e,je,n);Ae(Oe)&&(ce++,Array.isArray(G)?le[Q]=le[Q].concat(G):le[Q].push(G))}:(Oe,je)=>{if(Ae(Oe)){if(Ai()(t.discriminator?.mapping)&&t.discriminator.propertyName===Oe&&typeof t.$$ref=="string"){for(const G in t.discriminator.mapping)if(t.$$ref.search(t.discriminator.mapping[G])!==-1){le[Oe]=G;break}}else le[Oe]=io(ve[Oe],e,je,n);ce++}},a){let Oe;if(Oe=r!==void 0?r:Rw(t),!n){if(typeof Oe=="number"&&R==="string")return`${Oe}`;if(typeof Oe!="string"||R==="string")return Oe;try{return JSON.parse(Oe)}catch{return Oe}}if(R==="array"){if(!Array.isArray(Oe)){if(typeof Oe=="string")return Oe;Oe=[Oe]}let je=[];return Ys(k)&&(k.xml=k.xml||d||{},k.xml.name=k.xml.name||d.name,je=Oe.map((G=>io(k,e,G,n)))),Ys(P)&&(P.xml=P.xml||d||{},P.xml.name=P.xml.name||d.name,je=[io(P,e,void 0,n),...je]),je=Wv.array(t,{sample:je}),d.wrapped?(le[Q]=je,Ci()(c)||le[Q].push({_attr:c})):le=je,le}if(R==="object"){if(typeof Oe=="string")return Oe;for(const je in Oe)Object.hasOwn(Oe,je)&&(ve[je]?.readOnly&&!L||ve[je]?.writeOnly&&!V||(ve[je]?.xml?.attribute?c[ve[je].xml.name||je]=Oe[je]:be(je,Oe[je])));return Ci()(c)||le[Q].push({_attr:c}),le}return le[Q]=Ci()(c)?Oe:[{_attr:c},Oe],le}if(R==="array"){let Oe=[];if(Ys(P))if(n&&(P.xml=P.xml||t.xml||{},P.xml.name=P.xml.name||d.name),Array.isArray(P.anyOf)){const{anyOf:je,...G}=k;Oe.push(...P.anyOf.map((de=>io(Ed(de,G,e),e,void 0,n))))}else if(Array.isArray(P.oneOf)){const{oneOf:je,...G}=k;Oe.push(...P.oneOf.map((de=>io(Ed(de,G,e),e,void 0,n))))}else{if(!(!n||n&&d.wrapped))return io(P,e,void 0,n);Oe.push(io(P,e,void 0,n))}if(Ys(k))if(n&&(k.xml=k.xml||t.xml||{},k.xml.name=k.xml.name||d.name),Array.isArray(k.anyOf)){const{anyOf:je,...G}=k;Oe.push(...k.anyOf.map((de=>io(Ed(de,G,e),e,void 0,n))))}else if(Array.isArray(k.oneOf)){const{oneOf:je,...G}=k;Oe.push(...k.oneOf.map((de=>io(Ed(de,G,e),e,void 0,n))))}else{if(!(!n||n&&d.wrapped))return io(k,e,void 0,n);Oe.push(io(k,e,void 0,n))}return Oe=Wv.array(t,{sample:Oe}),n&&d.wrapped?(le[Q]=Oe,Ci()(c)||le[Q].push({_attr:c}),le):Oe}if(R==="object"){for(let Oe in ve)Object.hasOwn(ve,Oe)&&(ve[Oe]?.deprecated||ve[Oe]?.readOnly&&!L||ve[Oe]?.writeOnly&&!V||be(Oe));if(n&&c&&le[Q].push({_attr:c}),_e())return le;if(th(x)&&x)n?le[Q].push({additionalProp:"Anything can be here"}):le.additionalProp1={},ce++;else if(Ys(x)){const Oe=x,je=io(Oe,e,void 0,n);if(n&&typeof Oe?.xml?.name=="string"&&Oe?.xml?.name!=="notagname")le[Q].push(je);else{const G=Oe?.["x-additionalPropertiesName"]||"additionalProp",de=Number.isInteger(t.minProperties)&&t.minProperties>0&&ce{const n=io(t,e,r,!0);if(n)return typeof n=="string"?n:xp()(n,{declaration:!0,indent:" "})},Nk=(t,e,r)=>io(t,e,r,!1),Tk=(t,e,r)=>[t,JSON.stringify(e),JSON.stringify(r)],Gq=gu(Pk,Tk),Yq=gu(Nk,Tk),Ik=new class extends Uv{#e={};data={...this.#e};get defaults(){return{...this.#e}}},Xq=(t,e)=>(e!==void 0&&Ik.register(t,e),Ik.get(t)),Qq=[{when:/json/,shouldStringifyTypes:["string"]}],Zq=["object"],eU=t=>(e,r,n,a)=>{const{fn:o}=t(),p=o.jsonSchema202012.memoizedSampleFromSchema(e,r,a),c=typeof p,d=Qq.reduce(((E,x)=>x.when.test(n)?[...E,...x.shouldStringifyTypes]:E),Zq);return Wr()(d,(E=>E===c))?JSON.stringify(p,null,2):p},tU=t=>(e,r,n,a)=>{const{fn:o}=t(),p=o.jsonSchema202012.getJsonSampleSchema(e,r,n,a);let c;try{c=Pe.dump(Pe.load(p),{lineWidth:-1},{schema:Rt}),c[c.length-1]===` +`&&(c=c.slice(0,c.length-1))}catch(d){return console.error(d),"error: could not generate yaml example"}return c.replace(/\t/g," ")},rU=t=>(e,r,n)=>{const{fn:a}=t();if(e&&!e.xml&&(e.xml={}),e&&!e.xml.name){if(!e.$$ref&&(e.type||e.items||e.properties||e.additionalProperties))return` +`;if(e.$$ref){let o=e.$$ref.match(/\S*\/(\S+)$/);e.xml.name=o[1]}}return a.jsonSchema202012.memoizedCreateXMLExample(e,r,n)},nU=t=>(e,r="",n={},a=void 0)=>{const{fn:o}=t();return typeof e?.toJS=="function"&&(e=e.toJS()),typeof a?.toJS=="function"&&(a=a.toJS()),/xml/.test(r)?o.jsonSchema202012.getXmlSampleSchema(e,n,a):/(yaml|yml)/.test(r)?o.jsonSchema202012.getYamlSampleSchema(e,n,r,a):o.jsonSchema202012.getJsonSampleSchema(e,n,r,a)},Mk=({getSystem:t})=>{const e=eU(t),r=tU(t),n=rU(t),a=nU(t);return{fn:{jsonSchema202012:{sampleFromSchema:Nk,sampleFromSchemaGeneric:io,sampleOptionAPI:Xq,sampleEncoderAPI:xk,sampleFormatAPI:Vv,sampleMediaTypeAPI:Ok,createXMLExample:Pk,memoizedSampleFromSchema:Yq,memoizedCreateXMLExample:Gq,getJsonSampleSchema:e,getYamlSampleSchema:r,getXmlSampleSchema:n,getSampleSchema:a,mergeJsonSchema:Ed,foldType:Kv}}}};function Rk(){return[nO,Aw,vk,Mk,fB,WB]}const aU=t=>()=>({fn:t.fn,components:t.components}),oU=t=>{const e=ct()({layout:{layout:t.layout,filter:t.filter},spec:{spec:"",url:t.url},requestSnippets:t.requestSnippets},t.initialState);if(t.initialState)for(const[r,n]of Object.entries(t.initialState))n===void 0&&delete e[r];return{system:{configs:t.configs},plugins:t.presets,state:e}},iU=()=>t=>{const e=t.queryConfigEnabled?(()=>{const r=new URLSearchParams(Qe.location.search);return Object.fromEntries(r)})():{};return Object.entries(e).reduce(((r,[n,a])=>(n==="config"?r.configUrl=a:n==="urls.primaryName"?r[n]=a:r=pn()(r,n,a),r)),{})},sU=({url:t,system:e})=>async r=>{if(!t)return{};if(typeof e.configsActions?.getConfigByUrl!="function")return{};const n=(()=>{const a={};return a.promise=new Promise(((o,p)=>{a.resolve=o,a.reject=p})),a})();return e.configsActions.getConfigByUrl({url:t,loadRemoteConfig:!0,requestInterceptor:r.requestInterceptor,responseInterceptor:r.responseInterceptor},(a=>{n.resolve(a)})),n.promise},lU=()=>()=>{const t={};return globalThis.location&&(t.oauth2RedirectUrl=`${globalThis.location.protocol}//${globalThis.location.host}${globalThis.location.pathname.substring(0,globalThis.location.pathname.lastIndexOf("/"))}/oauth2-redirect.html`),t},Xn=Object.freeze({dom_id:null,domNode:null,spec:{},url:"",urls:null,configUrl:null,layout:"BaseLayout",docExpansion:"list",maxDisplayedTags:-1,filter:!1,validatorUrl:"https://validator.swagger.io/validator",oauth2RedirectUrl:void 0,persistAuthorization:!1,configs:{},displayOperationId:!1,displayRequestDuration:!1,deepLinking:!1,tryItOutEnabled:!1,requestInterceptor:t=>(t.curlOptions=[],t),responseInterceptor:t=>t,showMutatedRequest:!0,defaultModelRendering:"example",defaultModelExpandDepth:1,defaultModelsExpandDepth:1,showExtensions:!1,showCommonExtensions:!1,withCredentials:!1,requestSnippetsEnabled:!1,requestSnippets:{generators:{curl_bash:{title:"cURL (bash)",syntax:"bash"},curl_powershell:{title:"cURL (PowerShell)",syntax:"powershell"},curl_cmd:{title:"cURL (CMD)",syntax:"bash"}},defaultExpanded:!0,languages:null},supportedSubmitMethods:["get","put","post","delete","options","head","patch","trace"],queryConfigEnabled:!1,presets:[Rk],plugins:[],initialState:{},fn:{},components:{},syntaxHighlight:{activated:!0,theme:"agate"},operationsSorter:null,tagsSorter:null,onComplete:null,modelPropertyMacro:null,parameterMacro:null,fileUploadMediaTypes:["application/octet-stream","image/","audio/","video/"],uncaughtExceptionHandler:null});var cU=Be(61448),uU=Be.n(cU),pU=Be(77731),hU=Be.n(pU);const Gv=(t,e=[])=>Array.isArray(t)?t:e,us=(t,e=!1)=>t===!0||t==="true"||t===1||t==="1"||t!==!1&&t!=="false"&&t!==0&&t!=="0"&&e,dU=t=>t===null||t==="null"?null:t,fU=t=>{const e=String(t);return us(t,e)},Dk=(t,e)=>typeof t=="function"?t:e,mU=t=>Array.isArray(t)?t:null,Yv=t=>typeof t=="function"?t:null,$w=t=>t===null||t==="null"?null:String(t),Bw=(t,e=-1)=>{const r=parseInt(t,10);return Number.isNaN(r)?e:r},Sd=(t,e={})=>Ai()(t)?t:e,Fk=t=>typeof t=="function"||typeof t=="string"?t:null,xd=t=>String(t),gU=(t,e)=>Ai()(t)?t:t===!1||t==="false"||t===0||t==="0"?{activated:!1}:e,yU=t=>t===void 0||t==="undefined"?void 0:String(t),Lk={components:{typeCaster:Sd},configs:{typeCaster:Sd},configUrl:{typeCaster:$w},deepLinking:{typeCaster:us,defaultValue:Xn.deepLinking},defaultModelExpandDepth:{typeCaster:Bw,defaultValue:Xn.defaultModelExpandDepth},defaultModelRendering:{typeCaster:xd},defaultModelsExpandDepth:{typeCaster:Bw,defaultValue:Xn.defaultModelsExpandDepth},displayOperationId:{typeCaster:us,defaultValue:Xn.displayOperationId},displayRequestDuration:{typeCaster:us,defaultValue:Xn.displayRequestDuration},docExpansion:{typeCaster:xd},dom_id:{typeCaster:$w},domNode:{typeCaster:dU},fileUploadMediaTypes:{typeCaster:Gv,defaultValue:Xn.fileUploadMediaTypes},filter:{typeCaster:fU},fn:{typeCaster:Sd},initialState:{typeCaster:Sd},layout:{typeCaster:xd},maxDisplayedTags:{typeCaster:Bw,defaultValue:Xn.maxDisplayedTags},modelPropertyMacro:{typeCaster:Yv},oauth2RedirectUrl:{typeCaster:yU},onComplete:{typeCaster:Yv},operationsSorter:{typeCaster:Fk},paramaterMacro:{typeCaster:Yv},persistAuthorization:{typeCaster:us,defaultValue:Xn.persistAuthorization},plugins:{typeCaster:Gv,defaultValue:Xn.plugins},presets:{typeCaster:Gv,defaultValue:Xn.presets},requestInterceptor:{typeCaster:Dk,defaultValue:Xn.requestInterceptor},requestSnippets:{typeCaster:Sd,defaultValue:Xn.requestSnippets},requestSnippetsEnabled:{typeCaster:us,defaultValue:Xn.requestSnippetsEnabled},responseInterceptor:{typeCaster:Dk,defaultValue:Xn.responseInterceptor},showCommonExtensions:{typeCaster:us,defaultValue:Xn.showCommonExtensions},showExtensions:{typeCaster:us,defaultValue:Xn.showExtensions},showMutatedRequest:{typeCaster:us,defaultValue:Xn.showMutatedRequest},spec:{typeCaster:Sd,defaultValue:Xn.spec},supportedSubmitMethods:{typeCaster:Gv,defaultValue:Xn.supportedSubmitMethods},syntaxHighlight:{typeCaster:gU,defaultValue:Xn.syntaxHighlight},"syntaxHighlight.activated":{typeCaster:us,defaultValue:Xn.syntaxHighlight.activated},"syntaxHighlight.theme":{typeCaster:xd},tagsSorter:{typeCaster:Fk},tryItOutEnabled:{typeCaster:us,defaultValue:Xn.tryItOutEnabled},url:{typeCaster:xd},urls:{typeCaster:mU},"urls.primaryName":{typeCaster:xd},validatorUrl:{typeCaster:$w},withCredentials:{typeCaster:us,defaultValue:Xn.withCredentials},uncaughtExceptionHandler:{typeCaster:Yv}},$k=t=>Object.entries(Lk).reduce(((e,[r,{typeCaster:n,defaultValue:a}])=>{if(uU()(e,r)){const o=n(Cn()(e,r),a);e=hU()(r,o,e)}return e}),{...t}),vU=(t,...e)=>{let r=Symbol.for("domNode"),n=Symbol.for("primaryName");const a=[];for(const p of e){const c={...p};Object.hasOwn(c,"domNode")&&(r=c.domNode,delete c.domNode),Object.hasOwn(c,"urls.primaryName")?(n=c["urls.primaryName"],delete c["urls.primaryName"]):Array.isArray(c.urls)&&Object.hasOwn(c.urls,"primaryName")&&(n=c.urls.primaryName,delete c.urls.primaryName),a.push(c)}const o=ct()(t,...a);return r!==Symbol.for("domNode")&&(o.domNode=r),n!==Symbol.for("primaryName")&&Array.isArray(o.urls)&&(o.urls.primaryName=n),$k(o)};function Vu(t){const e=iU()(t),r=lU()(),n=Vu.config.merge({},Vu.config.defaults,r,t,e),a=oU(n),o=aU(n),p=new Xl(a);p.register([n.plugins,o]);const c=p.getSystem(),d=k=>{p.setConfigs(k),c.configsActions.loaded()},E=k=>{!e.url&&typeof k.spec=="object"&&Object.keys(k.spec).length>0?(c.specActions.updateUrl(""),c.specActions.updateLoadingStatus("success"),c.specActions.updateSpec(JSON.stringify(k.spec))):typeof c.specActions.download=="function"&&k.url&&!k.urls&&(c.specActions.updateUrl(k.url),c.specActions.download(k.url))},x=k=>{if(k.domNode)c.render(k.domNode,"App");else if(k.dom_id){const P=document.querySelector(k.dom_id);c.render(P,"App")}else k.dom_id===null||k.domNode===null||console.error("Skipped rendering: no `dom_id` or `domNode` was specified")};return n.configUrl?((async()=>{const{configUrl:k}=n,P=await sU({url:k,system:c})(n),R=Vu.config.merge({},n,P,e);d(R),P!==null&&E(R),x(R)})(),c):(d(n),E(n),x(n),c)}Vu.System=Xl,Vu.config={defaults:Xn,merge:vU,typeCast:$k,typeCastMappings:Lk},Vu.presets={base:nO,apis:Rk},Vu.plugins={Accessibility:Zl,Auth:Ss,Configs:Nd,DeepLining:up,Err:sh,Filter:Id,Icons:pp,JSONSchema5:Yd,JSONSchema5Samples:Ah,JSONSchema202012:vk,JSONSchema202012Samples:Mk,Layout:Fd,Logs:uh,OpenAPI30:Aw,OpenAPI31:Aw,OnComplete:Ld,RequestSnippets:Ol,Spec:ey,SwaggerClient:fx,Util:mx,View:Nx,ViewLegacy:Tx,DownloadUrl:Ix,SyntaxHighlighting:Hx,Versions:Kx,SafeRender:Xx};const bU=Vu})(),Qw.exports=Cd.default})()),Qw.exports}var qU=BU();const UU=$U(qU),zU={class:"openapi-head"},VU={style:{margin:"0","font-size":"19px"}},JU={class:"hint",style:{margin:"0"}},WU={key:0,class:"card empty",style:{"margin-top":"14px"}},HU={style:{"font-weight":"600",color:"var(--c-text)"}},KU={class:"hint",style:{"max-width":"460px",margin:"0 auto"}},Zw="swagger-ui-container",GU=jU({__name:"OpenApiView",setup(eE){const{t:hs}=PU(),Be=rA("loading"),Cd=rA(!1),w=nA(()=>Cd.value?hs("openapi.sourceEmbedded"):Xw()||"/docs/openapi.yaml");NU(()=>{try{let s;oA?(Cd.value=!0,s=URL.createObjectURL(new Blob([oA],{type:"application/yaml"}))):s=Xw()||"/docs/openapi.yaml",UU({dom_id:`#${Zw}`,url:s,deepLinking:!0,docExpansion:"list",tryItOutEnabled:!0,onComplete:()=>Be.value="ok",onFailure:()=>Be.value="error"}),setTimeout(()=>{if(Be.value==="loading"){const h=document.querySelector(`#${Zw} .opblock`);Be.value=h?"ok":"error"}},6e3)}catch{Be.value="error"}});const N=nA(()=>Be.value==="ok"?hs("openapi.statusOk"):Be.value==="error"?hs("openapi.statusError"):hs("openapi.statusLoading"));return(s,h)=>(aA(),TU(LU,null,{default:IU(()=>[Lc("div",zU,[Lc("h2",VU,Km(Gm(hs)("openapi.title")),1),Lc("span",{class:MU(["badge",Be.value==="ok"?"badge-success":Be.value==="error"?"badge-danger":"badge-muted"])},Km(N.value),3),Lc("span",JU,Km(Gm(hs)("openapi.source",{source:w.value})),1)]),Be.value==="error"?(aA(),RU("div",WU,[h[0]||(h[0]=Lc("div",{class:"empty-icon"},"🧾",-1)),Lc("p",HU,Km(Gm(hs)("openapi.notAvailable")),1),Lc("p",KU,Km(Gm(hs)("openapi.notAvailableHint",{url:Gm(Xw)()||"/docs/openapi.yaml"})),1)])):DU("",!0),Lc("div",{class:"card openapi-wrap",style:{padding:"0",overflow:"hidden"}},[Lc("div",{id:Zw})])]),_:1}))}}),oH=FU(GU,[["__scopeId","data-v-0a44808c"]]);export{oH as default}; diff --git a/server/web/dist/assets/OpenApiView-BCH8BgeP.css b/server/web/dist/assets/OpenApiView-BCH8BgeP.css new file mode 100644 index 0000000..a51674b --- /dev/null +++ b/server/web/dist/assets/OpenApiView-BCH8BgeP.css @@ -0,0 +1 @@ +.swagger-ui{color:#3b4151;container-name:swagger-ui;container-type:inline-size;font-family:sans-serif}.swagger-ui html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.swagger-ui body{margin:0}.swagger-ui article,.swagger-ui aside,.swagger-ui footer,.swagger-ui header,.swagger-ui nav,.swagger-ui section{display:block}.swagger-ui h1{font-size:2em;margin:.67em 0}.swagger-ui figcaption,.swagger-ui figure,.swagger-ui main{display:block}.swagger-ui figure{margin:1em 40px}.swagger-ui hr{box-sizing:content-box;height:0;overflow:visible}.swagger-ui pre{font-family:monospace,monospace;font-size:1em}.swagger-ui a{background-color:transparent;-webkit-text-decoration-skip:objects}.swagger-ui abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.swagger-ui b,.swagger-ui strong{font-weight:inherit;font-weight:bolder}.swagger-ui code,.swagger-ui kbd,.swagger-ui samp{font-family:monospace,monospace;font-size:1em}.swagger-ui dfn{font-style:italic}.swagger-ui mark{background-color:#ff0;color:#000}.swagger-ui small{font-size:80%}.swagger-ui sub,.swagger-ui sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.swagger-ui sub{bottom:-.25em}.swagger-ui sup{top:-.5em}.swagger-ui audio,.swagger-ui video{display:inline-block}.swagger-ui audio:not([controls]){display:none;height:0}.swagger-ui img{border-style:none}.swagger-ui svg:not(:root){overflow:hidden}.swagger-ui button,.swagger-ui input,.swagger-ui optgroup,.swagger-ui select,.swagger-ui textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}.swagger-ui button,.swagger-ui input{overflow:visible}.swagger-ui button,.swagger-ui select{text-transform:none}.swagger-ui [type=reset],.swagger-ui [type=submit],.swagger-ui button,.swagger-ui html [type=button]{-webkit-appearance:button}.swagger-ui [type=button]::-moz-focus-inner,.swagger-ui [type=reset]::-moz-focus-inner,.swagger-ui [type=submit]::-moz-focus-inner,.swagger-ui button::-moz-focus-inner{border-style:none;padding:0}.swagger-ui [type=button]:-moz-focusring,.swagger-ui [type=reset]:-moz-focusring,.swagger-ui [type=submit]:-moz-focusring,.swagger-ui button:-moz-focusring{outline:1px dotted ButtonText}.swagger-ui fieldset{padding:.35em .75em .625em}.swagger-ui legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}.swagger-ui progress{display:inline-block;vertical-align:baseline}.swagger-ui textarea{overflow:auto}.swagger-ui [type=checkbox],.swagger-ui [type=radio]{box-sizing:border-box;padding:0}.swagger-ui [type=number]::-webkit-inner-spin-button,.swagger-ui [type=number]::-webkit-outer-spin-button{height:auto}.swagger-ui [type=search]{-webkit-appearance:textfield;outline-offset:-2px}.swagger-ui [type=search]::-webkit-search-cancel-button,.swagger-ui [type=search]::-webkit-search-decoration{-webkit-appearance:none}.swagger-ui ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.swagger-ui details,.swagger-ui menu{display:block}.swagger-ui summary{display:list-item}.swagger-ui canvas{display:inline-block}.swagger-ui [hidden],.swagger-ui template{display:none}.swagger-ui .debug *{outline:1px solid gold}.swagger-ui .debug-white *{outline:1px solid #fff}.swagger-ui .debug-black *{outline:1px solid #000}.swagger-ui .debug-grid{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTRDOTY4N0U2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTRDOTY4N0Q2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3NjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3NzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsBS+GMAAAAjSURBVHjaYvz//z8DLsD4gcGXiYEAGBIKGBne//fFpwAgwAB98AaF2pjlUQAAAABJRU5ErkJggg==) repeat 0 0}.swagger-ui .debug-grid-16{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODYyRjhERDU2N0YyMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODYyRjhERDQ2N0YyMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3QTY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3QjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvCS01IAAABMSURBVHjaYmR4/5+BFPBfAMFm/MBgx8RAGWCn1AAmSg34Q6kBDKMGMDCwICeMIemF/5QawEipAWwUhwEjMDvbAWlWkvVBwu8vQIABAEwBCph8U6c0AAAAAElFTkSuQmCC) repeat 0 0}.swagger-ui .debug-grid-8-solid{background:#fff url(data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAAAAD/4QMxaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzExMSA3OS4xNTgzMjUsIDIwMTUvMDkvMTAtMDE6MTA6MjAgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE1IChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkIxMjI0OTczNjdCMzExRTZCMkJDRTI0MDgxMDAyMTcxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkIxMjI0OTc0NjdCMzExRTZCMkJDRTI0MDgxMDAyMTcxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QjEyMjQ5NzE2N0IzMTFFNkIyQkNFMjQwODEwMDIxNzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QjEyMjQ5NzI2N0IzMTFFNkIyQkNFMjQwODEwMDIxNzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAbGhopHSlBJiZBQi8vL0JHPz4+P0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHAR0pKTQmND8oKD9HPzU/R0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0f/wAARCAAIAAgDASIAAhEBAxEB/8QAWQABAQAAAAAAAAAAAAAAAAAAAAYBAQEAAAAAAAAAAAAAAAAAAAIEEAEBAAMBAAAAAAAAAAAAAAABADECA0ERAAEDBQAAAAAAAAAAAAAAAAARITFBUWESIv/aAAwDAQACEQMRAD8AoOnTV1QTD7JJshP3vSM3P//Z) repeat 0 0}.swagger-ui .debug-grid-16-solid{background:#fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzY3MkJEN0U2N0M1MTFFNkIyQkNFMjQwODEwMDIxNzEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzY3MkJEN0Y2N0M1MTFFNkIyQkNFMjQwODEwMDIxNzEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3QzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3RDY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pve6J3kAAAAzSURBVHjaYvz//z8D0UDsMwMjSRoYP5Gq4SPNbRjVMEQ1fCRDg+in/6+J1AJUxsgAEGAA31BAJMS0GYEAAAAASUVORK5CYII=) repeat 0 0}.swagger-ui .border-box,.swagger-ui a,.swagger-ui article,.swagger-ui body,.swagger-ui code,.swagger-ui dd,.swagger-ui div,.swagger-ui dl,.swagger-ui dt,.swagger-ui fieldset,.swagger-ui footer,.swagger-ui form,.swagger-ui h1,.swagger-ui h2,.swagger-ui h3,.swagger-ui h4,.swagger-ui h5,.swagger-ui h6,.swagger-ui header,.swagger-ui html,.swagger-ui input[type=email],.swagger-ui input[type=number],.swagger-ui input[type=password],.swagger-ui input[type=tel],.swagger-ui input[type=text],.swagger-ui input[type=url],.swagger-ui legend,.swagger-ui li,.swagger-ui main,.swagger-ui ol,.swagger-ui p,.swagger-ui pre,.swagger-ui section,.swagger-ui table,.swagger-ui td,.swagger-ui textarea,.swagger-ui th,.swagger-ui tr,.swagger-ui ul{box-sizing:border-box}.swagger-ui .aspect-ratio{height:0;position:relative}.swagger-ui .aspect-ratio--16x9{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1{padding-bottom:100%}.swagger-ui .aspect-ratio--object{height:100%;inset:0;position:absolute;width:100%;z-index:100}@media screen and (min-width:30em){.swagger-ui .aspect-ratio-ns{height:0;position:relative}.swagger-ui .aspect-ratio--16x9-ns{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16-ns{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3-ns{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4-ns{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4-ns{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6-ns{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5-ns{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8-ns{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5-ns{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7-ns{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1-ns{padding-bottom:100%}.swagger-ui .aspect-ratio--object-ns{height:100%;inset:0;position:absolute;width:100%;z-index:100}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .aspect-ratio-m{height:0;position:relative}.swagger-ui .aspect-ratio--16x9-m{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16-m{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3-m{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4-m{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4-m{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6-m{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5-m{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8-m{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5-m{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7-m{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1-m{padding-bottom:100%}.swagger-ui .aspect-ratio--object-m{height:100%;inset:0;position:absolute;width:100%;z-index:100}}@media screen and (min-width:60em){.swagger-ui .aspect-ratio-l{height:0;position:relative}.swagger-ui .aspect-ratio--16x9-l{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16-l{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3-l{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4-l{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4-l{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6-l{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5-l{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8-l{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5-l{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7-l{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1-l{padding-bottom:100%}.swagger-ui .aspect-ratio--object-l{height:100%;inset:0;position:absolute;width:100%;z-index:100}}.swagger-ui img{max-width:100%}.swagger-ui .cover{background-size:cover!important}.swagger-ui .contain{background-size:contain!important}@media screen and (min-width:30em){.swagger-ui .cover-ns{background-size:cover!important}.swagger-ui .contain-ns{background-size:contain!important}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .cover-m{background-size:cover!important}.swagger-ui .contain-m{background-size:contain!important}}@media screen and (min-width:60em){.swagger-ui .cover-l{background-size:cover!important}.swagger-ui .contain-l{background-size:contain!important}}.swagger-ui .bg-center{background-position:50%;background-repeat:no-repeat}.swagger-ui .bg-top{background-position:top;background-repeat:no-repeat}.swagger-ui .bg-right{background-position:100%;background-repeat:no-repeat}.swagger-ui .bg-bottom{background-position:bottom;background-repeat:no-repeat}.swagger-ui .bg-left{background-position:0;background-repeat:no-repeat}@media screen and (min-width:30em){.swagger-ui .bg-center-ns{background-position:50%;background-repeat:no-repeat}.swagger-ui .bg-top-ns{background-position:top;background-repeat:no-repeat}.swagger-ui .bg-right-ns{background-position:100%;background-repeat:no-repeat}.swagger-ui .bg-bottom-ns{background-position:bottom;background-repeat:no-repeat}.swagger-ui .bg-left-ns{background-position:0;background-repeat:no-repeat}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .bg-center-m{background-position:50%;background-repeat:no-repeat}.swagger-ui .bg-top-m{background-position:top;background-repeat:no-repeat}.swagger-ui .bg-right-m{background-position:100%;background-repeat:no-repeat}.swagger-ui .bg-bottom-m{background-position:bottom;background-repeat:no-repeat}.swagger-ui .bg-left-m{background-position:0;background-repeat:no-repeat}}@media screen and (min-width:60em){.swagger-ui .bg-center-l{background-position:50%;background-repeat:no-repeat}.swagger-ui .bg-top-l{background-position:top;background-repeat:no-repeat}.swagger-ui .bg-right-l{background-position:100%;background-repeat:no-repeat}.swagger-ui .bg-bottom-l{background-position:bottom;background-repeat:no-repeat}.swagger-ui .bg-left-l{background-position:0;background-repeat:no-repeat}}.swagger-ui .outline{outline:1px solid}.swagger-ui .outline-transparent{outline:1px solid transparent}.swagger-ui .outline-0{outline:0}@media screen and (min-width:30em){.swagger-ui .outline-ns{outline:1px solid}.swagger-ui .outline-transparent-ns{outline:1px solid transparent}.swagger-ui .outline-0-ns{outline:0}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .outline-m{outline:1px solid}.swagger-ui .outline-transparent-m{outline:1px solid transparent}.swagger-ui .outline-0-m{outline:0}}@media screen and (min-width:60em){.swagger-ui .outline-l{outline:1px solid}.swagger-ui .outline-transparent-l{outline:1px solid transparent}.swagger-ui .outline-0-l{outline:0}}.swagger-ui .ba{border-style:solid;border-width:1px}.swagger-ui .bt{border-top-style:solid;border-top-width:1px}.swagger-ui .br{border-right-style:solid;border-right-width:1px}.swagger-ui .bb{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl{border-left-style:solid;border-left-width:1px}.swagger-ui .bn{border-style:none;border-width:0}@media screen and (min-width:30em){.swagger-ui .ba-ns{border-style:solid;border-width:1px}.swagger-ui .bt-ns{border-top-style:solid;border-top-width:1px}.swagger-ui .br-ns{border-right-style:solid;border-right-width:1px}.swagger-ui .bb-ns{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl-ns{border-left-style:solid;border-left-width:1px}.swagger-ui .bn-ns{border-style:none;border-width:0}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .ba-m{border-style:solid;border-width:1px}.swagger-ui .bt-m{border-top-style:solid;border-top-width:1px}.swagger-ui .br-m{border-right-style:solid;border-right-width:1px}.swagger-ui .bb-m{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl-m{border-left-style:solid;border-left-width:1px}.swagger-ui .bn-m{border-style:none;border-width:0}}@media screen and (min-width:60em){.swagger-ui .ba-l{border-style:solid;border-width:1px}.swagger-ui .bt-l{border-top-style:solid;border-top-width:1px}.swagger-ui .br-l{border-right-style:solid;border-right-width:1px}.swagger-ui .bb-l{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl-l{border-left-style:solid;border-left-width:1px}.swagger-ui .bn-l{border-style:none;border-width:0}}.swagger-ui .b--black{border-color:#000}.swagger-ui .b--near-black{border-color:#111}.swagger-ui .b--dark-gray{border-color:#333}.swagger-ui .b--mid-gray{border-color:#555}.swagger-ui .b--gray{border-color:#777}.swagger-ui .b--silver{border-color:#999}.swagger-ui .b--light-silver{border-color:#aaa}.swagger-ui .b--moon-gray{border-color:#ccc}.swagger-ui .b--light-gray{border-color:#eee}.swagger-ui .b--near-white{border-color:#f4f4f4}.swagger-ui .b--white{border-color:#fff}.swagger-ui .b--white-90{border-color:#ffffffe6}.swagger-ui .b--white-80{border-color:#fffc}.swagger-ui .b--white-70{border-color:#ffffffb3}.swagger-ui .b--white-60{border-color:#fff9}.swagger-ui .b--white-50{border-color:#ffffff80}.swagger-ui .b--white-40{border-color:#fff6}.swagger-ui .b--white-30{border-color:#ffffff4d}.swagger-ui .b--white-20{border-color:#fff3}.swagger-ui .b--white-10{border-color:#ffffff1a}.swagger-ui .b--white-05{border-color:#ffffff0d}.swagger-ui .b--white-025{border-color:#ffffff06}.swagger-ui .b--white-0125{border-color:#ffffff03}.swagger-ui .b--black-90{border-color:#000000e6}.swagger-ui .b--black-80{border-color:#000c}.swagger-ui .b--black-70{border-color:#000000b3}.swagger-ui .b--black-60{border-color:#0009}.swagger-ui .b--black-50{border-color:#00000080}.swagger-ui .b--black-40{border-color:#0006}.swagger-ui .b--black-30{border-color:#0000004d}.swagger-ui .b--black-20{border-color:#0003}.swagger-ui .b--black-10{border-color:#0000001a}.swagger-ui .b--black-05{border-color:#0000000d}.swagger-ui .b--black-025{border-color:#00000006}.swagger-ui .b--black-0125{border-color:#00000003}.swagger-ui .b--dark-red{border-color:#e7040f}.swagger-ui .b--red{border-color:#ff4136}.swagger-ui .b--light-red{border-color:#ff725c}.swagger-ui .b--orange{border-color:#ff6300}.swagger-ui .b--gold{border-color:#ffb700}.swagger-ui .b--yellow{border-color:gold}.swagger-ui .b--light-yellow{border-color:#fbf1a9}.swagger-ui .b--purple{border-color:#5e2ca5}.swagger-ui .b--light-purple{border-color:#a463f2}.swagger-ui .b--dark-pink{border-color:#d5008f}.swagger-ui .b--hot-pink{border-color:#ff41b4}.swagger-ui .b--pink{border-color:#ff80cc}.swagger-ui .b--light-pink{border-color:#ffa3d7}.swagger-ui .b--dark-green{border-color:#137752}.swagger-ui .b--green{border-color:#19a974}.swagger-ui .b--light-green{border-color:#9eebcf}.swagger-ui .b--navy{border-color:#001b44}.swagger-ui .b--dark-blue{border-color:#00449e}.swagger-ui .b--blue{border-color:#357edd}.swagger-ui .b--light-blue{border-color:#96ccff}.swagger-ui .b--lightest-blue{border-color:#cdecff}.swagger-ui .b--washed-blue{border-color:#f6fffe}.swagger-ui .b--washed-green{border-color:#e8fdf5}.swagger-ui .b--washed-yellow{border-color:#fffceb}.swagger-ui .b--washed-red{border-color:#ffdfdf}.swagger-ui .b--transparent{border-color:transparent}.swagger-ui .b--inherit{border-color:inherit}.swagger-ui .br0{border-radius:0}.swagger-ui .br1{border-radius:.125rem}.swagger-ui .br2{border-radius:.25rem}.swagger-ui .br3{border-radius:.5rem}.swagger-ui .br4{border-radius:1rem}.swagger-ui .br-100{border-radius:100%}.swagger-ui .br-pill{border-radius:9999px}.swagger-ui .br--bottom{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right{border-bottom-left-radius:0;border-top-left-radius:0}.swagger-ui .br--left{border-bottom-right-radius:0;border-top-right-radius:0}@media screen and (min-width:30em){.swagger-ui .br0-ns{border-radius:0}.swagger-ui .br1-ns{border-radius:.125rem}.swagger-ui .br2-ns{border-radius:.25rem}.swagger-ui .br3-ns{border-radius:.5rem}.swagger-ui .br4-ns{border-radius:1rem}.swagger-ui .br-100-ns{border-radius:100%}.swagger-ui .br-pill-ns{border-radius:9999px}.swagger-ui .br--bottom-ns{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top-ns{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right-ns{border-bottom-left-radius:0;border-top-left-radius:0}.swagger-ui .br--left-ns{border-bottom-right-radius:0;border-top-right-radius:0}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .br0-m{border-radius:0}.swagger-ui .br1-m{border-radius:.125rem}.swagger-ui .br2-m{border-radius:.25rem}.swagger-ui .br3-m{border-radius:.5rem}.swagger-ui .br4-m{border-radius:1rem}.swagger-ui .br-100-m{border-radius:100%}.swagger-ui .br-pill-m{border-radius:9999px}.swagger-ui .br--bottom-m{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top-m{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right-m{border-bottom-left-radius:0;border-top-left-radius:0}.swagger-ui .br--left-m{border-bottom-right-radius:0;border-top-right-radius:0}}@media screen and (min-width:60em){.swagger-ui .br0-l{border-radius:0}.swagger-ui .br1-l{border-radius:.125rem}.swagger-ui .br2-l{border-radius:.25rem}.swagger-ui .br3-l{border-radius:.5rem}.swagger-ui .br4-l{border-radius:1rem}.swagger-ui .br-100-l{border-radius:100%}.swagger-ui .br-pill-l{border-radius:9999px}.swagger-ui .br--bottom-l{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top-l{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right-l{border-bottom-left-radius:0;border-top-left-radius:0}.swagger-ui .br--left-l{border-bottom-right-radius:0;border-top-right-radius:0}}.swagger-ui .b--dotted{border-style:dotted}.swagger-ui .b--dashed{border-style:dashed}.swagger-ui .b--solid{border-style:solid}.swagger-ui .b--none{border-style:none}@media screen and (min-width:30em){.swagger-ui .b--dotted-ns{border-style:dotted}.swagger-ui .b--dashed-ns{border-style:dashed}.swagger-ui .b--solid-ns{border-style:solid}.swagger-ui .b--none-ns{border-style:none}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .b--dotted-m{border-style:dotted}.swagger-ui .b--dashed-m{border-style:dashed}.swagger-ui .b--solid-m{border-style:solid}.swagger-ui .b--none-m{border-style:none}}@media screen and (min-width:60em){.swagger-ui .b--dotted-l{border-style:dotted}.swagger-ui .b--dashed-l{border-style:dashed}.swagger-ui .b--solid-l{border-style:solid}.swagger-ui .b--none-l{border-style:none}}.swagger-ui .bw0{border-width:0}.swagger-ui .bw1{border-width:.125rem}.swagger-ui .bw2{border-width:.25rem}.swagger-ui .bw3{border-width:.5rem}.swagger-ui .bw4{border-width:1rem}.swagger-ui .bw5{border-width:2rem}.swagger-ui .bt-0{border-top-width:0}.swagger-ui .br-0{border-right-width:0}.swagger-ui .bb-0{border-bottom-width:0}.swagger-ui .bl-0{border-left-width:0}@media screen and (min-width:30em){.swagger-ui .bw0-ns{border-width:0}.swagger-ui .bw1-ns{border-width:.125rem}.swagger-ui .bw2-ns{border-width:.25rem}.swagger-ui .bw3-ns{border-width:.5rem}.swagger-ui .bw4-ns{border-width:1rem}.swagger-ui .bw5-ns{border-width:2rem}.swagger-ui .bt-0-ns{border-top-width:0}.swagger-ui .br-0-ns{border-right-width:0}.swagger-ui .bb-0-ns{border-bottom-width:0}.swagger-ui .bl-0-ns{border-left-width:0}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .bw0-m{border-width:0}.swagger-ui .bw1-m{border-width:.125rem}.swagger-ui .bw2-m{border-width:.25rem}.swagger-ui .bw3-m{border-width:.5rem}.swagger-ui .bw4-m{border-width:1rem}.swagger-ui .bw5-m{border-width:2rem}.swagger-ui .bt-0-m{border-top-width:0}.swagger-ui .br-0-m{border-right-width:0}.swagger-ui .bb-0-m{border-bottom-width:0}.swagger-ui .bl-0-m{border-left-width:0}}@media screen and (min-width:60em){.swagger-ui .bw0-l{border-width:0}.swagger-ui .bw1-l{border-width:.125rem}.swagger-ui .bw2-l{border-width:.25rem}.swagger-ui .bw3-l{border-width:.5rem}.swagger-ui .bw4-l{border-width:1rem}.swagger-ui .bw5-l{border-width:2rem}.swagger-ui .bt-0-l{border-top-width:0}.swagger-ui .br-0-l{border-right-width:0}.swagger-ui .bb-0-l{border-bottom-width:0}.swagger-ui .bl-0-l{border-left-width:0}}.swagger-ui .shadow-1{box-shadow:0 0 4px 2px #0003}.swagger-ui .shadow-2{box-shadow:0 0 8px 2px #0003}.swagger-ui .shadow-3{box-shadow:2px 2px 4px 2px #0003}.swagger-ui .shadow-4{box-shadow:2px 2px 8px #0003}.swagger-ui .shadow-5{box-shadow:4px 4px 8px #0003}@media screen and (min-width:30em){.swagger-ui .shadow-1-ns{box-shadow:0 0 4px 2px #0003}.swagger-ui .shadow-2-ns{box-shadow:0 0 8px 2px #0003}.swagger-ui .shadow-3-ns{box-shadow:2px 2px 4px 2px #0003}.swagger-ui .shadow-4-ns{box-shadow:2px 2px 8px #0003}.swagger-ui .shadow-5-ns{box-shadow:4px 4px 8px #0003}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .shadow-1-m{box-shadow:0 0 4px 2px #0003}.swagger-ui .shadow-2-m{box-shadow:0 0 8px 2px #0003}.swagger-ui .shadow-3-m{box-shadow:2px 2px 4px 2px #0003}.swagger-ui .shadow-4-m{box-shadow:2px 2px 8px #0003}.swagger-ui .shadow-5-m{box-shadow:4px 4px 8px #0003}}@media screen and (min-width:60em){.swagger-ui .shadow-1-l{box-shadow:0 0 4px 2px #0003}.swagger-ui .shadow-2-l{box-shadow:0 0 8px 2px #0003}.swagger-ui .shadow-3-l{box-shadow:2px 2px 4px 2px #0003}.swagger-ui .shadow-4-l{box-shadow:2px 2px 8px #0003}.swagger-ui .shadow-5-l{box-shadow:4px 4px 8px #0003}}.swagger-ui .pre{overflow-x:auto;overflow-y:hidden;overflow:scroll}.swagger-ui .top-0{top:0}.swagger-ui .right-0{right:0}.swagger-ui .bottom-0{bottom:0}.swagger-ui .left-0{left:0}.swagger-ui .top-1{top:1rem}.swagger-ui .right-1{right:1rem}.swagger-ui .bottom-1{bottom:1rem}.swagger-ui .left-1{left:1rem}.swagger-ui .top-2{top:2rem}.swagger-ui .right-2{right:2rem}.swagger-ui .bottom-2{bottom:2rem}.swagger-ui .left-2{left:2rem}.swagger-ui .top--1{top:-1rem}.swagger-ui .right--1{right:-1rem}.swagger-ui .bottom--1{bottom:-1rem}.swagger-ui .left--1{left:-1rem}.swagger-ui .top--2{top:-2rem}.swagger-ui .right--2{right:-2rem}.swagger-ui .bottom--2{bottom:-2rem}.swagger-ui .left--2{left:-2rem}.swagger-ui .absolute--fill{inset:0}@media screen and (min-width:30em){.swagger-ui .top-0-ns{top:0}.swagger-ui .left-0-ns{left:0}.swagger-ui .right-0-ns{right:0}.swagger-ui .bottom-0-ns{bottom:0}.swagger-ui .top-1-ns{top:1rem}.swagger-ui .left-1-ns{left:1rem}.swagger-ui .right-1-ns{right:1rem}.swagger-ui .bottom-1-ns{bottom:1rem}.swagger-ui .top-2-ns{top:2rem}.swagger-ui .left-2-ns{left:2rem}.swagger-ui .right-2-ns{right:2rem}.swagger-ui .bottom-2-ns{bottom:2rem}.swagger-ui .top--1-ns{top:-1rem}.swagger-ui .right--1-ns{right:-1rem}.swagger-ui .bottom--1-ns{bottom:-1rem}.swagger-ui .left--1-ns{left:-1rem}.swagger-ui .top--2-ns{top:-2rem}.swagger-ui .right--2-ns{right:-2rem}.swagger-ui .bottom--2-ns{bottom:-2rem}.swagger-ui .left--2-ns{left:-2rem}.swagger-ui .absolute--fill-ns{inset:0}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .top-0-m{top:0}.swagger-ui .left-0-m{left:0}.swagger-ui .right-0-m{right:0}.swagger-ui .bottom-0-m{bottom:0}.swagger-ui .top-1-m{top:1rem}.swagger-ui .left-1-m{left:1rem}.swagger-ui .right-1-m{right:1rem}.swagger-ui .bottom-1-m{bottom:1rem}.swagger-ui .top-2-m{top:2rem}.swagger-ui .left-2-m{left:2rem}.swagger-ui .right-2-m{right:2rem}.swagger-ui .bottom-2-m{bottom:2rem}.swagger-ui .top--1-m{top:-1rem}.swagger-ui .right--1-m{right:-1rem}.swagger-ui .bottom--1-m{bottom:-1rem}.swagger-ui .left--1-m{left:-1rem}.swagger-ui .top--2-m{top:-2rem}.swagger-ui .right--2-m{right:-2rem}.swagger-ui .bottom--2-m{bottom:-2rem}.swagger-ui .left--2-m{left:-2rem}.swagger-ui .absolute--fill-m{inset:0}}@media screen and (min-width:60em){.swagger-ui .top-0-l{top:0}.swagger-ui .left-0-l{left:0}.swagger-ui .right-0-l{right:0}.swagger-ui .bottom-0-l{bottom:0}.swagger-ui .top-1-l{top:1rem}.swagger-ui .left-1-l{left:1rem}.swagger-ui .right-1-l{right:1rem}.swagger-ui .bottom-1-l{bottom:1rem}.swagger-ui .top-2-l{top:2rem}.swagger-ui .left-2-l{left:2rem}.swagger-ui .right-2-l{right:2rem}.swagger-ui .bottom-2-l{bottom:2rem}.swagger-ui .top--1-l{top:-1rem}.swagger-ui .right--1-l{right:-1rem}.swagger-ui .bottom--1-l{bottom:-1rem}.swagger-ui .left--1-l{left:-1rem}.swagger-ui .top--2-l{top:-2rem}.swagger-ui .right--2-l{right:-2rem}.swagger-ui .bottom--2-l{bottom:-2rem}.swagger-ui .left--2-l{left:-2rem}.swagger-ui .absolute--fill-l{inset:0}}.swagger-ui .cf:after,.swagger-ui .cf:before{content:" ";display:table}.swagger-ui .cf:after{clear:both}.swagger-ui .cf{zoom:1}.swagger-ui .cl{clear:left}.swagger-ui .cr{clear:right}.swagger-ui .cb{clear:both}.swagger-ui .cn{clear:none}@media screen and (min-width:30em){.swagger-ui .cl-ns{clear:left}.swagger-ui .cr-ns{clear:right}.swagger-ui .cb-ns{clear:both}.swagger-ui .cn-ns{clear:none}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .cl-m{clear:left}.swagger-ui .cr-m{clear:right}.swagger-ui .cb-m{clear:both}.swagger-ui .cn-m{clear:none}}@media screen and (min-width:60em){.swagger-ui .cl-l{clear:left}.swagger-ui .cr-l{clear:right}.swagger-ui .cb-l{clear:both}.swagger-ui .cn-l{clear:none}}.swagger-ui .flex{display:flex}.swagger-ui .inline-flex{display:inline-flex}.swagger-ui .flex-auto{flex:1 1 auto;min-height:0;min-width:0}.swagger-ui .flex-none{flex:none}.swagger-ui .flex-column{flex-direction:column}.swagger-ui .flex-row{flex-direction:row}.swagger-ui .flex-wrap{flex-wrap:wrap}.swagger-ui .flex-nowrap{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse{flex-direction:column-reverse}.swagger-ui .flex-row-reverse{flex-direction:row-reverse}.swagger-ui .items-start{align-items:flex-start}.swagger-ui .items-end{align-items:flex-end}.swagger-ui .items-center{align-items:center}.swagger-ui .items-baseline{align-items:baseline}.swagger-ui .items-stretch{align-items:stretch}.swagger-ui .self-start{align-self:flex-start}.swagger-ui .self-end{align-self:flex-end}.swagger-ui .self-center{align-self:center}.swagger-ui .self-baseline{align-self:baseline}.swagger-ui .self-stretch{align-self:stretch}.swagger-ui .justify-start{justify-content:flex-start}.swagger-ui .justify-end{justify-content:flex-end}.swagger-ui .justify-center{justify-content:center}.swagger-ui .justify-between{justify-content:space-between}.swagger-ui .justify-around{justify-content:space-around}.swagger-ui .content-start{align-content:flex-start}.swagger-ui .content-end{align-content:flex-end}.swagger-ui .content-center{align-content:center}.swagger-ui .content-between{align-content:space-between}.swagger-ui .content-around{align-content:space-around}.swagger-ui .content-stretch{align-content:stretch}.swagger-ui .order-0{order:0}.swagger-ui .order-1{order:1}.swagger-ui .order-2{order:2}.swagger-ui .order-3{order:3}.swagger-ui .order-4{order:4}.swagger-ui .order-5{order:5}.swagger-ui .order-6{order:6}.swagger-ui .order-7{order:7}.swagger-ui .order-8{order:8}.swagger-ui .order-last{order:99999}.swagger-ui .flex-grow-0{flex-grow:0}.swagger-ui .flex-grow-1{flex-grow:1}.swagger-ui .flex-shrink-0{flex-shrink:0}.swagger-ui .flex-shrink-1{flex-shrink:1}@media screen and (min-width:30em){.swagger-ui .flex-ns{display:flex}.swagger-ui .inline-flex-ns{display:inline-flex}.swagger-ui .flex-auto-ns{flex:1 1 auto;min-height:0;min-width:0}.swagger-ui .flex-none-ns{flex:none}.swagger-ui .flex-column-ns{flex-direction:column}.swagger-ui .flex-row-ns{flex-direction:row}.swagger-ui .flex-wrap-ns{flex-wrap:wrap}.swagger-ui .flex-nowrap-ns{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse-ns{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse-ns{flex-direction:column-reverse}.swagger-ui .flex-row-reverse-ns{flex-direction:row-reverse}.swagger-ui .items-start-ns{align-items:flex-start}.swagger-ui .items-end-ns{align-items:flex-end}.swagger-ui .items-center-ns{align-items:center}.swagger-ui .items-baseline-ns{align-items:baseline}.swagger-ui .items-stretch-ns{align-items:stretch}.swagger-ui .self-start-ns{align-self:flex-start}.swagger-ui .self-end-ns{align-self:flex-end}.swagger-ui .self-center-ns{align-self:center}.swagger-ui .self-baseline-ns{align-self:baseline}.swagger-ui .self-stretch-ns{align-self:stretch}.swagger-ui .justify-start-ns{justify-content:flex-start}.swagger-ui .justify-end-ns{justify-content:flex-end}.swagger-ui .justify-center-ns{justify-content:center}.swagger-ui .justify-between-ns{justify-content:space-between}.swagger-ui .justify-around-ns{justify-content:space-around}.swagger-ui .content-start-ns{align-content:flex-start}.swagger-ui .content-end-ns{align-content:flex-end}.swagger-ui .content-center-ns{align-content:center}.swagger-ui .content-between-ns{align-content:space-between}.swagger-ui .content-around-ns{align-content:space-around}.swagger-ui .content-stretch-ns{align-content:stretch}.swagger-ui .order-0-ns{order:0}.swagger-ui .order-1-ns{order:1}.swagger-ui .order-2-ns{order:2}.swagger-ui .order-3-ns{order:3}.swagger-ui .order-4-ns{order:4}.swagger-ui .order-5-ns{order:5}.swagger-ui .order-6-ns{order:6}.swagger-ui .order-7-ns{order:7}.swagger-ui .order-8-ns{order:8}.swagger-ui .order-last-ns{order:99999}.swagger-ui .flex-grow-0-ns{flex-grow:0}.swagger-ui .flex-grow-1-ns{flex-grow:1}.swagger-ui .flex-shrink-0-ns{flex-shrink:0}.swagger-ui .flex-shrink-1-ns{flex-shrink:1}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .flex-m{display:flex}.swagger-ui .inline-flex-m{display:inline-flex}.swagger-ui .flex-auto-m{flex:1 1 auto;min-height:0;min-width:0}.swagger-ui .flex-none-m{flex:none}.swagger-ui .flex-column-m{flex-direction:column}.swagger-ui .flex-row-m{flex-direction:row}.swagger-ui .flex-wrap-m{flex-wrap:wrap}.swagger-ui .flex-nowrap-m{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse-m{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse-m{flex-direction:column-reverse}.swagger-ui .flex-row-reverse-m{flex-direction:row-reverse}.swagger-ui .items-start-m{align-items:flex-start}.swagger-ui .items-end-m{align-items:flex-end}.swagger-ui .items-center-m{align-items:center}.swagger-ui .items-baseline-m{align-items:baseline}.swagger-ui .items-stretch-m{align-items:stretch}.swagger-ui .self-start-m{align-self:flex-start}.swagger-ui .self-end-m{align-self:flex-end}.swagger-ui .self-center-m{align-self:center}.swagger-ui .self-baseline-m{align-self:baseline}.swagger-ui .self-stretch-m{align-self:stretch}.swagger-ui .justify-start-m{justify-content:flex-start}.swagger-ui .justify-end-m{justify-content:flex-end}.swagger-ui .justify-center-m{justify-content:center}.swagger-ui .justify-between-m{justify-content:space-between}.swagger-ui .justify-around-m{justify-content:space-around}.swagger-ui .content-start-m{align-content:flex-start}.swagger-ui .content-end-m{align-content:flex-end}.swagger-ui .content-center-m{align-content:center}.swagger-ui .content-between-m{align-content:space-between}.swagger-ui .content-around-m{align-content:space-around}.swagger-ui .content-stretch-m{align-content:stretch}.swagger-ui .order-0-m{order:0}.swagger-ui .order-1-m{order:1}.swagger-ui .order-2-m{order:2}.swagger-ui .order-3-m{order:3}.swagger-ui .order-4-m{order:4}.swagger-ui .order-5-m{order:5}.swagger-ui .order-6-m{order:6}.swagger-ui .order-7-m{order:7}.swagger-ui .order-8-m{order:8}.swagger-ui .order-last-m{order:99999}.swagger-ui .flex-grow-0-m{flex-grow:0}.swagger-ui .flex-grow-1-m{flex-grow:1}.swagger-ui .flex-shrink-0-m{flex-shrink:0}.swagger-ui .flex-shrink-1-m{flex-shrink:1}}@media screen and (min-width:60em){.swagger-ui .flex-l{display:flex}.swagger-ui .inline-flex-l{display:inline-flex}.swagger-ui .flex-auto-l{flex:1 1 auto;min-height:0;min-width:0}.swagger-ui .flex-none-l{flex:none}.swagger-ui .flex-column-l{flex-direction:column}.swagger-ui .flex-row-l{flex-direction:row}.swagger-ui .flex-wrap-l{flex-wrap:wrap}.swagger-ui .flex-nowrap-l{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse-l{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse-l{flex-direction:column-reverse}.swagger-ui .flex-row-reverse-l{flex-direction:row-reverse}.swagger-ui .items-start-l{align-items:flex-start}.swagger-ui .items-end-l{align-items:flex-end}.swagger-ui .items-center-l{align-items:center}.swagger-ui .items-baseline-l{align-items:baseline}.swagger-ui .items-stretch-l{align-items:stretch}.swagger-ui .self-start-l{align-self:flex-start}.swagger-ui .self-end-l{align-self:flex-end}.swagger-ui .self-center-l{align-self:center}.swagger-ui .self-baseline-l{align-self:baseline}.swagger-ui .self-stretch-l{align-self:stretch}.swagger-ui .justify-start-l{justify-content:flex-start}.swagger-ui .justify-end-l{justify-content:flex-end}.swagger-ui .justify-center-l{justify-content:center}.swagger-ui .justify-between-l{justify-content:space-between}.swagger-ui .justify-around-l{justify-content:space-around}.swagger-ui .content-start-l{align-content:flex-start}.swagger-ui .content-end-l{align-content:flex-end}.swagger-ui .content-center-l{align-content:center}.swagger-ui .content-between-l{align-content:space-between}.swagger-ui .content-around-l{align-content:space-around}.swagger-ui .content-stretch-l{align-content:stretch}.swagger-ui .order-0-l{order:0}.swagger-ui .order-1-l{order:1}.swagger-ui .order-2-l{order:2}.swagger-ui .order-3-l{order:3}.swagger-ui .order-4-l{order:4}.swagger-ui .order-5-l{order:5}.swagger-ui .order-6-l{order:6}.swagger-ui .order-7-l{order:7}.swagger-ui .order-8-l{order:8}.swagger-ui .order-last-l{order:99999}.swagger-ui .flex-grow-0-l{flex-grow:0}.swagger-ui .flex-grow-1-l{flex-grow:1}.swagger-ui .flex-shrink-0-l{flex-shrink:0}.swagger-ui .flex-shrink-1-l{flex-shrink:1}}.swagger-ui .dn{display:none}.swagger-ui .di{display:inline}.swagger-ui .db{display:block}.swagger-ui .dib{display:inline-block}.swagger-ui .dit{display:inline-table}.swagger-ui .dt{display:table}.swagger-ui .dtc{display:table-cell}.swagger-ui .dt-row{display:table-row}.swagger-ui .dt-row-group{display:table-row-group}.swagger-ui .dt-column{display:table-column}.swagger-ui .dt-column-group{display:table-column-group}.swagger-ui .dt--fixed{table-layout:fixed;width:100%}@media screen and (min-width:30em){.swagger-ui .dn-ns{display:none}.swagger-ui .di-ns{display:inline}.swagger-ui .db-ns{display:block}.swagger-ui .dib-ns{display:inline-block}.swagger-ui .dit-ns{display:inline-table}.swagger-ui .dt-ns{display:table}.swagger-ui .dtc-ns{display:table-cell}.swagger-ui .dt-row-ns{display:table-row}.swagger-ui .dt-row-group-ns{display:table-row-group}.swagger-ui .dt-column-ns{display:table-column}.swagger-ui .dt-column-group-ns{display:table-column-group}.swagger-ui .dt--fixed-ns{table-layout:fixed;width:100%}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .dn-m{display:none}.swagger-ui .di-m{display:inline}.swagger-ui .db-m{display:block}.swagger-ui .dib-m{display:inline-block}.swagger-ui .dit-m{display:inline-table}.swagger-ui .dt-m{display:table}.swagger-ui .dtc-m{display:table-cell}.swagger-ui .dt-row-m{display:table-row}.swagger-ui .dt-row-group-m{display:table-row-group}.swagger-ui .dt-column-m{display:table-column}.swagger-ui .dt-column-group-m{display:table-column-group}.swagger-ui .dt--fixed-m{table-layout:fixed;width:100%}}@media screen and (min-width:60em){.swagger-ui .dn-l{display:none}.swagger-ui .di-l{display:inline}.swagger-ui .db-l{display:block}.swagger-ui .dib-l{display:inline-block}.swagger-ui .dit-l{display:inline-table}.swagger-ui .dt-l{display:table}.swagger-ui .dtc-l{display:table-cell}.swagger-ui .dt-row-l{display:table-row}.swagger-ui .dt-row-group-l{display:table-row-group}.swagger-ui .dt-column-l{display:table-column}.swagger-ui .dt-column-group-l{display:table-column-group}.swagger-ui .dt--fixed-l{table-layout:fixed;width:100%}}.swagger-ui .fl{_display:inline;float:left}.swagger-ui .fr{_display:inline;float:right}.swagger-ui .fn{float:none}@media screen and (min-width:30em){.swagger-ui .fl-ns{_display:inline;float:left}.swagger-ui .fr-ns{_display:inline;float:right}.swagger-ui .fn-ns{float:none}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .fl-m{_display:inline;float:left}.swagger-ui .fr-m{_display:inline;float:right}.swagger-ui .fn-m{float:none}}@media screen and (min-width:60em){.swagger-ui .fl-l{_display:inline;float:left}.swagger-ui .fr-l{_display:inline;float:right}.swagger-ui .fn-l{float:none}}.swagger-ui .sans-serif{font-family:-apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica,helvetica neue,ubuntu,roboto,noto,segoe ui,arial,sans-serif}.swagger-ui .serif{font-family:georgia,serif}.swagger-ui .system-sans-serif{font-family:sans-serif}.swagger-ui .system-serif{font-family:serif}.swagger-ui .code,.swagger-ui code{font-family:Consolas,monaco,monospace}.swagger-ui .courier{font-family:Courier Next,courier,monospace}.swagger-ui .helvetica{font-family:helvetica neue,helvetica,sans-serif}.swagger-ui .avenir{font-family:avenir next,avenir,sans-serif}.swagger-ui .athelas{font-family:athelas,georgia,serif}.swagger-ui .georgia{font-family:georgia,serif}.swagger-ui .times{font-family:times,serif}.swagger-ui .bodoni{font-family:Bodoni MT,serif}.swagger-ui .calisto{font-family:Calisto MT,serif}.swagger-ui .garamond{font-family:garamond,serif}.swagger-ui .baskerville{font-family:baskerville,serif}.swagger-ui .i{font-style:italic}.swagger-ui .fs-normal{font-style:normal}@media screen and (min-width:30em){.swagger-ui .i-ns{font-style:italic}.swagger-ui .fs-normal-ns{font-style:normal}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .i-m{font-style:italic}.swagger-ui .fs-normal-m{font-style:normal}}@media screen and (min-width:60em){.swagger-ui .i-l{font-style:italic}.swagger-ui .fs-normal-l{font-style:normal}}.swagger-ui .normal{font-weight:400}.swagger-ui .b{font-weight:700}.swagger-ui .fw1{font-weight:100}.swagger-ui .fw2{font-weight:200}.swagger-ui .fw3{font-weight:300}.swagger-ui .fw4{font-weight:400}.swagger-ui .fw5{font-weight:500}.swagger-ui .fw6{font-weight:600}.swagger-ui .fw7{font-weight:700}.swagger-ui .fw8{font-weight:800}.swagger-ui .fw9{font-weight:900}@media screen and (min-width:30em){.swagger-ui .normal-ns{font-weight:400}.swagger-ui .b-ns{font-weight:700}.swagger-ui .fw1-ns{font-weight:100}.swagger-ui .fw2-ns{font-weight:200}.swagger-ui .fw3-ns{font-weight:300}.swagger-ui .fw4-ns{font-weight:400}.swagger-ui .fw5-ns{font-weight:500}.swagger-ui .fw6-ns{font-weight:600}.swagger-ui .fw7-ns{font-weight:700}.swagger-ui .fw8-ns{font-weight:800}.swagger-ui .fw9-ns{font-weight:900}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .normal-m{font-weight:400}.swagger-ui .b-m{font-weight:700}.swagger-ui .fw1-m{font-weight:100}.swagger-ui .fw2-m{font-weight:200}.swagger-ui .fw3-m{font-weight:300}.swagger-ui .fw4-m{font-weight:400}.swagger-ui .fw5-m{font-weight:500}.swagger-ui .fw6-m{font-weight:600}.swagger-ui .fw7-m{font-weight:700}.swagger-ui .fw8-m{font-weight:800}.swagger-ui .fw9-m{font-weight:900}}@media screen and (min-width:60em){.swagger-ui .normal-l{font-weight:400}.swagger-ui .b-l{font-weight:700}.swagger-ui .fw1-l{font-weight:100}.swagger-ui .fw2-l{font-weight:200}.swagger-ui .fw3-l{font-weight:300}.swagger-ui .fw4-l{font-weight:400}.swagger-ui .fw5-l{font-weight:500}.swagger-ui .fw6-l{font-weight:600}.swagger-ui .fw7-l{font-weight:700}.swagger-ui .fw8-l{font-weight:800}.swagger-ui .fw9-l{font-weight:900}}.swagger-ui .input-reset{-webkit-appearance:none;-moz-appearance:none}.swagger-ui .button-reset::-moz-focus-inner,.swagger-ui .input-reset::-moz-focus-inner{border:0;padding:0}.swagger-ui .h1{height:1rem}.swagger-ui .h2{height:2rem}.swagger-ui .h3{height:4rem}.swagger-ui .h4{height:8rem}.swagger-ui .h5{height:16rem}.swagger-ui .h-25{height:25%}.swagger-ui .h-50{height:50%}.swagger-ui .h-75{height:75%}.swagger-ui .h-100{height:100%}.swagger-ui .min-h-100{min-height:100%}.swagger-ui .vh-25{height:25vh}.swagger-ui .vh-50{height:50vh}.swagger-ui .vh-75{height:75vh}.swagger-ui .vh-100{height:100vh}.swagger-ui .min-vh-100{min-height:100vh}.swagger-ui .h-auto{height:auto}.swagger-ui .h-inherit{height:inherit}@media screen and (min-width:30em){.swagger-ui .h1-ns{height:1rem}.swagger-ui .h2-ns{height:2rem}.swagger-ui .h3-ns{height:4rem}.swagger-ui .h4-ns{height:8rem}.swagger-ui .h5-ns{height:16rem}.swagger-ui .h-25-ns{height:25%}.swagger-ui .h-50-ns{height:50%}.swagger-ui .h-75-ns{height:75%}.swagger-ui .h-100-ns{height:100%}.swagger-ui .min-h-100-ns{min-height:100%}.swagger-ui .vh-25-ns{height:25vh}.swagger-ui .vh-50-ns{height:50vh}.swagger-ui .vh-75-ns{height:75vh}.swagger-ui .vh-100-ns{height:100vh}.swagger-ui .min-vh-100-ns{min-height:100vh}.swagger-ui .h-auto-ns{height:auto}.swagger-ui .h-inherit-ns{height:inherit}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .h1-m{height:1rem}.swagger-ui .h2-m{height:2rem}.swagger-ui .h3-m{height:4rem}.swagger-ui .h4-m{height:8rem}.swagger-ui .h5-m{height:16rem}.swagger-ui .h-25-m{height:25%}.swagger-ui .h-50-m{height:50%}.swagger-ui .h-75-m{height:75%}.swagger-ui .h-100-m{height:100%}.swagger-ui .min-h-100-m{min-height:100%}.swagger-ui .vh-25-m{height:25vh}.swagger-ui .vh-50-m{height:50vh}.swagger-ui .vh-75-m{height:75vh}.swagger-ui .vh-100-m{height:100vh}.swagger-ui .min-vh-100-m{min-height:100vh}.swagger-ui .h-auto-m{height:auto}.swagger-ui .h-inherit-m{height:inherit}}@media screen and (min-width:60em){.swagger-ui .h1-l{height:1rem}.swagger-ui .h2-l{height:2rem}.swagger-ui .h3-l{height:4rem}.swagger-ui .h4-l{height:8rem}.swagger-ui .h5-l{height:16rem}.swagger-ui .h-25-l{height:25%}.swagger-ui .h-50-l{height:50%}.swagger-ui .h-75-l{height:75%}.swagger-ui .h-100-l{height:100%}.swagger-ui .min-h-100-l{min-height:100%}.swagger-ui .vh-25-l{height:25vh}.swagger-ui .vh-50-l{height:50vh}.swagger-ui .vh-75-l{height:75vh}.swagger-ui .vh-100-l{height:100vh}.swagger-ui .min-vh-100-l{min-height:100vh}.swagger-ui .h-auto-l{height:auto}.swagger-ui .h-inherit-l{height:inherit}}.swagger-ui .tracked{letter-spacing:.1em}.swagger-ui .tracked-tight{letter-spacing:-.05em}.swagger-ui .tracked-mega{letter-spacing:.25em}@media screen and (min-width:30em){.swagger-ui .tracked-ns{letter-spacing:.1em}.swagger-ui .tracked-tight-ns{letter-spacing:-.05em}.swagger-ui .tracked-mega-ns{letter-spacing:.25em}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .tracked-m{letter-spacing:.1em}.swagger-ui .tracked-tight-m{letter-spacing:-.05em}.swagger-ui .tracked-mega-m{letter-spacing:.25em}}@media screen and (min-width:60em){.swagger-ui .tracked-l{letter-spacing:.1em}.swagger-ui .tracked-tight-l{letter-spacing:-.05em}.swagger-ui .tracked-mega-l{letter-spacing:.25em}}.swagger-ui .lh-solid{line-height:1}.swagger-ui .lh-title{line-height:1.25}.swagger-ui .lh-copy{line-height:1.5}@media screen and (min-width:30em){.swagger-ui .lh-solid-ns{line-height:1}.swagger-ui .lh-title-ns{line-height:1.25}.swagger-ui .lh-copy-ns{line-height:1.5}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .lh-solid-m{line-height:1}.swagger-ui .lh-title-m{line-height:1.25}.swagger-ui .lh-copy-m{line-height:1.5}}@media screen and (min-width:60em){.swagger-ui .lh-solid-l{line-height:1}.swagger-ui .lh-title-l{line-height:1.25}.swagger-ui .lh-copy-l{line-height:1.5}}.swagger-ui .link{-webkit-text-decoration:none;text-decoration:none}.swagger-ui .link,.swagger-ui .link:active,.swagger-ui .link:focus,.swagger-ui .link:hover,.swagger-ui .link:link,.swagger-ui .link:visited{transition:color .15s ease-in}.swagger-ui .link:focus{outline:1px dotted currentColor}.swagger-ui .list{list-style-type:none}.swagger-ui .mw-100{max-width:100%}.swagger-ui .mw1{max-width:1rem}.swagger-ui .mw2{max-width:2rem}.swagger-ui .mw3{max-width:4rem}.swagger-ui .mw4{max-width:8rem}.swagger-ui .mw5{max-width:16rem}.swagger-ui .mw6{max-width:32rem}.swagger-ui .mw7{max-width:48rem}.swagger-ui .mw8{max-width:64rem}.swagger-ui .mw9{max-width:96rem}.swagger-ui .mw-none{max-width:none}@media screen and (min-width:30em){.swagger-ui .mw-100-ns{max-width:100%}.swagger-ui .mw1-ns{max-width:1rem}.swagger-ui .mw2-ns{max-width:2rem}.swagger-ui .mw3-ns{max-width:4rem}.swagger-ui .mw4-ns{max-width:8rem}.swagger-ui .mw5-ns{max-width:16rem}.swagger-ui .mw6-ns{max-width:32rem}.swagger-ui .mw7-ns{max-width:48rem}.swagger-ui .mw8-ns{max-width:64rem}.swagger-ui .mw9-ns{max-width:96rem}.swagger-ui .mw-none-ns{max-width:none}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .mw-100-m{max-width:100%}.swagger-ui .mw1-m{max-width:1rem}.swagger-ui .mw2-m{max-width:2rem}.swagger-ui .mw3-m{max-width:4rem}.swagger-ui .mw4-m{max-width:8rem}.swagger-ui .mw5-m{max-width:16rem}.swagger-ui .mw6-m{max-width:32rem}.swagger-ui .mw7-m{max-width:48rem}.swagger-ui .mw8-m{max-width:64rem}.swagger-ui .mw9-m{max-width:96rem}.swagger-ui .mw-none-m{max-width:none}}@media screen and (min-width:60em){.swagger-ui .mw-100-l{max-width:100%}.swagger-ui .mw1-l{max-width:1rem}.swagger-ui .mw2-l{max-width:2rem}.swagger-ui .mw3-l{max-width:4rem}.swagger-ui .mw4-l{max-width:8rem}.swagger-ui .mw5-l{max-width:16rem}.swagger-ui .mw6-l{max-width:32rem}.swagger-ui .mw7-l{max-width:48rem}.swagger-ui .mw8-l{max-width:64rem}.swagger-ui .mw9-l{max-width:96rem}.swagger-ui .mw-none-l{max-width:none}}.swagger-ui .w1{width:1rem}.swagger-ui .w2{width:2rem}.swagger-ui .w3{width:4rem}.swagger-ui .w4{width:8rem}.swagger-ui .w5{width:16rem}.swagger-ui .w-10{width:10%}.swagger-ui .w-20{width:20%}.swagger-ui .w-25{width:25%}.swagger-ui .w-30{width:30%}.swagger-ui .w-33{width:33%}.swagger-ui .w-34{width:34%}.swagger-ui .w-40{width:40%}.swagger-ui .w-50{width:50%}.swagger-ui .w-60{width:60%}.swagger-ui .w-70{width:70%}.swagger-ui .w-75{width:75%}.swagger-ui .w-80{width:80%}.swagger-ui .w-90{width:90%}.swagger-ui .w-100{width:100%}.swagger-ui .w-third{width:33.3333333333%}.swagger-ui .w-two-thirds{width:66.6666666667%}.swagger-ui .w-auto{width:auto}@media screen and (min-width:30em){.swagger-ui .w1-ns{width:1rem}.swagger-ui .w2-ns{width:2rem}.swagger-ui .w3-ns{width:4rem}.swagger-ui .w4-ns{width:8rem}.swagger-ui .w5-ns{width:16rem}.swagger-ui .w-10-ns{width:10%}.swagger-ui .w-20-ns{width:20%}.swagger-ui .w-25-ns{width:25%}.swagger-ui .w-30-ns{width:30%}.swagger-ui .w-33-ns{width:33%}.swagger-ui .w-34-ns{width:34%}.swagger-ui .w-40-ns{width:40%}.swagger-ui .w-50-ns{width:50%}.swagger-ui .w-60-ns{width:60%}.swagger-ui .w-70-ns{width:70%}.swagger-ui .w-75-ns{width:75%}.swagger-ui .w-80-ns{width:80%}.swagger-ui .w-90-ns{width:90%}.swagger-ui .w-100-ns{width:100%}.swagger-ui .w-third-ns{width:33.3333333333%}.swagger-ui .w-two-thirds-ns{width:66.6666666667%}.swagger-ui .w-auto-ns{width:auto}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .w1-m{width:1rem}.swagger-ui .w2-m{width:2rem}.swagger-ui .w3-m{width:4rem}.swagger-ui .w4-m{width:8rem}.swagger-ui .w5-m{width:16rem}.swagger-ui .w-10-m{width:10%}.swagger-ui .w-20-m{width:20%}.swagger-ui .w-25-m{width:25%}.swagger-ui .w-30-m{width:30%}.swagger-ui .w-33-m{width:33%}.swagger-ui .w-34-m{width:34%}.swagger-ui .w-40-m{width:40%}.swagger-ui .w-50-m{width:50%}.swagger-ui .w-60-m{width:60%}.swagger-ui .w-70-m{width:70%}.swagger-ui .w-75-m{width:75%}.swagger-ui .w-80-m{width:80%}.swagger-ui .w-90-m{width:90%}.swagger-ui .w-100-m{width:100%}.swagger-ui .w-third-m{width:33.3333333333%}.swagger-ui .w-two-thirds-m{width:66.6666666667%}.swagger-ui .w-auto-m{width:auto}}@media screen and (min-width:60em){.swagger-ui .w1-l{width:1rem}.swagger-ui .w2-l{width:2rem}.swagger-ui .w3-l{width:4rem}.swagger-ui .w4-l{width:8rem}.swagger-ui .w5-l{width:16rem}.swagger-ui .w-10-l{width:10%}.swagger-ui .w-20-l{width:20%}.swagger-ui .w-25-l{width:25%}.swagger-ui .w-30-l{width:30%}.swagger-ui .w-33-l{width:33%}.swagger-ui .w-34-l{width:34%}.swagger-ui .w-40-l{width:40%}.swagger-ui .w-50-l{width:50%}.swagger-ui .w-60-l{width:60%}.swagger-ui .w-70-l{width:70%}.swagger-ui .w-75-l{width:75%}.swagger-ui .w-80-l{width:80%}.swagger-ui .w-90-l{width:90%}.swagger-ui .w-100-l{width:100%}.swagger-ui .w-third-l{width:33.3333333333%}.swagger-ui .w-two-thirds-l{width:66.6666666667%}.swagger-ui .w-auto-l{width:auto}}.swagger-ui .overflow-visible{overflow:visible}.swagger-ui .overflow-hidden{overflow:hidden}.swagger-ui .overflow-scroll{overflow:scroll}.swagger-ui .overflow-auto{overflow:auto}.swagger-ui .overflow-x-visible{overflow-x:visible}.swagger-ui .overflow-x-hidden{overflow-x:hidden}.swagger-ui .overflow-x-scroll{overflow-x:scroll}.swagger-ui .overflow-x-auto{overflow-x:auto}.swagger-ui .overflow-y-visible{overflow-y:visible}.swagger-ui .overflow-y-hidden{overflow-y:hidden}.swagger-ui .overflow-y-scroll{overflow-y:scroll}.swagger-ui .overflow-y-auto{overflow-y:auto}@media screen and (min-width:30em){.swagger-ui .overflow-visible-ns{overflow:visible}.swagger-ui .overflow-hidden-ns{overflow:hidden}.swagger-ui .overflow-scroll-ns{overflow:scroll}.swagger-ui .overflow-auto-ns{overflow:auto}.swagger-ui .overflow-x-visible-ns{overflow-x:visible}.swagger-ui .overflow-x-hidden-ns{overflow-x:hidden}.swagger-ui .overflow-x-scroll-ns{overflow-x:scroll}.swagger-ui .overflow-x-auto-ns{overflow-x:auto}.swagger-ui .overflow-y-visible-ns{overflow-y:visible}.swagger-ui .overflow-y-hidden-ns{overflow-y:hidden}.swagger-ui .overflow-y-scroll-ns{overflow-y:scroll}.swagger-ui .overflow-y-auto-ns{overflow-y:auto}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .overflow-visible-m{overflow:visible}.swagger-ui .overflow-hidden-m{overflow:hidden}.swagger-ui .overflow-scroll-m{overflow:scroll}.swagger-ui .overflow-auto-m{overflow:auto}.swagger-ui .overflow-x-visible-m{overflow-x:visible}.swagger-ui .overflow-x-hidden-m{overflow-x:hidden}.swagger-ui .overflow-x-scroll-m{overflow-x:scroll}.swagger-ui .overflow-x-auto-m{overflow-x:auto}.swagger-ui .overflow-y-visible-m{overflow-y:visible}.swagger-ui .overflow-y-hidden-m{overflow-y:hidden}.swagger-ui .overflow-y-scroll-m{overflow-y:scroll}.swagger-ui .overflow-y-auto-m{overflow-y:auto}}@media screen and (min-width:60em){.swagger-ui .overflow-visible-l{overflow:visible}.swagger-ui .overflow-hidden-l{overflow:hidden}.swagger-ui .overflow-scroll-l{overflow:scroll}.swagger-ui .overflow-auto-l{overflow:auto}.swagger-ui .overflow-x-visible-l{overflow-x:visible}.swagger-ui .overflow-x-hidden-l{overflow-x:hidden}.swagger-ui .overflow-x-scroll-l{overflow-x:scroll}.swagger-ui .overflow-x-auto-l{overflow-x:auto}.swagger-ui .overflow-y-visible-l{overflow-y:visible}.swagger-ui .overflow-y-hidden-l{overflow-y:hidden}.swagger-ui .overflow-y-scroll-l{overflow-y:scroll}.swagger-ui .overflow-y-auto-l{overflow-y:auto}}.swagger-ui .static{position:static}.swagger-ui .relative{position:relative}.swagger-ui .absolute{position:absolute}.swagger-ui .fixed{position:fixed}@media screen and (min-width:30em){.swagger-ui .static-ns{position:static}.swagger-ui .relative-ns{position:relative}.swagger-ui .absolute-ns{position:absolute}.swagger-ui .fixed-ns{position:fixed}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .static-m{position:static}.swagger-ui .relative-m{position:relative}.swagger-ui .absolute-m{position:absolute}.swagger-ui .fixed-m{position:fixed}}@media screen and (min-width:60em){.swagger-ui .static-l{position:static}.swagger-ui .relative-l{position:relative}.swagger-ui .absolute-l{position:absolute}.swagger-ui .fixed-l{position:fixed}}.swagger-ui .o-100{opacity:1}.swagger-ui .o-90{opacity:.9}.swagger-ui .o-80{opacity:.8}.swagger-ui .o-70{opacity:.7}.swagger-ui .o-60{opacity:.6}.swagger-ui .o-50{opacity:.5}.swagger-ui .o-40{opacity:.4}.swagger-ui .o-30{opacity:.3}.swagger-ui .o-20{opacity:.2}.swagger-ui .o-10{opacity:.1}.swagger-ui .o-05{opacity:.05}.swagger-ui .o-025{opacity:.025}.swagger-ui .o-0{opacity:0}.swagger-ui .rotate-45{transform:rotate(45deg)}.swagger-ui .rotate-90{transform:rotate(90deg)}.swagger-ui .rotate-135{transform:rotate(135deg)}.swagger-ui .rotate-180{transform:rotate(180deg)}.swagger-ui .rotate-225{transform:rotate(225deg)}.swagger-ui .rotate-270{transform:rotate(270deg)}.swagger-ui .rotate-315{transform:rotate(315deg)}@media screen and (min-width:30em){.swagger-ui .rotate-45-ns{transform:rotate(45deg)}.swagger-ui .rotate-90-ns{transform:rotate(90deg)}.swagger-ui .rotate-135-ns{transform:rotate(135deg)}.swagger-ui .rotate-180-ns{transform:rotate(180deg)}.swagger-ui .rotate-225-ns{transform:rotate(225deg)}.swagger-ui .rotate-270-ns{transform:rotate(270deg)}.swagger-ui .rotate-315-ns{transform:rotate(315deg)}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .rotate-45-m{transform:rotate(45deg)}.swagger-ui .rotate-90-m{transform:rotate(90deg)}.swagger-ui .rotate-135-m{transform:rotate(135deg)}.swagger-ui .rotate-180-m{transform:rotate(180deg)}.swagger-ui .rotate-225-m{transform:rotate(225deg)}.swagger-ui .rotate-270-m{transform:rotate(270deg)}.swagger-ui .rotate-315-m{transform:rotate(315deg)}}@media screen and (min-width:60em){.swagger-ui .rotate-45-l{transform:rotate(45deg)}.swagger-ui .rotate-90-l{transform:rotate(90deg)}.swagger-ui .rotate-135-l{transform:rotate(135deg)}.swagger-ui .rotate-180-l{transform:rotate(180deg)}.swagger-ui .rotate-225-l{transform:rotate(225deg)}.swagger-ui .rotate-270-l{transform:rotate(270deg)}.swagger-ui .rotate-315-l{transform:rotate(315deg)}}.swagger-ui .black-90{color:#000000e6}.swagger-ui .black-80{color:#000c}.swagger-ui .black-70{color:#000000b3}.swagger-ui .black-60{color:#0009}.swagger-ui .black-50{color:#00000080}.swagger-ui .black-40{color:#0006}.swagger-ui .black-30{color:#0000004d}.swagger-ui .black-20{color:#0003}.swagger-ui .black-10{color:#0000001a}.swagger-ui .black-05{color:#0000000d}.swagger-ui .white-90{color:#ffffffe6}.swagger-ui .white-80{color:#fffc}.swagger-ui .white-70{color:#ffffffb3}.swagger-ui .white-60{color:#fff9}.swagger-ui .white-50{color:#ffffff80}.swagger-ui .white-40{color:#fff6}.swagger-ui .white-30{color:#ffffff4d}.swagger-ui .white-20{color:#fff3}.swagger-ui .white-10{color:#ffffff1a}.swagger-ui .black{color:#000}.swagger-ui .near-black{color:#111}.swagger-ui .dark-gray{color:#333}.swagger-ui .mid-gray{color:#555}.swagger-ui .gray{color:#777}.swagger-ui .silver{color:#999}.swagger-ui .light-silver{color:#aaa}.swagger-ui .moon-gray{color:#ccc}.swagger-ui .light-gray{color:#eee}.swagger-ui .near-white{color:#f4f4f4}.swagger-ui .white{color:#fff}.swagger-ui .dark-red{color:#e7040f}.swagger-ui .red{color:#ff4136}.swagger-ui .light-red{color:#ff725c}.swagger-ui .orange{color:#ff6300}.swagger-ui .gold{color:#ffb700}.swagger-ui .yellow{color:gold}.swagger-ui .light-yellow{color:#fbf1a9}.swagger-ui .purple{color:#5e2ca5}.swagger-ui .light-purple{color:#a463f2}.swagger-ui .dark-pink{color:#d5008f}.swagger-ui .hot-pink{color:#ff41b4}.swagger-ui .pink{color:#ff80cc}.swagger-ui .light-pink{color:#ffa3d7}.swagger-ui .dark-green{color:#137752}.swagger-ui .green{color:#19a974}.swagger-ui .light-green{color:#9eebcf}.swagger-ui .navy{color:#001b44}.swagger-ui .dark-blue{color:#00449e}.swagger-ui .blue{color:#357edd}.swagger-ui .light-blue{color:#96ccff}.swagger-ui .lightest-blue{color:#cdecff}.swagger-ui .washed-blue{color:#f6fffe}.swagger-ui .washed-green{color:#e8fdf5}.swagger-ui .washed-yellow{color:#fffceb}.swagger-ui .washed-red{color:#ffdfdf}.swagger-ui .color-inherit{color:inherit}.swagger-ui .bg-black-90{background-color:#000000e6}.swagger-ui .bg-black-80{background-color:#000c}.swagger-ui .bg-black-70{background-color:#000000b3}.swagger-ui .bg-black-60{background-color:#0009}.swagger-ui .bg-black-50{background-color:#00000080}.swagger-ui .bg-black-40{background-color:#0006}.swagger-ui .bg-black-30{background-color:#0000004d}.swagger-ui .bg-black-20{background-color:#0003}.swagger-ui .bg-black-10{background-color:#0000001a}.swagger-ui .bg-black-05{background-color:#0000000d}.swagger-ui .bg-white-90{background-color:#ffffffe6}.swagger-ui .bg-white-80{background-color:#fffc}.swagger-ui .bg-white-70{background-color:#ffffffb3}.swagger-ui .bg-white-60{background-color:#fff9}.swagger-ui .bg-white-50{background-color:#ffffff80}.swagger-ui .bg-white-40{background-color:#fff6}.swagger-ui .bg-white-30{background-color:#ffffff4d}.swagger-ui .bg-white-20{background-color:#fff3}.swagger-ui .bg-white-10{background-color:#ffffff1a}.swagger-ui .bg-black{background-color:#000}.swagger-ui .bg-near-black{background-color:#111}.swagger-ui .bg-dark-gray{background-color:#333}.swagger-ui .bg-mid-gray{background-color:#555}.swagger-ui .bg-gray{background-color:#777}.swagger-ui .bg-silver{background-color:#999}.swagger-ui .bg-light-silver{background-color:#aaa}.swagger-ui .bg-moon-gray{background-color:#ccc}.swagger-ui .bg-light-gray{background-color:#eee}.swagger-ui .bg-near-white{background-color:#f4f4f4}.swagger-ui .bg-white{background-color:#fff}.swagger-ui .bg-transparent{background-color:transparent}.swagger-ui .bg-dark-red{background-color:#e7040f}.swagger-ui .bg-red{background-color:#ff4136}.swagger-ui .bg-light-red{background-color:#ff725c}.swagger-ui .bg-orange{background-color:#ff6300}.swagger-ui .bg-gold{background-color:#ffb700}.swagger-ui .bg-yellow{background-color:gold}.swagger-ui .bg-light-yellow{background-color:#fbf1a9}.swagger-ui .bg-purple{background-color:#5e2ca5}.swagger-ui .bg-light-purple{background-color:#a463f2}.swagger-ui .bg-dark-pink{background-color:#d5008f}.swagger-ui .bg-hot-pink{background-color:#ff41b4}.swagger-ui .bg-pink{background-color:#ff80cc}.swagger-ui .bg-light-pink{background-color:#ffa3d7}.swagger-ui .bg-dark-green{background-color:#137752}.swagger-ui .bg-green{background-color:#19a974}.swagger-ui .bg-light-green{background-color:#9eebcf}.swagger-ui .bg-navy{background-color:#001b44}.swagger-ui .bg-dark-blue{background-color:#00449e}.swagger-ui .bg-blue{background-color:#357edd}.swagger-ui .bg-light-blue{background-color:#96ccff}.swagger-ui .bg-lightest-blue{background-color:#cdecff}.swagger-ui .bg-washed-blue{background-color:#f6fffe}.swagger-ui .bg-washed-green{background-color:#e8fdf5}.swagger-ui .bg-washed-yellow{background-color:#fffceb}.swagger-ui .bg-washed-red{background-color:#ffdfdf}.swagger-ui .bg-inherit{background-color:inherit}.swagger-ui .hover-black:focus,.swagger-ui .hover-black:hover{color:#000}.swagger-ui .hover-near-black:focus,.swagger-ui .hover-near-black:hover{color:#111}.swagger-ui .hover-dark-gray:focus,.swagger-ui .hover-dark-gray:hover{color:#333}.swagger-ui .hover-mid-gray:focus,.swagger-ui .hover-mid-gray:hover{color:#555}.swagger-ui .hover-gray:focus,.swagger-ui .hover-gray:hover{color:#777}.swagger-ui .hover-silver:focus,.swagger-ui .hover-silver:hover{color:#999}.swagger-ui .hover-light-silver:focus,.swagger-ui .hover-light-silver:hover{color:#aaa}.swagger-ui .hover-moon-gray:focus,.swagger-ui .hover-moon-gray:hover{color:#ccc}.swagger-ui .hover-light-gray:focus,.swagger-ui .hover-light-gray:hover{color:#eee}.swagger-ui .hover-near-white:focus,.swagger-ui .hover-near-white:hover{color:#f4f4f4}.swagger-ui .hover-white:focus,.swagger-ui .hover-white:hover{color:#fff}.swagger-ui .hover-black-90:focus,.swagger-ui .hover-black-90:hover{color:#000000e6}.swagger-ui .hover-black-80:focus,.swagger-ui .hover-black-80:hover{color:#000c}.swagger-ui .hover-black-70:focus,.swagger-ui .hover-black-70:hover{color:#000000b3}.swagger-ui .hover-black-60:focus,.swagger-ui .hover-black-60:hover{color:#0009}.swagger-ui .hover-black-50:focus,.swagger-ui .hover-black-50:hover{color:#00000080}.swagger-ui .hover-black-40:focus,.swagger-ui .hover-black-40:hover{color:#0006}.swagger-ui .hover-black-30:focus,.swagger-ui .hover-black-30:hover{color:#0000004d}.swagger-ui .hover-black-20:focus,.swagger-ui .hover-black-20:hover{color:#0003}.swagger-ui .hover-black-10:focus,.swagger-ui .hover-black-10:hover{color:#0000001a}.swagger-ui .hover-white-90:focus,.swagger-ui .hover-white-90:hover{color:#ffffffe6}.swagger-ui .hover-white-80:focus,.swagger-ui .hover-white-80:hover{color:#fffc}.swagger-ui .hover-white-70:focus,.swagger-ui .hover-white-70:hover{color:#ffffffb3}.swagger-ui .hover-white-60:focus,.swagger-ui .hover-white-60:hover{color:#fff9}.swagger-ui .hover-white-50:focus,.swagger-ui .hover-white-50:hover{color:#ffffff80}.swagger-ui .hover-white-40:focus,.swagger-ui .hover-white-40:hover{color:#fff6}.swagger-ui .hover-white-30:focus,.swagger-ui .hover-white-30:hover{color:#ffffff4d}.swagger-ui .hover-white-20:focus,.swagger-ui .hover-white-20:hover{color:#fff3}.swagger-ui .hover-white-10:focus,.swagger-ui .hover-white-10:hover{color:#ffffff1a}.swagger-ui .hover-inherit:focus,.swagger-ui .hover-inherit:hover{color:inherit}.swagger-ui .hover-bg-black:focus,.swagger-ui .hover-bg-black:hover{background-color:#000}.swagger-ui .hover-bg-near-black:focus,.swagger-ui .hover-bg-near-black:hover{background-color:#111}.swagger-ui .hover-bg-dark-gray:focus,.swagger-ui .hover-bg-dark-gray:hover{background-color:#333}.swagger-ui .hover-bg-mid-gray:focus,.swagger-ui .hover-bg-mid-gray:hover{background-color:#555}.swagger-ui .hover-bg-gray:focus,.swagger-ui .hover-bg-gray:hover{background-color:#777}.swagger-ui .hover-bg-silver:focus,.swagger-ui .hover-bg-silver:hover{background-color:#999}.swagger-ui .hover-bg-light-silver:focus,.swagger-ui .hover-bg-light-silver:hover{background-color:#aaa}.swagger-ui .hover-bg-moon-gray:focus,.swagger-ui .hover-bg-moon-gray:hover{background-color:#ccc}.swagger-ui .hover-bg-light-gray:focus,.swagger-ui .hover-bg-light-gray:hover{background-color:#eee}.swagger-ui .hover-bg-near-white:focus,.swagger-ui .hover-bg-near-white:hover{background-color:#f4f4f4}.swagger-ui .hover-bg-white:focus,.swagger-ui .hover-bg-white:hover{background-color:#fff}.swagger-ui .hover-bg-transparent:focus,.swagger-ui .hover-bg-transparent:hover{background-color:transparent}.swagger-ui .hover-bg-black-90:focus,.swagger-ui .hover-bg-black-90:hover{background-color:#000000e6}.swagger-ui .hover-bg-black-80:focus,.swagger-ui .hover-bg-black-80:hover{background-color:#000c}.swagger-ui .hover-bg-black-70:focus,.swagger-ui .hover-bg-black-70:hover{background-color:#000000b3}.swagger-ui .hover-bg-black-60:focus,.swagger-ui .hover-bg-black-60:hover{background-color:#0009}.swagger-ui .hover-bg-black-50:focus,.swagger-ui .hover-bg-black-50:hover{background-color:#00000080}.swagger-ui .hover-bg-black-40:focus,.swagger-ui .hover-bg-black-40:hover{background-color:#0006}.swagger-ui .hover-bg-black-30:focus,.swagger-ui .hover-bg-black-30:hover{background-color:#0000004d}.swagger-ui .hover-bg-black-20:focus,.swagger-ui .hover-bg-black-20:hover{background-color:#0003}.swagger-ui .hover-bg-black-10:focus,.swagger-ui .hover-bg-black-10:hover{background-color:#0000001a}.swagger-ui .hover-bg-white-90:focus,.swagger-ui .hover-bg-white-90:hover{background-color:#ffffffe6}.swagger-ui .hover-bg-white-80:focus,.swagger-ui .hover-bg-white-80:hover{background-color:#fffc}.swagger-ui .hover-bg-white-70:focus,.swagger-ui .hover-bg-white-70:hover{background-color:#ffffffb3}.swagger-ui .hover-bg-white-60:focus,.swagger-ui .hover-bg-white-60:hover{background-color:#fff9}.swagger-ui .hover-bg-white-50:focus,.swagger-ui .hover-bg-white-50:hover{background-color:#ffffff80}.swagger-ui .hover-bg-white-40:focus,.swagger-ui .hover-bg-white-40:hover{background-color:#fff6}.swagger-ui .hover-bg-white-30:focus,.swagger-ui .hover-bg-white-30:hover{background-color:#ffffff4d}.swagger-ui .hover-bg-white-20:focus,.swagger-ui .hover-bg-white-20:hover{background-color:#fff3}.swagger-ui .hover-bg-white-10:focus,.swagger-ui .hover-bg-white-10:hover{background-color:#ffffff1a}.swagger-ui .hover-dark-red:focus,.swagger-ui .hover-dark-red:hover{color:#e7040f}.swagger-ui .hover-red:focus,.swagger-ui .hover-red:hover{color:#ff4136}.swagger-ui .hover-light-red:focus,.swagger-ui .hover-light-red:hover{color:#ff725c}.swagger-ui .hover-orange:focus,.swagger-ui .hover-orange:hover{color:#ff6300}.swagger-ui .hover-gold:focus,.swagger-ui .hover-gold:hover{color:#ffb700}.swagger-ui .hover-yellow:focus,.swagger-ui .hover-yellow:hover{color:gold}.swagger-ui .hover-light-yellow:focus,.swagger-ui .hover-light-yellow:hover{color:#fbf1a9}.swagger-ui .hover-purple:focus,.swagger-ui .hover-purple:hover{color:#5e2ca5}.swagger-ui .hover-light-purple:focus,.swagger-ui .hover-light-purple:hover{color:#a463f2}.swagger-ui .hover-dark-pink:focus,.swagger-ui .hover-dark-pink:hover{color:#d5008f}.swagger-ui .hover-hot-pink:focus,.swagger-ui .hover-hot-pink:hover{color:#ff41b4}.swagger-ui .hover-pink:focus,.swagger-ui .hover-pink:hover{color:#ff80cc}.swagger-ui .hover-light-pink:focus,.swagger-ui .hover-light-pink:hover{color:#ffa3d7}.swagger-ui .hover-dark-green:focus,.swagger-ui .hover-dark-green:hover{color:#137752}.swagger-ui .hover-green:focus,.swagger-ui .hover-green:hover{color:#19a974}.swagger-ui .hover-light-green:focus,.swagger-ui .hover-light-green:hover{color:#9eebcf}.swagger-ui .hover-navy:focus,.swagger-ui .hover-navy:hover{color:#001b44}.swagger-ui .hover-dark-blue:focus,.swagger-ui .hover-dark-blue:hover{color:#00449e}.swagger-ui .hover-blue:focus,.swagger-ui .hover-blue:hover{color:#357edd}.swagger-ui .hover-light-blue:focus,.swagger-ui .hover-light-blue:hover{color:#96ccff}.swagger-ui .hover-lightest-blue:focus,.swagger-ui .hover-lightest-blue:hover{color:#cdecff}.swagger-ui .hover-washed-blue:focus,.swagger-ui .hover-washed-blue:hover{color:#f6fffe}.swagger-ui .hover-washed-green:focus,.swagger-ui .hover-washed-green:hover{color:#e8fdf5}.swagger-ui .hover-washed-yellow:focus,.swagger-ui .hover-washed-yellow:hover{color:#fffceb}.swagger-ui .hover-washed-red:focus,.swagger-ui .hover-washed-red:hover{color:#ffdfdf}.swagger-ui .hover-bg-dark-red:focus,.swagger-ui .hover-bg-dark-red:hover{background-color:#e7040f}.swagger-ui .hover-bg-red:focus,.swagger-ui .hover-bg-red:hover{background-color:#ff4136}.swagger-ui .hover-bg-light-red:focus,.swagger-ui .hover-bg-light-red:hover{background-color:#ff725c}.swagger-ui .hover-bg-orange:focus,.swagger-ui .hover-bg-orange:hover{background-color:#ff6300}.swagger-ui .hover-bg-gold:focus,.swagger-ui .hover-bg-gold:hover{background-color:#ffb700}.swagger-ui .hover-bg-yellow:focus,.swagger-ui .hover-bg-yellow:hover{background-color:gold}.swagger-ui .hover-bg-light-yellow:focus,.swagger-ui .hover-bg-light-yellow:hover{background-color:#fbf1a9}.swagger-ui .hover-bg-purple:focus,.swagger-ui .hover-bg-purple:hover{background-color:#5e2ca5}.swagger-ui .hover-bg-light-purple:focus,.swagger-ui .hover-bg-light-purple:hover{background-color:#a463f2}.swagger-ui .hover-bg-dark-pink:focus,.swagger-ui .hover-bg-dark-pink:hover{background-color:#d5008f}.swagger-ui .hover-bg-hot-pink:focus,.swagger-ui .hover-bg-hot-pink:hover{background-color:#ff41b4}.swagger-ui .hover-bg-pink:focus,.swagger-ui .hover-bg-pink:hover{background-color:#ff80cc}.swagger-ui .hover-bg-light-pink:focus,.swagger-ui .hover-bg-light-pink:hover{background-color:#ffa3d7}.swagger-ui .hover-bg-dark-green:focus,.swagger-ui .hover-bg-dark-green:hover{background-color:#137752}.swagger-ui .hover-bg-green:focus,.swagger-ui .hover-bg-green:hover{background-color:#19a974}.swagger-ui .hover-bg-light-green:focus,.swagger-ui .hover-bg-light-green:hover{background-color:#9eebcf}.swagger-ui .hover-bg-navy:focus,.swagger-ui .hover-bg-navy:hover{background-color:#001b44}.swagger-ui .hover-bg-dark-blue:focus,.swagger-ui .hover-bg-dark-blue:hover{background-color:#00449e}.swagger-ui .hover-bg-blue:focus,.swagger-ui .hover-bg-blue:hover{background-color:#357edd}.swagger-ui .hover-bg-light-blue:focus,.swagger-ui .hover-bg-light-blue:hover{background-color:#96ccff}.swagger-ui .hover-bg-lightest-blue:focus,.swagger-ui .hover-bg-lightest-blue:hover{background-color:#cdecff}.swagger-ui .hover-bg-washed-blue:focus,.swagger-ui .hover-bg-washed-blue:hover{background-color:#f6fffe}.swagger-ui .hover-bg-washed-green:focus,.swagger-ui .hover-bg-washed-green:hover{background-color:#e8fdf5}.swagger-ui .hover-bg-washed-yellow:focus,.swagger-ui .hover-bg-washed-yellow:hover{background-color:#fffceb}.swagger-ui .hover-bg-washed-red:focus,.swagger-ui .hover-bg-washed-red:hover{background-color:#ffdfdf}.swagger-ui .hover-bg-inherit:focus,.swagger-ui .hover-bg-inherit:hover{background-color:inherit}.swagger-ui .pa0{padding:0}.swagger-ui .pa1{padding:.25rem}.swagger-ui .pa2{padding:.5rem}.swagger-ui .pa3{padding:1rem}.swagger-ui .pa4{padding:2rem}.swagger-ui .pa5{padding:4rem}.swagger-ui .pa6{padding:8rem}.swagger-ui .pa7{padding:16rem}.swagger-ui .pl0{padding-left:0}.swagger-ui .pl1{padding-left:.25rem}.swagger-ui .pl2{padding-left:.5rem}.swagger-ui .pl3{padding-left:1rem}.swagger-ui .pl4{padding-left:2rem}.swagger-ui .pl5{padding-left:4rem}.swagger-ui .pl6{padding-left:8rem}.swagger-ui .pl7{padding-left:16rem}.swagger-ui .pr0{padding-right:0}.swagger-ui .pr1{padding-right:.25rem}.swagger-ui .pr2{padding-right:.5rem}.swagger-ui .pr3{padding-right:1rem}.swagger-ui .pr4{padding-right:2rem}.swagger-ui .pr5{padding-right:4rem}.swagger-ui .pr6{padding-right:8rem}.swagger-ui .pr7{padding-right:16rem}.swagger-ui .pb0{padding-bottom:0}.swagger-ui .pb1{padding-bottom:.25rem}.swagger-ui .pb2{padding-bottom:.5rem}.swagger-ui .pb3{padding-bottom:1rem}.swagger-ui .pb4{padding-bottom:2rem}.swagger-ui .pb5{padding-bottom:4rem}.swagger-ui .pb6{padding-bottom:8rem}.swagger-ui .pb7{padding-bottom:16rem}.swagger-ui .pt0{padding-top:0}.swagger-ui .pt1{padding-top:.25rem}.swagger-ui .pt2{padding-top:.5rem}.swagger-ui .pt3{padding-top:1rem}.swagger-ui .pt4{padding-top:2rem}.swagger-ui .pt5{padding-top:4rem}.swagger-ui .pt6{padding-top:8rem}.swagger-ui .pt7{padding-top:16rem}.swagger-ui .pv0{padding-bottom:0;padding-top:0}.swagger-ui .pv1{padding-bottom:.25rem;padding-top:.25rem}.swagger-ui .pv2{padding-bottom:.5rem;padding-top:.5rem}.swagger-ui .pv3{padding-bottom:1rem;padding-top:1rem}.swagger-ui .pv4{padding-bottom:2rem;padding-top:2rem}.swagger-ui .pv5{padding-bottom:4rem;padding-top:4rem}.swagger-ui .pv6{padding-bottom:8rem;padding-top:8rem}.swagger-ui .pv7{padding-bottom:16rem;padding-top:16rem}.swagger-ui .ph0{padding-left:0;padding-right:0}.swagger-ui .ph1{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0{margin:0}.swagger-ui .ma1{margin:.25rem}.swagger-ui .ma2{margin:.5rem}.swagger-ui .ma3{margin:1rem}.swagger-ui .ma4{margin:2rem}.swagger-ui .ma5{margin:4rem}.swagger-ui .ma6{margin:8rem}.swagger-ui .ma7{margin:16rem}.swagger-ui .ml0{margin-left:0}.swagger-ui .ml1{margin-left:.25rem}.swagger-ui .ml2{margin-left:.5rem}.swagger-ui .ml3{margin-left:1rem}.swagger-ui .ml4{margin-left:2rem}.swagger-ui .ml5{margin-left:4rem}.swagger-ui .ml6{margin-left:8rem}.swagger-ui .ml7{margin-left:16rem}.swagger-ui .mr0{margin-right:0}.swagger-ui .mr1{margin-right:.25rem}.swagger-ui .mr2{margin-right:.5rem}.swagger-ui .mr3{margin-right:1rem}.swagger-ui .mr4{margin-right:2rem}.swagger-ui .mr5{margin-right:4rem}.swagger-ui .mr6{margin-right:8rem}.swagger-ui .mr7{margin-right:16rem}.swagger-ui .mb0{margin-bottom:0}.swagger-ui .mb1{margin-bottom:.25rem}.swagger-ui .mb2{margin-bottom:.5rem}.swagger-ui .mb3{margin-bottom:1rem}.swagger-ui .mb4{margin-bottom:2rem}.swagger-ui .mb5{margin-bottom:4rem}.swagger-ui .mb6{margin-bottom:8rem}.swagger-ui .mb7{margin-bottom:16rem}.swagger-ui .mt0{margin-top:0}.swagger-ui .mt1{margin-top:.25rem}.swagger-ui .mt2{margin-top:.5rem}.swagger-ui .mt3{margin-top:1rem}.swagger-ui .mt4{margin-top:2rem}.swagger-ui .mt5{margin-top:4rem}.swagger-ui .mt6{margin-top:8rem}.swagger-ui .mt7{margin-top:16rem}.swagger-ui .mv0{margin-bottom:0;margin-top:0}.swagger-ui .mv1{margin-bottom:.25rem;margin-top:.25rem}.swagger-ui .mv2{margin-bottom:.5rem;margin-top:.5rem}.swagger-ui .mv3{margin-bottom:1rem;margin-top:1rem}.swagger-ui .mv4{margin-bottom:2rem;margin-top:2rem}.swagger-ui .mv5{margin-bottom:4rem;margin-top:4rem}.swagger-ui .mv6{margin-bottom:8rem;margin-top:8rem}.swagger-ui .mv7{margin-bottom:16rem;margin-top:16rem}.swagger-ui .mh0{margin-left:0;margin-right:0}.swagger-ui .mh1{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7{margin-left:16rem;margin-right:16rem}@media screen and (min-width:30em){.swagger-ui .pa0-ns{padding:0}.swagger-ui .pa1-ns{padding:.25rem}.swagger-ui .pa2-ns{padding:.5rem}.swagger-ui .pa3-ns{padding:1rem}.swagger-ui .pa4-ns{padding:2rem}.swagger-ui .pa5-ns{padding:4rem}.swagger-ui .pa6-ns{padding:8rem}.swagger-ui .pa7-ns{padding:16rem}.swagger-ui .pl0-ns{padding-left:0}.swagger-ui .pl1-ns{padding-left:.25rem}.swagger-ui .pl2-ns{padding-left:.5rem}.swagger-ui .pl3-ns{padding-left:1rem}.swagger-ui .pl4-ns{padding-left:2rem}.swagger-ui .pl5-ns{padding-left:4rem}.swagger-ui .pl6-ns{padding-left:8rem}.swagger-ui .pl7-ns{padding-left:16rem}.swagger-ui .pr0-ns{padding-right:0}.swagger-ui .pr1-ns{padding-right:.25rem}.swagger-ui .pr2-ns{padding-right:.5rem}.swagger-ui .pr3-ns{padding-right:1rem}.swagger-ui .pr4-ns{padding-right:2rem}.swagger-ui .pr5-ns{padding-right:4rem}.swagger-ui .pr6-ns{padding-right:8rem}.swagger-ui .pr7-ns{padding-right:16rem}.swagger-ui .pb0-ns{padding-bottom:0}.swagger-ui .pb1-ns{padding-bottom:.25rem}.swagger-ui .pb2-ns{padding-bottom:.5rem}.swagger-ui .pb3-ns{padding-bottom:1rem}.swagger-ui .pb4-ns{padding-bottom:2rem}.swagger-ui .pb5-ns{padding-bottom:4rem}.swagger-ui .pb6-ns{padding-bottom:8rem}.swagger-ui .pb7-ns{padding-bottom:16rem}.swagger-ui .pt0-ns{padding-top:0}.swagger-ui .pt1-ns{padding-top:.25rem}.swagger-ui .pt2-ns{padding-top:.5rem}.swagger-ui .pt3-ns{padding-top:1rem}.swagger-ui .pt4-ns{padding-top:2rem}.swagger-ui .pt5-ns{padding-top:4rem}.swagger-ui .pt6-ns{padding-top:8rem}.swagger-ui .pt7-ns{padding-top:16rem}.swagger-ui .pv0-ns{padding-bottom:0;padding-top:0}.swagger-ui .pv1-ns{padding-bottom:.25rem;padding-top:.25rem}.swagger-ui .pv2-ns{padding-bottom:.5rem;padding-top:.5rem}.swagger-ui .pv3-ns{padding-bottom:1rem;padding-top:1rem}.swagger-ui .pv4-ns{padding-bottom:2rem;padding-top:2rem}.swagger-ui .pv5-ns{padding-bottom:4rem;padding-top:4rem}.swagger-ui .pv6-ns{padding-bottom:8rem;padding-top:8rem}.swagger-ui .pv7-ns{padding-bottom:16rem;padding-top:16rem}.swagger-ui .ph0-ns{padding-left:0;padding-right:0}.swagger-ui .ph1-ns{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2-ns{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3-ns{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4-ns{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5-ns{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6-ns{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7-ns{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0-ns{margin:0}.swagger-ui .ma1-ns{margin:.25rem}.swagger-ui .ma2-ns{margin:.5rem}.swagger-ui .ma3-ns{margin:1rem}.swagger-ui .ma4-ns{margin:2rem}.swagger-ui .ma5-ns{margin:4rem}.swagger-ui .ma6-ns{margin:8rem}.swagger-ui .ma7-ns{margin:16rem}.swagger-ui .ml0-ns{margin-left:0}.swagger-ui .ml1-ns{margin-left:.25rem}.swagger-ui .ml2-ns{margin-left:.5rem}.swagger-ui .ml3-ns{margin-left:1rem}.swagger-ui .ml4-ns{margin-left:2rem}.swagger-ui .ml5-ns{margin-left:4rem}.swagger-ui .ml6-ns{margin-left:8rem}.swagger-ui .ml7-ns{margin-left:16rem}.swagger-ui .mr0-ns{margin-right:0}.swagger-ui .mr1-ns{margin-right:.25rem}.swagger-ui .mr2-ns{margin-right:.5rem}.swagger-ui .mr3-ns{margin-right:1rem}.swagger-ui .mr4-ns{margin-right:2rem}.swagger-ui .mr5-ns{margin-right:4rem}.swagger-ui .mr6-ns{margin-right:8rem}.swagger-ui .mr7-ns{margin-right:16rem}.swagger-ui .mb0-ns{margin-bottom:0}.swagger-ui .mb1-ns{margin-bottom:.25rem}.swagger-ui .mb2-ns{margin-bottom:.5rem}.swagger-ui .mb3-ns{margin-bottom:1rem}.swagger-ui .mb4-ns{margin-bottom:2rem}.swagger-ui .mb5-ns{margin-bottom:4rem}.swagger-ui .mb6-ns{margin-bottom:8rem}.swagger-ui .mb7-ns{margin-bottom:16rem}.swagger-ui .mt0-ns{margin-top:0}.swagger-ui .mt1-ns{margin-top:.25rem}.swagger-ui .mt2-ns{margin-top:.5rem}.swagger-ui .mt3-ns{margin-top:1rem}.swagger-ui .mt4-ns{margin-top:2rem}.swagger-ui .mt5-ns{margin-top:4rem}.swagger-ui .mt6-ns{margin-top:8rem}.swagger-ui .mt7-ns{margin-top:16rem}.swagger-ui .mv0-ns{margin-bottom:0;margin-top:0}.swagger-ui .mv1-ns{margin-bottom:.25rem;margin-top:.25rem}.swagger-ui .mv2-ns{margin-bottom:.5rem;margin-top:.5rem}.swagger-ui .mv3-ns{margin-bottom:1rem;margin-top:1rem}.swagger-ui .mv4-ns{margin-bottom:2rem;margin-top:2rem}.swagger-ui .mv5-ns{margin-bottom:4rem;margin-top:4rem}.swagger-ui .mv6-ns{margin-bottom:8rem;margin-top:8rem}.swagger-ui .mv7-ns{margin-bottom:16rem;margin-top:16rem}.swagger-ui .mh0-ns{margin-left:0;margin-right:0}.swagger-ui .mh1-ns{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2-ns{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3-ns{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4-ns{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5-ns{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6-ns{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7-ns{margin-left:16rem;margin-right:16rem}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .pa0-m{padding:0}.swagger-ui .pa1-m{padding:.25rem}.swagger-ui .pa2-m{padding:.5rem}.swagger-ui .pa3-m{padding:1rem}.swagger-ui .pa4-m{padding:2rem}.swagger-ui .pa5-m{padding:4rem}.swagger-ui .pa6-m{padding:8rem}.swagger-ui .pa7-m{padding:16rem}.swagger-ui .pl0-m{padding-left:0}.swagger-ui .pl1-m{padding-left:.25rem}.swagger-ui .pl2-m{padding-left:.5rem}.swagger-ui .pl3-m{padding-left:1rem}.swagger-ui .pl4-m{padding-left:2rem}.swagger-ui .pl5-m{padding-left:4rem}.swagger-ui .pl6-m{padding-left:8rem}.swagger-ui .pl7-m{padding-left:16rem}.swagger-ui .pr0-m{padding-right:0}.swagger-ui .pr1-m{padding-right:.25rem}.swagger-ui .pr2-m{padding-right:.5rem}.swagger-ui .pr3-m{padding-right:1rem}.swagger-ui .pr4-m{padding-right:2rem}.swagger-ui .pr5-m{padding-right:4rem}.swagger-ui .pr6-m{padding-right:8rem}.swagger-ui .pr7-m{padding-right:16rem}.swagger-ui .pb0-m{padding-bottom:0}.swagger-ui .pb1-m{padding-bottom:.25rem}.swagger-ui .pb2-m{padding-bottom:.5rem}.swagger-ui .pb3-m{padding-bottom:1rem}.swagger-ui .pb4-m{padding-bottom:2rem}.swagger-ui .pb5-m{padding-bottom:4rem}.swagger-ui .pb6-m{padding-bottom:8rem}.swagger-ui .pb7-m{padding-bottom:16rem}.swagger-ui .pt0-m{padding-top:0}.swagger-ui .pt1-m{padding-top:.25rem}.swagger-ui .pt2-m{padding-top:.5rem}.swagger-ui .pt3-m{padding-top:1rem}.swagger-ui .pt4-m{padding-top:2rem}.swagger-ui .pt5-m{padding-top:4rem}.swagger-ui .pt6-m{padding-top:8rem}.swagger-ui .pt7-m{padding-top:16rem}.swagger-ui .pv0-m{padding-bottom:0;padding-top:0}.swagger-ui .pv1-m{padding-bottom:.25rem;padding-top:.25rem}.swagger-ui .pv2-m{padding-bottom:.5rem;padding-top:.5rem}.swagger-ui .pv3-m{padding-bottom:1rem;padding-top:1rem}.swagger-ui .pv4-m{padding-bottom:2rem;padding-top:2rem}.swagger-ui .pv5-m{padding-bottom:4rem;padding-top:4rem}.swagger-ui .pv6-m{padding-bottom:8rem;padding-top:8rem}.swagger-ui .pv7-m{padding-bottom:16rem;padding-top:16rem}.swagger-ui .ph0-m{padding-left:0;padding-right:0}.swagger-ui .ph1-m{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2-m{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3-m{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4-m{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5-m{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6-m{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7-m{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0-m{margin:0}.swagger-ui .ma1-m{margin:.25rem}.swagger-ui .ma2-m{margin:.5rem}.swagger-ui .ma3-m{margin:1rem}.swagger-ui .ma4-m{margin:2rem}.swagger-ui .ma5-m{margin:4rem}.swagger-ui .ma6-m{margin:8rem}.swagger-ui .ma7-m{margin:16rem}.swagger-ui .ml0-m{margin-left:0}.swagger-ui .ml1-m{margin-left:.25rem}.swagger-ui .ml2-m{margin-left:.5rem}.swagger-ui .ml3-m{margin-left:1rem}.swagger-ui .ml4-m{margin-left:2rem}.swagger-ui .ml5-m{margin-left:4rem}.swagger-ui .ml6-m{margin-left:8rem}.swagger-ui .ml7-m{margin-left:16rem}.swagger-ui .mr0-m{margin-right:0}.swagger-ui .mr1-m{margin-right:.25rem}.swagger-ui .mr2-m{margin-right:.5rem}.swagger-ui .mr3-m{margin-right:1rem}.swagger-ui .mr4-m{margin-right:2rem}.swagger-ui .mr5-m{margin-right:4rem}.swagger-ui .mr6-m{margin-right:8rem}.swagger-ui .mr7-m{margin-right:16rem}.swagger-ui .mb0-m{margin-bottom:0}.swagger-ui .mb1-m{margin-bottom:.25rem}.swagger-ui .mb2-m{margin-bottom:.5rem}.swagger-ui .mb3-m{margin-bottom:1rem}.swagger-ui .mb4-m{margin-bottom:2rem}.swagger-ui .mb5-m{margin-bottom:4rem}.swagger-ui .mb6-m{margin-bottom:8rem}.swagger-ui .mb7-m{margin-bottom:16rem}.swagger-ui .mt0-m{margin-top:0}.swagger-ui .mt1-m{margin-top:.25rem}.swagger-ui .mt2-m{margin-top:.5rem}.swagger-ui .mt3-m{margin-top:1rem}.swagger-ui .mt4-m{margin-top:2rem}.swagger-ui .mt5-m{margin-top:4rem}.swagger-ui .mt6-m{margin-top:8rem}.swagger-ui .mt7-m{margin-top:16rem}.swagger-ui .mv0-m{margin-bottom:0;margin-top:0}.swagger-ui .mv1-m{margin-bottom:.25rem;margin-top:.25rem}.swagger-ui .mv2-m{margin-bottom:.5rem;margin-top:.5rem}.swagger-ui .mv3-m{margin-bottom:1rem;margin-top:1rem}.swagger-ui .mv4-m{margin-bottom:2rem;margin-top:2rem}.swagger-ui .mv5-m{margin-bottom:4rem;margin-top:4rem}.swagger-ui .mv6-m{margin-bottom:8rem;margin-top:8rem}.swagger-ui .mv7-m{margin-bottom:16rem;margin-top:16rem}.swagger-ui .mh0-m{margin-left:0;margin-right:0}.swagger-ui .mh1-m{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2-m{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3-m{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4-m{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5-m{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6-m{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7-m{margin-left:16rem;margin-right:16rem}}@media screen and (min-width:60em){.swagger-ui .pa0-l{padding:0}.swagger-ui .pa1-l{padding:.25rem}.swagger-ui .pa2-l{padding:.5rem}.swagger-ui .pa3-l{padding:1rem}.swagger-ui .pa4-l{padding:2rem}.swagger-ui .pa5-l{padding:4rem}.swagger-ui .pa6-l{padding:8rem}.swagger-ui .pa7-l{padding:16rem}.swagger-ui .pl0-l{padding-left:0}.swagger-ui .pl1-l{padding-left:.25rem}.swagger-ui .pl2-l{padding-left:.5rem}.swagger-ui .pl3-l{padding-left:1rem}.swagger-ui .pl4-l{padding-left:2rem}.swagger-ui .pl5-l{padding-left:4rem}.swagger-ui .pl6-l{padding-left:8rem}.swagger-ui .pl7-l{padding-left:16rem}.swagger-ui .pr0-l{padding-right:0}.swagger-ui .pr1-l{padding-right:.25rem}.swagger-ui .pr2-l{padding-right:.5rem}.swagger-ui .pr3-l{padding-right:1rem}.swagger-ui .pr4-l{padding-right:2rem}.swagger-ui .pr5-l{padding-right:4rem}.swagger-ui .pr6-l{padding-right:8rem}.swagger-ui .pr7-l{padding-right:16rem}.swagger-ui .pb0-l{padding-bottom:0}.swagger-ui .pb1-l{padding-bottom:.25rem}.swagger-ui .pb2-l{padding-bottom:.5rem}.swagger-ui .pb3-l{padding-bottom:1rem}.swagger-ui .pb4-l{padding-bottom:2rem}.swagger-ui .pb5-l{padding-bottom:4rem}.swagger-ui .pb6-l{padding-bottom:8rem}.swagger-ui .pb7-l{padding-bottom:16rem}.swagger-ui .pt0-l{padding-top:0}.swagger-ui .pt1-l{padding-top:.25rem}.swagger-ui .pt2-l{padding-top:.5rem}.swagger-ui .pt3-l{padding-top:1rem}.swagger-ui .pt4-l{padding-top:2rem}.swagger-ui .pt5-l{padding-top:4rem}.swagger-ui .pt6-l{padding-top:8rem}.swagger-ui .pt7-l{padding-top:16rem}.swagger-ui .pv0-l{padding-bottom:0;padding-top:0}.swagger-ui .pv1-l{padding-bottom:.25rem;padding-top:.25rem}.swagger-ui .pv2-l{padding-bottom:.5rem;padding-top:.5rem}.swagger-ui .pv3-l{padding-bottom:1rem;padding-top:1rem}.swagger-ui .pv4-l{padding-bottom:2rem;padding-top:2rem}.swagger-ui .pv5-l{padding-bottom:4rem;padding-top:4rem}.swagger-ui .pv6-l{padding-bottom:8rem;padding-top:8rem}.swagger-ui .pv7-l{padding-bottom:16rem;padding-top:16rem}.swagger-ui .ph0-l{padding-left:0;padding-right:0}.swagger-ui .ph1-l{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2-l{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3-l{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4-l{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5-l{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6-l{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7-l{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0-l{margin:0}.swagger-ui .ma1-l{margin:.25rem}.swagger-ui .ma2-l{margin:.5rem}.swagger-ui .ma3-l{margin:1rem}.swagger-ui .ma4-l{margin:2rem}.swagger-ui .ma5-l{margin:4rem}.swagger-ui .ma6-l{margin:8rem}.swagger-ui .ma7-l{margin:16rem}.swagger-ui .ml0-l{margin-left:0}.swagger-ui .ml1-l{margin-left:.25rem}.swagger-ui .ml2-l{margin-left:.5rem}.swagger-ui .ml3-l{margin-left:1rem}.swagger-ui .ml4-l{margin-left:2rem}.swagger-ui .ml5-l{margin-left:4rem}.swagger-ui .ml6-l{margin-left:8rem}.swagger-ui .ml7-l{margin-left:16rem}.swagger-ui .mr0-l{margin-right:0}.swagger-ui .mr1-l{margin-right:.25rem}.swagger-ui .mr2-l{margin-right:.5rem}.swagger-ui .mr3-l{margin-right:1rem}.swagger-ui .mr4-l{margin-right:2rem}.swagger-ui .mr5-l{margin-right:4rem}.swagger-ui .mr6-l{margin-right:8rem}.swagger-ui .mr7-l{margin-right:16rem}.swagger-ui .mb0-l{margin-bottom:0}.swagger-ui .mb1-l{margin-bottom:.25rem}.swagger-ui .mb2-l{margin-bottom:.5rem}.swagger-ui .mb3-l{margin-bottom:1rem}.swagger-ui .mb4-l{margin-bottom:2rem}.swagger-ui .mb5-l{margin-bottom:4rem}.swagger-ui .mb6-l{margin-bottom:8rem}.swagger-ui .mb7-l{margin-bottom:16rem}.swagger-ui .mt0-l{margin-top:0}.swagger-ui .mt1-l{margin-top:.25rem}.swagger-ui .mt2-l{margin-top:.5rem}.swagger-ui .mt3-l{margin-top:1rem}.swagger-ui .mt4-l{margin-top:2rem}.swagger-ui .mt5-l{margin-top:4rem}.swagger-ui .mt6-l{margin-top:8rem}.swagger-ui .mt7-l{margin-top:16rem}.swagger-ui .mv0-l{margin-bottom:0;margin-top:0}.swagger-ui .mv1-l{margin-bottom:.25rem;margin-top:.25rem}.swagger-ui .mv2-l{margin-bottom:.5rem;margin-top:.5rem}.swagger-ui .mv3-l{margin-bottom:1rem;margin-top:1rem}.swagger-ui .mv4-l{margin-bottom:2rem;margin-top:2rem}.swagger-ui .mv5-l{margin-bottom:4rem;margin-top:4rem}.swagger-ui .mv6-l{margin-bottom:8rem;margin-top:8rem}.swagger-ui .mv7-l{margin-bottom:16rem;margin-top:16rem}.swagger-ui .mh0-l{margin-left:0;margin-right:0}.swagger-ui .mh1-l{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2-l{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3-l{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4-l{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5-l{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6-l{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7-l{margin-left:16rem;margin-right:16rem}}.swagger-ui .na1{margin:-.25rem}.swagger-ui .na2{margin:-.5rem}.swagger-ui .na3{margin:-1rem}.swagger-ui .na4{margin:-2rem}.swagger-ui .na5{margin:-4rem}.swagger-ui .na6{margin:-8rem}.swagger-ui .na7{margin:-16rem}.swagger-ui .nl1{margin-left:-.25rem}.swagger-ui .nl2{margin-left:-.5rem}.swagger-ui .nl3{margin-left:-1rem}.swagger-ui .nl4{margin-left:-2rem}.swagger-ui .nl5{margin-left:-4rem}.swagger-ui .nl6{margin-left:-8rem}.swagger-ui .nl7{margin-left:-16rem}.swagger-ui .nr1{margin-right:-.25rem}.swagger-ui .nr2{margin-right:-.5rem}.swagger-ui .nr3{margin-right:-1rem}.swagger-ui .nr4{margin-right:-2rem}.swagger-ui .nr5{margin-right:-4rem}.swagger-ui .nr6{margin-right:-8rem}.swagger-ui .nr7{margin-right:-16rem}.swagger-ui .nb1{margin-bottom:-.25rem}.swagger-ui .nb2{margin-bottom:-.5rem}.swagger-ui .nb3{margin-bottom:-1rem}.swagger-ui .nb4{margin-bottom:-2rem}.swagger-ui .nb5{margin-bottom:-4rem}.swagger-ui .nb6{margin-bottom:-8rem}.swagger-ui .nb7{margin-bottom:-16rem}.swagger-ui .nt1{margin-top:-.25rem}.swagger-ui .nt2{margin-top:-.5rem}.swagger-ui .nt3{margin-top:-1rem}.swagger-ui .nt4{margin-top:-2rem}.swagger-ui .nt5{margin-top:-4rem}.swagger-ui .nt6{margin-top:-8rem}.swagger-ui .nt7{margin-top:-16rem}@media screen and (min-width:30em){.swagger-ui .na1-ns{margin:-.25rem}.swagger-ui .na2-ns{margin:-.5rem}.swagger-ui .na3-ns{margin:-1rem}.swagger-ui .na4-ns{margin:-2rem}.swagger-ui .na5-ns{margin:-4rem}.swagger-ui .na6-ns{margin:-8rem}.swagger-ui .na7-ns{margin:-16rem}.swagger-ui .nl1-ns{margin-left:-.25rem}.swagger-ui .nl2-ns{margin-left:-.5rem}.swagger-ui .nl3-ns{margin-left:-1rem}.swagger-ui .nl4-ns{margin-left:-2rem}.swagger-ui .nl5-ns{margin-left:-4rem}.swagger-ui .nl6-ns{margin-left:-8rem}.swagger-ui .nl7-ns{margin-left:-16rem}.swagger-ui .nr1-ns{margin-right:-.25rem}.swagger-ui .nr2-ns{margin-right:-.5rem}.swagger-ui .nr3-ns{margin-right:-1rem}.swagger-ui .nr4-ns{margin-right:-2rem}.swagger-ui .nr5-ns{margin-right:-4rem}.swagger-ui .nr6-ns{margin-right:-8rem}.swagger-ui .nr7-ns{margin-right:-16rem}.swagger-ui .nb1-ns{margin-bottom:-.25rem}.swagger-ui .nb2-ns{margin-bottom:-.5rem}.swagger-ui .nb3-ns{margin-bottom:-1rem}.swagger-ui .nb4-ns{margin-bottom:-2rem}.swagger-ui .nb5-ns{margin-bottom:-4rem}.swagger-ui .nb6-ns{margin-bottom:-8rem}.swagger-ui .nb7-ns{margin-bottom:-16rem}.swagger-ui .nt1-ns{margin-top:-.25rem}.swagger-ui .nt2-ns{margin-top:-.5rem}.swagger-ui .nt3-ns{margin-top:-1rem}.swagger-ui .nt4-ns{margin-top:-2rem}.swagger-ui .nt5-ns{margin-top:-4rem}.swagger-ui .nt6-ns{margin-top:-8rem}.swagger-ui .nt7-ns{margin-top:-16rem}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .na1-m{margin:-.25rem}.swagger-ui .na2-m{margin:-.5rem}.swagger-ui .na3-m{margin:-1rem}.swagger-ui .na4-m{margin:-2rem}.swagger-ui .na5-m{margin:-4rem}.swagger-ui .na6-m{margin:-8rem}.swagger-ui .na7-m{margin:-16rem}.swagger-ui .nl1-m{margin-left:-.25rem}.swagger-ui .nl2-m{margin-left:-.5rem}.swagger-ui .nl3-m{margin-left:-1rem}.swagger-ui .nl4-m{margin-left:-2rem}.swagger-ui .nl5-m{margin-left:-4rem}.swagger-ui .nl6-m{margin-left:-8rem}.swagger-ui .nl7-m{margin-left:-16rem}.swagger-ui .nr1-m{margin-right:-.25rem}.swagger-ui .nr2-m{margin-right:-.5rem}.swagger-ui .nr3-m{margin-right:-1rem}.swagger-ui .nr4-m{margin-right:-2rem}.swagger-ui .nr5-m{margin-right:-4rem}.swagger-ui .nr6-m{margin-right:-8rem}.swagger-ui .nr7-m{margin-right:-16rem}.swagger-ui .nb1-m{margin-bottom:-.25rem}.swagger-ui .nb2-m{margin-bottom:-.5rem}.swagger-ui .nb3-m{margin-bottom:-1rem}.swagger-ui .nb4-m{margin-bottom:-2rem}.swagger-ui .nb5-m{margin-bottom:-4rem}.swagger-ui .nb6-m{margin-bottom:-8rem}.swagger-ui .nb7-m{margin-bottom:-16rem}.swagger-ui .nt1-m{margin-top:-.25rem}.swagger-ui .nt2-m{margin-top:-.5rem}.swagger-ui .nt3-m{margin-top:-1rem}.swagger-ui .nt4-m{margin-top:-2rem}.swagger-ui .nt5-m{margin-top:-4rem}.swagger-ui .nt6-m{margin-top:-8rem}.swagger-ui .nt7-m{margin-top:-16rem}}@media screen and (min-width:60em){.swagger-ui .na1-l{margin:-.25rem}.swagger-ui .na2-l{margin:-.5rem}.swagger-ui .na3-l{margin:-1rem}.swagger-ui .na4-l{margin:-2rem}.swagger-ui .na5-l{margin:-4rem}.swagger-ui .na6-l{margin:-8rem}.swagger-ui .na7-l{margin:-16rem}.swagger-ui .nl1-l{margin-left:-.25rem}.swagger-ui .nl2-l{margin-left:-.5rem}.swagger-ui .nl3-l{margin-left:-1rem}.swagger-ui .nl4-l{margin-left:-2rem}.swagger-ui .nl5-l{margin-left:-4rem}.swagger-ui .nl6-l{margin-left:-8rem}.swagger-ui .nl7-l{margin-left:-16rem}.swagger-ui .nr1-l{margin-right:-.25rem}.swagger-ui .nr2-l{margin-right:-.5rem}.swagger-ui .nr3-l{margin-right:-1rem}.swagger-ui .nr4-l{margin-right:-2rem}.swagger-ui .nr5-l{margin-right:-4rem}.swagger-ui .nr6-l{margin-right:-8rem}.swagger-ui .nr7-l{margin-right:-16rem}.swagger-ui .nb1-l{margin-bottom:-.25rem}.swagger-ui .nb2-l{margin-bottom:-.5rem}.swagger-ui .nb3-l{margin-bottom:-1rem}.swagger-ui .nb4-l{margin-bottom:-2rem}.swagger-ui .nb5-l{margin-bottom:-4rem}.swagger-ui .nb6-l{margin-bottom:-8rem}.swagger-ui .nb7-l{margin-bottom:-16rem}.swagger-ui .nt1-l{margin-top:-.25rem}.swagger-ui .nt2-l{margin-top:-.5rem}.swagger-ui .nt3-l{margin-top:-1rem}.swagger-ui .nt4-l{margin-top:-2rem}.swagger-ui .nt5-l{margin-top:-4rem}.swagger-ui .nt6-l{margin-top:-8rem}.swagger-ui .nt7-l{margin-top:-16rem}}.swagger-ui .collapse{border-collapse:collapse;border-spacing:0}.swagger-ui .striped--light-silver:nth-child(odd){background-color:#aaa}.swagger-ui .striped--moon-gray:nth-child(odd){background-color:#ccc}.swagger-ui .striped--light-gray:nth-child(odd){background-color:#eee}.swagger-ui .striped--near-white:nth-child(odd){background-color:#f4f4f4}.swagger-ui .stripe-light:nth-child(odd){background-color:#ffffff1a}.swagger-ui .stripe-dark:nth-child(odd){background-color:#0000001a}.swagger-ui .strike{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .underline{-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .no-underline{-webkit-text-decoration:none;text-decoration:none}@media screen and (min-width:30em){.swagger-ui .strike-ns{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .underline-ns{-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .no-underline-ns{-webkit-text-decoration:none;text-decoration:none}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .strike-m{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .underline-m{-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .no-underline-m{-webkit-text-decoration:none;text-decoration:none}}@media screen and (min-width:60em){.swagger-ui .strike-l{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .underline-l{-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .no-underline-l{-webkit-text-decoration:none;text-decoration:none}}.swagger-ui .tl{text-align:left}.swagger-ui .tr{text-align:right}.swagger-ui .tc{text-align:center}.swagger-ui .tj{text-align:justify}@media screen and (min-width:30em){.swagger-ui .tl-ns{text-align:left}.swagger-ui .tr-ns{text-align:right}.swagger-ui .tc-ns{text-align:center}.swagger-ui .tj-ns{text-align:justify}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .tl-m{text-align:left}.swagger-ui .tr-m{text-align:right}.swagger-ui .tc-m{text-align:center}.swagger-ui .tj-m{text-align:justify}}@media screen and (min-width:60em){.swagger-ui .tl-l{text-align:left}.swagger-ui .tr-l{text-align:right}.swagger-ui .tc-l{text-align:center}.swagger-ui .tj-l{text-align:justify}}.swagger-ui .ttc{text-transform:capitalize}.swagger-ui .ttl{text-transform:lowercase}.swagger-ui .ttu{text-transform:uppercase}.swagger-ui .ttn{text-transform:none}@media screen and (min-width:30em){.swagger-ui .ttc-ns{text-transform:capitalize}.swagger-ui .ttl-ns{text-transform:lowercase}.swagger-ui .ttu-ns{text-transform:uppercase}.swagger-ui .ttn-ns{text-transform:none}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .ttc-m{text-transform:capitalize}.swagger-ui .ttl-m{text-transform:lowercase}.swagger-ui .ttu-m{text-transform:uppercase}.swagger-ui .ttn-m{text-transform:none}}@media screen and (min-width:60em){.swagger-ui .ttc-l{text-transform:capitalize}.swagger-ui .ttl-l{text-transform:lowercase}.swagger-ui .ttu-l{text-transform:uppercase}.swagger-ui .ttn-l{text-transform:none}}.swagger-ui .f-6,.swagger-ui .f-headline{font-size:6rem}.swagger-ui .f-5,.swagger-ui .f-subheadline{font-size:5rem}.swagger-ui .f1{font-size:3rem}.swagger-ui .f2{font-size:2.25rem}.swagger-ui .f3{font-size:1.5rem}.swagger-ui .f4{font-size:1.25rem}.swagger-ui .f5{font-size:1rem}.swagger-ui .f6{font-size:.875rem}.swagger-ui .f7{font-size:.75rem}@media screen and (min-width:30em){.swagger-ui .f-6-ns,.swagger-ui .f-headline-ns{font-size:6rem}.swagger-ui .f-5-ns,.swagger-ui .f-subheadline-ns{font-size:5rem}.swagger-ui .f1-ns{font-size:3rem}.swagger-ui .f2-ns{font-size:2.25rem}.swagger-ui .f3-ns{font-size:1.5rem}.swagger-ui .f4-ns{font-size:1.25rem}.swagger-ui .f5-ns{font-size:1rem}.swagger-ui .f6-ns{font-size:.875rem}.swagger-ui .f7-ns{font-size:.75rem}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .f-6-m,.swagger-ui .f-headline-m{font-size:6rem}.swagger-ui .f-5-m,.swagger-ui .f-subheadline-m{font-size:5rem}.swagger-ui .f1-m{font-size:3rem}.swagger-ui .f2-m{font-size:2.25rem}.swagger-ui .f3-m{font-size:1.5rem}.swagger-ui .f4-m{font-size:1.25rem}.swagger-ui .f5-m{font-size:1rem}.swagger-ui .f6-m{font-size:.875rem}.swagger-ui .f7-m{font-size:.75rem}}@media screen and (min-width:60em){.swagger-ui .f-6-l,.swagger-ui .f-headline-l{font-size:6rem}.swagger-ui .f-5-l,.swagger-ui .f-subheadline-l{font-size:5rem}.swagger-ui .f1-l{font-size:3rem}.swagger-ui .f2-l{font-size:2.25rem}.swagger-ui .f3-l{font-size:1.5rem}.swagger-ui .f4-l{font-size:1.25rem}.swagger-ui .f5-l{font-size:1rem}.swagger-ui .f6-l{font-size:.875rem}.swagger-ui .f7-l{font-size:.75rem}}.swagger-ui .measure{max-width:30em}.swagger-ui .measure-wide{max-width:34em}.swagger-ui .measure-narrow{max-width:20em}.swagger-ui .indent{margin-bottom:0;margin-top:0;text-indent:1em}.swagger-ui .small-caps{font-feature-settings:"smcp";font-variant:small-caps}.swagger-ui .truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media screen and (min-width:30em){.swagger-ui .measure-ns{max-width:30em}.swagger-ui .measure-wide-ns{max-width:34em}.swagger-ui .measure-narrow-ns{max-width:20em}.swagger-ui .indent-ns{margin-bottom:0;margin-top:0;text-indent:1em}.swagger-ui .small-caps-ns{font-feature-settings:"smcp";font-variant:small-caps}.swagger-ui .truncate-ns{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .measure-m{max-width:30em}.swagger-ui .measure-wide-m{max-width:34em}.swagger-ui .measure-narrow-m{max-width:20em}.swagger-ui .indent-m{margin-bottom:0;margin-top:0;text-indent:1em}.swagger-ui .small-caps-m{font-feature-settings:"smcp";font-variant:small-caps}.swagger-ui .truncate-m{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}@media screen and (min-width:60em){.swagger-ui .measure-l{max-width:30em}.swagger-ui .measure-wide-l{max-width:34em}.swagger-ui .measure-narrow-l{max-width:20em}.swagger-ui .indent-l{margin-bottom:0;margin-top:0;text-indent:1em}.swagger-ui .small-caps-l{font-feature-settings:"smcp";font-variant:small-caps}.swagger-ui .truncate-l{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}.swagger-ui .overflow-container{overflow-y:scroll}.swagger-ui .center{margin-left:auto;margin-right:auto}.swagger-ui .mr-auto{margin-right:auto}.swagger-ui .ml-auto{margin-left:auto}@media screen and (min-width:30em){.swagger-ui .center-ns{margin-left:auto;margin-right:auto}.swagger-ui .mr-auto-ns{margin-right:auto}.swagger-ui .ml-auto-ns{margin-left:auto}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .center-m{margin-left:auto;margin-right:auto}.swagger-ui .mr-auto-m{margin-right:auto}.swagger-ui .ml-auto-m{margin-left:auto}}@media screen and (min-width:60em){.swagger-ui .center-l{margin-left:auto;margin-right:auto}.swagger-ui .mr-auto-l{margin-right:auto}.swagger-ui .ml-auto-l{margin-left:auto}}.swagger-ui .clip{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}@media screen and (min-width:30em){.swagger-ui .clip-ns{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .clip-m{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}}@media screen and (min-width:60em){.swagger-ui .clip-l{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}}.swagger-ui .ws-normal{white-space:normal}.swagger-ui .nowrap{white-space:nowrap}.swagger-ui .pre{white-space:pre}@media screen and (min-width:30em){.swagger-ui .ws-normal-ns{white-space:normal}.swagger-ui .nowrap-ns{white-space:nowrap}.swagger-ui .pre-ns{white-space:pre}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .ws-normal-m{white-space:normal}.swagger-ui .nowrap-m{white-space:nowrap}.swagger-ui .pre-m{white-space:pre}}@media screen and (min-width:60em){.swagger-ui .ws-normal-l{white-space:normal}.swagger-ui .nowrap-l{white-space:nowrap}.swagger-ui .pre-l{white-space:pre}}.swagger-ui .v-base{vertical-align:baseline}.swagger-ui .v-mid{vertical-align:middle}.swagger-ui .v-top{vertical-align:top}.swagger-ui .v-btm{vertical-align:bottom}@media screen and (min-width:30em){.swagger-ui .v-base-ns{vertical-align:baseline}.swagger-ui .v-mid-ns{vertical-align:middle}.swagger-ui .v-top-ns{vertical-align:top}.swagger-ui .v-btm-ns{vertical-align:bottom}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .v-base-m{vertical-align:baseline}.swagger-ui .v-mid-m{vertical-align:middle}.swagger-ui .v-top-m{vertical-align:top}.swagger-ui .v-btm-m{vertical-align:bottom}}@media screen and (min-width:60em){.swagger-ui .v-base-l{vertical-align:baseline}.swagger-ui .v-mid-l{vertical-align:middle}.swagger-ui .v-top-l{vertical-align:top}.swagger-ui .v-btm-l{vertical-align:bottom}}.swagger-ui .dim{opacity:1;transition:opacity .15s ease-in}.swagger-ui .dim:focus,.swagger-ui .dim:hover{opacity:.5;transition:opacity .15s ease-in}.swagger-ui .dim:active{opacity:.8;transition:opacity .15s ease-out}.swagger-ui .glow{transition:opacity .15s ease-in}.swagger-ui .glow:focus,.swagger-ui .glow:hover{opacity:1;transition:opacity .15s ease-in}.swagger-ui .hide-child .child{opacity:0;transition:opacity .15s ease-in}.swagger-ui .hide-child:active .child,.swagger-ui .hide-child:focus .child,.swagger-ui .hide-child:hover .child{opacity:1;transition:opacity .15s ease-in}.swagger-ui .underline-hover:focus,.swagger-ui .underline-hover:hover{-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .grow{-moz-osx-font-smoothing:grayscale;backface-visibility:hidden;transform:translateZ(0);transition:transform .25s ease-out}.swagger-ui .grow:focus,.swagger-ui .grow:hover{transform:scale(1.05)}.swagger-ui .grow:active{transform:scale(.9)}.swagger-ui .grow-large{-moz-osx-font-smoothing:grayscale;backface-visibility:hidden;transform:translateZ(0);transition:transform .25s ease-in-out}.swagger-ui .grow-large:focus,.swagger-ui .grow-large:hover{transform:scale(1.2)}.swagger-ui .grow-large:active{transform:scale(.95)}.swagger-ui .pointer:hover{cursor:pointer}.swagger-ui .shadow-hover{cursor:pointer;position:relative;transition:all .5s cubic-bezier(.165,.84,.44,1)}.swagger-ui .shadow-hover:after{border-radius:inherit;box-shadow:0 0 16px 2px #0003;content:"";height:100%;left:0;opacity:0;position:absolute;top:0;transition:opacity .5s cubic-bezier(.165,.84,.44,1);width:100%;z-index:-1}.swagger-ui .shadow-hover:focus:after,.swagger-ui .shadow-hover:hover:after{opacity:1}.swagger-ui .bg-animate,.swagger-ui .bg-animate:focus,.swagger-ui .bg-animate:hover{transition:background-color .15s ease-in-out}.swagger-ui .z-0{z-index:0}.swagger-ui .z-1{z-index:1}.swagger-ui .z-2{z-index:2}.swagger-ui .z-3{z-index:3}.swagger-ui .z-4{z-index:4}.swagger-ui .z-5{z-index:5}.swagger-ui .z-999{z-index:999}.swagger-ui .z-9999{z-index:9999}.swagger-ui .z-max{z-index:2147483647}.swagger-ui .z-inherit{z-index:inherit}.swagger-ui .z-initial,.swagger-ui .z-unset{z-index:auto}.swagger-ui .nested-copy-line-height ol,.swagger-ui .nested-copy-line-height p,.swagger-ui .nested-copy-line-height ul{line-height:1.5}.swagger-ui .nested-headline-line-height h1,.swagger-ui .nested-headline-line-height h2,.swagger-ui .nested-headline-line-height h3,.swagger-ui .nested-headline-line-height h4,.swagger-ui .nested-headline-line-height h5,.swagger-ui .nested-headline-line-height h6{line-height:1.25}.swagger-ui .nested-list-reset ol,.swagger-ui .nested-list-reset ul{list-style-type:none;margin-left:0;padding-left:0}.swagger-ui .nested-copy-indent p+p{margin-bottom:0;margin-top:0;text-indent:.1em}.swagger-ui .nested-copy-seperator p+p{margin-top:1.5em}.swagger-ui .nested-img img{display:block;max-width:100%;width:100%}.swagger-ui .nested-links a{color:#357edd;transition:color .15s ease-in}.swagger-ui .nested-links a:focus,.swagger-ui .nested-links a:hover{color:#96ccff;transition:color .15s ease-in}.swagger-ui .swagger-ui__skip-link{clip:rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}.swagger-ui .swagger-ui__skip-link:focus{clip:auto;background:#fff;border:1px solid #d8dde7;border-radius:4px;box-shadow:0 2px 8px #0003;clip-path:none;color:#3b4151;font-size:14px;font-weight:600;height:auto;left:8px;overflow:visible;padding:8px 16px;position:fixed;-webkit-text-decoration:none;text-decoration:none;top:8px;width:auto;z-index:10000}.swagger-ui .wrapper{box-sizing:border-box;margin:0 auto;max-width:1460px;padding:0 20px;width:100%}.swagger-ui .opblock-tag-section{display:flex;flex-direction:column}.swagger-ui .try-out.btn-group{display:flex;flex:.1 2 auto;padding:0}.swagger-ui .try-out__btn{margin-left:1.25rem}.swagger-ui .opblock-tag{align-items:center;border-bottom:1px solid rgba(59,65,81,.3);cursor:pointer;display:flex;padding:10px 20px 10px 10px;transition:all .2s}.swagger-ui .opblock-tag:hover{background:#00000005}.swagger-ui .opblock-tag{color:#3b4151;font-family:sans-serif;font-size:24px;margin:0 0 5px}.swagger-ui .opblock-tag.no-desc span{flex:1}.swagger-ui .opblock-tag svg{transition:all .4s}.swagger-ui .opblock-tag small{color:#3b4151;flex:2;font-family:sans-serif;font-size:14px;font-weight:400;padding:0 10px}.swagger-ui .opblock-tag>div{flex:1 1 150px;font-weight:400;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@container swagger-ui (max-width: 640px){.swagger-ui .opblock-tag small,.swagger-ui .opblock-tag>div{flex:1}}.swagger-ui .opblock-tag .info__externaldocs{text-align:right}.swagger-ui .parameter__type{color:#3b4151;font-family:monospace;font-size:12px;font-weight:600;padding:5px 0}.swagger-ui .parameter-controls{margin-top:.75em}.swagger-ui .examples__title{display:block;font-size:1.1em;font-weight:700;margin-bottom:.75em}.swagger-ui .examples__section{margin-top:1.5em}.swagger-ui .examples__section-header{font-size:.9rem;font-weight:700;margin-bottom:.5rem}.swagger-ui .examples-select{display:inline-block;margin-bottom:.75em}.swagger-ui .examples-select .examples-select-element{width:100%}.swagger-ui .examples-select__section-label{font-size:.9rem;font-weight:700;margin-right:.5rem}.swagger-ui .example__section{margin-top:1.5em}.swagger-ui .example__section-header{font-size:.9rem;font-weight:700;margin-bottom:.5rem}.swagger-ui .view-line-link{cursor:pointer;margin:0 5px;position:relative;top:3px;transition:all .5s;width:20px}.swagger-ui .opblock{border:1px solid #000;border-radius:4px;box-shadow:0 0 3px #00000030;margin:0 0 15px}.swagger-ui .opblock .tab-header{display:flex;flex:1}.swagger-ui .opblock .tab-header .tab-item{cursor:pointer;padding:0 40px}.swagger-ui .opblock .tab-header .tab-item:first-of-type{padding:0 40px 0 0}.swagger-ui .opblock .tab-header .tab-item.active h4 span{position:relative}.swagger-ui .opblock .tab-header .tab-item.active h4 span:after{background:gray;bottom:-15px;content:"";height:4px;left:50%;position:absolute;transform:translate(-50%);width:120%}.swagger-ui .opblock.is-open .opblock-summary{border-bottom:1px solid #000}.swagger-ui .opblock .opblock-section-header{align-items:center;background:#fffc;box-shadow:0 1px 2px #0000001a;display:flex;min-height:50px;padding:8px 20px}.swagger-ui .opblock .opblock-section-header>label{align-items:center;color:#3b4151;display:flex;font-family:sans-serif;font-size:12px;font-weight:700;margin:0 0 0 auto}.swagger-ui .opblock .opblock-section-header>label>span{padding:0 10px 0 0}.swagger-ui .opblock .opblock-section-header h4{color:#3b4151;flex:1;font-family:sans-serif;font-size:14px;margin:0}.swagger-ui .opblock .opblock-summary-method{background:#000;border-radius:3px;color:#fff;font-family:sans-serif;font-size:14px;font-weight:700;min-width:80px;padding:6px 0;text-align:center;text-shadow:0 1px 0 rgba(0,0,0,.1)}@container swagger-ui (max-width: 768px){.swagger-ui .opblock .opblock-summary-method{font-size:12px}}.swagger-ui .opblock .opblock-summary-operation-id,.swagger-ui .opblock .opblock-summary-path,.swagger-ui .opblock .opblock-summary-path__deprecated{align-items:center;color:#3b4151;display:flex;font-family:monospace;font-size:16px;font-weight:600;word-break:break-word}@container swagger-ui (max-width: 768px){.swagger-ui .opblock .opblock-summary-operation-id,.swagger-ui .opblock .opblock-summary-path,.swagger-ui .opblock .opblock-summary-path__deprecated{font-size:12px}}.swagger-ui .opblock .opblock-summary-path{flex-shrink:1}@container swagger-ui (max-width: 640px){.swagger-ui .opblock .opblock-summary-path{max-width:100%}}.swagger-ui .opblock .opblock-summary-path__deprecated{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .opblock .opblock-summary-operation-id{font-size:14px}.swagger-ui .opblock .opblock-summary-description{color:#3b4151;font-family:sans-serif;font-size:13px;word-break:break-word}.swagger-ui .opblock .opblock-summary-path-description-wrapper{align-items:center;display:flex;flex-direction:row;flex-grow:1;flex-wrap:wrap;gap:0 10px;padding:0 10px}@container swagger-ui (max-width: 550px){.swagger-ui .opblock .opblock-summary-path-description-wrapper{align-items:flex-start;flex-direction:column}}.swagger-ui .opblock .opblock-summary{align-items:center;cursor:pointer;display:flex;padding:5px}.swagger-ui .opblock .opblock-summary .view-line-link{cursor:pointer;margin:0;position:relative;top:2px;transition:all .5s;width:0}.swagger-ui .opblock .opblock-summary:hover .view-line-link{margin:0 5px;width:18px}.swagger-ui .opblock .opblock-summary:hover .view-line-link.copy-to-clipboard{width:24px}.swagger-ui .opblock.opblock-post{background:#49cc901a;border-color:#49cc90}.swagger-ui .opblock.opblock-post .opblock-summary-method{background:#49cc90}.swagger-ui .opblock.opblock-post .opblock-summary{border-color:#49cc90}.swagger-ui .opblock.opblock-post .tab-header .tab-item.active h4 span:after{background:#49cc90}.swagger-ui .opblock.opblock-put{background:#fca1301a;border-color:#fca130}.swagger-ui .opblock.opblock-put .opblock-summary-method{background:#fca130}.swagger-ui .opblock.opblock-put .opblock-summary{border-color:#fca130}.swagger-ui .opblock.opblock-put .tab-header .tab-item.active h4 span:after{background:#fca130}.swagger-ui .opblock.opblock-delete{background:#f93e3e1a;border-color:#f93e3e}.swagger-ui .opblock.opblock-delete .opblock-summary-method{background:#f93e3e}.swagger-ui .opblock.opblock-delete .opblock-summary{border-color:#f93e3e}.swagger-ui .opblock.opblock-delete .tab-header .tab-item.active h4 span:after{background:#f93e3e}.swagger-ui .opblock.opblock-get{background:#61affe1a;border-color:#61affe}.swagger-ui .opblock.opblock-get .opblock-summary-method{background:#61affe}.swagger-ui .opblock.opblock-get .opblock-summary{border-color:#61affe}.swagger-ui .opblock.opblock-get .tab-header .tab-item.active h4 span:after{background:#61affe}.swagger-ui .opblock.opblock-patch{background:#50e3c21a;border-color:#50e3c2}.swagger-ui .opblock.opblock-patch .opblock-summary-method{background:#50e3c2}.swagger-ui .opblock.opblock-patch .opblock-summary{border-color:#50e3c2}.swagger-ui .opblock.opblock-patch .tab-header .tab-item.active h4 span:after{background:#50e3c2}.swagger-ui .opblock.opblock-head{background:#9012fe1a;border-color:#9012fe}.swagger-ui .opblock.opblock-head .opblock-summary-method{background:#9012fe}.swagger-ui .opblock.opblock-head .opblock-summary{border-color:#9012fe}.swagger-ui .opblock.opblock-head .tab-header .tab-item.active h4 span:after{background:#9012fe}.swagger-ui .opblock.opblock-options{background:#0d5aa71a;border-color:#0d5aa7}.swagger-ui .opblock.opblock-options .opblock-summary-method{background:#0d5aa7}.swagger-ui .opblock.opblock-options .opblock-summary{border-color:#0d5aa7}.swagger-ui .opblock.opblock-options .tab-header .tab-item.active h4 span:after{background:#0d5aa7}.swagger-ui .opblock.opblock-query{background:#9d408a1a;border-color:#9d408a}.swagger-ui .opblock.opblock-query .opblock-summary-method{background:#9d408a}.swagger-ui .opblock.opblock-query .opblock-summary{border-color:#9d408a}.swagger-ui .opblock.opblock-query .tab-header .tab-item.active h4 span:after{background:#9d408a}.swagger-ui .opblock.opblock-deprecated{background:#ebebeb1a;border-color:#ebebeb;opacity:.6}.swagger-ui .opblock.opblock-deprecated .opblock-summary-method{background:#ebebeb}.swagger-ui .opblock.opblock-deprecated .opblock-summary{border-color:#ebebeb}.swagger-ui .opblock.opblock-deprecated .tab-header .tab-item.active h4 span:after{background:#ebebeb}.swagger-ui .opblock .opblock-schemes{padding:8px 20px}.swagger-ui .opblock .opblock-schemes .schemes-title{padding:0 10px 0 0}.swagger-ui .filter .operation-filter-input{border:2px solid #d8dde7;margin:20px 0;padding:10px;width:100%}.swagger-ui .download-url-wrapper .failed,.swagger-ui .filter .failed{color:red}.swagger-ui .download-url-wrapper .loading,.swagger-ui .filter .loading{color:#aaa}.swagger-ui .model-example{margin-top:1em}.swagger-ui .model-example .model-container{overflow-x:auto;width:100%}.swagger-ui .model-example .model-container .model-hint:not(.model-hint--embedded){top:-1.15em}.swagger-ui .tab{display:flex;list-style:none;padding:0}.swagger-ui .tab li{color:#3b4151;cursor:pointer;font-family:sans-serif;font-size:12px;min-width:60px;padding:0}.swagger-ui .tab li:first-of-type{padding-left:0;padding-right:12px;position:relative}.swagger-ui .tab li:first-of-type:after{background:#0003;content:"";height:100%;position:absolute;right:6px;top:0;width:1px}.swagger-ui .tab li.active{font-weight:700}.swagger-ui .tab li button.tablinks{background:none;border:0;color:inherit;font-family:inherit;font-weight:inherit;padding:0}.swagger-ui .opblock-description-wrapper,.swagger-ui .opblock-external-docs-wrapper,.swagger-ui .opblock-title_normal{color:#3b4151;font-family:sans-serif;font-size:12px;margin:0 0 5px;padding:15px 20px}.swagger-ui .opblock-description-wrapper h4,.swagger-ui .opblock-external-docs-wrapper h4,.swagger-ui .opblock-title_normal h4{color:#3b4151;font-family:sans-serif;font-size:12px;margin:0 0 5px}.swagger-ui .opblock-description-wrapper p,.swagger-ui .opblock-external-docs-wrapper p,.swagger-ui .opblock-title_normal p{color:#3b4151;font-family:sans-serif;font-size:14px;margin:0}.swagger-ui .opblock-external-docs-wrapper h4{padding-left:0}.swagger-ui .execute-wrapper{padding:20px;text-align:right}.swagger-ui .execute-wrapper .btn{padding:8px 40px;width:100%}.swagger-ui .body-param-options{display:flex;flex-direction:column}.swagger-ui .body-param-options .body-param-edit{padding:10px 0}.swagger-ui .body-param-options label{padding:8px 0}.swagger-ui .body-param-options label select{margin:3px 0 0}.swagger-ui .responses-inner{padding:20px}.swagger-ui .responses-inner h4,.swagger-ui .responses-inner h5{color:#3b4151;font-family:sans-serif;font-size:12px;margin:10px 0 5px}.swagger-ui .responses-inner .curl{max-height:400px;min-height:6em;overflow-y:auto}.swagger-ui .response-col_status{color:#3b4151;font-family:sans-serif;font-size:14px}.swagger-ui .response-col_status .response-undocumented{color:#909090;font-family:monospace;font-size:11px;font-weight:600}.swagger-ui .response-col_links{color:#3b4151;font-family:sans-serif;font-size:14px;max-width:40em;padding-left:2em}.swagger-ui .response-col_links .response-undocumented{color:#909090;font-family:monospace;font-size:11px;font-weight:600}.swagger-ui .response-col_links .operation-link{margin-bottom:1.5em}.swagger-ui .response-col_links .operation-link .description{margin-bottom:.5em}.swagger-ui .opblock-body .opblock-loading-animation{display:block;margin:3em auto}.swagger-ui .opblock-body pre.microlight{background:#333;border-radius:4px;font-size:12px;-webkit-hyphens:auto;hyphens:auto;margin:0;padding:10px;white-space:pre-wrap;word-break:break-all;word-break:break-word;word-wrap:break-word;color:#fff;font-family:monospace;font-weight:600}.swagger-ui .opblock-body pre.microlight .headerline{display:block}.swagger-ui .highlight-code{position:relative}.swagger-ui .highlight-code>.microlight{max-height:400px;min-height:6em;overflow-y:auto}.swagger-ui .highlight-code>.microlight code{white-space:pre-wrap!important;word-break:break-all}.swagger-ui .curl-command{position:relative}.swagger-ui .download-contents{align-items:center;background:#7d8293;border:none;border-radius:4px;bottom:10px;color:#fff;display:flex;font-family:sans-serif;font-size:14px;font-weight:600;height:30px;justify-content:center;padding:5px;position:absolute;right:10px;text-align:center}.swagger-ui .scheme-container{background:#fff;box-shadow:0 1px 2px #00000026;margin:0 0 20px;padding:30px 0}.swagger-ui .scheme-container .schemes{align-items:flex-end;display:flex;flex-wrap:wrap;gap:10px;justify-content:space-between}.swagger-ui .scheme-container .schemes>.schemes-server-container{display:flex;flex-wrap:wrap;gap:10px}.swagger-ui .scheme-container .schemes>.schemes-server-container>label{color:#3b4151;display:flex;flex-direction:column;font-family:sans-serif;font-size:12px;font-weight:700;margin:-20px 15px 0 0}.swagger-ui .scheme-container .schemes>.schemes-server-container>label select{min-width:130px;text-transform:uppercase}.swagger-ui .scheme-container .schemes:not(:has(.schemes-server-container)){justify-content:flex-end}.swagger-ui .scheme-container .schemes .auth-wrapper{flex:none;justify-content:start}.swagger-ui .scheme-container .schemes .auth-wrapper .authorize{display:flex;flex-wrap:nowrap;margin:0;padding-right:20px}.swagger-ui .loading-container{align-items:center;display:flex;flex-direction:column;justify-content:center;margin-top:1em;min-height:1px;padding:40px 0 60px}.swagger-ui .loading-container .loading{position:relative}.swagger-ui .loading-container .loading:after{color:#3b4151;content:"loading";font-family:sans-serif;font-size:10px;font-weight:700;left:50%;position:absolute;text-transform:uppercase;top:50%;transform:translate(-50%,-50%)}.swagger-ui .loading-container .loading:before{animation:rotation 1s linear infinite,opacity .5s;backface-visibility:hidden;border:2px solid rgba(85,85,85,.1);border-radius:100%;border-top-color:#0009;content:"";display:block;height:60px;left:50%;margin:-30px;opacity:1;position:absolute;top:50%;width:60px}@keyframes rotation{to{transform:rotate(1turn)}}.swagger-ui .response-controls{display:flex;padding-top:1em}.swagger-ui .response-control-media-type{margin-right:1em}.swagger-ui .response-control-media-type--accept-controller select{border-color:green}.swagger-ui .response-control-media-type__accept-message{color:green;font-size:.7em}.swagger-ui .response-control-examples__title,.swagger-ui .response-control-media-type__title{display:block;font-size:.7em;margin-bottom:.2em}@keyframes blinker{50%{opacity:0}}.swagger-ui .hidden{display:none}.swagger-ui .no-margin{border:none;height:auto;margin:0;padding:0}.swagger-ui .float-right{float:right}.swagger-ui .svg-assets{height:0;position:absolute;width:0}.swagger-ui section h3{color:#3b4151;font-family:sans-serif}.swagger-ui a.nostyle{display:inline}.swagger-ui a.nostyle,.swagger-ui a.nostyle:visited{color:inherit;cursor:pointer;text-decoration:inherit}.swagger-ui .fallback{color:#aaa;padding:1em}.swagger-ui .version-pragma{height:100%;padding:5em 0}.swagger-ui .version-pragma__message{display:flex;font-size:1.2em;height:100%;justify-content:center;line-height:1.5em;padding:0 .6em;text-align:center}.swagger-ui .version-pragma__message>div{flex:1;max-width:55ch}.swagger-ui .version-pragma__message code{background-color:#dedede;padding:4px 4px 2px;white-space:pre}.swagger-ui .opblock-link{font-weight:400}.swagger-ui .opblock-link.shown{font-weight:700}.swagger-ui span.token-string{color:#555}.swagger-ui span.token-not-formatted{color:#555;font-weight:700}.swagger-ui .btn{background:transparent;border:2px solid grey;border-radius:4px;box-shadow:0 1px 2px #0000001a;color:#3b4151;font-family:sans-serif;font-size:14px;font-weight:700;padding:5px 23px;transition:all .3s}.swagger-ui .btn.btn-sm{font-size:12px;padding:4px 23px}.swagger-ui .btn[disabled]{cursor:not-allowed;opacity:.3}.swagger-ui .btn:hover{box-shadow:0 0 5px #0000004d}.swagger-ui .btn.cancel{background-color:transparent;border-color:#ff6060;color:#ff6060;font-family:sans-serif}.swagger-ui .btn.authorize{background-color:transparent;border-color:#49cc90;color:#49cc90;display:inline;line-height:1}.swagger-ui .btn.authorize span{float:left;padding:4px 20px 0 0}.swagger-ui .btn.authorize svg{color:#49cc90}.swagger-ui .btn.execute{background-color:#4990e2;border-color:#4990e2;color:#fff}.swagger-ui .btn-group{display:flex;padding:30px}.swagger-ui .btn-group .btn{flex:1}.swagger-ui .btn-group .btn:first-child{border-radius:4px 0 0 4px}.swagger-ui .btn-group .btn:last-child{border-radius:0 4px 4px 0}.swagger-ui .authorization__btn{background:none;border:none;color:#000;padding:0 0 0 10px}.swagger-ui .authorization__btn .locked{opacity:1}.swagger-ui .authorization__btn .unlocked{opacity:.4}.swagger-ui .model-box-control,.swagger-ui .models-control,.swagger-ui .opblock-summary-control{all:inherit;border-bottom:0;cursor:pointer;flex:1;padding:0}.swagger-ui .model-box-control:focus,.swagger-ui .models-control:focus,.swagger-ui .opblock-summary-control:focus{outline:auto}.swagger-ui .expand-methods,.swagger-ui .expand-operation{background:none;border:none;color:#000}.swagger-ui .expand-methods svg,.swagger-ui .expand-operation svg{height:20px;width:20px}.swagger-ui .expand-methods{padding:0 10px}.swagger-ui .expand-methods:hover svg{color:#404040}.swagger-ui .expand-methods svg{color:#707070;transition:all .3s}.swagger-ui button{cursor:pointer}.swagger-ui button.invalid{animation:shake .4s 1;background:#feebeb;border-color:#f93e3e}.swagger-ui .copy-to-clipboard{align-items:center;background:#7d8293;border:none;border-radius:4px;bottom:10px;display:flex;height:30px;justify-content:center;position:absolute;right:100px;width:30px}.swagger-ui .copy-to-clipboard button{background:url('data:image/svg+xml;charset=utf-8,') 50% no-repeat;border:none;flex-grow:1;flex-shrink:1;height:25px}@media(forced-colors:active){.swagger-ui .copy-to-clipboard button{background-color:ButtonText;background-image:none;forced-color-adjust:none;-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,');-webkit-mask-position:center center;mask-position:center center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}}.swagger-ui .copy-to-clipboard:active{background:#5e626f}.swagger-ui .opblock-control-arrow{background:none;border:none;color:#000;text-align:center}.swagger-ui .curl-command .copy-to-clipboard{bottom:5px;height:20px;right:10px;width:20px}.swagger-ui .curl-command .copy-to-clipboard button{height:18px}.swagger-ui .opblock .opblock-summary .view-line-link.copy-to-clipboard{height:26px;position:static}@media(forced-colors:active){.swagger-ui .authorization__btn svg path,.swagger-ui .btn.authorize svg path,.swagger-ui .close-modal svg path,.swagger-ui .expand-operation svg path,.swagger-ui .json-schema-2020-12-accordion svg path,.swagger-ui .models-control svg path,.swagger-ui .opblock-control-arrow svg path{forced-color-adjust:none;fill:ButtonText}.swagger-ui .authorization__btn .unlocked{opacity:1}}.swagger-ui select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:#f7f7f7 url('data:image/svg+xml;charset=utf-8,') right 10px center no-repeat;background-size:20px;border:2px solid #41444e;border-radius:4px;box-shadow:0 1px 2px #00000040;color:#3b4151;font-family:sans-serif;font-size:14px;font-weight:700;padding:5px 40px 5px 10px}.swagger-ui select[multiple]{background:#f7f7f7;margin:5px 0;padding:5px}.swagger-ui select.invalid{animation:shake .4s 1;background:#feebeb;border-color:#f93e3e}.swagger-ui .opblock-body select{min-width:230px}@container swagger-ui (max-width: 768px){.swagger-ui .opblock-body select{min-width:180px}}@container swagger-ui (max-width: 640px){.swagger-ui .opblock-body select{min-width:100%;width:100%}}.swagger-ui label{color:#3b4151;font-family:sans-serif;font-size:12px;font-weight:700;margin:0 0 5px}.swagger-ui input[type=email],.swagger-ui input[type=file],.swagger-ui input[type=password],.swagger-ui input[type=search],.swagger-ui input[type=text]{line-height:1}@container swagger-ui (max-width: 768px){.swagger-ui input[type=email],.swagger-ui input[type=file],.swagger-ui input[type=password],.swagger-ui input[type=search],.swagger-ui input[type=text]{max-width:175px}}.swagger-ui input[type=email],.swagger-ui input[type=file],.swagger-ui input[type=password],.swagger-ui input[type=search],.swagger-ui input[type=text],.swagger-ui textarea{background:#fff;border:1px solid #d9d9d9;border-radius:4px;margin:5px 0;min-width:100px;padding:8px 10px}.swagger-ui input[type=email].invalid,.swagger-ui input[type=file].invalid,.swagger-ui input[type=password].invalid,.swagger-ui input[type=search].invalid,.swagger-ui input[type=text].invalid,.swagger-ui textarea.invalid{animation:shake .4s 1;background:#feebeb;border-color:#f93e3e}.swagger-ui input[disabled],.swagger-ui select[disabled],.swagger-ui textarea[disabled]{background-color:#fafafa;color:#888;cursor:not-allowed}.swagger-ui select[disabled]{border-color:#888}.swagger-ui textarea[disabled]{background-color:#41444e;color:#fff}@keyframes shake{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}.swagger-ui textarea{background:#fffc;border:none;border-radius:4px;color:#3b4151;font-family:monospace;font-size:12px;font-weight:600;min-height:280px;outline:none;padding:10px;width:100%}.swagger-ui textarea:focus{border:2px solid #61affe}.swagger-ui textarea.curl{background:#41444e;border-radius:4px;color:#fff;font-family:monospace;font-size:12px;font-weight:600;margin:0;min-height:100px;padding:10px;resize:none}.swagger-ui .checkbox{color:#303030;padding:5px 0 10px;transition:opacity .5s}.swagger-ui .checkbox label{display:flex}.swagger-ui .checkbox p{color:#3b4151;font-family:monospace;font-style:italic;font-weight:400!important;font-weight:600;margin:0!important}.swagger-ui .checkbox input[type=checkbox]{display:none}.swagger-ui .checkbox input[type=checkbox]+label>.item{background:#e8e8e8;border-radius:1px;box-shadow:0 0 0 2px #e8e8e8;cursor:pointer;display:inline-block;flex:none;height:16px;margin:0 8px 0 0;padding:5px;position:relative;top:3px;width:16px}.swagger-ui .checkbox input[type=checkbox]+label>.item:active{transform:scale(.9)}.swagger-ui .checkbox input[type=checkbox]:checked+label>.item{background:#e8e8e8 url('data:image/svg+xml;charset=utf-8,') 50% no-repeat}@media(forced-colors:active){.swagger-ui .checkbox input[type=checkbox]:checked+label>.item{background-color:ButtonText;background-image:none;forced-color-adjust:none;-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,');-webkit-mask-position:center center;mask-position:center center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}}.swagger-ui .dialog-ux{inset:0;position:fixed;z-index:9999}.swagger-ui .dialog-ux .backdrop-ux{background:#000c;inset:0;position:fixed}.swagger-ui .dialog-ux .modal-ux{background:#fff;border:1px solid #ebebeb;border-radius:4px;box-shadow:0 10px 30px #0003;left:50%;max-width:650px;min-width:300px;position:absolute;top:50%;transform:translate(-50%,-50%);width:100%;z-index:9999}.swagger-ui .dialog-ux .modal-ux-content{max-height:540px;overflow-y:auto;padding:20px}.swagger-ui .dialog-ux .modal-ux-content p{color:#41444e;color:#3b4151;font-family:sans-serif;font-size:12px;margin:0 0 5px}.swagger-ui .dialog-ux .modal-ux-content h4{color:#3b4151;font-family:sans-serif;font-size:18px;font-weight:600;margin:15px 0 0}.swagger-ui .dialog-ux .modal-ux-header{align-items:center;border-bottom:1px solid #ebebeb;display:flex;padding:12px 0}.swagger-ui .dialog-ux .modal-ux-header .close-modal{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:none;padding:0 10px}.swagger-ui .dialog-ux .modal-ux-header h3{color:#3b4151;flex:1;font-family:sans-serif;font-size:20px;font-weight:600;margin:0;padding:0 20px}.swagger-ui .model{color:#3b4151;font-family:monospace;font-size:12px;font-weight:300;font-weight:600}.swagger-ui .model .deprecated span,.swagger-ui .model .deprecated td{color:#a0a0a0!important}.swagger-ui .model .deprecated>td:first-of-type{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .model-toggle{cursor:pointer;display:inline-block;font-size:10px;margin:auto .3em;position:relative;top:6px;transform:rotate(90deg);transform-origin:50% 50%;transition:transform .15s ease-in}.swagger-ui .model-toggle.collapsed{transform:rotate(0)}.swagger-ui .model-toggle:after{background:url('data:image/svg+xml;charset=utf-8,') 50% no-repeat;background-size:100%;content:"";display:block;height:20px;width:20px}@media(forced-colors:active){.swagger-ui .model-toggle:after{background-color:CanvasText;background-image:none;forced-color-adjust:none;-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,');-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100%;mask-size:100%}}.swagger-ui .model-jump-to-path{cursor:pointer;position:relative}.swagger-ui .model-jump-to-path .view-line-link{cursor:pointer;position:absolute;top:-.4em}.swagger-ui .model-title{position:relative}.swagger-ui .model-title:hover .model-hint{display:block}.swagger-ui .model-hint{background:#000000b3;border-radius:4px;color:#ebebeb;display:none;padding:.1em .5em;position:absolute;top:-1.8em;white-space:nowrap}.swagger-ui .model p{margin:0 0 1em}.swagger-ui .model .property{color:#999;font-style:italic}.swagger-ui .model .property.primitive{color:#6b6b6b}.swagger-ui .model .property.primitive.extension{display:block}.swagger-ui .model .property.primitive.extension>td:first-child{padding-left:0;padding-right:0;width:auto}.swagger-ui .model .property.primitive.extension>td:first-child:after{content:": "}.swagger-ui .model .external-docs,.swagger-ui table.model tr.description{color:#666;font-weight:400}.swagger-ui table.model tr.description td:first-child,.swagger-ui table.model tr.property-row.required td:first-child{font-weight:700}.swagger-ui table.model tr.property-row td{vertical-align:top}.swagger-ui table.model tr.property-row td:first-child{padding-right:.2em}.swagger-ui table.model tr.property-row .star{color:red}.swagger-ui table.model tr.extension{color:#777}.swagger-ui table.model tr.extension td:last-child{vertical-align:top}.swagger-ui table.model tr.external-docs td:first-child{font-weight:700}.swagger-ui table.model tr .renderedMarkdown p:first-child{margin-top:0}.swagger-ui section.models{border:1px solid rgba(59,65,81,.3);border-radius:4px;margin:30px 0}.swagger-ui section.models .pointer{cursor:pointer}.swagger-ui section.models.is-open{padding:0 0 20px}.swagger-ui section.models.is-open h4{border-bottom:1px solid rgba(59,65,81,.3);margin:0 0 5px}.swagger-ui section.models h4{align-items:center;color:#606060;cursor:pointer;display:flex;font-family:sans-serif;font-size:16px;margin:0;padding:10px 20px 10px 10px;transition:all .2s}.swagger-ui section.models h4 svg{transition:all .4s}.swagger-ui section.models h4 span{flex:1}.swagger-ui section.models h4:hover{background:#00000005}.swagger-ui section.models h5{color:#707070;font-family:sans-serif;font-size:16px;margin:0 0 10px}.swagger-ui section.models .model-jump-to-path{position:relative;top:5px}.swagger-ui section.models .model-container{background:#0000000d;border-radius:4px;margin:0 20px 15px;position:relative;transition:all .5s}.swagger-ui section.models .model-container:hover{background:#00000012}.swagger-ui section.models .model-container:first-of-type{margin:20px}.swagger-ui section.models .model-container:last-of-type{margin:0 20px}.swagger-ui section.models .model-container .models-jump-to-path{opacity:.65;position:absolute;right:5px;top:8px}.swagger-ui section.models .model-box{background:none}.swagger-ui section.models .model-box:has(.model-box){overflow-x:auto;width:100%}.swagger-ui .model-box{background:#0000001a;border-radius:4px;display:inline-block;padding:10px}.swagger-ui .model-box .model-jump-to-path{position:relative;top:4px}.swagger-ui .model-box.deprecated{opacity:.5}.swagger-ui .model-title{color:#505050;font-family:sans-serif;font-size:16px;font-weight:inherit}.swagger-ui .model-title img{bottom:0;margin-left:1em;position:relative}.swagger-ui .model-deprecated-warning{color:#f93e3e;font-family:sans-serif;font-size:16px;font-weight:600;margin-right:1em}.swagger-ui span>span.model .brace-close{padding:0 0 0 10px}.swagger-ui .prop-name{display:inline-block;margin-right:1em}.swagger-ui .prop-type{color:#55a}.swagger-ui .prop-enum{display:block}.swagger-ui .prop-format{color:#606060}.swagger-ui .servers>label{color:#3b4151;font-family:sans-serif;font-size:12px;margin:-20px 15px 0 0}.swagger-ui .servers>label select{max-width:100%;min-width:130px;width:100%}.swagger-ui .servers h4.message{padding-bottom:2em}.swagger-ui .servers table tr{width:30em}.swagger-ui .servers table td{display:inline-block;max-width:15em;padding-bottom:10px;padding-top:10px;vertical-align:middle}.swagger-ui .servers table td:first-of-type{padding-right:1em}.swagger-ui .servers table td input{height:100%;width:100%}.swagger-ui .servers .computed-url{margin:2em 0}.swagger-ui .servers .computed-url code{display:inline-block;font-size:16px;margin:0 1em;padding:4px}.swagger-ui .servers-title{font-size:12px;font-weight:700}.swagger-ui .operation-servers h4.message{margin-bottom:2em}.swagger-ui table{border-collapse:collapse;padding:0 10px;width:100%}.swagger-ui table.model tbody tr td{padding:0 0 0 1em;vertical-align:top}.swagger-ui table.model tbody tr td:first-of-type{padding:0 0 0 2em;width:174px}.swagger-ui table.headers td{color:#3b4151;font-family:monospace;font-size:12px;font-weight:300;font-weight:600;vertical-align:middle}.swagger-ui table.headers .header-example{color:#999;font-style:italic}.swagger-ui table tbody tr td{padding:10px 0 0;vertical-align:top}.swagger-ui table tbody tr td:first-of-type{min-width:6em;padding:10px 0}.swagger-ui table tbody tr td:has(.model-box){max-width:1px}.swagger-ui table thead tr td,.swagger-ui table thead tr th{border-bottom:1px solid rgba(59,65,81,.2);color:#3b4151;font-family:sans-serif;font-size:12px;font-weight:700;padding:12px 0;text-align:left}.swagger-ui .parameters-col_description{margin-bottom:2em;width:99%}.swagger-ui .parameters-col_description input{max-width:340px;width:100%}.swagger-ui .parameters-col_description select{border-width:1px}.swagger-ui .parameters-col_description .markdown:first-child p:first-child,.swagger-ui .parameters-col_description .renderedMarkdown:first-child p:first-child{margin:0}.swagger-ui .parameter__name{color:#3b4151;font-family:sans-serif;font-size:16px;font-weight:400;margin-right:.75em}.swagger-ui .parameter__name.required{font-weight:700}.swagger-ui .parameter__name.required span{color:red}.swagger-ui .parameter__name.required:after{color:#f009;content:"required";font-size:10px;padding:5px;position:relative;top:-6px}.swagger-ui .parameter__extension,.swagger-ui .parameter__in{color:gray;font-family:monospace;font-size:12px;font-style:italic;font-weight:600}.swagger-ui .parameter__deprecated{color:red;font-family:monospace;font-size:12px;font-style:italic;font-weight:600}.swagger-ui .parameter__empty_value_toggle{display:block;font-size:13px;padding-bottom:12px;padding-top:5px}.swagger-ui .parameter__empty_value_toggle input{margin-right:7px;width:auto}.swagger-ui .parameter__empty_value_toggle.disabled{opacity:.7}.swagger-ui .table-container{padding:20px}.swagger-ui .response-col_description{width:99%}.swagger-ui .response-col_description .markdown p:first-child,.swagger-ui .response-col_description .renderedMarkdown p:first-child{margin:0}.swagger-ui .response-col_description .markdown p:last-child,.swagger-ui .response-col_description .renderedMarkdown p:last-child{margin-bottom:0}.swagger-ui .response-col_links{min-width:6em}.swagger-ui .response__extension{color:gray;font-family:monospace;font-size:12px;font-style:italic;font-weight:600}.swagger-ui .topbar{background-color:#1b1b1b;padding:10px 0}.swagger-ui .topbar .topbar-wrapper{align-items:center;display:flex;flex-wrap:wrap;gap:10px}@container swagger-ui (max-width: 550px){.swagger-ui .topbar .topbar-wrapper{align-items:start;flex-direction:column}}.swagger-ui .topbar a{align-items:center;color:#fff;display:flex;flex:1;font-family:sans-serif;font-size:1.5em;font-weight:700;max-width:300px;-webkit-text-decoration:none;text-decoration:none}.swagger-ui .topbar a span{margin:0;padding:0 10px}.swagger-ui .topbar .download-url-wrapper{display:flex;flex:3;justify-content:flex-end;margin-left:auto;max-width:600px}.swagger-ui .topbar .download-url-wrapper input[type=text]{border:2px solid #62a03f;border-radius:4px 0 0 4px;margin:0;max-width:100%;outline:none;width:100%}.swagger-ui .topbar .download-url-wrapper .select-label{align-items:center;color:#f0f0f0;display:flex;margin:0;max-width:600px;width:100%}.swagger-ui .topbar .download-url-wrapper .select-label span{flex:1;font-size:16px;padding:0 10px 0 0;text-align:right}.swagger-ui .topbar .download-url-wrapper .select-label select{border:2px solid #62a03f;box-shadow:none;flex:2;outline:none;width:100%}.swagger-ui .topbar .download-url-wrapper .download-url-button{background:#62a03f;border:none;border-radius:0 4px 4px 0;color:#fff;font-family:sans-serif;font-size:16px;font-weight:700;padding:4px 30px}@container swagger-ui (max-width: 550px){.swagger-ui .topbar .download-url-wrapper{width:100%}}.swagger-ui .topbar .dark-mode-toggle{cursor:pointer;margin-left:10px;opacity:.8;transition:all .2s}.swagger-ui .topbar .dark-mode-toggle button{background:none;border:none;padding:0}.swagger-ui .topbar .dark-mode-toggle button svg{fill:#e4e6e6}.swagger-ui .topbar .dark-mode-toggle:hover{opacity:1}@media(forced-colors:active){.swagger-ui .topbar a svg [class$=cls-2]{forced-color-adjust:none;fill:CanvasText}.swagger-ui .topbar .dark-mode-toggle{opacity:1}.swagger-ui .topbar .dark-mode-toggle button svg{forced-color-adjust:none;fill:ButtonText}}.swagger-ui .info{margin:50px 0}.swagger-ui .info.failed-config{margin-left:auto;margin-right:auto;max-width:880px;text-align:center}.swagger-ui .info hgroup.main{margin:0 0 20px}.swagger-ui .info hgroup.main a{font-size:12px}.swagger-ui .info li,.swagger-ui .info p,.swagger-ui .info pre,.swagger-ui .info table{font-size:14px}.swagger-ui .info h1,.swagger-ui .info h2,.swagger-ui .info h3,.swagger-ui .info h4,.swagger-ui .info h5,.swagger-ui .info li,.swagger-ui .info p,.swagger-ui .info table{color:#3b4151;font-family:sans-serif}.swagger-ui .info a{color:#4990e2;font-family:sans-serif;font-size:14px;transition:all .4s}.swagger-ui .info a:hover{color:#1f69c0}.swagger-ui .info>div{margin:0 0 5px}.swagger-ui .info .base-url{color:#3b4151;font-family:monospace;font-size:12px;font-weight:300!important;font-weight:600;margin:0}.swagger-ui .info .title{color:#3b4151;font-family:sans-serif;font-size:36px;margin:0}.swagger-ui .info .title small{background:#7d8492;border-radius:57px;display:inline-block;font-size:10px;margin:0 0 0 5px;padding:2px 4px;position:relative;top:-5px;vertical-align:super}.swagger-ui .info .title small.version-stamp{background-color:#89bf04}.swagger-ui .info .title small pre{color:#fff;font-family:sans-serif;margin:0;padding:0}.swagger-ui .auth-btn-wrapper{display:flex;justify-content:center;padding:10px 0}.swagger-ui .auth-btn-wrapper .btn-done{margin-left:1em}.swagger-ui .auth-wrapper{display:flex;flex:1;justify-content:flex-end}.swagger-ui .auth-wrapper .authorize{margin-left:10px;margin-right:10px;padding-right:20px}.swagger-ui .auth-container{border-bottom:1px solid #ebebeb;margin:0 0 10px;padding:10px 20px}.swagger-ui .auth-container:last-of-type{border:0;margin:0;padding:10px 20px}.swagger-ui .auth-container h4{margin:5px 0 15px!important}.swagger-ui .auth-container .wrapper{margin:0;padding:0}.swagger-ui .auth-container input[type=password],.swagger-ui .auth-container input[type=text]{min-width:230px}.swagger-ui .auth-container .errors{background-color:#fee;border-radius:4px;color:red;color:#3b4151;font-family:monospace;font-size:12px;font-weight:600;margin:1em;padding:10px}.swagger-ui .auth-container .errors b{margin-right:1em;text-transform:capitalize}.swagger-ui .scopes h2{color:#3b4151;font-family:sans-serif;font-size:14px}.swagger-ui .scopes h2 a{color:#4990e2;cursor:pointer;font-size:12px;padding-left:10px;-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .scope-def{padding:0 0 20px}.swagger-ui .errors-wrapper{animation:scaleUp .5s;background:#f93e3e1a;border:2px solid #f93e3e;border-radius:4px;margin:20px;padding:10px 20px}.swagger-ui .errors-wrapper .error-wrapper{margin:0 0 10px}.swagger-ui .errors-wrapper .errors h4{color:#3b4151;font-family:monospace;font-size:14px;font-weight:600;margin:0}.swagger-ui .errors-wrapper .errors small{color:#606060}.swagger-ui .errors-wrapper .errors .message{white-space:pre-line}.swagger-ui .errors-wrapper .errors .message.thrown{max-width:100%}.swagger-ui .errors-wrapper .errors .error-line{cursor:pointer;-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .errors-wrapper hgroup{align-items:center;display:flex}.swagger-ui .errors-wrapper hgroup h4{color:#3b4151;flex:1;font-family:sans-serif;font-size:20px;margin:0}@keyframes scaleUp{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}.swagger-ui .Resizer.vertical.disabled{display:none}.swagger-ui .markdown p,.swagger-ui .markdown pre,.swagger-ui .renderedMarkdown p,.swagger-ui .renderedMarkdown pre{margin:1em auto;word-break:break-all;word-break:break-word}.swagger-ui .markdown pre,.swagger-ui .renderedMarkdown pre{background:none;color:#000;font-weight:400;padding:0;white-space:pre-wrap}.swagger-ui .markdown code,.swagger-ui .renderedMarkdown code{background:#0000000d;border-radius:4px;color:#9012fe;font-family:monospace;font-size:14px;font-weight:600;padding:0 5px}.swagger-ui .markdown pre>code,.swagger-ui .renderedMarkdown pre>code{display:block}.swagger-ui .json-schema-2020-12-keyword--\$vocabulary ul{border-left:1px dashed rgba(0,0,0,.1);margin:0 0 0 20px}.swagger-ui .json-schema-2020-12-\$vocabulary-uri{margin-left:35px}.swagger-ui .json-schema-2020-12-\$vocabulary-uri--disabled{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .json-schema-2020-12-keyword--const .json-schema-2020-12-json-viewer__name,.swagger-ui .json-schema-2020-12-keyword--const .json-schema-2020-12-json-viewer__value{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12__constraint{background-color:#805ad5;border-radius:4px;color:#3b4151;color:#fff;font-family:monospace;font-weight:600;line-height:1.5;margin-left:10px;padding:1px 3px}.swagger-ui .json-schema-2020-12__constraint--string{background-color:#d69e2e;color:#fff}.swagger-ui .json-schema-2020-12-keyword--default .json-schema-2020-12-json-viewer__name,.swagger-ui .json-schema-2020-12-keyword--default .json-schema-2020-12-json-viewer__value{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12-keyword--dependentRequired>ul{display:inline-block;margin:0;padding:0}.swagger-ui .json-schema-2020-12-keyword--dependentRequired>ul li{display:inline;list-style-type:none}.swagger-ui .json-schema-2020-12-keyword--description{color:#6b6b6b;font-size:12px;margin-left:20px}.swagger-ui .json-schema-2020-12-keyword--description p{margin:0}.swagger-ui .json-schema-2020-12-keyword--enum .json-schema-2020-12-json-viewer__name,.swagger-ui .json-schema-2020-12-keyword--enum .json-schema-2020-12-json-viewer__value,.swagger-ui .json-schema-2020-12-keyword--examples .json-schema-2020-12-json-viewer__name,.swagger-ui .json-schema-2020-12-keyword--examples .json-schema-2020-12-json-viewer__value{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12-json-viewer-extension-keyword .json-schema-2020-12-json-viewer__name,.swagger-ui .json-schema-2020-12-json-viewer-extension-keyword .json-schema-2020-12-json-viewer__value{color:#929292;font-style:italic}.swagger-ui .json-schema-2020-12-keyword--patternProperties ul{border:none;margin:0;padding:0}.swagger-ui .json-schema-2020-12-keyword--patternProperties .json-schema-2020-12__title:first-of-type:after,.swagger-ui .json-schema-2020-12-keyword--patternProperties .json-schema-2020-12__title:first-of-type:before{color:#55a;content:"/"}.swagger-ui .json-schema-2020-12-keyword--properties>ul{border:none;margin:0;padding:0}.swagger-ui .json-schema-2020-12-property{list-style-type:none}.swagger-ui .json-schema-2020-12-property--required>.json-schema-2020-12:first-of-type>.json-schema-2020-12-head .json-schema-2020-12__title:after{color:red;content:"*";font-weight:700}.swagger-ui .json-schema-2020-12__title{color:#505050;display:inline-block;font-family:sans-serif;font-size:12px;font-weight:700;line-height:normal}.swagger-ui .json-schema-2020-12__title .json-schema-2020-12-keyword__name{margin:0}.swagger-ui .json-schema-2020-12-property{margin:7px 0}.swagger-ui .json-schema-2020-12-property .json-schema-2020-12__title{color:#3b4151;font-family:monospace;font-size:12px;font-weight:600;vertical-align:middle}.swagger-ui .json-schema-2020-12-keyword{margin:5px 0}.swagger-ui .json-schema-2020-12-keyword__children{border-left:1px dashed rgba(0,0,0,.1);margin:0 0 0 20px;padding:0}.swagger-ui .json-schema-2020-12-keyword__children--collapsed{display:none}.swagger-ui .json-schema-2020-12-keyword__name{font-size:12px;font-weight:700;margin-left:20px}.swagger-ui .json-schema-2020-12-keyword__name--primary{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12-keyword__name--secondary{color:#6b6b6b;font-style:italic}.swagger-ui .json-schema-2020-12-keyword__name--extension{color:#929292;font-style:italic}.swagger-ui .json-schema-2020-12-keyword__value{color:#6b6b6b;font-size:12px;font-style:italic;font-weight:400}.swagger-ui .json-schema-2020-12-keyword__value--primary{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12-keyword__value--secondary{color:#6b6b6b;font-style:italic}.swagger-ui .json-schema-2020-12-keyword__value--extension{color:#929292;font-style:italic}.swagger-ui .json-schema-2020-12-keyword__value--warning{border:1px dashed red;border-radius:4px;color:#3b4151;color:red;display:inline-block;font-family:monospace;font-style:normal;font-weight:600;line-height:1.5;margin-left:10px;padding:1px 4px}.swagger-ui .json-schema-2020-12-keyword__name--secondary+.json-schema-2020-12-keyword__value--secondary:before{content:"="}.swagger-ui .json-schema-2020-12__attribute{color:#3b4151;font-family:monospace;font-size:12px;padding-left:10px;text-transform:lowercase}.swagger-ui .json-schema-2020-12__attribute--primary{color:#55a}.swagger-ui .json-schema-2020-12__attribute--muted{color:gray}.swagger-ui .json-schema-2020-12__attribute--warning{color:red}.swagger-ui .json-schema-2020-12-json-viewer{margin:5px 0}.swagger-ui .json-schema-2020-12-json-viewer__children{border-left:1px dashed rgba(0,0,0,.1);margin:0 0 0 20px;padding:0}.swagger-ui .json-schema-2020-12-json-viewer__children--collapsed{display:none}.swagger-ui .json-schema-2020-12-json-viewer__name{font-size:12px;font-weight:700;margin-left:20px}.swagger-ui .json-schema-2020-12-json-viewer__name--primary{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12-json-viewer__name--secondary{color:#6b6b6b;font-style:italic}.swagger-ui .json-schema-2020-12-json-viewer__name--extension{color:#929292;font-style:italic}.swagger-ui .json-schema-2020-12-json-viewer__value{color:#6b6b6b;font-size:12px;font-style:italic;font-weight:400}.swagger-ui .json-schema-2020-12-json-viewer__value--primary{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12-json-viewer__value--secondary{color:#6b6b6b;font-style:italic}.swagger-ui .json-schema-2020-12-json-viewer__value--extension{color:#929292;font-style:italic}.swagger-ui .json-schema-2020-12-json-viewer__value--warning{border:1px dashed red;border-radius:4px;color:#3b4151;color:red;display:inline-block;font-family:monospace;font-style:normal;font-weight:600;line-height:1.5;margin-left:10px;padding:1px 4px}.swagger-ui .json-schema-2020-12-json-viewer__name--secondary+.json-schema-2020-12-json-viewer__value--secondary:before{content:"="}.swagger-ui .json-schema-2020-12{background-color:#0000000d;border-radius:4px;margin:0 20px 15px;padding:12px 0 12px 20px}.swagger-ui .json-schema-2020-12:first-of-type{margin:20px}.swagger-ui .json-schema-2020-12:last-of-type{margin:0 20px}.swagger-ui .json-schema-2020-12--embedded{background-color:inherit;padding-bottom:0;padding-left:inherit;padding-right:inherit;padding-top:0}.swagger-ui .json-schema-2020-12-body{border-left:1px dashed rgba(0,0,0,.1);margin:2px 0}.swagger-ui .json-schema-2020-12-body--collapsed{display:none}.swagger-ui .json-schema-2020-12-accordion{border:none;outline:none;padding-left:0}.swagger-ui .json-schema-2020-12-accordion__children{display:inline-block}.swagger-ui .json-schema-2020-12-accordion__icon{display:inline-block;height:18px;vertical-align:bottom;width:18px}.swagger-ui .json-schema-2020-12-accordion__icon--expanded{transform:rotate(-90deg);transform-origin:50% 50%;transition:transform .15s ease-in}.swagger-ui .json-schema-2020-12-accordion__icon--collapsed{transform:rotate(0);transform-origin:50% 50%;transition:transform .15s ease-in}.swagger-ui .json-schema-2020-12-accordion__icon svg{height:20px;width:20px}.swagger-ui .json-schema-2020-12-expand-deep-button{border:none;color:#505050;color:#afaeae;font-family:sans-serif;font-size:12px;padding-right:0}.swagger-ui .model-box .json-schema-2020-12:not(.json-schema-2020-12--embedded)>.json-schema-2020-12-head .json-schema-2020-12__title:first-of-type{font-size:16px}.swagger-ui .model-box>.json-schema-2020-12{margin:0}.swagger-ui .model-box .json-schema-2020-12{background-color:transparent;padding:0}.swagger-ui .model-box .json-schema-2020-12-accordion,.swagger-ui .model-box .json-schema-2020-12-expand-deep-button{background-color:transparent}.swagger-ui .models .json-schema-2020-12:not(.json-schema-2020-12--embedded)>.json-schema-2020-12-head .json-schema-2020-12__title:first-of-type{font-size:16px}.swagger-ui .models .json-schema-2020-12:not(.json-schema-2020-12--embedded){overflow-x:auto;width:calc(100% - 40px)}html.dark-mode{--csstools-color-scheme--light: ;background:#1c2022;color-scheme:dark}html.dark-mode .swagger-ui{background:#1c2022;color:#e4e6e6}html.dark-mode .swagger-ui .authorization__btn svg,html.dark-mode .swagger-ui .expand-operation svg,html.dark-mode .swagger-ui .opblock-control-arrow svg{color:#b7bcbf;opacity:1}html.dark-mode .swagger-ui .markdown li,html.dark-mode .swagger-ui .markdown p,html.dark-mode .swagger-ui .markdown pre,html.dark-mode .swagger-ui .renderedMarkdown li,html.dark-mode .swagger-ui .renderedMarkdown p,html.dark-mode .swagger-ui .renderedMarkdown pre,html.dark-mode .swagger-ui section h3,html.dark-mode .swagger-ui table thead tr td,html.dark-mode .swagger-ui table thead tr th{color:#e4e6e6}html.dark-mode .swagger-ui .markdown code,html.dark-mode .swagger-ui .renderedMarkdown code{background:#080a0b;color:#b68ae1}html.dark-mode .swagger-ui input{background:#1c2022;border-color:#b7bcbf;color:#f0f1f1}html.dark-mode .swagger-ui input:focus:not(.download-url-input){border-color:#51a8ff!important;box-shadow:none;outline:none}html.dark-mode .swagger-ui textarea{background:#0d1014;border:1px solid #0d1014;color:#f0f1f1}html.dark-mode .swagger-ui textarea:focus{border-color:#51a8ff}html.dark-mode .swagger-ui textarea[disabled]{background-color:#202225;border-color:#202225;color:#8c969a}html.dark-mode .swagger-ui select{background:#1c2022 url('data:image/svg+xml;charset=utf-8,') right 10px center no-repeat;border-color:#b7bcbf;box-shadow:none;color:#f0f1f1;outline:none}html.dark-mode .swagger-ui select[multiple]{background:#1c2022}html.dark-mode .swagger-ui select:focus{border-color:#51a8ff}html.dark-mode .swagger-ui input::-moz-placeholder,html.dark-mode .swagger-ui textarea::-moz-placeholder{color:#f0f1f1;opacity:.5}html.dark-mode .swagger-ui input::placeholder,html.dark-mode .swagger-ui textarea::placeholder{color:#f0f1f1;opacity:.5}html.dark-mode .swagger-ui input.invalid,html.dark-mode .swagger-ui select.invalid,html.dark-mode .swagger-ui textarea.invalid{background:#1c2022;border-color:#ff5f5f}html.dark-mode .swagger-ui .topbar{background:#2a2e30}html.dark-mode .swagger-ui .topbar .download-url-wrapper .download-url-button{background:#1d632e;color:#e4e6e6}html.dark-mode .swagger-ui .topbar .download-url-wrapper .download-url-input{border-color:#1d632e}html.dark-mode .swagger-ui .topbar .download-url-wrapper .download-url-input.failed{color:#ff5f5f}html.dark-mode .swagger-ui .dialog-ux .modal-ux{background-color:#2a2e30;border:none;color:#e4e6e6}html.dark-mode .swagger-ui .dialog-ux .modal-ux-header{border-color:#545d61}html.dark-mode .swagger-ui .dialog-ux .modal-ux h2,html.dark-mode .swagger-ui .dialog-ux .modal-ux h3,html.dark-mode .swagger-ui .dialog-ux .modal-ux h4,html.dark-mode .swagger-ui .dialog-ux .modal-ux h5,html.dark-mode .swagger-ui .dialog-ux .modal-ux label,html.dark-mode .swagger-ui .dialog-ux .modal-ux p,html.dark-mode .swagger-ui .dialog-ux .modal-ux-header .close-modal svg{color:#e4e6e6}html.dark-mode .swagger-ui .dialog-ux .modal-ux .scopes a{color:#51a8ff}html.dark-mode .swagger-ui .dialog-ux .modal-ux .btn.modal-btn{border-color:#3ece90;color:#3ece90}html.dark-mode .swagger-ui .dialog-ux .modal-ux .btn.modal-btn.btn-done{border-color:#e4e6e6;color:#e4e6e6}html.dark-mode .swagger-ui .dialog-ux .modal-ux .auth-container{border-color:#545d61}html.dark-mode .swagger-ui .dialog-ux .modal-ux .checkbox input[type=checkbox]+label>.item{background:#545d61;box-shadow:none;color:#f0f1f1!important}html.dark-mode .swagger-ui .dialog-ux .modal-ux .checkbox input[type=checkbox]:checked+label>.item{background:#545d61 url('data:image/svg+xml;charset=utf-8,') 50% no-repeat}@media(forced-colors:active){html.dark-mode .swagger-ui .dialog-ux .modal-ux .checkbox input[type=checkbox]:checked+label>.item{background-color:ButtonText;background-image:none;forced-color-adjust:none;-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,');-webkit-mask-position:center center;mask-position:center center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}}html.dark-mode .swagger-ui .loading-container .loading:before{border-color:#e4e6e6 #545d61 #545d61}html.dark-mode .swagger-ui .loading-container .loading:after{color:#e4e6e6}html.dark-mode .swagger-ui .scheme-container{background:#1c2022;box-shadow:0 1px 2px #545d61}html.dark-mode .swagger-ui .scheme-container .schemes>.schemes-server-container>label{color:#e4e6e6}html.dark-mode .swagger-ui .scheme-container .btn.authorize{border-color:#3ece90;color:#3ece90}html.dark-mode .swagger-ui .scheme-container .btn.authorize svg{color:#3ece90}html.dark-mode .swagger-ui .info .title,html.dark-mode .swagger-ui .info h1,html.dark-mode .swagger-ui .info h2,html.dark-mode .swagger-ui .info h3,html.dark-mode .swagger-ui .info h4,html.dark-mode .swagger-ui .info h5{color:#d2d6d7}html.dark-mode .swagger-ui .info .base-url,html.dark-mode .swagger-ui .info li,html.dark-mode .swagger-ui .info p,html.dark-mode .swagger-ui .info table{color:#e4e6e6}html.dark-mode .swagger-ui .info a{color:#51a8ff}html.dark-mode .swagger-ui .info .title small{background:#434b4f}html.dark-mode .swagger-ui .info .title small.version-stamp{background:#1d632e}html.dark-mode .swagger-ui .info .errors-wrapper{background:#434b4f;border-color:#ff5f5f}html.dark-mode .swagger-ui .info .errors-wrapper h4,html.dark-mode .swagger-ui .info .errors-wrapper span{color:#e4e6e6}html.dark-mode .swagger-ui .info .errors-wrapper .btn.errors__clear-btn{border-color:#e4e6e6;color:#e4e6e6}html.dark-mode .swagger-ui .copy-to-clipboard,html.dark-mode .swagger-ui .download-contents{background:#545d61;color:#e4e6e6}html.dark-mode .swagger-ui .copy-to-clipboard button,html.dark-mode .swagger-ui .download-contents button{background:url('data:image/svg+xml;charset=utf-8,') 50% no-repeat}@media(forced-colors:active){html.dark-mode .swagger-ui .copy-to-clipboard button,html.dark-mode .swagger-ui .download-contents button{background-color:ButtonText;background-image:none;forced-color-adjust:none;-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,');-webkit-mask-position:center center;mask-position:center center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}}html.dark-mode .swagger-ui .opblock-tag{border-bottom-color:#545d61;color:#e4e6e6}html.dark-mode .swagger-ui .opblock-tag small{color:#e4e6e6}html.dark-mode .swagger-ui .opblock-tag a.link{color:#51a8ff}html.dark-mode .swagger-ui .opblock.opblock-post{background:#112929;border-color:#104834}html.dark-mode .swagger-ui .opblock.opblock-post thead tr td,html.dark-mode .swagger-ui .opblock.opblock-post thead tr th{border-color:#104834;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-post .opblock-section-header{background:#14392c;border-bottom:1px solid #104834;border-top:1px solid #104834}html.dark-mode .swagger-ui .opblock.opblock-post .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#00b572}html.dark-mode .swagger-ui .opblock.opblock-post .opblock-summary{border-bottom:none;border-color:#104834}html.dark-mode .swagger-ui .opblock.opblock-post .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-post .opblock-summary-method{background:#00b572;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-post .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-post .opblock-body>.opblock-title_normal{border-top:1px solid #104834}html.dark-mode .swagger-ui .opblock.opblock-deprecated{background:#272c34;border-color:#495361}html.dark-mode .swagger-ui .opblock.opblock-deprecated thead tr td,html.dark-mode .swagger-ui .opblock.opblock-deprecated thead tr th{border-color:#495361;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-section-header{background:#262e36;border-bottom:1px solid #495361;border-top:1px solid #495361}html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#6a6a6a}html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-summary{border-bottom:none;border-color:#495361}html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-summary-method{background:#6a6a6a;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-body>.opblock-title_normal{border-top:1px solid #495361}html.dark-mode .swagger-ui .opblock.opblock-put{background:#27201e;border-color:#523524}html.dark-mode .swagger-ui .opblock.opblock-put thead tr td,html.dark-mode .swagger-ui .opblock.opblock-put thead tr th{border-color:#523524;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-put .opblock-section-header{background:#9a5b3e;border-bottom:1px solid #523524;border-top:1px solid #523524}html.dark-mode .swagger-ui .opblock.opblock-put .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#ff7d35}html.dark-mode .swagger-ui .opblock.opblock-put .opblock-summary{border-bottom:none;border-color:#523524}html.dark-mode .swagger-ui .opblock.opblock-put .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-put .opblock-summary-method{background:#ff7d35;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-put .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-put .opblock-body>.opblock-title_normal{border-top:1px solid #523524}html.dark-mode .swagger-ui .opblock.opblock-get{background:#182536;border-color:#294262}html.dark-mode .swagger-ui .opblock.opblock-get thead tr td,html.dark-mode .swagger-ui .opblock.opblock-get thead tr th{border-color:#294262;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-get .opblock-section-header{background:#1c3043;border-bottom:1px solid #294262;border-top:1px solid #294262}html.dark-mode .swagger-ui .opblock.opblock-get .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#55a1ff}html.dark-mode .swagger-ui .opblock.opblock-get .opblock-summary{border-bottom:none;border-color:#294262}html.dark-mode .swagger-ui .opblock.opblock-get .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-get .opblock-summary-method{background:#55a1ff;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-get .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-get .opblock-body>.opblock-title_normal{border-top:1px solid #294262}html.dark-mode .swagger-ui .opblock.opblock-delete{background:#241a20;border-color:#4b2420}html.dark-mode .swagger-ui .opblock.opblock-delete thead tr td,html.dark-mode .swagger-ui .opblock.opblock-delete thead tr th{border-color:#4b2420;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-section-header{background:#2f2020;border-bottom:1px solid #4b2420;border-top:1px solid #4b2420}html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#eb6156}html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-summary{border-bottom:none;border-color:#4b2420}html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-summary-method{background:#eb6156;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-body>.opblock-title_normal{border-top:1px solid #4b2420}html.dark-mode .swagger-ui .opblock.opblock-patch{background:#11282f;border-color:#16494b}html.dark-mode .swagger-ui .opblock.opblock-patch thead tr td,html.dark-mode .swagger-ui .opblock.opblock-patch thead tr th{border-color:#16494b;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-section-header{background:#113239;border-bottom:1px solid #16494b;border-top:1px solid #16494b}html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#03b7bf}html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-summary{border-bottom:none;border-color:#16494b}html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-summary-method{background:#03b7bf;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-body>.opblock-title_normal{border-top:1px solid #16494b}html.dark-mode .swagger-ui .opblock.opblock-head{background:#282231;border-color:#44336a}html.dark-mode .swagger-ui .opblock.opblock-head thead tr td,html.dark-mode .swagger-ui .opblock.opblock-head thead tr th{border-color:#44336a;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-head .opblock-section-header{background:#352c45;border-bottom:1px solid #44336a;border-top:1px solid #44336a}html.dark-mode .swagger-ui .opblock.opblock-head .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#b889ff}html.dark-mode .swagger-ui .opblock.opblock-head .opblock-summary{border-bottom:none;border-color:#44336a}html.dark-mode .swagger-ui .opblock.opblock-head .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-head .opblock-summary-method{background:#b889ff;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-head .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-head .opblock-body>.opblock-title_normal{border-top:1px solid #44336a}html.dark-mode .swagger-ui .opblock.opblock-options{background:#202c3c;border-color:#33465e}html.dark-mode .swagger-ui .opblock.opblock-options thead tr td,html.dark-mode .swagger-ui .opblock.opblock-options thead tr th{border-color:#33465e;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-options .opblock-section-header{background:#314558;border-bottom:1px solid #33465e;border-top:1px solid #33465e}html.dark-mode .swagger-ui .opblock.opblock-options .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#6895c8}html.dark-mode .swagger-ui .opblock.opblock-options .opblock-summary{border-bottom:none;border-color:#33465e}html.dark-mode .swagger-ui .opblock.opblock-options .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-options .opblock-summary-method{background:#6895c8;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-options .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-options .opblock-body>.opblock-title_normal{border-top:1px solid #33465e}html.dark-mode .swagger-ui .opblock.opblock-query{background:#2a1a28;border-color:#4a2848}html.dark-mode .swagger-ui .opblock.opblock-query thead tr td,html.dark-mode .swagger-ui .opblock.opblock-query thead tr th{border-color:#4a2848;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-query .opblock-section-header{background:#3a2238;border-bottom:1px solid #4a2848;border-top:1px solid #4a2848}html.dark-mode .swagger-ui .opblock.opblock-query .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#d977c6}html.dark-mode .swagger-ui .opblock.opblock-query .opblock-summary{border-bottom:none;border-color:#4a2848}html.dark-mode .swagger-ui .opblock.opblock-query .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-query .opblock-summary-method{background:#d977c6;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-query .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-query .opblock-body>.opblock-title_normal{border-top:1px solid #4a2848}html.dark-mode .swagger-ui .opblock .opblock-section-header{box-shadow:none}html.dark-mode .swagger-ui .opblock .opblock-section-header h4,html.dark-mode .swagger-ui .opblock .opblock-section-header label{color:#e4e6e6}html.dark-mode .swagger-ui .opblock .opblock-section-header .try-out__btn{border-color:#b7bcbf;box-shadow:none;color:#e4e6e6}html.dark-mode .swagger-ui .opblock .opblock-section-header .try-out__btn.cancel{border-color:#ff5f5f;color:#ff5f5f}html.dark-mode .swagger-ui .opblock .btn.json-schema-form-item-add,html.dark-mode .swagger-ui .opblock .btn.json-schema-form-item-remove{border-color:#e4e6e6;color:#e4e6e6}html.dark-mode .swagger-ui .opblock .validation-errors.errors-wrapper{background:#434b4f;border-color:#ff5f5f;color:#e4e6e6}html.dark-mode .swagger-ui .opblock .body-param-options label span,html.dark-mode .swagger-ui .opblock .opblock-description-wrapper i,html.dark-mode .swagger-ui .opblock .opblock-description-wrapper p,html.dark-mode .swagger-ui .opblock .opblock-external-docs-wrapper,html.dark-mode .swagger-ui .opblock .opblock-summary-description,html.dark-mode .swagger-ui .opblock .opblock-summary-operation-id,html.dark-mode .swagger-ui .opblock .opblock-summary-path,html.dark-mode .swagger-ui .opblock .opblock-summary-path__deprecated,html.dark-mode .swagger-ui .opblock .opblock-title_normal,html.dark-mode .swagger-ui .opblock .parameter__in,html.dark-mode .swagger-ui .opblock .parameter__name,html.dark-mode .swagger-ui .opblock .parameter__type,html.dark-mode .swagger-ui .opblock .parameter__type .prop-format,html.dark-mode .swagger-ui .opblock .response-col_links,html.dark-mode .swagger-ui .opblock .response-col_status,html.dark-mode .swagger-ui .opblock .response-col_undocumented{color:#e4e6e6}html.dark-mode .swagger-ui .opblock .opblock-external-docs a.link{color:#51a8ff}html.dark-mode .swagger-ui .opblock .parameter__name.required span,html.dark-mode .swagger-ui .opblock .parameter__name.required:after{color:#ff5f5f}html.dark-mode .swagger-ui .opblock .parameter__empty_value_toggle{color:#e4e6e6}html.dark-mode .swagger-ui .opblock .btn.execute{background:#51a8ff;border-color:#51a8ff;color:#080a0b}html.dark-mode .swagger-ui .opblock .btn.btn-clear{border-color:#e4e6e6;color:#e4e6e6}html.dark-mode .swagger-ui .opblock .highlight-code pre.microlight{background:#2a2e30!important;color:#f0f1f1}html.dark-mode .swagger-ui .opblock .curl-command .btn{background:#3b424d!important;border-color:#2a2e30!important;color:#ebebeb!important}html.dark-mode .swagger-ui .opblock .curl-command .btn.active{background:#2a2e30!important;color:#e4e6e6!important}html.dark-mode .swagger-ui .opblock pre.microlight{background:#2a2e30!important;color:#f0f1f1}html.dark-mode .swagger-ui .opblock .model-example .tab button{color:#e4e6e6}html.dark-mode .swagger-ui .opblock .model-example .tabitem:after{background:#6b757a}html.dark-mode .swagger-ui .opblock .responses-inner h4,html.dark-mode .swagger-ui .opblock .responses-inner h5{color:#e4e6e6}html.dark-mode .swagger-ui .opblock .response-control-media-type--accept-controller select.content-type{border-color:#4ac966}html.dark-mode .swagger-ui .opblock .response-control-media-type--accept-controller .response-control-media-type__accept-message{color:#4ac966}html.dark-mode .swagger-ui .model-toggle:after{background:url('data:image/svg+xml;charset=utf-8,') 50% no-repeat;background-size:100%}@media(forced-colors:active){html.dark-mode .swagger-ui .model-toggle:after{background-color:CanvasText;background-image:none;forced-color-adjust:none;-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,');-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100%;mask-size:100%}}html.dark-mode .swagger-ui .model .prop-type{color:#b68ae1}html.dark-mode .swagger-ui .model .brace-close,html.dark-mode .swagger-ui .model .brace-open,html.dark-mode .swagger-ui .model .description,html.dark-mode .swagger-ui .model .prop-format,html.dark-mode .swagger-ui .model .property,html.dark-mode .swagger-ui .model .property-row{color:#e4e6e6}html.dark-mode .swagger-ui .model .property-row.required .star{color:#ff5f5f}html.dark-mode .swagger-ui .model-box{background:#2a2e30}html.dark-mode .swagger-ui .model-box .model,html.dark-mode .swagger-ui .model-box .model-title{color:#e4e6e6}html.dark-mode .swagger-ui .model-box-control:focus{outline:none}html.dark-mode .swagger-ui .model-box-control:not(.prop){color:#e4e6e6}html.dark-mode .swagger-ui .json-schema-2020-12,html.dark-mode .swagger-ui .json-schema-2020-12 button{background:#2a2e30}html.dark-mode .swagger-ui .json-schema-2020-12 button svg{color:#e4e6e6}html.dark-mode .swagger-ui .json-schema-2020-12 a{color:#51a8ff}html.dark-mode .swagger-ui .json-schema-2020-12__title{color:#e4e6e6}html.dark-mode .swagger-ui .json-schema-2020-12-property--required>.json-schema-2020-12:first-of-type>.json-schema-2020-12-head .json-schema-2020-12__title:after{color:#ff5f5f}html.dark-mode .swagger-ui .json-schema-2020-12-expand-deep-button{color:#b7bcbf}html.dark-mode .swagger-ui .json-schema-2020-12-body{border-color:#b7bcbf}html.dark-mode .swagger-ui .json-schema-2020-12-keyword__name--primary{color:#e4e6e6}html.dark-mode .swagger-ui .json-schema-2020-12-keyword__name--secondary,html.dark-mode .swagger-ui .json-schema-2020-12-keyword__value--secondary{color:#b7bcbf}html.dark-mode .swagger-ui .json-schema-2020-12-keyword__value--warning{border-color:#ff5f5f;color:#ff5f5f}html.dark-mode .swagger-ui .json-schema-2020-12-keyword--\$vocabulary ul{border-color:#b7bcbf}html.dark-mode .swagger-ui .json-schema-2020-12-keyword--patternProperties .json-schema-2020-12__title:after,html.dark-mode .swagger-ui .json-schema-2020-12-keyword--patternProperties .json-schema-2020-12__title:before,html.dark-mode .swagger-ui .json-schema-2020-12__attribute--primary{color:#9898ff}html.dark-mode .swagger-ui .json-schema-2020-12__attribute--muted{color:#b7bcbf}html.dark-mode .swagger-ui .json-schema-2020-12__attribute--warning{color:#ff5f5f}html.dark-mode .swagger-ui .json-schema-2020-12-json-viewer__name--secondary,html.dark-mode .swagger-ui .json-schema-2020-12-json-viewer__value--secondary{color:#b7bcbf}html.dark-mode .swagger-ui .json-schema-2020-12__constraint{background:#9898ff;color:#080a0b}html.dark-mode .swagger-ui .json-schema-2020-12__constraint--string{background:#d4aa53}html.dark-mode .swagger-ui section.models,html.dark-mode .swagger-ui section.models h4{border-color:#545d61}html.dark-mode .swagger-ui section.models h4 span{color:#e4e6e6}html.dark-mode .swagger-ui section.models .model-container{background:#2a2e30}html.dark-mode .swagger-ui section.models .models-control:focus{outline:none}html.dark-mode .swagger-ui section.models .models-control svg{color:#b7bcbf}.openapi-head[data-v-0a44808c]{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:14px}.openapi-wrap[data-v-0a44808c] .swagger-ui{border:none} diff --git a/server/web/dist/assets/PageShell-D87JkPkG.js b/server/web/dist/assets/PageShell-D87JkPkG.js new file mode 100644 index 0000000..48a7b77 --- /dev/null +++ b/server/web/dist/assets/PageShell-D87JkPkG.js @@ -0,0 +1 @@ +import{d as h,u as g,a as k,c as n,G as i,b as t,Q as y,e as v,f as e,t as a,g as c,q as r,A as d,F as x,h as B,O as C,o as l,_ as b}from"./index-DYsKpclu.js";import{_ as w}from"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";const N={class:"site-footer"},S={class:"footer-left"},F={key:0,class:"footer-text"},P={key:1,class:"footer-beian"},T={key:2},V=["aria-label"],$=h({__name:"PageShell",setup(D){const{t:s}=g(),o=k(),m=new Date().getFullYear(),u=B(()=>!!(o.footerText.trim()||o.footerBeian.trim()));return(p,_)=>{const f=C("RouterLink");return l(),n(x,null,[i(w),t("main",{class:v(["page",{"page-wide":p.$route.meta.wide}])},[y(p.$slots,"default",{},void 0,!0)],2),t("footer",N,[t("div",S,[e(o).footerText.trim()?(l(),n("span",F,a(e(o).footerText),1)):c("",!0),e(o).footerBeian.trim()?(l(),n("span",P,a(e(o).footerBeian),1)):c("",!0),u.value?c("",!0):(l(),n("span",T,a(e(s)("footer.copyright",{year:e(m),name:e(o).displayName})),1))]),_[0]||(_[0]=t("span",{class:"footer-powered"},[r(" Powered by "),t("a",{href:"https://skymirror.top",target:"_blank",rel:"noopener noreferrer"},"SKYMirror"),r(" 26.9 ")],-1)),t("nav",{"aria-label":e(s)("footer.linkNav")},[i(f,{to:"/docs"},{default:d(()=>[r(a(e(s)("footer.docs")),1)]),_:1}),i(f,{to:"/openapi"},{default:d(()=>[r(a(e(s)("footer.openapi")),1)]),_:1}),i(f,{to:"/admin/files"},{default:d(()=>[r(a(e(s)("footer.admin")),1)]),_:1})],8,V)])],64)}}}),R=b($,[["__scopeId","data-v-80685d6f"]]);export{R as P}; diff --git a/server/web/dist/assets/PageShell-PYQzNiuf.css b/server/web/dist/assets/PageShell-PYQzNiuf.css new file mode 100644 index 0000000..c1bca7c --- /dev/null +++ b/server/web/dist/assets/PageShell-PYQzNiuf.css @@ -0,0 +1 @@ +.footer-left[data-v-80685d6f]{display:flex;flex-direction:column;gap:2px;min-width:0}.footer-powered[data-v-80685d6f]{font-size:13px;color:var(--c-text-2);white-space:nowrap}.footer-powered a[data-v-80685d6f]{color:var(--c-text-2);text-decoration:none;border-bottom:1px dotted currentColor}.footer-powered a[data-v-80685d6f]:hover{color:var(--c-text)}@media(min-width:720px){.site-footer[data-v-80685d6f]{display:flex;align-items:center;justify-content:space-between;gap:16px}.footer-left[data-v-80685d6f]{flex-direction:row;align-items:center;gap:16px}.footer-left .footer-beian[data-v-80685d6f]:before{content:"·";margin-right:16px;opacity:.7}} diff --git a/server/web/dist/assets/Pager.vue_vue_type_script_setup_true_lang-B3GeH0mY.js b/server/web/dist/assets/Pager.vue_vue_type_script_setup_true_lang-B3GeH0mY.js new file mode 100644 index 0000000..636189a --- /dev/null +++ b/server/web/dist/assets/Pager.vue_vue_type_script_setup_true_lang-B3GeH0mY.js @@ -0,0 +1 @@ +import{d as b,u as p,c as r,b as o,t as i,f as c,h,o as f}from"./index-DYsKpclu.js";const v={class:"pager"},x={class:"pager-info"},k=["disabled"],M=["disabled"],y=b({__name:"Pager",props:{page:{},size:{},total:{}},emits:["change"],setup(t,{emit:m}){const a=t,d=m,{t:n}=p(),s=h(()=>Math.max(1,Math.ceil(a.total/a.size)));function g(l){const e=Math.min(Math.max(1,l),s.value);e!==a.page&&d("change",e,a.size)}return(l,e)=>(f(),r("div",v,[o("span",x,i(c(n)("common.pagerInfo",{total:t.total,page:t.page,pages:s.value})),1),o("button",{class:"btn btn-ghost btn-sm",type:"button",disabled:t.page<=1,onClick:e[0]||(e[0]=u=>g(t.page-1))},i(c(n)("common.previousPage")),9,k),o("button",{class:"btn btn-ghost btn-sm",type:"button",disabled:t.page>=s.value,onClick:e[1]||(e[1]=u=>g(t.page+1))},i(c(n)("common.nextPage")),9,M)]))}});export{y as _}; diff --git a/server/web/dist/assets/PickupView-ClAAhXsM.js b/server/web/dist/assets/PickupView-ClAAhXsM.js new file mode 100644 index 0000000..482dc40 --- /dev/null +++ b/server/web/dist/assets/PickupView-ClAAhXsM.js @@ -0,0 +1 @@ +import{d as N,u as U,i as q,I,J as b,z as L,A as R,H as C,h as T,j as p,b as t,c as u,q as z,t as s,f as n,w as E,C as H,D as $,F as y,s as _,g as w,K as j,L as J,n as K,M as B,k as W,N as G,B as O,o as l}from"./index-DYsKpclu.js";import{P as Q}from"./PageShell-D87JkPkG.js";import{g as X,p as Y,b as Z}from"./share-CqDDLJWI.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";const ee={class:"card",style:{"max-width":"640px",margin:"12px auto 0"}},te={key:0,class:"loading-block"},ae={class:"empty"},oe={style:{"font-weight":"600",color:"var(--c-text)"}},ne={class:"hint"},se=["placeholder"],ie={class:"btn",type:"submit"},le={style:{display:"flex","align-items":"center",gap:"10px","flex-wrap":"wrap","margin-bottom":"4px"}},ue={class:"badge"},re={style:{"font-size":"16px","word-break":"break-all"}},ce={key:0,class:"hint",style:{margin:"0"}},pe={class:"hint",style:{"margin-bottom":"18px"}},de={key:0,class:"loading-block"},me={class:"text-view"},ve={style:{display:"flex",gap:"10px","margin-top":"14px","flex-wrap":"wrap"}},ye={class:"file-summary"},ke={style:{"font-weight":"600","word-break":"break-all"}},fe={class:"hint",style:{margin:"0"}},ge={key:0,style:{margin:"16px 0 6px"}},he={class:"progress"},xe={class:"hint"},_e=["disabled"],Be=N({__name:"PickupView",setup(we){const{t:e}=U(),D=G(),F=O(),k=q(),d=T(()=>String(D.params.code??"").trim().split(/\s+/)[0]),r=p("loading"),m=p(""),o=p(null),v=p(""),f=p(!1),c=p(null);async function g(){if(!d.value){r.value="error",m.value=e("pickup.emptyCode");return}r.value="loading",m.value="",o.value=null,v.value="";try{const a=await X(d.value);if(o.value=a,r.value="ready",a.isText){f.value=!0;try{v.value=await Y(d.value)}finally{f.value=!1}}}catch(a){r.value="error",m.value=a instanceof C?a.code===404?e("pickup.notFound"):a.code===423?e("home.rateLimited"):a.code===428?e("home.notInitialized"):a.msg:e("pickup.failedDefault")}}I(g),b(d,()=>{g()}),b(()=>e("pickup.emptyCode"),()=>{r.value==="error"&&m.value&&g()});async function A(){if(o.value){c.value=0;try{const{blob:a,filename:i}=await Z(d.value,x=>c.value=x);B(a,i||o.value.name||"download"),k.success(e("pickup.downloaded"))}catch(a){k.error(a instanceof C?a.msg:e("pickup.downloadFailed"))}finally{c.value=null}}}async function M(){await W(v.value)?k.success(e("pickup.copied")):k.error(e("common.copyFailed"))}function P(){if(!o.value)return;const a=new Blob([v.value],{type:"text/plain;charset=utf-8"}),i=o.value.name?.includes(".")?o.value.name:`${o.value.name||"text"}.txt`;B(a,i)}const h=p("");function S(){const a=h.value.trim();a&&F.push({name:"pickup",params:{code:a}})}const V=T(()=>{const a=o.value;return a?a.remainingDownloads===null||a.remainingDownloads===void 0||a.remainingDownloads<0?e("pickup.remainingUnlimited"):e("pickup.remainingCount",{n:a.remainingDownloads}):""});return(a,i)=>(l(),L(Q,null,{default:R(()=>[t("section",ee,[r.value==="loading"?(l(),u("div",te,[i[1]||(i[1]=t("span",{class:"spin","aria-hidden":"true"},null,-1)),z(" "+s(n(e)("pickup.querying",{code:d.value})),1)])):r.value==="error"?(l(),u(y,{key:1},[t("div",ae,[i[2]||(i[2]=t("div",{class:"empty-icon"},"📮",-1)),t("p",oe,s(m.value||n(e)("pickup.failed")),1),t("p",ne,s(n(e)("pickup.confirmHint")),1)]),t("form",{class:"quick-pickup",style:{"margin-top":"6px","max-width":"none"},onSubmit:E(S,["prevent"])},[H(t("input",{"onUpdate:modelValue":i[0]||(i[0]=x=>h.value=x),class:"input input-mono",placeholder:n(e)("pickup.retryPlaceholder"),maxlength:"32"},null,8,se),[[$,h.value]]),t("button",ie,s(n(e)("pickup.retryButton")),1)],32)],64)):o.value?(l(),u(y,{key:2},[t("div",le,[t("span",ue,s(o.value.isText?n(e)("common.text"):n(e)("common.file")),1),t("strong",re,s(o.value.name),1),o.value.isText?w("",!0):(l(),u("span",ce,s(n(_)(o.value.size)),1))]),t("p",pe,s(V.value)+" · "+s(n(e)("pickup.expireAt",{time:o.value.expiredAt?n(j)(o.value.expiredAt):n(e)("time.permanent")}))+" · "+s(n(J)(o.value.expiredAt)),1),o.value.isText?(l(),u(y,{key:0},[f.value?(l(),u("div",de,[i[3]||(i[3]=t("span",{class:"spin","aria-hidden":"true"},null,-1)),z(" "+s(n(e)("pickup.loadingText")),1)])):(l(),u(y,{key:1},[t("pre",me,s(v.value),1),t("div",ve,[t("button",{class:"btn",type:"button",onClick:M},s(n(e)("pickup.copyContent")),1),t("button",{class:"btn btn-ghost",type:"button",onClick:P},s(n(e)("pickup.downloadTxt")),1)])],64))],64)):(l(),u(y,{key:1},[t("div",ye,[i[4]||(i[4]=t("span",{style:{"font-size":"30px"},"aria-hidden":"true"},"📄",-1)),t("div",null,[t("div",ke,s(o.value.name),1),t("div",fe,s(n(e)("pickup.sizeUsed",{size:n(_)(o.value.size),n:o.value.usedCount})),1)])]),c.value!==null?(l(),u("div",ge,[t("div",he,[t("i",{style:K({width:`${c.value}%`})},null,4)]),t("p",xe,s(n(e)("pickup.downloading",{percent:c.value})),1)])):w("",!0),t("button",{class:"btn btn-block",type:"button",disabled:c.value!==null,onClick:A}," ⬇ "+s(n(e)("pickup.downloadFile",{size:n(_)(o.value.size)})),9,_e)],64))],64)):w("",!0)])]),_:1}))}});export{Be as default}; diff --git a/server/web/dist/assets/SettingsView-CjSvbH8z.js b/server/web/dist/assets/SettingsView-CjSvbH8z.js new file mode 100644 index 0000000..1758aea --- /dev/null +++ b/server/web/dist/assets/SettingsView-CjSvbH8z.js @@ -0,0 +1,129 @@ +import{a0 as ge,h as F,a1 as Ze,a2 as Me,a3 as Xe,a4 as Qe,a5 as Pe,a6 as et,o as w,a7 as ze,z as ce,c as y,F as G,b as e,a8 as We,a9 as tt,e as st,aa as nt,G as Fe,A as it,ab as ot,ac as at,j as V,ad as Ae,ae as K,J as lt,I as Ye,af as rt,P as dt,ag as ct,ah as ut,d as ve,ai as _t,aj as mt,ak as Ue,al as b,n as Re,am as Ge,an as ft,ao as ht,ap as pt,aq as I,ar as Ee,as as qe,at as Q,au as gt,u as vt,i as bt,a as wt,t as l,f as o,q as M,w as He,C as f,D as v,g as L,Z as _e,s as Oe,R as ke,S as p,H as re,B as yt,_ as xt}from"./index-DYsKpclu.js";import{f as kt,g as St,h as De,i as Ct}from"./admin-DEOkRyTC.js";import{u as Bt}from"./auth-B7MDTgxJ.js";function Vt(s={},t={defaultBordered:!0}){const d=ge(Me,null);return{inlineThemeDisabled:d?.inlineThemeDisabled,mergedRtlRef:d?.mergedRtlRef,mergedComponentPropsRef:d?.mergedComponentPropsRef,mergedBreakpointsRef:d?.mergedBreakpointsRef,mergedBorderedRef:F(()=>{const{bordered:u}=s;return u!==void 0?u:d?.mergedBorderedRef.value??t.defaultBordered??!0}),mergedClsPrefixRef:d?d.mergedClsPrefixRef:Ze("n"),namespaceRef:F(()=>d?.mergedNamespaceRef.value)}}function $t(s,t,d){if(!t)return;const u=Xe(),_=ge(Me,null),h=()=>{const x=d.value;t.mount({id:x===void 0?s:x+s,head:!0,anchorMetaName:Pe,props:{bPrefix:x?`.${x}-`:void 0},ssr:u,parent:_?.styleMountTarget}),_?.preflightStyleDisabled||et.mount({id:"n-global",head:!0,anchorMetaName:Pe,ssr:u,parent:_?.styleMountTarget})};u?h():Qe(h)}function z(s,t=1){let d=Fe,u=!1;return typeof s=="function"&&(u=!0,w(),d=ce,s=s()),ze(s)?u?ce(Ie(s)):Ie(s):Array.isArray(s)?u?y(G,null,s.map(_=>z(()=>_)),-2):e(G,null,s.slice()):s==null||typeof s=="boolean"?d(We):d(tt,null,String(s),t)}function Ie(s){return s.el===null&&s.patchFlag!==-1||s.memo?s:nt(s)}const Tt=s=>typeof s=="function"||Object.prototype.toString.call(s)==="[object Object]"&&!ze(s)?s:{default:it(()=>[z(()=>s)])},C=s=>st(s)||null;function j(s){return typeof s=="string"?s.endsWith("px")?Number(s.slice(0,s.length-2)):Number(s):s}function Se(s){if(s!=null)return typeof s=="number"?`${s}px`:s.endsWith("px")?s:`${s}px`}function Ut(s,t,d,u){d||ot("useThemeClass","cssVarsRef is not passed");const _=ge(Me,null),h=_?.mergedThemeHashRef,x=_?.styleMountTarget,B=V(""),a=Xe();let k;const $=`__${s}`,R=()=>{let m=$;const T=t?t.value:void 0,N=h?.value;N&&(m+=`-${N}`),T&&(m+=`-${T}`);const{themeOverrides:E,builtinThemeOverrides:q}=u;E&&(m+=`-${Ae(JSON.stringify(E))}`),q&&(m+=`-${Ae(JSON.stringify(q))}`),B.value=m,k=()=>{const te=d.value;let se="";for(const ne in te)se+=`${ne}: ${te[ne]};`;K(`.${m}`,se).mount({id:m,ssr:a,parent:x}),k=void 0}};return at(()=>{R()}),{themeClass:B,onRender:()=>{k?.()}}}function Rt(s,t){return lt(s,d=>{d!==void 0&&(t.value=d)}),F(()=>s.value===void 0?t.value:s.value)}function Mt(){const s=V(!1);return Ye(()=>{s.value=!0}),rt(s)}function he(s,...t){if(Array.isArray(s))s.forEach(d=>he(d,...t));else return s(...t)}function Ne(s){return s.some(t=>ze(t)?!(t.type===We||t.type===G&&!Ne(t.children)):!0)?s:null}function ee(s,t){return t(s&&Ne(s())||null)}function Ce(s){return!(s&&Ne(s()))}const Le=ct("n-form-item");function zt(s,{defaultSize:t="medium",mergedSize:d,mergedDisabled:u}={}){const _=ge(Le,null);ut(Le,null);const h=F(d?()=>d(_):()=>{const{size:a}=s;if(a)return a;if(_){const{mergedSize:k}=_;if(k.value!==void 0)return k.value}return t}),x=F(u?()=>u(_):()=>{const{disabled:a}=s;return a!==void 0?a:_?_.disabled.value:!1}),B=F(()=>{const{status:a}=s;return a||_?.mergedValidationStatus.value});return dt(()=>{_&&_.restoreValidation()}),{mergedSizeRef:h,mergedDisabledRef:x,mergedStatusRef:B,nTriggerFormBlur(){_&&_.handleContentBlur()},nTriggerFormChange(){_&&_.handleContentChange()},nTriggerFormFocus(){_&&_.handleContentFocus()},nTriggerFormInput(){_&&_.handleContentInput()}}}var Je=ve({name:"BaseIconSwitchTransition",setup(s,{slots:t}){const d=Mt();return()=>(w(),ce(_t,{name:"icon-switch-transition",appear:d.value},Tt(t),1032,["appear"]))}});const{cubicBezierEaseInOut:Ft}=mt;function pe({originalTransform:s="",left:t=0,top:d=0,transition:u=`all .3s ${Ft} !important`}={}){return[K("&.icon-switch-transition-enter-from, &.icon-switch-transition-leave-to",{transform:`${s} scale(0.75)`,left:t,top:d,opacity:0}),K("&.icon-switch-transition-enter-to, &.icon-switch-transition-leave-from",{transform:`scale(1) ${s}`,left:t,top:d,opacity:1}),K("&.icon-switch-transition-enter-active, &.icon-switch-transition-leave-active",{transformOrigin:"center",position:"absolute",left:t,top:d,transition:u})]}var Nt=K([K("@keyframes rotator",` + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + }`),Ue("base-loading",` + position: relative; + line-height: 0; + width: 1em; + height: 1em; + `,[b("transition-wrapper",` + position: absolute; + width: 100%; + height: 100%; + `,[pe()]),b("placeholder",` + position: absolute; + left: 50%; + top: 50%; + transform: translateX(-50%) translateY(-50%); + `,[pe({left:"50%",top:"50%",originalTransform:"translateX(-50%) translateY(-50%)"})]),b("container",` + animation: rotator 3s linear infinite both; + `,[b("icon",` + height: 1em; + width: 1em; + `)])])]);const Pt=["viewBox"],At=["values","dur"],Et=["stroke-width","cx","cy","r","stroke-dasharray","stroke-dashoffset"],Ht=["values","dur"],Ot=["values","dur"],Be="1.6s",Dt={strokeWidth:{type:Number,default:28},stroke:{type:String,default:void 0},scale:{type:Number,default:1},radius:{type:Number,default:100}};var It=ve({name:"BaseLoading",props:{clsPrefix:{type:String,required:!0},show:{type:Boolean,default:!0},...Dt},setup(s){$t("-base-loading",Nt,Ge(s,"clsPrefix"))},render(){const{clsPrefix:s,radius:t,strokeWidth:d,stroke:u,scale:_}=this,h=t/_;return w(),y("div",{class:C(`${s}-base-loading`),role:"img","aria-label":"loading"},[Fe(Je,null,{default:()=>this.show?(w(),y("div",{key:"icon",class:C(`${s}-base-loading__transition-wrapper`)},[e("div",{class:C(`${s}-base-loading__container`)},[(w(),y("svg",{class:C(`${s}-base-loading__icon`),viewBox:`0 0 ${2*h} ${2*h}`,xmlns:"http://www.w3.org/2000/svg",style:Re({color:u})},[e("g",null,[e("animateTransform",{attributeName:"transform",type:"rotate",values:`0 ${h} ${h};270 ${h} ${h}`,begin:"0s",dur:Be,fill:"freeze",repeatCount:"indefinite"},null,8,At),e("circle",{class:C(`${s}-base-loading__icon`),fill:"none",stroke:"currentColor","stroke-width":d,"stroke-linecap":"round",cx:h,cy:h,r:t-d/2,"stroke-dasharray":5.67*t,"stroke-dashoffset":18.48*t},[e("animateTransform",{attributeName:"transform",type:"rotate",values:`0 ${h} ${h};135 ${h} ${h};450 ${h} ${h}`,begin:"0s",dur:Be,fill:"freeze",repeatCount:"indefinite"},null,8,Ht),e("animate",{attributeName:"stroke-dashoffset",values:`${5.67*t};${1.42*t};${5.67*t}`,begin:"0s",dur:Be,fill:"freeze",repeatCount:"indefinite"},null,8,Ot)],10,Et)])],14,Pt))],2)],2)):(w(),y("div",{key:"placeholder",class:C(`${s}-base-loading__placeholder`)},[z(()=>this.$slots.default?.())],2))},1024)],2)}});function Lt(s){const{primaryColor:t,opacityDisabled:d,borderRadius:u,textColor3:_}=s;return{...ht,iconColor:_,textColor:"white",loadingColor:t,opacityDisabled:d,railColor:"rgba(0, 0, 0, .14)",railColorActive:t,buttonBoxShadow:"0 1px 4px 0 rgba(0, 0, 0, 0.3), inset 0 0 1px 0 rgba(0, 0, 0, 0.05)",buttonColor:"#FFF",railBorderRadiusSmall:u,railBorderRadiusMedium:u,railBorderRadiusLarge:u,buttonBorderRadiusSmall:u,buttonBorderRadiusMedium:u,buttonBorderRadiusLarge:u,boxShadowFocus:`0 0 0 2px ${pt(t,{alpha:.2})}`}}const jt={common:ft,self:Lt};var Kt=Ue("switch",` + height: var(--n-height); + min-width: var(--n-width); + vertical-align: middle; + user-select: none; + -webkit-user-select: none; + display: inline-flex; + outline: none; + justify-content: center; + align-items: center; +`,[b("children-placeholder",` + height: var(--n-rail-height); + display: flex; + flex-direction: column; + overflow: hidden; + pointer-events: none; + visibility: hidden; + `),b("rail-placeholder",` + display: flex; + flex-wrap: none; + `),b("button-placeholder",` + width: calc(1.75 * var(--n-rail-height)); + height: var(--n-rail-height); + `),Ue("base-loading",` + position: absolute; + top: 50%; + left: 50%; + transform: translateX(-50%) translateY(-50%); + font-size: calc(var(--n-button-width) - 4px); + color: var(--n-loading-color); + transition: color .3s var(--n-bezier); + `,[pe({left:"50%",top:"50%",originalTransform:"translateX(-50%) translateY(-50%)"})]),b("checked, unchecked",` + transition: color .3s var(--n-bezier); + color: var(--n-text-color); + box-sizing: border-box; + position: absolute; + white-space: nowrap; + top: 0; + bottom: 0; + display: flex; + align-items: center; + line-height: 1; + `),b("checked",` + right: 0; + padding-right: calc(1.25 * var(--n-rail-height) - var(--n-offset)); + `),b("unchecked",` + left: 0; + justify-content: flex-end; + padding-left: calc(1.25 * var(--n-rail-height) - var(--n-offset)); + `),K("&:focus",[b("rail",` + box-shadow: var(--n-box-shadow-focus); + `)]),I("round",[b("rail","border-radius: calc(var(--n-rail-height) / 2);",[b("button","border-radius: calc(var(--n-button-height) / 2);")])]),Ee("disabled",[Ee("icon",[I("rubber-band",[I("pressed",[b("rail",[b("button","max-width: var(--n-button-width-pressed);")])]),b("rail",[K("&:active",[b("button","max-width: var(--n-button-width-pressed);")])]),I("active",[I("pressed",[b("rail",[b("button","left: calc(100% - var(--n-offset) - var(--n-button-width-pressed));")])]),b("rail",[K("&:active",[b("button","left: calc(100% - var(--n-offset) - var(--n-button-width-pressed));")])])])])])]),I("active",[b("rail",[b("button","left: calc(100% - var(--n-button-width) - var(--n-offset))")])]),b("rail",` + overflow: hidden; + height: var(--n-rail-height); + min-width: var(--n-rail-width); + border-radius: var(--n-rail-border-radius); + cursor: pointer; + position: relative; + transition: + opacity .3s var(--n-bezier), + background .3s var(--n-bezier), + box-shadow .3s var(--n-bezier); + background-color: var(--n-rail-color); + `,[b("button-icon",` + color: var(--n-icon-color); + transition: color .3s var(--n-bezier); + font-size: calc(var(--n-button-height) - 4px); + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + display: flex; + justify-content: center; + align-items: center; + line-height: 1; + `,[pe()]),b("button",` + align-items: center; + top: var(--n-offset); + left: var(--n-offset); + height: var(--n-button-height); + width: var(--n-button-width-pressed); + max-width: var(--n-button-width); + border-radius: var(--n-button-border-radius); + background-color: var(--n-button-color); + box-shadow: var(--n-button-box-shadow); + box-sizing: border-box; + cursor: inherit; + content: ""; + position: absolute; + transition: + background-color .3s var(--n-bezier), + left .3s var(--n-bezier), + opacity .3s var(--n-bezier), + max-width .3s var(--n-bezier), + box-shadow .3s var(--n-bezier); + `)]),I("active",[b("rail","background-color: var(--n-rail-color-active);")]),I("loading",[b("rail",` + cursor: wait; + `)]),I("disabled",[b("rail",` + cursor: not-allowed; + opacity: .5; + `)])]);const Xt=["aria-checked","tabindex","onClick","onFocus","onBlur","onKeyup","onKeydown"],Wt={...qe.props,size:String,value:{type:[String,Number,Boolean],default:void 0},loading:Boolean,defaultValue:{type:[String,Number,Boolean],default:!1},disabled:{type:Boolean,default:void 0},round:{type:Boolean,default:!0},"onUpdate:value":[Function,Array],onUpdateValue:[Function,Array],checkedValue:{type:[String,Number,Boolean],default:!0},uncheckedValue:{type:[String,Number,Boolean],default:!1},railStyle:Function,rubberBand:{type:Boolean,default:!0},spinProps:Object,onChange:[Function,Array]};let de;var Yt=ve({name:"Switch",props:Wt,slots:Object,setup(s){de===void 0&&(typeof CSS<"u"?typeof CSS.supports<"u"?de=CSS.supports("width","max(1px)"):de=!1:de=!0);const{mergedClsPrefixRef:t,inlineThemeDisabled:d,mergedComponentPropsRef:u}=Vt(s),_=qe("Switch","-switch",Kt,jt,s,t),h=zt(s,{mergedSize(g){if(s.size!==void 0)return s.size;if(g)return g.mergedSize.value;const H=u?.value?.Switch?.size;return H||"medium"}}),{mergedSizeRef:x,mergedDisabledRef:B}=h,a=V(s.defaultValue),k=Ge(s,"value"),$=Rt(k,a),R=F(()=>$.value===s.checkedValue),m=V(!1),T=V(!1),N=F(()=>{const{railStyle:g}=s;if(g)return g({focused:T.value,checked:R.value})});function E(g){const{"onUpdate:value":H,onChange:O,onUpdateValue:U}=s,{nTriggerFormInput:X,nTriggerFormChange:W}=h;H&&he(H,g),U&&he(U,g),O&&he(O,g),a.value=g,X(),W()}function q(){const{nTriggerFormFocus:g}=h;g()}function te(){const{nTriggerFormBlur:g}=h;g()}function se(){s.loading||B.value||($.value!==s.checkedValue?E(s.checkedValue):E(s.uncheckedValue))}function ne(){T.value=!0,q()}function J(){T.value=!1,te(),m.value=!1}function be(g){s.loading||B.value||g.key===" "&&($.value!==s.checkedValue?E(s.checkedValue):E(s.uncheckedValue),m.value=!1)}function Z(g){s.loading||B.value||g.key===" "&&(g.preventDefault(),m.value=!0)}const ue=F(()=>{const{value:g}=x,{self:{opacityDisabled:H,railColor:O,railColorActive:U,buttonBoxShadow:X,buttonColor:W,boxShadowFocus:c,loadingColor:we,textColor:ae,iconColor:ye,[Q("buttonHeight",g)]:P,[Q("buttonWidth",g)]:S,[Q("buttonWidthPressed",g)]:ie,[Q("railHeight",g)]:A,[Q("railWidth",g)]:i,[Q("railBorderRadius",g)]:n,[Q("buttonBorderRadius",g)]:r},common:{cubicBezierEaseInOut:Y}}=_.value;let D,le,xe;return de?(D=`calc((${A} - ${P}) / 2)`,le=`max(${A}, ${P})`,xe=`max(${i}, calc(${i} + ${P} - ${A}))`):(D=Se((j(A)-j(P))/2),le=Se(Math.max(j(A),j(P))),xe=j(A)>j(P)?i:Se(j(i)+j(P)-j(A))),{"--n-bezier":Y,"--n-button-border-radius":r,"--n-button-box-shadow":X,"--n-button-color":W,"--n-button-width":S,"--n-button-width-pressed":ie,"--n-button-height":P,"--n-height":le,"--n-offset":D,"--n-opacity-disabled":H,"--n-rail-border-radius":n,"--n-rail-color":O,"--n-rail-color-active":U,"--n-rail-height":A,"--n-rail-width":i,"--n-width":xe,"--n-box-shadow-focus":c,"--n-loading-color":we,"--n-text-color":ae,"--n-icon-color":ye}}),oe=d?Ut("switch",F(()=>x.value[0]),ue,s):void 0;return{handleClick:se,handleBlur:J,handleFocus:ne,handleKeyup:be,handleKeydown:Z,mergedRailStyle:N,pressed:m,mergedClsPrefix:t,mergedValue:$,checked:R,mergedDisabled:B,cssVars:d?void 0:ue,themeClass:oe?.themeClass,onRender:oe?.onRender}},render(){const{mergedClsPrefix:s,mergedDisabled:t,checked:d,mergedRailStyle:u,onRender:_,$slots:h}=this;_?.();const{checked:x,unchecked:B,icon:a,"checked-icon":k,"unchecked-icon":$}=h,R=!(Ce(a)&&Ce(k)&&Ce($));return w(),y("div",{role:"switch","aria-checked":d,class:C([`${s}-switch`,this.themeClass,R&&`${s}-switch--icon`,d&&`${s}-switch--active`,t&&`${s}-switch--disabled`,this.round&&`${s}-switch--round`,this.loading&&`${s}-switch--loading`,this.pressed&&`${s}-switch--pressed`,this.rubberBand&&`${s}-switch--rubber-band`]),tabindex:this.mergedDisabled?void 0:0,style:Re(this.cssVars),onClick:this.handleClick,onFocus:this.handleFocus,onBlur:this.handleBlur,onKeyup:this.handleKeyup,onKeydown:this.handleKeydown},[e("div",{class:C(`${s}-switch__rail`),"aria-hidden":"true",style:Re(u)},[z(()=>ee(x,m=>ee(B,T=>m||T?(w(),y("div",{key:4,"aria-hidden":!0,class:C(`${s}-switch__children-placeholder`)},[e("div",{class:C(`${s}-switch__rail-placeholder`)},[e("div",{class:C(`${s}-switch__button-placeholder`)},null,2),z(()=>m)],2),e("div",{class:C(`${s}-switch__rail-placeholder`)},[e("div",{class:C(`${s}-switch__button-placeholder`)},null,2),z(()=>T)],2)],2)):null))),e("div",{class:C(`${s}-switch__button`)},[z(()=>ee(a,m=>ee(k,T=>ee($,N=>(w(),ce(Je,null,{default:()=>this.loading?(w(),ce(It,gt({key:"loading",clsPrefix:s,strokeWidth:20},this.spinProps),null,16,["clsPrefix"])):this.checked&&(T||m)?(w(),y("div",{class:C(`${s}-switch__button-icon`),key:T?"checked-icon":"icon"},[z(()=>T||m)],2)):!this.checked&&(N||m)?(w(),y("div",{class:C(`${s}-switch__button-icon`),key:N?"unchecked-icon":"icon"},[z(()=>N||m)],2)):null},1024)))))),z(()=>ee(x,m=>m&&(w(),y("div",{key:"checked",class:C(`${s}-switch__checked`)},[z(()=>m)],2)))),z(()=>ee(B,m=>m&&(w(),y("div",{key:"unchecked",class:C(`${s}-switch__unchecked`)},[z(()=>m)],2))))],2)],6)],46,Xt)}});const Gt={class:"toolbar"},qt={class:"page-title"},Jt={class:"page-sub"},Zt={key:0,class:"loading-block"},Qt={class:"card"},es={class:"card-title"},ts={class:"field"},ss={for:"set-site-name"},ns={class:"hint"},is={class:"field"},os={for:"set-site-domain"},as={class:"hint"},ls={class:"card"},rs={class:"card-title"},ds={class:"field"},cs={for:"set-logo-url"},us={class:"logo-input-row"},_s={class:"hint"},ms={class:"logo-preview-row"},fs={class:"logo-box"},hs=["src"],ps={style:{"font-size":"13px",color:"var(--c-text-2)"}},gs={style:{display:"inline-flex","align-items":"center",gap:"8px","font-weight":"700",color:"var(--c-text)"}},vs=["src"],bs={class:"card"},ws={class:"card-title"},ys={class:"field"},xs={for:"set-favicon-url"},ks={class:"logo-input-row"},Ss={class:"hint"},Cs={class:"logo-preview-row"},Bs={class:"logo-box",style:{width:"44px",height:"44px"}},Vs=["src"],$s={style:{"font-size":"13px",color:"var(--c-text-2)"}},Ts={class:"card"},Us={class:"card-title"},Rs={class:"field"},Ms={for:"set-bg-url"},zs={class:"hint"},Fs={key:0,class:"logo-box",style:{width:"120px"}},Ns=["src"],Ps={class:"card"},As={class:"card-title"},Es={class:"field"},Hs={for:"set-footer-text"},Os={class:"hint"},Ds={class:"field"},Is={for:"set-footer-beian"},Ls={class:"hint"},js={class:"card"},Ks={class:"card-title"},Xs={class:"field notify-switch-row"},Ws={for:"set-notify-enabled"},Ys={class:"field"},Gs={for:"set-notify-title"},qs={class:"hint"},Js={class:"field"},Zs={for:"set-notify-content"},Qs={class:"hint"},en={class:"card"},tn={class:"card-title"},sn={class:"field"},nn={for:"set-max-save-time"},on={class:"unit-row"},an=["placeholder"],ln={value:"hour"},rn={value:"day"},dn={class:"hint"},cn={class:"field"},un={for:"set-max-save-count"},_n={class:"hint"},mn={class:"card"},fn={class:"card-title"},hn={class:"field"},pn={for:"set-max-file-size"},gn={class:"unit-row"},vn=["placeholder"],bn={value:"MB"},wn={value:"GB"},yn={class:"hint"},xn={class:"field"},kn={for:"set-allowed-types"},Sn={class:"hint"},Cn={class:"card"},Bn={class:"card-title"},Vn={class:"field"},$n={class:"field"},Tn={for:"set-engine-type"},Un={class:"unit-row"},Rn={value:"local"},Mn={value:"webdav"},zn={value:"s3"},Fn=["disabled"],Nn={key:0,class:"spin","aria-hidden":"true"},Pn={class:"hint"},An={class:"card-sub",style:{"margin-top":"8px"}},En={key:0,class:"field"},Hn={for:"set-local-root"},On={class:"hint"},Dn={class:"field"},In={for:"set-webdav-url"},Ln={class:"hint"},jn={class:"field"},Kn={for:"set-webdav-root"},Xn={class:"hint"},Wn={class:"field-row"},Yn={class:"field"},Gn={for:"set-webdav-user"},qn={class:"field"},Jn={for:"set-webdav-pass"},Zn={class:"hint"},Qn={class:"field-row"},ei={class:"field"},ti={for:"set-s3-endpoint"},si={class:"hint"},ni={class:"field"},ii={for:"set-s3-bucket"},oi={class:"field-row"},ai={class:"field"},li={for:"set-s3-region"},ri={class:"field"},di={for:"set-s3-style"},ci={value:"auto"},ui={value:"path"},_i={value:"virtual"},mi={class:"field-row"},fi={class:"field"},hi={for:"set-s3-ak"},pi={class:"field"},gi={for:"set-s3-sk"},vi={class:"hint"},bi={class:"field"},wi={for:"set-s3-token"},yi={class:"hint"},xi={class:"save-row"},ki=["disabled"],Si={key:0,class:"spin","aria-hidden":"true"},Ci={class:"card"},Bi={class:"card-title"},Vi={class:"field-row"},$i={class:"field"},Ti={for:"set-upload-count"},Ui={class:"hint"},Ri={class:"field"},Mi={for:"set-upload-minute"},zi={class:"hint"},Fi={class:"save-row"},Ni=["disabled"],Pi={key:0,class:"spin","aria-hidden":"true"},Ai={class:"card"},Ei={class:"card-title"},Hi={class:"card-sub"},Oi={class:"field"},Di={for:"pwd-old"},Ii={class:"field"},Li={for:"pwd-new"},ji={class:"field"},Ki={for:"pwd-confirm"},Xi={class:"save-row"},Wi=["disabled"],Yi={key:0,class:"spin","aria-hidden":"true"},Gi=365*24*60*60,Ve=1e5,qi=10*1024*1024*1024,Ji=128,Zi=2e3,Qi=2e3,eo=128,to=2048,$e=1e4,Te=1440,je=3600,me=86400,Ke=1024*1024,fe=1024*1024*1024,so=256*1024,no="******",io=ve({__name:"SettingsView",setup(s){const{t}=vt(),d=bt(),u=wt(),_=Bt(),h=yt(),x=V(!1),B=V(!1),a=ke({site_name:"",site_domain:"",logo_url:"",favicon_url:"",background_url:"",footer_text:"",footer_beian:"",notify_enabled:!0,notify_title:"",notify_content:"",max_save_seconds:0,max_save_count:0,max_file_size:0,allowed_file_types:"",uploadCount:0,uploadMinute:0}),k=V(0),$=V("day"),R=V(0),m=V("MB");function T(i){if(i<=0)return{value:0,unit:"day"};if(i%me===0||i>=me){const n=i/me;if(Number.isInteger(n))return{value:n,unit:"day"}}return{value:Math.round(i/je*100)/100,unit:"hour"}}function N(i){if(i<=0)return{value:0,unit:"MB"};if(i%fe===0||i>=fe){const n=i/fe;if(Number.isInteger(n))return{value:n,unit:"GB"}}return{value:Math.round(i/Ke*100)/100,unit:"MB"}}function E(i,n){const r=n==="day"?i*me:i*je;return Z(r,0,Gi)}function q(i,n){const r=n==="GB"?i*fe:i*Ke;return Z(r,0,qi)}const te=F(()=>k.value<=0),se=F(()=>R.value<=0);function ne(i,n){return i==null?n:typeof i=="boolean"?i:typeof i=="number"?i!==0:String(i)!=="0"&&String(i)!=="false"&&String(i)!==""}function J(i,n){const r=Number(i);return Number.isFinite(r)?r:n}function be(i){return Array.isArray(i)?i.map(n=>String(n).trim()).filter(Boolean):typeof i=="string"?i.split(",").map(n=>n.trim()).filter(Boolean):[]}function Z(i,n,r){return Math.min(Math.max(Math.round(i)||0,n),r)}async function ue(){x.value=!0;try{const i=await kt();a.site_name=String(p(i,["site_name","name"])??""),a.site_domain=String(p(i,["site_domain","siteDomain"])??""),a.logo_url=String(p(i,["logo_url"])??""),a.favicon_url=String(p(i,["favicon_url"])??""),a.background_url=String(p(i,["background_url","backgroundUrl"])??""),a.footer_text=String(p(i,["footer_text","footerText"])??""),a.footer_beian=String(p(i,["footer_beian","footerBeian"])??""),a.notify_enabled=ne(p(i,["notify_enabled","notifyEnabled"]),!0),a.notify_title=String(p(i,["notify_title","notifyTitle"])??""),a.notify_content=String(p(i,["notify_content","notifyContent"])??""),a.max_save_seconds=J(p(i,["max_save_seconds","maxSaveSeconds"]),0),a.max_save_count=J(p(i,["max_save_count","maxSaveCount"]),0),a.max_file_size=J(p(i,["max_file_size","maxFileSize"]),0);const n=T(a.max_save_seconds);k.value=n.value,$.value=n.unit;const r=N(a.max_file_size);R.value=r.value,m.value=r.unit;const Y=be(p(i,["allowed_file_types","allowedFileTypes"]));a.allowed_file_types=Y.join(","),a.uploadCount=J(p(i,["uploadCount","upload_count"]),0),a.uploadMinute=J(p(i,["uploadMinute","upload_minute"]),0);const D=p(i,["_engine_hint"]),le=D?String(p(D,["storage_backend"])??""):"";O.value=le||String(p(i,["storage_engine"])??"local"),U.value=O.value,we(i)}catch(i){d.error(i instanceof re?i.msg:t("admin.settings.loadFailed"))}finally{x.value=!1}}function oe(i){const n=document.createElement("input");n.type="file",n.accept="image/*",n.onchange=()=>{const r=n.files?.[0];if(!r)return;if(r.size>so){d.error(t("admin.settings.imageTooLarge"));return}const Y=new FileReader;Y.onload=()=>{const D=String(Y.result??"");i==="logo"?a.logo_url=D:a.favicon_url=D,d.success(t("admin.settings.imageLoaded"))},Y.onerror=()=>d.error(t("admin.settings.imageReadFailed")),Y.readAsDataURL(r)},n.click()}async function g(){B.value=!0;try{const i=a.allowed_file_types.split(",").map(n=>n.trim()).filter(Boolean);await De({site_name:a.site_name.trim(),site_domain:a.site_domain.trim(),logo_url:a.logo_url.trim(),favicon_url:a.favicon_url.trim(),background_url:a.background_url.trim(),footer_text:a.footer_text.trim(),footer_beian:a.footer_beian.trim(),notify_enabled:a.notify_enabled?1:0,notify_title:a.notify_title.trim(),notify_content:a.notify_content.trim(),max_save_seconds:E(k.value,$.value),max_save_count:Z(a.max_save_count,0,Ve),max_file_size:q(R.value,m.value),allowed_file_types:i.length?i:["*"],uploadCount:Z(a.uploadCount||0,1,$e),uploadMinute:Z(a.uploadMinute||0,1,Te)}),d.success(t("admin.settings.saved")),await u.load()}catch(i){d.error(i instanceof re?i.msg:t("admin.settings.saveFailed"))}finally{B.value=!1}}function H(){a.site_name="文件快传",a.site_domain="",a.logo_url="",a.favicon_url="",a.background_url="",a.footer_text="",a.footer_beian="",a.notify_enabled=!0,a.notify_title="",a.notify_content="",a.max_save_seconds=0,a.max_save_count=0,a.max_file_size=0,a.allowed_file_types="*",a.uploadCount=10,a.uploadMinute=1,k.value=0,$.value="day",R.value=0,m.value="MB",d.info(t("admin.settings.restoreDefaultsDone"))}const O=V("local"),U=V("local"),X=V(!1),W=V(!1),c=ke({local_storage_path:"",webdav_url:"",webdav_root_path:"filebox_storage",webdav_username:"",webdav_password:"",s3_endpoint_url:"",s3_bucket_name:"",s3_region_name:"auto",s3_access_key_id:"",s3_secret_access_key:"",aws_session_token:"",s3_addressing_style:"auto"});function we(i){c.local_storage_path=String(p(i,["local_storage_path","localStoragePath"])??""),c.webdav_url=String(p(i,["webdav_url","webdavUrl"])??""),c.webdav_root_path=String(p(i,["webdav_root_path","webdavRootPath"])??"filebox_storage"),c.webdav_username=String(p(i,["webdav_username","webdavUsername"])??""),c.webdav_password=String(p(i,["webdav_password","webdavPassword"])??""),c.s3_endpoint_url=String(p(i,["s3_endpoint_url","s3EndpointUrl"])??""),c.s3_bucket_name=String(p(i,["s3_bucket_name","s3BucketName"])??""),c.s3_region_name=String(p(i,["s3_region_name","s3RegionName"])??"auto"),c.s3_access_key_id=String(p(i,["s3_access_key_id","s3AccessKeyId"])??""),c.s3_secret_access_key=String(p(i,["s3_secret_access_key","s3SecretAccessKey"])??""),c.aws_session_token=String(p(i,["aws_session_token","awsSessionToken"])??""),c.s3_addressing_style=String(p(i,["s3_addressing_style","s3AddressingStyle"])??"auto")}function ae(i){const n=i.trim();if(!(n===""||n===no))return n}async function ye(){X.value=!0;try{const i=await St(U.value);O.value=String(i)||U.value,d.success(t("admin.settings.engineSwitchOk",{engine:i})),await u.load()}catch(i){d.error(i instanceof re?i.msg:t("admin.settings.engineSwitchFail"))}finally{X.value=!1}}async function P(){W.value=!0;try{const i={};if(U.value==="local")i.local_storage_path=c.local_storage_path.trim();else if(U.value==="webdav"){i.webdav_url=c.webdav_url.trim(),i.webdav_root_path=c.webdav_root_path.trim(),i.webdav_username=c.webdav_username.trim();const n=ae(c.webdav_password);n!==void 0&&(i.webdav_password=n)}else{i.s3_endpoint_url=c.s3_endpoint_url.trim(),i.s3_bucket_name=c.s3_bucket_name.trim(),i.s3_region_name=c.s3_region_name.trim()||"auto",i.s3_access_key_id=c.s3_access_key_id.trim();const n=ae(c.s3_secret_access_key);n!==void 0&&(i.s3_secret_access_key=n);const r=ae(c.aws_session_token);r!==void 0&&(i.aws_session_token=r),i.s3_addressing_style=c.s3_addressing_style}await De(i),d.success(t("admin.settings.engineParamsSaved"))}catch(i){d.error(i instanceof re?i.msg:t("admin.settings.saveFailed"))}finally{W.value=!1}}const S=ke({old:"",next:"",confirm:""}),ie=V(!1);async function A(){if(!S.old||!S.next){d.error(t("admin.settings.pwdRequired"));return}if(S.next.length<6){d.error(t("admin.settings.pwdTooShort"));return}if(S.next!==S.confirm){d.error(t("admin.settings.pwdMismatch"));return}ie.value=!0;try{await Ct(S.old,S.next),d.success(t("admin.settings.pwdChanged")),S.old=S.next=S.confirm="",await _.logout(),h.replace({name:"admin-login"})}catch(i){d.error(i instanceof re?i.code===401?t("admin.settings.pwdWrong"):i.msg:t("admin.settings.pwdChangeFailed"))}finally{ie.value=!1}}return Ye(ue),(i,n)=>(w(),y("div",null,[e("div",Gt,[e("div",null,[e("h2",qt,l(o(t)("admin.settings.title")),1),e("p",Jt,l(o(t)("admin.settings.subtitle")),1)]),e("button",{class:"btn btn-ghost",type:"button",onClick:H},l(o(t)("admin.settings.restoreDefaults")),1)]),x.value?(w(),y("div",Zt,[n[36]||(n[36]=e("span",{class:"spin","aria-hidden":"true"},null,-1)),M(" "+l(o(t)("admin.settings.loading")),1)])):(w(),y("form",{key:1,class:"settings-grid",onSubmit:He(g,["prevent"])},[e("section",Qt,[e("h3",es,l(o(t)("admin.settings.sectionBasic")),1),e("div",ts,[e("label",ss,l(o(t)("admin.settings.siteName")),1),f(e("input",{id:"set-site-name","onUpdate:modelValue":n[0]||(n[0]=r=>a.site_name=r),class:"input",placeholder:"文件快传",maxlength:"64"},null,512),[[v,a.site_name]]),e("p",ns,l(o(t)("admin.settings.siteNameHint")),1)]),e("div",is,[e("label",os,l(o(t)("admin.settings.siteDomain")),1),f(e("input",{id:"set-site-domain","onUpdate:modelValue":n[1]||(n[1]=r=>a.site_domain=r),class:"input",placeholder:"https://share.example.com",maxlength:"256"},null,512),[[v,a.site_domain]]),e("p",as,l(o(t)("admin.settings.siteDomainHint")),1)])]),e("section",ls,[e("h3",rs,l(o(t)("admin.settings.sectionLogo")),1),e("div",ds,[e("label",cs,l(o(t)("admin.settings.logoUrl")),1),e("div",us,[f(e("input",{id:"set-logo-url","onUpdate:modelValue":n[2]||(n[2]=r=>a.logo_url=r),class:"input",placeholder:"https://…/logo.svg"},null,512),[[v,a.logo_url]]),e("button",{class:"btn btn-ghost",type:"button",onClick:n[3]||(n[3]=r=>oe("logo"))},l(o(t)("admin.settings.uploadImage")),1)]),e("p",_s,l(o(t)("admin.settings.logoHint")),1)]),e("div",ms,[e("div",fs,[e("img",{src:a.logo_url||o(u).displayLogoUrl,alt:"Logo preview"},null,8,hs)]),e("div",ps,[M(l(o(t)("admin.settings.navPreview"))+" ",1),e("span",gs,[e("img",{src:a.logo_url||o(u).displayLogoUrl,alt:"",style:{width:"22px",height:"22px","object-fit":"contain"}},null,8,vs),M(" "+l(a.site_name||o(u).displayName),1)])])])]),e("section",bs,[e("h3",ws,l(o(t)("admin.settings.sectionFavicon")),1),e("div",ys,[e("label",xs,l(o(t)("admin.settings.faviconUrl")),1),e("div",ks,[f(e("input",{id:"set-favicon-url","onUpdate:modelValue":n[4]||(n[4]=r=>a.favicon_url=r),class:"input",placeholder:"https://…/favicon.png"},null,512),[[v,a.favicon_url]]),e("button",{class:"btn btn-ghost",type:"button",onClick:n[5]||(n[5]=r=>oe("favicon"))},l(o(t)("admin.settings.uploadImage")),1)]),e("p",Ss,l(o(t)("admin.settings.faviconHint")),1)]),e("div",Cs,[e("div",Bs,[e("img",{src:a.favicon_url||o(u).displayFaviconUrl,alt:"Favicon preview"},null,8,Vs)]),e("span",$s,l(o(t)("admin.settings.faviconPreviewHint")),1)])]),e("section",Ts,[e("h3",Us,l(o(t)("admin.settings.sectionBackground")),1),e("div",Rs,[e("label",Ms,l(o(t)("admin.settings.backgroundUrl")),1),f(e("input",{id:"set-bg-url","onUpdate:modelValue":n[6]||(n[6]=r=>a.background_url=r),class:"input",placeholder:"https://…/bg.jpg",maxlength:to},null,512),[[v,a.background_url]]),e("p",zs,l(o(t)("admin.settings.backgroundHint")),1)]),a.background_url.trim()?(w(),y("div",Fs,[e("img",{src:a.background_url,alt:"Background preview",style:{width:"100%",height:"100%","object-fit":"cover"}},null,8,Ns)])):L("",!0)]),e("section",Ps,[e("h3",As,l(o(t)("admin.settings.sectionFooter")),1),e("div",Es,[e("label",Hs,l(o(t)("admin.settings.footerText")),1),f(e("textarea",{id:"set-footer-text","onUpdate:modelValue":n[7]||(n[7]=r=>a.footer_text=r),class:"textarea",style:{"min-height":"72px"},maxlength:Qi},null,512),[[v,a.footer_text]]),e("p",Os,l(o(t)("admin.settings.footerTextHint")),1)]),e("div",Ds,[e("label",Is,l(o(t)("admin.settings.footerBeian")),1),f(e("input",{id:"set-footer-beian","onUpdate:modelValue":n[8]||(n[8]=r=>a.footer_beian=r),class:"input",maxlength:eo},null,512),[[v,a.footer_beian]]),e("p",Ls,l(o(t)("admin.settings.footerBeianHint")),1)])]),e("section",js,[e("h3",Ks,l(o(t)("admin.settings.sectionNotify")),1),e("div",Xs,[e("label",Ws,l(o(t)("admin.settings.notifyEnabled")),1),Fe(o(Yt),{id:"set-notify-enabled",value:a.notify_enabled,"onUpdate:value":n[9]||(n[9]=r=>a.notify_enabled=r),size:"small"},null,8,["value"])]),e("div",Ys,[e("label",Gs,l(o(t)("admin.settings.notifyTitle")),1),f(e("input",{id:"set-notify-title","onUpdate:modelValue":n[10]||(n[10]=r=>a.notify_title=r),class:"input",maxlength:Ji},null,512),[[v,a.notify_title]]),e("p",qs,l(o(t)("admin.settings.notifyTitleHint")),1)]),e("div",Js,[e("label",Zs,l(o(t)("admin.settings.notifyContent")),1),f(e("textarea",{id:"set-notify-content","onUpdate:modelValue":n[11]||(n[11]=r=>a.notify_content=r),class:"textarea",style:{"min-height":"72px"},maxlength:Zi},null,512),[[v,a.notify_content]]),e("p",Qs,l(o(t)("admin.settings.notifyContentHint")),1)])]),e("section",en,[e("h3",tn,l(o(t)("admin.settings.sectionSavePolicy")),1),e("div",sn,[e("label",nn,l(o(t)("admin.settings.maxSaveTime")),1),e("div",on,[f(e("input",{id:"set-max-save-time","onUpdate:modelValue":n[12]||(n[12]=r=>k.value=r),class:"input",type:"number",min:0,step:"1",placeholder:o(t)("admin.settings.saveTimeUnlimited")},null,8,an),[[v,k.value,void 0,{number:!0}]]),f(e("select",{"onUpdate:modelValue":n[13]||(n[13]=r=>$.value=r),class:"input unit-select","aria-label":"unit"},[e("option",ln,l(o(t)("admin.settings.unitHour")),1),e("option",rn,l(o(t)("admin.settings.unitDay")),1)],512),[[_e,$.value]])]),e("p",dn,[M(l(o(t)("admin.settings.maxSaveTimeHint"))+" ",1),te.value?(w(),y(G,{key:0},[M("("+l(o(t)("admin.settings.saveTimeUnlimited"))+")",1)],64)):L("",!0)])]),e("div",cn,[e("label",un,l(o(t)("admin.settings.maxSaveCount")),1),f(e("input",{id:"set-max-save-count","onUpdate:modelValue":n[14]||(n[14]=r=>a.max_save_count=r),class:"input",type:"number",min:0,max:Ve,step:"1"},null,512),[[v,a.max_save_count,void 0,{number:!0}]]),e("p",_n,l(o(t)("admin.settings.maxSaveCountHint",{max:Ve})),1)])]),e("section",mn,[e("h3",fn,l(o(t)("admin.settings.sectionStorage")),1),e("div",hn,[e("label",pn,l(o(t)("admin.settings.maxFileSizeFriendly")),1),e("div",gn,[f(e("input",{id:"set-max-file-size","onUpdate:modelValue":n[15]||(n[15]=r=>R.value=r),class:"input",type:"number",min:0,step:"1",placeholder:o(t)("admin.settings.sizeUnlimited")},null,8,vn),[[v,R.value,void 0,{number:!0}]]),f(e("select",{"onUpdate:modelValue":n[16]||(n[16]=r=>m.value=r),class:"input unit-select","aria-label":"unit"},[e("option",bn,l(o(t)("admin.settings.unitMB")),1),e("option",wn,l(o(t)("admin.settings.unitGB")),1)],512),[[_e,m.value]])]),e("p",yn,[M(l(o(t)("admin.settings.maxFileSizeHintV3",{fallback:o(Oe)(o(u).uploadSize)}))+" ",1),se.value?(w(),y(G,{key:0},[M("("+l(o(t)("admin.settings.sizeUnlimited"))+")",1)],64)):(w(),y(G,{key:1},[M("("+l(o(t)("admin.settings.approxSize",{size:o(Oe)(q(R.value,m.value))}))+")",1)],64))])]),e("div",xn,[e("label",kn,l(o(t)("admin.settings.allowedFileTypes")),1),f(e("input",{id:"set-allowed-types","onUpdate:modelValue":n[17]||(n[17]=r=>a.allowed_file_types=r),class:"input",placeholder:"jpg, png, pdf / image/*, *"},null,512),[[v,a.allowed_file_types]]),e("p",Sn,l(o(t)("admin.settings.allowedFileTypesHint")),1)])]),e("section",Cn,[e("h3",Bn,l(o(t)("admin.settings.sectionEngine")),1),e("div",Vn,[e("label",null,[M(l(o(t)("admin.settings.engineCurrent"))+": ",1),e("strong",null,l(O.value),1)])]),e("div",$n,[e("label",Tn,l(o(t)("admin.settings.sectionEngine")),1),e("div",Un,[f(e("select",{id:"set-engine-type","onUpdate:modelValue":n[18]||(n[18]=r=>U.value=r),class:"input",style:{flex:"1"}},[e("option",Rn,l(o(t)("admin.settings.engineLocal")),1),e("option",Mn,l(o(t)("admin.settings.engineWebdav")),1),e("option",zn,l(o(t)("admin.settings.engineS3")),1)],512),[[_e,U.value]]),e("button",{class:"btn",type:"button",disabled:X.value,onClick:ye},[X.value?(w(),y("span",Nn)):L("",!0),M(" "+l(o(t)("admin.settings.engineSwitch")),1)],8,Fn)]),e("p",Pn,l(o(t)("admin.settings.engineSwitchFail")),1)]),e("h4",An,l(o(t)("admin.settings.engineParamsTitle")),1),U.value==="local"?(w(),y("div",En,[e("label",Hn,l(o(t)("admin.settings.localRoot")),1),f(e("input",{id:"set-local-root","onUpdate:modelValue":n[19]||(n[19]=r=>c.local_storage_path=r),class:"input",placeholder:"/app/data/storage"},null,512),[[v,c.local_storage_path]]),e("p",On,l(o(t)("admin.settings.localRootHint")),1)])):L("",!0),U.value==="webdav"?(w(),y(G,{key:1},[e("div",Dn,[e("label",In,l(o(t)("admin.settings.webdavUrl")),1),f(e("input",{id:"set-webdav-url","onUpdate:modelValue":n[20]||(n[20]=r=>c.webdav_url=r),class:"input",placeholder:"https://dav.example.com/dav/"},null,512),[[v,c.webdav_url]]),e("p",Ln,l(o(t)("admin.settings.webdavUrlHint")),1)]),e("div",jn,[e("label",Kn,l(o(t)("admin.settings.webdavRoot")),1),f(e("input",{id:"set-webdav-root","onUpdate:modelValue":n[21]||(n[21]=r=>c.webdav_root_path=r),class:"input",placeholder:"filebox_storage"},null,512),[[v,c.webdav_root_path]]),e("p",Xn,l(o(t)("admin.settings.webdavRootHint")),1)]),e("div",Wn,[e("div",Yn,[e("label",Gn,l(o(t)("admin.settings.webdavUser")),1),f(e("input",{id:"set-webdav-user","onUpdate:modelValue":n[22]||(n[22]=r=>c.webdav_username=r),class:"input",autocomplete:"off"},null,512),[[v,c.webdav_username]])]),e("div",qn,[e("label",Jn,l(o(t)("admin.settings.webdavPass")),1),f(e("input",{id:"set-webdav-pass","onUpdate:modelValue":n[23]||(n[23]=r=>c.webdav_password=r),class:"input",type:"password",autocomplete:"new-password"},null,512),[[v,c.webdav_password]]),e("p",Zn,l(o(t)("admin.settings.secretKeepHint")),1)])])],64)):L("",!0),U.value==="s3"?(w(),y(G,{key:2},[e("div",Qn,[e("div",ei,[e("label",ti,l(o(t)("admin.settings.s3Endpoint")),1),f(e("input",{id:"set-s3-endpoint","onUpdate:modelValue":n[24]||(n[24]=r=>c.s3_endpoint_url=r),class:"input",placeholder:"https://s3.example.com:9000"},null,512),[[v,c.s3_endpoint_url]]),e("p",si,l(o(t)("admin.settings.s3EndpointHint")),1)]),e("div",ni,[e("label",ii,l(o(t)("admin.settings.s3Bucket")),1),f(e("input",{id:"set-s3-bucket","onUpdate:modelValue":n[25]||(n[25]=r=>c.s3_bucket_name=r),class:"input",placeholder:"filecodebox"},null,512),[[v,c.s3_bucket_name]])])]),e("div",oi,[e("div",ai,[e("label",li,l(o(t)("admin.settings.s3Region")),1),f(e("input",{id:"set-s3-region","onUpdate:modelValue":n[26]||(n[26]=r=>c.s3_region_name=r),class:"input",placeholder:"auto"},null,512),[[v,c.s3_region_name]])]),e("div",ri,[e("label",di,l(o(t)("admin.settings.s3Style")),1),f(e("select",{id:"set-s3-style","onUpdate:modelValue":n[27]||(n[27]=r=>c.s3_addressing_style=r),class:"input"},[e("option",ci,l(o(t)("admin.settings.styleAuto")),1),e("option",ui,l(o(t)("admin.settings.stylePath")),1),e("option",_i,l(o(t)("admin.settings.styleVirtual")),1)],512),[[_e,c.s3_addressing_style]])])]),e("div",mi,[e("div",fi,[e("label",hi,l(o(t)("admin.settings.s3Ak")),1),f(e("input",{id:"set-s3-ak","onUpdate:modelValue":n[28]||(n[28]=r=>c.s3_access_key_id=r),class:"input",autocomplete:"off"},null,512),[[v,c.s3_access_key_id]])]),e("div",pi,[e("label",gi,l(o(t)("admin.settings.s3Sk")),1),f(e("input",{id:"set-s3-sk","onUpdate:modelValue":n[29]||(n[29]=r=>c.s3_secret_access_key=r),class:"input",type:"password",autocomplete:"new-password"},null,512),[[v,c.s3_secret_access_key]]),e("p",vi,l(o(t)("admin.settings.secretKeepHint")),1)])]),e("div",bi,[e("label",wi,l(o(t)("admin.settings.s3Token")),1),f(e("input",{id:"set-s3-token","onUpdate:modelValue":n[30]||(n[30]=r=>c.aws_session_token=r),class:"input",type:"password",autocomplete:"new-password"},null,512),[[v,c.aws_session_token]]),e("p",yi,l(o(t)("admin.settings.secretKeepHint")),1)])],64)):L("",!0),e("div",xi,[e("button",{class:"btn",type:"button",disabled:W.value,onClick:P},[W.value?(w(),y("span",Si)):L("",!0),M(" "+l(o(t)("admin.settings.engineParamsSave")),1)],8,ki)])]),e("section",Ci,[e("h3",Bi,l(o(t)("admin.settings.sectionUploadRate")),1),e("div",Vi,[e("div",$i,[e("label",Ti,l(o(t)("admin.settings.uploadCount")),1),f(e("input",{id:"set-upload-count","onUpdate:modelValue":n[31]||(n[31]=r=>a.uploadCount=r),class:"input",type:"number",min:1,max:$e,step:"1"},null,512),[[v,a.uploadCount,void 0,{number:!0}]]),e("p",Ui,l(o(t)("admin.settings.uploadCountHint",{max:$e})),1)]),e("div",Ri,[e("label",Mi,l(o(t)("admin.settings.uploadMinute")),1),f(e("input",{id:"set-upload-minute","onUpdate:modelValue":n[32]||(n[32]=r=>a.uploadMinute=r),class:"input",type:"number",min:1,max:Te,step:"1"},null,512),[[v,a.uploadMinute,void 0,{number:!0}]]),e("p",zi,l(o(t)("admin.settings.uploadMinuteHint",{max:Te})),1)])])]),e("div",Fi,[e("button",{class:"btn",type:"submit",disabled:B.value},[B.value?(w(),y("span",Pi)):L("",!0),M(" "+l(o(t)("admin.settings.saveAll")),1)],8,Ni)]),e("section",Ai,[e("h3",Ei,l(o(t)("admin.settings.sectionPassword")),1),e("p",Hi,l(o(t)("admin.settings.passwordHint")),1),e("form",{onSubmit:He(A,["prevent"])},[e("div",Oi,[e("label",Di,l(o(t)("admin.settings.oldPassword")),1),f(e("input",{id:"pwd-old","onUpdate:modelValue":n[33]||(n[33]=r=>S.old=r),class:"input",type:"password",autocomplete:"current-password"},null,512),[[v,S.old]])]),e("div",Ii,[e("label",Li,l(o(t)("admin.settings.newPassword")),1),f(e("input",{id:"pwd-new","onUpdate:modelValue":n[34]||(n[34]=r=>S.next=r),class:"input",type:"password",autocomplete:"new-password"},null,512),[[v,S.next]])]),e("div",ji,[e("label",Ki,l(o(t)("admin.settings.confirmPassword")),1),f(e("input",{id:"pwd-confirm","onUpdate:modelValue":n[35]||(n[35]=r=>S.confirm=r),class:"input",type:"password",autocomplete:"new-password"},null,512),[[v,S.confirm]])]),e("div",Xi,[e("button",{class:"btn",type:"submit",disabled:ie.value},[ie.value?(w(),y("span",Yi)):L("",!0),M(" "+l(o(t)("common.save")),1)],8,Wi)])],32)])],32))]))}}),ro=xt(io,[["__scopeId","data-v-03aa64c9"]]);export{ro as default}; diff --git a/server/web/dist/assets/SettingsView-GpXVMuCV.css b/server/web/dist/assets/SettingsView-GpXVMuCV.css new file mode 100644 index 0000000..316f4ca --- /dev/null +++ b/server/web/dist/assets/SettingsView-GpXVMuCV.css @@ -0,0 +1 @@ +.settings-grid[data-v-03aa64c9]{display:grid;gap:16px}.save-row[data-v-03aa64c9]{display:flex;justify-content:flex-end}.notify-switch-row[data-v-03aa64c9]{display:flex;align-items:center;justify-content:space-between;gap:12px}.notify-switch-row label[data-v-03aa64c9]{margin-bottom:0}.unit-row[data-v-03aa64c9]{display:flex;gap:8px;align-items:center}.unit-row .input[data-v-03aa64c9]{flex:1}.unit-select[data-v-03aa64c9]{flex:0 0 110px!important} diff --git a/server/web/dist/assets/SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js b/server/web/dist/assets/SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js new file mode 100644 index 0000000..9d89fcf --- /dev/null +++ b/server/web/dist/assets/SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js @@ -0,0 +1 @@ +import{d as z,u as E,a as x,c as i,G as B,f as t,A as m,b as a,F as _,r as v,az as S,t as r,h as d,aA as p,aB as T,O as A,o,z as M,q as V,e as b,aC as w}from"./index-DYsKpclu.js";const D={class:"site-nav"},F=["src"],I={class:"brand-name"},O=["aria-label"],R={class:"nav-controls"},U=["aria-label"],q=["aria-checked","title","onClick"],G={class:"nav-control-icon","aria-hidden":"true"},H=["title"],K=z({__name:"SiteNav",setup($){const{t:s}=E(),c=x(),{mode:u,setMode:k}=w(),g=d(()=>[{to:"/",label:s("nav.home"),match:n=>n==="/"}]);function y(n){return n.match(location.pathname)}const C={light:"☀️",dark:"🌙",system:"💻"},f=d(()=>({light:s("theme.light"),dark:s("theme.dark"),system:s("theme.system")})),L=d(()=>p()==="zh-CN"?"中文":"English");function N(){const n=p()==="zh-CN"?"en-US":"zh-CN";T(n)}return(n,l)=>{const h=A("RouterLink");return o(),i("header",D,[B(h,{class:"brand",to:"/",title:t(s)("nav.homeTitle",{name:t(c).displayName})},{default:m(()=>[a("img",{src:t(c).displayLogoUrl,alt:"Logo",onError:l[0]||(l[0]=e=>e.target.style.visibility="hidden")},null,40,F),a("span",I,r(t(c).displayName),1)]),_:1},8,["title"]),a("nav",{class:"nav-links","aria-label":t(s)("nav.mainNav")},[(o(!0),i(_,null,v(g.value,e=>(o(),M(h,{key:e.to,to:e.to,class:b({"router-link-active":y(e)})},{default:m(()=>[V(r(e.label),1)]),_:2},1032,["to","class"]))),128))],8,O),a("div",R,[a("div",{class:"theme-seg",role:"radiogroup","aria-label":t(s)("theme.label")},[(o(!0),i(_,null,v(t(S),e=>(o(),i("button",{key:e,type:"button",role:"radio","aria-checked":t(u)===e,class:b({active:t(u)===e}),title:f.value[e],onClick:j=>t(k)(e)},[a("span",G,r(C[e]),1)],10,q))),128))],8,U),a("button",{class:"nav-control",type:"button",title:t(s)("lang.label"),onClick:N},[l[1]||(l[1]=a("span",{class:"nav-control-icon","aria-hidden":"true"},"🌐",-1)),a("span",null,r(L.value),1)],8,H)])])}}});export{K as _}; diff --git a/server/web/dist/assets/admin-DEOkRyTC.js b/server/web/dist/assets/admin-DEOkRyTC.js new file mode 100644 index 0000000..e461064 --- /dev/null +++ b/server/web/dist/assets/admin-DEOkRyTC.js @@ -0,0 +1 @@ +import{v as n,S as e,x as s}from"./index-DYsKpclu.js";function u(t){return n(s.adminLogin,{method:"POST",json:{password:t}})}function m(){return n(s.adminVerify)}async function l(){await n(s.adminLogout,{method:"POST"})}async function g(t){const a=await n(s.adminFileList,{query:{page:t.page,size:t.size,keyword:t.keyword||void 0}}),o=e(a,["data","list","items","files"])??[],d=Number(e(a,["total","count"])??o.length);return{page:Number(e(a,["page"])??t.page),size:Number(e(a,["size"])??t.size),total:d,data:o.map(i=>({id:Number(e(i,["id"])??0),code:String(e(i,["code"])??""),name:String(e(i,["name"])??`${e(i,["prefix"])??""}${e(i,["suffix"])??""}`),suffix:String(e(i,["suffix"])??""),size:Number(e(i,["size"])??0),isText:!!(e(i,["isText","is_text"])??!1),expiredAt:e(i,["expiredAt","expired_at","expires_at"])??null,expiredCount:e(i,["expiredCount","expired_count"])??null,usedCount:Number(e(i,["usedCount","used_count"])??0),createdAt:e(i,["createdAt","created_at"])??null,isExpired:!!(e(i,["isExpired","is_expired"])??!1)}))}}async function p(t){await n(s.adminFileDelete,{method:"DELETE",json:{id:t}})}async function f(t){await n(s.adminFileBatchDelete,{method:"POST",json:{ids:t}})}async function y(t){await n(s.adminFileUpdate,{method:"PATCH",json:t})}async function w(){const t=await n(s.adminConfigGet);if(t&&typeof t=="object"&&!Array.isArray(t)){const a=t;return e(a,["config","data","settings"])??a}return{}}async function S(t){await n(s.adminConfigUpdate,{method:"PATCH",json:t})}async function _(t){const a=await n(s.adminStorageSwitch,{method:"POST",json:{engine:t}});return String(a?.engine??t)}async function x(t,a){await n(s.adminPasswordUpdate,{method:"PATCH",json:{old_password:t,new_password:a}})}async function b(t){const a=await n(s.adminAuditList,{query:{page:t.page,size:t.size,action:t.action||void 0,result:t.result||void 0,ip:t.ip||void 0,start_time:t.startTime||void 0,end_time:t.endTime||void 0}}),o=e(a,["data","list","items","logs"])??[],d=Number(e(a,["total","count"])??o.length);return{page:Number(e(a,["page"])??t.page),size:Number(e(a,["size"])??t.size),total:d,data:o.map((i,r)=>({id:Number(e(i,["id"])??r+1),action:String(e(i,["action"])??""),result:String(e(i,["result"])??""),fileCode:String(e(i,["file_code","fileCode","code"])??""),fileName:String(e(i,["file_name","fileName","name"])??""),sizeBytes:e(i,["size_bytes","sizeBytes","size"])??null,transferredBytes:e(i,["transferred_bytes","transferredBytes","bytes"])??null,ip:String(e(i,["ip","client_ip","clientIp"])??""),userAgent:String(e(i,["user_agent","userAgent"])??""),deviceOs:String(e(i,["device_os","deviceOs","os"])??""),deviceBrowser:String(e(i,["device_browser","deviceBrowser","browser"])??""),deviceType:String(e(i,["device_type","deviceType"])??""),actor:String(e(i,["actor"])??""),errorMsg:String(e(i,["error_msg","errorMsg","error"])??""),durationMs:e(i,["duration_ms","durationMs","duration"])??null,createdAt:e(i,["created_at","createdAt","time"])??null}))}}export{g as a,f as b,p as c,y as d,b as e,w as f,_ as g,S as h,x as i,l as j,m as k,u as l}; diff --git a/server/web/dist/assets/auth-B7MDTgxJ.js b/server/web/dist/assets/auth-B7MDTgxJ.js new file mode 100644 index 0000000..652cd8a --- /dev/null +++ b/server/web/dist/assets/auth-B7MDTgxJ.js @@ -0,0 +1 @@ +import{av as o,aw as a,W as r}from"./index-DYsKpclu.js";import{j as n,k as i,l as c}from"./admin-DEOkRyTC.js";const s="fcb_admin_flag",l=o("auth",{state:()=>({token:r(),checked:!1}),getters:{isAuthed:t=>!!t.token},actions:{async login(t){const e=(await c(t))?.token;if(!e)throw new Error("登录响应缺少 token");this.token=e,a(e);try{localStorage.setItem(s,"1")}catch{}this.checked=!0},async verify(){if(!this.token)return!1;try{return await i(),this.checked=!0,!0}catch{return this.reset(),!1}},async logout(){try{this.token&&await n()}catch{}this.reset()},reset(){this.token="",this.checked=!1,a("");try{localStorage.removeItem(s)}catch{}}}});export{l as u}; diff --git a/server/web/dist/assets/docsSource-BXkkn0HE.js b/server/web/dist/assets/docsSource-BXkkn0HE.js new file mode 100644 index 0000000..e3ede68 --- /dev/null +++ b/server/web/dist/assets/docsSource-BXkkn0HE.js @@ -0,0 +1,2517 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-DYsKpclu.js","assets/index-CtyCxWf5.css"])))=>i.map(i=>d[i]); +import{ay as i}from"./index-DYsKpclu.js";const p='# API 概述\n\n文件快传 Go 版(v2.5.6)对外提供一套 REST API,覆盖文本/文件分享、分片上传、\n预签名直传、管理后台与审计日志查询。本文档与 `server/internal/api/` 实际实现逐一对齐,\n交互式规范见站内 `/openapi`(源文件 `docs/openapi.yaml`)。\n\n## Base URL\n\n- 服务默认监听 `:8466`,Base URL 为 `http://:8466`(下文示例统一用 `http://localhost:8466`)。\n- **业务路由挂根路径**(与参考实现一致):`/share/*`、`/chunk/*`、`/presign/*`、`/admin/*`、`/setup`。\n- 仅两个公共接口带 `/api/v1` 前缀:`/api/v1/health`、`/api/v1/config`。\n\n## 统一响应封装\n\n所有 JSON 接口返回统一结构,HTTP 状态码与 `code` 一致;失败时 `data` 缺省:\n\n```json\n{ "code": 200, "msg": "ok", "data": { } }\n```\n\n失败示例(404):\n\n```json\n{ "code": 404, "msg": "文件不存在" }\n```\n\n个别端点直接返回原始内容而非 JSON 封装(文档中已单独标注):\n\n| 端点 | 响应形式 |\n|---|---|\n| `GET /share/select?code=`(文本分享) | `text/plain; charset=utf-8` 正文 |\n| `GET /share/select?code=`(文件分享) | 文件二进制流(200/206,支持 Range) |\n| `GET /share/download?key=&code=`(文件分享) | 文件二进制流(200/206,支持 Range) |\n| `GET /admin/file/download?id=`(文件分享) | 文件二进制流 |\n| `GET /setup` / `POST /setup`(表单) | HTML 向导/成功页 |\n\n## 字段命名约定\n\n- 接口字段以 **snake_case** 为主(`file_code`、`size_bytes`)。\n- 文件列表与审计日志的行字段同时输出 **snake_case 与 camelCase 双份**(如 `expired_at` 与 `expiredAt`),文档以 snake_case 为准,camelCase 仅为前端兼容保留。\n\n## 认证\n\n- 游客接口无需认证;是否允许游客上传由配置 `openUpload` 控制(关闭时上传类接口要求管理员 `Authorization: Bearer `,否则 403)。\n- 管理接口(`/admin/login` 除外)一律要求 `Authorization: Bearer `,无效/缺失返回 401。\n- 详情见《认证与限流》。\n\n## 限流\n\n按 IP(可信代理场景解析 `X-Forwarded-For`)维度限流,超限返回 **423**:\n\n| 规则 | 计数时机 | 默认(次/窗口) | 相关配置 |\n|---|---|---|---|\n| `upload` | **上传成功**后计数 | 10 次 / 1 分钟 | `uploadCount` / `uploadMinute` |\n| `error` | 取件失败(404/过期)时计数 | 10 次 / 1 分钟 | `errorCount` / `errorMinute` |\n| `login` | 登录失败时计数 | 5 次 / 15 分钟 | `loginCount` / `loginMinute` |\n| `metadata` | 每次访问即计数 | 同 `error` | `errorCount` / `errorMinute` |\n\n## 初始化守卫\n\n系统未初始化(未设置管理员密码)时,除 `GET|POST /setup` 与 `GET /api/v1/health` 外,\n**所有接口一律返回 428**:\n\n```json\n{ "code": 428, "msg": "系统未初始化,请先完成初始化" }\n```\n\n首次部署请先访问 `GET /setup` 获取 HTML 向导,或直接 `POST /setup` 完成初始化(见《管理后台 API》初始化章节)。\n\n## 审计\n\n所有上传/下载端点经审计中间件自动落库(操作时间/IP/UA/设备解析/动作/结果/字节数/耗时/角色),\n失败与被拒绝的请求同样记录;管理端经 `GET /admin/audit/list` 查询,详见《审计日志》。\n\n## 端点总览\n\n| 模块 | 端点 |\n|---|---|\n| 公共 | `GET /api/v1/health` · `GET /api/v1/config` · `GET /robots.txt`(输出 `robotsText` 配置) |\n| 初始化 | `GET /setup` · `POST /setup` |\n| 文本分享 | `POST /share/text` |\n| 文件分享 | `POST /share/file` |\n| 查询与取件 | `GET/POST /share/metadata` · `GET /share/select` · `POST /share/select` · `GET /share/download` |\n| 分片上传 | `POST /chunk/upload/init` · `POST /chunk/upload/{uploadID}/{chunkIndex}` · `GET /chunk/upload/status/{uploadID}` · `POST /chunk/upload/complete/{uploadID}` · `DELETE /chunk/upload/{uploadID}` |\n| 预签名直传 | `POST /presign/upload/init` · `PUT /presign/upload/proxy/{uploadID}` · `POST /presign/upload/confirm/{uploadID}` · `GET /presign/upload/status/{uploadID}` · `DELETE /presign/upload/{uploadID}` |\n| 管理后台 | `POST /admin/login` · `GET /admin/verify` · `POST /admin/logout` · `GET /admin/dashboard` · 文件管理 `/admin/file/*` · 配置 `/admin/config/*` · 密码 `/admin/settings/password` |\n| 审计日志 | `GET /admin/audit/list`(别名 `/admin/audit/logs`) |\n\n## 时间与编码\n\n- 时间字段一律 RFC 3339(如 `2025-06-01T12:00:00+08:00`);管理员会话过期时间为 Unix 秒。\n- 请求体支持 `application/json` 与 `application/x-www-form-urlencoded`(上传类为 `multipart/form-data`),文档示例以 JSON/curl 为主。\n- CORS:公开接口放开(Bearer 认证,无 Cookie CSRF 面);**管理端 `/admin/*` 已收紧**——携带 Origin 且既不同源也不在 `site_domain` 白名单时不下发 CORS 头(浏览器拦截跨域读取)。\n\n## 交互式文档\n\n- 站内文档页:`/docs`(渲染本目录 markdown,构建时内嵌)。\n- Swagger UI:`/openapi`(渲染 `docs/openapi.yaml`,构建时内嵌)。\n- OpenAPI 规范源文件为仓库内 `docs/openapi.yaml`;如需经后端直接下载,\n 需在部署时把它拷贝进前端静态产物 `web/dist/`(未拷贝时该路径按 SPA 回退返回页面)。\n',d='# 认证与限流\n\n## 角色\n\n| 角色 | 能力 |\n|---|---|\n| 游客(无 Authorization 头) | 取件、查询元信息;`openUpload=1` 时可上传 |\n| 管理员(`Authorization: Bearer `) | 全部能力 + `/admin/*` 管理接口 |\n\n## 管理员令牌\n\n- 由 `POST /admin/login` 用管理员密码换取,HS256 JWT,默认有效期 **7 天**(`adminSessionExpire`,1~365 整天,v2.5.6 起由 30 天缩短)。\n- 请求头格式:`Authorization: Bearer `。\n- **改密/重置管理员密码会轮换 `jwt_secret`,所有已签发令牌立即失效**(401)。\n- 密码存储为 bcrypt(cost 12);历史 `sha256$`/明文格式在登录成功后自动升级重哈希,无需手动迁移。\n- 游客上传关闭(`openUpload=0`)时,上传类接口也可用管理员 Bearer 令牌通过鉴权。\n\n## 认证失败语义\n\n| 场景 | 状态码 |\n|---|---|\n| `/admin/*` 缺失/无效令牌 | 401 |\n| `POST /admin/login` 密码错误 | 401(并计入 login 限流) |\n| 游客上传被关闭且未携带有效令牌 | 403 |\n| 代理下载 `key` 校验失败 | 403 |\n\n## 未初始化(428)\n\n管理员密码未设置(`admin_token` 为空)时,除 `GET|POST /setup` 与 `GET /api/v1/health` 外全部接口返回 428。\n完成 `POST /setup` 初始化后自动解除。\n\n## 限流规则\n\n限流按 **客户端 IP** 维度(配置 `FCB_TRUSTED_PROXIES` 声明可信代理 CIDR,命中时解析 `X-Forwarded-For` 取真实 IP),\n窗口计数原子化存储于缓存(未配置 Redis 时为进程内存)。**超限一律返回 423**:\n\n```json\n{ "code": 423, "msg": "请求次数过多,请稍后再试" }\n```\n\n| 规则 | 生效端点 | 计数时机 | 默认 | 配置键 |\n|---|---|---|---|---|\n| `upload` | `/share/text`、`/share/file`、`/chunk/upload/*`、`/presign/upload/*` | **成功后**计数(进入时仅检查) | 10 次 / 1 分钟 | `uploadCount`、`uploadMinute` |\n| `error` | `/share/select`、`/share/download` | 取件失败(不存在/过期/鉴权失败)时计数 | 10 次 / 1 分钟 | `errorCount`、`errorMinute` |\n| `login` | `/admin/login` | 登录失败时计数 | 5 次 / 15 分钟 | `loginCount`、`loginMinute` |\n| `metadata` | `/share/metadata` | **每次访问即计数**(含失败) | 同 `error` | `errorCount`、`errorMinute` |\n\n- 规则值可由管理端 `PATCH /admin/config/update` 运行时修改,立即生效(无需重启)。\n- 取件成功(`/share/select`、`/share/download`)不计入 `error` 限流。\n\n## 代理下载令牌(key)\n\n`GET /share/download` 的 `key` 由服务端按窗口生成:\n`sha256(code + timeFactor + "000" + jwt_secret)`,`timeFactor = unix秒 / 1000`(约 16.7 分钟一个窗口)。\n服务端**同时接受当前与上一窗口**的令牌,避免窗口边界竞态。令牌通过 `POST /share/select` 的响应\n`download_url` 下发,客户端不应自行构造。\n\n## 示例\n\n登录获取令牌:\n\n```bash\ncurl -s http://localhost:8466/admin/login \\\n -H \'Content-Type: application/json\' \\\n -d \'{"password":"your-admin-password"}\'\n```\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": {\n "id": "admin", "username": "admin",\n "token": "eyJhbGciOiJIUzI1NiIs...",\n "token_type": "Bearer",\n "expires_at": 1750000000,\n "expires_in": 604800\n }\n}\n```\n\n携带令牌调用管理接口:\n\n```bash\nTOKEN="eyJhbGciOiJIUzI1NiIs..."\ncurl -s http://localhost:8466/admin/dashboard -H "Authorization: Bearer $TOKEN"\n```\n\n校验令牌是否有效:\n\n```bash\ncurl -s http://localhost:8466/admin/verify -H "Authorization: Bearer $TOKEN"\n```\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": { "id": "admin", "username": "admin", "token": "eyJhbGciOiJIUzI1NiIs...", "token_type": "Bearer", "expires_at": 1750000000 }\n}\n```\n\n令牌失效时:\n\n```json\n{ "code": 401, "msg": "令牌无效或已过期" }\n```\n',c='# 文本分享\n\n创建纯文本分享,返回取件码。文本大小上限 **222KB**(超限建议改用文件分享);请求体全局上限 1MiB,`Content-Length` >441KB 时读前直接 403。\n经审计中间件落库(action=upload)。\n\n## POST /share/text\n\n**请求参数**(`application/x-www-form-urlencoded`,亦支持 multipart;`text` 为必需):\n\n| 参数 | 类型 | 必需 | 默认 | 说明 |\n|---|---|---|---|---|\n| `code` | string,可选;自定义提取码,5-8 位字母或数字(空=随机生成;占用 400「该提取码已被占用」) |\n| `text` | string | ✅ | - | 文本内容(≤222KB,按 UTF-8 字节数) |\n| `expire_value` | int | ❌ | `1` | 过期值(配合 `expire_style`) |\n| `expire_style` | string | ❌ | `day` | `day`/`hour`/`minute`/`count`/`forever`(须在站点允许列表内) |\n\n过期语义:\n\n- `day`/`hour`/`minute`:按时间过期,`expired_count = -1`。\n- `count`:按次数过期,取件 `expire_value` 次后失效(`expired_count = expire_value`);\n **v2 需求 ④**:`max_save_count>0` 时 `expire_value` 不得超出该上限,超限 403。\n- `forever`:永久(需站点允许;`max_save_seconds>0` 时其他方式受最长保存上限约束,超限 403)。\n\n> 可选值与上限来自公开配置 `GET /api/v1/config`(`expireStyle`、`max_save_seconds`、\n> `max_save_count`),上传页动态读取并在范围内选择;管理端改策略后立即生效。\n\n**curl 示例**:\n\n```bash\n# 自定义提取码(可选):-d \'code=MYCODE1\'\ncurl -s -X POST http://localhost:8466/share/text \\\n -d \'text=你好,文件快传\' \\\n -d \'expire_value=1\' \\\n -d \'expire_style=day\'\n```\n\n**成功响应**(200):\n\n```json\n{ "code": 200, "msg": "ok", "data": { "code": "8XQ2M" } }\n```\n\n`data.code` 为 5 位取件码(数字或大写字母+数字,取决于 `code_generate_type`)。\n\n**错误响应**:\n\n```json\n{ "code": 400, "msg": "过期时间类型错误" }\n```\n\n```json\n{ "code": 400, "msg": "过期时间值必须大于 0" }\n```\n\n```json\n{ "code": 403, "msg": "内容过多,建议采用文件形式" }\n```\n\n```json\n{ "code": 403, "msg": "限制最长时间为 7天,可换用其他方式" }\n```\n\n```json\n{ "code": 403, "msg": "限制次数最多为 5 次" }\n```\n\n```json\n{ "code": 423, "msg": "请求次数过多,请稍后再试" }\n```\n\n> 游客上传关闭(`openUpload=0`)时需携带管理员令牌,否则 403:\n> `{"code":403,"msg":"本站未开启游客上传,如需上传请先登录后台"}`\n\n## 取回文本\n\n文本分享的取回走统一的取件接口(消耗次数):\n\n- `GET /share/select?code=` → `text/plain` 正文即文本内容(响应头 `Content-Disposition` 带文件名,无扩展名时为 `.txt`)。\n- `POST /share/select`(`{"code":"8XQ2M"}`)→ JSON,`data.text` / `data.content` 为文本内容。\n\n示例:\n\n```bash\ncurl -s "http://localhost:8466/share/select?code=8XQ2M"\n```\n\n```text\n你好,文件快传\n```\n\n**v3.1 变更**:① 支持 JSON 提交(`Content-Type: application/json`,字段同名);② 空文本 400「分享内容不能为空」;③ 可选 `code` 自定义提取码(5-8 位字母数字,占用 400)。\n',l='# 文件分享\n\n上传单个文件并创建分享。支持扩展名/MIME 白名单 + **magic bytes 防伪**(读文件前 64 字节校验,\n伪造类型返回 403)。经审计中间件落库(action=upload,记录文件总大小与实际传输字节)。\n\n## POST /share/file\n\n**请求参数**(`multipart/form-data`):\n\n| 参数 | 类型 | 必需 | 默认 | 说明 |\n|---|---|---|---|---|\n| `code` | string,可选;自定义提取码,5-8 位字母或数字(空=随机生成;占用 400) |\n| `file` | file | ✅ | - | 上传的文件(大小 ≤ 生效上限:`max_file_size>0` 时为其,否则 `uploadSize`) |\n| `expire_value` | int | ❌ | `1` | 过期值(配合 `expire_style`;`count` 型受 `max_save_count` 约束) |\n| `expire_style` | string | ❌ | `day` | `day`/`hour`/`minute`/`count`/`forever`(须在 `expireStyle` 白名单内) |\n\n**curl 示例**:\n\n```bash\ncurl -s -X POST http://localhost:8466/share/file \\\n -F \'file=@./report.pdf;type=application/pdf\' \\\n -F \'expire_value=7\' \\\n -F \'expire_style=day\'\n```\n\n**成功响应**(200):\n\n```json\n{ "code": 200, "msg": "ok", "data": { "code": "K3P9W", "name": "report.pdf" } }\n```\n\n**错误响应**:\n\n```json\n{ "code": 400, "msg": "缺少上传文件 file 字段" }\n```\n\n```json\n{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" }\n```\n\n> 大小上限为动态策略(v2 需求 ④⑩):管理端改 `max_file_size`(0=回落 `uploadSize`)后\n> **下一次上传立即按新上限执行**,无需重启;上限值可经 `GET /api/v1/config` 的\n> `max_file_size`/`maxFileSize` 字段读取。\n\n```json\n{ "code": 403, "msg": "不允许上传该类型文件" }\n```\n\n```json\n{ "code": 403, "msg": "文件内容与扩展名不匹配,疑似伪造类型" }\n```\n\n```json\n{ "code": 403, "msg": "限制最长时间为 7天,可换用其他方式" }\n```\n\n```json\n{ "code": 403, "msg": "限制次数最多为 5 次" }\n```\n\n```json\n{ "code": 403, "msg": "请求次数过多,请稍后再试" }\n```\n\n```json\n{ "code": 507, "msg": "存储空间已达到管理员设置的容量上限" }\n```\n\n```json\n{ "code": 503, "msg": "存储服务不可用,请稍后再试" }\n```\n\n## 文件类型白名单\n\n由配置 `allowed_file_types` 控制(管理端可改):\n\n- `*`:不限制(默认)。\n- 扩展名规则:`.png`、`pdf`(自动补点)等,按文件名后缀匹配。\n- MIME 规则:`image/*`、`application/pdf` 等,按请求 `Content-Type` 通配匹配。\n\n已知类型(png/jpg/gif/webp/bmp/pdf/zip/rar/7z/gz/mp3/mp4/exe/elf)会做 **magic bytes 交叉校验**:\n扩展名或 Content-Type 声明了已知类型,但文件头不匹配时拒绝(403「疑似伪造类型」)。\n\n## 下载取件\n\n- `GET /share/select?code=`:消耗 1 次取件,返回文件流(`200` 全量 / `206` 区间,\n 支持 `Range` 请求头;响应含 `Accept-Ranges: bytes`、`Content-Disposition: attachment; filename*=UTF-8\'\'...`)。\n- `POST /share/select`:返回详情 JSON,`download_url` 为代理下载地址(见下)。\n- `GET /share/download?key=&code=`:代理下载,消耗 1 次,同样支持 Range。\n\nRange 示例(取前 1024 字节):\n\n```bash\ncurl -s -H \'Range: bytes=0-1023\' -o part.bin \\\n "http://localhost:8466/share/select?code=K3P9W"\n```\n\n区间越界返回:\n\n```json\n{ "code": 416, "msg": "请求范围超出文件大小" }\n```\n',m=`# 分享查询与取件 + +查询分享元信息(不消耗次数)与真正取件(消耗次数)的完整接口。 +除 \`metadata\` 每次 423 限流计数外,取件失败还会计入 \`error\` 限流。 + +## 元信息:GET /share/metadata + +按取件码查询元信息,**不消耗次数**。每次访问即计入 \`metadata\` 限流。 + +**参数**:\`code\`(query,必需)。 + +\`\`\`bash +curl -s "http://localhost:8466/share/metadata?code=K3P9W" +\`\`\` + +**成功响应**(200): + +\`\`\`json +{ + "code": 200, "msg": "ok", + "data": { + "code": "K3P9W", + "name": "report.pdf", + "size": 1048576, + "type": "file", + "is_text": false, + "created_at": "2025-06-01T12:00:00+08:00", + "expired_at": "2025-06-08T12:00:00+08:00", + "expires_at": "2025-06-08T12:00:00+08:00", + "expired_count": -1, + "used_count": 3, + "remaining_downloads": null + } +} +\`\`\` + +字段说明: + +| 字段 | 说明 | +|---|---| +| \`type\` | \`text\`(文本分享)/ \`file\`(文件分享) | +| \`is_text\` | 是否文本分享 | +| \`size\` | 字节数 | +| \`expired_at\` / \`expires_at\` | 过期时间(RFC 3339);永久分享为 \`null\` | +| \`expired_count\` | \`-1\` 按时间/永久;\`>0\` 剩余可取次数(原始限额) | +| \`used_count\` | 已取次数 | +| \`remaining_downloads\` | 剩余可取次数(仅次数型分享有值,否则 \`null\`) | + +> 不返回存储路径等敏感字段。 + +**错误响应**: + +\`\`\`json +{ "code": 404, "msg": "文件不存在" } +\`\`\` + +\`\`\`json +{ "code": 404, "msg": "文件已过期" } +\`\`\` + +## 元信息(POST):POST /share/metadata + +等价的 JSON 版本(\`code\` 放请求体): + +\`\`\`bash +curl -s -X POST http://localhost:8466/share/metadata \\ + -H 'Content-Type: application/json' \\ + -d '{"code":"K3P9W"}' +\`\`\` + +响应与 GET 版本一致。 + +## 取件(消耗次数):GET /share/select + +**每调用一次消耗 1 次取件**(次数型分享扣减 \`expired_count\`;时间型扣减不计)。 + +- 文本分享:返回 \`text/plain; charset=utf-8\` 正文(非 JSON 封装),\`Content-Disposition\` 携带文件名。 +- 文件分享:返回文件流(\`200\` 全量 / \`206\` 区间),支持 \`Range\`。 + +\`\`\`bash +curl -s -OJ "http://localhost:8466/share/select?code=K3P9W" +\`\`\` + +次数耗尽或已过期: + +\`\`\`json +{ "code": 404, "msg": "文件已过期" } +\`\`\` + +超限(计入 error 限流): + +\`\`\`json +{ "code": 423, "msg": "请求次数过多,请稍后再试" } +\`\`\` + +## 取件详情:POST /share/select + +返回元信息 + 文本内容/下载地址的 JSON 详情。**消耗语义**:次数型分享(\`expired_count >= 0\`) +返回代理地址 \`download_url\` 且本次**不消耗**(消耗发生在访问代理地址时);时间型/永久/文本分享在本次消耗。 + +\`\`\`bash +curl -s -X POST http://localhost:8466/share/select \\ + -H 'Content-Type: application/json' \\ + -d '{"code":"K3P9W"}' +\`\`\` + +**文件分享响应**(200): + +\`\`\`json +{ + "code": 200, "msg": "ok", + "data": { + "code": "K3P9W", + "name": "report.pdf", + "size": 1048576, + "type": "file", + "is_text": false, + "created_at": "2025-06-01T12:00:00+08:00", + "expired_at": "2025-06-08T12:00:00+08:00", + "expires_at": "2025-06-08T12:00:00+08:00", + "expired_count": -1, + "used_count": 4, + "remaining_downloads": null, + "text": "/share/download?key=9f2c…&code=K3P9W", + "download_url": "/share/download?key=9f2c…&code=K3P9W" + } +} +\`\`\` + +> S3 引擎下时间型/永久分享的 \`download_url\` 可能是预签名直链(1 小时有效)而非代理地址; +> 次数型分享恒为代理地址 \`"/share/download?key=…&code=…"\`。 + +**文本分享响应**(200): + +\`\`\`json +{ + "code": 200, "msg": "ok", + "data": { + "code": "8XQ2M", "name": "Text.txt", "size": 24, "type": "text", "is_text": true, + "created_at": "2025-06-01T12:00:00+08:00", + "expired_at": "2025-06-02T12:00:00+08:00", "expires_at": "2025-06-02T12:00:00+08:00", + "expired_count": -1, "used_count": 1, "remaining_downloads": null, + "text": "你好,文件快传", + "content": "你好,文件快传", + "download_url": null + } +} +\`\`\` + +## 代理下载:GET /share/download + +\`POST /share/select\` 返回的代理地址,**每次访问消耗 1 次**,支持 Range。 + +| 参数 | 说明 | +|---|---| +| \`key\` | 窗口令牌(服务端下发,双窗口校验) | +| \`code\` | 取件码 | + +\`\`\`bash +curl -s -OJ "http://localhost:8466/share/download?key=9f2c…&code=K3P9W" +\`\`\` + +**错误响应**: + +\`\`\`json +{ "code": 403, "msg": "下载鉴权失败" } +\`\`\` + +\`\`\`json +{ "code": 404, "msg": "文件已过期" } +\`\`\` + +> \`key\` 鉴权失败会计入 error 限流;文本分享经该接口返回 JSON 封装 \`data\` 为文本内容。 +`,u=`# 分片上传 + +大文件分片上传:客户端把文件切成固定大小的分片逐个上传,服务端按索引合并并做 SHA256 校验。 +支持**断点续传**(相同 \`file_hash\` + 大小 + 文件名的未完成会话自动续传)。 +需站点开启 \`enableChunk\`(公共配置 \`enableChunk\` 返回 \`true\`)。 +分片会话保留 24 小时。全部端点经审计中间件落库(action=upload)。 + +## 上传流程 + +\`\`\`text +POST /chunk/upload/init → upload_id, total_chunks +POST /chunk/upload/{id}/{index} → 逐片上传(0 起,可并发) +GET /chunk/upload/status/{id} → 断点续传时查进度 +POST /chunk/upload/complete/{id} → 合并 + SHA256 → 取件码 +DELETE /chunk/upload/{id} → 取消(可选) +\`\`\` + +## 初始化:POST /chunk/upload/init + +**请求体**(JSON,亦兼容表单): + +| 参数 | 类型 | 必需 | 默认 | 说明 | +|---|---|---|---|---| +| \`file_name\` | string | ✅ | - | 文件名(会做清理与白名单校验) | +| \`file_size\` | int | ✅ | - | 文件总字节数(>0;服务端按分片数校验上限) | +| \`chunk_size\` | int | ❌ | \`5242880\`(5MB) | 每片大小(字节),硬上限 32MiB(超出 400「chunk_size 过大」) | +| \`file_hash\` | string | ❌ | - | 整文件 SHA256(断点续传的匹配键) | + +**curl 示例**: + +\`\`\`bash +curl -s -X POST http://localhost:8466/chunk/upload/init \\ + -H 'Content-Type: application/json' \\ + -d '{"file_name":"movie.mp4","file_size":15728640,"chunk_size":5242880,"file_hash":""}' +\`\`\` + +**成功响应**(200,新建会话): + +\`\`\`json +{ + "code": 200, "msg": "ok", + "data": { + "existed": false, + "upload_id": "3f6b8c2a4d5e6f708192a3b4c5d6e7f8", + "chunk_size": 5242880, + "total_chunks": 3, + "uploaded_chunks": [] + } +} +\`\`\` + +**断点续传响应**(200,命中未完成会话):返回既有会话,\`uploaded_chunks\` 为已传分片索引列表, +客户端只需补传缺失分片(注意:\`existed\` 字段恒为 \`false\`,是否续传以 \`upload_id\` 复用且 +\`uploaded_chunks\` 非空为准): + +\`\`\`json +{ + "code": 200, "msg": "ok", + "data": { + "existed": false, + "upload_id": "3f6b8c2a4d5e6f708192a3b4c5d6e7f8", + "chunk_size": 5242880, + "total_chunks": 3, + "uploaded_chunks": [0, 1] + } +} +\`\`\` + +**错误响应**: + +\`\`\`json +{ "code": 400, "msg": "file_size 必须大于 0" } +\`\`\` + +\`\`\`json +{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" } +\`\`\` + +\`\`\`json +{ "code": 403, "msg": "分片上传未启用" } +\`\`\` + +## 上传分片:POST /chunk/upload/{uploadID}/{chunkIndex} + +主路径(与参考实现语义一致)。\`chunkIndex\` 从 \`0\` 起。 + +**multipart 字段**:\`chunk\`(必需,该分片的二进制数据)。 + +**curl 示例**: + +\`\`\`bash +split -b 5242880 movie.mp4 part- # 本地分片 +curl -s -X POST http://localhost:8466/chunk/upload/3f6b8c2a4d5e6f708192a3b4c5d6e7f8/0 \\ + -F 'chunk=@./part-aa' +\`\`\` + +**成功响应**(200): + +\`\`\`json +{ "code": 200, "msg": "ok", "data": { "chunk_hash": "9af1…", "chunk_index": 0 } } +\`\`\` + +重复上传已完成的分片(幂等跳过): + +\`\`\`json +{ "code": 200, "msg": "ok", "data": { "chunk_hash": "9af1…", "chunk_index": 0, "skipped": true } } +\`\`\` + +**错误响应**: + +\`\`\`json +{ "code": 404, "msg": "上传会话不存在" } +\`\`\` + +\`\`\`json +{ "code": 400, "msg": "无效的分片索引" } +\`\`\` + +\`\`\`json +{ "code": 400, "msg": "分片大小超过声明值: 最大 5242880, 实际 5300000" } +\`\`\` + +\`\`\`json +{ "code": 400, "msg": "缺少分片文件字段 chunk" } +\`\`\` + +\`\`\`json +{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" } +\`\`\` + +> 约束:单分片 ≤ \`chunk_size\`(init 声明值)且 ≤ **32MiB 硬上限**(init 时 \`chunk_size>33554432\` 直接 400「chunk_size 过大」); +> 总大小(init 按分片数上限、上传/合并按累计)受**动态策略上限**约束——\`max_file_size>0\` 时为其, +> 否则回落 \`uploadSize\`(v2 需求 ④⑩,管理端改后立即生效,超限清理会话);首个分片做 magic bytes +> 防伪校验(403「文件内容校验失败:…」)。分片哈希由服务端计算;合并时与分片记录交叉校验,不一致报 400。 +> +> **enableChunk 开关**:管理端关闭分片上传后,\`/chunk/upload/*\` 全部端点返回 403「分片上传未启用」(后端强制,前端仅隐藏入口)。 + +## 查询进度:GET /chunk/upload/status/{uploadID} + +\`\`\`bash +curl -s http://localhost:8466/chunk/upload/status/3f6b8c2a4d5e6f708192a3b4c5d6e7f8 +\`\`\` + +**成功响应**(200): + +\`\`\`json +{ + "code": 200, "msg": "ok", + "data": { + "upload_id": "3f6b8c2a4d5e6f708192a3b4c5d6e7f8", + "file_name": "movie.mp4", + "file_size": 15728640, + "chunk_size": 5242880, + "total_chunks": 3, + "uploaded_chunks": [0, 1], + "progress": 66.66666666666667 + } +} +\`\`\` + +## 完成合并:POST /chunk/upload/complete/{uploadID} + +全部分片到齐后调用。服务端按索引有序合并 + SHA256 校验,成功后创建分享并清理分片。 + +**请求体**(JSON,亦兼容表单): + +| 参数 | 类型 | 必需 | 默认 | 说明 | +|---|---|---|---|---| +| \`expire_value\` | int | ❌ | \`1\` | 过期值 | +| \`expire_style\` | string | ❌ | \`day\` | 过期方式(同文件分享) | + +\`\`\`bash +curl -s -X POST http://localhost:8466/chunk/upload/complete/3f6b8c2a4d5e6f708192a3b4c5d6e7f8 \\ + -H 'Content-Type: application/json' \\ + -d '{"expire_value":1,"expire_style":"day"}' +\`\`\` + +**成功响应**(200): + +\`\`\`json +{ "code": 200, "msg": "ok", "data": { "code": "R7T2K", "name": "movie.mp4" } } +\`\`\` + +**错误响应**: + +\`\`\`json +{ "code": 400, "msg": "分片不完整" } +\`\`\` + +\`\`\`json +{ "code": 400, "msg": "分片哈希校验失败,请重新上传" } +\`\`\` + +\`\`\`json +{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" } +\`\`\` + +## 取消上传:DELETE /chunk/upload/{uploadID} + +清理分片文件与上传记录,释放容量预留。 + +\`\`\`bash +curl -s -X DELETE http://localhost:8466/chunk/upload/3f6b8c2a4d5e6f708192a3b4c5d6e7f8 +\`\`\` + +\`\`\`json +{ "code": 200, "msg": "ok", "data": { "message": "上传已取消" } } +\`\`\` + +\`\`\`json +{ "code": 404, "msg": "上传会话不存在" } +\`\`\` +`,g=`# 预签名直传 + +服务端预生成上传地址,客户端直接向存储引擎(或服务端代理)上传文件,最后确认建分享。 +两种模式: + +| 模式 | 引擎 | 上传方式 | +|---|---|---| +| \`direct\` | S3(含 MinIO/R2 等 S3 兼容存储) | 客户端 \`PUT\` 到预签名 URL,直连对象存储 | +| \`proxy\` | 本地 / WebDAV | 客户端 \`PUT\` multipart 到服务端代理接口 | + +> 本地 / WebDAV 引擎不支持预签名直链,init 返回正常 \`proxy\` 模式(仅当引擎预签名调用本身异常时才报错)。 +> 会话有效期 **900 秒**(15 分钟)。proxy 模式响应另含 \`legacy_proxy_upload_url\` +> (\`/api\` 前缀的兼容别名,已废弃,与 \`upload_url\` 等价)。 +> 全部端点经审计中间件落库(action=upload)。 + +## 上传流程 + +\`\`\`text +direct 模式: +POST /presign/upload/init → upload_url(S3 预签名 PUT) +PUT → 客户端直传 S3(无认证头) +POST /presign/upload/confirm/{id} → 确认 → 取件码 + +proxy 模式: +POST /presign/upload/init → upload_url = /presign/upload/proxy/{id} +PUT /presign/upload/proxy/{id} → multipart 上传,服务端转存并直接建分享 +\`\`\` + +## 初始化:POST /presign/upload/init + +**请求体**(JSON,亦兼容表单): + +| 参数 | 类型 | 必需 | 默认 | 说明 | +|---|---|---|---|---| +| \`file_name\` | string | ✅ | - | 文件名(清理 + 白名单校验) | +| \`file_size\` | int | ✅ | - | 文件字节数(≤ 生效上限:\`max_file_size>0\` 时为其,否则 \`uploadSize\`) | +| \`expire_value\` | int | ❌ | \`1\` | 过期值(\`count\` 型受 \`max_save_count\` 约束) | +| \`expire_style\` | string | ❌ | \`day\` | 过期方式(须在 \`expireStyle\` 白名单内) | + +**curl 示例**: + +\`\`\`bash +curl -s -X POST http://localhost:8466/presign/upload/init \\ + -H 'Content-Type: application/json' \\ + -d '{"file_name":"backup.zip","file_size":20971520,"expire_value":7,"expire_style":"day"}' +\`\`\` + +**S3(direct)成功响应**(200): + +\`\`\`json +{ + "code": 200, "msg": "ok", + "data": { + "upload_id": "6a1e…", + "upload_url": "https://minio:9000/filecodebox/share/data/2025/06/01/6a1e…/backup.zip?X-Amz-…", + "mode": "direct", + "expires_in": 900, + "file_path": "share/data/2025/06/01/6a1e…" + } +} +\`\`\` + +**local/WebDAV(proxy)成功响应**(200): + +\`\`\`json +{ + "code": 200, "msg": "ok", + "data": { + "upload_id": "6a1e…", + "upload_url": "/presign/upload/proxy/6a1e…", + "proxy_upload_url": "/presign/upload/proxy/6a1e…", + "mode": "proxy", + "expires_in": 900, + "file_path": "share/data/2025/06/01/6a1e…" + } +} +\`\`\` + +**错误响应**: + +\`\`\`json +{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" } +\`\`\` + +> 大小上限为动态策略(v2 需求 ④⑩):管理端改 \`max_file_size\`(0=回落 \`uploadSize\`)后 +> 立即按新上限校验 init 声明的 \`file_size\`。 + +\`\`\`json +{ "code": 403, "msg": "不允许上传该类型文件" } +\`\`\` + +\`\`\`json +{ "code": 507, "msg": "存储空间已达到管理员设置的容量上限" } +\`\`\` + +## 直传确认:POST /presign/upload/confirm/{uploadID} + +\`direct\` 模式专用:客户端向 S3 \`PUT\` 完成后调用。服务端会核对实际对象(多引擎一致): + +- **大小核对**:实际大小与声明差 >1KB → 400「文件大小与声明不符」;超过策略上限(\`max_file_size\`)→ **删除对象、释放容量预留**并 403; +- **内容校验**:取对象前 64 字节做 magic bytes 白名单校验(失败删除对象并报错); +- 全部通过后创建分享记录。 + +\`\`\`bash +# 1) 直传 S3(注意:不要带 Authorization 头,预签名 URL 自带鉴权) +curl -X PUT "" --upload-file ./backup.zip +# 2) 确认 +curl -s -X POST http://localhost:8466/presign/upload/confirm/6a1e… +\`\`\` + +**成功响应**(200): + +\`\`\`json +{ "code": 200, "msg": "ok", "data": { "code": "B4N8Q", "name": "backup.zip" } } +\`\`\` + +**错误响应**: + +\`\`\`json +{ "code": 404, "msg": "文件未上传或上传失败" } +\`\`\` + +\`\`\`json +{ "code": 400, "msg": "文件大小与声明不符" } +\`\`\` + +\`\`\`json +{ "code": 403, "msg": "文件大小超过限制" } +\`\`\` + +\`\`\`json +{ "code": 400, "msg": "此会话不支持direct模式" } +\`\`\` + +## 代理上传:PUT /presign/upload/proxy/{uploadID} + +\`proxy\` 模式专用:multipart 上传到服务端,服务端流式转存到存储引擎后立即创建分享记录。 + +**multipart 字段**:\`file\`(必需)。 + +**curl 示例**: + +\`\`\`bash +curl -s -X PUT http://localhost:8466/presign/upload/proxy/6a1e… -F 'file=@./backup.zip' +\`\`\` + +**成功响应**(200): + +\`\`\`json +{ "code": 200, "msg": "ok", "data": { "code": "B4N8Q", "name": "backup.zip" } } +\`\`\` + +**错误响应**: + +\`\`\`json +{ "code": 400, "msg": "缺少上传文件 file 字段" } +\`\`\` + +\`\`\`json +{ "code": 400, "msg": "文件大小与声明不符" } +\`\`\` + +\`\`\`json +{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" } +\`\`\` + +> 文件实际大小须与 init 声明的 \`file_size\` 一致(±1KB 容差);成功后会话即删除,不可重复使用。 + +## 查询会话:GET /presign/upload/status/{uploadID} + +\`\`\`bash +curl -s http://localhost:8466/presign/upload/status/6a1e… +\`\`\` + +**成功响应**(200): + +\`\`\`json +{ + "code": 200, "msg": "ok", + "data": { + "upload_id": "6a1e…", + "file_name": "backup.zip", + "file_size": 20971520, + "mode": "proxy", + "created_at": "2025-06-01T12:00:00+08:00", + "expires_at": "2025-06-01T12:15:00+08:00", + "is_expired": false + } +} +\`\`\` + +## 取消会话:DELETE /presign/upload/{uploadID} + +删除会话并释放容量预留;\`direct\` 模式会尽力清理已直传到 S3 的对象。 + +\`\`\`bash +curl -s -X DELETE http://localhost:8466/presign/upload/6a1e… +\`\`\` + +\`\`\`json +{ "code": 200, "msg": "ok", "data": { "message": "上传会话已取消" } } +\`\`\` + +\`\`\`json +{ "code": 404, "msg": "上传会话不存在" } +\`\`\` + +\`\`\`json +{ "code": 404, "msg": "上传会话已过期" } +\`\`\` +`,_='# 管理后台 API\n\n管理端接口:除 `POST /admin/login` 与初始化向导 `/setup` 外,一律要求\n`Authorization: Bearer `(见《认证与限流》),无效令牌 401。\n\n## 初始化向导:GET /setup\n\n未初始化时返回 HTML 配置页(站点名称、管理员密码、上传/限流/保存策略);\n已初始化时 `303` 重定向到 `/`。\n\n```bash\ncurl -i http://localhost:8466/setup\n```\n\n## 初始化提交:POST /setup\n\n表单(浏览器向导)或 JSON 均可;成功后写入库配置 KV 并生成密码哈希与 `jwt_secret`。\n表单提交返回成功 HTML 页;JSON 提交返回 JSON。\n\n**主要字段**:\n\n| 字段 | 必需 | 默认 | 说明 |\n|---|---|---|---|\n| `admin_password` | ✅ | - | 管理员密码(≥8 位) |\n| `confirm_password` | ✅ | - | 确认密码(须一致) |\n| `site_name` | ❌ | 文件快传 | 站点名称 |\n| `upload_size_value` / `upload_size_unit` | ❌ | 10 / MB | 单文件大小限制(单位 KB/MB/GB) |\n| `save_time_value` / `save_time_unit` | ❌ | 0 / day | 最长保存秒数(0=不限) |\n| `expireStyle` | ❌ | day,hour,minute,forever,count | 过期方式(可多值/逗号分隔) |\n| `code_generate_type` | ❌ | secret | 取件码类型 `number`/`secret` |\n| `errorCount` / `errorMinute` | ❌ | 10 / 1 | 取件错误限流 |\n| `loginCount` / `loginMinute` | ❌ | 5 / 15 | 登录失败限流 |\n| `uploadCount` / `uploadMinute` | ❌ | 10 / 1 | 上传限流 |\n| `allowed_file_types` | ❌ | `*` | 逗号分隔白名单 |\n| `openUpload` / `enableChunk` | ❌ | 1 / 0 | 游客上传 / 分片开关(`1`/`true`/`on`/`yes`) |\n\n```bash\ncurl -s -X POST http://localhost:8466/setup \\\n -H \'Content-Type: application/json\' \\\n -d \'{"admin_password":"admin12345","confirm_password":"admin12345","site_name":"我的文件柜","upload_size_value":10,"upload_size_unit":"MB"}\'\n```\n\n```json\n{ "code": 200, "msg": "ok", "data": { "ok": true, "admin": "/#/admin" } }\n```\n\n**错误响应**(400,HTML 表单时内嵌错误提示):\n\n```json\n{ "code": 400, "msg": "管理员密码至少 8 位" }\n```\n\n## 登录:POST /admin/login\n\n```bash\ncurl -s -X POST http://localhost:8466/admin/login \\\n -H \'Content-Type: application/json\' \\\n -d \'{"password":"admin12345"}\'\n```\n\n**成功响应**(200):\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": {\n "id": "admin", "username": "admin",\n "token": "eyJhbGciOiJIUzI1NiIs...",\n "token_type": "Bearer",\n "expires_at": 1750000000,\n "expires_in": 604800\n }\n}\n```\n\n**错误响应**:\n\n```json\n{ "code": 401, "msg": "密码错误" }\n```\n\n```json\n{ "code": 423, "msg": "请求次数过多,请稍后再试" }\n```\n\n## 校验会话:GET /admin/verify\n\n```bash\ncurl -s http://localhost:8466/admin/verify -H "Authorization: Bearer $TOKEN"\n```\n\n```json\n{ "code": 200, "msg": "ok", "data": { "id": "admin", "username": "admin", "token": "eyJ…", "token_type": "Bearer", "expires_at": 1750000000 } }\n```\n\n## 登出:POST /admin/logout\n\n无状态 JWT,服务端仅返回确认(客户端应丢弃令牌):\n\n```json\n{ "code": 200, "msg": "ok", "data": { "ok": true } }\n```\n\n## 仪表盘:GET /admin/dashboard\n\n```bash\ncurl -s http://localhost:8466/admin/dashboard -H "Authorization: Bearer $TOKEN"\n```\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": {\n "totalFiles": 42,\n "storageUsed": "123456789",\n "sysUptime": 1750000000000,\n "yesterdayCount": 5, "yesterdaySize": "1048576",\n "todayCount": 12, "todaySize": "5242880",\n "activeCount": 40, "expiredCount": 2,\n "textCount": 10, "fileCount": 32, "chunkedCount": 3,\n "usedCount": 156,\n "storageBackend": "local",\n "uploadSizeLimit": 10485760,\n "openUpload": 1, "enableChunk": 1,\n "maxSaveSeconds": 0,\n "topSuffixes": [ { "suffix": ".pdf", "count": 12 }, { "suffix": "Text", "count": 10 } ],\n "recentFiles": [ { "id": 42, "code": "K3P9W", "name": "report.pdf", "size": 1048576, "created_at": "2025-06-01T12:00:00+08:00", "expired_at": "2025-06-08T12:00:00+08:00", "is_expired": false, "expired_count": -1, "used_count": 3, "is_text": false, "is_chunked": false, "is_permanent": false, "has_download_limit": false, "remaining_downloads": null, "file_hash": null, "prefix": "report", "suffix": ".pdf", "text": false, "createdAt": "2025-06-01T12:00:00+08:00", "expiredAt": "2025-06-08T12:00:00+08:00", "isExpired": false, "expiredCount": -1, "usedCount": 3, "isText": false, "isChunked": false, "isPermanent": false, "hasDownloadLimit": false, "remainingDownloads": null, "fileHash": null } ],\n "recentActivities": []\n }\n}\n```\n\n> `storageUsed`/`todaySize`/`yesterdaySize` 为字符串字节数;`sysUptime` 为服务启动时刻的 Unix 毫秒。\n\n## 文件列表:GET /admin/file/list\n\n**参数**:\n\n| 参数 | 默认 | 说明 |\n|---|---|---|\n| `page` / `size` | 1 / 10 | 分页(size 1~100) |\n| `keyword` | - | 模糊匹配取件码/文件名/哈希/文本内容 |\n| `status` | - | `active` / `expired` |\n| `type` | - | `text` / `file` / `chunked` |\n| `sortBy` | `created_at` | `created_at`/`expired_at`/`name`/`size`/`used_count`/`code` |\n| `sortOrder` | `desc` | `asc` / `desc` |\n\n```bash\ncurl -s "http://localhost:8466/admin/file/list?page=1&size=10&status=active&sortBy=size&sortOrder=desc" \\\n -H "Authorization: Bearer $TOKEN"\n```\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": {\n "page": 1, "size": 10, "total": 40,\n "summary": { "totalFiles": 42, "activeCount": 40, "expiredCount": 2, "textCount": 10, "fileCount": 32, "chunkedCount": 3, "storageUsed": 123456789, "usedCount": 156 },\n "data": [\n { "id": 42, "code": "K3P9W", "name": "report.pdf", "prefix": "report", "suffix": ".pdf", "size": 1048576, "is_text": false, "is_chunked": false, "is_expired": false, "expired_at": "2025-06-08T12:00:00+08:00", "expired_count": -1, "used_count": 3, "created_at": "2025-06-01T12:00:00+08:00", "has_download_limit": false, "is_permanent": false, "remaining_downloads": null, "file_hash": null, "text": false, "isText": false, "isChunked": false, "isExpired": false, "expiredAt": "2025-06-08T12:00:00+08:00", "expiredCount": -1, "usedCount": 3, "createdAt": "2025-06-01T12:00:00+08:00", "hasDownloadLimit": false, "isPermanent": false, "remainingDownloads": null, "fileHash": null }\n ]\n }\n}\n```\n\n## 文件详情:GET /admin/file/detail\n\n`GET ?id=42` 或 `POST {"id":42}`。返回列表条目字段;文本分享额外含 `content`(全文)。\n\n```json\n{ "code": 200, "msg": "ok", "data": { "id": 42, "code": "K3P9W", "name": "report.pdf", "size": 1048576, "is_text": false, "expired_at": "2025-06-08T12:00:00+08:00", "expired_count": -1, "used_count": 3, "created_at": "2025-06-01T12:00:00+08:00", "file_hash": null } }\n```\n\n```json\n{ "code": 404, "msg": "文件不存在" }\n```\n\n## 更新文件:PATCH /admin/file/update\n\n更新取件码/文件名(前后缀)/过期策略。**PATCH 为主名,POST 为兼容别名**。\n\n**请求体**:\n\n| 字段 | 类型 | 说明 |\n|---|---|---|\n| `id` | int | 必需 |\n| `code` | string | 新取件码(冲突 400「code已存在」) |\n| `prefix` / `suffix` | string | 文件名前后缀 |\n| `expired_at` | string | 过期时间(ISO 8601,如 `2025-07-01T00:00:00+08:00`) |\n| `expired_count` | int | 取件次数上限(`-1` 按时间/永久) |\n\n```bash\ncurl -s -X PATCH http://localhost:8466/admin/file/update \\\n -H "Authorization: Bearer $TOKEN" -H \'Content-Type: application/json\' \\\n -d \'{"id":42,"expired_at":"2025-07-01T00:00:00+08:00","expired_count":10}\'\n```\n\n```json\n{ "code": 200, "msg": "ok", "data": "更新成功" }\n```\n\n```json\n{ "code": 400, "msg": "code已存在" }\n```\n\n## 删除文件:DELETE /admin/file/delete\n\n删除分享记录并连带删除存储文件(文本分享无存储文件)。`DELETE` 或 `POST`,请求体 `{"id":42}`。\n\n```bash\ncurl -s -X DELETE http://localhost:8466/admin/file/delete \\\n -H "Authorization: Bearer $TOKEN" -H \'Content-Type: application/json\' -d \'{"id":42}\'\n```\n\n```json\n{ "code": 200, "msg": "ok", "data": null }\n```\n\n```json\n{ "code": 400, "msg": "请选择要删除的文件" }\n```\n\n## 批量删除:POST /admin/file/batch-delete\n\n`POST` 或 `DELETE`,请求体 `{"ids":[41,42,43]}`。返回逐条统计:\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": {\n "requestedCount": 3, "deletedCount": 2, "missingCount": 1, "failedCount": 0,\n "deleted": [41, 42], "missing": [43], "failed": [],\n "requested_count": 3, "deleted_count": 2, "missing_count": 1, "failed_count": 0\n }\n}\n```\n\n## 批量更新:PATCH /admin/file/batch-update\n\n`PATCH` 或 `POST`。请求体:`ids[]` 必需;`expired_at`(ISO 8601)/`expired_count` 二选一;\n`clearExpiredAt: true`(或 `clear_expired_at`)= 清空过期时间并置 `expired_count=-1`(永久)。\n\n```bash\ncurl -s -X PATCH http://localhost:8466/admin/file/batch-update \\\n -H "Authorization: Bearer $TOKEN" -H \'Content-Type: application/json\' \\\n -d \'{"ids":[41,42],"clearExpiredAt":true}\'\n```\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": { "requestedCount": 2, "updatedCount": 2, "missingCount": 0, "failedCount": 0, "updated": 2, "missing": [], "failed": [], "requested_count": 2, "updated_count": 2, "missing_count": 0, "failed_count": 0 }\n}\n```\n\n## 过期策略动作:PATCH /admin/file/policy-action\n\n对单个文件执行快捷策略。`PATCH` 或 `POST`。批量版为 `/admin/file/batch-policy-action`(`{"ids":[…]}`),响应结构同批量更新。\n\n**请求体**:\n\n| 字段 | 说明 |\n|---|---|\n| `id` | 文件 ID |\n| `action` | `extend_24h` / `extend_7d` / `make_permanent` / `reset_download_limit` |\n| `downloadLimit` | 仅 `reset_download_limit` 用:新取件次数(默认 5,须 >0) |\n\n```bash\ncurl -s -X PATCH http://localhost:8466/admin/file/policy-action \\\n -H "Authorization: Bearer $TOKEN" -H \'Content-Type: application/json\' \\\n -d \'{"id":42,"action":"reset_download_limit","downloadLimit":3}\'\n```\n\n```json\n{ "code": 200, "msg": "ok", "data": { "id": 42, "action": "reset_download_limit" } }\n```\n\n```json\n{ "code": 400, "msg": "不支持的策略动作" }\n```\n\n> `extend_24h`/`extend_7d` 在当前过期时间(未过期时)基础上顺延;`make_permanent` 清空过期时间并置次数 -1。\n\n## 管理员下载:GET /admin/file/download?id=\n\n下载原文件(**不消耗取件次数**);文件返回二进制流(支持 Range),文本分享返回 JSON(`data` 为文本内容)。\n\n```bash\ncurl -s -OJ "http://localhost:8466/admin/file/download?id=42" -H "Authorization: Bearer $TOKEN"\n```\n\n## 文本预览:GET /admin/file/preview\n\n仅文本分享可用(文件分享返回 400)。`maxChars` 截断长度(默认 4000,1~20000)。\n\n```bash\ncurl -s "http://localhost:8466/admin/file/preview?id=41&maxChars=100" -H "Authorization: Bearer $TOKEN"\n```\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": {\n "id": 41, "code": "8XQ2M", "name": "Text.txt", "type": "text",\n "content": "你好,文件快传",\n "length": 24, "previewLength": 24, "truncated": false,\n "maxChars": 100, "max_chars": 100,\n "created_at": "2025-06-01T12:00:00+08:00", "createdAt": "2025-06-01T12:00:00+08:00"\n }\n}\n```\n\n```json\n{ "code": 400, "msg": "仅文本分享支持预览" }\n```\n\n## 读取配置:GET /admin/config/get\n\n返回运行时配置 KV(含默认值与管理端修改)。`admin_token` 恒返回空串(屏蔽);\n`jwt_secret` 不下发;存储引擎为进程级单例,`_engine_hint` 提示引擎配置修改需重启。\nv2 新增键(需求 ①②③④⑩)一并返回:`background_url`、`footer_text`、`footer_beian`、\n`notify_enabled`、`max_save_count`、`max_file_size` 等。\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": {\n "site_name": "文件快传",\n "name": "文件快传",\n "description": "开箱即用的文件快传系统",\n "page_explain": "…", "keywords": "…",\n "notify_title": "系统通知", "notify_content": "…", "notify_enabled": 1,\n "logo_url": "",\n "favicon_url": "",\n "background_url": "", "footer_text": "", "footer_beian": "",\n "openUpload": 1, "uploadSize": 10485760,\n "max_file_size": 0, "max_save_count": 0,\n "allowed_file_types": ["*"], "expireStyle": ["day","hour","minute","forever","count"],\n "code_generate_type": "secret", "enableChunk": 1,\n "uploadMinute": 1, "uploadCount": 10,\n "errorMinute": 1, "errorCount": 10,\n "loginCount": 5, "loginMinute": 15,\n "max_save_seconds": 0, "storageLimit": 0,\n "opacity": 0.9, "background": "", "showAdminAddr": 0, "robotsText": "User-agent: *\\nDisallow: /",\n "adminSessionExpire": 604800,\n "storage_path": "", "local_storage_path": "/app/data",\n "file_storage": "local",\n "admin_token": "",\n "_engine_hint": { "storage_backend": "local", "note": "存储引擎为进程级单例,修改存储引擎相关配置后需重启服务生效" }\n }\n}\n```\n\n## 存储引擎热切换:POST /admin/storage/switch(v3)\n\n运行时切换存储引擎,**无需重启**:\n\n```bash\ncurl -s -X POST http://localhost:8466/admin/storage/switch \\\n -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \\\n -d \'{"engine":"s3"}\'\n# 成功:{"code":200,"msg":"ok","data":{"ok":true,"engine":"s3"}}\n# 失败:503 {"code":503,"msg":"存储引擎切换失败,已保持原引擎: …"}\n```\n\n- `engine` 仅接受 `local|s3|webdav`(400 中文错误)。\n- 切换流程:用最新 KV 参数构建新引擎 → 健康检查 → 通过才替换当前引擎并持久化 `storage_engine`。\n- 失败(构建/健康检查不过)返回 503,**原引擎与 KV 均保持不变**。\n- 切到当前引擎为幂等操作(直接返回 200)。\n- 旧文件按归属引擎(`file_codes.engine`)取回,切换后旧引擎文件仍可下载。\n\n## 更新配置:PATCH /admin/config/update\n\n部分更新(JSON 对象,未提供的键不变;表单亦可)。**PATCH 为主名,POST 为兼容别名**。\n\n- 仅接受管理端可见键(见上响应键集合),未知键忽略。\n- 数值型键自动转型:`openUpload`、`enableChunk`、`uploadSize`、`storageLimit`、限流四组、`max_save_seconds`、`adminSessionExpire`、`showAdminAddr`;v2 新增 `max_save_count`、`max_file_size`、`notify_enabled`;`opacity` 为浮点。\n- **v3.1**:`site_domain`(站点对外域名)可经本端点设置,非法格式 400(仅 http/https、主机+端口、不带路径)。\n- **v3 引擎键**:`storage_engine` 不经本端点修改(走 `POST /admin/storage/switch`);引擎参数键\n (`local_storage_path`、`webdav_url`、`webdav_root_path`、`webdav_username`、`webdav_password`、\n `s3_endpoint_url`、`s3_region_name`、`s3_bucket_name`、`s3_access_key_id`、`s3_secret_access_key`、\n `aws_session_token`、`s3_addressing_style`)可经本端点保存——保存后对应引擎实例缓存失效,\n 下次切换/构建生效;敏感键空串或 `******` 表示不修改。\n- **v2 schema 校验**(`settings.KVSchema`,越界一律 400,中文错误信息):\n - 整型边界:`max_file_size` ≤ 10GiB(10737418240)、`max_save_count` ≤ 100000、`max_save_seconds` ≤ 31536000(365 天)、`notify_enabled` ∈ {0,1}、`uploadCount` 1~10000、`uploadMinute` 1~1440 等;\n - 字符串长度:`background_url` ≤ 2048、`footer_text` ≤ 2000、`footer_beian` ≤ 128、`notify_title` ≤ 128、`notify_content` ≤ 2000 字符;\n - 列表键 `expireStyle` / `allowed_file_types`:须为字符串数组(或逗号分隔串)且至少保留一项;\n - 错误示例:`{"code":400,"msg":"max_file_size 必须是整数"}`、`{"code":400,"msg":"max_file_size 不能大于 10737418240"}`、`{"code":400,"msg":"footer_beian 长度不能超过 128 字符"}`、`{"code":400,"msg":"notify_enabled 不能大于 1"}`。\n- `background_url` 协议白名单(需求 ①,防 `javascript:` 注入):仅 `http(s)://`、`data:image/*` 与站内相对路径(`/`开头);空串=清除背景。非法值 400:\n `{"code":400,"msg":"background_url 仅支持 http(s) 地址、data:image 图片或站内相对路径"}`\n- `admin_token`:明文密码自动哈希,**并轮换 `jwt_secret`(全部管理员令牌立即失效)**;空串忽略;已是哈希格式则原样保存。\n- `adminSessionExpire` 须为 1~365 的整天秒数(86400 的整数倍),否则 400。\n- `storageLimit` 不能小于 0。\n- 修改限流/策略配置**立即生效**(无需重启:限流规则运行时同步,策略由上传链路每次实时读取);引擎相关(`file_storage`/`s3_*`/`webdav_*`/`storage_path`/`local_storage_path`)需重启。\n\n```bash\ncurl -s -X PATCH http://localhost:8466/admin/config/update \\\n -H "Authorization: Bearer $TOKEN" -H \'Content-Type: application/json\' \\\n -d \'{"site_name":"我的文件柜","uploadSize":52428800,"openUpload":1,"footer_beian":"京ICP备20240001号","max_file_size":10485760,"max_save_count":5}\'\n```\n\n```json\n{ "code": 200, "msg": "ok", "data": { "ok": true } }\n```\n\n```json\n{ "code": 400, "msg": "adminSessionExpire 必须是 1 到 365 个整天" }\n```\n\n```json\n{ "code": 400, "msg": "background_url 仅支持 http(s) 地址、data:image 图片或站内相对路径" }\n```\n\n## 修改管理员密码:PATCH /admin/settings/password\n\n`PATCH` 或 `POST`。新密码 ≥8 位;成功后哈希保存并**轮换 `jwt_secret`,所有旧令牌失效(401)**,需重新登录。\n\n```bash\ncurl -s -X PATCH http://localhost:8466/admin/settings/password \\\n -H "Authorization: Bearer $TOKEN" -H \'Content-Type: application/json\' \\\n -d \'{"old_password":"admin12345","new_password":"new-pass-6789"}\'\n```\n\n```json\n{ "code": 200, "msg": "ok", "data": { "ok": true } }\n```\n\n```json\n{ "code": 400, "msg": "新密码长度至少 8 位" }\n```\n\n```json\n{ "code": 401, "msg": "旧密码错误" }\n```\n',y='# 审计日志查询\n\n所有上传/下载请求由审计中间件自动落库(需求 ③),管理端分页查询。\n认证:`Authorization: Bearer `。\n\n## 审计记录内容\n\n每条审计日志覆盖以下维度(需求 ③):\n\n| 维度 | 字段 | 说明 |\n|---|---|---|\n| 操作时间 | `created_at` | RFC 3339 |\n| 客户端 | `ip` | 可信代理场景解析 XFF 后的真实 IP |\n| 终端信息 | `user_agent` | 原始 UA |\n| 设备解析 | `device_os` / `device_browser` / `device_type` | 由 UA 解析(如 Windows/Chrome/desktop) |\n| 动作 | `action` | `upload`(上传类) / `download`(取件/下载类) / `admin`(管理端敏感操作,v2.5.6 新增) |\n| 结果 | `result` | `success` / `denied`(拒绝:401/403/423/428)/ `failed`(失败:其余 4xx/5xx 或业务报错) |\n| 字节数 | `size_bytes` | 文件总大小;`transferred_bytes` 实际传输(**Range 下载只计实际区间字节**;下载由中间件自动统计,上传由各 handler 填充) |\n| 耗时 | `duration_ms` | 毫秒 |\n| 角色 | `actor` | `admin`(有效管理员令牌)/ `guest` |\n| 业务 | `file_code` / `file_name` | 取件码 / 文件名(分片上传时 `file_code` 为 `upload_id`) |\n| 错误 | `error_msg` | 失败/拒绝原因 |\n\n**命中审计的端点**:上传类 `POST /share/text`、`POST /share/file`、`/chunk/upload*`、`/presign*`(POST/PUT);\n下载类 `GET /share/download`、`GET /share/select`、`GET /share/metadata`;\n管理类(`action=admin`)`POST /admin/login`、`POST /admin/logout`、`PATCH|POST /admin/config/update`、\n`PATCH|POST /admin/settings/password`、`POST /admin/storage/switch`、`PATCH|DELETE /admin/file/update|delete|batch-delete|batch-update|policy-action|batch-policy-action`。\n管理类动作未显式填结果时按 HTTP 状态兜底落库(401/403/423 → `denied`,5xx → `failed`,其余 → `success`)。\n失败与被拒绝的请求同样落库。\n\n## 查询接口:GET /admin/audit/list\n\n**参数**:\n\n| 参数 | 默认 | 说明 |\n|---|---|---|\n| `page` | 1 | 页码(≥1) |\n| `size` | 20 | 每页条数(1~200;兼容 `pageSize`) |\n| `action` | - | `upload` / `download` / `admin` |\n| `result` | - | `success` / `denied` / `failed` |\n| `ip` | - | 按客户端 IP 过滤 |\n| `start_time` / `end_time` | - | 时间范围,ISO 8601(如 `2025-06-01T00:00:00+08:00`;也接受 `2006-01-02 15:04:05` / 日期) |\n\n```bash\ncurl -s "http://localhost:8466/admin/audit/list?page=1&size=20&action=download&result=success&start_time=2025-06-01T00:00:00%2B08:00" \\\n -H "Authorization: Bearer $TOKEN"\n```\n\n**成功响应**(200):\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": {\n "data": [\n {\n "id": 318,\n "action": "download",\n "file_code": "K3P9W",\n "file_name": "report.pdf",\n "size_bytes": 1048576,\n "transferred_bytes": 524288,\n "ip": "203.0.113.7",\n "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",\n "device_os": "Windows",\n "device_browser": "Chrome",\n "device_type": "desktop",\n "actor": "guest",\n "result": "success",\n "error_msg": "",\n "duration_ms": 128,\n "created_at": "2025-06-01T12:03:45+08:00",\n "fileCode": "K3P9W",\n "fileName": "report.pdf",\n "sizeBytes": 1048576,\n "transferredBytes": 524288,\n "userAgent": "Mozilla/5.0 …",\n "deviceOs": "Windows",\n "deviceBrowser": "Chrome",\n "deviceType": "desktop",\n "errorMsg": "",\n "durationMs": 128,\n "createdAt": "2025-06-01T12:03:45+08:00"\n }\n ],\n "total": 1180,\n "page": 1,\n "size": 20\n }\n}\n```\n\n> 行字段以 snake_case 为准;camelCase 为兼容双份输出(文档不再重复列出)。\n\n**错误响应**:\n\n```json\n{ "code": 400, "msg": "start_time 时间格式错误" }\n```\n\n```json\n{ "code": 401, "msg": "令牌无效或已过期" }\n```\n\n## 别名:GET /admin/audit/logs\n\n与 `/admin/audit/list` 完全相同(同一 handler 的兼容别名),参数与响应一致。\n\n## 典型查询\n\n```bash\n# 最近的下载行为\ncurl -s "http://localhost:8466/admin/audit/list?action=download&size=50" -H "Authorization: Bearer $TOKEN"\n\n# 某 IP 的全部被拒请求(限流/鉴权失败)\ncurl -s "http://localhost:8466/admin/audit/list?ip=203.0.113.7&result=denied" -H "Authorization: Bearer $TOKEN"\n\n# 今天 0 点以来的上传失败\ncurl -s "http://localhost:8466/admin/audit/list?action=upload&result=failed&start_time=2025-06-01T00:00:00%2B08:00" \\\n -H "Authorization: Bearer $TOKEN"\n```\n',h='# 存储引擎配置\n\n存储引擎只支持三种:**本地磁盘 / S3 / WebDAV**,由进程级环境变量 `FCB_STORAGE_ENGINE` 选择。\n引擎与引擎相关配置在启动时一次性读取(`storage.SetEngineOptions` → `NewEngine`),\n**运行时修改引擎 KV 需重启服务**(管理端 `GET /admin/config/get` 的 `_engine_hint` 亦有提示)。\n\n## 引擎选择\n\n```bash\nFCB_STORAGE_ENGINE=local # 启动默认(KV storage_engine 为空时生效)\nFCB_STORAGE_ENGINE=s3\nFCB_STORAGE_ENGINE=webdav\n```\n\n非法值直接启动失败:`FCB_STORAGE_ENGINE 无效值 "xxx",仅支持 local|s3|webdav`。\n\n**v3 运行时热切换**:管理端 `POST /admin/storage/switch`(或后台设置页「存储引擎」卡)可在不重启的情况下切换引擎——\n先构建新引擎并健康检查,通过才生效;失败 503 保持原引擎。当前引擎持久化在 settings KV `storage_engine`(空=回落启动值)。\n各引擎参数(存储目录/服务地址/存储桶/密钥)同样在后台设置页运行时可改;保存后对应引擎实例缓存失效,下次切换/构建生效。\n\n## 文件归属引擎(v3)\n\n每条分享记录(`file_codes.engine`)与上传会话(`upload_chunks.engine` / `presign_upload_sessions.engine`)\n在创建时戳记当时的引擎名。下载、分片合并、删除按**归属引擎**操作——切换引擎后,旧引擎里的文件仍可正常下载与删除\n(空戳为历史数据,回落当前引擎)。\n\n## 按日期目录存储\n\n文件落盘路径:`[storage_path/]share/data/YYYY/MM/DD//<文件名>`(如 `share/data/2026/09/04/…`)。\n按日嵌套目录自然排序、无日月歧义、避免单日海量文件挤在单目录;三种引擎一致适用;历史路径记录在\n`file_codes.file_path`,不受路径规则调整影响。\n\n## 配置键与环境变量\n\n引擎相关配置键(DB settings KV)可由环境变量种子注入(优先级:默认 < 环境变量 < DB KV):\n\n| KV 键 | 环境变量 | 引擎 | 说明 |\n|---|---|---|---|\n| `local_storage_path` | `FCB_LOCAL_STORAGE_PATH` | local | 本地存储根目录(容器内默认 `/app/data`) |\n| `storage_path` | `FCB_STORAGE_PATH` | 全部 | 存储相对路径前缀(空 = `share/data/…`) |\n| `s3_access_key_id` | `FCB_S3_ACCESS_KEY_ID` | s3 | 访问密钥 |\n| `s3_secret_access_key` | `FCB_S3_SECRET_ACCESS_KEY` | s3 | 私有密钥 |\n| `aws_session_token` | `FCB_AWS_SESSION_TOKEN` | s3 | 可选临时会话令牌 |\n| `s3_bucket_name` | `FCB_S3_BUCKET_NAME` | s3 | 桶名 |\n| `s3_endpoint_url` | `FCB_S3_ENDPOINT_URL` | s3 | S3 兼容端点(MinIO/R2 等;AWS 原生可空) |\n| `s3_region_name` | `FCB_S3_REGION_NAME` | s3 | 区域(默认 `auto`) |\n| `s3_addressing_style` | `FCB_S3_ADDRESSING_STYLE` | s3 | `auto`/`path`/`virtual` |\n| `webdav_url` | `FCB_WEBDAV_URL` | webdav | WebDAV 服务地址(如 `http://webdav:5000`) |\n| `webdav_username` | `FCB_WEBDAV_USERNAME` | webdav | 用户名 |\n| `webdav_password` | `FCB_WEBDAV_PASSWORD` | webdav | 密码 |\n| `webdav_root_path` | `FCB_WEBDAV_ROOT_PATH` | webdav | 根目录(默认 `filebox_storage`,不存在自动逐级创建) |\n\n`FCB_STORAGE_ENGINE` 本身不落库(`GET /admin/config/get` 中的 `file_storage` 为 KV 记忆键,\n进程实际引擎以 `FCB_STORAGE_ENGINE` 与 `_engine_hint.storage_backend` 为准)。\n\n## 各引擎要点\n\n### 本地引擎(local)\n\n- 原子写:临时文件 + fsync + rename,避免半写文件。\n- 路径安全:`SanitizePath` + 符号链接逃逸双重防穿越。\n- Range 下载基于 `SectionReader`;分片按索引有序合并 + SHA256 校验,合并后清理分片目录。\n\n### S3 引擎(s3)\n\n- 原生 multipart 流式合并(失败自动 Abort),分片落临时文件保证精确 Content-Length 与可重放。\n- 预签名 GET/PUT 直链(预签名直传唯一 `direct` 模式引擎)。\n- SDK 内置 5xx 指数退避重试;`when_required` 校验模式兼容 MinIO/R2 与纯流式转发。\n\n### WebDAV 引擎(webdav,重点优化)\n\n- **连接复用**:池化 Transport,连接复用(实测 25 次请求仅 1 条 TCP 连接)。\n- **认证**:Basic + Digest(RFC 2617 `qop=auth`,MD5/SHA-256)自动协商,401 挑战驱动。\n- **Range**:`Range` 头透传 + `206` 解析,支持分块/断点下载。\n- **重试**:5xx/429/408 指数退避(封顶 2s ± 20% 抖动,尊重 `Retry-After`)。\n- **流式**:下载经 `io.Pipe` 流式转发不落盘;上下文取消挂到响应体读完之后,防止提前断连。\n- **目录**:按需逐级 `MKCOL` + 目录缓存,避免重复建目录。\n- **超时**:可配置(`webdav_url` 同级暂无独立超时键,引擎默认值内置)。\n\n## 健康检查\n\n三引擎均实现 `HealthCheck`:local 写探针、s3 `ListObjectsV2`、webdav `PROPFIND`(根目录不存在时自建)。\n服务启动时预检失败仅告警不阻断;运行状态可经 `GET /api/v1/health` 的 `data.storage` 查看当前引擎名。\n\n## 预签名直传支持矩阵\n\n| 引擎 | `PresignPutURL` / `PresignGetURL` | init 返回 mode |\n|---|---|---|\n| s3 | ✅ | `direct` |\n| local / webdav | ❌(`ErrNotSupported`) | `proxy`(走服务端代理上传) |\n\n引擎不支持的操作经统一映射返回 501:\n\n```json\n{ "code": 501, "msg": "当前存储引擎不支持该操作" }\n```\n\n## Docker Compose 冒烟编排\n\n`deploy/docker-compose.yml` 提供可选 profile(详见 deploy/README.md):\n\n```bash\ndocker compose --profile minio up -d --build # MinIO(含 mc 自动建桶)+ FCB_STORAGE_ENGINE=s3\ndocker compose --profile webdav up -d --build # dufs WebDAV 冒烟(admin/admin123)+ FCB_STORAGE_ENGINE=webdav\ndocker compose --profile redis up -d --build # Redis 缓存增强(非引擎)\n```\n\n## 存储哨兵错误 → HTTP 状态\n\n| 哨兵错误 | HTTP | 文案 |\n|---|---|---|\n| `ErrNotFound` | 404 | 文件不存在 |\n| `ErrInvalidPath` | 400 | 非法文件路径 |\n| `ErrUnavailable` | 503 | 存储服务不可用,请稍后再试 |\n| `ErrNotSupported` | 501 | 当前存储引擎不支持该操作 |\n| `ErrRangeNotSatisfiable` | 416 | 请求范围超出文件大小 |\n| `ErrHashMismatch` | 400 | 分片哈希校验失败,请重新上传 |\n\n容量超限(`storageLimit`,经容量预留判定)返回 507:`存储空间已达到管理员设置的容量上限`。\n',f='# 环境变量与配置项\n\n配置分三层:**默认值 → `FCB_*` 环境变量 → 数据库 settings KV(管理端运行时修改)**。\nv2 起进程必需的环境变量为空集:数据库默认 **SQLite**(modernc.org/sqlite 纯 Go 驱动,零外部依赖,\nDSN 缺省落 `./data/filecodebox.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DSN` 必需(需求 ⑧)。\n\n## 环境变量(进程级)\n\n| 变量 | 必需 | 默认 | 说明 |\n|---|---|---|---|\n| `FCB_DB_DRIVER` | ❌ | `sqlite` | 数据库驱动:`sqlite` / `postgres`(需求 ⑧) |\n| `FCB_DB_DSN` | 视驱动 | `./data/filecodebox.db` | postgres:连接串(**必需**,如 `postgres://user:pass@host:5432/filecodebox?sslmode=disable`);sqlite:数据库文件路径(可空,父目录自动创建) |\n| `FCB_REDIS_ADDR` | ❌ | 空 | Redis 地址(如 `redis:6379`),也支持 `redis://[:password@]host:port[/db]` / `rediss://`(TLS)URL 形式;**空则缓存降级为进程内存实现**(缓存故障时自动降级为进程内限流计数) |\n| `FCB_REDIS_DB` | ❌ | `0` | Redis 逻辑库号 0-15;URL 形式地址显式携带 `/N` 时以 URL 为准 |\n| `FCB_ADMIN_PASSWORD` | ❌ | 空 | 设置后服务首次启动即自动初始化管理员(≥8 位,不足告警跳过),消除 `/setup` 被抢占窗口;初始化完成后建议移除 |\n| `FCB_LISTEN` | ❌ | `:8466` | HTTP 监听地址 |\n| `FCB_STORAGE_ENGINE` | ❌ | `local` | 存储引擎:`local` / `s3` / `webdav` |\n| `FCB_TRUSTED_PROXIES` | ❌ | 空 | 可信代理 CIDR(逗号分隔),命中时从 `X-Forwarded-For` 解析真实客户端 IP |\n\n- SQLite 连接参数(驱动自动注入):`busy_timeout=10s` + `WAL` 日志模式 + `foreign_keys=1`;连接池 8/4。\n- Postgres 连接池沿用 v1 参数(32/8,1h 轮换);`FCB_DB_DRIVER=postgres` 且未设 `FCB_DB_DSN` 时**启动直接报错**。\n\n引擎相关环境变量(种子注入 settings KV,见《存储引擎配置》):`FCB_LOCAL_STORAGE_PATH`、\n`FCB_STORAGE_PATH`、`FCB_S3_ACCESS_KEY_ID`、`FCB_S3_SECRET_ACCESS_KEY`、`FCB_AWS_SESSION_TOKEN`、\n`FCB_S3_BUCKET_NAME`、`FCB_S3_ENDPOINT_URL`、`FCB_S3_REGION_NAME`、`FCB_S3_ADDRESSING_STYLE`、\n`FCB_WEBDAV_URL`、`FCB_WEBDAV_USERNAME`、`FCB_WEBDAV_PASSWORD`、`FCB_WEBDAV_ROOT_PATH`。\n\n部署用编排变量(`deploy/.env.example`):`WEB_PORT`(默认 8466)、\n`POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB`(默认 filecodebox,仅 `--profile postgres` 时使用)。\n\n## 配置项(settings KV,默认值对齐参考实现)\n\n> 键名/类型/默认值/边界以 `server/internal/config/schema.go` 的 `KVSchema()` 为单一事实来源\n> (schema 同步测试保证与 defaults() 逐键一致);v2 新增键统一 snake_case。\n\n### 站点信息与展示(需求 ①②③)\n\n| 键 | 类型/边界 | 默认 | 说明 |\n|---|---|---|---|\n| `site_name` / `name` | string | 文件快传 | 站点名称(`site_name` 优先) |\n| `site_domain` | string,≤256 | 空 | **v3.1**:站点对外域名(`http(s)://host[:port]`,不带路径;裸主机自动补 `http://`)。配置后分享链接(结果卡/管理端复制)用该域名生成——内网部署也能把公网链接发出去;留空=用当前访问地址 |\n| `description` | string | 开箱即用的文件快传系统 | 站点描述 |\n| `page_explain` | string | (合规声明) | 页面说明文案 |\n| `keywords` | string | 文件快传, 文件分享… | SEO 关键词 |\n| `logo_url` | string | 空(前端回落本地打包 `/assets/logo-*.svg`,需求 ⑤) | 页面导航 Logo,管理端可设任意 URL |\n| `favicon_url` | string | 空(前端回落本地打包 `/assets/favicon-*.png`,需求 ⑤) | favicon / 备用 Logo |\n| `opacity` | float | 0.9 | 界面不透明度 |\n| `background` | string | 空 | 背景图 URL(参考实现既有键,v1 兼容保留) |\n| `background_url` | string,≤2048 字符 | 空 | **v2 需求 ①**:背景图 URL 或上传后地址(空=主题默认;取值时 legacy `background` 键兜底)。管理端保存时校验协议白名单:仅 `http(s)`、`data:image/*` 与站内相对路径(防 `javascript:` 注入,非法 400) |\n| `footer_text` | string,≤2000 字符 | 空 | **v2 需求 ②**:页脚自定义内容(纯文本或受控 HTML 片段) |\n| `footer_beian` | string,≤128 字符 | 空 | **v2 需求 ②**:备案号(如 `京ICP备2024xxxxxx号-1`),展示于页脚 |\n| `notify_enabled` | int(0/1) | 1 | **v2 需求 ③**:通知开关(1=前台右上角悬浮窗展示 / 0=关闭) |\n| `notify_title` | string,≤128 字符 | 系统通知 | 通知标题 |\n| `notify_content` | string,≤2000 字符 | 欢迎使用… | 通知正文(**服务端白名单净化**:仅保留纯文本与 `` 为 http(s)/站内相对/`#` 锚点的链接,其余标签与事件属性剥离,保存与读取双侧生效) |\n| `showAdminAddr` | int(0/1) | 0 | 是否展示后台入口 |\n| `robotsText` | string | `User-agent: *\\nDisallow: /` | robots.txt 内容(由公开端点 `GET /robots.txt` 输出) |\n\n### 保存策略(需求 ④,上传页动态读取并在范围内选择)\n\n| 键 | 类型/边界 | 默认 | 说明 |\n|---|---|---|---|\n| `max_save_seconds` | int64,0~31536000 | 0 | 最长保存秒数上限(0=仅默认 7 天兜底;>0 时按时间过期超限 403「限制最长时间为 X,可换用其他方式」)。**v3**:管理界面以「小时/天」下拉单位编辑(≥1 天自动显示天),提交时前端换算为秒——canonical 单位保持秒,接口语义不变 |\n| `max_save_count` | int,0~100000 | 0 | **v2 新增**:单次分享最大可取(保存)次数上限(0=不限制;`expire_style=count` 且 `expire_value` 超上限时 403「限制次数最多为 N 次」) |\n| `expireStyle` | []string | `["day","hour","minute","forever","count"]` | 允许的过期方式白名单(上传时不在白名单 400「过期时间类型错误」) |\n\n### 存储策略(需求 ④⑩)\n\n| 键 | 类型/边界 | 默认 | 说明 |\n|---|---|---|---|\n| `uploadSize` | int64,1024~10GiB | 10485760(10MB) | 单文件大小上限(字节),参考实现语义;`max_file_size=0` 时作为生效上限 |\n| `max_file_size` | int64,0~10GiB | 0 | **v2 新增**:存储策略-单文件上限(字节),0=回落 `uploadSize`;超出 403(文案 humanSize 自适应 B/KB/MB/GB)。**v3**:管理界面以「MB/GB」下拉单位编辑(≥1 GiB 自动显示 GB),提交时前端换算为字节 |\n| `allowed_file_types` | []string | `["*"]` | 允许类型白名单(扩展名/MIME 通配,`*` 不限制;非白名单 403「不允许上传该类型文件」) |\n| `storageLimit` | int64,≥0 | 0 | 站点总容量(字节),0=不限制(超限 507) |\n| `openUpload` | int(0/1) | 1 | 游客上传开关(0 时上传接口要求管理员令牌 403) |\n| `enableChunk` | int(0/1) | 0 | 启用分片上传 |\n\n### 上传频率限制(需求 ④,既有键对齐参考 ip_limit["upload"])\n\n| 键 | 类型/边界 | 默认 | 说明 |\n|---|---|---|---|\n| `uploadCount` / `uploadMinute` | int(1~10000 / 1~1440) | 10 / 1 | 窗口内允许上传次数 / 窗口分钟(上传成功才计数,超限 423;管理端修改后运行时同步限流规则,立即生效) |\n| `errorCount` / `errorMinute` | int | 10 / 1 | 取件错误(失败计数)+ metadata 每次计数 |\n| `loginCount` / `loginMinute` | int | 5 / 15 | 登录失败计数 |\n\n### 安全与会话\n\n| 键 | 默认 | 说明 |\n|---|---|---|\n| `admin_token` | 空(未初始化) | 管理员密码哈希(`sha256$salt$hash`);GET 配置时屏蔽为空串 |\n| `jwt_secret` | 空 | JWT 签名密钥(初始化/改密时自动生成轮换;不下发;`settings.SensitiveKeys` 双模式下一致屏蔽) |\n| `adminSessionExpire` | 604800(7 天) | 管理员会话秒数(须 1~365 整天) |\n\n### 存储引擎(v3 运行时可配 + 热切换)\n\n**`storage_engine`**(v3 新增键):string,`local|s3|webdav`,默认空=回落启动值 `FCB_STORAGE_ENGINE`。\n运行时切换走 **`POST /admin/storage/switch`**(JWT 保护):构建新引擎 → 健康检查通过才生效;\n失败返回 503「存储引擎切换失败,已保持原引擎: …」且不改 KV。成功后持久化 `storage_engine`,重启沿用。\n`GET /api/v1/config` 公开下发 `storage_engine` 当前名(仅名称,任何引擎参数/凭据不下发)。\n\n引擎参数键(管理端可改;保存后对应引擎实例缓存失效,下次切换/构建生效):\n\n| 键 | 默认 |\n|---|---|\n| `file_storage` | `local` |\n| `storage_path` | 空 |\n| `local_storage_path` | 空(容器内由 `FCB_LOCAL_STORAGE_PATH=/app/data` 注入) |\n| `s3_access_key_id` / `s3_secret_access_key` / `aws_session_token` | 空 |\n| `s3_bucket_name` / `s3_endpoint_url` / `s3_hostname` | 空 |\n| `s3_region_name` | `auto` |\n| `s3_signature_version` | `s3v4` |\n| `s3_addressing_style` | `auto` |\n| `s3_proxy` | 0 |\n| `webdav_url` / `webdav_username` / `webdav_password` | 空 |\n| `webdav_root_path` | `filebox_storage` |\n| `webdav_proxy` | 0 |\n\n> 敏感键 `webdav_password` / `s3_secret_access_key` / `aws_session_token`(v3 加入 `settings.SensitiveKeys`):\n> 管理端 GET 返回掩码 `******`;PATCH 时空串或 `******` 表示不修改。直接写库 settings KV 后重启同样生效。\n\n## 策略动态生效机制(v2 需求 ④⑩)\n\n上传页通过 `GET /api/v1/config` 的 `config` 字段读取**当前策略快照**并在范围内渲染选项;\n上传链路(`/share/file`、`/chunk/*`、`/presign/*`)**每次请求实时读取** settings KV 同一组值校验:\n\n- 管理端改策略(`PATCH /admin/config/update`)→ 公开 config 即时反映 → 后续上传立即按新策略执行(含 403/400 拒绝与恢复放行)。\n- 生效上限:`max_file_size > 0` 时为 `max_file_size`,否则回落 `uploadSize`。\n- 校验点覆盖:单文件(`/share/file`)、分片 init 按分片数上限、分片上传累计、分片 complete 累计、预签名 init 声明大小,五处口径一致(`api.UploadPolicy.CheckSize`)。\n\n## 公共配置接口\n\n前端启动时经 `GET /api/v1/config` 获取站点公开配置(无需认证;v2 扩展需求 ①②③④⑩):\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": {\n "config": {\n "name": "文件快传",\n "description": "开箱即用的文件快传系统",\n "explain": "请勿上传或分享违法内容…",\n "logo_url": "",\n "favicon_url": "",\n "background_url": "",\n "footer_text": "自定义页脚内容",\n "footer_beian": "京ICP备2024xxxxxx号-1",\n "notify_enabled": 1,\n "notify_title": "系统通知",\n "notify_content": "欢迎使用文件快传…",\n "uploadSize": 10485760,\n "max_file_size": 10485760,\n "maxFileSize": 10485760,\n "allowedFileTypes": ["*"],\n "expireStyle": ["day", "hour", "minute", "forever", "count"],\n "max_save_seconds": 0,\n "maxSaveSeconds": 0,\n "max_save_count": 0,\n "maxSaveCount": 0,\n "uploadCount": 10,\n "uploadMinute": 1,\n "enableChunk": false,\n "openUpload": true\n },\n "meta": {\n "version": "2.5.6",\n "features": { "chunkUpload": false, "guestUpload": true }\n }\n }\n}\n```\n\n> 策略字段 snake_case 与 camelCase 双份下发(前端宽松解析);响应为白名单显式构造,\n> 任何敏感键(`admin_token`/`jwt_secret`)均不会出现。\n\n## 健康检查\n\n```bash\ncurl -s http://localhost:8466/api/v1/health\n```\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": {\n "status": "ok",\n "version": "2.5.6",\n "storage": "local",\n "time": "2025-06-01T12:00:00+08:00"\n }\n}\n```\n',x='# 错误码\n\n## 响应结构\n\n```json\n{ "code": 404, "msg": "文件不存在" }\n```\n\n- `code` 与 HTTP 状态码一致;失败时无 `data` 字段。\n- `msg` 为中文可读信息,可直接展示给用户。\n\n## 业务状态码\n\n| 状态码 | 语义 | 典型场景 |\n|---|---|---|\n| 200 | 成功 | 全部正常响应 |\n| 400 | 参数/格式错误 | 缺字段、过期策略非法、时间格式错误、分片哈希不匹配、code 冲突、`chunk_size` 超 32MiB 上限、presign 实际大小与声明不符、请求体超过大小上限 |\n| 401 | 未认证 | 管理端令牌缺失/无效;登录密码错误 |\n| 403 | 拒绝 | 类型白名单拒绝、magic bytes 防伪、游客上传未开启、**分片上传未启用**(enableChunk=0)、presign 直传对象超限(服务端删除对象并释放预留)、下载 `key` 鉴权失败、超过大小/时长限制 |\n| 404 | 不存在/已过期 | 取件码不存在、文件已过期、上传会话不存在、`/api/*` 未命中路由 |\n| 409 | 冲突 | 上传容量预留信息不一致 |\n| 416 | Range 越界 | `Range: bytes=…` 超出文件大小 |\n| 423 | 限流 | upload/error/login/metadata 任一规则超限 |\n| 428 | 未初始化 | 系统未初始化时访问除 `/setup`、`/api/v1/health` 外的接口 |\n| 500 | 服务器错误 | 数据库/内部异常 |\n| 501 | 引擎不支持 | 引擎不支持预签名等操作(local/webdav 的 `PresignGetURL/PutURL`) |\n| 503 | 存储不可用 | 存储引擎连接失败/健康检查不通过时的操作 |\n| 507 | 容量超限 | 达到 `storageLimit` 上限(含上传预留判定) |\n\n## 存储哨兵错误映射\n\n存储层哨兵错误统一映射(支持错误包装链判定):\n\n| 哨兵错误 | HTTP | 响应 msg |\n|---|---|---|\n| `ErrNotFound` | 404 | 文件不存在 |\n| `ErrInvalidPath` | 400 | 非法文件路径 |\n| `ErrUnavailable` | 503 | 存储服务不可用,请稍后再试 |\n| `ErrNotSupported` | 501 | 当前存储引擎不支持该操作 |\n| `ErrRangeNotSatisfiable` | 416 | 请求范围超出文件大小 |\n| `ErrHashMismatch` | 400 | 分片哈希校验失败,请重新上传 |\n\n未识别的存储错误归入 500(`存储操作失败: …`)。\n\n## 错误结果的审计归类\n\n错误响应同时写入审计日志(需求 ③):\n\n- `denied`:401 / 403 / 423 / 429 / 428(拒绝类)。\n- `failed`:其余 4xx / 5xx 及业务显式报错。\n\n## 常见排障\n\n| 现象 | 原因与处理 |\n|---|---|\n| 全部接口 428 | 未初始化:访问 `GET /setup` 或 `POST /setup` 完成向导 |\n| 上传 403「本站未开启游客上传」 | `openUpload=0`,携带管理员 Bearer 令牌或后台开启 |\n| 上传 423 | 触发 upload 限流,等待窗口或调大 `uploadCount/uploadMinute` |\n| 取件 404「文件已过期」 | 分享过期/次数耗尽;管理员可 `PATCH /admin/file/update` 调整 |\n| 下载 403「下载鉴权失败」 | `key` 窗口令牌过期/伪造:重新 `POST /share/select` 获取新地址 |\n| 预签名 init 返回 proxy | local/webdav 引擎不支持直链,按 proxy 流程走服务端代理上传 |\n| 503 存储服务不可用 | 检查引擎配置与远端服务(S3/WebDAV)连通性;`GET /api/v1/health` 的 `storage` 字段确认引擎 |\n',b='# Logo 自定义\n\n## 默认 Logo(内置,v2 需求 ⑤)\n\n| 项 | 默认值 | 用途 |\n|---|---|---|\n| 页面导航 Logo | 前端打包本地资源 `/assets/logo-*.svg`(源:`web/src/assets/brand/logo.svg`) | 导航栏 ``;`config.logo_url` 为空时回落使用 |\n| favicon / 备用 Logo | 前端打包本地资源 `/assets/favicon-*.png`(源:`web/src/assets/brand/favicon.png`) | `index.html` `` + 动态 favicon 回落 |\n\nv2 起默认不再引用远程 URL:`GET /api/v1/config` 中 `logo_url`/`favicon_url` 默认下发空串,\n前端 `displayLogoUrl`/`displayFaviconUrl` 判空后回落到打包的本地资源。\n管理端仍可设置任意 URL 全站替换(三步如下)。\n\n## 管理端自定义(三步)\n\n1. **登录后台**:`POST /admin/login` 获取 Bearer 令牌。\n2. **保存配置**:`PATCH /admin/config/update` 更新 `logo_url`(与可选 `favicon_url`),值为图片 URL 或经管理端上传后得到的地址。\n3. **全站生效**:保存即写入 settings KV 并热更新,前端读取公共配置立即换新 Logo,无需重启。\n\ncurl 示例:\n\n```bash\ncurl -s -X PATCH http://localhost:8466/admin/config/update \\\n -H "Authorization: Bearer $TOKEN" -H \'Content-Type: application/json\' \\\n -d \'{"logo_url":"https://cdn.example.com/logo.svg","favicon_url":"https://cdn.example.com/favicon.png"}\'\n```\n\n```json\n{ "code": 200, "msg": "ok", "data": { "ok": true } }\n```\n\n> 也可以在管理界面「系统设置」页操作(上传图片或填写 URL),效果相同。\n\n## 校验生效\n\n```bash\ncurl -s http://localhost:8466/api/v1/config\n```\n\n```json\n{\n "code": 200, "msg": "ok",\n "data": { "config": { "logo_url": "https://cdn.example.com/logo.svg", "favicon_url": "https://cdn.example.com/favicon.png" } }\n}\n```\n\n## 恢复默认\n\n把 `logo_url` / `favicon_url` 置回默认值(空串,前端回落本地打包资源)即可:\n\n```bash\ncurl -s -X PATCH http://localhost:8466/admin/config/update \\\n -H "Authorization: Bearer $TOKEN" -H \'Content-Type: application/json\' \\\n -d \'{"logo_url":"","favicon_url":""}\'\n```\n\n## 相关行为\n\n- 前端运行时优先读取配置值;空值回退前端打包的本地资源(`web/src/assets/brand/logo.svg` + `favicon.png`,经 `displayLogoUrl`/`displayFaviconUrl` 判空回落)。\n- `site_name` 同样支持运行时自定义(`PATCH /admin/config/update` 的 `site_name` 键)。\n- Logo/favicon 仅涉及展示层,修改不影响会话与令牌(不轮换 `jwt_secret`)。\n',T=`openapi: 3.0.3 +info: + title: 文件快传 Go 版 API + version: 2.5.6 + description: | + 文件快传 Go 重写版(Gin + GORM)完整 API 规范,与 \`server/internal/api/\` 实现逐一对齐。 + + 约定: + - 业务路由挂根路径(/share /chunk /presign /admin),公共接口保留 /api/v1 前缀。 + - 统一响应 \`{"code":200,"msg":"ok","data":...}\`,HTTP 状态码与 code 一致,失败时 data 缺省。 + - 管理接口(/admin/login 除外)需 \`Authorization: Bearer \`。 + - 未初始化时除 /setup 与 /api/v1/health 外一律 428;限流超限 423。 + contact: + name: 文件快传 Rewrite Team +servers: + - url: / + description: 同源部署(默认 :8466) +tags: + - name: 公共 + - name: 初始化 + - name: 分享 + - name: 分片上传 + - name: 预签名直传 + - name: 管理后台 + - name: 审计日志 + +paths: + /api/v1/health: + get: + tags: [公共] + summary: 健康检查 + operationId: health + responses: + '200': + description: 服务状态 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + status: { type: string, example: ok } + version: { type: string, example: 2.5.6 } + storage: { type: string, example: local } + time: { type: string, example: '2025-06-01T12:00:00+08:00' } + /robots.txt: + get: + tags: [公共] + summary: robots.txt + operationId: robotsText + description: | + 输出管理端可配置的 \`robotsText\` 内容(text/plain)。 + 内容可在管理后台「站点配置」中自定义,默认 \`User-agent: *\\nDisallow: /\`。 + responses: + '200': + description: robots.txt 内容 + content: + text/plain: + schema: { type: string, example: 'User-agent: *\\nDisallow: /' } + + /api/v1/config: + get: + tags: [公共] + summary: 公共站点配置 + operationId: publicConfig + responses: + '200': + description: 站点公开配置与功能开关 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + config: + type: object + description: 策略字段为上传页动态读取的当前快照(管理端改后即时反映);snake_case 与 camelCase 双份 + properties: + name: { type: string } + description: { type: string } + explain: { type: string } + logo_url: { type: string } + favicon_url: { type: string } + background_url: { type: string, description: '需求 ① 背景图 URL/上传地址,空=主题默认' } + footer_text: { type: string, description: '需求 ② 页脚自定义内容' } + footer_beian: { type: string, description: '需求 ② 备案号' } + storage_engine: { type: string, description: 当前存储引擎名(仅名称,凭据不下发) } + site_domain: { type: string, description: 'v3.1 站点对外域名(空=用当前访问地址)' } + notify_enabled: { type: integer, enum: [0, 1], description: '需求 ③ 通知开关(1=右上角悬浮窗)' } + notify_title: { type: string } + notify_content: { type: string } + uploadSize: { type: integer, format: int64, description: '回落上限(max_file_size=0 时生效)' } + max_file_size: { type: integer, format: int64, description: '需求 ⑩ 存储策略-单文件上限字节(0=回落 uploadSize)' } + maxFileSize: { type: integer, format: int64, description: 同 max_file_size } + allowedFileTypes: { type: array, items: { type: string } } + expireStyle: { type: array, items: { type: string } } + max_save_seconds: { type: integer, format: int64, description: '需求 ④ 最长保存秒数上限(0=默认 7 天兜底)' } + maxSaveSeconds: { type: integer, format: int64, description: 同 max_save_seconds } + max_save_count: { type: integer, description: '需求 ④ 单次分享最大可取次数上限(0=不限制)' } + maxSaveCount: { type: integer, description: 同 max_save_count } + uploadCount: { type: integer, description: 上传频率限制-窗口内次数 } + uploadMinute: { type: integer, description: 上传频率限制-窗口分钟 } + enableChunk: { type: boolean } + openUpload: { type: boolean } + meta: + type: object + properties: + version: { type: string } + features: + type: object + properties: + chunkUpload: { type: boolean } + guestUpload: { type: boolean } + + /setup: + get: + tags: [初始化] + summary: 初始化向导页面 + operationId: setupPage + description: 未初始化返回 HTML 向导;已初始化 303 重定向到 /。 + responses: + '200': + description: HTML 向导页 + content: + text/html: + schema: { type: string } + '303': + description: 已初始化,重定向到 / + post: + tags: [初始化] + summary: 提交初始化 + operationId: setupSubmit + description: | + 表单或 JSON。管理员密码 ≥8 位;\`expireStyle\` 支持多值(复选框或逗号分隔)。 + 成功写入站点配置、密码哈希并生成 jwt_secret。表单提交返回成功 HTML,JSON 提交返回 JSON。 + requestBody: + content: + application/json: + schema: + type: object + properties: + admin_password: { type: string, minLength: 8 } + confirm_password: { type: string } + site_name: { type: string } + upload_size_value: { type: integer, default: 10 } + upload_size_unit: { type: string, enum: [KB, MB, GB], default: MB } + save_time_value: { type: integer, default: 0 } + save_time_unit: { type: string, enum: [second, minute, hour, day], default: day } + expireStyle: + oneOf: + - type: string + - type: array + items: { type: string } + code_generate_type: { type: string, enum: [number, secret], default: secret } + errorCount: { type: integer, default: 10 } + errorMinute: { type: integer, default: 1 } + loginCount: { type: integer, default: 5 } + loginMinute: { type: integer, default: 15 } + uploadCount: { type: integer, default: 10 } + uploadMinute: { type: integer, default: 1 } + allowed_file_types: { type: string, default: '*' } + openUpload: { type: boolean, default: true } + enableChunk: { type: boolean, default: false } + application/x-www-form-urlencoded: + schema: + type: object + properties: + admin_password: { type: string } + confirm_password: { type: string } + site_name: { type: string } + responses: + '200': + description: 初始化成功 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + ok: { type: boolean, example: true } + admin: { type: string, example: '/#/admin' } + text/html: + schema: { type: string } + '400': + $ref: '#/components/responses/BadRequest' + + /share/text: + post: + tags: [分享] + summary: 创建文本分享 + operationId: shareText + description: | + 文本 ≤222KB(请求体全局上限 1MiB,Content-Length>441KB 直接 403);上传类接口(成功计数 upload 限流,423 超限),经审计中间件落库。 + 保存策略(需求 ④):expire_style 须在 expireStyle 白名单(400);count 型受 + max_save_count 约束(403「限制次数最多为 N 次」);时间型受 max_save_seconds 约束(403)。 + v3.1:支持 JSON 提交(字段同名);空文本 400「分享内容不能为空」; + 可选 code 自定义提取码(5-8 位字母或数字,占用 400「该提取码已被占用」)。 + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + required: [text] + properties: + text: { type: string, maxLength: 227328 } + expire_value: { type: integer, default: 1 } + expire_style: { type: string, enum: [day, hour, minute, count, forever], default: day } + code: { type: string, minLength: 5, maxLength: 8, pattern: '^[A-Za-z0-9]+$', description: 自定义提取码(5-8 位字母数字,可省略=随机) } + responses: + '200': + description: 取件码 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + code: { type: string, example: 8XQ2M } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /share/file: + post: + tags: [分享] + summary: 上传文件并创建分享 + operationId: shareFile + description: | + multipart 上传;扩展名/MIME 白名单 + magic bytes 防伪;大小受动态策略上限限制 + (max_file_size>0 时为其,否则回落 uploadSize;403「大小超过限制,最大为10.00 MB」, + 管理端改后立即生效);保存策略同 /share/text(403/400);容量超限 507。 + requestBody: + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + code: { type: string, minLength: 5, maxLength: 8, pattern: '^[A-Za-z0-9]+$', description: 自定义提取码(5-8 位字母数字,可省略=随机) } + file: + type: string + format: binary + expire_value: { type: integer, default: 1 } + expire_style: { type: string, enum: [day, hour, minute, count, forever], default: day } + responses: + '200': + description: 取件码与文件名 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + code: { type: string, example: K3P9W } + name: { type: string, example: report.pdf } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '423': { $ref: '#/components/responses/RateLimited' } + '503': { $ref: '#/components/responses/ServiceUnavailable' } + '507': { $ref: '#/components/responses/InsufficientStorage' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /share/metadata: + get: + tags: [分享] + summary: 查询分享元信息(GET) + operationId: shareMetadataGet + description: 不消耗次数;每次访问计入 metadata 限流。 + parameters: + - $ref: '#/components/parameters/CodeQuery' + responses: + '200': + description: 元信息 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ShareMetadata' } + '404': { $ref: '#/components/responses/NotFound' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [分享] + summary: 查询分享元信息(POST) + operationId: shareMetadataPost + requestBody: + content: + application/json: + schema: + type: object + required: [code] + properties: + code: { type: string } + responses: + '200': + description: 元信息(同 GET 版本) + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ShareMetadata' } + '400': { $ref: '#/components/responses/BadRequest' } + '404': { $ref: '#/components/responses/NotFound' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /share/select: + get: + tags: [分享] + summary: 取件(消耗次数) + operationId: shareSelectGet + description: | + 每调用一次消耗 1 次取件。文本分享返回 text/plain 正文;文件分享返回文件流 + (200 全量 / 206 区间,支持 Range,Accept-Ranges: bytes)。失败计入 error 限流(423)。 + parameters: + - $ref: '#/components/parameters/CodeQuery' + - name: Range + in: header + required: false + schema: { type: string, example: 'bytes=0-1023' } + description: 文件分享的区间请求(单区间) + responses: + '200': + description: 文本正文或文件流 + content: + text/plain: + schema: { type: string } + application/octet-stream: + schema: { type: string, format: binary } + '206': + description: Range 区间文件流(Content-Range) + content: + application/octet-stream: + schema: { type: string, format: binary } + '404': { $ref: '#/components/responses/NotFound' } + '416': { $ref: '#/components/responses/RangeNotSatisfiable' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [分享] + summary: 取件详情(JSON) + operationId: shareSelectPost + description: | + 返回元信息 + 内容/下载地址。文本分享含 text/content;文件分享含 download_url + (代理地址或 S3 预签名直链)。次数型分享返回代理地址且本次不消耗(消耗在代理下载时)。 + requestBody: + content: + application/json: + schema: + type: object + required: [code] + properties: + code: { type: string } + responses: + '200': + description: 分享详情 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ShareDetail' } + '404': { $ref: '#/components/responses/NotFound' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /share/download: + get: + tags: [分享] + summary: 代理下载(消耗次数) + operationId: shareDownload + description: key 为窗口令牌(双窗口校验,鉴权失败 403 且计入 error 限流);文件流 200/206 支持 Range。 + parameters: + - name: key + in: query + required: true + schema: { type: string } + - $ref: '#/components/parameters/CodeQuery' + responses: + '200': + description: 文本 JSON 或文件流 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + application/octet-stream: + schema: { type: string, format: binary } + '206': + description: Range 区间文件流 + content: + application/octet-stream: + schema: { type: string, format: binary } + '403': { $ref: '#/components/responses/Forbidden' } + '404': { $ref: '#/components/responses/NotFound' } + '416': { $ref: '#/components/responses/RangeNotSatisfiable' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /chunk/upload/init: + post: + tags: [分片上传] + summary: 初始化分片上传会话 + operationId: chunkInit + description: | + 相同 file_hash+file_size+file_name 的未完成会话自动续传(uploaded_chunks 非空即续传)。 + chunk_size 缺省 5MB、硬上限 32MiB(超出 400「chunk_size 过大」);需 enableChunk 开启(关闭时全部 /chunk 端点 403)。总大小按分片数上限受动态策略约束 + (max_file_size>0 时为其,否则 uploadSize;403);类型白名单 403。 + requestBody: + content: + application/json: + schema: + type: object + required: [file_name, file_size] + properties: + file_name: { type: string } + file_size: { type: integer, format: int64, minimum: 1 } + chunk_size: { type: integer, format: int64, default: 5242880, maximum: 33554432, description: 每片大小(字节),≤32MiB } + file_hash: { type: string, description: 整文件 SHA256,断点续传匹配键 } + responses: + '200': + description: 会话信息 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ChunkInitData' } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '423': { $ref: '#/components/responses/RateLimited' } + '507': { $ref: '#/components/responses/InsufficientStorage' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /chunk/upload/{uploadID}/{chunkIndex}: + post: + tags: [分片上传] + summary: 上传单个分片 + operationId: chunkUpload + description: multipart 字段 chunk;单分片 ≤ min(chunk_size, 32MiB);重复上传幂等(skipped:true);首分片做 magic bytes 防伪。 + parameters: + - $ref: '#/components/parameters/UploadID' + - name: chunkIndex + in: path + required: true + schema: { type: integer, minimum: 0 } + requestBody: + content: + multipart/form-data: + schema: + type: object + required: [chunk] + properties: + chunk: { type: string, format: binary } + responses: + '200': + description: 分片结果 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ChunkUploadData' } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /chunk/upload/status/{uploadID}: + get: + tags: [分片上传] + summary: 查询上传进度 + operationId: chunkStatus + parameters: + - $ref: '#/components/parameters/UploadID' + responses: + '200': + description: 进度 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ChunkStatusData' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /chunk/upload/complete/{uploadID}: + post: + tags: [分片上传] + summary: 合并分片并创建分享 + operationId: chunkComplete + description: 分片到齐后调用;按索引有序合并 + SHA256 校验(不完整 400 / 哈希不符 400);累计大小受动态策略上限(403,超限清理会话);保存策略同 /share/text(403/400)。 + parameters: + - $ref: '#/components/parameters/UploadID' + requestBody: + content: + application/json: + schema: + type: object + properties: + expire_value: { type: integer, default: 1 } + expire_style: { type: string, enum: [day, hour, minute, count, forever], default: day } + responses: + '200': + description: 取件码与文件名 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ShareFileData' } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '404': { $ref: '#/components/responses/NotFound' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /chunk/upload/{uploadID}: + delete: + tags: [分片上传] + summary: 取消上传 + operationId: chunkCancel + parameters: + - $ref: '#/components/parameters/UploadID' + responses: + '200': + description: 已取消 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + message: { type: string, example: 上传已取消 } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /presign/upload/init: + post: + tags: [预签名直传] + summary: 初始化预签名上传 + operationId: presignInit + description: | + S3 引擎返回 mode=direct(upload_url 为预签名 PUT URL);local/webdav 返回 mode=proxy + (upload_url=/presign/upload/proxy/{uploadID})。会话 900 秒有效。 + file_size 受动态策略上限(max_file_size>0 时为其,否则 uploadSize;403);保存策略同 /share/text。 + requestBody: + content: + application/json: + schema: + type: object + required: [file_name, file_size] + properties: + file_name: { type: string } + file_size: { type: integer, format: int64, minimum: 1 } + expire_value: { type: integer, default: 1 } + expire_style: { type: string, enum: [day, hour, minute, count, forever], default: day } + responses: + '200': + description: 上传会话 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/PresignInitData' } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '423': { $ref: '#/components/responses/RateLimited' } + '507': { $ref: '#/components/responses/InsufficientStorage' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /presign/upload/proxy/{uploadID}: + put: + tags: [预签名直传] + summary: 代理上传(proxy 模式) + operationId: presignProxy + description: multipart 字段 file;实际大小须与声明一致(±1KB);成功即建分享并删除会话。 + parameters: + - $ref: '#/components/parameters/UploadID' + requestBody: + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: { type: string, format: binary } + responses: + '200': + description: 取件码与文件名 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ShareFileData' } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '404': { $ref: '#/components/responses/NotFound' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /presign/upload/confirm/{uploadID}: + post: + tags: [预签名直传] + summary: 直传确认(direct 模式) + operationId: presignConfirm + description: | + 客户端完成 S3 PUT 后调用。服务端 HeadObject 核对实际对象: + 超过策略上限 → 删除对象、释放容量预留并 403;实际大小与声明差 >1KB → 400; + 前 64 字节做 magic bytes 校验(失败删除对象)。全部通过后创建分享记录。 + parameters: + - $ref: '#/components/parameters/UploadID' + responses: + '200': + description: 取件码与文件名 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/ShareFileData' } + '400': { $ref: '#/components/responses/BadRequest' } + '403': { $ref: '#/components/responses/Forbidden' } + '404': { $ref: '#/components/responses/NotFound' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /presign/upload/status/{uploadID}: + get: + tags: [预签名直传] + summary: 查询预签名会话 + operationId: presignStatus + parameters: + - $ref: '#/components/parameters/UploadID' + responses: + '200': + description: 会话状态 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + upload_id: { type: string } + file_name: { type: string } + file_size: { type: integer, format: int64 } + mode: { type: string, enum: [direct, proxy] } + created_at: { type: string } + expires_at: { type: string } + is_expired: { type: boolean } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /presign/upload/{uploadID}: + delete: + tags: [预签名直传] + summary: 取消预签名会话 + operationId: presignCancel + description: direct 模式会尽力清理已直传的对象。 + parameters: + - $ref: '#/components/parameters/UploadID' + responses: + '200': + description: 已取消 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + message: { type: string, example: 上传会话已取消 } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/login: + post: + tags: [管理后台] + summary: 管理员登录 + operationId: adminLogin + description: 失败 401 并计入 login 限流(423 超限)。 + requestBody: + content: + application/json: + schema: + type: object + required: [password] + properties: + password: { type: string } + responses: + '200': + description: JWT 会话 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/LoginData' } + '401': { $ref: '#/components/responses/Unauthorized' } + '423': { $ref: '#/components/responses/RateLimited' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/verify: + get: + tags: [管理后台] + summary: 校验管理员会话 + operationId: adminVerify + security: [{ bearerAuth: [] }] + responses: + '200': + description: 会话信息 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/LoginData' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/logout: + post: + tags: [管理后台] + summary: 管理员登出 + operationId: adminLogout + description: 无状态 JWT,客户端丢弃令牌即可。 + security: [{ bearerAuth: [] }] + responses: + '200': + description: 确认 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/OkData' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/dashboard: + get: + tags: [管理后台] + summary: 管理端仪表盘统计 + operationId: adminDashboard + security: [{ bearerAuth: [] }] + responses: + '200': + description: 计数/存储用量/最近文件等 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + totalFiles: { type: integer } + storageUsed: { type: string, example: '123456789' } + sysUptime: { type: integer, nullable: true, description: 启动时刻 Unix 毫秒 } + yesterdayCount: { type: integer } + yesterdaySize: { type: string } + todayCount: { type: integer } + todaySize: { type: string } + activeCount: { type: integer } + expiredCount: { type: integer } + textCount: { type: integer } + fileCount: { type: integer } + chunkedCount: { type: integer } + usedCount: { type: integer } + storageBackend: { type: string, enum: [local, s3, webdav] } + uploadSizeLimit: { type: integer, format: int64 } + openUpload: { type: integer } + enableChunk: { type: integer } + maxSaveSeconds: { type: integer, format: int64 } + topSuffixes: + type: array + items: + type: object + properties: + suffix: { type: string } + count: { type: integer } + recentFiles: + type: array + items: { $ref: '#/components/schemas/AdminFileItem' } + recentActivities: { type: array, items: { type: object } } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/list: + get: + tags: [管理后台] + summary: 分页查询分享列表 + operationId: adminFileList + security: [{ bearerAuth: [] }] + parameters: + - { name: page, in: query, schema: { type: integer, default: 1, minimum: 1 } } + - { name: size, in: query, schema: { type: integer, default: 10, minimum: 1, maximum: 100 } } + - { name: keyword, in: query, schema: { type: string } } + - { name: status, in: query, schema: { type: string, enum: [active, expired] } } + - { name: type, in: query, schema: { type: string, enum: [text, file, chunked] } } + - { name: sortBy, in: query, schema: { type: string, enum: [created_at, expired_at, name, size, used_count, code] } } + - { name: sortOrder, in: query, schema: { type: string, enum: [asc, desc], default: desc } } + responses: + '200': + description: 列表与统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + page: { type: integer } + size: { type: integer } + total: { type: integer } + summary: + type: object + properties: + totalFiles: { type: integer } + activeCount: { type: integer } + expiredCount: { type: integer } + textCount: { type: integer } + fileCount: { type: integer } + chunkedCount: { type: integer } + storageUsed: { type: integer, format: int64 } + usedCount: { type: integer, format: int64 } + data: + type: array + items: { $ref: '#/components/schemas/AdminFileItem' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/detail: + get: + tags: [管理后台] + summary: 文件详情(GET) + operationId: adminFileDetailGet + security: [{ bearerAuth: [] }] + parameters: + - $ref: '#/components/parameters/FileIDQuery' + responses: + '200': + description: 文件条目(文本分享额外含 content 全文) + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/AdminFileItem' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 文件详情(POST) + operationId: adminFileDetailPost + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [id] + properties: + id: { type: integer, format: int64 } + responses: + '200': + description: 同 GET 版本 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/AdminFileItem' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/update: + patch: + tags: [管理后台] + summary: 更新分享字段(主名) + operationId: adminFileUpdate + description: code 冲突 400;POST /admin/file/update 为兼容别名。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: { $ref: '#/components/schemas/FileUpdateInput' } + responses: + '200': + description: 更新成功(data 为字符串“更新成功”) + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 更新分享字段(POST 别名) + operationId: adminFileUpdateAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: { $ref: '#/components/schemas/FileUpdateInput' } + responses: + '200': + description: 更新成功 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/delete: + delete: + tags: [管理后台] + summary: 删除单个分享(主名) + operationId: adminFileDelete + description: 连带删除存储文件;POST /admin/file/delete 为兼容别名。 + security: [{ bearerAuth: [] }] + parameters: + - { name: id, in: query, schema: { type: integer, format: int64 }, description: 亦可放请求体 } + requestBody: + content: + application/json: + schema: + type: object + properties: + id: { type: integer, format: int64 } + responses: + '200': + description: 删除成功(data 为 null) + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 删除单个分享(POST 别名) + operationId: adminFileDeleteAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + properties: + id: { type: integer, format: int64 } + responses: + '200': + description: 删除成功 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/batch-delete: + post: + tags: [管理后台] + summary: 批量删除 + operationId: adminFileBatchDelete + description: DELETE 方法亦可。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [ids] + properties: + ids: { type: array, items: { type: integer, format: int64 } } + responses: + '200': + description: 批量统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/BatchStats' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + delete: + tags: [管理后台] + summary: 批量删除(DELETE 别名) + operationId: adminFileBatchDeleteAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [ids] + properties: + ids: { type: array, items: { type: integer, format: int64 } } + responses: + '200': + description: 批量统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/BatchStats' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/batch-update: + patch: + tags: [管理后台] + summary: 批量更新过期策略 + operationId: adminFileBatchUpdate + description: expired_at / expired_count / clearExpiredAt 三选一;POST 亦可。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [ids] + properties: + ids: { type: array, items: { type: integer, format: int64 } } + expired_at: { type: string, description: ISO 8601 } + expired_count: { type: integer } + clearExpiredAt: { type: boolean, description: 清空过期时间并置 expired_count=-1(永久) } + clear_expired_at: { type: boolean, description: 同 clearExpiredAt } + responses: + '200': + description: 批量统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/BatchStats' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 批量更新(POST 别名) + operationId: adminFileBatchUpdateAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [ids] + properties: + ids: { type: array, items: { type: integer, format: int64 } } + expired_at: { type: string } + expired_count: { type: integer } + clearExpiredAt: { type: boolean } + responses: + '200': + description: 批量统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/BatchStats' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/policy-action: + patch: + tags: [管理后台] + summary: 单文件过期策略动作 + operationId: adminFilePolicyAction + description: 动作:extend_24h / extend_7d / make_permanent / reset_download_limit;POST 亦可。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: { $ref: '#/components/schemas/PolicyInput' } + responses: + '200': + description: 执行结果 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/PolicyResult' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 单文件策略动作(POST 别名) + operationId: adminFilePolicyActionAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: { $ref: '#/components/schemas/PolicyInput' } + responses: + '200': + description: 执行结果 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/PolicyResult' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/batch-policy-action: + patch: + tags: [管理后台] + summary: 批量过期策略动作 + operationId: adminFileBatchPolicyAction + description: 响应结构同批量更新;POST 亦可。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + allOf: + - type: object + required: [ids] + properties: + ids: { type: array, items: { type: integer, format: int64 } } + - $ref: '#/components/schemas/PolicyInput' + responses: + '200': + description: 批量统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/BatchStats' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 批量策略动作(POST 别名) + operationId: adminFileBatchPolicyActionAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + allOf: + - type: object + required: [ids] + properties: + ids: { type: array, items: { type: integer, format: int64 } } + - $ref: '#/components/schemas/PolicyInput' + responses: + '200': + description: 批量统计 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/BatchStats' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/download: + get: + tags: [管理后台] + summary: 管理员下载原文件 + operationId: adminFileDownload + description: 不消耗取件次数;文件返回二进制流(支持 Range),文本分享返回 JSON。 + security: [{ bearerAuth: [] }] + parameters: + - $ref: '#/components/parameters/FileIDQuery' + responses: + '200': + description: 文件流或文本 JSON + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + application/octet-stream: + schema: { type: string, format: binary } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/file/preview: + get: + tags: [管理后台] + summary: 文本预览 + operationId: adminFilePreview + description: 仅文本分享可用(文件分享 400);maxChars 默认 4000(1~20000)。 + security: [{ bearerAuth: [] }] + parameters: + - $ref: '#/components/parameters/FileIDQuery' + - { name: maxChars, in: query, schema: { type: integer, default: 4000, minimum: 1, maximum: 20000 } } + responses: + '200': + description: 预览内容 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + id: { type: integer, format: int64 } + code: { type: string } + name: { type: string } + type: { type: string, example: text } + content: { type: string } + length: { type: integer } + previewLength: { type: integer } + truncated: { type: boolean } + maxChars: { type: integer } + createdAt: { type: string } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '404': { $ref: '#/components/responses/NotFound' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/config/get: + get: + tags: [管理后台] + summary: 读取运行时配置 + operationId: adminConfigGet + description: admin_token 屏蔽(恒空串)、jwt_secret 不下发;_engine_hint 提示引擎配置需重启。 + security: [{ bearerAuth: [] }] + responses: + '200': + description: 配置 KV + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + additionalProperties: true + properties: + site_name: { type: string } + logo_url: { type: string } + favicon_url: { type: string } + background_url: { type: string } + footer_text: { type: string } + footer_beian: { type: string } + notify_enabled: { type: integer, enum: [0, 1] } + openUpload: { type: integer } + uploadSize: { type: integer, format: int64 } + max_file_size: { type: integer, format: int64, description: '0=回落 uploadSize' } + max_save_count: { type: integer, description: '0=不限制' } + max_save_seconds: { type: integer, format: int64 } + allowed_file_types: { type: array, items: { type: string } } + expireStyle: { type: array, items: { type: string } } + storageLimit: { type: integer, format: int64 } + uploadCount: { type: integer } + uploadMinute: { type: integer } + admin_token: { type: string, example: '' } + _engine_hint: + type: object + properties: + storage_backend: { type: string } + note: { type: string } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/config/update: + patch: + tags: [管理后台] + summary: 部分更新配置(主名) + operationId: adminConfigUpdate + description: | + JSON 对象部分更新,未知键忽略;数值键自动转型;v2 起新键按 settings.KVSchema 校验 + (整型边界/字符串长度/列表非空,越界 400 中文错误);background_url 协议白名单 + (http(s)/data:image/站内相对路径);admin_token 明文自动哈希并轮换 jwt_secret + (全部令牌立即失效);adminSessionExpire 须 1~365 整天(默认 604800 即 7 天);notify_content 服务端白名单净化(仅保留文本与 );策略与限流修改立即生效,引擎配置需重启。 + POST /admin/config/update 为兼容别名。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + additionalProperties: true + properties: + site_name: { type: string } + logo_url: { type: string } + favicon_url: { type: string } + background_url: + type: string + maxLength: 2048 + description: '需求 ① 背景图;仅 http(s)、data:image/* 与站内相对路径(/开头),空串清除;非法协议 400' + footer_text: { type: string, maxLength: 2000, description: '需求 ② 页脚自定义内容' } + footer_beian: { type: string, maxLength: 128, description: '需求 ② 备案号' } + notify_enabled: { type: integer, enum: [0, 1], description: '需求 ③ 通知开关' } + notify_title: { type: string, maxLength: 128 } + notify_content: { type: string, maxLength: 2000 } + openUpload: { type: integer, enum: [0, 1] } + uploadSize: { type: integer, format: int64, minimum: 1024, maximum: 10737418240 } + max_file_size: { type: integer, format: int64, minimum: 0, maximum: 10737418240, description: '需求 ⑩ 存储策略-单文件上限字节;0=回落 uploadSize' } + max_save_count: { type: integer, minimum: 0, maximum: 100000, description: '需求 ④ 单次分享最大可取次数上限;0=不限制' } + max_save_seconds: { type: integer, format: int64, minimum: 0, maximum: 31536000, description: '需求 ④ 最长保存秒数上限;0=默认 7 天兜底' } + allowed_file_types: + oneOf: + - type: array + items: { type: string } + minItems: 1 + - type: string + description: 逗号分隔亦可;"*" 不限制 + expireStyle: + oneOf: + - type: array + items: { type: string } + minItems: 1 + - type: string + description: 允许的过期方式白名单(day/hour/minute/forever/count 子集) + storageLimit: { type: integer, format: int64, minimum: 0 } + uploadCount: { type: integer, minimum: 1, maximum: 10000 } + uploadMinute: { type: integer, minimum: 1, maximum: 1440 } + errorCount: { type: integer } + errorMinute: { type: integer } + loginCount: { type: integer } + loginMinute: { type: integer } + admin_token: { type: string, description: 明文密码自动哈希并轮换 jwt_secret;空串忽略 } + adminSessionExpire: { type: integer, default: 604800, description: 秒,须 86400 的 1~365 整数倍(默认 7 天) } + responses: + '200': + description: 保存成功 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/OkData' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 部分更新配置(POST 别名) + operationId: adminConfigUpdateAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + additionalProperties: true + responses: + '200': + description: 保存成功 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/OkData' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/storage/switch: + post: + tags: [admin] + summary: 存储引擎热切换(v3) + description: | + 运行时切换存储引擎(local|s3|webdav)。构建新引擎并做健康检查, + 通过才生效;失败返回 503 且保持原引擎。成功后持久化 storage_engine KV, + 重启后沿用。 + operationId: adminStorageSwitch + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [engine] + properties: + engine: + type: string + enum: [local, s3, webdav] + responses: + "200": + description: 切换成功 + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Envelope" + - type: object + properties: + data: + type: object + properties: + ok: { type: boolean } + engine: { type: string } + "400": { $ref: "#/components/responses/BadRequest" } + "401": { $ref: "#/components/responses/Unauthorized" } + "503": { $ref: "#/components/responses/ServiceUnavailable" } + + /admin/settings/password: + patch: + tags: [管理后台] + summary: 修改管理员密码(主名) + operationId: adminChangePassword + description: 新密码 ≥8 位;成功后轮换 jwt_secret,全部旧令牌失效(401)。POST 亦可。 + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [old_password, new_password] + properties: + old_password: { type: string } + new_password: { type: string, minLength: 8 } + responses: + '200': + description: 修改成功 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/OkData' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + post: + tags: [管理后台] + summary: 修改管理员密码(POST 别名) + operationId: adminChangePasswordAlias + deprecated: true + security: [{ bearerAuth: [] }] + requestBody: + content: + application/json: + schema: + type: object + required: [old_password, new_password] + properties: + old_password: { type: string } + new_password: { type: string, minLength: 8 } + responses: + '200': + description: 修改成功 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: { $ref: '#/components/schemas/OkData' } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/audit/list: + get: + tags: [审计日志] + summary: 分页查询审计日志(主名) + operationId: adminAuditList + description: | + 需求 ③ 审计查询。行字段 snake_case 为主(camelCase 兼容双份)。 + action: upload|download;result: success|denied|failed;时间参数 ISO 8601。 + security: [{ bearerAuth: [] }] + parameters: + - { name: page, in: query, schema: { type: integer, default: 1, minimum: 1 } } + - { name: size, in: query, schema: { type: integer, default: 20, minimum: 1, maximum: 200 }, description: 兼容 pageSize } + - { name: action, in: query, schema: { type: string, enum: [upload, download] } } + - { name: result, in: query, schema: { type: string, enum: [success, denied, failed] } } + - { name: ip, in: query, schema: { type: string } } + - { name: start_time, in: query, schema: { type: string, example: '2025-06-01T00:00:00+08:00' } } + - { name: end_time, in: query, schema: { type: string, example: '2025-06-02T00:00:00+08:00' } } + responses: + '200': + description: 审计日志分页 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + data: + type: array + items: { $ref: '#/components/schemas/AuditItem' } + total: { type: integer } + page: { type: integer } + size: { type: integer } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + + /admin/audit/logs: + get: + tags: [审计日志] + summary: 分页查询审计日志(别名) + operationId: adminAuditLogs + deprecated: true + description: 与 /admin/audit/list 完全相同(同一 handler 的兼容别名)。 + security: [{ bearerAuth: [] }] + parameters: + - { name: page, in: query, schema: { type: integer, default: 1 } } + - { name: size, in: query, schema: { type: integer, default: 20 } } + - { name: action, in: query, schema: { type: string, enum: [upload, download] } } + - { name: result, in: query, schema: { type: string, enum: [success, denied, failed] } } + - { name: ip, in: query, schema: { type: string } } + - { name: start_time, in: query, schema: { type: string } } + - { name: end_time, in: query, schema: { type: string } } + responses: + '200': + description: 审计日志分页 + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Envelope' + - type: object + properties: + data: + type: object + properties: + data: + type: array + items: { $ref: '#/components/schemas/AuditItem' } + total: { type: integer } + page: { type: integer } + size: { type: integer } + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '428': { $ref: '#/components/responses/NotInitialized' } + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: POST /admin/login 获取 + parameters: + CodeQuery: + name: code + in: query + required: true + schema: { type: string } + description: 取件码 + UploadID: + name: uploadID + in: path + required: true + schema: { type: string } + description: 上传会话 ID + FileIDQuery: + name: id + in: query + required: true + schema: { type: integer, format: int64 } + description: 文件记录 ID + responses: + BadRequest: + description: 参数/格式错误 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 400, msg: '过期时间类型错误' } + Unauthorized: + description: 未认证 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 401, msg: '密码错误' } + Forbidden: + description: 拒绝(类型/鉴权/上限) + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 403, msg: '不允许上传该类型文件' } + NotFound: + description: 不存在/已过期 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 404, msg: '文件不存在' } + RangeNotSatisfiable: + description: Range 越界 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 416, msg: '请求范围超出文件大小' } + RateLimited: + description: 限流超限 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 423, msg: '请求次数过多,请稍后再试' } + NotInitialized: + description: 系统未初始化(除 /setup 与 /api/v1/health 外) + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 428, msg: '系统未初始化,请先完成初始化' } + NotImplemented: + description: 引擎不支持 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 501, msg: '当前存储引擎不支持该操作' } + ServiceUnavailable: + description: 存储不可用 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 503, msg: '存储服务不可用,请稍后再试' } + InsufficientStorage: + description: 容量超限 + content: + application/json: + schema: { $ref: '#/components/schemas/Envelope' } + example: { code: 507, msg: '存储空间已达到管理员设置的容量上限' } + schemas: + Envelope: + type: object + description: 统一响应封装(HTTP 状态码 = code;失败时 data 缺省) + required: [code, msg] + properties: + code: { type: integer, example: 200 } + msg: { type: string, example: ok } + data: { description: 业务数据,失败时缺省 } + OkData: + type: object + properties: + ok: { type: boolean, example: true } + ShareMetadata: + type: object + properties: + code: { type: string } + name: { type: string } + size: { type: integer, format: int64 } + type: { type: string, enum: [text, file] } + is_text: { type: boolean } + created_at: { type: string } + expired_at: { type: string, nullable: true } + expires_at: { type: string, nullable: true } + expired_count: { type: integer, description: '-1 按时间/永久;>0 次数型限额' } + used_count: { type: integer } + remaining_downloads: { type: integer, nullable: true } + ShareDetail: + allOf: + - $ref: '#/components/schemas/ShareMetadata' + - type: object + properties: + text: { type: string, description: 文本内容或下载地址 } + content: { type: string, description: 文本内容(仅文本分享) } + download_url: { type: string, nullable: true, description: 代理地址或 S3 预签名直链 } + ShareFileData: + type: object + properties: + code: { type: string } + name: { type: string } + ChunkInitData: + type: object + properties: + existed: { type: boolean, example: false } + upload_id: { type: string } + chunk_size: { type: integer, format: int64 } + total_chunks: { type: integer } + uploaded_chunks: { type: array, items: { type: integer } } + ChunkUploadData: + type: object + properties: + chunk_hash: { type: string } + chunk_index: { type: integer } + skipped: { type: boolean, description: 幂等命中已传分片时为 true } + ChunkStatusData: + type: object + properties: + upload_id: { type: string } + file_name: { type: string } + file_size: { type: integer, format: int64 } + chunk_size: { type: integer, format: int64 } + total_chunks: { type: integer } + uploaded_chunks: { type: array, items: { type: integer } } + progress: { type: number } + PresignInitData: + type: object + properties: + upload_id: { type: string } + upload_url: { type: string, description: 'direct=S3 预签名 PUT URL;proxy=/presign/upload/proxy/{id}' } + mode: { type: string, enum: [direct, proxy] } + expires_in: { type: integer, example: 900 } + file_path: { type: string } + proxy_upload_url: { type: string, description: '仅 proxy 模式,与 upload_url 等价' } + legacy_proxy_upload_url: { type: string, description: '仅 proxy 模式;/api 前缀兼容别名,已废弃' } + LoginData: + type: object + properties: + id: { type: string, example: admin } + username: { type: string, example: admin } + token: { type: string } + token_type: { type: string, example: Bearer } + expires_at: { type: integer, format: int64, description: Unix 秒 } + expires_in: { type: integer, description: 秒 } + AdminFileItem: + type: object + description: snake_case 与 camelCase 双份输出(文档以 snake_case 为准) + properties: + id: { type: integer, format: int64 } + code: { type: string } + name: { type: string } + prefix: { type: string } + suffix: { type: string } + size: { type: integer, format: int64 } + is_text: { type: boolean } + is_chunked: { type: boolean } + is_expired: { type: boolean } + expired_at: { type: string, nullable: true } + expired_count: { type: integer } + used_count: { type: integer } + created_at: { type: string } + has_download_limit: { type: boolean } + is_permanent: { type: boolean } + remaining_downloads: { type: integer, nullable: true } + file_hash: { type: string, nullable: true } + text: { type: boolean, description: true 表示为文本分享 } + FileUpdateInput: + type: object + required: [id] + properties: + id: { type: integer, format: int64 } + code: { type: string } + prefix: { type: string } + suffix: { type: string } + expired_at: { type: string, description: ISO 8601 } + expired_count: { type: integer } + PolicyInput: + type: object + required: [action] + properties: + id: { type: integer, format: int64 } + action: { type: string, enum: [extend_24h, extend_7d, make_permanent, reset_download_limit] } + downloadLimit: { type: integer, minimum: 1, description: 仅 reset_download_limit;默认 5 } + PolicyResult: + type: object + properties: + id: { type: integer, format: int64 } + action: { type: string } + BatchStats: + type: object + description: 请求/成功/缺失/失败统计(snake_case 与 camelCase 双份) + properties: + requestedCount: { type: integer } + deletedCount: { type: integer, description: 仅批量删除 } + updatedCount: { type: integer, description: 仅批量更新 } + missingCount: { type: integer } + failedCount: { type: integer } + deleted: { type: array, items: { type: integer, format: int64 } } + missing: { type: array, items: { type: integer, format: int64 } } + failed: + type: array + items: + type: object + properties: + id: { type: integer, format: int64 } + reason: { type: string } + AuditItem: + type: object + description: 需求 ③ 审计行(snake_case 与 camelCase 双份) + properties: + id: { type: integer, format: int64 } + action: { type: string, enum: [upload, download] } + file_code: { type: string } + file_name: { type: string } + size_bytes: { type: integer, format: int64 } + transferred_bytes: { type: integer, format: int64 } + ip: { type: string } + user_agent: { type: string } + device_os: { type: string } + device_browser: { type: string } + device_type: { type: string } + actor: { type: string, enum: [admin, guest] } + result: { type: string, enum: [success, denied, failed] } + error_msg: { type: string } + duration_ms: { type: integer, format: int64 } + created_at: { type: string } + +security: [] +`,S=Object.assign({"../../../docs/api/00-overview.md":p,"../../../docs/api/01-auth.md":d,"../../../docs/api/02-text-share.md":c,"../../../docs/api/03-file-share.md":l,"../../../docs/api/04-pickup.md":m,"../../../docs/api/05-chunk-upload.md":u,"../../../docs/api/06-presign.md":g,"../../../docs/api/07-admin.md":_,"../../../docs/api/08-audit.md":y,"../../../docs/api/09-storage.md":h,"../../../docs/api/10-config.md":f,"../../../docs/api/11-errors.md":x,"../../../docs/api/12-logo.md":b}),v=["README","share","chunk","presign","admin","audit","errors","config","logo","webdav","env"];function j(n){return(n.split("/").pop()??n).replace(/\.md$/,"")}function E(n,e){const o=/^#\s+(.+)$/m.exec(n);return o?o[1].trim():e}const r=Object.entries(S).map(([n,e])=>{const o=j(n),s=v.indexOf(o);return{slug:o,title:E(e,o),embedded:e,order:s>=0?s:100+o.localeCompare("")}}).sort((n,e)=>n.order-e.order||n.slug.localeCompare(e.slug));r.length>0;const k=Object.assign({"../../../docs/openapi.yaml":T}),O=Object.values(k)[0]??null;async function C(n){if(n.embedded!==null)return n.embedded;const{API_BASE:e}=await i(async()=>{const{API_BASE:t}=await import("./index-DYsKpclu.js").then(a=>a.aD);return{API_BASE:t}},__vite__mapDeps([0,1])),o=`${e}/docs/api/${encodeURIComponent(n.slug)}.md`,s=await fetch(o);if(!s.ok)throw new Error(`文档加载失败(HTTP ${s.status})`);return s.text()}async function I(){try{const{API_BASE:n}=await i(async()=>{const{API_BASE:t}=await import("./index-DYsKpclu.js").then(a=>a.aD);return{API_BASE:t}},__vite__mapDeps([0,1])),e=await fetch(`${n}/docs/api/index.json`);if(!e.ok)return[];const o=await e.json(),s=new Set(r.map(t=>t.slug));return o.filter(t=>t.slug&&!s.has(t.slug)).map(t=>({slug:t.slug,title:t.title??t.slug,embedded:null,order:200}))}catch{return[]}}export{I as a,r as d,O as e,C as l}; diff --git a/server/web/dist/assets/favicon-Dl6ZLL7S.png b/server/web/dist/assets/favicon-Dl6ZLL7S.png new file mode 100644 index 0000000000000000000000000000000000000000..43b873ab823e6a9e4c9e91b5883e74f78b3d0c78 GIT binary patch literal 61232 zcmdqIi9gie7eB64Dj^Bki=?uJgcwUip%5|leaU3c3??RJ-(?qL-?y(Kidu=wU3|DR0HrgIW_Q6HTDpuP)boW75o)){y^QRPRWNIiCED}N?) z`Q7XD5AM3$;43egck4X&%%ZDCSr5OudhChZFO%`%Zo+$GX0qVx^|VxgBL9E%6zrm>uOdhX$sZ z=RYirci6t4ssDNBSo$9_?|)^MgqEm?pk% zGce86@_?y|t(FUnD1RfXy$2(2GzD14OmAfdm`tnxu>WBi*S_pP#Z*)!4=U*(0{?zy z-sA8H1wZZ^yh5gyuNd&Ah6v9Ozx~JJFXkD$26mgwcbxPYu=bn6P{}DKoWN-$t17MzI^p* z_m^H4#*WrT#WzL8)_htsJm_0q3r#4QuKf8dgG}`)ZXM-l_;GQ$*XkPY67}x5wJclsW(n&wv(wZ}?LlzfeYeP1kS>|zr{)u_ZITiG;{MXyxt{(C~}cZaLnFZ1>pL`(NwbT-O7?t=0mB; z<|u64?so=jI$<)!X!Wi))p;_LhId^SN%=msPu9V8PTKRhgpMLVt4Q&GM5(X5w}ofx zEVs%8zz~$(hT%b*|8(ibSI{zfT2tc59Oo0Q>-b@86W@ z_`aZwBfvU*nE8GCyc^1;>)EOZx^yGfn2k(udv@7jTLU{;>%kwYOb|?w0JJx6EA{W{ zEu|;VN7=Z|&s09y9hk3q*Yzw3WkU<-I@92SZym8?AQRBow(Zq3I}r`*)b?7#B;z^= zCqKfyLxp3F#mLi|VuWKR)9P}k1!TB|meQyS?Ge+uNsHyeVbwqPp9bt&$Fji$*uOSk z=Xotn#Rl!4ulK>@#H=T-lNlu1kk+x`Y*2xj3K#sdqjYp z29^yfbfSLxEHvMX7Al}_R2+-Renn|`-ycjjLy`5?J1`5(JD8r36uHkiIX{NFhD(^Xri2Wh z3N8VfnT)iwggzk$ucla20vjF>3|l;!-M0SBp7Mr(D99OL?^ zF#;%18|cqZJ<-cl>ZT&Gj$F(5r*&d3?0cOVX}umjv4P}gSW2&8O(dNcB#Zd|m<>8W zk|s;?7cXCFD?$kfT#*E00 zPuT09Yj(sKCEcbP%RlErmG;j@O>x38KCS52U*}izHc>rdH6qp)`pev^)5)Hpy*{+%{A7# zY2f@y{|B`|-xTPWd3-&Iu}t_*x5602Y!>Oy^pBZ*hY+#lR0{&zkn8;%{v53j0K0$U z{~yPYk~wXImKsEjdK6`)z1gd?nb7}0Wi&+Fu0Izr(634cOg`FgiTMwn@@ly1h8eIy zV@v-08?OJ{tX`Z{nf7mNlyy=J&I0=9L>2&0K3@R0(2HR%olk#G_G6u9=e4xiP5`o^ z9_opWkxcUbJ*>$^K!eDHZfP#5v;PG`{^aT@_GhwXDD`Ld#J7}+|5x2gxgTUJ0QW#h z{Bxyr3&@de7r3s%S-kR+?nct=>wl4yf6LGxP@dG*v3g39`=9vt?q|gB$-Y=o1={{X z-!anhS>*rB0!)|&S5?@FS6@1^Jq!GQqw{2M9P4KVgB~5T{0~sw{e{@YFCx%ZTD0JD?>@`g> z%fpha|1f$#``t%^`U%sT&G;vTT}|+Ry3`B)7V|n*gP}aEx9nAEb`l&jKx9Y-44uwS z=PLnsvsb1Gn#%mgLtuLK2T59&Ze19Nx>fN?z?6ymqYcK%HEdwf^v5M=l6d&?hpKP@L7k}e78{hVtYGhAyN z0vtvl&D9MSn2(-2|%LbJR)-3spZ>3JP?c`#-zbVoYZfavipjCS(3zOn(}^a za0qaM)-Zyn17`0&ygEA-N=oAb7C(Ah`v#1a2A@DNnk#eGMmuWWgYLV|TRr5B70SEh z6>ya+O24g1F=tn&zN6A}T+T+%_~FxU-B(tSWM?c3uCs0@}tIO6F26?tIov#GAk(=FPWw9uqRbdNxw@O0fCPy!!`ir z8fxMB2PJdo7SAV10fObf(3iFRVG;5{+2(tM7W;o6?r=uTJrJi?J?vl4c=P%wGYI8D z8xr#uMio1E8$|b|gMg+AadMCCof z+8XbRa-?ktR0SNKIDZ@cI-%yT;cc${{WT}_XMeule;2;@FB+FK;rT!3#f$_Dc^d*l zJiFrmvi#j>2=Q+``FMT2PhsDA3;u>M0z{9kIy~U2iZvdWW|8l#)6a(+dsDGZR)TFZ zJsY#Z`Ov2tp6}89mHJEc$+i_fABcNZtmp0G#4oKSkbo-Pm0c;Fx|v?7RZbyNg2#zG z6bY==jO_i*2;%9e{Xoaa^xJd>DcKNsHH(%S0OiyGDs&n0TV9~sYQmZv2Oy)y*`xZ0 zR>hk<n|}$go@cg;$$$5TX>_V|2E6Wol6vWLRO*+E)&*q7 z$@2<=%gP~P;aNHDHcSr?Sn<7dxV}Q0T=P=b+Y#m}(Sadc>#Ra^_eh!571XoeW_vmS z>xae=q^yR3P^p!}+g1~c5*$oF#f>~1KoidokI)oPZu?=Th4`^d`;A$Hc)N zd2Q#tlvOb+OnkWpxB2}1*sn3F;;60#Ui?7ASXT3#<{D!cTqTSXdk9pnpDxV)ILQUJ z`|Yr>YSB$&#g14>&EBgWqadA=c;d(FqOO{f7IBmT4a!$BReCfx*8vqj4qcTd-`O=O zPcyK&r5UZifn~=_reV4g4VS2eE0yRU{1!t8T-C1FRv49}QtWV&5@;>m1zZgLha4=@ zEx_VO!IBGAw)qF8r5U)iHMH~nlfj1|2r1fYE?E0OW7X~>V4FS~5(8!y#fJd4`X|KXybWBgA z1v24v`Hx!@Vc|D7)>t1HH9skpHGmERkfE*_^Z1L;wsnYLnG?mE5;=s_4GQcU0@?h`Lun)$m|+5r9CZNGe#ZGmJv>rXguMG-?) zS%##HK323nYY0@^h)!i_t$IF!0zL&$|4`;2lrFSZNq%^}cMDkg%c>?8^XCea@CxUH zx!cFwZyaxyq72G|xNa=r{qtREZHoV{vMIdRbKF568@F{Fx9=^G10A~vDE*0{N$x|o za|w}JrAP@tMxcmMb;5Z;x?=m84nLK@Aw1TotnC@=TKT7#FwD6v9^_k-XrP|^xp@L) zx4~$OI>KblbDWdZEg^NoQpt9RtMb*XFzu#^cMIeo_)SZFk`c>U@{|Rlo;o&V0UxP$P* z=Tmuc*2NuX^LG1CPM6DCfTo{zW=6F>(BjX45b^j*EC);WwN64&G|EwH>yl@*Hj) z2iknIio64WGQ0i2Et1IIgUEEtc82SrH19p^|M6nsj9r&^z3~#YoYngh!}-)7tsX$r z%qXzq!snd458+F6eS;dtT`x|TbJp5a57n`YK(jzIq2aTC0_@^8?+P>YNzh<)t2Ypl z_X7Qcx$axxgC$L9|AsK8#jK}39$aC4V0EjqaDX1(L3ggAixD_F9Cz*?hCWrf+JgIB zHAP);5=R?KxVAh$!10(MkS?uqRk{P}70!`_(S+d08s({ z^w=qBmA}&KEglu$pQn&A-R|zI26kKw*e+YYy1WseTBO{f=v1#aC$rO4a%_d~sS)|w zQqK?e;F=Bu)^p0gSGTBvw|(JvY0!z}7#*Y}pyA{l`H{Ypn{JDkPy`cQJ zB~QqHg4s9vDGf1y0DwHT68y-!_7|3%(9)6 z{=CGxlQUWj9!$w|xR(0kgkDJY_w(C{8)bQ}0I?9w1?v4bVm2pHT9eZvDbs&YPjf!n znB6_2vX}omBl30P>6Z@X9wBlIT002;kdnt=AKCT_SqPo)E_v@)q)vx;I_u) z3)BH8GgzMUJpR*^KTZ&1edlCwj)dWwx714!;G-Wr%X)jTo>HLO`KfS)Rj9GX?XKyP z1;&E8k;gySi+>yFxP06SVOps#It$vPy(O!XO3%jui&&yFyp3odX8+p4e_i0?kGy^m z&=CTZ(djQ+622O?g!u6s`BvCrj%;+aeKSKIZ za*LjqtRScF(1~e7>^S0{>9a~S}qaZ zNMr@Vf<|?whaOgY{>RMF_i|}3zYf=5TmzWHFa8sftg(}Yf9RHF|8Nbg#ekT^gIX?p zt`~*^MQy~TadD%%M*x&l=NA=Z9#Q?6S&)C!QZhsUx>%zJg8`;J2-!!;a@`$C;=->< z7KWR4>ujZ`1kUGfCzfpGh{yYg)1euf8dga8^CyW2i83C~Pp+Q+rXq9oRf)CVZ_%GU zmY7|pTb2_-YS=ge)#~#71icHft>4;>WrW&`;UcSC1E%zs6UK)w@ukONh}TvzH>Tea zYT^@Uje6+-E%71ER>hGRhJhLYOj)QuxIAFnUwMuOV5L4RRV;^v3FW{oxJGSg zx|XG1d41+NDgV>M)a)^H%??LHPDy;d>BZPq_?pKv)JmX4_;OjaY(Tpr8J)Awc++zf z@^9z5SZ@t%EJXO{2tTE}r0kYfv;FRecqP!m8}4b(>Qp(Ob(k%e_$^MZ<}iD5Z;GQw z+Bz?<6eu?~&U|r9)H?!O8dBXHTanAqkQRxGK44o%S+-815s%NVARZy4ne~~Py00xq z9DS)bcWU44DP@*sfJsQlwu%9a_Yc0*bA?DNt+}-QsjcP5>22Uc1~I5B=MTV%etc*ZKN=CFWXsd^rA|uKW_7SQ zRlVWU*ptAH8kOE#;9Q`w@HBRY7YL|Vf8pOG2S4spA@1)#US$W8K7E<`m9S-@?Z{<1 zmo2NY(5{*2PhU4S-iAHtWy%*y6P;k!bWNC_w`KYR!)-|2GWOD_8)gx@ln)1DiQ+2*kX7F;KmxTOQDg`#zLQFgB_-Qco8K;Bws|#m!X+d9F^`Crwi5{a1i@^ zo>;_Z)1I>c>bnG!yGL;+DU+SuZYJ_H<=&I4<-hd)Zs3xfDJb3#a`iNjLdwU=ZmFE8 zma2nAsi>rt-4eFE*yK4i9#0QvED70`R@sHIB8Zn)*fpsrKF$7X{BeYAEzt79zU@%c z;x0%%iuiC}86p3my04U(qU!*-r5rIX()AmTr=S6Dmbed2tH9yO_UAII@IJrt>%EJY zwjME?hHO<1rkm7a%DohiVklMDvmlGBS=I<9FrhRDF?vvF+OxLG@kq#dv}k3eGJ>>e zdlKi_AL3T;qSs}&u7*ojz~%s^KD>SzLO2O=@jnT@iZgeOqPu^nH{s?BQ~5hc+flc0 zisqs^3E48hrd|2#e!qSK7RD+en?ByMp6_NM;Wx>+z$G3bTBh%6V!Fv+U+XLcWH|TC zS1;3qk6?IS9?Mn?yq~&zrS*RPO0Bcs~5G}97Qur;|f97)DPG3Sk ze61hrQ!bzKhzi&TZKr(a4CAYMG8)TV0|Rn1iLm?i@RO|i$Z~hgX?x^N3&QbBW`>Lt z2KFb?1;zs1SlRbOhltU-k+NHc9%sP4;;Z|$A|xQ=mtI)bsV3kAB2>zyJhAYcHk*?> zi+y>~fn1@~AF*jH%YQU?U@*Thv_?6Tk7Jk1=7Y{kz0Z5I>Ta!*3&izMY#*Asl{y_<*|pR9p|`{91Q+DXRDKpZJQW~3dRvqma*S|@O1 z%z*yld2ktSKZj{~a||CGSWf``)|w@)(Q1x6mW7Lx{#@oi*bko7a~;RF+60ClDjwnm zE)Vf{pvt>j&J2ubQK_iXQIt9$O=rD}%Z0KB}&(Vz+O&w6f0(Wh}`ql6!V& zU7!#ixFZQjw3g+C1yNrQHSv28ut4`)2s&w8-`L75x~D7bRR8Vi&BZp0&>Uh}Kpv{)@ctVH|L0u*s~K*SU~{}fB;AsBm_*W7HJAMG%od%!{WitmsrAb&Tw4!1OYZGp z^w@auIDJ@J=2@+9Ec(@6Zg}B~eDq=ECFZyA-bEd9u)(pcEVmmMWtiCxS$96&R zQ35?(X%A0xRC#suG8Bm28=9(X&6t>e08*#2KK9$n~fjJ@7r4p!q$ zL4W;S^_}jYeN`5i18#GlBXTCNqcR(E%owWP)6?+QtR>`B#;uWR4$H+LaJv+1w1998O^VKxxTYua6>Cqo!5z2y*Y)XULUn5Q_jQPrJzlXPd10S8K| z*tP(oeeYVD#fj2hozU}1kNT`1o;(2<=HcUxXD^RDu6HFNc3MIXRfthvje)QpRr>Z- zcAj~-Wtn;<@M)_W^Luh(L`|^7HPu)7wGGifsWEi?`0S{J!+i1EoU@O(<%grZ3&%G} zG|i~y2?I-eD>hHPHcHR7dOXxG+`FR7Ptu&$=3~g#d-4(U$rHGE&P8q6n0AAgK7UVU ze%_2it_R%`PN#`jx%~SH)Ni+~qU*OG{cz+_Yo|uL^ar-zaY}JF^0EPD8c@F-&y__e z&ZCSdqaOgg^sto0V>M&t>vAVTZ2yB_xOrM8+?ovjn4E%WvFVzj0Xw~JpRYvmJTCMP z*HQ#V4oEiH#B!93O-Tu2k%{Kk<4r$QqmZHn`TZ{XjS~wIdUjcd= z1Mq4b9bE6~`@zwS+H$F(qoi0xddRx2WQr7XEkrgm3V+;b1GbK5AGocdbp<@lB6qe5 zJkD8pC>~j_qiP0+(lM%DZJm5gcX;@{1zyWFb4w$1XGy=PS@iVHJ_aogN(J;fa!|;7 zCnIJ1*q+D|X;y0g2F@ijKLx~7M`p_DLJPoq;sDyPimrTBml6q!6Fs0kwu0I&sG?uI z2*`GKnmn)sooO!95MZ7b^|V;2>W8Lac$`Ew-a3*2NGwWIZ5^M8PtO5OSC7`C?Iv?j z8y7UxWX5l&J_5Dp)qB|9m@(6_#|B%3nsy%)O4oasG(|S?e}#Fq9ibcVSSCnh=S8fs zqvRusnBi9V$zCtMaJP1j-Q(JVUQT}89k=kw_%cNNS{nsL-(P-%1PLppLq*3~X{ouW z(~HsqM*VkbrTzx&rVzJ8MIM)Lx{a1wMsmHtWCdq%ZTW?D^$b>yxwpTK1TP&&aJ;ec zb>ZPm{E}00lp=eL0(GPOI-zD`ni=wYu6y@xQM6A zp>`To2n?%STq@q{=_de6A7StQpvBR0pHa4PScrC$^~4p5^dFvzM=gh~M*;{`iRTcG zhD~fPXivC;kZn@cKBh8{cd1zHm}-)4fgArvkpIS?lf~KpKfN6UUEj^+Ed=)st_dX9P;Sb3R|$h|HLX~ z5jwHNE`4}taCrDwqLH37TcK?n`7~2GQlhxaI+DvJC7G5G2#izv8t1r?A<-d;Qz7lv z3z~zrpr-8yg~G-B%|WMpPT&>=qwKU{E`O9qXDfKee|O%4NBJk4NHZ~fS;*vQOyfaX zPgE+a>D_$`Q>A$fmj%Qm%%klH(?m}a8FjP%NN)*I9DS*@Q8`ki#hPB`bR9--jc+gs zp1AAD#h4Xmn+xt~#AyLS`0o6`k z&!)E9k8$iY>M|>k`x#&bbFEM>L&vgJ7_di#FnCv!_E7MKn^huE0R zX}a~cZ*t?S)^!3OM@wXK)U?;#Kwy0NGUtg`3O0Ui=2|8LkJQ>1-Xj5}G{AJ8 z+y<|W(#AdJlD3tqP<6`t0i*b;2P@mWwU2HG9*B3b4Ye%L5WG0lXQ-O!1y`;>Hr(%B zEmbaH5mE5u_G-EKSA(683P-08mt+8HQ|PYGL!I&iUWKvWUAO`XY&3s0lS+z` zR8)rcQfLR8hBld#wC93K$F}_2%r(Gif0cLCh$sEgl9Pq6eRS;VHvM*4oWS0tMAq_& zOqJts5?Tw~0F4wMmgD%leH)wS;es_9L^i1;?;cKCR_ z&)Z7*h+wMmZ$6cLtpta{0k$V5Be&h5No~vPEex79FgraUdCeZg)~j%$`*>?C`#4Q; z3R0J+T;Qw2!4vdrYRDj6R7avP$Rdw4!!SN?)3x=C@;YWFnCE2UOg#7f+0WCvtTl6K zf$m+Sq4uZOF1l1qJtfi*r-ep#(SO8BOMU-#9aLJD!OX%7qDv zUlw;<*7sTl6&v%wQ-Uo+)4k+k->)=Ap`Zm0>!nD00Z>*EO(@q*>&SD?Gt$L?MYeI~c%a~ZB<3_Q7yKs1k3 z8r7hs4i0LE*G!d;=}O;aPaL5++zSmK6yKB(1w!rK%C`TK)7r2YV#}Pm`6bH)cseJewdUGcty^8fv87y{dir8q5dMeFx4TlJ zaNd0XXiuriA>tCbL<6E5O+1;n-F#nqBD|GBZ^DcFDEne(qX>6q!pWl8s|YVqkas*^ zVbcWL*r@;r!9=TJwX%tElF(pf?&cJI{PVdl-^00$0*<&@irt&Ye4og=pZVX=?*vRB z+HCY|2{=swY}@P$87*%#iJ;m7O3$9=py_28lbxrq{D`_U4|@TD%bH)If6C%PP?T7CRPc(Eo662NnJy*^rHfL|Djx=z$I_Pa zsSJB2P{KEfrb}86CaLr_7J^%E93Z>!G>%4`gPG>IT(B47!ENpr< z*Fx~>*;=q&Z4V~`9=<&Pj-z+5ade}Wh?uMVWX@|;DkAA|IryV_o{!F>p*}C+T*6mL)yMt=O zEXNQO&ON1d=x!Dh^oKISOH-)KxvL7+z%x}c>}C^Lr?{lOhibhyqeTjToF_G~8ezYP zV|+{-%FvaB&N>1yCC;o$R`KB0FJsDk7q1X738qxbfzt0AUs@y!?jlWKIucfqfh+Vw zrLn05<>hdK22`bLUajnlS7cDUi?Qcy7CRGO_o@#}=d57oBlgjZmhqK#)gr4%k=4Wd z8$nM5&{1og>-mz=SdWl4+>FTDVujn=+B-f39j|-^kG54e zYy6Z~VE+mt3M6C`D4mIsjQyKbF*5t7{bj*={$5E=m#=^(vpWO*nl~*q0eH$m?_^+R z=uu*CvBDm577tp_XFw(%J%Q;o^)ZYoZC$VKRZs%01{bEefD64f0e3AhN-K6D6;R}el1vKUl0`pLN zA~9O-{_=W@SctIV5{7$hVvM32wP()NdN(PLI)sthaXragjZ^gN8OxF_$AiG~iF%pN%E zqWyEP{1((I(?U+$eHK17f9cG*Ra%Hff-zf^`plib?W1fUkrH*_^~v?bBly{YdQ97A zAffSBZ1ad`;-qxfVq_5J#%o&MO?-v36BOI+%vSgY&bpX)Ez-Gj)@SuE*J4s3DT#Fl zd+k+_?1|uhDgHvv8<=y3<*xw;SF6}oOtEtuS$`jCFC1;m6Q#s1u@+H)a2GZ{xUM^( z09S8%{T^0jkVQU38!YWjpg#6otI>P;Q?|eHuFmRk9kt#v=8;7&Bt*u2O=PVII43xu z<3Ea?vrd(3ADxYGXrBqRm}IvnuK&ARJ;g zjRx@pEt3-qg)gHMcZWDfO6d_x#pCyJ=U+k{`{~5zIR{R^5selfuiLa&HPL7$=y&bx z=#O@+yo+hU8va2>#>b3=WgdB}Eb&ujeIv*-M1d zO$V@j;>!h5x2dCsG@+W&I?T8dBcx0|h-;X)s9*ne$e3w=^zi6~-Ueq*fwIa2c@1^787!=}m zeasOC6hc-LHwx9+M}I{W)(){llh{Yk@ssQy&_-5hco)c3PjTs`JcX$r`;jGNu4?J& zAa1yq%VbXEMCSAiagH$w?#tYH9Sqb*c9De&)1wys1{=vthASNw22hir)qb~Tky(Cp zt|qU=qmH)C$AL}vg7y-R#QDWvl7w%?zkcBpN0iBQ#Zgo$OFl3*>VL!6hYx0pWXSWI zZZWA6991LPN7|$b&I#Q{N5nH~t(igY5};r|i4^@=&N0VmBy(o6)Fk2cI!FWRV4l>z z9R)a(L2?_C!H=)BdG_I}H?@Y3v!;IRJUrQq)d1cm( zYEv5u5VE|p#E3$<{35|pt+h*ijgX^Bh*OzrgPrKhdNMtrW7tvyLs$LI|tB z-+s{bn)m?MLF+(eFOT3GcyKfiDZqG*3*Da&6>zA>@TE$#<^pR}5ru)}qp3J4wFf~u9AyxZf=3-sD$we4cgK#3M4 zXCUgf5K<%ozyLG+O~+SQ1{;>l10e-pY)Dr^q;gFZIlh&F3Y(Pnz!P?kRsHXF@@4g& z9VhJ7C4V)x9rW8%-N6-$ z$*Zq$5k+T0IZPc~`CusD^yjrQ5VEO?ep%Y(&O@No&7_A4)LURL&cW>5Mn-JwEmophKMSwn&mA21 z_&mi&yBbXz)A}~?tQ&p5$hZ!-dSO>b+5zY}tZfm^v5O{O>i@cp#*yk{42t+m9gV$W z^83NmW54-&evp!RV3gQ&nDK{&qzo2wzX@e5p_c`9&`l#@#*+~F? zJY0E=Nvi4kvRlU}aGIngv18jxUqBglbqv>Bsx?k0zj4Bz7vKOufg?0|kH{V*o!UeouaXyaRc)vmun3g&B+96HAgUmYXM ziCTIB(tp@SnkriO?Yn?OpKa-Lc4R|g(>QR4V1hE;LTD?_1ZOp~z5ts3EZXx{d%Kyu z(2pF)=j_#?k@Vv|zWlk4F~%`DR_@=&~C4wqJ_&I z*bfeePp6>C8+xE!UDI`|y)9r`1-E0Mryd@Hk0NXZ=IqrS>92^Jd0Ek+ZzP3RxY0Z) z3plJ`DZ{vQjD|7Nenp>sdPu!<%RH6cicZ>c)dpLQ-E0?bWADC;W+`SXt@Fwu-lJzO zdFx|C{KTKzJa84kt_2@d+ItZ0{4ATl)Teyz^^$wXm?`Jku~lRcrRdb+Dx?Eq%kKUi zcoCvv8S|&FKn$7TsJU)6HP5LRSoAclQ`9M%)YM)LSb6#q>O?=@=gW_5dIt4;BBLjB ztcdlO2TN0x)*TsP!*L3<}^KVKO2m$?3(C3YZUUmP1RL)&h{B8m6-M2xV)c`ATH}7-ifaJ8LwkHa@l(> z{^uNIWe9l1wnz}!)SlFB!%vC;z)-f8Ii`299WnoHd!Au+!)ij224?_8J+3=WL%4U2 zVME|gk+T;5^*t;YCc&TDe$U;0se>0cQ2|R~Me{Mn6iTT}w~^ij{RTAZ34`vuqlWwk zGsh`C{o7VPz$xU>^u|a$d;e?Cg1VJ>A2PxisEBy0CW0~Gri~Qi54@rqRjaK9g1NE- zj2O;1V`lN7+MYr2B?h^>Qj4p$##eSDMvBwmC7bHab<_5~Cfou@N?Y#Z^VsSs=PADM zC$>gS+IznkcB3f)!*htq?z|OpPs3kY&oS!FJC23wk0OC>&cEX&p5z(FNGJjoG_}WR zo>}2wExPRaIMi)h8EO0D1~n`EEKvRff{PUdo}~s8U+ZzhmtyVjmnvH!Y_?85x+8RW zh{=FtX+g9RTLgcG$vyb5;GJTSSLJzoU0T>6`NWmJ_9}ot21k z??{7RRJl}cU80zq8;D|K+KQJznj)YkEu{uM_8hUh=3eW7oz{5PaqidVjFGD;A0`w8 zw!N@v?%2q?^fYmlc}ozS@{A~~iKxK0DnPaM9&CdR>Btw$m~;D#ft5`j2|;gkpM;_A z*&30QDR=N%dNSKqf&M0t#V}hw-rtKRw%qV~zGMd%u%B3|kFi&HA}4k_m0TGawRr&! z&yaG(9j8PM)({xGb1c@-&R>_ke_cyLD#&)R0#ajMcOz+{{)YaRO2BQZJZ;MT8X(J9 zkC%!?51p=vb%?2?2EWgD7pOE?#(#FN2io9b$olE^CZU?Frvl$oN5`cY^q=qKUUYR0 zmHhq=_&TH?vGFm`ItZf7vBGCx+BySH;_)#~4JYRgF~ig?Vk#X`8%rf$!&ix^k>9xP z9`~i++{r3U!)Er$HCt{mLB#jqrvpFotj0_EqhFm^{z!fqf_^G0M^}uA>%OL+?xaow zSgT?9c#KC3v$mM@z1((6G|(Eqc!wUd4D3%4$5$!K6_^^v|FFJUL*O-8x?FpKV6;#9 zf%!{zvwO$eL0sQ+_Gz_XQqG-~X$i*Y8a(?1x@1#LQma;~yYIwWM+UtdV%vgKw`1-- zx|Ywi)-%e$X4QNgQOq7%l3`(3Du$G@@|BT!FS(eMrba4PV?Vut$+p7(*pcBAT^x43 zmU7Q!LjB|yH`*!asLxjnIg7cIe$TB_EG-W7Qz;ey{TXKH99J?cvc zBwa9iR`~e##b4$Qz@aFXG(?=kI|LKon>BoJd{t-;d;_4yYFW^shnQ00TDRjpDlz8z z8p5Sf0tnjgIbF}iLj~Y3OI)w&Sgc4HQtw>V>XLe4FA!zKBX z%gQ!>Q<%Er{1r^`^Rueb}HeL3`rY(XM@XU_iwNN zoOqb`eZ^kQipqNbiq@Hw-76cDXZuOtle$g#;LcazOMvo+lCQ4v-@usE@K#DpHdDnU z%_n;&pmWQwoek!yEQ{ITZ{+(CdI_Ar z)onJS=6RHw@Bl-sGIR3Z!p}K!y~9+A`Dg4^KuXGZY5sgrX&F*++_rPw&6E51=C-Tk zBA^&z^zyX6#&sA#lqL#is}ala89hHgw$wed9%6)lEEXxx+pY^eBq>bZp#m_Z!Hs%6 zEe}bt#Gb{1P=4O706;Uwm9~UW+IQDut9iv>*ygjZ*mGhb&)9YDu15uT1Q0ju{O#0i zLL6T1ira{%dkFO=^)Fik&!yVw272#k7SveT^q+51n7m6>vem!%r}Lq`nvJp*-9vLU zyG-0Y|G1!wsTYr0NA1+CHj0mQ$6Y<28*;n37>a6tC6S76IRy7t>0@?7{mY3Pwmw$0 zjmd{cKQzW=2EJjncr;8f_YN+@rkPIAG{7tk(_=Wwmn ztb?YtcuN*npNDT|g-WJ27)VjZwoNEqp*8G}_e)=VUVF<8qec+?{=^HB-59ixzT$~KKx7Y6F zpj@R=r-2}|@!hb1_P>Ror8T!AmHr-oLRr$@Sg9R~>#K%sU>z)z;8UHVz_&(q(3cFl zDxA_^-ceIC^8P>oFDoBo99Eh`{)&lg`;A*I+aIR2h-A78xG4g29C40;jIfUBPSMJW zmcYa4Qyt#JbA=g#NnRNzoVSUIUw{QgA67)gyGGco=?vfmv^%LaH)gOB=#;oq0+ zUge&B{K2@gJmM1}o-{Y~Bwm*Lcw55ms%8{UyPIE-I3aQ?g=UW9y*j! zNl$-Z_CAGqAA8k3u{%#cBu#aSG52x4Xl(}KKi9KdCfg18n`sGfZqe_FnSadNGS$U!(ac%Ca%m|=`DKJ$C! z7Nx$Cqn0q6zJgKXB6EL{+x6~E_s`^hV!X-OJrx4@h%e9Uwl=|IFUO&$O6XXOfo@RY zXs@QczpEqiYM#NgU{8{J4WgYdsb1qF#`@KH`HNGX!_zqva^@KJyJFjqe~VT}SVLa| z%xlQ0t2F1O7K9w6#Accr@uX%naPA&i)!Thj->u>uVW^N3cKHeb=mh|8s!`t=v*I)< zo7bamfsHxmoNUlFEV5FqF)2z_(zA1i)fGTxSS|q+&AR~aI@ zjSYO&I@e@d8(}Rwlibf9OVSS>VVT%@iz5cdjCo|Sk1na(AoT|7&q=b-7RSu*LnGqK zA$mjpeLs`cHxfWWe%@}kg_7TCj()9!Sf4ek=5abg3dEwl6%0ikx=&M-I;84UX0#ad zEaX0x*v^|Bv-61|ig#|>q~CC`eD}(0mZ4@qfuIDXNv*$DgLoYfh5YjU!-`SzP4SFa z%zF;CJnM;ae;6!Wb5E=cQTNr!&*9hLR{ytG-^4yq z?dwnyt#034Hj~+XoLu_sl^&z+o5sf|toU~bjV6D|;tCAw{Tjm2_wlT5`g6>Rnv~#V zsU`AsD;Q~Oa#YTpRIk5h`&i26@Z8O6Zl6aL@&gP{27KLQwm&x14=<}{!>#FVo1d{i z<{j}#zG(x|9>&++6PxHOg~crD!rnlC?p;x@`e%YbNnsdC`m7N}IbxpSVmP2bc7Ojr95y#Cq ziZtN!)s?j1{CBhlUz0upoA#Az0s#>PnGwog>F`HSJ{Wn5JT(U#NfwY1 z75^o^Tpc`0n)i~Q33lMX^kcpLKdQbuuBzqz`Urx7pppU-lF}e09n#$;p&%hhcMC^A zx)f=U?vUxA`?>#Ib@rYo)>_XqGn+vnqDGNC?-O{6Y{&|F ze`43C4+E71mMLEwC4R$jv7_MKJqs3U@%g5L73v--N#v`ai%nK%sv~kEzg{xGp><%7 zYc4hwJP%%yUGUFtSzSmd{(aG$;Q2J?M;p3hRao|RoZFk~=Y;msX&uRD!t}ou(@mMZ^S~^n&Vr7Sn_bVD$;^2I5?-}<21O;p<8}`k zR%dTkg=ZVdUm5;lHujq7qw`=EyZpM_lH3x<5{G5Fy=A&7)j3;F9{Z;4H=UExPgcog z_Bfr7w$nYC5Q04WYooiBwbqJIGDI6o{OAJQBL&!fqtPwXqrsERNP6fe)_Mxqp8$RB zhaR@Y(d$&V{piSK-S=W`@zb>^u!lrW)>}Q0mB%m1S7FL5MBUJQ!$A}q(Tu4_H)Lz9 z6+jH=xqra5?@{Q^=SH3K^ka27BGf`Dad?B|&h}WAtg3Y}UjF5?3*(CnOp;2>PPJ3? z;cUkQT~%ZYi^*!y!3o`~BzSfCfz|57O>SEAR7OwOjilvg%;4jEA#Xy(5KG+B3kza- zvM9rut9gj=h7?o({1gg)2jfz+)rC^+1CPYKtI9E z6`5Uf zh?gA8iAOl~P6>%!>Ts@y*p}1n+~+)r_#*>?2_6p|2^vH|# z*n}>Qe)j&#_-Od9Mk0>ER>!Qwi8}PG7B-i?2T1`^@WRc5;_tJ`d7!!u+rDU*)HJN& ziRN$D|Exc(cAms%;B^V8|XD-r+uh+wJ*{XYIaKA2eWY%1)a zQK0Tn&`h6M8+anJkG-!bk94%-o2pY_%0(z8#x8MXVkXiLF=B_Tot>l@K1(%D%>bE+ zof(wz4%8+3KJq9v@kR6V=9l@uop*KCT2}fP0pZBf#a<4qvFWRS=6V<>B%+ul(slOb zOafa4J@>%XT+lhlZH$wXE$+~j9?89QeRSO});OXZXDL{y&u-!GZ?KoB$%qF z3?E9@Eb;pcEyy!uRr7Q5d!$sQdlY*V5ioAMdhP)(j(tuuBWCGgH_Ozzpt{^TIX;WT z_~3zPO@v@7<6t_M1%cHpFYud>UXh)igC0OqG@EjD8m&Q41$3e%rb$VE1 zy9vre;oz}^6V?3}1z>TshG^|s2CynH6-4SUQ7utfz9!Kz!^XcK+sW7SC2ytPjmr6k z;~hs8$M+5c{8ih#PR0bqi84)L0F;H==%=aT@C+)jZ_1_Ir~lpSix(8}@5O#O!3pvS z!K%S=6osiv!3mi?PkNs8eoqS`BwO6O$zPsG7w(uyujZQ#l4d&A`lY8AN0KiVQ<653 ze%RKO+?d>Br7>n=j4Q!MYX3*owC2KT>WCoUet1qWA{^{?I>eCFBG0l$zr6~R<5J8j zc@6>J&;`e=deDy!gl1e33Td97Dhd93JNm=aF|O!DbLg!D8O~+yL$$LHFfy|!2D#q* znCwfeeL&pec5H7Ti{+d#my!Y|sHK_NJhypiXojPgGgO)mmUvuz`=x*o^^+tz+%L))U;Ja5dHRBqcil=sjWp}!y^tIJ^V}f zOYfEzt?`Vaa9e{g_Y)X2+@notb`yPrPruFEHeg zN4dwV!kXXvVs&)z9P3A#iZo&nZuu-mml#+OUl(!w<~XZvFJRqV~Y_7xI^foav08UhS>xv2O3r5woxV!qc3+>2+YV(D|h&+0C$Of}V{>vA40({Si0y z`RUy~M|O@_7X{_Cj$7fwyDNpCSbFg|gR;ihRD<~9$JwUeiFPFvy`(~Z&oOcBzwTfM zE_aA#beQC2OmZ}fInXodt0|;YcjZycV&y~vR>{l6vzbMdB*qiJ ztfv}LE!4vT8-CV_@y8a!0AykA@X>RLwSGnIO#|J+M7R)_Vy~d4$0jcFJv^3Y zWL3ifoT)naet;O_d~lWTu{*Xx&Q|dv#=}>}V4onT8)4{CG`BP^0!&V2E!@!REHS9E zWJDr0)$<#eqp&hM`Es)8 zoSN@`g6BHp+&@+H=GQWla?EV36BF;o3X0*7=V1OOnV*QD{eMGbXV3xkL9!_Xaq?XB_<+!fG~RK8skTD%e^iPSk9tby_(r zO^@amJ@qI7Csc~+rx!A|k9fILEDRGS4^75RCkASn#2tkA z*vk#oDPj$*j)HRX6Ap_K6YJL9OsX6hk+xc2-@6$J33WFjoX)J~Gv^2K#>gP{`zb*rVa9-Cu$xer1vQomN94_PIc*)V=bb70;KDlZS@B zBFmE^8zwt_#iuqS#U&o5l{Q^e9aK~0n0ikuMkfaqaL>v!HZQ{$>P9=c>qd!>Fto@$ zlN|eXVv~M`rDi-;erZk8-|H=3qa6N%ei?_fzlwF9h~|J#>TTZ@-@PXxMtdHUhIONO zW51SsYC;!~+X}b~QtRSH(g&H`0{V`91_}mAQC#op4o&=w{kBxl>qh%cbt%uk*}-mYEyMO7TS+1W#iiG8rQ?1j&n71Ug_1#1Ys!S7P-;?WXY3W2-~AnF7ronC z0(CyVx`Z-3ysjr(_djg352NSq*b=B;CVAeS`GK}Bok^aURJh-@q*H29qiq^i*Fw?X zYoMI!`KFcM#yaz?#IAIn^v8?vQGy1~1Y)bivFNwdFX2pz2#cDB*Gc~_(kz zSIpTiFR7j6elk8;uN+S5=$j!-c>Ot_Mc##BHRGeDlia;ruDBnb!fh`&I0iv`{i?*d)6K>v_2dU-TpFCa?&vduJNwdOPFcE&++ffCTh#^S zFm?sE)c)JufNc#E?B8}cqw%|>c~*A`$4xn`s%=PlmnYtm_xcS^#PwDgvJ9JRg2;US ze%|oO%*r90KqTp8dr;GZQCQHbPe|Ob!cO5SF4k(V7n>xIDob@<5Sa1`JpyCM z6PwDavgF+=@daYSBCTl9L*{IT@6c}4A|;UQZO7L{$F!^xgD)|or;5M|k$IsVA%smS zU`tbw(Lnl^hQ4QUvDYxA*k-ADIBD?*klx`bekEe7sr7h?nB110(ai&7s7|*bM}&4S zqIH{fHfz$Pw=F)Y|JQPAKTQnxa`R|!OM(bt8n@o1Kej;J;l*w8yK&&AGTEtp>1{w8 z@-iM0f?9n~oR)50n@ZuC@Fc6ZHl==};lz&Th6wJg4U>Jd^_Zz7@{z1DlEV%=d@@xK6@-FWGuU@}5z2rrjC?`J}f&qy;7$jp$g)Yts6GrtV;`M<|I zmYT*8^DYSoI_LPNbte|zOwtjNF|aKQI%0ofAo!M`+mGRAX#|jppG-a*;aWbE`mXrM znUXib!NQIEnM%Ij5*<@tDQm3IS$D80xNv-!b)$A^ST=fV>$83TvR!mE`BY&vzQK2K zy?~o~q43rjCgo^+vp1bp&s0d03S!!6(P52B+==^|_V?fO$B;m+l8abdlkw__%&hM% zi6PvhM)NmEyx@36@)#bm9Iiq$c9HcjJl4*&Ifa9bj-n_$zks_TW_Aqh^W~-c^iof| zipEcT zC972Lw-y^tDKGWXZOX()%c&1cBYEe2&2hhx^wOL59=YtDQA#Y%t|PcH&`gVuG<7+B z@UbK!>7se&BfD!wX_~(_P+4;+KNC8;AaB`cX|oq)j)8{}6LmT|N~?|C+<0ikW0b(L zK)9y;8%b*0dvEvf#R>DIoXjH0safyQsoE5mkJr83CNEA=x(CyavAz_}5qG|S6YC7? zkKXmR)X52Ob)C1?p|s=;4kMPYg72i6Iu>iorwL?g_AJdWeToYSc$k?7lWh zP1xJE-4#BXlxKzE^4vIs&E!k&i{8;w2)-liC;q;<47D;O}9Ipv?#o0}EK`SE}7DK=^| zdOwssI`(6iL${EdTFmf+U0=9C0>!dIpmv3`h;!1MjW>I@Ou^P+N5v$Bu4Er_T>o8NI=K3BJFM5tX{^C@>DI|RM!)9luM{k!-C_-$Va9l+F!#D2K_Z&}a)Ru3S%V6v7 zWap&Fg_n zb>M>MBdzt!g3H9)4dy)|X;J0#X*-82<*YBZu)X$k>c->@#v+C-ZYH8hu7QNnQ$1mb482iIVOE8Y(>6rR* z0aNs?h27(YSmcw07e|;x#G$^0%B7^3Crw=w{IR~jQtJ($Et+l~)tz|@@9|4)Z~q$sFBXGdL6EnT5La2ZMVk=N|Ls7kIKH@H_;|F7VLE&s8O5<+^y0|Ca!ruZ zMT@#cIcb+vyIxq-;r%z6&XAUPo~O4>&SfT_^o+8+1iA~Fx27T57>5k+A+xh{g*}g` zt93dbWu6u*b4u=L3wvxHeZ9M@X`*%ZYHi_mOiN}g&r``0dOKBL7V3)HEN*eGdU-wJ znehtx@gAco~<6{%m24S(Ln9~4Q9G~4UF)R=eXQ+dKa<7EV9y^ZBmhi>IC#2;ka*@_>V>}2m( zQSs3Xy+xn}2A55xKgZyQ#xBc_L(0;j z84G^ftN}p_Nuv7)Sw-VsrW2OcU)RaY3muOUB|Z;+wUn6^9W4_xrAiCCHq4~<;WrB} zE#JJqF60w4C&Vb^em&${eE1j`bCY>s${@X0VE7o^eeBN5F)mwr(esOlUcYB!5W|3< z;_`uekyArXk86>_(_YLyhC>VoehTIN!@(6T@&g(lbta0mnDY6C{4tnuF5i##YwFEU z4;CT~CTvopy1PES*y}C+I)iKZHDsm(zq;(UjDB1B%qsV84qy|y0Osd!C;NT5TKhQ6 z10AehNrz?|M-`8}et6~M=#YkkBso9s{9gCgYE(^X-856mNz_4_aQkt$FVT+(<#vN- z>F=Bq7l@fI$bHIVn~elxsuRuWO1*S;S$rfXEkA`X z9y*guxCmj=x31i*;v=~=$qK6H+%?&I^@^P~XV&~PXU>T&%d%-Rtdfof-z-ymjJPxV zcw4uu!0NVWY5j;^W9Y2T<(zOSg@Nh9a3bRN*zVGkiICFtehxIg6+P_94&@^LsaO8f zZ6djyGX(O>3oI*lA1hlJpnHw!V(%)?Wfyfyt7%mfblHb&E9vg){usDZ*-DYp`(w8y zr^qevQe3vVHST_|z;Q==JVA@xGS>9F5y20_W7mJBGtrM7gV>Ta?_wkQ(RE77+b5$} zz4XOPTaU$ml9VNqx~Hs9vK-~c4jmsco!y|^^V*)vY-K1sF03R-s~zsYypji+TC$kF z8sXIHd54J*UUjy{skc8pp6i@fR1&us`6p9Hq3_b`GNt|^x{k2AC0eN=>|^!K(lDsm z&oL;D`x!TB)1!OXN$WUDy1lov_j;@Lno33{6F<$5zxc{S%C;I{W&E0q$7y~$FlVn@ zYj1tn#nEaa;^({QZ}ds>28n0lMQw2#o>T^}goI3aDRP7{um2LY!`4qIU*J_KWqV!X zU~^Ate0N#xc(7(4OdD(Dm z^isfw+E>z#pB6TR{psvO?>jPI9_uc=mkoR$s*@sJOjVbXeyX{6nebEmWN0`RF>yKT zD-v?Ox2~zm74>OBcP$nVjqi@jP*u|5ZmxjGqLL7-tA!}#(}LrR+Ph(*IA4(aZs>-) zI&Qo?2Co|5_?ppM^R`sm72oF_=DECZa~#Iq^YERpF?|1Np|)v_9U{xbCjfWi0OZUYtA^J`)jadbN1?*BBazov&ZCH>C9OvMDLeA1{6A zDq500mi{OWdKoWts{2tbvuTgoB{5kaapn850B6xB$pQ^KUD*^D7gYR_Z-I3P(eho~ zHD_9Z3o?g`M_<8)(1G zgv}nbH}p^)J>P48;vMK|m`z`k?QS#BZRdY%i9KZelDd>$9xQSN4J2fXmV^8 zKmQqq#uwFy*||Xnw$z51U3>4uaklnh{~Uaf@3qjb(Oe$xu?Yxj@cYo~v;5+E|Cf49 z*z1m}pM_GJow6p!OG34^tsMc$+yg%nKP4?k5G?)vtcLl6(<^s~qd_;@s0)x|7`wZ( z$>g}RYZnJxQ@_C@jK4Dnb6vK#ryc3gY*r=VCY659fX;l0Y05a$nBt{K9R=^2ic;Xg z0hjdXglIJHhHx~v)3UPK*^B9-o4D-X1`d+xbin3!$bNTiEWOP4a-{YU^bmZGE%B1c z=fNnra)~)@;a2WmxVpVP9E;T74|y@@wV?jW_?$BCk*MpA@{6#=qz}*CBqNQ+Nh~H4 z7`cRBclm=`t!foGJmd-n*wq8fNd4|R)nM+BGquW*GIf^9jrS6s2%LCoTeaHQC=n!@ z7XeY$0ynxkY1ZFKi~mH*yD*PQ#5+}&_Yz+^)Gd;TXxUjnCao>Tmxg}C} zx1zlM3hY_OiD%JySCd(i$2^Y zS`Shi19up;;{3)bk;~m!s!?}wBq!pntsU?1R3e;qL{sSjt>d8ag1Sk-(m6DfKpn%l z%=j(`Vi8=oQ6|6YweZr!g_E~>XPOF)@2G|39t&5g9KkO5Z5i*gL5Y#Z>gDXb*~_wG z`?%{oyM=d)jEPIQkB;sn&kEe_`YbXGe;%)EqZ7NftSz87Zwj2NrrRg`FjK>X!t{Du zh*`M4uj~plL|}0UTV@)czu5P+e+s=_*o|uw0z4jP&QxF1bUZa{D8+ zl`DAb*N)W*GF=wL4#vqJkNpTwIzIHt|BeGFVwzVeye%?U44(zLsiq)sZvG61Zf5=AK z_AKPk07R7N?B3hzk@r^BB(7d(lX`&Osun)pYtnTSspIbx_=ELYW=DN^|4;DJ4ZjB$ z$I0U7p9TZE-rxDccaMRqgoFX1EO8h8^&LDsZ|Z=PAgPxU79YiV;c7SMwhy19IP6!K&&Ts)cmcn8Pg=O3eu&$;3n z%&w1M1K{aL42$R$q=gCWGr4nop0T$)d%Vc~Tk;CJXiawV3Ndv+eZZV^_KBDGy3Ahl z?xYViA*>B-LO0o&`$+njb_giJMEzT0*!3*V?xMfa4jNsJRyz=X_ZarNBpI1sE`Bbv z=8MVR8TnbJd~3E{YNu5%kM}rpJdXoLY1&}|eDaMXTu&NU4)}eX6u5aJaLjswMqGx$#Ux~1A z4VhEh&qiG8yCqi*uE^x?z8^F;SupOpRW5@Q5-u$jf-IdYVS~wB9@*vFvv?fG^qCVu zRRPg;=fb%1NBqqBg7yrTn&l*?o~1soS`n|be7Hp=!tR(!h&sETTdzQQ=N`j|FCUoB zt^6JXPcxs|E3buJ4n51{s*1~=tuJ=@7u%1k(~G0k&aESj!ZJHfkeMBU{iN#_uM`!EW*Jh~&}!{Cs7A#&L- z7&w&xKfau@c;GQ@SAOBp+Je_goX8$OK#vgphCD2q=Xr9Fwq+koPv4OA0{a3@yJ}<*QLwMP4mna>9aLMuy-|XZ6$Vht z+KeS)cpmjZ3vQJ^r?!j-Bt;$xMl=;WgyFAW!f~VZvmZTDEAaKM)Je-NA<{L@(rBS} z*MlWFz+PiY$q(Mh>{#(wn`Ts`S#DTqIBTdybYZ$E6o{J9P~@Qn%BK>MrMFxj{h}lt&NgfewVEkF# zg~CNJ*IH`B-re)m%RPk@-<7$xD*S-Q7wNRkO}yu5PknBE$f!cpy~*A*>)pPkHr5{^ zQQmByrE__8+8*t7>L55=VC7LFVU=|KEgI?jqE_b;4cb*taaq}HbFS4X&A=3u&+O<_ zMPMnYyvuXEAw-={~^JK{1(7IT;{jpyBktw^2~qEUCOY(yJZhSyWL z(o=od&QMM<*WvHQ93!#3Qpu&C=$%g(TB(ti(?`qPFZ292#^>x6tu;Mdqa@W(k(kI6 z*lR;xy%0kYwSpO$sSg3tAAN2*+dOx#B+<85yp@%eNDc`eVpmnxDuo!d9m`grI`E?0k!c0yF6^rD$D%Bb(6CyIJgtm=7 zvSBBm6!yA>M?Z`;XRBWv9bUKxW|2p}GVE&QEi$un&@ZIECO5yw|ofZjN**{S`q>{4N^N2@Z{J-@sBKg28;P^YBxor_v-bF}0B| zxohJxnDX~;3=llYUTOHOgczl)PM2^Z*6+5G)!FGkRawS|iSmGd(_gz_eiWFj+qh6r zJEz8lvJT%s5@j8$qY;`p8(Nt;x`6GrDIP#aM0>cDr~II4fO&z2MxC5Xs!? zrvfQC2~4p_t&eyUfM@VGv}SBHM$i1gQ^J5Hy}(9T)Llp!k=*fF4az4UbU%_##2^ zP2#-mJ!)1VnG|Ihj2N50LWFL6Q2bO#I4HCJ0evEUDYY2KiUcntj7)*wDn6*Qv8#fJ zdM9F+9D=`uiAn*iQ16kmRL+>#X{MOL*A-Fmd_ezA6%1{=pG#qy!IOeH*5=@J?T=wP z5k4|gnE@aHi%_C7lTe9=H(&Lj>FO7wLtK{3Tm0h?^Cd9s5apqV+sx$zI z)6C#Z!Y~}zK-)7u{?LCTL1v|EP$a_q9{r@LRRZI?0q2`Xs!2-l^(anH+JBb!(QFSI zpPn-y_yQIOE$y7mwk3pK$6pa=s4X;P~nlc;VG58k%nvEC_U1YeeAW$}JEe zF8102o4otI!(-9$_n7;q|A5qqI_;0wnZRohj(Sx%fDP;s31h#HvOhTsJ9tW|ux%uK z>*HTXK0k1+E`IXNZp73syOj2UYHD!a{tXyq=EuDr*KW_sAFY|~A*77`J?oC=9HHA^ z;i;J4Ijh8=XV{s@K{S|S@iw0rNTZoxB0;N`dBt@}0QbP+`Cuf7FMKh&7U)njF7a&$4=!&}ketICJrw?TSlv?-SSJu4HoI274eo{g(hZkZtb~clw zJQiPb0#bUtrPluyP$`P6zfSA@0)>WM;9G_Qf=4TFE4M$`gbv^tSi%lQqAUpdW5L!2 z{|N>iUHtHU>4ZFMM8dRDCSDNW6hQZLN4ltRXmT@j6m!xAJux8W06OumNnKlUoR4<~ zF9EUi`xDF7Rsj_2oz4Fc0C-(Uqw`GM|Jcz=h0cpgTRCtnMko{HIm1eD2{vb_tQkV>Aw%U_q#xaGzv2?oHvTC^selRHNe5<`}( z^!2EZMVHx`lV_uxcjNShLgYU_?3oF?-{`xqU?81g+?v9og5n?g^v3GHIkn)0ybTE` z-I?vvXKF*k!#yKS2gt%c1#O};4I^e_g5V!4&-NvYO=bR(Tb^1Q6uJ%?D#oyBh{o5h zU1C1`CSmAi0P?Bz9j)Bi~Ks~ur0_<>ZO?$b1hxyhn-~A43k1BIuI0Q==j10g> zZQu&TIDfL@K96d#B>q)NelbnJkf=4AH6(~hJ8k`9xD(yXe5c$=! z@+)Yg8hva%QX3<#VmglIf>478c^!jUaSO5$c{~gi6sW}QRg&t% zDHTC)Fn7|FAW)^7H4G39W|gu-g5zzFM0iG#|$9CfIII&gx9#b{<`8_!xLJmHjV|E#Op!+pGnW{Fvcx- zYfmmnpfHVX_f9@(sx6b5>&HBs7zAi%1_*#TA(nfS6{i6|eC#1Zn0$J!kRd%e4=pQX zc6HwDoxoR@A(pnz-h>UPrtzM00)>CXj*2D$X`Fw=$$7%*fo_&ts*&^g)&^GIKR{fE zGkuk6Xd_5_dllT&StWuz9kwR(A6JdzPb(8VH$(;i3)oqoKr{fJUDRC#nmJ3Py@>Rz za^K^}y;r0+1@&|7;Mvntc-AT-#gCJx-se7>lK*4+SRc@4;|2WtmQ`S!`;9dj9d>gM zUzESg$|3_q3N_BxyByPr#eFpI_% zBLM;RvHhJ5=3_*KDzJ^wHkSQ3+p%es(F}$pHXwh~~ zzFo_x)X+8(iGzZLw~sZPCzLBhN|nlT{dTUA5)eidSk3S0i}8ThKX-fh&vuAzcAAE1 zMnZA*5fY$B16!sZ-kXAJzLBt5)U8eRjJw+`>= z699mcNGSiceNz?8v6c7$3es7^m}DUWk|hrC@Q4R{06u8pG_akq11yddAg{V%Am#P4oF#Z`fq+n1n%iy zff24Fhrwb1bTUGi+9SYmOitxo$nnC@t1N}?pgYvT$4-|(j1noefUZ=cgHe$27HnX! z;MvI-C^Z&PQm^!z>0Vg@2gNYhb>W=G}009 z`xoUtR2Kk+YZ(&cA{Wp1TA`IwhT$N8X-0g}|Bcha7`Xv5&b1SY(xbu=Y6yN>4NZE5 zk`p$i;!yVTfcF1INFNwK^RM{?c=FkN2uSpnn`1C{gbHI3iBZK1IFhGBFv^;PN}HQ+ z*4Ci-U$y(ZQwQbYyZuR(URHWzbJ=gge;MHaJbUxgpp9LzMyfO*O?ifeXrbiIZy>d{ z|5BUIdBF&=k>oE`+79+1TSrfj@It1lYQno(H6H~LV?-J=Qik&W#+n$BH_>5EpBRe_ z#izj1{D9%{kmXY{t>x8q8T~T5C0mrEth|+$b7)V`uNOX5B`3LVWlLX5Ywza@VS-qn zc4e^L|2fEQK^cFkwxX7yC}i4w^LUTvkeqwuDYU>FyFk8Em$C-n3hs>)2bzNdvG4Kh9^K~zOqJi67Z=&ttt8c+K^(cNQk&PReFJthl;?%H&wndz(Z6( zlpqMA3=R2I$T(6>S02ruLMfWP*N=;c$pcL}sD?!a8g!^2Ycl4}iWXXH>LiR6(7`9Z zLq#-hAq=wOt`>!uyG6R5H=8?4@Sh?I9?PKtxq(6ftp2>Sh=SGOSea#2u`E~#h*g~k z->{U`(3eP2g25Ox8ToE_RMxh#m8=zNEO8fhYpdj#B{hM*^9opMP_tO|Q=36B@ZdDWAs4%br5lzu3 z%=uf^kgGhkfpdlV>VSjyg#Emp2Zvq*xXjZKrV(TLBe^M_O|)RmO9UFIpf&`jqMkj6%;V3~ z5@G$%QVmAh{~4^e>5LkD9p}Y}+|$_F+lwFly34FFpLrRcHG=0Q(dH^vK^^&eJu1R0C?0eWJR ze&dXp%UrjCh;t`!W+<1A1L|`%ZX|(r8>;B3fVbIuu8_e@*He{R%C1V@GmJ#NwOWch z*a%9`?-O=tdkCs;5IQcN{$(y4V(z*Ms3@PeWmM#i|DnSo`tQaK2!_}W52Oj2#lo@B zXe+4xz_(@pjuth4|BMW@@g$&)NAa9{TleJFjW%vb?HSvMPY^WZ1emau$@S03zXpRn zaai~9#oZ#@%Br{QM?w&m*dNPhCVqjIM=74?1YqE+XWGw40xlY_f@SPZ1e1kyZd=v7 zks>4txL#?^f5F)&eklhUcpgxiaNta@f3@$7<8pb5%^698fs_ZnqDFrZFKT|4tO%+r z*t;45>xd}*?7jQmfmbi()|~ud&v&T3Epbs*_)A($257H9oYo+m2{$j?Ow9L?9%5q_ zYn%CS1*v(KW}y-524M`Z`6Csm@}&3lHBo*y`@Xx{Yp79_3*`yZT>k%}Iem!^0`%WN zu^nENjis586z=*e{k5^xxamUguQN(DZQKW79}VbR`%c{nvx+YcfcSujbXwvUR7KMC zUx#1<)^sS4fE`Y3Jyd<2{XjB8Gi?uKYI41l9;_z*`U0QrML~LAI8yVhntZMUqWRfA z*aug61103OlK3U&pWQA-wu2?md_23#3Jxz6dPNeUv|~b98fXyp*Tcm_F}~1^7ijs5KDUH~nlxQMvB#p~Oi z69^i}0#FKC5*q((0iuL5*K0H~ge#E{eFR^9@Lz9g_-Do@ruN}g6g`oVgBE_6kXI!il@(EiYI42F6QBg8 zIw*U;v1Qu+Zg2ksJZ8{&D4;ewn4Px4E(%H=KqDl>>SUxevk?#=4{^xJ`oCdC^V8CQ zV+3-ExrizoaY}Hjl{l#OyGj0I5x&P^?EgSRd)Kh7{TJF(xE&dk$g{^#XV>NF3^40t7bqHvQ}^)Gh(v(rn;`Mm{WmHJ{}v$kNrVTTrEx)SJsKc0F3+Og(9dOwx(KfO{CR4kz`6^Neq@;wMo9j^C> zm$Fn-C4f^8YyJxZjZ=-xe{qGHz%6-HqhTbhlol&>>;SAqb0zzq!s=cKVlc~n9UK~f z;U|5e&~Pmk;Z#Uesvx*7B2gVCw2>jJGEMilXUzRMt7mT{nxgia^;XFu5Uevu0bA?T zK$(Ds-f;INyzl3D6Q^<>%^d7$Ndy}4Cqmf>$j18$NK<k&L%5p%1pdbbuygID<$KWD3S2Z!}mL%$*TJ;$3I>x5E8&O!hgsV15j( zr)t_p_ERMxo%bI~g2-Nb4~$Lsg8ygtv-1|7GGuoJ><6*u;ie0vO%n5 z`9XEQ`oni@a2z@KA39Q9AEQ9`(yk*aJt*Ca)0^cZ_|m7o(>`$Zv|<*N52!s3+1Efx z2*V{A=)MU10wDkTia{aq0fpp;VfG{O|BGDxRxH#!#2I-pVWoi8>^q-uKs}ZE|6sQ% zX(dg&((hj?wv$)3=RYLqPIe=F{v)bCdDx@~V6v)>3U>+!H;S#V`agBjpq@bCwQa=t zb`A=!`!0~?rGph_+!htI;DvfA&xqKVbdk_z%r;kjDB2{yZf8NG-|q=PiGNIi!ih8} zRH!|ZYVGf);N?&qgy5Z#x{8Re;0Ua%jIBFe|l&4T{}f zsBdY%Y^D<{Se5!Yiu{8%NTU6XlZshXqb3M~2-czypwbVNfXyusZJ32*4CMi>K*Rmp z%QyV$x$Xh?^=W&}6dU;ZNw65ICh%`l8R^W=6QLsh`5zI=nnLP6fbp*&?EnI+G9d*P zh`Zv;6kovx-NO_(?x5)GtMtF!|1;0w-_!pL_rNZR1Z!r|M!DUoU!n4ovJ2)9?_$5n z4*xIMk+vjH{$N}6`N%3)?Or(Rl}fyK7tt8m)#R#zknp`W@_5%A_Xf2--V7MUgOZc^ z8uZ3K_NmPg5|Xn8Gh-t3IBv=Qxoe{b^yh-MKHlh2Pr7b zl&RWh^xlLoSLuO0N7=>y+0$?T`$M`277ldq2afX$K#vF81*_`9gX0CTb_1;=8thf6+G81i6eSszOWQ`kS=nnhzkS0K@anUn z+bUEc;5#6&iwk-cfE0$S;bV(gu$!pChven0XWKp!G7*< zK7qoAYQ0JH&*ypn*zF~9JdaD*aVZ)6un&-NT?VxB5Z81BHU1fr+s6IyTmWS_7R58M z@?zQK09;l5N*4`(u%V2BdV3+ia>jOlJ2=^VJP@*vwSVAD^Y9FOtPjOT2^5{jc(fUh zyJVxsq6Z#Mb~X+%4K_mylycifn3@v#Q(*HL0lp86^fTJolKsFu zce1+IW!wx1YaX@i6#qIm0N-)|D(R>QrZn~P{IxvjAkOYM9C)Zv_oL!)c!*|+jP5syJw7T4BP`GI(M__PfYm1 z;gi%{!^67)#{qb{Dvc415%nYiz041NsK?*jgr#e{WfOf$SIORPV{#FC^d0TA1MxB_ zl}w*^Zt2F#4LFku({ftpS^K-I;Xbr%mL1&@F1^7U7mvob$xzE2{YR^!f%xCw?5|K#9sPe6hZyNXYv1-`P{0$pv#^q@r zdIewvT*~+y-N4#57TY~~fhX1C#N~+9Z_@0ogBwwCd;gsK#|#?yK}oFXKDH8y=RUh~ zg!tY|M05#8f+EL;jJ}hDY(5dk0sS<0`mv;t6?j_xao=^TTNwAF&6$ z0;<`(>J+#z*n{ozz>Y&&fdchIt>gK_?kM!Vz0exEv1GA1`JRfIR>mbm#3c>Ptug&* zE+9~^HvAZaY5W|)bb*JJyj(Hh;qTNvPfCtRrhhae9EZ`N%-(WjMTlQ&bPslOCgWnc zjrC0*r;bV`ZSCtQ_M0uHCGPB&*J1B|{p>1BzcYBK)@?F35}&J1!3u*Rcjh#LQo4?2 z+9-Nq?i8d8yo6q!sTdf-Sr7P^XXk$1&3Z_ZF8l%pYoKK&|8~|M&{I?Mv1WUzu9r>l z8``PwPQi8X@uO1%29eDdj9zl=^|RxjOzd;xU4~jM72H!_G--2a{?-Td1#jk7{Jg%XQ)cc9oojC>`7+4Ii@H(X-{86)z zkwc2!FlC&IR70@CiXLC+M*dQ#{B3q{$T|~+#3GGJYKh8it;{=Pqc z5Kxg4328~C5n*&F5>g@{APoY_fYGB|{65d`&-b(4 zbMHO(yiVME?mcY5G!W>K!0|j9 zoXG1F;CY$ZkTZ@78m#t38Y*e0(BqW}LXWADn|I19!Q?#CduNL{F&V;{9(^D#?>M&I zPv~sl!05wy|GOj`kX75Q3zClLLkbg~>oa&mUv*Lc4QbE|B6MhK%AJ%;hItt-wfRE| zh`H0}MlW_bttaH&*e1&z`Pl((UW9UQee#*G3!&L?WjK)h$;PS|atr?6I2pGN-dHp~ zHY+j3Um+;*WsnpRI{gI86QBY0o#zq~@F(&G$t$Pe3mgf;r6QWta1!Bu6l86l&vZ@s z;!PdHbZWLg1unlk&*w&Dc&wg%o8tl6ltw%SXZ4F!sd4h4)aJ{3R!(?!hM^I??>yc} zClC68B+Uazi2Fe_6!Qd9C zgCM${uKfmzH@$G6J3IypMx*J+;S=!YkN8<4*_a5>qQR~+PQo%9(R0G^rvGSOH{|S$ z5~%y}oSn*cU76l{a39H&mJQ8^<-?$AB>MHj0(Wg+w3TrEm`mL_BDVzk{>|vqb`bJ8 z(h!Bk-D-F+Vk0O3qsEOY^EZw^)XgKf{Gn5E4eq~kePf``cO?9_4{Y(^{sh~}IGCN4 zgUqu3J9ig%Pv1(HS_n1k!2LTbEp@xgyR0EKemk$%MjngS@5TF1Y*g^9?9QysO%}bN z$U@{lvv)?uSsOmKwg34kNhOOv%h@9(&R8@NQZfzX`_mIGzybIzK?HX^_<~FWnYa0> zoz@V*0txVzR}RKyzN3*^!}T%NSr?NV`P(K~s)4)V{0x7(^tvkz(O&`5U1!^y=?yvU z1R<_RuxqqS0;LGvzPO*vc^TMzo#Wy4GYcB=$_n!e-~W=uEeAM1`C;D;+_VoIr@>yM zTgE#%^;lu`^snI856{XpE8D#oU7R%)u=m_kfJ_-n6sN;?U$QQQkp`q^<@BjG_GWD} zD_sqdp4N%zS*u}TO`9zA+841d<3iHxdZ97vtVa8X;}=-?yYs+bn*D2{))=bmWO z8i%$f@qM(#U0~ZTT$CU!>UFp{fNW0O6KZ=_Xk*VYd>1`j?Kfbw*E%eerI1O7=Xt-e zh#kKGWc=&#FP{1rAoHvXLu7iknqT9=OrJJtS!Apw3Bw*DM|!#c{hD7&_g-c7N|tOK zdbY9QdH4!sA3ameWYvZ)-=N~m4AmXGradcrbXLx{A#N(=;IU5>17MHk#y|f#?j?FQ zEFe7A^f{;prJqX11xERt!M%|Drg5?+r;50{M(&^RpA)V;K4PR*9$~^tG_w-xxUYd;` zhiCRDJrnHAh9{0eLz01~eT!pONuBc3Lk|NofNjxeA~g5hh*bHSnw|#IV5gVbVf`DOc4s`@_>P6>jqPgT7Z>6Tv0NIDM&C3W>;tIJ zDkgUgy$(y=Ma8raAalN_^TvPowE6=k@wdMxcO5A5mtv#|LPU);`m^|pRG{Q$_S-pH zfraQlV7qYXz!Vdn1Y(O3yBiI{KJ>-kA|(N?S`>g}PC>CZ)>=VQlZ5d5N&cpyDfD?V z?0Ax-WyH*KZFyJJg%?W5v^&T-mjvqMY`Au#5l7KN^VedkO=h-_1+g4Wo0}16i9zxF z3n_~0F&{9hB+UQr_1}T!IY0B|O`+C!H1Z}9sE+3xh<=YWbUO`s-j`XBn6`c{Ohu4V zkn~rKIFMm~VnMC#p6{2EmLx3-qoP72~R4@>eBH6bO{4i=Mn#@FVY=H87y58yt|ANdkCQ9*nSp9 z)gO5Byl^END`jgr(1&T;c=;}%;xo$Zl)35y_~t*)EYIHhq#FeyxEV2*tx?&w5q?%< z5*jH!EJB)N;(#aVSe_ybw~K>Pc2`{Y=MMw?M|{>b`)8bYdr(yt-a#0iv*=K#;gIt% z6>3~GFH@G2Zfh=!q^Gfr=SQ=m7dDK|l01-i2oHhMXE+EukvYFW_dsoA`hCuX=P$^A zlWQ;yN}aKux&_X^gA3O2JubU6A8^7cHtVdD);>am4bYhL2OFHeQddtYZ=_+D-_N^jB=L38urI zz0CS%E#Y2&c%B2xCpcPCPp~z z(a|5TS*}!&UANQ0f_N%4P55V{BH=?0F!_jTrE-YCxWvD(MW^v5Rw?IlWBznZp0WNd z6U||-KRr#sBcuKk-0t5}wel?edDceIJcX=4m1#kTECn?jLIPT07$xOLMa^|3$`0!^8bx*k6Hh|1@=Fq-9CT z9sTn6;3km}9QE)ldZfQ(xHW{Cd0h>uSz%D+M%Zd&-q2wgdfGQK0C>U0d7bPS6>Hfh z&eZ{DG4)W?xMo~2UiTaZRIrWdLJ97g-kG&TT^|QOLWR_W{LZSPRktJc?oi+Us8;VE zkIq>1fwWImxnXS0O6Uv;sezYFT#1q>o+miX&tY8o-@e`0gE1-kErY+oFod~}!{4p1 zE3&A=k}M~#TR6Ll2NHjHKw4{;lY+Krb?@0vkxtpWf_aUp%1)fhO0#JgR{izJqQ^N- z%J2MX$zH3)VUN-Jr|BM3#`?$<-zC(t(VSqM-oQ;Xuuk8gYGg)E#s{Bn4)xk)gs2}# zS5Tz@}-Pu{&EyFb`Flxnl0 zXqqEU4_3WfI|$KyuC=A|qnIljLOw9m8susHE(k_aHN`+n!<7!j+kXj{i1+Lj(YJhZ zMLRd&E4AKvxA?Nb5JjRnhOsBx5?7Xa$;O5~{|S`X5PxH*CyewQ+?OKZX&=iRUShag zc>xoAJHBZ~dFFf+--I+?2S^*S)eFmBVJos6hkDafQw(v-5uyK@s4;q2q-~HFSO#>% zyMCc5ljtc<34DK=g+MZV8ofP;m{oM{n*4hHDJxp3U>Os0B)|5|1=k~QGCt2=Uq&xl z|D(jBB?EbTwKOJz(TM)HTvPolX`qoO9(fa3ySoiKc z;7kLDxQ55~hZ1Y_-kyz9PTKmXObFzx@SIp+S(qmb?82<<5Ot~077Y@7c4EFTvaFoN z++8tSn&@Lq?nMp*uF(Ojl-cc{VrUo*8hc-Epbe_}jfXJ1!MRY zTp}M0PE2j(PU$;UVq56<6tB6yY9w2)i}+bi5BW-a<^{_DmLK}@8yAb z=|2meACY*4+3xsfT&p;7{2 zJ-b#sXU<#fn(T5%2zrl3MdCdLgvlBmeFA;gE7A`cmQvxKjgiz@Z3!%DCH&=SU4s%RM z@@kdo$1K?-7{AN~^R7j{zW)DQfXDPSiF3>rHb561muS=XcMp?c5`A>l3k*|V-f|8& zbQ5Wh*_a&qoi4E?rv_()lcc+D{UQ>cYaoqOXjDA0=VTKA2i|X_Xf*Gx|LK8YugPj} zSt3hISyamcbhC>0-#-8QmH50Du#JGN>|i{KsN;D!vYE6;I&YwNWtcry3}?L^RQp&j zAc217$p%qX)WR#y-(N5XP4tnylaYNifeu!Nw16{3{SavkYle!y+=jXVy6exLgZmikGUk zOIFi?n=)m(%NI*n@?L?TbaY;4(zDs@TO65Nyhi(xyIh#^gQBeMee&s44SRfU@lwiKmG>+tO1005~#JwJ)tA;b%-}-RKNxK?&nK4tCj|v-Rm*9zgy8E``B%s(8Y&8T*168 zOFXH7+B6w~u*36!rC?scEDdApN`{r`zxph6q7T5kpRE_Yhic`+hq=bsZGdS2H?cjc z0Vq_Pe&8%--hh0n(OPmO1hzG4 zJ99WpSEKd`a4&M;7<-sEBm$_FY2+jzI3WXC27X;)FC5;8DdnYZ6JAIbL?(dcytTxy zjF9Ta0v$qfylpXnSK!@Jv5EJ+LyT$r3Xuy*-Um&MvYgq`fqkNTs-nS(oAY*Y$hp}K zUAl1R;B9wO{v3XB{mJ^0W1uS8>rere%hEmLCmUa$qhRmquk=$|gcfxoF&-@LTF^$r z1Y=vPn^AM9)9m5dd{H3^>QQW1;v%?d-c?PCS%RxLbxmd_Y|)>a9Lf%y1?sO|3(F`N zyI0!ach~B(OXD^~mp+yFssCIv*26ND0xs;Wagm{`^5qMI5X?M_I~$QU)lM6Dp65os zq0!&c;ZSC}}vhMwjhE|n1h^}B>kWL9Q`4ygpvGhXQyzT#lxYi)Fv*H24YaXEmDO#qveN3mS4!XvbysSX_Ij@|3#z}9IiHNo<4tY zQ9RqrgCTsmk9KI{xfl$Stc^zZQha1!#iE6Ff4%!~6(#U7ayLjrt_qOLe0$^(2K$ZtIThhZr2SZ<0)o=4|;Fg&Z_~4Zk zA0L^pTFADgrr+T^3mPEV8lVAFl9-y(9E{OtO?HmCA=`a2iL9mY!|u-#e!)&VsS#pk zo|g%q(+La&Mn;ou`aJ#)qv=VP%mcePk|xS~?urj*E^%Ux3{gidVdA}s#divujoMo( zAE05+Pr-tuQwoV;yjBJQR>hF4a zu%HRJQ|O-o z&ucf&|F$%4WgkBJ@3|`J(g4rz2H}HE6F%foT2m4_k;{^1fd81wbSlZTmx8BZrN}{z z$v*@}UCwBAIQ?xneJRXK$4C8CNk%^U0%3%;JAK=8b(2$J@V!hN^$15AZakLRg3CUY z>;H)l{uo0h;H`a8a7Us|p9(;4ewh;5MhZ!!x0q1;=PYRO#pJL*%anUSKsoX5=hE>T z;}Tfi(txK=q!R%;F;obAI?2IQYH*6}0xfuJ&&AF0y%}X_z)?!{W_3%$-d!F_NhLsx zFeo1GFcy{|mK?jopymc6s&_^dt%xAv!!Sgzu0W89+lS+b2oH1XSBd7<39gf>kU4f~ zl_sG0%sr(KMjxmFa_({Wo+X=`)xou?Farr`DiX%N8oIu+V`o@=nFPNvZ;hD}3AdQv zpDTIaf}fKkH*<7hBVo~zg}cn()O z1^O20${K^iy{#sYv)^d-I&ui;dlhY^xV+9+S{60yWN^yo(q5!MI@WB8ftdN z^deg6)*jgDPtAJ}C|>yK0thu=g%Dp5ra=UrG@rf>ztxz}==+88>(uy*{FpeFTyouc z2NU{Q+V9u8j*K7CAry;L= z6Hio-$-#!3&OoK*f^<@COPfI*X^$X{xZs01xl+GO3RS0IBL2rukY{}wxuyl?c8tv* z3wMvmrwG;+OWV{6cj-s^=_x!(gX(wN%lg7j@W0sd>TA8ZV<$31rUu83Pc)q(A#Fbp zUjlwG@E%1fu?bR8 zQN31jWtu9J&(?PT%eRmL&i;;lU^`UORb%*bB_-c2&pXTgdBT%D)z0O&>ho(0%r{Y} zi}drYkZosD(FoYC<1$AW?Zf1sHWHWVXmeB|&%DS8^Nld$Pg04i zQSmy1rIWXADS{U49#2$ERp&N(pm09ye0}W!a)Hjf8FevKvlTe5<8L`sJI#m~V)n+r zO%hT?iX2Am!ihN7`g9$&IEW5r+sXdXP$>7ZLbV5DliP&mpr!)Yd9J29c@5zHgOfg^ z16cZaYH*dec-DU|$T@!vnALs)I$9c5xb?1w*-(L~Ab51$`n>75Tw2huyo$k6BchK1 z=GTZSU3s?C7+2oZgZ~77;j?#?Z+74_90BNrX|qC^%PJAsHdmJXQ6!={`_t{G0}{1v zNg`W}2#!&|eu*i6w%n_oqOe&?3!$~OU1 zl$A+)WfaNwYi73>&jGv?Hw^cOYy-rjr7yC-K@B?t7``VCuN-^)axa-^{jDo&_}TJm z?@s!=yf{iK_!W{Dg;DW8c%srDC_Af!Bl5{NPTMRG7Uvp5p)@Ax zZi;GpgEpRQnE8n~uJuQ{ro6T&6!GG|jj6h#5Lt~SpcnJCPERDYI^f360}4dyeq87J zS(n}O-tJnUZWOPv$ujS9j}Z~umN=@iwQoccg$4Lhy5vnK{ua3g z_df5IuGl6(31NWQN^h;9gW1eAC$CRV$YIge^r7e8oHIQ=Aeiu%y7%&~VKm zbbj3O)zITm_%@Cq%FP+Ekf-DxL?;{2`NeV9gG6n{M46~3qqcWt)T7(uUjd4<(!hy} zNjmXV3fco^GI4jZb@KH%S9e6yr#d@NhF0gc&-r~*;yW3P(0j9|`Nr7PuGH?-|20f) z$m+t2sw=rxedbT=&mO^mvK-5`8Px-x`}`Z>nTX)EzpF+Kc&c(YX&>32+U*uj_w}s2 zH*<}`P_8+M=;&OwclFbK@l-lb0HX`8A6OxdJ!~G3ZikrEzJ}ML!wuaH|9Y}lGlGqd zN1~T$LM4-hv2nqCnJ@s;EqAL+TY4_a@=7b?@dt|O_F7zL2c6f?5wX(k^WF3RTDo|c zd8vEwOA9h%^_ka6*JBjo@%{IWB({8tcjYJPNRr*2Y~)nqBtwQ=hW6gV44r`*?2FyM z;x-0z?K%ftj~i0!&93yW87)CP2@Fx=Z@1>e;VN6dT@>~3H*Ln{C+bXpX7gjWm!q_z zv~U$jDsMvz*itGjxg_#LobfG5En^fCgJs86x7bkB-w_*yFaFTcN(PZec*bbSnnhVe z{VUdT0JCS5PMDxRqvjR4QUVAe1(C{6#gCO{CsnudMgBzL#4oCh5EH+?os*jK4-Ebr z%u(7=+W)$XrZYn`O4P&t^Nl{KqcSH&k@_=XCL*_UT*v!uaboui_!2n+&FmEG5qpPY zhha%zv@;GDw76&WXkuEwSN*6`%p2~&nWXLV0}Do3z}tAz4{WA|E*2~N_3lso&*>aE zX`#jUY|VZoNvl4ynh?Tn1E1N@+OQ8Ys71G=GA;TM87tuwlHw{b4K)yu}5H&@4|Z>jF&O!@RcyC8c)d_u@}gYo~H zjQ_r{{`T74{QEm4bUw3HlzCMA*Q01z6{vlm&OwN)hJ;PG2yX2ZaKG}eQ?FCwfD%S` z!;sG-Q6b{Rx=y?vsgQ=>TOo>`0Vo66Q_++gEO~Urnee|nk3MSZnsQ_NIj?!wMMdIv z=^8%%^m?<~_trH_);Zzcg*v+gQLVs)+oF)jsl#ow67JLQU<=0xqtfIllf{3YPx*+s#Y&Dc~@!8|z6HXAXxEkBNXE0h=ap2S0 z0MoN)x)f=j4lEad8OrXYn0U}+@Zwr*i>~)N#lUEw>?xZB7wZHaTK3=qSIx>cXK3!{ zf^}o%^&>@H1#8IVy=xK6W`$qD3p?klbrM_K@&Drk<(%+NMp$>qMNx!lbD~g`P!yfA z772>&ZB%iXPU*G#ftv=qByJ8*y__U56LS&FCJ!DsldF>yFMzi*!lLNV)V?VvlC>f^ zilX5Vksjzw%^ja{_`>zE@I99I<%1O0z)WRV($6zvQ{jtix~93UCt=^W%+4cSH59CC zYpYRgfNkiRgTg^9B9@l|=3a`0g^Xw6iy*>HrKR?XI==8T`mPp56^3QVUNYR){u@iJ z=6K@bZpKX41%N4jo1q4RQ%4jpVV7yH=LPX&GIoFb3Q@Hr3wNA(H{u`I_7rB?I1Q=y zabkp4qJ&f1Zp0j);j!4_RQtkSrm`2QjDnIwsG;n5KrdTD2EMN3uhDR2gV}XvFZ#6e zHrG)$zj{6I+TsPPg**PdND;wTRBa?)uXS&9PZx79-4s8%_9XMg5~maZp`NT&;>Tj6 zrP3X1%YOFxsM^&$xwzobIXU3@CS%)gyjUuu`w1)lx(C*cOA99yu$##ht_aiH!s1=u zg*zG6L53wtCjGnP`m#GjEJF%vsdQK5l}sDuTjr)-=&ic6Wyx==Mcy0# z3GUNSB5pLi;u!eeyH$y4Cea#mP#z!rZsya@-A!1`mfQ@TA)}H2M4E$Y&QbTqu{)nA zu5<5UE*}xi%w_&it?JAt?H1RSuMVP+^mip(I1Eu?w@W*Jsk-`CtZ>)W-pslPo^~9M zlwHCSx8+D@!yvvAMs*D$kn8I1tV_okL)7xkD*^%)y z;ifJ%cdgw<3-0fiPF1ZNJ%>ZW$I2nm?q9#)A$y^%n%=jR1H*w3<2T?8GTqyVOGWmC zU%`(vDUFLDThBh+O!!}k8FnD0JCLGHch(m4;>9a3AgA#L0^#=2KCjT|$19wACW5i? zcwe&(y|b_~a_p~&AWcCP;>>;rBMbEL%GRVh7dJSBFaNBqFWy~vdJetIaMK6^jodEW zg_*YM-N4nQc!_2%5PJUXg9~o6_Rlh`Ahz`!GO>`lni1R^Lc=BWkU9--S(b#by6RPKEYLb|95;41tgy#gT-^3d#u{sjF7|9|I zKN0;pg}7kS89A#~BZmX!t?#1cyEc^YY~9VaV?JBSI^KDColTcK$lW+ra+0Dx43Z@V zwIs(tKySPjHK%<$~wWUlIIOsu3TOgcNh%%uEGKzSeL z+<4FT`ND^!K+jDT-79j~>C~}#;vqbt`@@V|9>iv3M{+g|m9+jb0ZO@*#Fe9+QFZ(O z1kbi}GRb*&rk7I{{y67w+m+qB#q3tS+y8~sHlw~GbGca~pn;D)ibtgRS`ywUHrsM+yfshtMunO1qh=N=6B_T&bVzAjmRreok+P>JfI^Ppt;t{k<9uk8Z^fL#I$)veUcm2vfhY~ z-YIq4lwB)FoUWTccp$mqradC_Jn-*o1P zbPqP^PPTnQMQT6n4^FWTG;=d2$7JYTvkowiKZHn>!D7jU#yWbBB_c?*sm20&IS|>w zmOJT!8iF0;a6n#6b7w)p#OAT1R+26uM&mY@J3sIJSjvkMTQ&kuyE|pB9Qw~Q>$O*u ztcT*qJ7rqOrKYcQoWeb}R{cg-gX8_Q{F;gfKjU93Pft*K@*CCye$cKfMJ*pqSd-ZhydSh z{=?@a9;Kay>*d9MLKjzT%?h*{U@sCjrjP1KSz= zGn@Wh@IsdJ1A#v_|=DFncPv~c4tyN15`A+*#gkH>4N_Bj2j{tNoJGB3- ztY(w;WL5tr62eQZiw-S5ZJ+>c6#>3Az4gC)Zjp$uiw-;dqfR8!lu|q>aX_Nc%|!(H zIBs0mo`JIBBDg)3(DK!sytCIwgMV!8#2oXTY|h4fcG*mES#Y87tQbnKZ5{c64$@`y zLr2CMS84fWr^@M$&aQ-prEcW-lv9uyeSE8%ybE$=6&3%g8$+&rAO2T;#-#XWEMQIN zqJYe|uYu#QmW(KZCsbEjc6j z)6gfr_*B5UBHbC5+WzeCRxZin`4va!IYUNXv(c25*T5tpK3$feG!aCKqp+dUHLUn_ z8KEcoW9aGfU~bbLJMFN*)wd(ri9X{sZaPXe1Mf)FM!6gO;8hs&(t~=)*i&tk6+GZ` zxWpk%y(_LlPM6@6s^b{7uBRh?t&3%Ye8NK=v<=}oN9Q)fPY}r-opEwZx`aiW`0-#L z?G!+_;4}=z*Fv70^2{yxQCD03`<|(@vmW-2M*i5)S?(NNywpn5F0-l{nJzZs@Rd3j>^$Q@>K@xB7)A zN}*jmNOp1cvJ+R57@Bl)lIfcA)vILELTht1PZRe@@2uRhbjeFV2jo2cpe|moNxNlo zu4l64A@Q)2*1=0e5=ceM*tre z;S|v(jipnrD6}=9muErZq)@M}3FwlNCDAQ&DED?R@4fLPq@D zWK&sdb7;#zm06TC5#p0~&o@%=C6NibeyvM?`!J&b^i6=UvxdR$6;e5vfY_Z}x2>#I zzOZH?^~gF$(=j*gjwA_9G2J{s+1QR+byySW<5^x>IpV$L^og^&K6>o2DO@|J0Ny>k z#Z4*tXDzgN@{p;iJ9zFHSWuk;YVB?o1 z1nY9ikKUvDU=9StM^6bjD71}gUDX$X*5Gz!6}LwN^+vb(m83GbVb<4t^qFiI;t$#= zMK(R6ck0VCy|gdo8b)q$fyOS@V4OV_!|oe(Hs~L{{AXm<&53p9x+^Z z@%RsO`_o*I4v?~0d|Q+YZq)q>iG$xeH)&R1y2N%Xkkuox_NG6JvL)Qh9cJc zU$*J%c;;V9gyBjo710^bmK|`Jh+mDrjV$hkjv(~N0EPB?hLVxocLM=f#qGgBJqZ=? zGmFyZ;v|P0m)~BF4fCmFV_iq8gL?0-3|0Nt7t5ZWzTcu7A7hP~q>JI4P)_2yn3LN$ z31~=q>sI*8*mS`k;<4%d5&fZC0LD#4CLPWv?4UyzDoyo~N9 zos;kUx0jKB+h4v5s`uEL-?`{(($0MEfPR6$&_;hEyj-G4lX437cWGAd^<~X@4;RXT zQ!I~kwC=|~A?hINca3>XN@_}(DC+y6Ko)#b4|)VEAWoKlj-GUs(m=65{;7_l_b7G0 zovQ(8`DEs+ZwDok%VGM0e#IXREX%2e7c<*7fJgoc02jx+!#`bRDUWIHSRqe&#n~p~ z@S(4%R$S9q#a0b8nGPRjLkQtDERBV+;;8aVKN{c1I~s0~ZMymELtC6V3zQxa~Kj5!TXT9qt~pJPJunIHsBhyr`g9^h(x(PkQtK zYmwbNCISvS`95=TW`kb79+tmb`+m)A|9>vP-8;JU&MJlpVkURb9{R^$&%*k8C;t~X zaU^|!pd7S%VBetC{z`A7THJp!y=DO@d=YlR1}kk!?Sum%nKcKvDJw_RC3?GG&r8VRDrd$=tISb90X=d;kt~ zy4Qa$TgY)bD$r4wzxyXQe~}BjN5FA}?32W(7#V{W#K@1$uQ(WHXn_hQI`SDKz(8nK ze?H3LJ?ER~)2Vk!v%8?J{`DuHa@mK^MXuCN-e1vwFg<}tLL0^dmdG=4sJbpYob2?u zuNtJYV3*3qqH0 zBlN2*QA#-EFt_>V_&0~0=&2!&9fp$;d8gKRTwYOnUc;fq0aaukglI?OIkKbbl16tS zH3;iZ=r$?ep$}>1AHiQd&k9=BO4Kd7wSbs$7}CXbzt?#R5^*MAl^Evpd%+0edP0@1kR;>q z#q>Dx=rxOMW(55^DosIWA_coUJDJ;ysc3zLm#zVN5-S09`-&MFt=;ZLdsJk`H}&j$ z4`7JB?rqBu#Gf_Yh5QCZ3}VPN<#E$HDp}U7T6rCzuRfg{3?8%AT0H`^f5rK6zG^Wd z`$_7VX@n(Xu4HQ}+t|kiAlK;vuS`EBQe(dkWL)0Lmmj+qwM0&|*jV`ajsxO>yT(tc zr4IvmQD!E)RpL;1u@ydVOWJ}sKqQ;yHS3Jqo1C=fm0h@!PH~mbrbHhwWL-pm-)hSS zaapj>+o^(=T{+6kmrG(qbTOab?{*LCN~ET7Gx9+^LahA3dfe5d z?z4wGbotMC39+bPaJ3_#fDjUYuoBih%N6CvT@rD{*>=!^kj^Wc>Z*S&n5~zg?5F-Y z9p{S)f@!R?xgj2*m!eTpWpKw0J+)$td|uff?5~|&T6b_mN!CCRiGK^?sAem|!-KSf zQugk@<$`;-(nV^ge}2;b&})(PVRLw8B~r6hQfh9@*LESZ{H@RZ#a=;PMEd!h>c-kf zV*3X%loN3GiBKTpJ6~D{*YknA2sjP2LBqRs`vLT;3PTLbDX*b-Mc57pawH?I`Quys zKCkCPtWnPxg6i*qUNtd$^k~w>ajBb&Dt#TQXZM4QB0%ZG-&O?!z5{|^hp0qU!B#44F}Joi zz-T9OCwhcB(%AFYA+r7P5+-*lo%Y!Wp7VsW_t4II__D>B140qL!9(a16?zwwJ z&(9u~fR4(S&Ahtwc0l?4>zodkk8=l!zF(=Y99!e>vMLHKg)IYa*7z^GQg+6SSrMO3 z$}C+0FB3ZHvA;dK?q26qCcdf?=c85)zU!&>%aaD3MAE+_$tHK$#mlVqj<{D#?t7n1 z^;myULY(mc8X$5Oi4=>fxI!g0p~k2Ef`afvhSODd4koW7fQ` z)bfaX@0iwU+r-=@Wtu|3A^4G(OFgfpfVHR)y&Z?%>0fOn4T|233QS#B;g$zb$z{R{ z+9nYK%VFm3KTNpK5+td=^7JvhcJ9qmS2fd9xL0|%yD0%M?FBIZQmU6jY7dQ?LzeO2Z&q*)2LrAr7%p; zSJyDh>J$nHqmdDLZavz;=~UjPuc2QE*D-#x=w4Gd7Xi$qLsBnO^3GkB!P(oAgHPti zU;&elR{U_WC%`BBQZ!uVS51B4*>G?@utUVepM+h&sFdku%uZ*m@_7I2*g~;%cHwv6 zGE-akzmRiTN|{I3tIcD!_;S?Dx{SI{;tgVuUIre(z$t;FioBMr^ztgmRoVZt< z@0WUxOLw^WUgzdx>oniq=TZK7aozpXL-5;KnLq@W45!qsN2(IGu9*|SMV}4ov5eQuh)swSfYuAMD@mK?~O{ej#SPA_XTf{A7Mh_GQMd(lx)&KuB8p%SP3-2G-i) zOMEzIoMvRAgrcZbMqd@f>Eq^}_8!~kl~G!PV~m$PJ}LSef}aP4hJD+3x{{G_DXj{I zP-^0K{y?a$wXS!|2bsIH{Hh7DA0CO4W%vm`In!nB0(MvXasLZpz7Z^JZJBVI0dRU*H(?gFvokBr#@L7e_U^C|w*AC9C3b#KVA%zWHWW9L@%bgNV!~`d& zNB@gai^#V#CMLKd|HyEx91B?ZR96aNI47!OG&f?tPN{xh7t-gHUh1?yc_g0vpQ7$w z*Vb3idd2q(1C_*8?P#4Sve^NgSFhjkM5F##^1xnAN`eR$H=Vhph5GIh65$bY^SCrL zy_@emoZBar=&SMsee4=ELeH^>&ra}$&>W@VMEvfgEb({RqGrM{ALfI1Te#BJs)-XZ z{9y`Zt_x?mbPpd!7=7`nK}e*QvWpqR&BEfHEGvKXKi zI+(pYh=!wsn-_WeMI8qQA(KJHVJboff5EU;gVh%dMxA=N+UZh{+fS{ zr!0HrnWu-`(%K!hz}2n}UoCN)s$(d0EAQKCbg-mPxkt#=9)G;T$q!9cr33w<_jLCq z^$PQ)r_cw^IzdE|Cgy>GpxUq=t?D4V1T(zb1l@qaSdK*8r4D}(BL5f#-N?LF{*=)$J==jtz zT;aIzrNz2rLIL16ic0|ouPP5i$7DJ z6Lq8?bpkx&@$gd96|GZ3leH^0{{yb7D>ekRON0<(r0x&pEUob*n(~Br|JL&J3WC|+ zx;(nRiNlR^WAXBg>2nYSrjC=5cHFxMBzOV*#H4rb#5P6i58}lX*EI9Wz~cf7<)A(M z>%~f9iv8ai3bv^4PtQ+?#OR%5MDnu89V+`zL<@V%*S5@YPm-%0vrp4AiLA0J4QB-Z zsVNICLDh|7F>Z*xLB=*&r#>EnY?1Fb%(QWE<@{qxoI}W`D<(nJtlsB_ZkF_s_c$lU z!;3$D1fr>TEbs_f{SkrNAaPjpb0W408EO&o);KI*SgeqoNH6~5=zJv1QbeKT7hSx| z)TGTQ>3$g(I@cMx13p2}Fi zx(%ui>*AAo;eZ|Q{`o!Ss%Go;uH&P-0?CmooZ;>g9taNgXj+U(HGez^XEVXRt}0`6 zOTj$^P@ZnwfF?2et=^(FaQEJBueNE3om%HG34sI@&s6Dw2>< z#Rjk2pUyoScj#6VcO)_#i+uAV6h^S{iiBd;UCxssh26<%X+tHww~H`;#f21(7Q>>~CkueP(^=x0 z;pXp*k&;Ku)PTF8`*qqXV6W&+>J^dOX;YsNirlsb0026KuBxTXB< z9P6%gFKS`t>{oH-luz#&iskf51?|HEK|uzfl`2Lnl6ML31z1&TfyckdqtV34MPM>hl+E+6p489m zW)e`Jwp-EQ1NSxEslG+paZa2d>EmYi(-929!~3}Q@7y@$uE_&kyTp*{5&W}Ra|r{s z!X2jnPD;7W!@AGg+C33~KTW!kYZ&6LJHj1NbPxHeEMasBYvB3oN4QEbuU*P+dqzmY z(--aJyvbun1JnIKv|t+d!}0Kxp{t9u0)Y0Bh$_H)f13JZd6m5`D!?5L{=%wxeDm8D z%w0sLv;x@60yvc17oP5)-hh61dw_<%@KKwzy?{3XeSku2Lh+&4bU1^?j% zh!1rwVS1LtXuZ}qkiJgD0`WBuIS-m#WEtzj(`AF}85u$Fl(3ZQ8W}N{Gno28?^lgW z{gYEY`f*s;GobL*+_VhtO1{|*z4KEU92}UJlxaQLYE@c{6zS56igph&Mvo4bmTK3o zzI-$JvrK{=Uo*GY6zDi~mxR6h=-Hy~uoNH^ohnXr^@D7%w+}pN%#9PggU)^Jo{?-W z69+Z`HQ|arL36ejdg|kueSx8e<&{gujU|da)PE8JpQuv4BVRBiM_MqAOeI~YJP@Q z6XMG4*#W^$r&|$;{&Do=l%RI!y)KS`j(9eeP2>$*YD*a%FSFBEdDjFKi{wSm- z<8y(l=NmOSol8pXrBySQZW)L5bak*S%d*7P({w&R#2W88;mY*EFaI8KiXL^mr3GC1 zI{n$JiB;;J?PH3W{tqulb!nfdXT)SII=l*CgA)O7H^g;t;*AOsC*NFnx^K#1c93XE z$6Q!qs}IwGza=ZY^8dPf@<*t{1wH$xc~9 zJvD_f7$ww0_A++k86*rM``BWPj4c|=V7~W5@B2r5=g0H8=iGD8dheZk&*c-$VXZI3 zHJ@!fWb5>-8&c(4GwI@3a#=C8H4ils8S3XP5RSS+LHy(Brf1TX_pT4kc>b*3v*YKu zVm7w(=N;LAER8b0fh2P9%@=3)Rb5T7ip90JPlw_;*&2OURwBh=FgjRHk1-lbhDG;V z5)0_kZtNv$3P01nX0XA2;$h*U``3+Z=_mXHqC?eMWv1|t{50_IzXtH+VuUa^u9ElO z?g3e6Sx_6`Z=2OjA}4kSY7eyq-dhucTC$F*7`gYYOySUnFK<6)&*~u#fOEp4)h5G3 zrTLVa(&BtGBeYdNhdRH{dLH>~f!4LKX;A2OsuLQh4G|scXhsTd&faJUOH$_Rb0jxi zPx)gw=6-(AzpXAsveTPaI*z-|>Wnem2F~ipn;Kb?SAmOFy;x{%U!XV9CEaGFV+JJ! zVWCEzAOm&Q z2&Ax5$Myxi`VO8{I;Z6{KQU&1YAow>7qRX&AGIm1(LY;1@gpzk81}TrP-IkqV*?Ub zBrN`N2kdVo_a^V;Ub<~<>n`QRo$RcF_+BwMleTc$%}3$;^Y`Bu+K-P-SYI#)Th$-J z#g5Wh!lRbDGbkkWy63#yrHK5FTYu_HJ^l;rkZ-fD?6Y3=oX-yi8@YpWU!8n{R=ZPs zVK>Cx>(yktAGz=d!h+b*5(j|au3j_4yKFpgNC%!+j+(FfdSd zx3Ke~A93m$a$49aPRyy1N)tB-;(>+UN6Z8jkqw8B$OhExKq*19lcJD=D30R!zR--U zEqi}@(3hDpip|EdOOys*=f~jDXFxQo7uV9(UOv1gZ&47T(Fd<@9cm!kGRZ&exhIB3 zESaF(qK#WP51sz{A2w0g7J+(bm_ro{6wee>&JGD#@NA4$UAlmaN_ay22pnC3)*h+Z zJ$VxssdyMwVU_aQJvuR{0>dDgaEx1M)z*V7_oJqJb%z>SgC0XFsa@ZR>gjY}SsEVM zZ^=2#$_CjQkvX(uwmodjUQgbQeGgZ(4;Q)F@ffij63P8wlW91Q4EoxS|HOi+`-N&4 zmdzt@4FWukZLj0U9229z%o@VtTK0@l1Wy*xrQt*1(%txmChL zpD4nCB#nq3k#aI>IR$O}6IjJ6QM;(O47d&b(ZTXN_2#ziWGC#WIz3 z;04lc)_82M^gTD~kN9c0m=)RZsQAu%izf(MM)*eKS5-{=cECdWSgejQfz31AR=Bbq@Cv?qG3~!V zmNVI%nI`GRw#V^uu@&5$R6a{vr`vKSc7oapDJh*b7dAE@n)IFpeHX57K6ogFtJox- zw3R~3&-0QxA8P@YSmE_i%TU( zNPN8oGzm*R5NO_X=@7D^uJ@In^rAN|YAqWj^EIs|sea_b=yQGCguvmY$-j4bDOJTT zE<2PgH% z2GjL?UE;xpp76usSy1{1#@t-a_hE?QR0N~p)jMR$6Czr^6|N3-h-+E1T4uWbvj13M zaNQDZYPD0a>Zn|~_I1bb@As>z&=g3LKDcREPZKH=p6QQgB5}OKl3A|yJ_ls+nTq8X z;p~w)V}~>aoXrjNlhm^Ca^{yu4>B|&bPBDm1BUkt2h3lv6||=9a~a3KB#GRvr^tNa zPdM(wX}z2*U3@)-B6v=8ryyX$aYNJ8+oKYcoT|NktDj?pQyCUQdp+BMax;mOGybL1 z#N55Nx}t=S>!P3c^$5kjU0fTj9o6TJbtJF@kE`bGL7f4$ifZN6yf`sBNuwXmXD4nr zJX89#2@bcXXb?KZZ+^+;Fmz@vsct602l{WZD2RQZuc_40Kv9gF)QJIKAf*g4A)~JJ zQMvn%g9iu_G4I1mYkjib=>0|g_s4`3p@c1jSLb`0(W-T^n4Nwc#Hn&39LQ(0IckP- z<%X^{3DMa?s>?9K2vv?2^4&s8BC*M;<}p9e=P-^=XO|{V<~?6EB$tAYU|OH2hHtaN z0AnMOu}P)^$vp0;qQepN^D`~SOR}4OX?qm+8b-x7RlGnGeyuGE<&5|tfVY1x+i(|h z8WH3Lb8hMI<7DLJAP$f^CLlPvZ+t}J>t|79RuNzDIW+AbF;q-y)8~~ZRS!_YFqpp9 ziC;T$yk-ZSMikvFO4VJ&EM((bD7MKX0a{KYcl#C3!ky4wvE+z0xLqRA$*n6*x)`i> z0jDK6Pfj#`>PUc1@eWiZ<8i8HGx8h&QPZ%ayh5dW?J+GX35SJ{8zx z;2d;qfVqoI=!wCvDxmTNN%ngO#l?kf`%xcgwGuAb7G^2=C{MnA4|r+I@ZQhQG;pxKb|W=Z8L(SFws-w}h;{U;*%OsS#PUU%ktLEo zCvxVBS}+PcbQ{`rrLT(GB)sX0HNkmfQ{2p}4hT+hzB|PBx}L-*A&Xe6QJ6tIyV8L$J|`<0DAn+l8e`RAKUo z*Y`t$$WLVaiuVFxsELv~xvo5J253ZUSX+!HQ5pOd7Qu8BK*l9w$~NXqu5t!8rm0ih z9gWa?yKgUe;Ab3llhRAWQ85lPCy)hv)o%K_(RpThyx2qdwNh1&Ybrm|LogzQhzHr= zm1UULt&;VVV@up~tT2w9u8Rge+TgCeKx~j=jF0O)Wxm4kN-xjtxCfT~Jc*{oa|-31 zK(R1^ zq~~WDQ{|l?bBAgaOF(+q@{aH29k5K{kdJHd$n3;j3`;Am#D`508avYV)TN6qaT-`Eti5}W4u-hz zui(NP#&?6p%2_IHhqG(4Hjutq&H|OUd>>M0xY2BFFiWi!TOlV&`r+TBkEjMEx-Ey8 zULD4JXM>d?%;fu0%?(y{Gj(d%)~GiqWKSFB+ez}mCk{7H=g1>}%>s_w%WvH@+y7*9 z&HERX#X5%z|EUgoki8SaJ&X~G$PB^`-|u%1k666?pqPX0N#m;<6eVwP6_6fl)#*Cm zDV#C%bEeFL8XLaCO2;HyS}9^@MZeiE&njZM zK{q{ZmN%}YY%WiMCm&(3uPwJWwQ+0H^cvyQHdl}pTzMYkS#Tw^fI~YB*Vr(;wK?Pa z*gYj;F&=6yy^|=T8D#D=mqnh_`?xvOw1+K}?f!vna^Oy|0IT}Jm{q|uCL>q-lWC1_ zMDK>FePI1ic?guH<}J?aoMBbxKWdiFs8X)J#jzC4p2N(*rKGT$D`Cvj!9&>Rr|y?> zL_na+;$(wN3M&pkxGXEYbX;LLnC>h*=c*dV*1f^nL`$vR2WQpWSrznXvB(Iqpp4ac z^BB5s+$;%_d&%-f4*mAr$^}mk`DfVH>Qm07T?ZDL!#cVCA08mO-M9f-oQk#CmyTSQ z(E%%fAlc0d%gF5W_5aK5B{9gg?s!xAy}q9sg`B)DU4E&t#0bY>(nk_n6r(g#{ZVX1 z0r9qPxY;)sV4h1b4IrIpt#*&5`zH1VSuzgDecXK7@Fjz>x(s)ro$;EeFhW(FLbnZH zeAQGvIlT#@HB8?-oPLccml#VgeP2anY%V+BPh)&JHiMUtqG=YCbkBM3ljyM?o+g68 z@v)p62vE`kQeK@pQZZMjIi#I`esKEpx0nBZtKBfacr@TH)DXe(IpAUQ~)YYhbg4eVykk^8wCXOiXIQzkAfz3uMyydG@Lp+|Q1;NAUUOvttA;XbP=7t+%Hg|;(ZnFr}2a?j}OM-L`W zvV=fHg0)a*^wb7d99&Ud_#@humSb1vtUD*aiYP4{8%vA0Fs7(?(NZDmSLF!5?J|Ao z9sl&JQAGF1Fl0}?0!|Z_|IYmoUrqSYb`xBln(NXVJqFHd=SRCyc1JS4sN3eV@Qf$Y z|7LPPa~MO?ZyGLdUSdA9+CGp@a&0A5c~V201i*Rh++5cWVAc#KZq{!qip-OP+&cn! zXXtCPXd_S0f}hsa$Nm5x+cjxlP)rhYe+*<1j^!r-46G~6gKx85Q7l!$MZNQQljTA% zN46RZvv)`FBk=59cSm}>FG0i?OTiAMZN5b_^;L}PPW1xa^$`{u=n}$PdrI z>D#T|S-QB@xYJ_uBr*?Um5~!RA(0Ua8Fi9Gz=S5oia-Xpty*8{z1{|r7+-K(4TD(PYhe5!DDG7d`oRMigL?n<^aPbAaZduPDiq52gkaxXupZ1SS z)`|4W+@-*oSpW>{|1KY*y2VmYXlISyEhx6@2Z+dFZN|P()qjd4SuGqaKLLQDY_&vE z9W!<^$Zue;9PM{Y=UnY4iclm>*J+)YRK_`s^*&J z3Y1;u4E9w<{STC|)s6qY-XoO}{{>FOCIMm0fOe1n2hwwd2DpaGtIB`#n;pB=_ggZz z=79(CZOgYLyKw!=Z{XdIylwT)6eddi3rXUwdOLX}xLkHiJn9KmFJt83J#rSs2IGBpF?<+Oj>wsPah+Q>lVaZtwZ!|!KA&m4Yx8DV_;+hCC}qfqQggzF^uBI)blt1(KM++kV4i>53Y_|y zZ#O`K1IR_ot-q0btb7jP&5`+H@4`OX-avodX3DyF8MB3ck;i{q!p>?55)k%urEBf1 zPV$x}CAC_IOp_{A{8JmVFg>5ap_2A3bR!@UDM%uV@50=-M{bk@<`&6Su9kS9fC*dEI$xydId63^jN%yfx*^Id9Oa#Z6%bS@{4VF8y~~*akY7WVHrx>=~47W z6@pbcp-B$kbH6*3&Uv^+6&4hpYD*w5bU$!aSL$=^*pebseJJ3a+QS}Ie}|uIy(ZIN zIS#f>XSM~mc0qA9MlFGNSOpYB9adK=8^_QyA!cBmcbiF4@yAiBE_P#^a{vortkC~T z5sCw~qHrAvCt&)&?x=f&&%S^-3T;fbQu#9eswoGfc*}ShqF{rpSibSq!V`t>xoT$J z^JRR@>4%~*^rg4F;l(KtC4VXJiF6=iI+Q#@`=ATNrjb{)aA{U!k7hvSTNtbX0cNIZ z2}5s!q>R12zCJW3UxntvFx!j{&S@8UI(1z64>+^wIMKm&3Ri9lEpB0qyuu}Pzw9-m zBgj0{w#)zs7QL{Xr(^6Q-)8`>qUj_?JIuVR=z>0^2Z2b3#GB@I=~1M#(juCUyRPJc zoeY@1idur0JYJ_%#UIl??(f;e>%s;TG8f4H^(WOHS|7uk&UWaIN@=}}NITV`0}+!k zvzsa$C2Ap`DU^7dffo8_5^a z0~Ng21{V8=q!#^3OZg0!by1szNb}!NnNG>gG@rk%f6t4BP7SPaCL%l|;si z$#ZrK*D&Jo1nnce(OXIvS6F#k)M1BEaxrzsC=SWT*E@X%7Z@}2p@G;SLBzK&njDfx@?c8f@dgQS+yy zfv0H@d6fi?Gj~1{)0OHt{d4`AW!cO2qSc|!rO=+FfwLaQ5&&-4Ol++6S z;`mw8$XvnC)Hl|`dhYJqR58lcE1%~PG_??pRv}Z7V6iU!{#3uYuNOoV_Q|nw(qVL< zA;x$)+o`U6uVk&S$ymE*77%~8$Nu>_3#8aa+sil6m{ zTmjbq5#)ZA4zCi7Vo{yD-UBm@NwL`3y~8K+Z~AF$#U_}EYJWwd+CyyeP`m~f-)rML zQXiyJHctn^?*~>IB9e|^4bdHnCNoqDPq;EY#}M|O3xBATF}F7FPC`i*cCKP!(fR~_ zPmL6OPb|Q`Nw6x(tSFpkduT3F>rQ?C=W*M&)Gen9onk#U!#9kTHjTgObnv`G!pN7O zLmsAU&GG-BzS%cQ^|Kv)hf!P)i#ruaQlsfvEVn`P8kkJ|ihqTP*TNqAz>aB$pX=P0 zO~xdA)8p*N%7x4dpc&Sb#fk#K!taUN;WQcip5asRi(JEoHncvli}Dc2ME;-sWX^0# XbaT{ga>AQGKtnSz#v0`r*o6NdkJicW literal 0 HcmV?d00001 diff --git a/server/web/dist/assets/index-CtyCxWf5.css b/server/web/dist/assets/index-CtyCxWf5.css new file mode 100644 index 0000000..f89dd51 --- /dev/null +++ b/server/web/dist/assets/index-CtyCxWf5.css @@ -0,0 +1 @@ +.notify-title-text[data-v-6154d8f4]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.app-root[data-v-b2dd3b97]{display:flex;flex-direction:column;min-height:100vh}:root,html[data-theme=light]{color-scheme:light;--c-primary: #4f6ef7;--c-primary-strong: #3d5bf0;--c-primary-soft: rgb(79 110 247 / 10%);--c-danger: #e5484d;--c-danger-soft: rgb(229 72 77 / 10%);--c-success: #2f9e6e;--c-success-soft: rgb(47 158 110 / 12%);--c-warn: #b36f0a;--c-warn-soft: rgb(196 127 23 / 12%);--c-text: #1c2230;--c-text-2: #4d576a;--c-text-3: #7f8ba0;--c-border: rgb(20 32 64 / 10%);--c-bg: #eef1f6;--c-card: #ffffff;--c-input-bg: rgb(255 255 255 / 72%);--glass-bg: rgb(255 255 255 / 52%);--glass-bg-strong: rgb(255 255 255 / 66%);--glass-bg-soft: rgb(255 255 255 / 38%);--glass-border: rgb(255 255 255 / 55%);--glass-inner: rgb(255 255 255 / 65%);--glass-blur: 18px;--glass-saturate: 160%;--ambient: radial-gradient(1200px 640px at 12% -8%, rgb(96 138 255 / 28%), transparent 60%), radial-gradient(1100px 720px at 105% 8%, rgb(167 139 250 / 24%), transparent 55%), radial-gradient(900px 560px at 50% 118%, rgb(64 199 255 / 18%), transparent 60%), linear-gradient(180deg, #f3f6fc 0%, #e8edf7 100%);--ambient-overlay: rgb(255 255 255 / 52%);--bg-tint: rgb(245 247 252 / 72%);--shadow-card: 0 1px 2px rgb(16 24 40 / 5%), 0 16px 40px -20px rgb(16 24 40 / 25%);--shadow-float: 0 24px 60px -24px rgb(16 24 40 / 35%);--radius: 16px;--radius-lg: 20px;--radius-sm: 10px;--nav-h: 62px;--font: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;--mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace}html[data-theme=dark]{color-scheme:dark;--c-primary: #7d95ff;--c-primary-strong: #98abff;--c-primary-soft: rgb(125 149 255 / 14%);--c-danger: #ff6b70;--c-danger-soft: rgb(255 107 112 / 14%);--c-success: #4cc38a;--c-success-soft: rgb(76 195 138 / 14%);--c-warn: #e5a34d;--c-warn-soft: rgb(229 163 77 / 14%);--c-text: #e8ecf6;--c-text-2: #a9b3c6;--c-text-3: #77839b;--c-border: rgb(148 170 220 / 16%);--c-bg: #0b0f1a;--c-card: #131a2b;--c-input-bg: rgb(15 21 36 / 60%);--glass-bg: rgb(19 26 43 / 58%);--glass-bg-strong: rgb(23 31 51 / 74%);--glass-bg-soft: rgb(19 26 43 / 42%);--glass-border: rgb(148 170 220 / 18%);--glass-inner: rgb(255 255 255 / 8%);--glass-blur: 20px;--glass-saturate: 150%;--ambient: radial-gradient(1200px 640px at 12% -8%, rgb(64 98 255 / 26%), transparent 60%), radial-gradient(1100px 720px at 105% 8%, rgb(126 87 252 / 22%), transparent 55%), radial-gradient(900px 560px at 50% 118%, rgb(20 150 200 / 16%), transparent 60%), linear-gradient(180deg, #0c1120 0%, #0a0e19 100%);--ambient-overlay: rgb(8 11 20 / 55%);--bg-tint: rgb(10 14 25 / 68%);--shadow-card: 0 1px 2px rgb(0 0 0 / 30%), 0 18px 44px -22px rgb(0 0 0 / 60%);--shadow-float: 0 26px 64px -26px rgb(0 0 0 / 70%)}*{box-sizing:border-box}html,body{margin:0;padding:0}body{font-family:var(--font);background:var(--c-bg);color:var(--c-text);font-size:15px;line-height:1.6;-webkit-font-smoothing:antialiased;transition:background-color .25s,color .25s}#app{min-height:100vh;display:flex;flex-direction:column}a{color:var(--c-primary);text-decoration:none}a:hover{color:var(--c-primary-strong)}h1,h2,h3,h4{line-height:1.35;margin:0 0 12px}::selection{background:#4f6ef74d}.glass{background:var(--glass-bg);-webkit-backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-saturate));backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-saturate));border:1px solid var(--glass-border);box-shadow:var(--shadow-card),inset 0 1px 0 var(--glass-inner)}.site-nav{position:sticky;top:0;z-index:50;height:var(--nav-h);display:flex;align-items:center;gap:20px;padding:0 clamp(14px,4vw,32px);background:var(--glass-bg-strong);-webkit-backdrop-filter:blur(calc(var(--glass-blur) + 4px)) saturate(var(--glass-saturate));backdrop-filter:blur(calc(var(--glass-blur) + 4px)) saturate(var(--glass-saturate));border-bottom:1px solid var(--glass-border);box-shadow:0 1px 0 var(--glass-inner)}.brand{display:flex;align-items:center;gap:10px;font-weight:700;font-size:17px;color:var(--c-text);min-width:0}.brand:hover{color:var(--c-text)}.brand img{width:30px;height:30px;object-fit:contain;flex:none;filter:drop-shadow(0 2px 6px rgb(16 24 40 / 20%))}.brand .brand-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.nav-links{display:flex;align-items:center;gap:4px;margin-left:auto}.nav-links a{padding:7px 13px;border-radius:999px;color:var(--c-text-2);font-size:14px;white-space:nowrap}.nav-links a:hover{background:var(--c-primary-soft);color:var(--c-primary-strong)}.nav-links a.router-link-active{background:var(--c-primary-soft);color:var(--c-primary-strong);font-weight:600}.nav-controls{display:flex;align-items:center;gap:8px;margin-left:8px}.nav-control{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--c-border);background:var(--glass-bg-soft);color:var(--c-text-2);font:inherit;font-size:13px;font-weight:600;padding:5px 10px;border-radius:999px;cursor:pointer;white-space:nowrap;transition:background .15s,color .15s,border-color .15s}.nav-control:hover{color:var(--c-primary-strong);border-color:var(--c-primary-soft);background:var(--c-primary-soft)}.nav-control .nav-control-icon{font-size:14px;line-height:1}.theme-seg{display:inline-flex;padding:3px;gap:2px;border:1px solid var(--c-border);border-radius:999px;background:var(--glass-bg-soft)}.theme-seg button{border:none;background:transparent;font-size:13px;line-height:1;padding:5px 9px;border-radius:999px;cursor:pointer;color:var(--c-text-3);transition:background .15s,color .15s}.theme-seg button:hover{color:var(--c-primary-strong)}.theme-seg button.active{background:var(--c-primary-soft);color:var(--c-primary-strong);box-shadow:0 1px 4px #1018281f}.page{flex:1;width:100%;max-width:1080px;margin:0 auto;padding:28px clamp(14px,4vw,32px) 48px}.page-wide{max-width:1280px}.site-footer{border-top:1px solid var(--glass-border);background:var(--glass-bg-soft);-webkit-backdrop-filter:blur(var(--glass-blur));backdrop-filter:blur(var(--glass-blur));padding:16px clamp(14px,4vw,32px);color:var(--c-text-3);font-size:13px;display:flex;flex-wrap:wrap;gap:6px 20px;align-items:center;justify-content:space-between}.site-footer .footer-text{white-space:pre-line;word-break:break-word}.site-footer .footer-beian{font-size:12px;opacity:.9}.site-footer nav{display:flex;gap:14px;align-items:center;font-size:12.5px}.site-footer nav a{color:var(--c-text-3)}.site-footer nav a:hover{color:var(--c-primary-strong)}.card{background:var(--glass-bg);-webkit-backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-saturate));backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-saturate));border:1px solid var(--glass-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-card),inset 0 1px 0 var(--glass-inner);padding:22px}.card-title{font-size:17px;font-weight:700;margin:0 0 4px}.card-sub{color:var(--c-text-3);font-size:13px;margin:0 0 16px}.field{margin-bottom:16px}.field>label{display:block;font-size:13px;font-weight:600;color:var(--c-text-2);margin-bottom:6px}.input,.textarea,.select{width:100%;border:1px solid var(--c-border);border-radius:var(--radius-sm);background:var(--c-input-bg);color:var(--c-text);font:inherit;padding:9px 12px;outline:none;transition:border-color .15s,box-shadow .15s,background .15s}.input::placeholder,.textarea::placeholder{color:var(--c-text-3)}.input:focus,.textarea:focus,.select:focus{border-color:var(--c-primary);box-shadow:0 0 0 3px #4f6ef72e}.textarea{min-height:140px;resize:vertical;font-family:inherit}.select{appearance:auto}.select option{background:var(--c-card);color:var(--c-text)}.input-mono{font-family:var(--mono);letter-spacing:.5px}.field-row{display:flex;gap:10px;flex-wrap:wrap}.field-row>*{flex:1;min-width:120px}.hint{color:var(--c-text-3);font-size:12.5px;margin-top:5px}.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:1px solid transparent;border-radius:var(--radius-sm);padding:9px 18px;font:inherit;font-weight:600;font-size:14px;cursor:pointer;background:var(--c-primary);color:#fff;box-shadow:0 8px 20px -10px #4f6ef7b3;transition:background .15s,opacity .15s,transform .05s,box-shadow .15s;-webkit-user-select:none;user-select:none;white-space:nowrap}.btn:hover{background:var(--c-primary-strong)}.btn:active{transform:translateY(1px)}.btn:disabled{opacity:.55;cursor:not-allowed}.btn-ghost{background:var(--glass-bg-soft);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:var(--c-text-2);border-color:var(--glass-border);box-shadow:inset 0 1px 0 var(--glass-inner)}.btn-ghost:hover{background:var(--c-primary-soft);color:var(--c-primary-strong);border-color:var(--c-primary-soft)}.btn-danger{background:var(--c-danger)}.btn-danger:hover{filter:brightness(.94)}.btn-danger-ghost{background:var(--glass-bg-soft);color:var(--c-danger);border-color:#e5484d59}.btn-danger-ghost:hover{background:var(--c-danger-soft)}.btn-sm{padding:5px 11px;font-size:13px;border-radius:8px}.btn-block{width:100%}.tabs{display:flex;gap:6px;background:var(--glass-bg-soft);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid var(--glass-border);border-radius:12px;padding:4px;width:fit-content;max-width:100%}.tab{border:none;background:transparent;font:inherit;font-weight:600;font-size:14px;color:var(--c-text-2);padding:7px 18px;border-radius:9px;cursor:pointer;transition:background .15s,color .15s}.tab.active{background:var(--glass-bg-strong);color:var(--c-primary-strong);box-shadow:0 1px 6px #10182824,inset 0 1px 0 var(--glass-inner)}.badge{display:inline-flex;align-items:center;gap:4px;border-radius:999px;font-size:12px;font-weight:600;padding:2px 10px;background:var(--c-primary-soft);color:var(--c-primary-strong);white-space:nowrap}.badge-success{background:var(--c-success-soft);color:var(--c-success)}.badge-danger{background:var(--c-danger-soft);color:var(--c-danger)}.badge-warn{background:var(--c-warn-soft);color:var(--c-warn)}.badge-muted{background:var(--glass-bg-soft);color:var(--c-text-2);border:1px solid var(--c-border)}.table-wrap{overflow-x:auto;border:1px solid var(--glass-border);border-radius:var(--radius);background:var(--glass-bg-strong);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);will-change:scroll-position}table.table{width:100%;border-collapse:collapse;font-size:13.5px;min-width:760px}.table th,.table td{text-align:left;padding:10px 14px;border-bottom:1px solid var(--c-border);white-space:nowrap}.table th{background:var(--glass-bg-strong);color:var(--c-text-2);font-weight:600;font-size:12.5px;position:sticky;top:0;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.table tbody tr:hover{background:var(--c-primary-soft)}.table td.wrap{white-space:normal;word-break:break-all;min-width:160px}.table .code-cell{font-family:var(--mono);font-weight:600;color:var(--c-primary-strong)}.pager{display:flex;align-items:center;justify-content:flex-end;gap:10px;margin-top:14px;font-size:13px;color:var(--c-text-2);flex-wrap:wrap}.pager .pager-info{color:var(--c-text-3)}.progress{height:8px;background:var(--glass-bg-soft);border:1px solid var(--c-border);border-radius:999px;overflow:hidden}.progress>i{display:block;height:100%;background:linear-gradient(90deg,var(--c-primary),#7a8ffb,#a78bfa);border-radius:999px;transition:width .2s}.code-display{font-family:var(--mono);font-size:clamp(26px,6vw,38px);font-weight:700;letter-spacing:4px;color:var(--c-primary-strong);background:var(--c-primary-soft);border:1px dashed rgb(79 110 247 / 40%);border-radius:var(--radius);padding:14px 18px;text-align:center;-webkit-user-select:all;user-select:all;word-break:break-all}.notice{display:flex;gap:10px;align-items:flex-start;background:var(--c-warn-soft);color:var(--c-warn);border:1px solid rgb(196 127 23 / 25%);border-radius:var(--radius-sm);padding:10px 14px;font-size:13.5px}.notice-info{background:var(--c-primary-soft);color:var(--c-primary-strong);border-color:#4f6ef740}.modal-overlay{position:fixed;inset:0;background:#080c1873;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);display:flex;align-items:flex-start;justify-content:center;padding:8vh 16px 16px;z-index:100;overflow-y:auto}.modal{background:var(--glass-bg-strong);-webkit-backdrop-filter:blur(calc(var(--glass-blur) + 6px)) saturate(var(--glass-saturate));backdrop-filter:blur(calc(var(--glass-blur) + 6px)) saturate(var(--glass-saturate));border:1px solid var(--glass-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-float),inset 0 1px 0 var(--glass-inner);width:100%;max-width:520px;padding:22px}.modal-title{font-size:16px;font-weight:700;margin:0 0 16px}.modal-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:18px}.toast-host{position:fixed;top:14px;left:50%;transform:translate(-50%);z-index:200;display:flex;flex-direction:column;gap:8px;width:min(92vw,420px);pointer-events:none}.toast{background:var(--glass-bg-strong);-webkit-backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-saturate));backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-saturate));color:var(--c-text);border:1px solid var(--glass-border);border-radius:12px;padding:10px 16px;font-size:13.5px;box-shadow:var(--shadow-float);display:flex;gap:8px;align-items:center;animation:toast-in .18s ease-out;pointer-events:auto}.toast-success{border-color:#4cc38a73}.toast-success .toast-icon{color:var(--c-success)}.toast-error{border-color:#ff6b7073}.toast-error .toast-icon{color:var(--c-danger)}@keyframes toast-in{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.notify-pop{position:fixed;top:calc(var(--nav-h) + 14px);right:16px;z-index:90;width:min(92vw,340px);background:var(--glass-bg-strong);-webkit-backdrop-filter:blur(calc(var(--glass-blur) + 6px)) saturate(var(--glass-saturate));backdrop-filter:blur(calc(var(--glass-blur) + 6px)) saturate(var(--glass-saturate));border:1px solid var(--glass-border);border-radius:var(--radius);box-shadow:var(--shadow-float),inset 0 1px 0 var(--glass-inner);padding:14px 16px;animation:notify-in .22s ease-out}@keyframes notify-in{0%{opacity:0;transform:translate(14px)}to{opacity:1;transform:translate(0)}}.notify-pop .notify-head{display:flex;align-items:center;gap:8px;font-weight:700;font-size:14px;margin-bottom:6px}.notify-pop .notify-content{font-size:13px;color:var(--c-text-2);word-break:break-word;max-height:40vh;overflow-y:auto;will-change:scroll-position}.notify-pop .notify-content a{text-decoration:underline}.notify-pop .notify-close{margin-left:auto;border:none;background:transparent;color:var(--c-text-3);font-size:15px;cursor:pointer;padding:2px 4px;border-radius:6px}.notify-pop .notify-close:hover{color:var(--c-primary-strong);background:var(--c-primary-soft)}.admin-shell{display:flex;min-height:calc(100vh - var(--nav-h));max-width:1280px;width:100%;margin:0 auto}.admin-aside{width:200px;flex:none;padding:20px 12px;border-right:1px solid var(--c-border)}.admin-aside .aside-title{font-size:12.5px;color:var(--c-text-3);font-weight:600;padding:0 10px;margin-bottom:8px;letter-spacing:1px}.aside-menu{display:flex;flex-direction:column;gap:2px}.aside-menu a{display:flex;align-items:center;gap:8px;padding:9px 12px;border-radius:var(--radius-sm);color:var(--c-text-2);font-size:14px;font-weight:500}.aside-menu a:hover{background:var(--c-primary-soft);color:var(--c-primary-strong)}.aside-menu a.router-link-active{background:var(--c-primary-soft);color:var(--c-primary-strong);font-weight:600}.aside-menu .aside-sep{height:1px;background:var(--c-border);margin:10px 6px}.admin-main{flex:1;min-width:0;padding:22px clamp(14px,3vw,28px)}.docs-shell{display:flex;gap:24px;align-items:flex-start}.docs-sidebar{width:240px;flex:none;position:sticky;top:calc(var(--nav-h) + 16px);max-height:calc(100vh - var(--nav-h) - 32px);overflow-y:auto;will-change:scroll-position}.docs-sidebar .docs-search{margin-bottom:12px}.doc-list{display:flex;flex-direction:column;gap:2px}.doc-list a{display:block;padding:7px 11px;border-radius:var(--radius-sm);color:var(--c-text-2);font-size:13.5px}.doc-list a:hover{background:var(--c-primary-soft)}.doc-list a.active{background:var(--c-primary-soft);color:var(--c-primary-strong);font-weight:600}.doc-toc{margin-top:16px;border-top:1px solid var(--c-border);padding-top:12px}.doc-toc .toc-title{font-size:12px;color:var(--c-text-3);font-weight:600;margin-bottom:6px}.doc-toc a{display:block;padding:3px 11px;font-size:12.5px;color:var(--c-text-3);border-left:2px solid transparent}.doc-toc a:hover{color:var(--c-primary-strong)}.doc-toc a.lvl-3{padding-left:24px}.docs-content{flex:1;min-width:0;padding:clamp(18px,4vw,36px)}.markdown-body{font-size:14.5px;color:var(--c-text);word-break:break-word}.markdown-body h1{font-size:24px;border-bottom:1px solid var(--c-border);padding-bottom:10px}.markdown-body h2{font-size:19px;margin-top:30px}.markdown-body h3{font-size:16px;margin-top:22px}.markdown-body p{margin:10px 0}.markdown-body ul,.markdown-body ol{padding-left:22px;margin:8px 0}.markdown-body code{font-family:var(--mono);font-size:.88em;background:var(--c-primary-soft);border-radius:5px;padding:1.5px 6px;color:var(--c-primary-strong)}.markdown-body pre{background:#0a0f1ee0;color:#dbe4ff;border:1px solid var(--c-border);border-radius:var(--radius-sm);padding:14px 16px;overflow-x:auto;font-size:13px;line-height:1.55}.markdown-body pre code{background:transparent;color:inherit;padding:0;font-size:inherit}.markdown-body table{border-collapse:collapse;width:100%;margin:12px 0;font-size:13px}.markdown-body th,.markdown-body td{border:1px solid var(--c-border);padding:7px 11px;text-align:left}.markdown-body th{background:var(--glass-bg-soft)}.markdown-body blockquote{margin:12px 0;padding:8px 16px;border-left:3px solid var(--c-primary);background:var(--c-primary-soft);border-radius:0 var(--radius-sm) var(--radius-sm) 0;color:var(--c-text-2)}.markdown-body a{word-break:break-all}.openapi-wrap .swagger-ui{font-family:var(--font)}.openapi-wrap .swagger-ui .topbar{display:none}.openapi-wrap .swagger-ui .info .title{font-size:22px}.empty{text-align:center;color:var(--c-text-3);padding:48px 16px;font-size:14px}.empty .empty-icon{font-size:34px;margin-bottom:8px}.spin{display:inline-block;width:18px;height:18px;border:2px solid rgb(79 110 247 / 25%);border-top-color:var(--c-primary);border-radius:50%;animation:spin .7s linear infinite;vertical-align:-4px}@keyframes spin{to{transform:rotate(360deg)}}.loading-block{text-align:center;color:var(--c-text-3);padding:40px 0;font-size:14px}.dropzone{border:1.5px dashed var(--c-border);border-radius:var(--radius);background:var(--glass-bg-soft);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);padding:34px 16px;text-align:center;color:var(--c-text-2);cursor:pointer;transition:border-color .15s,background .15s}.dropzone:hover,.dropzone.dragover{border-color:var(--c-primary);background:var(--c-primary-soft)}.dropzone .dz-icon{font-size:30px;margin-bottom:6px}.dropzone .dz-main{font-weight:600;color:var(--c-text)}.dropzone .dz-sub{font-size:12.5px;color:var(--c-text-3);margin-top:3px}.file-chip{display:flex;align-items:center;gap:10px;background:var(--c-primary-soft);border:1px solid rgb(79 110 247 / 25%);border-radius:var(--radius-sm);padding:10px 14px;font-size:14px}.file-chip .fc-name{font-weight:600;word-break:break-all}.file-chip .fc-size{color:var(--c-text-3);font-size:12.5px;flex:none}.file-chip .fc-remove{margin-left:auto;cursor:pointer;color:var(--c-text-3);border:none;background:none;font-size:16px;padding:2px 6px}.file-chip .fc-remove:hover{color:var(--c-danger)}.logo-preview-row{display:flex;gap:14px;align-items:center;flex-wrap:wrap}.logo-input-row{display:flex;gap:8px}.logo-input-row .input{flex:1}.logo-box{width:64px;height:64px;border:1px solid var(--c-border);border-radius:var(--radius-sm);background:linear-gradient(45deg,var(--glass-bg-soft) 25%,transparent 25%,transparent 75%,var(--glass-bg-soft) 75%),linear-gradient(45deg,var(--glass-bg-soft) 25%,transparent 25%,transparent 75%,var(--glass-bg-soft) 75%);background-size:12px 12px;background-position:0 0,6px 6px;display:flex;align-items:center;justify-content:center;overflow:hidden}.logo-box img{max-width:52px;max-height:52px;object-fit:contain}.hero{text-align:center;padding:26px 0 8px}.hero h1{font-size:clamp(22px,4.5vw,30px);margin-bottom:6px}.hero p{color:var(--c-text-2);margin:0 auto;max-width:560px;font-size:14.5px}.quick-pickup{display:flex;gap:10px;max-width:460px;margin:18px auto 0}.quick-pickup .input{font-family:var(--mono);letter-spacing:2px;text-transform:uppercase}.share-card{max-width:640px;margin:22px auto 0}.text-view{background:var(--glass-bg-soft);border:1px solid var(--c-border);border-radius:var(--radius-sm);padding:14px 16px;font-family:var(--mono);font-size:13px;white-space:pre-wrap;word-break:break-word;max-height:50vh;overflow-y:auto;margin:0;will-change:scroll-position}.file-summary{display:flex;gap:14px;align-items:center;background:var(--glass-bg-soft);border:1px solid var(--c-border);border-radius:var(--radius-sm);padding:14px 16px}.toolbar{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:16px}.page-title{font-size:19px;margin:0}.page-sub{color:var(--c-text-3);font-size:13px;margin:2px 0 0}.toolbar-actions{display:flex;gap:8px;flex-wrap:wrap}.row-actions{display:flex;gap:6px;flex-wrap:wrap}.app-ambient,.app-bg,.app-bg-tint{position:fixed;inset:0;z-index:-1;pointer-events:none}.app-ambient{background:var(--ambient);will-change:transform}.app-bg{background-size:cover;background-position:center;will-change:transform}.app-bg-tint{background:var(--bg-tint);-webkit-backdrop-filter:none;backdrop-filter:none}@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){.site-nav,.card,.modal,.toast,.notify-pop,.table-wrap,.tabs,.dropzone,.btn-ghost,.site-footer{background:var(--c-card);-webkit-backdrop-filter:none;backdrop-filter:none}.btn-ghost,.tabs,.dropzone,.site-footer{background:var(--c-bg)}}@media(prefers-reduced-transparency:reduce){.site-nav,.card,.modal,.toast,.notify-pop,.table-wrap,.tabs,.dropzone,.btn-ghost,.site-footer{-webkit-backdrop-filter:none;backdrop-filter:none;background:var(--c-card);border-color:var(--c-border)}.app-bg-tint{background:var(--c-card)}}@media(prefers-reduced-motion:reduce){.toast,.notify-pop{animation:none}.btn:active{transform:none}}@media(max-width:860px){.docs-shell{flex-direction:column}.docs-sidebar{position:static;width:100%;max-height:none}.doc-list{flex-direction:row;flex-wrap:wrap;gap:6px}.doc-list a{border:1px solid var(--c-border);background:var(--glass-bg-soft)}.doc-toc{display:none}}@media(max-width:720px){.site-nav{gap:10px}.nav-links a{padding:6px 9px;font-size:13px}.site-nav,.card,.modal,.notify-pop,.table-wrap{-webkit-backdrop-filter:none;backdrop-filter:none;background:var(--c-card)}.admin-shell{flex-direction:column}.admin-aside{width:100%;border-right:none;border-bottom:1px solid var(--c-border);padding:10px 12px}.aside-menu{flex-direction:row;overflow-x:auto;gap:4px}.aside-menu a{white-space:nowrap;padding:7px 11px}.aside-menu .aside-sep{width:1px;height:auto;margin:0 4px}.admin-main{padding:16px 14px}.page{padding-top:18px}.card{padding:16px}.quick-pickup{flex-direction:column}.pager{justify-content:center}}@media(max-width:420px){.brand .brand-name{font-size:15px}.table{min-width:640px}.theme-seg button{padding:5px 7px}} diff --git a/server/web/dist/assets/index-DYsKpclu.js b/server/web/dist/assets/index-DYsKpclu.js new file mode 100644 index 0000000..9461f57 --- /dev/null +++ b/server/web/dist/assets/index-DYsKpclu.js @@ -0,0 +1,28 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/HomeView-D38U_AAJ.js","assets/PageShell-D87JkPkG.js","assets/SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js","assets/PageShell-PYQzNiuf.css","assets/share-CqDDLJWI.js","assets/HomeView-DcN_X0wH.css","assets/PickupView-ClAAhXsM.js","assets/LoginView-BnVXNxFO.js","assets/auth-B7MDTgxJ.js","assets/admin-DEOkRyTC.js","assets/LoginView-BgHqRIwi.css","assets/AdminLayout-DJ2FR9f-.js","assets/FilesView-ZJ5eqPp5.js","assets/Pager.vue_vue_type_script_setup_true_lang-B3GeH0mY.js","assets/AuditView-BHN4e5xt.js","assets/AuditView-f2PBwbQe.css","assets/SettingsView-CjSvbH8z.js","assets/SettingsView-GpXVMuCV.css","assets/DocsView-Ds78oqBY.js","assets/docsSource-BXkkn0HE.js","assets/markdown-B5D8JARp.js","assets/OpenApiView-4SbIV1Bx.js","assets/swagger-CqkleIqs.js","assets/OpenApiView-BCH8BgeP.css","assets/NotFoundView-CbfqSXyJ.js"])))=>i.map(i=>d[i]); +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))r(n);new MutationObserver(n=>{for(const i of n)if(i.type==="childList")for(const l of i.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&r(l)}).observe(document,{childList:!0,subtree:!0});function o(n){const i={};return n.integrity&&(i.integrity=n.integrity),n.referrerPolicy&&(i.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?i.credentials="include":n.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function r(n){if(n.ep)return;n.ep=!0;const i=o(n);fetch(n.href,i)}})();function $l(e){const t=Object.create(null);for(const o of e.split(","))t[o]=1;return o=>o in t}const Se={},dr=[],Gt=()=>{},Fc=()=>!1,Jn=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Qn=e=>e.startsWith("onUpdate:"),Be=Object.assign,Bl=(e,t)=>{const o=e.indexOf(t);o>-1&&e.splice(o,1)},zd=Object.prototype.hasOwnProperty,_e=(e,t)=>zd.call(e,t),oe=Array.isArray,To=e=>un(e)==="[object Map]",er=e=>un(e)==="[object Set]",Ta=e=>un(e)==="[object Date]",le=e=>typeof e=="function",Le=e=>typeof e=="string",yt=e=>typeof e=="symbol",be=e=>e!==null&&typeof e=="object",Oc=e=>(be(e)||le(e))&&le(e.then)&&le(e.catch),Nc=Object.prototype.toString,un=e=>Nc.call(e),Ud=e=>un(e).slice(8,-1),Mc=e=>un(e)==="[object Object]",Zn=e=>Le(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Hr=$l(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),ei=e=>{const t=Object.create(null);return(o=>t[o]||(t[o]=e(o)))},Vd=/-\w/g,ct=ei(e=>e.replace(Vd,t=>t.slice(1).toUpperCase())),jd=/\B([A-Z])/g,Ro=ei(e=>e.replace(jd,"-$1").toLowerCase()),ti=ei(e=>e.charAt(0).toUpperCase()+e.slice(1)),Di=ei(e=>e?`on${ti(e)}`:""),Vt=(e,t)=>!Object.is(e,t),In=(e,...t)=>{for(let o=0;o{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:r,value:o})},oi=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Gd=e=>{const t=Le(e)?Number(e):NaN;return isNaN(t)?e:t};let Pa;const ri=()=>Pa||(Pa=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function ni(e){if(oe(e)){const t={};for(let o=0;o{if(o){const r=o.split(Yd);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function ii(e){let t="";if(Le(e))t=e;else if(oe(e))for(let o=0;oPo(o,t))}const $c=e=>!!(e&&e.__v_isRef===!0),Dn=e=>Le(e)?e:e==null?"":oe(e)||be(e)&&(e.toString===Nc||!le(e.toString))?$c(e)?Dn(e.value):JSON.stringify(e,Bc,2):String(e),Bc=(e,t)=>$c(t)?Bc(e,t.value):To(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((o,[r,n],i)=>(o[Ri(r,i)+" =>"]=n,o),{})}:er(t)?{[`Set(${t.size})`]:[...t.values()].map(o=>Ri(o))}:yt(t)?Ri(t):be(t)&&!oe(t)&&!Mc(t)?String(t):t,Ri=(e,t="")=>{var o;return yt(e)?`Symbol(${(o=e.description)!=null?o:t})`:e};let He;class Wc{constructor(t=!1){this.detached=t,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this._warnOnRun=!0,this.__v_skip=!0,!t&&He&&(He.active?(this.parent=He,this.index=(He.scopes||(He.scopes=[])).push(this)-1):(this._active=!1,this._warnOnRun=!1))}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let t,o;if(this.scopes){const r=this.scopes.slice();for(t=0,o=r.length;t0&&--this._on===0){if(He===this)He=this.prevScope;else{let t=He;for(;t;){if(t.prevScope===this){t.prevScope=this.prevScope;break}t=t.prevScope}}this.prevScope=void 0}}stop(t){if(this._active){this._active=!1;let o,r;for(o=0,r=this.effects.length;o0)return;if(Br){let t=Br;for(Br=void 0;t;){const o=t.next;t.next=void 0,t.flags&=-9,t=o}}let e;for(;$r;){let t=$r;for($r=void 0;t;){const o=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(r){e||(e=r)}t=o}}if(e)throw e}function Gc(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function Kc(e){let t,o=e.depsTail,r=o;for(;r;){const n=r.prevDep;r.version===-1?(r===o&&(o=n),Vl(r),op(r)):t=r,r.dep.activeLink=r.prevActiveLink,r.prevActiveLink=void 0,r=n}e.deps=t,e.depsTail=o}function rl(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(Yc(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function Yc(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===Kr)||(e.globalVersion=Kr,!e.isSSR&&e.flags&128&&(!e.deps&&!e._dirty||!rl(e))))return;e.flags|=2;const t=e.dep,o=Te,r=Mt;Te=e,Mt=!0;try{Gc(e);const n=e.fn(e._value);(t.version===0||Vt(n,e._value))&&(e.flags|=128,e._value=n,t.version++)}catch(n){throw t.version++,n}finally{Te=o,Mt=r,Kc(e),e.flags&=-3}}function Vl(e,t=!1){const{dep:o,prevSub:r,nextSub:n}=e;if(r&&(r.nextSub=n,e.prevSub=void 0),n&&(n.prevSub=r,e.nextSub=void 0),o.subs===e&&(o.subs=r,!r&&o.computed)){o.computed.flags&=-5;for(let i=o.computed.deps;i;i=i.nextDep)Vl(i,!0)}!t&&!--o.sc&&o.map&&o.map.delete(o.key)}function op(e){const{prevDep:t,nextDep:o}=e;t&&(t.nextDep=o,e.prevDep=void 0),o&&(o.prevDep=t,e.nextDep=void 0)}let Mt=!0;const qc=[];function so(){qc.push(Mt),Mt=!1}function co(){const e=qc.pop();Mt=e===void 0?!0:e}function Aa(e){const{cleanup:t}=e;if(e.cleanup=void 0,t){const o=Te;Te=void 0;try{t()}finally{Te=o}}}let Kr=0;class rp{constructor(t,o){this.sub=t,this.dep=o,this.version=o.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class jl{constructor(t){this.computed=t,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(t){if(!Te||!Mt||Te===this.computed)return;let o=this.activeLink;if(o===void 0||o.sub!==Te)o=this.activeLink=new rp(Te,this),Te.deps?(o.prevDep=Te.depsTail,Te.depsTail.nextDep=o,Te.depsTail=o):Te.deps=Te.depsTail=o,Xc(o);else if(o.version===-1&&(o.version=this.version,o.nextDep)){const r=o.nextDep;r.prevDep=o.prevDep,o.prevDep&&(o.prevDep.nextDep=r),o.prevDep=Te.depsTail,o.nextDep=void 0,Te.depsTail.nextDep=o,Te.depsTail=o,Te.deps===o&&(Te.deps=r)}return o}trigger(t){this.version++,Kr++,this.notify(t)}notify(t){zl();try{for(let o=this.subs;o;o=o.prevSub)o.sub.notify()&&o.sub.dep.notify()}finally{Ul()}}}function Xc(e){if(e.dep.sc++,e.sub.flags&4){const t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let r=t.deps;r;r=r.nextDep)Xc(r)}const o=e.dep.subs;o!==e&&(e.prevSub=o,o&&(o.nextSub=e)),e.dep.subs=e}}const Rn=new WeakMap,Jo=Symbol(""),nl=Symbol(""),Yr=Symbol("");function Ye(e,t,o){if(Mt&&Te){let r=Rn.get(e);r||Rn.set(e,r=new Map);let n=r.get(o);n||(r.set(o,n=new jl),n.map=r,n.key=o),n.track()}}function ro(e,t,o,r,n,i){const l=Rn.get(e);if(!l){Kr++;return}const a=s=>{s&&s.trigger()};if(zl(),t==="clear")l.forEach(a);else{const s=oe(e),c=s&&Zn(o);if(s&&o==="length"){const u=Number(r);l.forEach((f,d)=>{(d==="length"||d===Yr||!yt(d)&&d>=u)&&a(f)})}else switch((o!==void 0||l.has(void 0))&&a(l.get(o)),c&&a(l.get(Yr)),t){case"add":s?c&&a(l.get("length")):(a(l.get(Jo)),To(e)&&a(l.get(nl)));break;case"delete":s||(a(l.get(Jo)),To(e)&&a(l.get(nl)));break;case"set":To(e)&&a(l.get(Jo));break}}Ul()}function np(e,t){const o=Rn.get(e);return o&&o.get(t)}function ir(e){const t=ge(e);return t===e?t:(Ye(t,"iterate",Yr),vt(e)?t:t.map(kt))}function li(e){return Ye(e=ge(e),"iterate",Yr),e}function zt(e,t){return uo(e)?gr(lo(e)?kt(t):t):kt(t)}const ip={__proto__:null,[Symbol.iterator](){return Oi(this,Symbol.iterator,e=>zt(this,e))},concat(...e){return ir(this).concat(...e.map(t=>oe(t)?ir(t):t))},entries(){return Oi(this,"entries",e=>(e[1]=zt(this,e[1]),e))},every(e,t){return Xt(this,"every",e,t,void 0,arguments)},filter(e,t){return Xt(this,"filter",e,t,o=>o.map(r=>zt(this,r)),arguments)},find(e,t){return Xt(this,"find",e,t,o=>zt(this,o),arguments)},findIndex(e,t){return Xt(this,"findIndex",e,t,void 0,arguments)},findLast(e,t){return Xt(this,"findLast",e,t,o=>zt(this,o),arguments)},findLastIndex(e,t){return Xt(this,"findLastIndex",e,t,void 0,arguments)},forEach(e,t){return Xt(this,"forEach",e,t,void 0,arguments)},includes(...e){return Ni(this,"includes",e)},indexOf(...e){return Ni(this,"indexOf",e)},join(e){return ir(this).join(e)},lastIndexOf(...e){return Ni(this,"lastIndexOf",e)},map(e,t){return Xt(this,"map",e,t,void 0,arguments)},pop(){return Ar(this,"pop")},push(...e){return Ar(this,"push",e)},reduce(e,...t){return La(this,"reduce",e,t)},reduceRight(e,...t){return La(this,"reduceRight",e,t)},shift(){return Ar(this,"shift")},some(e,t){return Xt(this,"some",e,t,void 0,arguments)},splice(...e){return Ar(this,"splice",e)},toReversed(){return ir(this).toReversed()},toSorted(e){return ir(this).toSorted(e)},toSpliced(...e){return ir(this).toSpliced(...e)},unshift(...e){return Ar(this,"unshift",e)},values(){return Oi(this,"values",e=>zt(this,e))}};function Oi(e,t,o){const r=li(e),n=r[t]();return r!==e&&!vt(e)&&(n._next=n.next,n.next=()=>{const i=n._next();return i.done||(i.value=o(i.value)),i}),n}const lp=Array.prototype;function Xt(e,t,o,r,n,i){const l=li(e),a=l!==e&&!vt(e),s=l[t];if(s!==lp[t]){const f=s.apply(e,i);return a?kt(f):f}let c=o;l!==e&&(a?c=function(f,d){return o.call(this,zt(e,f),d,e)}:o.length>2&&(c=function(f,d){return o.call(this,f,d,e)}));const u=s.call(l,c,r);return a&&n?n(u):u}function La(e,t,o,r){const n=li(e),i=n!==e&&!vt(e);let l=o,a=!1;n!==e&&(i?(a=r.length===0,l=function(c,u,f){return a&&(a=!1,c=zt(e,c)),o.call(this,c,zt(e,u),f,e)}):o.length>3&&(l=function(c,u,f){return o.call(this,c,u,f,e)}));const s=n[t](l,...r);return a?zt(e,s):s}function Ni(e,t,o){const r=ge(e);Ye(r,"iterate",Yr);const n=r[t](...o);return(n===-1||n===!1)&&ai(o[0])?(o[0]=ge(o[0]),r[t](...o)):n}function Ar(e,t,o=[]){so(),zl();const r=ge(e)[t].apply(e,o);return Ul(),co(),r}const ap=$l("__proto__,__v_isRef,__isVue"),Jc=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(yt));function sp(e){yt(e)||(e=String(e));const t=ge(this);return Ye(t,"has",e),t.hasOwnProperty(e)}class Qc{constructor(t=!1,o=!1){this._isReadonly=t,this._isShallow=o}get(t,o,r){if(o==="__v_skip")return t.__v_skip;const n=this._isReadonly,i=this._isShallow;if(o==="__v_isReactive")return!n;if(o==="__v_isReadonly")return n;if(o==="__v_isShallow")return i;if(o==="__v_raw")return r===(n?i?bp:ou:i?tu:eu).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(r)?t:void 0;const l=oe(t);if(!n){let s;if(l&&(s=ip[o]))return s;if(o==="hasOwnProperty")return sp}const a=Reflect.get(t,o,we(t)?t:r);if((yt(o)?Jc.has(o):ap(o))||(n||Ye(t,"get",o),i))return a;if(we(a)){const s=l&&Zn(o)?a:a.value;return n&&be(s)?ll(s):s}return be(a)?n?ll(a):fn(a):a}}class Zc extends Qc{constructor(t=!1){super(!1,t)}set(t,o,r,n){let i=t[o];const l=oe(t)&&Zn(o);if(!this._isShallow){const c=uo(i);if(!vt(r)&&!uo(r)&&(i=ge(i),r=ge(r)),!l&&we(i)&&!we(r))return c||(i.value=r),!0}const a=l?Number(o)e,Cn=e=>Reflect.getPrototypeOf(e);function pp(e,t,o){return function(...r){const n=this.__v_raw,i=ge(n),l=To(i),a=e==="entries"||e===Symbol.iterator&&l,s=e==="keys"&&l,c=n[e](...r),u=o?il:t?gr:kt;return!t&&Ye(i,"iterate",s?nl:Jo),Be(Object.create(c),{next(){const{value:f,done:d}=c.next();return d?{value:f,done:d}:{value:a?[u(f[0]),u(f[1])]:u(f),done:d}}})}}function bn(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function mp(e,t){const o={get(n){const i=this.__v_raw,l=ge(i),a=ge(n);e||(Vt(n,a)&&Ye(l,"get",n),Ye(l,"get",a));const{has:s}=Cn(l),c=t?il:e?gr:kt;if(s.call(l,n))return c(i.get(n));if(s.call(l,a))return c(i.get(a));i!==l&&i.get(n)},get size(){const n=this.__v_raw;return!e&&Ye(ge(n),"iterate",Jo),n.size},has(n){const i=this.__v_raw,l=ge(i),a=ge(n);return e||(Vt(n,a)&&Ye(l,"has",n),Ye(l,"has",a)),n===a?i.has(n):i.has(n)||i.has(a)},forEach(n,i){const l=this,a=l.__v_raw,s=ge(a),c=t?il:e?gr:kt;return!e&&Ye(s,"iterate",Jo),a.forEach((u,f)=>n.call(i,c(u),c(f),l))}};return Be(o,e?{add:bn("add"),set:bn("set"),delete:bn("delete"),clear:bn("clear")}:{add(n){const i=ge(this),l=Cn(i),a=ge(n),s=!t&&!vt(n)&&!uo(n)?a:n;return l.has.call(i,s)||Vt(n,s)&&l.has.call(i,n)||Vt(a,s)&&l.has.call(i,a)||(i.add(s),ro(i,"add",s,s)),this},set(n,i){!t&&!vt(i)&&!uo(i)&&(i=ge(i));const l=ge(this),{has:a,get:s}=Cn(l);let c=a.call(l,n);c||(n=ge(n),c=a.call(l,n));const u=s.call(l,n);return l.set(n,i),c?Vt(i,u)&&ro(l,"set",n,i):ro(l,"add",n,i),this},delete(n){const i=ge(this),{has:l,get:a}=Cn(i);let s=l.call(i,n);s||(n=ge(n),s=l.call(i,n)),a&&a.call(i,n);const c=i.delete(n);return s&&ro(i,"delete",n,void 0),c},clear(){const n=ge(this),i=n.size!==0,l=n.clear();return i&&ro(n,"clear",void 0,void 0),l}}),["keys","values","entries",Symbol.iterator].forEach(n=>{o[n]=pp(n,e,t)}),o}function Gl(e,t){const o=mp(e,t);return(r,n,i)=>n==="__v_isReactive"?!e:n==="__v_isReadonly"?e:n==="__v_raw"?r:Reflect.get(_e(o,n)&&n in r?o:r,n,i)}const hp={get:Gl(!1,!1)},gp={get:Gl(!1,!0)},Cp={get:Gl(!0,!1)};const eu=new WeakMap,tu=new WeakMap,ou=new WeakMap,bp=new WeakMap;function xp(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function fn(e){return uo(e)?e:Kl(e,!1,up,hp,eu)}function ru(e){return Kl(e,!1,dp,gp,tu)}function ll(e){return Kl(e,!0,fp,Cp,ou)}function Kl(e,t,o,r,n){if(!be(e)||e.__v_raw&&!(t&&e.__v_isReactive)||e.__v_skip||!Object.isExtensible(e))return e;const i=n.get(e);if(i)return i;const l=xp(Ud(e));if(l===0)return e;const a=new Proxy(e,l===2?r:o);return n.set(e,a),a}function lo(e){return uo(e)?lo(e.__v_raw):!!(e&&e.__v_isReactive)}function uo(e){return!!(e&&e.__v_isReadonly)}function vt(e){return!!(e&&e.__v_isShallow)}function ai(e){return e?!!e.__v_raw:!1}function ge(e){const t=e&&e.__v_raw;return t?ge(t):e}function qr(e){return!_e(e,"__v_skip")&&Object.isExtensible(e)&&kc(e,"__v_skip",!0),e}const kt=e=>be(e)?fn(e):e,gr=e=>be(e)?ll(e):e;function we(e){return e?e.__v_isRef===!0:!1}function mt(e){return nu(e,!1)}function Yl(e){return nu(e,!0)}function nu(e,t){return we(e)?e:new _p(e,t)}class _p{constructor(t,o){this.dep=new jl,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=o?t:ge(t),this._value=o?t:kt(t),this.__v_isShallow=o}get value(){return this.dep.track(),this._value}set value(t){const o=this._rawValue,r=this.__v_isShallow||vt(t)||uo(t);t=r?t:ge(t),Vt(t,o)&&(this._rawValue=t,this._value=r?t:kt(t),this.dep.trigger())}}function bt(e){return we(e)?e.value:e}const vp={get:(e,t,o)=>t==="__v_raw"?e:bt(Reflect.get(e,t,o)),set:(e,t,o,r)=>{const n=e[t];return we(n)&&!we(o)?(n.value=o,!0):Reflect.set(e,t,o,r)}};function iu(e){return lo(e)?e:new Proxy(e,vp)}function Sp(e){const t=oe(e)?new Array(e.length):{};for(const o in e)t[o]=lu(e,o);return t}class yp{constructor(t,o,r){this._object=t,this._defaultValue=r,this.__v_isRef=!0,this._value=void 0,this._key=yt(o)?o:String(o),this._raw=ge(t);let n=!0,i=t;if(!oe(t)||yt(this._key)||!Zn(this._key))do n=!ai(i)||vt(i);while(n&&(i=i.__v_raw));this._shallow=n}get value(){let t=this._object[this._key];return this._shallow&&(t=bt(t)),this._value=t===void 0?this._defaultValue:t}set value(t){if(this._shallow&&we(this._raw[this._key])){const o=this._object[this._key];if(we(o)){o.value=t;return}}this._object[this._key]=t}get dep(){return np(this._raw,this._key)}}class Ep{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0,this._value=void 0}get value(){return this._value=this._getter()}}function aT(e,t,o){return we(e)?e:le(e)?new Ep(e):be(e)&&arguments.length>1?lu(e,t,o):mt(e)}function lu(e,t,o){return new yp(e,t,o)}class Tp{constructor(t,o,r){this.fn=t,this.setter=o,this._value=void 0,this.dep=new jl(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=Kr-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!o,this.isSSR=r}notify(){if(this.flags|=16,!(this.flags&8)&&Te!==this)return jc(this,!0),!0}get value(){const t=this.dep.track();return Yc(this),t&&(t.version=this.dep.version),this._value}set value(t){this.setter&&this.setter(t)}}function Pp(e,t,o=!1){let r,n;return le(e)?r=e:(r=e.get,n=e.set),new Tp(r,n,o)}const xn={},Fn=new WeakMap;let Uo;function Ip(e,t=!1,o=Uo){if(o){let r=Fn.get(o);r||Fn.set(o,r=[]),r.push(e)}}function Ap(e,t,o=Se){const{immediate:r,deep:n,once:i,scheduler:l,augmentJob:a,call:s}=o,c=y=>n?y:vt(y)||n===!1||n===0?no(y,1):no(y);let u,f,d,p,g=!1,C=!1;if(we(e)?(f=()=>e.value,g=vt(e)):lo(e)?(f=()=>c(e),g=!0):oe(e)?(C=!0,g=e.some(y=>lo(y)||vt(y)),f=()=>e.map(y=>{if(we(y))return y.value;if(lo(y))return c(y);if(le(y))return s?s(y,2):y()})):le(e)?t?f=s?()=>s(e,2):e:f=()=>{if(d){so();try{d()}finally{co()}}const y=Uo;Uo=u;try{return s?s(e,3,[p]):e(p)}finally{Uo=y}}:f=Gt,t&&n){const y=f,L=n===!0?1/0:n;f=()=>no(y(),L)}const S=zc(),E=()=>{u.stop(),S&&S.active&&Bl(S.effects,u)};if(i&&t){const y=t;t=(...L)=>{const w=y(...L);return E(),w}}let T=C?new Array(e.length).fill(xn):xn;const v=y=>{if(!(!(u.flags&1)||!u.dirty&&!y))if(t){const L=u.run();if(y||n||g||(C?L.some((w,D)=>Vt(w,T[D])):Vt(L,T))){d&&d();const w=Uo;Uo=u;try{const D=[L,T===xn?void 0:C&&T[0]===xn?[]:T,p];T=L,s?s(t,3,D):t(...D)}finally{Uo=w}}}else u.run()};return a&&a(v),u=new Uc(f),u.scheduler=l?()=>l(v,!1):v,p=y=>Ip(y,!1,u),d=u.onStop=()=>{const y=Fn.get(u);if(y){if(s)s(y,4);else for(const L of y)L();Fn.delete(u)}},t?r?v(!0):T=u.run():l?l(v.bind(null,!0),!0):u.run(),E.pause=u.pause.bind(u),E.resume=u.resume.bind(u),E.stop=E,E}function no(e,t=1/0,o){if(t<=0||!be(e)||e.__v_skip||(o=o||new Map,(o.get(e)||0)>=t))return e;if(o.set(e,t),t--,we(e))no(e.value,t,o);else if(oe(e))for(let r=0;r{no(r,t,o)});else if(Mc(e)){for(const r in e)no(e[r],t,o);for(const r of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,r)&&no(e[r],t,o)}return e}function dn(e,t,o,r){try{return r?e(...r):e()}catch(n){si(n,t,o)}}function Lt(e,t,o,r){if(le(e)){const n=dn(e,t,o,r);return n&&Oc(n)&&n.catch(i=>{si(i,t,o)}),n}if(oe(e)){const n=[];for(let i=0;i>>1,n=at[r],i=Xr(n);i=Xr(o)?at.push(e):at.splice(wp(t),0,e),e.flags|=1,su()}}function su(){On||(On=au.then(uu))}function Dp(e){if(!oe(e))So&&e.id===-1?So.splice(sr+1,0,e):e.flags&1||(pr.push(e),e.flags|=1);else for(let t=0;tXr(o)-Xr(r));if(pr.length=0,So){for(let o=0;oe.id==null?e.flags&2?-1:1/0:e.id;function uu(e){try{for(Wt=0;Wt{r._d&&$n(-1);const i=Nn(t),l=ao.length;let a;try{a=e(...n)}finally{for(let s=ao.length;s>l;s--)oa();Nn(i),r._d&&$n(1)}return a};return r._n=!0,r._c=!0,r._d=!0,r}function sT(e,t){if(Ve===null)return e;const o=hi(Ve),r=e.dirs||(e.dirs=[]);for(let n=0;n1)return o&&le(t)?t.call(r&&r.proxy):t}}function Rp(){return!!(Ao()||Qo)}const Fp=Symbol.for("v-scx"),Op=()=>Ze(Fp);function cT(e,t){return Xl(e,null,t)}function St(e,t,o){return Xl(e,t,o)}function Xl(e,t,o=Se){const{immediate:r,deep:n,flush:i,once:l}=o,a=Be({},o),s=t&&r||!t&&i!=="post";let c;if(on){if(i==="sync"){const p=Op();c=p.__watcherHandles||(p.__watcherHandles=[])}else if(!s){const p=()=>{};return p.stop=Gt,p.resume=Gt,p.pause=Gt,p}}const u=Qe;a.call=(p,g,C)=>Lt(p,u,g,C);let f=!1;i==="post"?a.scheduler=p=>{nt(p,u&&u.suspense)}:i!=="sync"&&(f=!0,a.scheduler=(p,g)=>{g?p():ql(p)}),a.augmentJob=p=>{t&&(p.flags|=4),f&&(p.flags|=2,u&&(p.id=u.uid,p.i=u))};const d=Ap(e,t,a);return on&&(c?c.push(d):s&&d()),d}function Np(e,t,o){const r=this.proxy,n=Le(e)?e.includes(".")?pu(r,e):()=>r[e]:e.bind(r,r);let i;le(t)?i=t:(i=t.handler,o=t);const l=mn(this),a=Xl(n,i.bind(r),o);return l(),a}function pu(e,t){const o=t.split(".");return()=>{let r=e;for(let n=0;ne.__isTeleport,Vo=e=>e&&(e.disabled||e.disabled===""),Mp=e=>e&&(e.defer||e.defer===""),Da=e=>typeof SVGElement<"u"&&e instanceof SVGElement,Ra=e=>typeof MathMLElement=="function"&&e instanceof MathMLElement,al=(e,t)=>{const o=e&&e.to;return Le(o)?t?t(o):null:o},kp={name:"Teleport",__isTeleport:!0,process(e,t,o,r,n,i,l,a,s,c){const{mc:u,pc:f,pbc:d,o:{insert:p,querySelector:g,createText:C,createComment:S,parentNode:E}}=c,T=Vo(t.props);let{dynamicChildren:v}=t;const y=(D,F,P)=>{D.shapeFlag&16&&u(D.children,F,P,n,i,l,a,s)},L=(D=t)=>{const F=Vo(D.props),P=D.target=al(D.props,g),U=sl(P,D,C,p);P&&(l!=="svg"&&Da(P)?l="svg":l!=="mathml"&&Ra(P)&&(l="mathml"),n&&n.isCE&&(n.ce._teleportTargets||(n.ce._teleportTargets=new Set)).add(P),F||(y(D,P,U),Or(D,!1)))},w=D=>{const F=()=>{if(xo.get(D)===F){if(xo.delete(D),Vo(D.props)){const P=E(D.el)||o;y(D,P,D.anchor),Or(D,!0)}L(D)}};xo.set(D,F),nt(F,i)};if(e==null){const D=t.el=C(""),F=t.anchor=C("");if(p(D,o,r),p(F,o,r),Mp(t.props)||i&&i.pendingBranch){w(t);return}T&&(y(t,o,F),Or(t,!0)),L()}else{t.el=e.el;const D=t.anchor=e.anchor,F=xo.get(e);if(F){F.flags|=8,xo.delete(e),w(t);return}t.targetStart=e.targetStart;const P=t.target=e.target,U=t.targetAnchor=e.targetAnchor,X=Vo(e.props),k=X?o:P,Q=X?D:U;if(l==="svg"||Da(P)?l="svg":(l==="mathml"||Ra(P))&&(l="mathml"),v?(d(e.dynamicChildren,v,k,n,i,l,a),ta(e,t,!0)):s||f(e,t,k,Q,n,i,l,a,!1),T)X?t.props&&e.props&&t.props.to!==e.props.to&&(t.props.to=e.props.to):_n(t,o,D,c,1);else if((t.props&&t.props.to)!==(e.props&&e.props.to)){const me=al(t.props,g);me&&(t.target=me,_n(t,me,null,c,0))}else X&&_n(t,P,U,c,1);Or(t,T)}},remove(e,t,o,{um:r,o:{remove:n}},i){const{shapeFlag:l,children:a,anchor:s,targetStart:c,targetAnchor:u,target:f,props:d}=e,p=Vo(d),g=i||!p,C=xo.get(e);if(C&&(C.flags|=8,xo.delete(e)),f&&(n(c),n(u)),i&&n(s),!C&&(p||f)&&l&16)for(let S=0;S{e.isMounted=!0}),Su(()=>{e.isUnmounting=!0}),e}const Pt=[Function,Array],hu={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Pt,onEnter:Pt,onAfterEnter:Pt,onEnterCancelled:Pt,onBeforeLeave:Pt,onLeave:Pt,onAfterLeave:Pt,onLeaveCancelled:Pt,onBeforeAppear:Pt,onAppear:Pt,onAfterAppear:Pt,onAppearCancelled:Pt},gu=e=>{const t=e.subTree;return t.component?gu(t.component):t},Bp={name:"BaseTransition",props:hu,setup(e,{slots:t}){const o=Ao(),r=$p();return()=>{const n=t.default&&xu(t.default(),!0),i=n&&n.length?Cu(n):o.subTree?Ln():void 0;if(!i)return;const l=ge(e),{mode:a}=l;if(r.isLeaving)return Mi(i);const s=Mn(i);if(!s)return Mi(i);let c=cl(s,l,r,o,f=>c=f);s.type!==Je&&Jr(s,c);let u=o.subTree&&Mn(o.subTree);if(u&&u.type!==Je&&!jo(u,s)&&gu(o).type!==Je){let f=cl(u,l,r,o);if(Jr(u,f),a==="out-in"&&s.type!==Je)return r.isLeaving=!0,f.afterLeave=()=>{r.isLeaving=!1,o.job.flags&8||o.update(),delete f.afterLeave,u=void 0},Mi(i);a==="in-out"&&s.type!==Je?f.delayLeave=(d,p,g)=>{const C=bu(r,u);C[String(u.key)]=u,d[It]=()=>{p(),d[It]=void 0,delete c.delayedLeave,u=void 0},c.delayedLeave=()=>{g(),delete c.delayedLeave,u=void 0}}:u=void 0}else u&&(u=void 0);return i}}};function Cu(e){let t=e[0];if(e.length>1){for(const o of e)if(o.type!==Je){t=o;break}}return t}const Wp=Bp;function bu(e,t){const{leavingVNodes:o}=e;let r=o.get(t.type);return r||(r=Object.create(null),o.set(t.type,r)),r}function cl(e,t,o,r,n){const{appear:i,mode:l,persisted:a=!1,onBeforeEnter:s,onEnter:c,onAfterEnter:u,onEnterCancelled:f,onBeforeLeave:d,onLeave:p,onAfterLeave:g,onLeaveCancelled:C,onBeforeAppear:S,onAppear:E,onAfterAppear:T,onAppearCancelled:v}=t,y=String(e.key),L=bu(o,e),w=(P,U)=>{P&&Lt(P,r,9,U)},D=(P,U)=>{const X=U[1];w(P,U),oe(P)?P.every(k=>k.length<=1)&&X():P.length<=1&&X()},F={mode:l,persisted:a,beforeEnter(P){let U=s;if(!o.isMounted)if(i)U=S||s;else return;P[It]&&P[It](!0);const X=L[y];X&&jo(e,X)&&X.el[It]&&X.el[It](),w(U,[P])},enter(P){if(L[y]===e)return;let U=c,X=u,k=f;if(!o.isMounted)if(i)U=E||c,X=T||u,k=v||f;else return;let Q=!1;P[Lr]=ye=>{Q||(Q=!0,ye?w(k,[P]):w(X,[P]),F.delayedLeave&&F.delayedLeave(),P[Lr]=void 0)};const me=P[Lr].bind(null,!1);U?D(U,[P,me]):me()},leave(P,U){const X=String(e.key);if(P[Lr]&&P[Lr](!0),o.isUnmounting)return U();w(d,[P]);let k=!1;P[It]=me=>{k||(k=!0,U(),me?w(C,[P]):w(g,[P]),P[It]=void 0,L[X]===e&&delete L[X])};const Q=P[It].bind(null,!1);L[X]=e,p?D(p,[P,Q]):Q()},clone(P){const U=cl(P,t,o,r,n);return n&&n(U),U}};return F}function Mi(e){if(fi(e))return e=Io(e),e.children=null,e}function Mn(e){if(!fi(e))return ui(e.type)&&e.children?Cu(e.children):e;if(e.component)return e.component.subTree;const{shapeFlag:t,children:o}=e;if(o){if(t&16)return o[0];if(t&32&&le(o.default))return o.default()}}function Jr(e,t){if(e.shapeFlag&6&&e.component){e.transition=t;const o=e.component.subTree;Jr(ui(o.type)&&Mn(o)||o,t)}else e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function xu(e,t=!1,o){let r=[],n=0;for(let i=0;i1)for(let i=0;izr(C,t&&(oe(t)?t[S]:t),o,r,n));return}if(mr(r)&&!n){r.shapeFlag&512&&r.type.__asyncResolved&&r.component.subTree.component&&zr(e,t,o,r.component.subTree);return}const i=r.shapeFlag&4?hi(r.component):r.el,l=n?null:i,{i:a,r:s}=e,c=t&&t.r,u=a.refs===Se?a.refs={}:a.refs,f=a.setupState,d=ge(f),p=f===Se?Fc:C=>Fa(u,C)?!1:_e(d,C),g=(C,S)=>!(S&&Fa(u,S));if(c!=null&&c!==s){if(Oa(t),Le(c))u[c]=null,p(c)&&(f[c]=null);else if(we(c)){const C=t;g(c,C.k)&&(c.value=null),C.k&&(u[C.k]=null)}}if(le(s))dn(s,a,12,[l,u]);else{const C=Le(s),S=we(s);if(C||S){const E=()=>{if(e.f){const T=C?p(s)?f[s]:u[s]:g()||!e.k?s.value:u[e.k];if(n)oe(T)&&Bl(T,i);else if(oe(T))T.includes(i)||T.push(i);else if(C)u[s]=[i],p(s)&&(f[s]=u[s]);else{const v=[i];g(s,e.k)&&(s.value=v),e.k&&(u[e.k]=v)}}else C?(u[s]=l,p(s)&&(f[s]=l)):S&&(g(s,e.k)&&(s.value=l),e.k&&(u[e.k]=l))};if(l){const T=()=>{E(),kn.delete(e)};T.id=-1,kn.set(e,T),nt(T,o)}else Oa(e),E()}}}function Oa(e){const t=kn.get(e);t&&(t.flags|=8,kn.delete(e))}ri().requestIdleCallback;ri().cancelIdleCallback;const mr=e=>!!e.type.__asyncLoader,fi=e=>e.type.__isKeepAlive;function zp(e,t){vu(e,"a",t)}function Up(e,t){vu(e,"da",t)}function vu(e,t,o=Qe){const r=e.__wdc||(e.__wdc=()=>{let n=o;for(;n;){if(n.isDeactivated)return;n=n.parent}return e()});if(di(t,r,o),o){let n=o.parent;for(;n&&n.parent;)fi(n.parent.vnode)&&Vp(r,t,o,n),n=n.parent}}function Vp(e,t,o,r){const n=di(t,e,r,!0);Ql(()=>{Bl(r[t],n)},o)}function di(e,t,o=Qe,r=!1){if(o){const n=o[e]||(o[e]=[]),i=t.__weh||(t.__weh=(...l)=>{so();const a=mn(o),s=Lt(t,o,e,l);return a(),co(),s});return r?n.unshift(i):n.push(i),i}}const mo=e=>(t,o=Qe)=>{(!on||e==="sp")&&di(e,(...r)=>t(...r),o)},Jl=mo("bm"),pi=mo("m"),jp=mo("bu"),Gp=mo("u"),Su=mo("bum"),Ql=mo("um"),Kp=mo("sp"),Yp=mo("rtg"),qp=mo("rtc");function Xp(e,t=Qe){di("ec",e,t)}const Jp="components";function Qp(e,t){return em(Jp,e,!0,t)||e}const Zp=Symbol.for("v-ndc");function em(e,t,o=!0,r=!1){const n=Ve||Qe;if(n){const i=n.type;{const a=$m(i,!1);if(a&&(a===t||a===ct(t)||a===ti(ct(t))))return i}const l=Na(n[e]||i[e],t)||Na(n.appContext[e],t);return!l&&r?i:l}}function Na(e,t){return e&&(e[t]||e[ct(t)]||e[ti(ct(t))])}function tm(e,t,o,r){let n;const i=o,l=oe(e);if(l||Le(e)){const a=l&&lo(e);let s=!1,c=!1;a&&(s=!vt(e),c=uo(e),e=li(e)),n=new Array(e.length);for(let u=0,f=e.length;ut(a,s,void 0,i));else{const a=Object.keys(e);n=new Array(a.length);for(let s=0,c=a.length;s0;return Ft(),Zr(qe,null,[je("slot",c,r)],u?-2:64)}let l=e[t];l&&l._c&&(l._d=!1);const a=ao.length;Ft();let s;try{const c=l&&yu(l(o)),u=o.key||i||c&&c.key;s=Zr(qe,{key:(u&&!yt(u)?u:`_${t}`)+(!c&&r?"_fb":"")},c||(r?r():[]),c&&e._===1?64:-2)}catch(c){for(let u=ao.length;u>a;u--)oa();throw c}finally{l&&l._c&&(l._d=!0)}return!n&&s.scopeId&&(s.slotScopeIds=[s.scopeId+"-s"]),s}function yu(e){return e.some(t=>en(t)?!(t.type===Je||t.type===qe&&!yu(t.children)):!0)?e:null}const ul=e=>e?zu(e)?hi(e):ul(e.parent):null,Ur=Be(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>ul(e.parent),$root:e=>ul(e.root),$host:e=>e.ce,$emit:e=>e.emit,$options:e=>Tu(e),$forceUpdate:e=>e.f||(e.f=()=>{ql(e.update)}),$nextTick:e=>e.n||(e.n=ci.bind(e.proxy)),$watch:e=>Np.bind(e)}),ki=(e,t)=>e!==Se&&!e.__isScriptSetup&&_e(e,t),om={get({_:e},t){if(t==="__v_skip")return!0;const{ctx:o,setupState:r,data:n,props:i,accessCache:l,type:a,appContext:s}=e;if(t[0]!=="$"){const d=l[t];if(d!==void 0)switch(d){case 1:return r[t];case 2:return n[t];case 4:return o[t];case 3:return i[t]}else{if(ki(r,t))return l[t]=1,r[t];if(n!==Se&&_e(n,t))return l[t]=2,n[t];if(_e(i,t))return l[t]=3,i[t];if(o!==Se&&_e(o,t))return l[t]=4,o[t];fl&&(l[t]=0)}}const c=Ur[t];let u,f;if(c)return t==="$attrs"&&Ye(e.attrs,"get",""),c(e);if((u=a.__cssModules)&&(u=u[t]))return u;if(o!==Se&&_e(o,t))return l[t]=4,o[t];if(f=s.config.globalProperties,_e(f,t))return f[t]},set({_:e},t,o){const{data:r,setupState:n,ctx:i}=e;return ki(n,t)?(n[t]=o,!0):r!==Se&&_e(r,t)?(r[t]=o,!0):_e(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(i[t]=o,!0)},has({_:{data:e,setupState:t,accessCache:o,ctx:r,appContext:n,props:i,type:l}},a){let s;return!!(o[a]||e!==Se&&a[0]!=="$"&&_e(e,a)||ki(t,a)||_e(i,a)||_e(r,a)||_e(Ur,a)||_e(n.config.globalProperties,a)||(s=l.__cssModules)&&s[a])},defineProperty(e,t,o){return o.get!=null?e._.accessCache[t]=0:_e(o,"value")&&this.set(e,t,o.value,null),Reflect.defineProperty(e,t,o)}};function Ma(e){return oe(e)?e.reduce((t,o)=>(t[o]=null,t),{}):e}let fl=!0;function rm(e){const t=Tu(e),o=e.proxy,r=e.ctx;fl=!1,t.beforeCreate&&ka(t.beforeCreate,e,"bc");const{data:n,computed:i,methods:l,watch:a,provide:s,inject:c,created:u,beforeMount:f,mounted:d,beforeUpdate:p,updated:g,activated:C,deactivated:S,beforeDestroy:E,beforeUnmount:T,destroyed:v,unmounted:y,render:L,renderTracked:w,renderTriggered:D,errorCaptured:F,serverPrefetch:P,expose:U,inheritAttrs:X,components:k,directives:Q,filters:me}=t;if(c&&nm(c,r,null),l)for(const ne in l){const de=l[ne];le(de)&&(r[ne]=de.bind(o))}if(n){const ne=n.call(o,o);be(ne)&&(e.data=fn(ne))}if(fl=!0,i)for(const ne in i){const de=i[ne],tt=le(de)?de.bind(o,o):le(de.get)?de.get.bind(o,o):Gt,ft=!le(de)&&le(de.set)?de.set.bind(o):Gt,Re=fe({get:tt,set:ft});Object.defineProperty(r,ne,{enumerable:!0,configurable:!0,get:()=>Re.value,set:Fe=>Re.value=Fe})}if(a)for(const ne in a)Eu(a[ne],r,o,ne);if(s){const ne=le(s)?s.call(o):s;Reflect.ownKeys(ne).forEach(de=>{Wr(de,ne[de])})}u&&ka(u,e,"c");function se(ne,de){oe(de)?de.forEach(tt=>ne(tt.bind(o))):de&&ne(de.bind(o))}if(se(Jl,f),se(pi,d),se(jp,p),se(Gp,g),se(zp,C),se(Up,S),se(Xp,F),se(qp,w),se(Yp,D),se(Su,T),se(Ql,y),se(Kp,P),oe(U))if(U.length){const ne=e.exposed||(e.exposed={});U.forEach(de=>{Object.defineProperty(ne,de,{get:()=>o[de],set:tt=>o[de]=tt,enumerable:!0})})}else e.exposed||(e.exposed={});L&&e.render===Gt&&(e.render=L),X!=null&&(e.inheritAttrs=X),k&&(e.components=k),Q&&(e.directives=Q),P&&_u(e)}function nm(e,t,o=Gt){oe(e)&&(e=dl(e));for(const r in e){const n=e[r];let i;be(n)?"default"in n?i=Ze(n.from||r,n.default,!0):i=Ze(n.from||r):i=Ze(n),we(i)?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>i.value,set:l=>i.value=l}):t[r]=i}}function ka(e,t,o){Lt(oe(e)?e.map(r=>r.bind(t.proxy)):e.bind(t.proxy),t,o)}function Eu(e,t,o,r){let n=r.includes(".")?pu(o,r):()=>o[r];if(Le(e)){const i=t[e];le(i)&&St(n,i)}else if(le(e))St(n,e.bind(o));else if(be(e))if(oe(e))e.forEach(i=>Eu(i,t,o,r));else{const i=le(e.handler)?e.handler.bind(o):t[e.handler];le(i)&&St(n,i,e)}}function Tu(e){const t=e.type,{mixins:o,extends:r}=t,{mixins:n,optionsCache:i,config:{optionMergeStrategies:l}}=e.appContext,a=i.get(t);let s;return a?s=a:!n.length&&!o&&!r?s=t:(s={},n.length&&n.forEach(c=>Hn(s,c,l,!0)),Hn(s,t,l)),be(t)&&i.set(t,s),s}function Hn(e,t,o,r=!1){const{mixins:n,extends:i}=t;i&&Hn(e,i,o,!0),n&&n.forEach(l=>Hn(e,l,o,!0));for(const l in t)if(!(r&&l==="expose")){const a=im[l]||o&&o[l];e[l]=a?a(e[l],t[l]):t[l]}return e}const im={data:Ha,props:$a,emits:$a,methods:Nr,computed:Nr,beforeCreate:rt,created:rt,beforeMount:rt,mounted:rt,beforeUpdate:rt,updated:rt,beforeDestroy:rt,beforeUnmount:rt,destroyed:rt,unmounted:rt,activated:rt,deactivated:rt,errorCaptured:rt,serverPrefetch:rt,components:Nr,directives:Nr,watch:am,provide:Ha,inject:lm};function Ha(e,t){return t?e?function(){return Be(le(e)?e.call(this,this):e,le(t)?t.call(this,this):t)}:t:e}function lm(e,t){return Nr(dl(e),dl(t))}function dl(e){if(oe(e)){const t={};for(let o=0;ot==="modelValue"||t==="model-value"?e.modelModifiers:e[`${t}Modifiers`]||e[`${ct(t)}Modifiers`]||e[`${Ro(t)}Modifiers`];function fm(e,t,...o){if(e.isUnmounted)return;const r=e.vnode.props||Se;let n=o;const i=t.startsWith("update:"),l=i&&um(r,t.slice(7));l&&(l.trim&&(n=o.map(u=>Le(u)?u.trim():u)),l.number&&(n=n.map(oi)));let a,s=r[a=Di(t)]||r[a=Di(ct(t))];!s&&i&&(s=r[a=Di(Ro(t))]),s&&Lt(s,e,6,n);const c=r[a+"Once"];if(c){if(!e.emitted)e.emitted={};else if(e.emitted[a])return;e.emitted[a]=!0,Lt(c,e,6,n)}}const dm=new WeakMap;function Iu(e,t,o=!1){const r=o?dm:t.emitsCache,n=r.get(e);if(n!==void 0)return n;const i=e.emits;let l={},a=!1;if(!le(e)){const s=c=>{const u=Iu(c,t,!0);u&&(a=!0,Be(l,u))};!o&&t.mixins.length&&t.mixins.forEach(s),e.extends&&s(e.extends),e.mixins&&e.mixins.forEach(s)}return!i&&!a?(be(e)&&r.set(e,null),null):(oe(i)?i.forEach(s=>l[s]=null):Be(l,i),be(e)&&r.set(e,l),l)}function mi(e,t){return!e||!Jn(t)?!1:(t=t.slice(2),t=t==="Once"?t:t.replace(/Once$/,""),_e(e,t[0].toLowerCase()+t.slice(1))||_e(e,Ro(t))||_e(e,t))}function Ba(e){const{type:t,vnode:o,proxy:r,withProxy:n,propsOptions:[i],slots:l,attrs:a,emit:s,render:c,renderCache:u,props:f,data:d,setupState:p,ctx:g,inheritAttrs:C}=e,S=Nn(e);let E,T;try{if(o.shapeFlag&4){const y=n||r,L=y;E=Ut(c.call(L,y,u,f,p,d,g)),T=a}else{const y=t;E=Ut(y.length>1?y(f,{attrs:a,slots:l,emit:s}):y(f,null)),T=t.props?a:pm(a)}}catch(y){ao.length=0,si(y,e,1),E=je(Je)}let v=E;if(T&&C!==!1){const y=Object.keys(T),{shapeFlag:L}=v;y.length&&L&7&&(i&&y.some(Qn)&&(T=mm(T,i)),v=Io(v,T,!1,!0))}if(o.dirs&&(v=Io(v,null,!1,!0),v.dirs=v.dirs?v.dirs.concat(o.dirs):o.dirs),o.transition){const y=ui(v.type)&&Mn(v)||v;Jr(y,o.transition)}return E=v,Nn(S),E}const pm=e=>{let t;for(const o in e)(o==="class"||o==="style"||Jn(o))&&((t||(t={}))[o]=e[o]);return t},mm=(e,t)=>{const o={};for(const r in e)(!Qn(r)||!(r.slice(9)in t))&&(o[r]=e[r]);return o};function hm(e,t,o){const{props:r,children:n,component:i}=e,{props:l,children:a,patchFlag:s}=t,c=i.emitsOptions;if(t.dirs||t.transition)return!0;if(o&&s>=0){if(s&1024)return!0;if(s&16)return r?Wa(r,l,c):!!l;if(s&8){const u=t.dynamicProps;for(let f=0;fObject.create(Lu),Du=e=>Object.getPrototypeOf(e)===Lu;function Cm(e,t,o,r=!1){const n={},i=wu();e.propsDefaults=Object.create(null),Ru(e,t,n,i);for(const l in e.propsOptions[0])l in n||(n[l]=void 0);o?e.props=r?n:ru(n):e.type.props?e.props=n:e.props=i,e.attrs=i}function bm(e,t,o,r){const{props:n,attrs:i,vnode:{patchFlag:l}}=e,a=ge(n),[s]=e.propsOptions;let c=!1;if((r||l>0)&&!(l&16)){if(l&8){const u=e.vnode.dynamicProps;for(let f=0;f{s=!0;const[d,p]=Fu(f,t,!0);Be(l,d),p&&a.push(...p)};!o&&t.mixins.length&&t.mixins.forEach(u),e.extends&&u(e.extends),e.mixins&&e.mixins.forEach(u)}if(!i&&!s)return be(e)&&r.set(e,dr),dr;if(oe(i))for(let u=0;ue==="_"||e==="_ctx"||e==="$stable",ea=e=>oe(e)?e.map(Ut):[Ut(e)],_m=(e,t,o)=>{if(t._n)return t;const r=du((...n)=>ea(t(...n)),o);return r._c=!1,r},Ou=(e,t,o)=>{const r=e._ctx;for(const n in e){if(Zl(n))continue;const i=e[n];if(le(i))t[n]=_m(n,i,r);else if(i!=null){const l=ea(i);t[n]=()=>l}}},Nu=(e,t)=>{const o=ea(t);e.slots.default=()=>o},Mu=(e,t,o)=>{for(const r in t)(o||!Zl(r))&&(e[r]=t[r])},vm=(e,t,o)=>{const r=e.slots=wu();if(e.vnode.shapeFlag&32){const n=t._;n?(Mu(r,t,o),o&&kc(r,"_",n,!0)):Ou(t,r)}else t&&Nu(e,t)},Sm=(e,t,o)=>{const{vnode:r,slots:n}=e;let i=!0,l=Se;if(r.shapeFlag&32){const a=t._;a?o&&a===1?i=!1:Mu(n,t,o):(i=!t.$stable,Ou(t,n)),l=t}else t&&(Nu(e,t),l={default:1});if(i)for(const a in n)!Zl(a)&&l[a]==null&&delete n[a]},nt=Im;function ym(e){return Em(e)}function Em(e,t){const o=ri();o.__VUE__=!0;const{insert:r,remove:n,patchProp:i,createElement:l,createText:a,createComment:s,setText:c,setElementText:u,parentNode:f,nextSibling:d,setScopeId:p=Gt,insertStaticContent:g}=e,C=(b,_,x,R=null,$=null,N=null,V=void 0,z=null,m=!!_.dynamicChildren)=>{if(b===_)return;b&&!jo(b,_)&&(R=H(b),Fe(b,$,N,!0),b=null),_.patchFlag===-2&&(m=!1,_.dynamicChildren=null);const{type:h,ref:A,shapeFlag:O}=_;switch(h){case pn:S(b,_,x,R);break;case Je:E(b,_,x,R);break;case $i:b==null&&T(_,x,R,V);break;case qe:k(b,_,x,R,$,N,V,z,m);break;default:O&1?L(b,_,x,R,$,N,V,z,m):O&6?Q(b,_,x,R,$,N,V,z,m):(O&64||O&128)&&h.process(b,_,x,R,$,N,V,z,m,ee)}A!=null&&$?zr(A,b&&b.ref,N,_||b,!_):A==null&&b&&b.ref!=null&&zr(b.ref,null,N,b,!0)},S=(b,_,x,R)=>{if(b==null)r(_.el=a(_.children),x,R);else{const $=_.el=b.el;_.children!==b.children&&c($,_.children)}},E=(b,_,x,R)=>{b==null?r(_.el=s(_.children||""),x,R):_.el=b.el},T=(b,_,x,R)=>{[b.el,b.anchor]=g(b.children,_,x,R,b.el,b.anchor)},v=({el:b,anchor:_},x,R)=>{let $;for(;b&&b!==_;)$=d(b),r(b,x,R),b=$;r(_,x,R)},y=({el:b,anchor:_})=>{let x;for(;b&&b!==_;)x=d(b),n(b),b=x;n(_)},L=(b,_,x,R,$,N,V,z,m)=>{if(_.type==="svg"?V="svg":_.type==="math"&&(V="mathml"),b==null)w(_,x,R,$,N,V,z,m);else{const h=b.el&&b.el._isVueCE?b.el:null;try{h&&h._beginPatch(),P(b,_,$,N,V,z,m)}finally{h&&h._endPatch()}}},w=(b,_,x,R,$,N,V,z)=>{let m,h;const{props:A,shapeFlag:O,transition:j,dirs:B}=b;if(m=b.el=l(b.type,N,A&&A.is,A),O&8?u(m,b.children):O&16&&F(b.children,m,null,R,$,Hi(b,N),V,z),B&&ko(b,null,R,"created"),D(m,b,b.scopeId,V,R),A){for(const M in A)M!=="value"&&!Hr(M)&&i(m,M,null,A[M],N,R);"value"in A&&i(m,"value",null,A.value,N),(h=A.onVnodeBeforeMount)&&Bt(h,R,b)}B&&ko(b,null,R,"beforeMount");const I=Tm($,j);I&&j.beforeEnter(m),r(m,_,x),((h=A&&A.onVnodeMounted)||I||B)&&nt(()=>{h&&Bt(h,R,b),I&&j.enter(m),B&&ko(b,null,R,"mounted")},$)},D=(b,_,x,R,$)=>{if(x&&p(b,x),R)for(let N=0;N{for(let h=m;h{const z=_.el=b.el;let{patchFlag:m,dynamicChildren:h,dirs:A}=_;m|=b.patchFlag&16;const O=b.props||Se,j=_.props||Se;let B;if(x&&Ho(x,!1),(B=j.onVnodeBeforeUpdate)&&Bt(B,x,_,b),A&&ko(_,b,x,"beforeUpdate"),x&&Ho(x,!0),h&&(!b.dynamicChildren||b.dynamicChildren.length!==h.length)&&(m=0,V=!1,h=null),(O.innerHTML&&j.innerHTML==null||O.textContent&&j.textContent==null)&&u(z,""),h?U(b.dynamicChildren,h,z,x,R,Hi(_,$),N):V||de(b,_,z,null,x,R,Hi(_,$),N,!1),m>0){if(m&16)X(z,O,j,x,$);else if(m&2&&O.class!==j.class&&i(z,"class",null,j.class,$),m&4&&i(z,"style",O.style,j.style,$),m&8){const I=_.dynamicProps;for(let M=0;M{B&&Bt(B,x,_,b),A&&ko(_,b,x,"updated")},R)},U=(b,_,x,R,$,N,V)=>{for(let z=0;z<_.length;z++){const m=b[z],h=_[z],A=m.el&&(m.type===qe||!jo(m,h)||m.shapeFlag&198)?f(m.el):x;C(m,h,A,null,R,$,N,V,!0)}},X=(b,_,x,R,$)=>{if(_!==x){if(_!==Se)for(const N in _)!Hr(N)&&!(N in x)&&i(b,N,_[N],null,$,R);for(const N in x){if(Hr(N))continue;const V=x[N],z=_[N];V!==z&&N!=="value"&&i(b,N,z,V,$,R)}"value"in x&&i(b,"value",_.value,x.value,$)}},k=(b,_,x,R,$,N,V,z,m)=>{const h=_.el=b?b.el:a(""),A=_.anchor=b?b.anchor:a("");let{patchFlag:O,dynamicChildren:j,slotScopeIds:B}=_;B&&(z=z?z.concat(B):B),b==null?(r(h,x,R),r(A,x,R),F(_.children||[],x,A,$,N,V,z,m)):O>0&&O&64&&j&&b.dynamicChildren&&b.dynamicChildren.length===j.length?(U(b.dynamicChildren,j,x,$,N,V,z),(_.key!=null||$&&_===$.subTree)&&ta(b,_,!0)):de(b,_,x,A,$,N,V,z,m)},Q=(b,_,x,R,$,N,V,z,m)=>{_.slotScopeIds=z,b==null?_.shapeFlag&512?$.ctx.activate(_,x,R,V,m):me(_,x,R,$,N,V,m):ye(b,_,m)},me=(b,_,x,R,$,N,V)=>{const z=b.component=Om(b,R,$);if(fi(b)&&(z.ctx.renderer=ee),Nm(z,!1,V),z.asyncDep){if($&&$.registerDep(z,se,V),!b.el){const m=z.subTree=je(Je);E(null,m,_,x),b.placeholder=m.el}}else se(z,b,_,x,$,N,V)},ye=(b,_,x)=>{const R=_.component=b.component;if(hm(b,_,x))if(R.asyncDep&&!R.asyncResolved){ne(R,_,x);return}else R.next=_,R.update();else _.el=b.el,R.vnode=_},se=(b,_,x,R,$,N,V)=>{const z=()=>{if(b.isMounted){let{next:O,bu:j,u:B,parent:I,vnode:M}=b;{const Ue=ku(b);if(Ue){O&&(O.el=M.el,ne(b,O,V)),Ue.asyncDep.then(()=>{nt(()=>{b.isUnmounted||h()},$)});return}}let te=O,ce;Ho(b,!1),O?(O.el=M.el,ne(b,O,V)):O=M,j&&In(j),(ce=O.props&&O.props.onVnodeBeforeUpdate)&&Bt(ce,I,O,M),Ho(b,!0);const Ee=Ba(b),ot=b.subTree;b.subTree=Ee,C(ot,Ee,f(ot.el),H(ot),b,$,N),O.el=Ee.el,te===null&&gm(b,Ee.el),B&&nt(B,$),(ce=O.props&&O.props.onVnodeUpdated)&&nt(()=>Bt(ce,I,O,M),$)}else{let O;const{el:j,props:B}=_,{bm:I,m:M,parent:te,root:ce,type:Ee}=b,ot=mr(_);Ho(b,!1),I&&In(I),!ot&&(O=B&&B.onVnodeBeforeMount)&&Bt(O,te,_),Ho(b,!0);{ce.ce&&ce.ce._hasShadowRoot()&&ce.ce._injectChildStyle(Ee,b.parent?b.parent.type:void 0);const Ue=b.subTree=Ba(b);C(null,Ue,x,R,b,$,N),_.el=Ue.el}if(M&&nt(M,$),!ot&&(O=B&&B.onVnodeMounted)){const Ue=_;nt(()=>Bt(O,te,Ue),$)}(_.shapeFlag&256||te&&mr(te.vnode)&&te.vnode.shapeFlag&256)&&b.a&&nt(b.a,$),b.isMounted=!0,_=x=R=null}};b.scope.on();const m=b.effect=new Uc(z);b.scope.off();const h=b.update=m.run.bind(m),A=b.job=m.runIfDirty.bind(m);A.i=b,A.id=b.uid,m.scheduler=()=>ql(A),Ho(b,!0),h()},ne=(b,_,x)=>{_.component=b;const R=b.vnode.props;b.vnode=_,b.next=null,bm(b,_.props,R,x),Sm(b,_.children,x),so(),wa(b),co()},de=(b,_,x,R,$,N,V,z,m=!1)=>{const h=b&&b.children,A=b?b.shapeFlag:0,O=_.children,{patchFlag:j,shapeFlag:B}=_;if(j>0){if(j&128){ft(h,O,x,R,$,N,V,z,m);return}else if(j&256){tt(h,O,x,R,$,N,V,z,m);return}}B&8?(A&16&&We(h,$,N),O!==h&&u(x,O)):A&16?B&16?ft(h,O,x,R,$,N,V,z,m):We(h,$,N,!0):(A&8&&u(x,""),B&16&&F(O,x,R,$,N,V,z,m))},tt=(b,_,x,R,$,N,V,z,m)=>{b=b||dr,_=_||dr;const h=b.length,A=_.length,O=Math.min(h,A);let j;for(j=0;jA?We(b,$,N,!0,!1,O):F(_,x,R,$,N,V,z,m,O)},ft=(b,_,x,R,$,N,V,z,m)=>{let h=0;const A=_.length;let O=b.length-1,j=A-1;for(;h<=O&&h<=j;){const B=b[h],I=_[h]=m?oo(_[h]):Ut(_[h]);if(jo(B,I))C(B,I,x,null,$,N,V,z,m);else break;h++}for(;h<=O&&h<=j;){const B=b[O],I=_[j]=m?oo(_[j]):Ut(_[j]);if(jo(B,I))C(B,I,x,null,$,N,V,z,m);else break;O--,j--}if(h>O){if(h<=j){const B=j+1,I=Bj)for(;h<=O;)Fe(b[h],$,N,!0),h++;else{const B=h,I=h,M=new Map;for(h=I;h<=j;h++){const Ct=_[h]=m?oo(_[h]):Ut(_[h]);Ct.key!=null&&M.set(Ct.key,h)}let te,ce=0;const Ee=j-I+1;let ot=!1,Ue=0;const Mo=new Array(Ee);for(h=0;h=Ee){Fe(Ct,$,N,!0);continue}let $t;if(Ct.key!=null)$t=M.get(Ct.key);else for(te=I;te<=j;te++)if(Mo[te-I]===0&&jo(Ct,_[te])){$t=te;break}$t===void 0?Fe(Ct,$,N,!0):(Mo[$t-I]=h+1,$t>=Ue?Ue=$t:ot=!0,C(Ct,_[$t],x,null,$,N,V,z,m),ce++)}const wi=ot?Pm(Mo):dr;for(te=wi.length-1,h=Ee-1;h>=0;h--){const Ct=I+h,$t=_[Ct],ya=_[Ct+1],Ea=Ct+1{const{el:N,type:V,transition:z,children:m,shapeFlag:h}=b;if(h&6){Re(b.component.subTree,_,x,R);return}if(h&128){b.suspense.move(_,x,R);return}if(h&64){V.move(b,_,x,ee);return}if(V===qe){r(N,_,x);for(let O=0;Oz.enter(N),$));else{const{leave:O,delayLeave:j,afterLeave:B}=z,I=()=>{b.ctx.isUnmounted?n(N):r(N,_,x)},M=()=>{const te=N._isLeaving||!!N[It];N._isLeaving&&N[It](!0),z.persisted&&!te?I():O(N,()=>{I(),B&&B()})};j?j(N,I,M):M()}else r(N,_,x)},Fe=(b,_,x,R=!1,$=!1)=>{const{type:N,props:V,ref:z,children:m,dynamicChildren:h,shapeFlag:A,patchFlag:O,dirs:j,cacheIndex:B,memo:I}=b;if(O===-2&&($=!1),z!=null&&(so(),zr(z,null,x,b,!0),co()),B!=null&&(_.renderCache[B]=void 0),A&256){_.ctx.deactivate(b);return}const M=A&1&&j,te=!mr(b);let ce;if(te&&(ce=V&&V.onVnodeBeforeUnmount)&&Bt(ce,_,b),A&6)gt(b.component,x,R);else{if(A&128){b.suspense.unmount(x,R);return}M&&ko(b,null,_,"beforeUnmount"),A&64?b.type.remove(b,_,x,ee,R):h&&!h.hasOnce&&(N!==qe||O>0&&O&64)?We(h,_,x,!1,!0):(N===qe&&O&384||!$&&A&16)&&We(m,_,x),R&&Tt(b)}const Ee=I!=null&&B==null;(te&&(ce=V&&V.onVnodeUnmounted)||M||Ee)&&nt(()=>{ce&&Bt(ce,_,b),M&&ko(b,null,_,"unmounted"),Ee&&(b.el=null)},x)},Tt=b=>{const{type:_,el:x,anchor:R,transition:$}=b;if(_===qe){ht(x,R);return}if(_===$i){y(b);return}const N=()=>{n(x),$&&!$.persisted&&$.afterLeave&&$.afterLeave()};if(b.shapeFlag&1&&$&&!$.persisted){const{leave:V,delayLeave:z}=$,m=()=>V(x,N);z?z(b.el,N,m):m()}else N()},ht=(b,_)=>{let x;for(;b!==_;)x=d(b),n(b),b=x;n(_)},gt=(b,_,x)=>{const{bum:R,scope:$,job:N,subTree:V,um:z,m,a:h}=b;Ua(m),Ua(h),R&&In(R),$.stop(),N&&(N.flags|=8,Fe(V,b,_,x)),z&&nt(z,_),nt(()=>{b.isUnmounted=!0},_)},We=(b,_,x,R=!1,$=!1,N=0)=>{for(let V=N;V{if(b.shapeFlag&6)return H(b.component.subTree);if(b.shapeFlag&128)return b.suspense.next();const _=d(b.anchor||b.el),x=_&&_[mu];return x?d(x):_};let Y=!1;const G=(b,_,x)=>{let R;b==null?_._vnode&&(Fe(_._vnode,null,null,!0),R=_._vnode.component):C(_._vnode||null,b,_,null,null,null,x),_._vnode=b,Y||(Y=!0,wa(R),cu(),Y=!1)},ee={p:C,um:Fe,m:Re,r:Tt,mt:me,mc:F,pc:de,pbc:U,n:H,o:e};return{render:G,hydrate:void 0,createApp:cm(G)}}function Hi({type:e,props:t},o){return o==="svg"&&e==="foreignObject"||o==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:o}function Ho({effect:e,job:t},o){o?(e.flags|=32,t.flags|=4):(e.flags&=-33,t.flags&=-5)}function Tm(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function ta(e,t,o=!1){const r=e.children,n=t.children;if(oe(r)&&oe(n))for(let i=0;i>1,e[o[a]]0&&(t[r]=o[i-1]),o[i]=r)}}for(i=o.length,l=o[i-1];i-- >0;)o[i]=l,l=t[l];return o}function ku(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:ku(t)}function Ua(e){if(e)for(let t=0;te.__isSuspense;function Im(e,t){t&&t.pendingBranch?oe(e)?t.effects.push(...e):t.effects.push(e):Dp(e)}const qe=Symbol.for("v-fgt"),pn=Symbol.for("v-txt"),Je=Symbol.for("v-cmt"),$i=Symbol.for("v-stc"),ao=[];let xt=null;function Ft(e=!1){ao.push(xt=e?null:[])}function oa(){ao.pop(),xt=ao[ao.length-1]||null}let Qr=1;function $n(e,t=!1){Qr+=e,e<0&&xt&&t&&(xt.hasOnce=!0)}function Bu(e){return e.dynamicChildren=Qr>0?xt||dr:null,oa(),Qr>0&&xt&&xt.push(e),e}function hr(e,t,o,r,n,i){return Bu(Rt(e,t,o,r,n,i,!0))}function Zr(e,t,o,r,n){return Bu(je(e,t,o,r,n,!0))}function en(e){return e?e.__v_isVNode===!0:!1}function jo(e,t){return e.type===t.type&&e.key===t.key}const Wu=({key:e})=>e??null,An=({ref:e,ref_key:t,ref_for:o})=>(typeof e=="number"&&(e=""+e),e!=null?Le(e)||we(e)||le(e)?{i:Ve,r:e,k:t,f:!!o}:e:null);function Rt(e,t=null,o=null,r=0,n=null,i=e===qe?0:1,l=!1,a=!1){const s={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Wu(t),ref:t&&An(t),scopeId:fu,slotScopeIds:null,children:o,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:r,dynamicProps:n,dynamicChildren:null,appContext:null,ctx:Ve};return a?(Bn(s,o),i&128&&e.normalize(s)):o&&(s.shapeFlag|=Le(o)?8:16),Qr>0&&!l&&xt&&(s.patchFlag>0||i&6)&&s.patchFlag!==32&&xt.push(s),s}const je=Am;function Am(e,t=null,o=null,r=0,n=null,i=!1){if((!e||e===Zp)&&(e=Je),en(e)){const a=Io(e,t,!0);return o&&Bn(a,o),Qr>0&&!i&&xt&&(a.shapeFlag&6?xt[xt.indexOf(e)]=a:xt.push(a)),a.patchFlag=-2,a}if(Bm(e)&&(e=e.__vccOpts),t){t=Lm(t);let{class:a,style:s}=t;a&&!Le(a)&&(t.class=ii(a)),be(s)&&(ai(s)&&!oe(s)&&(s=Be({},s)),t.style=ni(s))}const l=Le(e)?1:$u(e)?128:ui(e)?64:be(e)?4:le(e)?2:0;return Rt(e,t,o,r,n,l,i,!0)}function Lm(e){return e?ai(e)||Du(e)?Be({},e):e:null}function Io(e,t,o=!1,r=!1){const{props:n,ref:i,patchFlag:l,children:a,transition:s}=e,c=t?Dm(n||{},t):n,u={__v_isVNode:!0,__v_skip:!0,type:e.type,props:c,key:c&&Wu(c),ref:t&&t.ref?o&&i?oe(i)?i.concat(An(t)):[i,An(t)]:An(t):i,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:a,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==qe?l===-1?16:l|16:l,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:s,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Io(e.ssContent),ssFallback:e.ssFallback&&Io(e.ssFallback),placeholder:e.placeholder,el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return s&&r&&Jr(u,s.clone(u)),u}function wm(e=" ",t=0){return je(pn,null,e,t)}function Ln(e="",t=!1){return t?(Ft(),Zr(Je,null,e)):je(Je,null,e)}function Ut(e){return e==null||typeof e=="boolean"?je(Je):oe(e)?je(qe,null,e.slice()):en(e)?oo(e):je(pn,null,String(e))}function oo(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Io(e)}function Bn(e,t){let o=0;const{shapeFlag:r}=e;if(t==null)t=null;else if(oe(t))o=16;else if(typeof t=="object")if(r&65){const n=t.default;n&&(n._c&&(n._d=!1),Bn(e,n()),n._c&&(n._d=!0));return}else{o=32;const n=t._;!n&&!Du(t)?t._ctx=Ve:n===3&&Ve&&(Ve.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else if(le(t)){if(r&65){Bn(e,{default:t});return}t={default:t,_ctx:Ve},o=32}else t=String(t),r&64?(o=16,t=[wm(t)]):o=8;e.children=t,e.shapeFlag|=o}function Dm(...e){const t={};for(let o=0;oQe||Ve;let Wn,tn;{const e=ri(),t=(o,r)=>{let n;return(n=e[o])||(n=e[o]=[]),n.push(r),i=>{n.length>1?n.forEach(l=>l(i)):n[0](i)}};Wn=t("__VUE_INSTANCE_SETTERS__",o=>Qe=o),tn=t("__VUE_SSR_SETTERS__",o=>on=o)}const mn=e=>{const t=Qe;return Wn(e),e.scope.on(),()=>{e.scope.off(),Wn(t)}},Va=()=>{Qe&&Qe.scope.off(),Wn(null)};function zu(e){return e.vnode.shapeFlag&4}let on=!1;function Nm(e,t=!1,o=!1){t&&tn(t);const{props:r,children:n}=e.vnode,i=zu(e);Cm(e,r,i,t),vm(e,n,o||t);const l=i?Mm(e,t):void 0;return t&&tn(!1),l}function Mm(e,t){const o=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,om);const{setup:r}=o;if(r){so();const n=e.setupContext=r.length>1?Hm(e):null,i=mn(e),l=dn(r,e,0,[e.props,n]),a=Oc(l);if(co(),i(),(a||e.sp)&&!mr(e)&&_u(e),a){if(l.then(Va,Va),t)return l.then(s=>{tn(!0);try{ja(e,s,t)}finally{tn(!1)}}).catch(s=>{si(s,e,0)});e.asyncDep=l}else ja(e,l)}else Uu(e)}function ja(e,t,o){le(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:be(t)&&(e.setupState=iu(t)),Uu(e)}function Uu(e,t,o){const r=e.type;e.render||(e.render=r.render||Gt);{const n=mn(e);so();try{rm(e)}finally{co(),n()}}}const km={get(e,t){return Ye(e,"get",""),e[t]}};function Hm(e){const t=o=>{e.exposed=o||{}};return{attrs:new Proxy(e.attrs,km),slots:e.slots,emit:e.emit,expose:t}}function hi(e){return e.exposed?e.exposeProxy||(e.exposeProxy=new Proxy(iu(qr(e.exposed)),{get(t,o){if(o in t)return t[o];if(o in Ur)return Ur[o](e)},has(t,o){return o in t||o in Ur}})):e.proxy}function $m(e,t=!0){return le(e)?e.displayName||e.name:e.name||t&&e.__name}function Bm(e){return le(e)&&"__vccOpts"in e}const fe=(e,t)=>Pp(e,t,on);function vr(e,t,o){try{$n(-1);const r=arguments.length;return r===2?be(t)&&!oe(t)?en(t)?je(e,null,[t]):je(e,t):je(e,null,t):(r>3?o=Array.prototype.slice.call(arguments,2):r===3&&en(o)&&(o=[o]),je(e,t,o))}finally{$n(1)}}const Wm="3.5.42";let ml;const Ga=typeof window<"u"&&window.trustedTypes;if(Ga)try{ml=Ga.createPolicy("vue",{createHTML:e=>e})}catch{}const Vu=ml?e=>ml.createHTML(e):e=>e,zm="http://www.w3.org/2000/svg",Um="http://www.w3.org/1998/Math/MathML",to=typeof document<"u"?document:null,Ka=to&&to.createElement("template"),Vm={insert:(e,t,o)=>{t.insertBefore(e,o||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,o,r)=>{const n=t==="svg"?to.createElementNS(zm,e):t==="mathml"?to.createElementNS(Um,e):o?to.createElement(e,{is:o}):to.createElement(e);return e==="select"&&r&&r.multiple!=null&&n.setAttribute("multiple",r.multiple),n},createText:e=>to.createTextNode(e),createComment:e=>to.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>to.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,o,r,n,i){const l=o?o.previousSibling:t.lastChild;if(n&&(n===i||n.nextSibling))for(;t.insertBefore(n.cloneNode(!0),o),!(n===i||!(n=n.nextSibling)););else{Ka.innerHTML=Vu(r==="svg"?`${e}`:r==="mathml"?`${e}`:e);const a=Ka.content;if(r==="svg"||r==="mathml"){const s=a.firstChild;for(;s.firstChild;)a.appendChild(s.firstChild);a.removeChild(s)}t.insertBefore(a,o)}return[l?l.nextSibling:t.firstChild,o?o.previousSibling:t.lastChild]}},go="transition",wr="animation",rn=Symbol("_vtc"),ju={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},jm=Be({},hu,ju),Gm=e=>(e.displayName="Transition",e.props=jm,e),dT=Gm((e,{slots:t})=>vr(Wp,Km(e),t)),$o=(e,t=[])=>{oe(e)?e.forEach(o=>o(...t)):e&&e(...t)},Ya=e=>e?oe(e)?e.some(t=>t.length>1):e.length>1:!1;function Km(e){const t={};for(const k in e)k in ju||(t[k]=e[k]);if(e.css===!1)return t;const{name:o="v",type:r,duration:n,enterFromClass:i=`${o}-enter-from`,enterActiveClass:l=`${o}-enter-active`,enterToClass:a=`${o}-enter-to`,appearFromClass:s=i,appearActiveClass:c=l,appearToClass:u=a,leaveFromClass:f=`${o}-leave-from`,leaveActiveClass:d=`${o}-leave-active`,leaveToClass:p=`${o}-leave-to`}=e,g=Ym(n),C=g&&g[0],S=g&&g[1],{onBeforeEnter:E,onEnter:T,onEnterCancelled:v,onLeave:y,onLeaveCancelled:L,onBeforeAppear:w=E,onAppear:D=T,onAppearCancelled:F=v}=t,P=(k,Q,me,ye)=>{k._enterCancelled=ye,Bo(k,Q?u:a),Bo(k,Q?c:l),me&&me()},U=(k,Q)=>{k._isLeaving=!1,Bo(k,f),Bo(k,p),Bo(k,d),Q&&Q()},X=k=>(Q,me)=>{const ye=k?D:T,se=()=>P(Q,k,me);$o(ye,[Q,se]),qa(()=>{Bo(Q,k?s:i),Jt(Q,k?u:a),Ya(ye)||Xa(Q,r,C,se)})};return Be(t,{onBeforeEnter(k){$o(E,[k]),Jt(k,i),Jt(k,l)},onBeforeAppear(k){$o(w,[k]),Jt(k,s),Jt(k,c)},onEnter:X(!1),onAppear:X(!0),onLeave(k,Q){k._isLeaving=!0;const me=()=>U(k,Q);Jt(k,f),k._enterCancelled?(Jt(k,d),Za(k)):(Za(k),Jt(k,d)),qa(()=>{k._isLeaving&&(Bo(k,f),Jt(k,p),Ya(y)||Xa(k,r,S,me))}),$o(y,[k,me])},onEnterCancelled(k){P(k,!1,void 0,!0),$o(v,[k])},onAppearCancelled(k){P(k,!0,void 0,!0),$o(F,[k])},onLeaveCancelled(k){U(k),$o(L,[k])}})}function Ym(e){if(e==null)return null;if(be(e))return[Bi(e.enter),Bi(e.leave)];{const t=Bi(e);return[t,t]}}function Bi(e){return Gd(e)}function Jt(e,t){t.split(/\s+/).forEach(o=>o&&e.classList.add(o)),(e[rn]||(e[rn]=new Set)).add(t)}function Bo(e,t){t.split(/\s+/).forEach(r=>r&&e.classList.remove(r));const o=e[rn];o&&(o.delete(t),o.size||(e[rn]=void 0))}function qa(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let qm=0;function Xa(e,t,o,r){const n=e._endId=++qm,i=()=>{n===e._endId&&r()};if(o!=null)return setTimeout(i,o);const{type:l,timeout:a,propCount:s}=Xm(e,t);if(!l)return r();const c=l+"end";let u=0;const f=()=>{e.removeEventListener(c,d),i()},d=p=>{p.target===e&&++u>=s&&f()};setTimeout(()=>{u(o[g]||"").split(", "),n=r(`${go}Delay`),i=r(`${go}Duration`),l=Ja(n,i),a=r(`${wr}Delay`),s=r(`${wr}Duration`),c=Ja(a,s);let u=null,f=0,d=0;t===go?l>0&&(u=go,f=l,d=i.length):t===wr?c>0&&(u=wr,f=c,d=s.length):(f=Math.max(l,c),u=f>0?l>c?go:wr:null,d=u?u===go?i.length:s.length:0);const p=u===go&&/\b(?:transform|all)(?:,|$)/.test(r(`${go}Property`).toString());return{type:u,timeout:f,propCount:d,hasTransform:p}}function Ja(e,t){for(;e.lengthQa(o)+Qa(e[r])))}function Qa(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function Za(e){return(e?e.ownerDocument:document).body.offsetHeight}function Jm(e,t,o){const r=e[rn];r&&(t=(t?[t,...r]:[...r]).join(" ")),t==null?e.removeAttribute("class"):o?e.setAttribute("class",t):e.className=t}const es=Symbol("_vod"),Qm=Symbol("_vsh"),Zm=Symbol(""),eh=/(?:^|;)\s*display\s*:/;function th(e,t,o){const r=e.style,n=Le(o);let i=!1;if(o&&!n){if(t)if(Le(t))for(const l of t.split(";")){const a=l.slice(0,l.indexOf(":")).trim();o[a]==null&&Mr(r,a,"")}else for(const l in t)o[l]==null&&Mr(r,l,"");for(const l in o){l==="display"&&(i=!0);const a=o[l];a!=null?rh(e,l,!Le(t)&&t?t[l]:void 0,a)||Mr(r,l,a):Mr(r,l,"")}}else if(n){if(t!==o){const l=r[Zm];l&&(o+=";"+l),r.cssText=o,i=eh.test(o)}}else t&&e.removeAttribute("style");es in e&&(e[es]=i?r.display:"",e[Qm]&&(r.display="none"))}const vn=/\s*!important$/;function Mr(e,t,o){if(oe(o))o.forEach(r=>Mr(e,t,r));else if(o==null&&(o=""),t.startsWith("--"))vn.test(o)?e.setProperty(t,o.replace(vn,""),"important"):e.setProperty(t,o);else{const r=oh(e,t);vn.test(o)?e.setProperty(Ro(r),o.replace(vn,""),"important"):e[r]=o}}const ts=["Webkit","Moz","ms"],Wi={};function oh(e,t){const o=Wi[t];if(o)return o;let r=ct(t);if(r!=="filter"&&r in e)return Wi[t]=r;r=ti(r);for(let n=0;nzi||(ch.then(()=>zi=0),zi=Date.now());function fh(e,t){const o=r=>{if(!r._vts)r._vts=Date.now();else if(r._vts<=o.attached)return;const n=o.value;if(oe(n)){const i=r.stopImmediatePropagation;r.stopImmediatePropagation=()=>{i.call(r),r._stopped=!0};const l=n.slice(),a=[r];for(let s=0;se.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,dh=(e,t,o,r,n,i)=>{const l=n==="svg";t==="class"?Jm(e,r,l):t==="style"?th(e,o,r):Jn(t)?Qn(t)||ih(e,t,o,r,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):ph(e,t,r,l))?(ns(e,t,r),!e.tagName.includes("-")&&(t==="value"||t==="checked"||t==="selected")&&rs(e,t,r,l,i,t!=="value")):e._isVueCE&&(mh(e,t)||e._def.__asyncLoader&&(/[A-Z]/.test(t)||!Le(r)))?ns(e,ct(t),r,i,t):(t==="true-value"?e._trueValue=r:t==="false-value"&&(e._falseValue=r),rs(e,t,r,l))};function ph(e,t,o,r){if(r)return!!(t==="innerHTML"||t==="textContent"||t in e&&ls(t)&&le(o));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="autocorrect"||t==="sandbox"&&e.tagName==="IFRAME"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const n=e.tagName;if(n==="IMG"||n==="VIDEO"||n==="CANVAS"||n==="SOURCE")return!1}return ls(t)&&Le(o)?!1:t in e}function mh(e,t){const o=e._def.props;if(!o)return!1;const r=ct(t);return Array.isArray(o)?o.some(n=>ct(n)===r):Object.keys(o).some(n=>ct(n)===r)}const zn=e=>{const t=e.props["onUpdate:modelValue"]||!1;return oe(t)?o=>In(t,o):t};function hh(e){e.target.composing=!0}function as(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event("input")))}const Ko=Symbol("_assign"),Sn=Symbol("_initialValue");function Ui(e,t,o){return t&&(e=e.trim()),o&&(e=oi(e)),e}const pT={created(e,{modifiers:{lazy:t,trim:o,number:r}},n){e.parentNode&&(e.type==="text"?e[Sn]=e.defaultValue.replace(/[\r\n]/g,""):e.type==="textarea"&&(e[Sn]=e.defaultValue.replace(/\r\n?/g,` +`))),e[Ko]=zn(n);const i=r||n.props&&n.props.type==="number";Go(e,t?"change":"input",l=>{l.target.composing||e[Ko](Ui(e.value,o,i))}),(o||i)&&Go(e,"change",()=>{e.value=Ui(e.value,o,i)}),t||(Go(e,"compositionstart",hh),Go(e,"compositionend",as),Go(e,"change",as))},mounted(e,{value:t,modifiers:{trim:o,number:r}}){const n=t??"",i=e[Sn];delete e[Sn],i!==void 0&&(e.type==="text"||e.type==="textarea")&&e.value!==i?e[Ko](Ui(e.value,o,r)):e.value=n},beforeUpdate(e,{value:t,oldValue:o,modifiers:{lazy:r,trim:n,number:i}},l){if(e[Ko]=zn(l),e.composing)return;const a=(i||e.type==="number")&&!/^0\d/.test(e.value)?oi(e.value):e.value,s=t??"";if(a===s)return;const c=e.getRootNode();(c instanceof Document||c instanceof ShadowRoot)&&c.activeElement===e&&e.type!=="range"&&(r&&t===o||n&&e.value.trim()===s)||(e.value=s)}},mT={deep:!0,created(e,{value:t,modifiers:{number:o}},r){e._modelValue=t,Go(e,"change",()=>{const n=Array.prototype.filter.call(e.options,s=>s.selected).map(s=>o?oi(Un(s)):Un(s)),i=e.multiple,l=i?er(e._modelValue)?new Set(n):n:n[0],a=e._pendingValue=[i,i?oe(l)?n.slice():n:l];try{e[Ko](l)}finally{ci(()=>{e._pendingValue===a&&(e._pendingValue=void 0)})}}),e[Ko]=zn(r)},mounted(e,{value:t}){ss(e,t)},beforeUpdate(e,{value:t},o){e._modelValue=t,e[Ko]=zn(o)},updated(e,{value:t}){const o=e._pendingValue;e._pendingValue=void 0,(!o||o[0]!==e.multiple||!gh(t,o[1],o[0]))&&ss(e,t)}};function gh(e,t,o){if(!o||oe(e))return Po(e,t);if(er(e)){if(e.size!==t.length)return!1;for(const r of t)if(!e.has(r))return!1;return!0}return!1}function ss(e,t){const o=e.multiple,r=oe(t);if(!(o&&!r&&!er(t))){for(let n=0,i=e.options.length;nString(c)===String(a)):l.selected=ep(t,a)>-1}else l.selected=t.has(a);else if(Po(Un(l),t)){e.selectedIndex!==n&&(e.selectedIndex=n);return}}!o&&e.selectedIndex!==-1&&(e.selectedIndex=-1)}}function Un(e){return"_value"in e?e._value:e.value}const Ch=["ctrl","shift","alt","meta"],bh={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Ch.some(o=>e[`${o}Key`]&&!t.includes(o))},hT=(e,t)=>{if(!e)return e;const o=e._withMods||(e._withMods={}),r=t.join(".");return o[r]||(o[r]=((n,...i)=>{for(let l=0;l{const o=e._withKeys||(e._withKeys={}),r=t.join(".");return o[r]||(o[r]=(n=>{if(!("key"in n))return;const i=Ro(n.key);if(t.some(l=>l===i||xh[l]===i))return e(n)}))},_h=Be({patchProp:dh},Vm);let cs;function vh(){return cs||(cs=ym(_h))}const Sh=((...e)=>{const t=vh().createApp(...e),{mount:o}=t;return t.mount=r=>{const n=Eh(r);if(!n)return;const i=t._component;!le(i)&&!i.render&&!i.template&&(i.template=n.innerHTML),n.nodeType===1&&(n.textContent="");const l=o(n,!1,yh(n));return n instanceof Element&&(n.removeAttribute("v-cloak"),n.setAttribute("data-v-app","")),l},t});function yh(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function Eh(e){return Le(e)?document.querySelector(e):e}let Gu;const gi=e=>Gu=e,Ku=Symbol();function hl(e){return e&&typeof e=="object"&&Object.prototype.toString.call(e)==="[object Object]"&&typeof e.toJSON!="function"}var Vr;(function(e){e.direct="direct",e.patchObject="patch object",e.patchFunction="patch function"})(Vr||(Vr={}));function Th(){const e=Wl(!0),t=e.run(()=>mt({}));let o=[],r=[];const n=qr({install(i){gi(n),n._a=i,i.provide(Ku,n),i.config.globalProperties.$pinia=n,r.forEach(l=>o.push(l)),r=[]},use(i){return this._a?o.push(i):r.push(i),this},_p:o,_a:null,_e:e,_s:new Map,state:t});return n}const Yu=()=>{};function us(e,t,o,r=Yu){e.add(t);const n=()=>{e.delete(t)&&r()};return!o&&zc()&&tp(n),n}function lr(e,...t){e.forEach(o=>{o(...t)})}const Ph=e=>e(),fs=Symbol(),Vi=Symbol();function gl(e,t){e instanceof Map&&t instanceof Map?t.forEach((o,r)=>e.set(r,o)):e instanceof Set&&t instanceof Set&&t.forEach(e.add,e);for(const o in t){if(!t.hasOwnProperty(o))continue;const r=t[o],n=e[o];hl(n)&&hl(r)&&e.hasOwnProperty(o)&&!we(r)&&!lo(r)?e[o]=gl(n,r):e[o]=r}return e}const Ih=Symbol();function Ah(e){return!hl(e)||!Object.prototype.hasOwnProperty.call(e,Ih)}const{assign:_o}=Object;function Lh(e){return!!(we(e)&&e.effect)}function wh(e,t,o,r){const{state:n,actions:i,getters:l}=t,a=o.state.value[e];let s;function c(){a||(o.state.value[e]=n?n():{});const u=Sp(o.state.value[e]);return _o(u,i,Object.keys(l||{}).reduce((f,d)=>(f[d]=qr(fe(()=>{gi(o);const p=o._s.get(e);return l[d].call(p,p)})),f),{}))}return s=qu(e,c,t,o,r,!0),s}function qu(e,t,o={},r,n,i){let l;const a=_o({actions:{}},o),s={deep:!0};let c,u,f=new Set,d=new Set,p;const g=r.state.value[e];!i&&!g&&(r.state.value[e]={});let C;function S(F){let P;c=u=!1,typeof F=="function"?(F(r.state.value[e]),P={type:Vr.patchFunction,storeId:e,events:p}):(gl(r.state.value[e],F),P={type:Vr.patchObject,payload:F,storeId:e,events:p});const U=C=Symbol();ci().then(()=>{C===U&&(c=!0)}),u=!0,lr(f,P,r.state.value[e])}const E=i?function(){const{state:P}=o,U=P?P():{};this.$patch(X=>{_o(X,U)})}:Yu;function T(){l.stop(),f.clear(),d.clear(),r._s.delete(e)}const v=(F,P="")=>{if(fs in F)return F[Vi]=P,F;const U=function(){gi(r);const X=Array.from(arguments),k=new Set,Q=new Set;function me(ne){k.add(ne)}function ye(ne){Q.add(ne)}lr(d,{args:X,name:U[Vi],store:L,after:me,onError:ye});let se;try{se=F.apply(this&&this.$id===e?this:L,X)}catch(ne){throw lr(Q,ne),ne}return se instanceof Promise?se.then(ne=>(lr(k,ne),ne)).catch(ne=>(lr(Q,ne),Promise.reject(ne))):(lr(k,se),se)};return U[fs]=!0,U[Vi]=P,U},y={_p:r,$id:e,$onAction:us.bind(null,d),$patch:S,$reset:E,$subscribe(F,P={}){const U=us(f,F,P.detached,()=>X()),X=l.run(()=>St(()=>r.state.value[e],k=>{(P.flush==="sync"?u:c)&&F({storeId:e,type:Vr.direct,events:p},k)},_o({},s,P)));return U},$dispose:T},L=fn(y);r._s.set(e,L);const D=(r._a&&r._a.runWithContext||Ph)(()=>r._e.run(()=>(l=Wl()).run(()=>t({action:v}))));for(const F in D){const P=D[F];if(we(P)&&!Lh(P)||lo(P))i||(g&&Ah(P)&&(we(P)?P.value=g[F]:gl(P,g[F])),r.state.value[e][F]=P);else if(typeof P=="function"){const U=v(P,F);D[F]=U,a.actions[F]=P}}return _o(L,D),_o(ge(L),D),Object.defineProperty(L,"$state",{get:()=>r.state.value[e],set:F=>{S(P=>{_o(P,F)})}}),r._p.forEach(F=>{_o(L,l.run(()=>F({store:L,app:r._a,pinia:r,options:a})))}),g&&i&&o.hydrate&&o.hydrate(L.$state,g),c=!0,u=!0,L}function Xu(e,t,o){let r;const n=typeof t=="function";r=n?o:t;function i(l,a){const s=Rp();return l=l||(s?Ze(Ku,null):null),l&&gi(l),l=Gu,l._s.has(e)||(n?qu(e,t,r,l):wh(e,r,l)),l._s.get(e)}return i.$id=e,i}const cr=typeof document<"u";function Ju(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function Dh(e){return e.__esModule||e[Symbol.toStringTag]==="Module"||e.default&&Ju(e.default)}const xe=Object.assign;function ji(e,t){const o={};for(const r in t){const n=t[r];o[r]=Ht(n)?n.map(e):e(n)}return o}const jr=()=>{},Ht=Array.isArray;function ds(e,t){const o={};for(const r in e)o[r]=r in t?t[r]:e[r];return o}const Qu=/#/g,Rh=/&/g,Fh=/\//g,Oh=/=/g,Nh=/\?/g,Zu=/\+/g,Mh=/%5B/g,kh=/%5D/g,ef=/%5E/g,Hh=/%60/g,tf=/%7B/g,$h=/%7C/g,of=/%7D/g,Bh=/%20/g;function ra(e){return e==null?"":encodeURI(""+e).replace($h,"|").replace(Mh,"[").replace(kh,"]")}function Wh(e){return ra(e).replace(tf,"{").replace(of,"}").replace(ef,"^")}function Cl(e){return ra(e).replace(Zu,"%2B").replace(Bh,"+").replace(Qu,"%23").replace(Rh,"%26").replace(Hh,"`").replace(tf,"{").replace(of,"}").replace(ef,"^")}function zh(e){return Cl(e).replace(Oh,"%3D")}function Uh(e){return ra(e).replace(Qu,"%23").replace(Nh,"%3F")}function Vh(e){return Uh(e).replace(Fh,"%2F")}function nn(e){if(e==null)return null;try{return decodeURIComponent(""+e)}catch{}return""+e}const jh=/\/$/,Gh=e=>e.replace(jh,"");function Gi(e,t,o="/"){let r,n={},i="",l="";const a=t.indexOf("#");let s=t.indexOf("?");return s=a>=0&&s>a?-1:s,s>=0&&(r=t.slice(0,s),i=t.slice(s,a>0?a:t.length),n=e(i.slice(1))),a>=0&&(r=r||t.slice(0,a),l=t.slice(a,t.length)),r=Xh(r??t,o),{fullPath:r+i+l,path:r,query:n,hash:nn(l)}}function Kh(e,t){const o=t.query?e(t.query):"";return t.path+(o&&"?")+o+(t.hash||"")}function ps(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function Yh(e,t,o){const r=t.matched.length-1,n=o.matched.length-1;return r>-1&&r===n&&Cr(t.matched[r],o.matched[n])&&rf(t.params,o.params)&&e(t.query)===e(o.query)&&t.hash===o.hash}function Cr(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function rf(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(var o in e)if(!qh(e[o],t[o]))return!1;return!0}function qh(e,t){return Ht(e)?ms(e,t):Ht(t)?ms(t,e):e?.valueOf()===t?.valueOf()}function ms(e,t){return Ht(t)?e.length===t.length&&e.every((o,r)=>o===t[r]):e.length===1&&e[0]===t}function Xh(e,t){if(e.startsWith("/"))return e;if(!e)return t;const o=t.split("/"),r=e.split("/"),n=r[r.length-1];(n===".."||n===".")&&r.push("");let i=o.length-1,l,a;for(l=0;l1&&i--;else break;return o.slice(0,i).join("/")+"/"+r.slice(l).join("/")}const Co={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0};let bl=(function(e){return e.pop="pop",e.push="push",e})({}),Ki=(function(e){return e.back="back",e.forward="forward",e.unknown="",e})({});function Jh(e){if(!e)if(cr){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),Gh(e)}const Qh=/^[^#]+#/;function Zh(e,t){return e.replace(Qh,"#")+t}function eg(e,t){const o=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-o.left-(t.left||0),top:r.top-o.top-(t.top||0)}}const Ci=()=>({left:window.scrollX,top:window.scrollY});function tg(e){let t;if("el"in e){const o=e.el,r=typeof o=="string"&&o.startsWith("#"),n=typeof o=="string"?r?document.getElementById(o.slice(1)):document.querySelector(o):o;if(!n)return;t=eg(n,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.scrollX,t.top!=null?t.top:window.scrollY)}function hs(e,t){return(history.state?history.state.position-t:-1)+e}const xl=new Map;function og(e,t){xl.set(e,t)}function rg(e){const t=xl.get(e);return xl.delete(e),t}function ng(e){return typeof e=="string"||e&&typeof e=="object"}function nf(e){return typeof e=="string"||typeof e=="symbol"}let Oe=(function(e){return e[e.MATCHER_NOT_FOUND=1]="MATCHER_NOT_FOUND",e[e.NAVIGATION_GUARD_REDIRECT=2]="NAVIGATION_GUARD_REDIRECT",e[e.NAVIGATION_ABORTED=4]="NAVIGATION_ABORTED",e[e.NAVIGATION_CANCELLED=8]="NAVIGATION_CANCELLED",e[e.NAVIGATION_DUPLICATED=16]="NAVIGATION_DUPLICATED",e})({});const lf=Symbol("");Oe.MATCHER_NOT_FOUND+"",Oe.NAVIGATION_GUARD_REDIRECT+"",Oe.NAVIGATION_ABORTED+"",Oe.NAVIGATION_CANCELLED+"",Oe.NAVIGATION_DUPLICATED+"";function br(e,t){return xe(new Error,{type:e,[lf]:!0},t)}function Qt(e,t){return e instanceof Error&&lf in e&&(t==null||!!(e.type&t))}const ig=["params","query","hash"];function lg(e){if(typeof e=="string")return e;if(e.path!=null)return e.path;const t={};for(const o of ig)o in e&&(t[o]=e[o]);return JSON.stringify(t,null,2)}function ag(e){const t={};if(e===""||e==="?")return t;const o=(e[0]==="?"?e.slice(1):e).split("&");for(let r=0;rn&&Cl(n)):[r&&Cl(r)]).forEach(n=>{n!==void 0&&(t+=(t.length?"&":"")+o,n!=null&&(t+="="+n))})}return t}function sg(e){const t={};for(const o in e){const r=e[o];r!==void 0&&(t[o]=Ht(r)?r.map(n=>n==null?null:""+n):r==null?r:""+r)}return t}const cg=Symbol(""),Cs=Symbol(""),bi=Symbol(""),na=Symbol(""),_l=Symbol("");function Dr(){let e=[];function t(r){return e.push(r),()=>{const n=e.indexOf(r);n>-1&&e.splice(n,1)}}function o(){e=[]}return{add:t,list:()=>e.slice(),reset:o}}function yo(e,t,o,r,n,i=l=>l()){const l=r&&(r.enterCallbacks[n]=r.enterCallbacks[n]||[]);return()=>new Promise((a,s)=>{const c=d=>{d===!1?s(br(Oe.NAVIGATION_ABORTED,{from:o,to:t})):d instanceof Error?s(d):ng(d)?s(br(Oe.NAVIGATION_GUARD_REDIRECT,{from:t,to:d})):(l&&r.enterCallbacks[n]===l&&typeof d=="function"&&l.push(d),a())},u=i(()=>e.call(r&&r.instances[n],t,o,c));let f=Promise.resolve(u);e.length<3&&(f=f.then(c)),f.catch(d=>s(d))})}function Yi(e,t,o,r,n=i=>i()){const i=[];for(const l of e)for(const a in l.components){let s=l.components[a];if(!(t!=="beforeRouteEnter"&&!l.instances[a]))if(Ju(s)){const c=(s.__vccOpts||s)[t];c&&i.push(yo(c,o,r,l,a,n))}else{let c=s();i.push(()=>c.then(u=>{if(!u)throw new Error(`Couldn't resolve component "${a}" at "${l.path}"`);const f=Dh(u)?u.default:u;l.mods[a]=u,l.components[a]=f;const d=(f.__vccOpts||f)[t];return d&&yo(d,o,r,l,a,n)()}))}}return i}function ug(e,t){const o=[],r=[],n=[],i=Math.max(t.matched.length,e.matched.length);for(let l=0;lCr(c,a))?r.push(a):o.push(a));const s=e.matched[l];s&&(t.matched.find(c=>Cr(c,s))||n.push(s))}return[o,r,n]}let fg=()=>location.protocol+"//"+location.host;function af(e,t){const{pathname:o,search:r,hash:n}=t,i=e.indexOf("#");if(i>-1){let l=n.includes(e.slice(i))?e.slice(i).length:1,a=n.slice(l);return a[0]!=="/"&&(a="/"+a),ps(a,"")}return ps(o,e)+r+n}function dg(e,t,o,r){let n=[],i=[],l=null;const a=({state:d})=>{const p=af(e,location),g=o.value,C=t.value;let S=0;if(d){if(o.value=p,t.value=d,l&&l===g){l=null;return}S=C?d.position-C.position:0}else r(p);n.forEach(E=>{E(o.value,g,{delta:S,type:bl.pop,direction:S?S>0?Ki.forward:Ki.back:Ki.unknown})})};function s(){l=o.value}function c(d){n.push(d);const p=()=>{const g=n.indexOf(d);g>-1&&n.splice(g,1)};return i.push(p),p}function u(){if(document.visibilityState==="hidden"){const{history:d}=window;if(!d.state)return;d.replaceState(xe({},d.state,{scroll:Ci()}),"")}}function f(){for(const d of i)d();i=[],window.removeEventListener("popstate",a),window.removeEventListener("pagehide",u),document.removeEventListener("visibilitychange",u)}return window.addEventListener("popstate",a),window.addEventListener("pagehide",u),document.addEventListener("visibilitychange",u),{pauseListeners:s,listen:c,destroy:f}}function bs(e,t,o,r=!1,n=!1){return{back:e,current:t,forward:o,replaced:r,position:window.history.length,scroll:n?Ci():null}}function pg(e){const{history:t,location:o}=window,r={value:af(e,o)},n={value:t.state};n.value||i(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function i(s,c,u){const f=e.indexOf("#"),d=f>-1?(o.host&&document.querySelector("base")?e:e.slice(f))+s:fg()+e+s;try{t[u?"replaceState":"pushState"](c,"",d),n.value=c}catch(p){console.error(p),o[u?"replace":"assign"](d)}}function l(s,c){i(s,xe({},t.state,bs(n.value.back,s,n.value.forward,!0),c,{position:n.value.position}),!0),r.value=s}function a(s,c){const u=xe({},n.value,t.state,{forward:s,scroll:Ci()});i(u.current,u,!0),i(s,xe({},bs(r.value,s,null),{position:u.position+1},c),!1),r.value=s}return{location:r,state:n,push:a,replace:l}}function mg(e){e=Jh(e);const t=pg(e),o=dg(e,t.state,t.location,t.replace);function r(i,l=!0){l||o.pauseListeners(),history.go(i)}const n=xe({location:"",base:e,go:r,createHref:Zh.bind(null,e)},t,o);return Object.defineProperty(n,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(n,"state",{enumerable:!0,get:()=>t.state.value}),n}let Yo=(function(e){return e[e.Static=0]="Static",e[e.Param=1]="Param",e[e.Group=2]="Group",e})({});var ke=(function(e){return e[e.Static=0]="Static",e[e.Param=1]="Param",e[e.ParamRegExp=2]="ParamRegExp",e[e.ParamRegExpEnd=3]="ParamRegExpEnd",e[e.EscapeNext=4]="EscapeNext",e})(ke||{});const hg={type:Yo.Static,value:""},gg=/[a-zA-Z0-9_]/;function Cg(e){if(!e)return[[]];if(e==="/")return[[hg]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(p){throw new Error(`ERR (${o})/"${c}": ${p}`)}let o=ke.Static,r=o;const n=[];let i;function l(){i&&n.push(i),i=[]}let a=0,s,c="",u="";function f(){c&&(o===ke.Static?i.push({type:Yo.Static,value:c}):o===ke.Param||o===ke.ParamRegExp||o===ke.ParamRegExpEnd?(i.length>1&&(s==="*"||s==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),i.push({type:Yo.Param,value:c,regexp:u,repeatable:s==="*"||s==="+",optional:s==="*"||s==="?"})):t("Invalid state to consume buffer"),c="")}function d(){c+=s}for(;at.length?t.length===1&&t[0]===lt.Static+lt.Segment?1:-1:0}function sf(e,t){let o=0;const r=e.score,n=t.score;for(;o0&&t[t.length-1]<0}const Sg={strict:!1,end:!0,sensitive:!1};function yg(e,t,o){const r=_g(Cg(e.path),o),n=xe(r,{record:e,parent:t,children:[],alias:[]});return t&&!n.record.aliasOf==!t.record.aliasOf&&t.children.push(n),n}function Eg(e,t){const o=[],r=new Map;t=ds(Sg,t);function n(f){return r.get(f)}function i(f,d,p){const g=!p,C=Ss(f);C.aliasOf=p&&p.record;const S=ds(t,f),E=[C];if("alias"in f){const y=typeof f.alias=="string"?[f.alias]:f.alias;for(const L of y)E.push(Ss(xe({},C,{components:p?p.record.components:C.components,path:L,aliasOf:p?p.record:C})))}let T,v;for(const y of E){const{path:L}=y;if(d&&L[0]!=="/"){const w=d.record.path,D=w[w.length-1]==="/"?"":"/";y.path=d.record.path+(L&&D+L)}if(T=yg(y,d,S),p?p.alias.push(T):(v=v||T,v!==T&&v.alias.push(T),g&&f.name&&!ys(T)&&l(f.name)),cf(T)&&s(T),C.children){const w=C.children;for(let D=0;D{l(v)}:jr}function l(f){if(nf(f)){const d=r.get(f);d&&(r.delete(f),o.splice(o.indexOf(d),1),d.children.forEach(l),d.alias.forEach(l))}else{const d=o.indexOf(f);d>-1&&(o.splice(d,1),f.record.name&&r.delete(f.record.name),f.children.forEach(l),f.alias.forEach(l))}}function a(){return o}function s(f){const d=Ig(f,o);o.splice(d,0,f),f.record.name&&!ys(f)&&r.set(f.record.name,f)}function c(f,d){let p,g={},C,S;if("name"in f&&f.name){if(p=r.get(f.name),!p)throw br(Oe.MATCHER_NOT_FOUND,{location:f});S=p.record.name,g=xe(vs(d.params,p.keys.filter(v=>!v.optional).concat(p.parent?p.parent.keys.filter(v=>v.optional):[]).map(v=>v.name)),f.params&&vs(f.params,p.keys.map(v=>v.name))),C=p.stringify(g)}else if(f.path!=null)C=f.path,p=o.find(v=>v.re.test(C)),p&&(g=p.parse(C),S=p.record.name);else{if(p=d.name?r.get(d.name):o.find(v=>v.re.test(d.path)),!p)throw br(Oe.MATCHER_NOT_FOUND,{location:f,currentLocation:d});S=p.record.name,g=xe({},d.params,f.params),C=p.stringify(g)}const E=[];let T=p;for(;T;)E.unshift(T.record),T=T.parent;return{name:S,path:C,params:g,matched:E,meta:Pg(E)}}e.forEach(f=>i(f));function u(){o.length=0,r.clear()}return{addRoute:i,resolve:c,removeRoute:l,clearRoutes:u,getRoutes:a,getRecordMatcher:n}}function vs(e,t){const o={};for(const r of t)r in e&&(o[r]=e[r]);return o}function Ss(e){const t={path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:e.aliasOf,beforeEnter:e.beforeEnter,props:Tg(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}};return Object.defineProperty(t,"mods",{value:{}}),t}function Tg(e){const t={},o=e.props||!1;if("component"in e)t.default=o;else for(const r in e.components)t[r]=typeof o=="object"?o[r]:o;return t}function ys(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function Pg(e){return e.reduce((t,o)=>xe(t,o.meta),{})}function Ig(e,t){let o=0,r=t.length;for(;o!==r;){const i=o+r>>1;sf(e,t[i])<0?r=i:o=i+1}const n=Ag(e);return n&&(r=t.lastIndexOf(n,r-1)),r}function Ag(e){let t=e;for(;t=t.parent;)if(cf(t)&&sf(e,t)===0)return t}function cf({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function Es(e){const t=Ze(bi),o=Ze(na),r=fe(()=>{const s=bt(e.to);return t.resolve(s)}),n=fe(()=>{const{matched:s}=r.value,{length:c}=s,u=s[c-1],f=o.matched;if(!u||!f.length)return-1;const d=f.findIndex(Cr.bind(null,u));if(d>-1)return d;const p=Ts(s[c-2]);return c>1&&Ts(u)===p&&f[f.length-1].path!==p?f.findIndex(Cr.bind(null,s[c-2])):d}),i=fe(()=>n.value>-1&&Fg(o.params,r.value.params)),l=fe(()=>n.value>-1&&n.value===o.matched.length-1&&rf(o.params,r.value.params));function a(s={}){if(Rg(s)){const c=t[bt(e.replace)?"replace":"push"](bt(e.to)).catch(jr);return e.viewTransition&&typeof document<"u"&&"startViewTransition"in document&&document.startViewTransition(()=>c),c}return Promise.resolve()}return{route:r,href:fe(()=>r.value.href),isActive:i,isExactActive:l,navigate:a}}function Lg(e){return e.length===1?e[0]:e}const wg=po({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"},viewTransition:Boolean},useLink:Es,setup(e,{slots:t}){const o=fn(Es(e)),{options:r}=Ze(bi),n=fe(()=>({[Ps(e.activeClass,r.linkActiveClass,"router-link-active")]:o.isActive,[Ps(e.exactActiveClass,r.linkExactActiveClass,"router-link-exact-active")]:o.isExactActive}));return()=>{const i=t.default&&Lg(t.default(o));return e.custom?i:vr("a",{"aria-current":o.isExactActive?e.ariaCurrentValue:null,href:o.href,onClick:o.navigate,class:n.value},i)}}}),Dg=wg;function Rg(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Fg(e,t){for(const o in t){const r=t[o],n=e[o];if(typeof r=="string"){if(r!==n)return!1}else if(!Ht(n)||n.length!==r.length||r.some((i,l)=>i.valueOf()!==n[l].valueOf()))return!1}return!0}function Ts(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const Ps=(e,t,o)=>e??t??o,Og=po({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:o}){const r=Ze(_l),n=fe(()=>e.route||r.value),i=Ze(Cs,0),l=fe(()=>{let c=bt(i);const{matched:u}=n.value;let f;for(;(f=u[c])&&!f.components;)c++;return c}),a=fe(()=>n.value.matched[l.value]);Wr(Cs,fe(()=>l.value+1)),Wr(cg,a),Wr(_l,n);const s=mt();return St(()=>[s.value,a.value,e.name],([c,u,f],[d,p,g])=>{u&&(u.instances[f]=c,p&&p!==u&&c&&c===d&&(u.leaveGuards.size||(u.leaveGuards=p.leaveGuards),u.updateGuards.size||(u.updateGuards=p.updateGuards))),c&&u&&(!p||!Cr(u,p)||!d)&&(u.enterCallbacks[f]||[]).forEach(C=>C(c))},{flush:"post"}),()=>{const c=n.value,u=e.name,f=a.value,d=f&&f.components[u];if(!d)return Is(o.default,{Component:d,route:c});const p=f.props[u],g=p?p===!0?c.params:typeof p=="function"?p(c):p:null,S=vr(d,xe({},g,t,{onVnodeUnmounted:E=>{E.component.isUnmounted&&(f.instances[u]=null)},ref:s}));return Is(o.default,{Component:S,route:c})||S}}});function Is(e,t){if(!e)return null;const o=e(t);return o.length===1?o[0]:o}const Ng=Og;function Mg(e){const t=Eg(e.routes,e),o=e.parseQuery||ag,r=e.stringifyQuery||gs,n=e.history,i=Dr(),l=Dr(),a=Dr(),s=Yl(Co);let c=Co;cr&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=ji.bind(null,H=>""+H),f=ji.bind(null,Vh),d=ji.bind(null,nn);function p(H,Y){let G,ee;return nf(H)?(G=t.getRecordMatcher(H),ee=Y):ee=H,t.addRoute(ee,G)}function g(H){const Y=t.getRecordMatcher(H);Y&&t.removeRoute(Y)}function C(){return t.getRoutes().map(H=>H.record)}function S(H){return!!t.getRecordMatcher(H)}function E(H,Y){if(Y=xe({},Y||s.value),typeof H=="string"){const x=Gi(o,H,Y.path),R=t.resolve({path:x.path},Y),$=n.createHref(x.fullPath);return xe(x,R,{params:d(R.params),hash:nn(x.hash),redirectedFrom:void 0,href:$})}let G;if(H.path!=null)G=xe({},H,{path:Gi(o,H.path,Y.path).path});else{const x=xe({},H.params);for(const R in x)x[R]==null&&delete x[R];G=xe({},H,{params:f(x)}),Y.params=f(Y.params)}const ee=t.resolve(G,Y),ue=H.hash||"";ee.params=u(d(ee.params));const b=Kh(r,xe({},H,{hash:Wh(ue),path:ee.path})),_=n.createHref(b);return xe({fullPath:b,hash:ue,query:r===gs?sg(H.query):H.query||{}},ee,{redirectedFrom:void 0,href:_})}function T(H){return typeof H=="string"?Gi(o,H,s.value.path):xe({},H)}function v(H,Y){if(c!==H)return br(Oe.NAVIGATION_CANCELLED,{from:Y,to:H})}function y(H){return D(H)}function L(H){return y(xe(T(H),{replace:!0}))}function w(H,Y){const G=H.matched[H.matched.length-1];if(G&&G.redirect){const{redirect:ee}=G;let ue=typeof ee=="function"?ee(H,Y):ee;return typeof ue=="string"&&(ue=ue.includes("?")||ue.includes("#")?ue=T(ue):{path:ue},ue.params={}),xe({query:H.query,hash:H.hash,params:ue.path!=null?{}:H.params},ue)}}function D(H,Y){const G=c=E(H),ee=s.value,ue=H.state,b=H.force,_=H.replace===!0,x=w(G,ee);if(x)return D(xe(T(x),{state:typeof x=="object"?xe({},ue,x.state):ue,force:b,replace:_}),Y||G);const R=G;R.redirectedFrom=Y;let $;return!b&&Yh(r,ee,G)&&($=br(Oe.NAVIGATION_DUPLICATED,{to:R,from:ee}),Re(ee,ee,!0,!1)),($?Promise.resolve($):U(R,ee)).catch(N=>Qt(N)?Qt(N,Oe.NAVIGATION_GUARD_REDIRECT)?N:ft(N):de(N,R,ee)).then(N=>{if(N){if(Qt(N,Oe.NAVIGATION_GUARD_REDIRECT))return D(xe({replace:_},T(N.to),{state:typeof N.to=="object"?xe({},ue,N.to.state):ue,force:b}),Y||R)}else N=k(R,ee,!0,_,ue);return X(R,ee,N),N})}function F(H,Y){const G=v(H,Y);return G?Promise.reject(G):Promise.resolve()}function P(H){const Y=ht.values().next().value;return Y&&typeof Y.runWithContext=="function"?Y.runWithContext(H):H()}function U(H,Y){let G;const[ee,ue,b]=ug(H,Y);G=Yi(ee.reverse(),"beforeRouteLeave",H,Y);for(const x of ee)x.leaveGuards.forEach(R=>{G.push(yo(R,H,Y))});const _=F.bind(null,H,Y);return G.push(_),We(G).then(()=>{G=[];for(const x of i.list())G.push(yo(x,H,Y));return G.push(_),We(G)}).then(()=>{G=Yi(ue,"beforeRouteUpdate",H,Y);for(const x of ue)x.updateGuards.forEach(R=>{G.push(yo(R,H,Y))});return G.push(_),We(G)}).then(()=>{G=[];for(const x of b)if(x.beforeEnter)if(Ht(x.beforeEnter))for(const R of x.beforeEnter)G.push(yo(R,H,Y));else G.push(yo(x.beforeEnter,H,Y));return G.push(_),We(G)}).then(()=>(H.matched.forEach(x=>x.enterCallbacks={}),G=Yi(b,"beforeRouteEnter",H,Y,P),G.push(_),We(G))).then(()=>{G=[];for(const x of l.list())G.push(yo(x,H,Y));return G.push(_),We(G)}).catch(x=>Qt(x,Oe.NAVIGATION_CANCELLED)?x:Promise.reject(x))}function X(H,Y,G){a.list().forEach(ee=>P(()=>ee(H,Y,G)))}function k(H,Y,G,ee,ue){const b=v(H,Y);if(b)return b;const _=Y===Co,x=cr?history.state:{};G&&(ee||_?n.replace(H.fullPath,xe({scroll:_&&x&&x.scroll},ue)):n.push(H.fullPath,ue)),s.value=H,Re(H,Y,G,_),ft()}let Q;function me(){Q||(Q=n.listen((H,Y,G)=>{if(!gt.listening)return;const ee=E(H),ue=w(ee,gt.currentRoute.value);if(ue){D(xe(ue,{replace:!0,force:!0}),ee).catch(jr);return}c=ee;const b=s.value;cr&&og(hs(b.fullPath,G.delta),Ci()),U(ee,b).catch(_=>Qt(_,Oe.NAVIGATION_ABORTED|Oe.NAVIGATION_CANCELLED)?_:Qt(_,Oe.NAVIGATION_GUARD_REDIRECT)?(D(xe(T(_.to),{force:!0}),ee).then(x=>{Qt(x,Oe.NAVIGATION_ABORTED|Oe.NAVIGATION_DUPLICATED)&&!G.delta&&G.type===bl.pop&&n.go(-1,!1)}).catch(jr),Promise.reject()):(G.delta&&n.go(-G.delta,!1),de(_,ee,b))).then(_=>{_=_||k(ee,b,!1),_&&(G.delta&&!Qt(_,Oe.NAVIGATION_CANCELLED)?n.go(-G.delta,!1):G.type===bl.pop&&Qt(_,Oe.NAVIGATION_ABORTED|Oe.NAVIGATION_DUPLICATED)&&n.go(-1,!1)),X(ee,b,_)}).catch(jr)}))}let ye=Dr(),se=Dr(),ne;function de(H,Y,G){ft(H);const ee=se.list();return ee.length?ee.forEach(ue=>ue(H,Y,G)):console.error(H),Promise.reject(H)}function tt(){return ne&&s.value!==Co?Promise.resolve():new Promise((H,Y)=>{ye.add([H,Y])})}function ft(H){return ne||(ne=!H,me(),ye.list().forEach(([Y,G])=>H?G(H):Y()),ye.reset()),H}function Re(H,Y,G,ee){const{scrollBehavior:ue}=e;if(!cr||!ue)return Promise.resolve();const b=!G&&rg(hs(H.fullPath,0))||(ee||!G)&&history.state&&history.state.scroll||null;return ci().then(()=>ue(H,Y,b)).then(_=>_&&tg(_)).catch(_=>de(_,H,Y))}const Fe=H=>n.go(H);let Tt;const ht=new Set,gt={currentRoute:s,listening:!0,addRoute:p,removeRoute:g,clearRoutes:t.clearRoutes,hasRoute:S,getRoutes:C,resolve:E,options:e,push:y,replace:L,go:Fe,back:()=>Fe(-1),forward:()=>Fe(1),beforeEach:i.add,beforeResolve:l.add,afterEach:a.add,onError:se.add,isReady:tt,install(H){H.component("RouterLink",Dg),H.component("RouterView",Ng),H.config.globalProperties.$router=gt,Object.defineProperty(H.config.globalProperties,"$route",{enumerable:!0,get:()=>bt(s)}),cr&&!Tt&&s.value===Co&&(Tt=!0,y(n.location).catch(ee=>{}));const Y={};for(const ee in Co)Object.defineProperty(Y,ee,{get:()=>s.value[ee],enumerable:!0});H.provide(bi,gt),H.provide(na,ru(Y)),H.provide(_l,s);const G=H.unmount;ht.add(H),H.unmount=function(){ht.delete(H),ht.size<1&&(c=Co,Q&&Q(),Q=null,s.value=Co,Tt=!1,ne=!1),G()}}};function We(H){return H.reduce((Y,G)=>Y.then(()=>P(G)),Promise.resolve())}return gt}function CT(){return Ze(bi)}function kg(e){return Ze(na)}function Hg(e){let t=".",o="__",r="--",n;if(e){let g=e.blockPrefix;g&&(t=g),g=e.elementPrefix,g&&(o=g),g=e.modifierPrefix,g&&(r=g)}const i={install(g){n=g.c;const C=g.context;C.bem={},C.bem.b=null,C.bem.els=null}};function l(g){let C,S;return{before(E){C=E.bem.b,S=E.bem.els,E.bem.els=null},after(E){E.bem.b=C,E.bem.els=S},$({context:E,props:T}){return g=typeof g=="string"?g:g({context:E,props:T}),E.bem.b=g,`${T?.bPrefix||t}${E.bem.b}`}}}function a(g){let C;return{before(S){C=S.bem.els},after(S){S.bem.els=C},$({context:S,props:E}){return g=typeof g=="string"?g:g({context:S,props:E}),S.bem.els=g.split(",").map(T=>T.trim()),S.bem.els.map(T=>`${E?.bPrefix||t}${S.bem.b}${o}${T}`).join(", ")}}}function s(g){return{$({context:C,props:S}){g=typeof g=="string"?g:g({context:C,props:S});const E=g.split(",").map(y=>y.trim());function T(y){return E.map(L=>`&${S?.bPrefix||t}${C.bem.b}${y!==void 0?`${o}${y}`:""}${r}${L}`).join(", ")}const v=C.bem.els;return v!==null?T(v[0]):T()}}}function c(g){return{$({context:C,props:S}){g=typeof g=="string"?g:g({context:C,props:S});const E=C.bem.els;return`&:not(${S?.bPrefix||t}${C.bem.b}${E!==null&&E.length>0?`${o}${E[0]}`:""}${r}${g})`}}}return Object.assign(i,{cB:((...g)=>n(l(g[0]),g[1],g[2])),cE:((...g)=>n(a(g[0]),g[1],g[2])),cM:((...g)=>n(s(g[0]),g[1],g[2])),cNotM:((...g)=>n(c(g[0]),g[1],g[2]))}),i}function $g(e){let t=0;for(let o=0;o{let n=$g(r);if(n){if(n===1){e.forEach(l=>{o.push(r.replace("&",l))});return}}else{e.forEach(l=>{o.push((l&&l+" ")+r)});return}let i=[r];for(;n--;){const l=[];i.forEach(a=>{e.forEach(s=>{l.push(a.replace("&",s))})}),i=l}i.forEach(l=>o.push(l))}),o}function zg(e,t){const o=[];return t.split(uf).forEach(r=>{e.forEach(n=>{o.push((n&&n+" ")+r)})}),o}function Ug(e){let t=[""];return e.forEach(o=>{o=o&&o.trim(),o&&(o.includes("&")?t=Wg(t,o):t=zg(t,o))}),t.join(", ").replace(Bg," ")}function As(e){if(!e)return;const t=e.parentElement;t&&t.removeChild(e)}function ia(e,t){return(t??document.head).querySelector(`style[cssr-id="${e}"]`)}function Vg(e){const t=document.createElement("style");return t.setAttribute("cssr-id",e),t}function yn(e){return e?/^\s*@(s|m)/.test(e):!1}const jg=/[A-Z]/g;function ff(e){return e.replace(jg,t=>"-"+t.toLowerCase())}function Gg(e,t=" "){return typeof e=="object"&&e!==null?` { +`+Object.entries(e).map(o=>t+` ${ff(o[0])}: ${o[1]};`).join(` +`)+` +`+t+"}":`: ${e};`}function Kg(e,t,o){return typeof e=="function"?e({context:t.context,props:o}):e}function Ls(e,t,o,r){if(!t)return"";const n=Kg(t,o,r);if(!n)return"";if(typeof n=="string")return`${e} { +${n} +}`;const i=Object.keys(n);if(i.length===0)return o.config.keepEmptyBlock?e+` { +}`:"";const l=e?[e+" {"]:[];return i.forEach(a=>{const s=n[a];if(a==="raw"){l.push(` +`+s+` +`);return}a=ff(a),s!=null&&l.push(` ${a}${Gg(s)}`)}),e&&l.push("}"),l.join(` +`)}function vl(e,t,o){e&&e.forEach(r=>{if(Array.isArray(r))vl(r,t,o);else if(typeof r=="function"){const n=r(t);Array.isArray(n)?vl(n,t,o):n&&o(n)}else r&&o(r)})}function df(e,t,o,r,n){const i=e.$;let l="";if(!i||typeof i=="string")yn(i)?l=i:t.push(i);else if(typeof i=="function"){const c=i({context:r.context,props:n});yn(c)?l=c:t.push(c)}else if(i.before&&i.before(r.context),!i.$||typeof i.$=="string")yn(i.$)?l=i.$:t.push(i.$);else if(i.$){const c=i.$({context:r.context,props:n});yn(c)?l=c:t.push(c)}const a=Ug(t),s=Ls(a,e.props,r,n);l?o.push(`${l} {`):s.length&&o.push(s),e.children&&vl(e.children,{context:r.context,props:n},c=>{if(typeof c=="string"){const u=Ls(a,{raw:c},r,n);o.push(u)}else df(c,t,o,r,n)}),t.pop(),l&&o.push("}"),i&&i.after&&i.after(r.context)}function Yg(e,t,o){const r=[];return df(e,[],r,t,o),r.join(` + +`)}function Sl(e){for(var t=0,o,r=0,n=e.length;n>=4;++r,n-=4)o=e.charCodeAt(r)&255|(e.charCodeAt(++r)&255)<<8|(e.charCodeAt(++r)&255)<<16|(e.charCodeAt(++r)&255)<<24,o=(o&65535)*1540483477+((o>>>16)*59797<<16),o^=o>>>24,t=(o&65535)*1540483477+((o>>>16)*59797<<16)^(t&65535)*1540483477+((t>>>16)*59797<<16);switch(n){case 3:t^=(e.charCodeAt(r+2)&255)<<16;case 2:t^=(e.charCodeAt(r+1)&255)<<8;case 1:t^=e.charCodeAt(r)&255,t=(t&65535)*1540483477+((t>>>16)*59797<<16)}return t^=t>>>13,t=(t&65535)*1540483477+((t>>>16)*59797<<16),((t^t>>>15)>>>0).toString(36)}typeof window<"u"&&(window.__cssrContext={});function qg(e,t,o,r){const{els:n}=t;if(o===void 0)n.forEach(As),t.els=[];else{const i=ia(o,r);i&&n.includes(i)&&(As(i),t.els=n.filter(l=>l!==i))}}function ws(e,t){e.push(t)}function Xg(e,t,o,r,n,i,l,a,s){let c;if(o===void 0&&(c=t.render(r),o=Sl(c)),s){s.adapter(o,c??t.render(r));return}a===void 0&&(a=document.head);const u=ia(o,a);if(u!==null&&!i)return u;const f=u??Vg(o);if(c===void 0&&(c=t.render(r)),f.textContent=c,u!==null)return u;if(l){const d=a.querySelector(`meta[name="${l}"]`);if(d)return a.insertBefore(f,d),ws(t.els,f),f}return n?a.insertBefore(f,a.querySelector("style, link")):a.appendChild(f),ws(t.els,f),f}function Jg(e){return Yg(this,this.instance,e)}function Qg(e={}){const{id:t,ssr:o,props:r,head:n=!1,force:i=!1,anchorMetaName:l,parent:a}=e;return Xg(this.instance,this,t,r,n,i,l,a,o)}function Zg(e={}){const{id:t,parent:o}=e;qg(this.instance,this,t,o)}const En=function(e,t,o,r){return{instance:e,$:t,props:o,children:r,els:[],render:Jg,mount:Qg,unmount:Zg}},eC=function(e,t,o,r){return Array.isArray(t)?En(e,{$:null},null,t):Array.isArray(o)?En(e,t,null,o):Array.isArray(r)?En(e,t,o,r):En(e,t,o,null)};function tC(e={}){const t={c:((...o)=>eC(t,...o)),use:(o,...r)=>o.install(t,...r),find:ia,context:{},config:e};return t}const oC=".n-",rC="__",nC="--",pf=tC(),mf=Hg({blockPrefix:oC,elementPrefix:rC,modifierPrefix:nC});pf.use(mf);const{c:Ds,find:bT}=pf,{cB:xT,cE:_T,cM:vT,cNotM:ST}=mf;function yT(e,t){return e+(t==="default"?"":t.replace(/^[a-z]/,o=>o.toUpperCase()))}var hf=typeof global=="object"&&global&&global.Object===Object&&global,iC=typeof self=="object"&&self&&self.Object===Object&&self,Sr=hf||iC||Function("return this")(),Vn=Sr.Symbol,gf=Object.prototype,lC=gf.hasOwnProperty,aC=gf.toString,Rr=Vn?Vn.toStringTag:void 0;function sC(e){var t=lC.call(e,Rr),o=e[Rr];try{e[Rr]=void 0;var r=!0}catch{}var n=aC.call(e);return r&&(t?e[Rr]=o:delete e[Rr]),n}var cC=Object.prototype,uC=cC.toString;function fC(e){return uC.call(e)}var dC="[object Null]",pC="[object Undefined]",Rs=Vn?Vn.toStringTag:void 0;function xi(e){return e==null?e===void 0?pC:dC:Rs&&Rs in Object(e)?sC(e):fC(e)}function hn(e){return e!=null&&typeof e=="object"}var yl=Array.isArray;function or(e){var t=typeof e;return e!=null&&(t=="object"||t=="function")}function Cf(e){return e}var mC="[object AsyncFunction]",hC="[object Function]",gC="[object GeneratorFunction]",CC="[object Proxy]";function la(e){if(!or(e))return!1;var t=xi(e);return t==hC||t==gC||t==mC||t==CC}var qi=Sr["__core-js_shared__"],Fs=(function(){var e=/[^.]+$/.exec(qi&&qi.keys&&qi.keys.IE_PROTO||"");return e?"Symbol(src)_1."+e:""})();function bC(e){return!!Fs&&Fs in e}var xC=Function.prototype,_C=xC.toString;function vC(e){if(e!=null){try{return _C.call(e)}catch{}try{return e+""}catch{}}return""}var SC=/[\\^$.*+?()[\]{}|]/g,yC=/^\[object .+?Constructor\]$/,EC=Function.prototype,TC=Object.prototype,PC=EC.toString,IC=TC.hasOwnProperty,AC=RegExp("^"+PC.call(IC).replace(SC,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");function LC(e){if(!or(e)||bC(e))return!1;var t=la(e)?AC:yC;return t.test(vC(e))}function wC(e,t){return e?.[t]}function aa(e,t){var o=wC(e,t);return LC(o)?o:void 0}var Os=Object.create,DC=(function(){function e(){}return function(t){if(!or(t))return{};if(Os)return Os(t);e.prototype=t;var o=new e;return e.prototype=void 0,o}})();function RC(e,t,o){switch(o.length){case 0:return e.call(t);case 1:return e.call(t,o[0]);case 2:return e.call(t,o[0],o[1]);case 3:return e.call(t,o[0],o[1],o[2])}return e.apply(t,o)}function FC(e,t){var o=-1,r=e.length;for(t||(t=Array(r));++o0){if(++t>=OC)return arguments[0]}else t=0;return e.apply(void 0,arguments)}}function HC(e){return function(){return e}}var jn=(function(){try{var e=aa(Object,"defineProperty");return e({},"",{}),e}catch{}})(),$C=jn?function(e,t){return jn(e,"toString",{configurable:!0,enumerable:!1,value:HC(t),writable:!0})}:Cf,BC=kC($C),WC=9007199254740991,zC=/^(?:0|[1-9]\d*)$/;function bf(e,t){var o=typeof e;return t=t??WC,!!t&&(o=="number"||o!="symbol"&&zC.test(e))&&e>-1&&e%1==0&&e-1&&e%1==0&&e<=qC}function ca(e){return e!=null&&xf(e.length)&&!la(e)}function XC(e,t,o){if(!or(o))return!1;var r=typeof t;return(r=="number"?ca(o)&&bf(t,o.length):r=="string"&&t in o)?_i(o[t],e):!1}function JC(e){return YC(function(t,o){var r=-1,n=o.length,i=n>1?o[n-1]:void 0,l=n>2?o[2]:void 0;for(i=e.length>3&&typeof i=="function"?(n--,i):void 0,l&&XC(o[0],o[1],l)&&(i=n<3?void 0:i,n=1),t=Object(t);++r-1}function ox(e,t){var o=this.__data__,r=vi(o,e);return r<0?(++this.size,o.push([e,t])):o[r][1]=t,this}function ho(e){var t=-1,o=e==null?0:e.length;for(this.clear();++t +${t} +`}function Hx(e,t,o){const{styles:r,ids:n}=o;n.has(e)||r!==null&&(n.add(e),r.push(kx(e,t)))}const $x=typeof document<"u";function Bx(){if($x)return;const e=Ze(Mx,null);if(e!==null)return{adapter:(t,o)=>Hx(t,o,e),context:e}}const js={aliceblue:"#F0F8FF",antiquewhite:"#FAEBD7",aqua:"#0FF",aquamarine:"#7FFFD4",azure:"#F0FFFF",beige:"#F5F5DC",bisque:"#FFE4C4",black:"#000",blanchedalmond:"#FFEBCD",blue:"#00F",blueviolet:"#8A2BE2",brown:"#A52A2A",burlywood:"#DEB887",cadetblue:"#5F9EA0",chartreuse:"#7FFF00",chocolate:"#D2691E",coral:"#FF7F50",cornflowerblue:"#6495ED",cornsilk:"#FFF8DC",crimson:"#DC143C",cyan:"#0FF",darkblue:"#00008B",darkcyan:"#008B8B",darkgoldenrod:"#B8860B",darkgray:"#A9A9A9",darkgrey:"#A9A9A9",darkgreen:"#006400",darkkhaki:"#BDB76B",darkmagenta:"#8B008B",darkolivegreen:"#556B2F",darkorange:"#FF8C00",darkorchid:"#9932CC",darkred:"#8B0000",darksalmon:"#E9967A",darkseagreen:"#8FBC8F",darkslateblue:"#483D8B",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",darkturquoise:"#00CED1",darkviolet:"#9400D3",deeppink:"#FF1493",deepskyblue:"#00BFFF",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1E90FF",firebrick:"#B22222",floralwhite:"#FFFAF0",forestgreen:"#228B22",fuchsia:"#F0F",gainsboro:"#DCDCDC",ghostwhite:"#F8F8FF",gold:"#FFD700",goldenrod:"#DAA520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#ADFF2F",honeydew:"#F0FFF0",hotpink:"#FF69B4",indianred:"#CD5C5C",indigo:"#4B0082",ivory:"#FFFFF0",khaki:"#F0E68C",lavender:"#E6E6FA",lavenderblush:"#FFF0F5",lawngreen:"#7CFC00",lemonchiffon:"#FFFACD",lightblue:"#ADD8E6",lightcoral:"#F08080",lightcyan:"#E0FFFF",lightgoldenrodyellow:"#FAFAD2",lightgray:"#D3D3D3",lightgrey:"#D3D3D3",lightgreen:"#90EE90",lightpink:"#FFB6C1",lightsalmon:"#FFA07A",lightseagreen:"#20B2AA",lightskyblue:"#87CEFA",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#B0C4DE",lightyellow:"#FFFFE0",lime:"#0F0",limegreen:"#32CD32",linen:"#FAF0E6",magenta:"#F0F",maroon:"#800000",mediumaquamarine:"#66CDAA",mediumblue:"#0000CD",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",mediumseagreen:"#3CB371",mediumslateblue:"#7B68EE",mediumspringgreen:"#00FA9A",mediumturquoise:"#48D1CC",mediumvioletred:"#C71585",midnightblue:"#191970",mintcream:"#F5FFFA",mistyrose:"#FFE4E1",moccasin:"#FFE4B5",navajowhite:"#FFDEAD",navy:"#000080",oldlace:"#FDF5E6",olive:"#808000",olivedrab:"#6B8E23",orange:"#FFA500",orangered:"#FF4500",orchid:"#DA70D6",palegoldenrod:"#EEE8AA",palegreen:"#98FB98",paleturquoise:"#AFEEEE",palevioletred:"#DB7093",papayawhip:"#FFEFD5",peachpuff:"#FFDAB9",peru:"#CD853F",pink:"#FFC0CB",plum:"#DDA0DD",powderblue:"#B0E0E6",purple:"#800080",rebeccapurple:"#663399",red:"#F00",rosybrown:"#BC8F8F",royalblue:"#4169E1",saddlebrown:"#8B4513",salmon:"#FA8072",sandybrown:"#F4A460",seagreen:"#2E8B57",seashell:"#FFF5EE",sienna:"#A0522D",silver:"#C0C0C0",skyblue:"#87CEEB",slateblue:"#6A5ACD",slategray:"#708090",slategrey:"#708090",snow:"#FFFAFA",springgreen:"#00FF7F",steelblue:"#4682B4",tan:"#D2B48C",teal:"#008080",thistle:"#D8BFD8",tomato:"#FF6347",turquoise:"#40E0D0",violet:"#EE82EE",wheat:"#F5DEB3",white:"#FFF",whitesmoke:"#F5F5F5",yellow:"#FF0",yellowgreen:"#9ACD32",transparent:"#0000"};function Wx(e,t,o){t/=100,o/=100;let r=(n,i=(n+e/60)%6)=>o-o*t*Math.max(Math.min(i,4-i,1),0);return[r(5)*255,r(3)*255,r(1)*255]}function zx(e,t,o){t/=100,o/=100;let r=t*Math.min(o,1-o),n=(i,l=(i+e/30)%12)=>o-r*Math.max(Math.min(l-3,9-l,1),-1);return[n(0)*255,n(8)*255,n(4)*255]}const Yt="^\\s*",qt="\\s*$",Lo="\\s*((\\.\\d+)|(\\d+(\\.\\d*)?))%\\s*",_t="\\s*((\\.\\d+)|(\\d+(\\.\\d*)?))\\s*",qo="([0-9A-Fa-f])",Xo="([0-9A-Fa-f]{2})",Rf=new RegExp(`${Yt}hsl\\s*\\(${_t},${Lo},${Lo}\\)${qt}`),Ff=new RegExp(`${Yt}hsv\\s*\\(${_t},${Lo},${Lo}\\)${qt}`),Of=new RegExp(`${Yt}hsla\\s*\\(${_t},${Lo},${Lo},${_t}\\)${qt}`),Nf=new RegExp(`${Yt}hsva\\s*\\(${_t},${Lo},${Lo},${_t}\\)${qt}`),Ux=new RegExp(`${Yt}rgb\\s*\\(${_t},${_t},${_t}\\)${qt}`),Vx=new RegExp(`${Yt}rgba\\s*\\(${_t},${_t},${_t},${_t}\\)${qt}`),jx=new RegExp(`${Yt}#${qo}${qo}${qo}${qt}`),Gx=new RegExp(`${Yt}#${Xo}${Xo}${Xo}${qt}`),Kx=new RegExp(`${Yt}#${qo}${qo}${qo}${qo}${qt}`),Yx=new RegExp(`${Yt}#${Xo}${Xo}${Xo}${Xo}${qt}`);function dt(e){return parseInt(e,16)}function qx(e){try{let t;if(t=Of.exec(e))return[Gn(t[1]),Eo(t[5]),Eo(t[9]),Zo(t[13])];if(t=Rf.exec(e))return[Gn(t[1]),Eo(t[5]),Eo(t[9]),1];throw new Error(`[seemly/hsla]: Invalid color value ${e}.`)}catch(t){throw t}}function Xx(e){try{let t;if(t=Nf.exec(e))return[Gn(t[1]),Eo(t[5]),Eo(t[9]),Zo(t[13])];if(t=Ff.exec(e))return[Gn(t[1]),Eo(t[5]),Eo(t[9]),1];throw new Error(`[seemly/hsva]: Invalid color value ${e}.`)}catch(t){throw t}}function fo(e){try{let t;if(t=Gx.exec(e))return[dt(t[1]),dt(t[2]),dt(t[3]),1];if(t=Ux.exec(e))return[Xe(t[1]),Xe(t[5]),Xe(t[9]),1];if(t=Vx.exec(e))return[Xe(t[1]),Xe(t[5]),Xe(t[9]),Zo(t[13])];if(t=jx.exec(e))return[dt(t[1]+t[1]),dt(t[2]+t[2]),dt(t[3]+t[3]),1];if(t=Yx.exec(e))return[dt(t[1]),dt(t[2]),dt(t[3]),Zo(dt(t[4])/255)];if(t=Kx.exec(e))return[dt(t[1]+t[1]),dt(t[2]+t[2]),dt(t[3]+t[3]),Zo(dt(t[4]+t[4])/255)];if(e in js)return fo(js[e]);if(Rf.test(e)||Of.test(e)){const[o,r,n,i]=qx(e);return[...zx(o,r,n),i]}else if(Ff.test(e)||Nf.test(e)){const[o,r,n,i]=Xx(e);return[...Wx(o,r,n),i]}throw new Error(`[seemly/rgba]: Invalid color value ${e}.`)}catch(t){throw t}}function Jx(e){return e>1?1:e<0?0:e}function Al(e,t,o,r){return`rgba(${Xe(e)}, ${Xe(t)}, ${Xe(o)}, ${Jx(r)})`}function Ji(e,t,o,r,n){return Xe((e*t*(1-r)+o*r)/n)}function Z(e,t){Array.isArray(e)||(e=fo(e)),Array.isArray(t)||(t=fo(t));const o=e[3],r=t[3],n=Zo(o+r-o*r);return Al(Ji(e[0],o,t[0],r,n),Ji(e[1],o,t[1],r,n),Ji(e[2],o,t[2],r,n),n)}function J(e,t){const[o,r,n,i=1]=Array.isArray(e)?e:fo(e);return typeof t.alpha=="number"?Al(o,r,n,t.alpha):Al(o,r,n,i)}function Ne(e,t){const[o,r,n,i=1]=Array.isArray(e)?e:fo(e),{lightness:l=1,alpha:a=1}=t;return Qx([o*l,r*l,n*l,i*a])}function Zo(e){const t=Math.round(Number(e)*100)/100;return t>1?1:t<0?0:t}function Gn(e){const t=Math.round(Number(e));return t>=360||t<0?0:t}function Xe(e){const t=Math.round(Number(e));return t>255?255:t<0?0:t}function Eo(e){const t=Math.round(Number(e));return t>100?100:t<0?0:t}function Qx(e){const[t,o,r]=e;return 3 in e?`rgba(${Xe(t)}, ${Xe(o)}, ${Xe(r)}, ${Zo(e[3])})`:`rgba(${Xe(t)}, ${Xe(o)}, ${Xe(r)}, 1)`}const q={neutralBase:"#000",neutralInvertBase:"#fff",neutralTextBase:"#fff",neutralPopover:"rgb(72, 72, 78)",neutralCard:"rgb(24, 24, 28)",neutralModal:"rgb(44, 44, 50)",neutralBody:"rgb(16, 16, 20)",alpha1:"0.9",alpha2:"0.82",alpha3:"0.52",alpha4:"0.38",alpha5:"0.28",alphaClose:"0.52",alphaDisabled:"0.38",alphaDisabledInput:"0.06",alphaPending:"0.09",alphaTablePending:"0.06",alphaTableStriped:"0.05",alphaPressed:"0.05",alphaAvatar:"0.18",alphaRail:"0.2",alphaProgressRail:"0.12",alphaBorder:"0.24",alphaDivider:"0.09",alphaInput:"0.1",alphaAction:"0.06",alphaTab:"0.04",alphaScrollbar:"0.2",alphaScrollbarHover:"0.3",alphaCode:"0.12",alphaTag:"0.2",primaryHover:"#7fe7c4",primaryDefault:"#63e2b7",primaryActive:"#5acea7",primarySuppl:"rgb(42, 148, 125)",infoHover:"#8acbec",infoDefault:"#70c0e8",infoActive:"#66afd3",infoSuppl:"rgb(56, 137, 197)",errorHover:"#e98b8b",errorDefault:"#e88080",errorActive:"#e57272",errorSuppl:"rgb(208, 58, 82)",warningHover:"#f5d599",warningDefault:"#f2c97d",warningActive:"#e6c260",warningSuppl:"rgb(240, 138, 0)",successHover:"#7fe7c4",successDefault:"#63e2b7",successActive:"#5acea7",successSuppl:"rgb(42, 148, 125)"},Zx=fo(q.neutralBase),Mf=fo(q.neutralInvertBase),e_=`rgba(${Mf.slice(0,3).join(", ")}, `;function he(e){return`${e_+String(e)})`}function t_(e){const t=Array.from(Mf);return t[3]=Number(e),Z(Zx,t)}const W={name:"common",...ua,baseColor:q.neutralBase,primaryColor:q.primaryDefault,primaryColorHover:q.primaryHover,primaryColorPressed:q.primaryActive,primaryColorSuppl:q.primarySuppl,infoColor:q.infoDefault,infoColorHover:q.infoHover,infoColorPressed:q.infoActive,infoColorSuppl:q.infoSuppl,successColor:q.successDefault,successColorHover:q.successHover,successColorPressed:q.successActive,successColorSuppl:q.successSuppl,warningColor:q.warningDefault,warningColorHover:q.warningHover,warningColorPressed:q.warningActive,warningColorSuppl:q.warningSuppl,errorColor:q.errorDefault,errorColorHover:q.errorHover,errorColorPressed:q.errorActive,errorColorSuppl:q.errorSuppl,textColorBase:q.neutralTextBase,textColor1:he(q.alpha1),textColor2:he(q.alpha2),textColor3:he(q.alpha3),textColorDisabled:he(q.alpha4),placeholderColor:he(q.alpha4),placeholderColorDisabled:he(q.alpha5),iconColor:he(q.alpha4),iconColorDisabled:he(q.alpha5),iconColorHover:he(Number(q.alpha4)*1.25),iconColorPressed:he(Number(q.alpha4)*.8),opacity1:q.alpha1,opacity2:q.alpha2,opacity3:q.alpha3,opacity4:q.alpha4,opacity5:q.alpha5,dividerColor:he(q.alphaDivider),borderColor:he(q.alphaBorder),closeIconColorHover:he(Number(q.alphaClose)),closeIconColor:he(Number(q.alphaClose)),closeIconColorPressed:he(Number(q.alphaClose)),closeColorHover:"rgba(255, 255, 255, .12)",closeColorPressed:"rgba(255, 255, 255, .08)",clearColor:he(q.alpha4),clearColorHover:Ne(he(q.alpha4),{alpha:1.25}),clearColorPressed:Ne(he(q.alpha4),{alpha:.8}),scrollbarColor:he(q.alphaScrollbar),scrollbarColorHover:he(q.alphaScrollbarHover),scrollbarWidth:"5px",scrollbarHeight:"5px",scrollbarBorderRadius:"5px",progressRailColor:he(q.alphaProgressRail),railColor:he(q.alphaRail),popoverColor:q.neutralPopover,tableColor:q.neutralCard,cardColor:q.neutralCard,modalColor:q.neutralModal,bodyColor:q.neutralBody,tagColor:t_(q.alphaTag),avatarColor:he(q.alphaAvatar),invertedColor:q.neutralBase,inputColor:he(q.alphaInput),codeColor:he(q.alphaCode),tabColor:he(q.alphaTab),actionColor:he(q.alphaAction),tableHeaderColor:he(q.alphaAction),hoverColor:he(q.alphaPending),tableColorHover:he(q.alphaTablePending),tableColorStriped:he(q.alphaTableStriped),pressedColor:he(q.alphaPressed),opacityDisabled:q.alphaDisabled,inputColorDisabled:he(q.alphaDisabledInput),buttonColor2:"rgba(255, 255, 255, .08)",buttonColor2Hover:"rgba(255, 255, 255, .12)",buttonColor2Pressed:"rgba(255, 255, 255, .08)",boxShadow1:"0 1px 2px -2px rgba(0, 0, 0, .24), 0 3px 6px 0 rgba(0, 0, 0, .18), 0 5px 12px 4px rgba(0, 0, 0, .12)",boxShadow2:"0 3px 6px -4px rgba(0, 0, 0, .24), 0 6px 12px 0 rgba(0, 0, 0, .16), 0 9px 18px 8px rgba(0, 0, 0, .10)",boxShadow3:"0 6px 16px -9px rgba(0, 0, 0, .08), 0 9px 28px 0 rgba(0, 0, 0, .05), 0 12px 48px 16px rgba(0, 0, 0, .03)"},re={neutralBase:"#FFF",neutralInvertBase:"#000",neutralTextBase:"#000",neutralPopover:"#fff",neutralCard:"#fff",neutralModal:"#fff",neutralBody:"#fff",alpha1:"0.82",alpha2:"0.72",alpha3:"0.38",alpha4:"0.24",alpha5:"0.18",alphaClose:"0.6",alphaDisabled:"0.5",alphaAvatar:"0.2",alphaProgressRail:".08",alphaInput:"0",alphaScrollbar:"0.25",alphaScrollbarHover:"0.4",primaryHover:"#36ad6a",primaryDefault:"#18a058",primaryActive:"#0c7a43",primarySuppl:"#36ad6a",infoHover:"#4098fc",infoDefault:"#2080f0",infoActive:"#1060c9",infoSuppl:"#4098fc",errorHover:"#de576d",errorDefault:"#d03050",errorActive:"#ab1f3f",errorSuppl:"#de576d",warningHover:"#fcb040",warningDefault:"#f0a020",warningActive:"#c97c10",warningSuppl:"#fcb040",successHover:"#36ad6a",successDefault:"#18a058",successActive:"#0c7a43",successSuppl:"#36ad6a"},o_=fo(re.neutralBase),kf=fo(re.neutralInvertBase),r_=`rgba(${kf.slice(0,3).join(", ")}, `;function Gs(e){return`${r_+String(e)})`}function Ke(e){const t=Array.from(kf);return t[3]=Number(e),Z(o_,t)}const n_={name:"common",...ua,baseColor:re.neutralBase,primaryColor:re.primaryDefault,primaryColorHover:re.primaryHover,primaryColorPressed:re.primaryActive,primaryColorSuppl:re.primarySuppl,infoColor:re.infoDefault,infoColorHover:re.infoHover,infoColorPressed:re.infoActive,infoColorSuppl:re.infoSuppl,successColor:re.successDefault,successColorHover:re.successHover,successColorPressed:re.successActive,successColorSuppl:re.successSuppl,warningColor:re.warningDefault,warningColorHover:re.warningHover,warningColorPressed:re.warningActive,warningColorSuppl:re.warningSuppl,errorColor:re.errorDefault,errorColorHover:re.errorHover,errorColorPressed:re.errorActive,errorColorSuppl:re.errorSuppl,textColorBase:re.neutralTextBase,textColor1:"rgb(31, 34, 37)",textColor2:"rgb(51, 54, 57)",textColor3:"rgb(118, 124, 130)",textColorDisabled:Ke(re.alpha4),placeholderColor:Ke(re.alpha4),placeholderColorDisabled:Ke(re.alpha5),iconColor:Ke(re.alpha4),iconColorHover:Ne(Ke(re.alpha4),{lightness:.75}),iconColorPressed:Ne(Ke(re.alpha4),{lightness:.9}),iconColorDisabled:Ke(re.alpha5),opacity1:re.alpha1,opacity2:re.alpha2,opacity3:re.alpha3,opacity4:re.alpha4,opacity5:re.alpha5,dividerColor:"rgb(239, 239, 245)",borderColor:"rgb(224, 224, 230)",closeIconColor:Ke(Number(re.alphaClose)),closeIconColorHover:Ke(Number(re.alphaClose)),closeIconColorPressed:Ke(Number(re.alphaClose)),closeColorHover:"rgba(0, 0, 0, .09)",closeColorPressed:"rgba(0, 0, 0, .13)",clearColor:Ke(re.alpha4),clearColorHover:Ne(Ke(re.alpha4),{lightness:.75}),clearColorPressed:Ne(Ke(re.alpha4),{lightness:.9}),scrollbarColor:Gs(re.alphaScrollbar),scrollbarColorHover:Gs(re.alphaScrollbarHover),scrollbarWidth:"5px",scrollbarHeight:"5px",scrollbarBorderRadius:"5px",progressRailColor:Ke(re.alphaProgressRail),railColor:"rgb(219, 219, 223)",popoverColor:re.neutralPopover,tableColor:re.neutralCard,cardColor:re.neutralCard,modalColor:re.neutralModal,bodyColor:re.neutralBody,tagColor:"#eee",avatarColor:Ke(re.alphaAvatar),invertedColor:"rgb(0, 20, 40)",inputColor:Ke(re.alphaInput),codeColor:"rgb(244, 244, 248)",tabColor:"rgb(247, 247, 250)",actionColor:"rgb(250, 250, 252)",tableHeaderColor:"rgb(250, 250, 252)",hoverColor:"rgb(243, 243, 245)",tableColorHover:"rgba(0, 0, 100, 0.03)",tableColorStriped:"rgba(0, 0, 100, 0.02)",pressedColor:"rgb(237, 237, 239)",opacityDisabled:re.alphaDisabled,inputColorDisabled:"rgb(250, 250, 252)",buttonColor2:"rgba(46, 51, 56, .05)",buttonColor2Hover:"rgba(46, 51, 56, .09)",buttonColor2Pressed:"rgba(46, 51, 56, .13)",boxShadow1:"0 1px 2px -2px rgba(0, 0, 0, .08), 0 3px 6px 0 rgba(0, 0, 0, .06), 0 5px 12px 4px rgba(0, 0, 0, .04)",boxShadow2:"0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05)",boxShadow3:"0 6px 16px -9px rgba(0, 0, 0, .08), 0 9px 28px 0 rgba(0, 0, 0, .05), 0 12px 48px 16px rgba(0, 0, 0, .03)"},i_={railInsetHorizontalBottom:"auto 2px 4px 2px",railInsetHorizontalTop:"4px 2px auto 2px",railInsetVerticalRight:"2px 4px 2px auto",railInsetVerticalLeft:"2px auto 2px 4px",railColor:"transparent"};function l_(e){const{scrollbarColor:t,scrollbarColorHover:o,scrollbarHeight:r,scrollbarWidth:n,scrollbarBorderRadius:i}=e;return{...i_,height:r,width:n,borderRadius:i,color:t,colorHover:o}}const et={name:"Scrollbar",common:W,self:l_};var a_={iconSizeTiny:"28px",iconSizeSmall:"34px",iconSizeMedium:"40px",iconSizeLarge:"46px",iconSizeHuge:"52px"};function Hf(e){const{textColorDisabled:t,iconColor:o,textColor2:r,fontSizeTiny:n,fontSizeSmall:i,fontSizeMedium:l,fontSizeLarge:a,fontSizeHuge:s}=e;return{...a_,fontSizeTiny:n,fontSizeSmall:i,fontSizeMedium:l,fontSizeLarge:a,fontSizeHuge:s,textColor:t,iconColor:o,extraTextColor:r}}const s_={name:"Empty",common:n_,self:Hf},rr={name:"Empty",common:W,self:Hf};function c_(e,t,o,r,n,i){const l=Bx(),a=Ze(Il,null);if(o){const s=()=>{const c=i?.value;o.mount({id:c===void 0?t:c+t,head:!0,props:{bPrefix:c?`.${c}-`:void 0},anchorMetaName:Vs,ssr:l,parent:a?.styleMountTarget}),a?.preflightStyleDisabled||Nx.mount({id:"n-global",head:!0,anchorMetaName:Vs,ssr:l,parent:a?.styleMountTarget})};l?s():Jl(s)}return fe(()=>{const{theme:{common:s,self:c,peers:u={}}={},themeOverrides:f={},builtinThemeOverrides:d={}}=n,{common:p,peers:g}=f,{common:C=void 0,[e]:{common:S=void 0,self:E=void 0,peers:T={}}={}}=a?.mergedThemeRef.value||{},{common:v=void 0,[e]:y={}}=a?.mergedThemeOverridesRef.value||{},{common:L,peers:w={}}=y,D=kr({},s||S||C||r.common,v,L,p);return{common:D,self:kr((c||E||r.self)?.(D),d,y,f),peers:kr({},r.peers,T,u),peerOverrides:kr({},d.peers,w,g)}})}c_.props={theme:Object,themeOverrides:Object,builtinThemeOverrides:Object};var u_={height:"calc(var(--n-option-height) * 7.6)",paddingTiny:"4px 0",paddingSmall:"4px 0",paddingMedium:"4px 0",paddingLarge:"4px 0",paddingHuge:"4px 0",optionPaddingTiny:"0 12px",optionPaddingSmall:"0 12px",optionPaddingMedium:"0 12px",optionPaddingLarge:"0 12px",optionPaddingHuge:"0 12px",loadingSize:"18px"};function f_(e){const{borderRadius:t,popoverColor:o,textColor3:r,dividerColor:n,textColor2:i,primaryColorPressed:l,textColorDisabled:a,primaryColor:s,opacityDisabled:c,hoverColor:u,fontSizeTiny:f,fontSizeSmall:d,fontSizeMedium:p,fontSizeLarge:g,fontSizeHuge:C,heightTiny:S,heightSmall:E,heightMedium:T,heightLarge:v,heightHuge:y}=e;return{...u_,optionFontSizeTiny:f,optionFontSizeSmall:d,optionFontSizeMedium:p,optionFontSizeLarge:g,optionFontSizeHuge:C,optionHeightTiny:S,optionHeightSmall:E,optionHeightMedium:T,optionHeightLarge:v,optionHeightHuge:y,borderRadius:t,color:o,groupHeaderTextColor:r,actionDividerColor:n,optionTextColor:i,optionTextColorPressed:l,optionTextColorDisabled:a,optionTextColorActive:s,optionOpacityDisabled:c,optionCheckColor:s,optionColorPending:u,optionColorActive:"rgba(0, 0, 0, 0)",optionColorActivePending:u,actionTextColor:i,loadingColor:s}}const gn={name:"InternalSelectMenu",common:W,peers:{Scrollbar:et,Empty:rr},self:f_};var d_={space:"6px",spaceArrow:"10px",arrowOffset:"10px",arrowOffsetVertical:"10px",arrowHeight:"6px",padding:"8px 14px"};function p_(e){const{boxShadow2:t,popoverColor:o,textColor2:r,borderRadius:n,fontSize:i,dividerColor:l}=e;return{...d_,fontSize:i,borderRadius:n,color:o,dividerColor:l,textColor:r,boxShadow:t}}const nr={name:"Popover",common:W,peers:{Scrollbar:et},self:p_};function Ks(e){const t=fe(e),o=mt(t.value);return St(t,r=>{o.value=r}),typeof e=="function"?o:{__v_isRef:!0,get value(){return o.value},set value(r){e.set(r)}}}var m_={closeIconSizeTiny:"12px",closeIconSizeSmall:"12px",closeIconSizeMedium:"14px",closeIconSizeLarge:"14px",closeSizeTiny:"16px",closeSizeSmall:"16px",closeSizeMedium:"18px",closeSizeLarge:"18px",padding:"0 7px",closeMargin:"0 0 0 4px"};const $f={name:"Tag",common:W,self(e){const{textColor2:t,primaryColorHover:o,primaryColorPressed:r,primaryColor:n,infoColor:i,successColor:l,warningColor:a,errorColor:s,baseColor:c,borderColor:u,tagColor:f,opacityDisabled:d,closeIconColor:p,closeIconColorHover:g,closeIconColorPressed:C,closeColorHover:S,closeColorPressed:E,borderRadiusSmall:T,fontSizeMini:v,fontSizeTiny:y,fontSizeSmall:L,fontSizeMedium:w,heightMini:D,heightTiny:F,heightSmall:P,heightMedium:U,buttonColor2Hover:X,buttonColor2Pressed:k,fontWeightStrong:Q}=e;return{...m_,closeBorderRadius:T,heightTiny:D,heightSmall:F,heightMedium:P,heightLarge:U,borderRadius:T,opacityDisabled:d,fontSizeTiny:v,fontSizeSmall:y,fontSizeMedium:L,fontSizeLarge:w,fontWeightStrong:Q,textColorCheckable:t,textColorHoverCheckable:t,textColorPressedCheckable:t,textColorChecked:c,colorCheckable:"#0000",colorHoverCheckable:X,colorPressedCheckable:k,colorChecked:n,colorCheckedHover:o,colorCheckedPressed:r,border:`1px solid ${u}`,textColor:t,color:f,colorBordered:"#0000",closeIconColor:p,closeIconColorHover:g,closeIconColorPressed:C,closeColorHover:S,closeColorPressed:E,borderPrimary:`1px solid ${J(n,{alpha:.3})}`,textColorPrimary:n,colorPrimary:J(n,{alpha:.16}),colorBorderedPrimary:"#0000",closeIconColorPrimary:Ne(n,{lightness:.7}),closeIconColorHoverPrimary:Ne(n,{lightness:.7}),closeIconColorPressedPrimary:Ne(n,{lightness:.7}),closeColorHoverPrimary:J(n,{alpha:.16}),closeColorPressedPrimary:J(n,{alpha:.12}),borderInfo:`1px solid ${J(i,{alpha:.3})}`,textColorInfo:i,colorInfo:J(i,{alpha:.16}),colorBorderedInfo:"#0000",closeIconColorInfo:Ne(i,{alpha:.7}),closeIconColorHoverInfo:Ne(i,{alpha:.7}),closeIconColorPressedInfo:Ne(i,{alpha:.7}),closeColorHoverInfo:J(i,{alpha:.16}),closeColorPressedInfo:J(i,{alpha:.12}),borderSuccess:`1px solid ${J(l,{alpha:.3})}`,textColorSuccess:l,colorSuccess:J(l,{alpha:.16}),colorBorderedSuccess:"#0000",closeIconColorSuccess:Ne(l,{alpha:.7}),closeIconColorHoverSuccess:Ne(l,{alpha:.7}),closeIconColorPressedSuccess:Ne(l,{alpha:.7}),closeColorHoverSuccess:J(l,{alpha:.16}),closeColorPressedSuccess:J(l,{alpha:.12}),borderWarning:`1px solid ${J(a,{alpha:.3})}`,textColorWarning:a,colorWarning:J(a,{alpha:.16}),colorBorderedWarning:"#0000",closeIconColorWarning:Ne(a,{alpha:.7}),closeIconColorHoverWarning:Ne(a,{alpha:.7}),closeIconColorPressedWarning:Ne(a,{alpha:.7}),closeColorHoverWarning:J(a,{alpha:.16}),closeColorPressedWarning:J(a,{alpha:.11}),borderError:`1px solid ${J(s,{alpha:.3})}`,textColorError:s,colorError:J(s,{alpha:.16}),colorBorderedError:"#0000",closeIconColorError:Ne(s,{alpha:.7}),closeIconColorHoverError:Ne(s,{alpha:.7}),closeIconColorPressedError:Ne(s,{alpha:.7}),closeColorHoverError:J(s,{alpha:.16}),closeColorPressedError:J(s,{alpha:.12})}}};var h_={paddingSingle:"0 26px 0 12px",paddingMultiple:"3px 26px 0 12px",clearSize:"16px",arrowSize:"16px"};const fa={name:"InternalSelection",common:W,peers:{Popover:nr},self(e){const{borderRadius:t,textColor2:o,textColorDisabled:r,inputColor:n,inputColorDisabled:i,primaryColor:l,primaryColorHover:a,warningColor:s,warningColorHover:c,errorColor:u,errorColorHover:f,iconColor:d,iconColorDisabled:p,clearColor:g,clearColorHover:C,clearColorPressed:S,placeholderColor:E,placeholderColorDisabled:T,fontSizeTiny:v,fontSizeSmall:y,fontSizeMedium:L,fontSizeLarge:w,heightTiny:D,heightSmall:F,heightMedium:P,heightLarge:U,fontWeight:X}=e;return{...h_,fontWeight:X,fontSizeTiny:v,fontSizeSmall:y,fontSizeMedium:L,fontSizeLarge:w,heightTiny:D,heightSmall:F,heightMedium:P,heightLarge:U,borderRadius:t,textColor:o,textColorDisabled:r,placeholderColor:E,placeholderColorDisabled:T,color:n,colorDisabled:i,colorActive:J(l,{alpha:.1}),border:"1px solid #0000",borderHover:`1px solid ${a}`,borderActive:`1px solid ${l}`,borderFocus:`1px solid ${a}`,boxShadowHover:"none",boxShadowActive:`0 0 8px 0 ${J(l,{alpha:.4})}`,boxShadowFocus:`0 0 8px 0 ${J(l,{alpha:.4})}`,caretColor:l,arrowColor:d,arrowColorDisabled:p,loadingColor:l,borderWarning:`1px solid ${s}`,borderHoverWarning:`1px solid ${c}`,borderActiveWarning:`1px solid ${s}`,borderFocusWarning:`1px solid ${c}`,boxShadowHoverWarning:"none",boxShadowActiveWarning:`0 0 8px 0 ${J(s,{alpha:.4})}`,boxShadowFocusWarning:`0 0 8px 0 ${J(s,{alpha:.4})}`,colorActiveWarning:J(s,{alpha:.1}),caretColorWarning:s,borderError:`1px solid ${u}`,borderHoverError:`1px solid ${f}`,borderActiveError:`1px solid ${u}`,borderFocusError:`1px solid ${f}`,boxShadowHoverError:"none",boxShadowActiveError:`0 0 8px 0 ${J(u,{alpha:.4})}`,boxShadowFocusError:`0 0 8px 0 ${J(u,{alpha:.4})}`,colorActiveError:J(u,{alpha:.1}),caretColorError:u,clearColor:g,clearColorHover:C,clearColorPressed:S}}};var g_={iconMargin:"11px 8px 0 12px",iconMarginRtl:"11px 12px 0 8px",iconSize:"24px",closeIconSize:"16px",closeSize:"20px",closeMargin:"13px 14px 0 0",closeMarginRtl:"13px 0 0 14px",padding:"13px"};const C_={name:"Alert",common:W,self(e){const{lineHeight:t,borderRadius:o,fontWeightStrong:r,dividerColor:n,inputColor:i,textColor1:l,textColor2:a,closeColorHover:s,closeColorPressed:c,closeIconColor:u,closeIconColorHover:f,closeIconColorPressed:d,infoColorSuppl:p,successColorSuppl:g,warningColorSuppl:C,errorColorSuppl:S,fontSize:E}=e;return{...g_,fontSize:E,lineHeight:t,titleFontWeight:r,borderRadius:o,border:`1px solid ${n}`,color:i,titleTextColor:l,iconColor:a,contentTextColor:a,closeBorderRadius:o,closeColorHover:s,closeColorPressed:c,closeIconColor:u,closeIconColorHover:f,closeIconColorPressed:d,borderInfo:`1px solid ${J(p,{alpha:.35})}`,colorInfo:J(p,{alpha:.25}),titleTextColorInfo:l,iconColorInfo:p,contentTextColorInfo:a,closeColorHoverInfo:s,closeColorPressedInfo:c,closeIconColorInfo:u,closeIconColorHoverInfo:f,closeIconColorPressedInfo:d,borderSuccess:`1px solid ${J(g,{alpha:.35})}`,colorSuccess:J(g,{alpha:.25}),titleTextColorSuccess:l,iconColorSuccess:g,contentTextColorSuccess:a,closeColorHoverSuccess:s,closeColorPressedSuccess:c,closeIconColorSuccess:u,closeIconColorHoverSuccess:f,closeIconColorPressedSuccess:d,borderWarning:`1px solid ${J(C,{alpha:.35})}`,colorWarning:J(C,{alpha:.25}),titleTextColorWarning:l,iconColorWarning:C,contentTextColorWarning:a,closeColorHoverWarning:s,closeColorPressedWarning:c,closeIconColorWarning:u,closeIconColorHoverWarning:f,closeIconColorPressedWarning:d,borderError:`1px solid ${J(S,{alpha:.35})}`,colorError:J(S,{alpha:.25}),titleTextColorError:l,iconColorError:S,contentTextColorError:a,closeColorHoverError:s,closeColorPressedError:c,closeIconColorError:u,closeIconColorHoverError:f,closeIconColorPressedError:d}}};var b_={linkFontSize:"13px",linkPadding:"0 0 0 16px",railWidth:"4px"};function x_(e){const{borderRadius:t,railColor:o,primaryColor:r,primaryColorHover:n,primaryColorPressed:i,textColor2:l}=e;return{...b_,borderRadius:t,railColor:o,railColorActive:r,linkColor:J(r,{alpha:.15}),linkTextColor:l,linkTextColorHover:n,linkTextColorPressed:i,linkTextColorActive:r}}const __={name:"Anchor",common:W,self:x_};var v_={paddingTiny:"0 8px",paddingSmall:"0 10px",paddingMedium:"0 12px",paddingLarge:"0 14px",clearSize:"16px"};function S_(e){const{textColor2:t,textColor3:o,textColorDisabled:r,primaryColor:n,primaryColorHover:i,inputColor:l,inputColorDisabled:a,warningColor:s,warningColorHover:c,errorColor:u,errorColorHover:f,borderRadius:d,lineHeight:p,fontSizeTiny:g,fontSizeSmall:C,fontSizeMedium:S,fontSizeLarge:E,heightTiny:T,heightSmall:v,heightMedium:y,heightLarge:L,clearColor:w,clearColorHover:D,clearColorPressed:F,placeholderColor:P,placeholderColorDisabled:U,iconColor:X,iconColorDisabled:k,iconColorHover:Q,iconColorPressed:me,fontWeight:ye}=e;return{...v_,fontWeight:ye,countTextColorDisabled:r,countTextColor:o,heightTiny:T,heightSmall:v,heightMedium:y,heightLarge:L,fontSizeTiny:g,fontSizeSmall:C,fontSizeMedium:S,fontSizeLarge:E,lineHeight:p,lineHeightTextarea:p,borderRadius:d,iconSize:"16px",groupLabelColor:l,textColor:t,textColorDisabled:r,textDecorationColor:t,groupLabelTextColor:t,caretColor:n,placeholderColor:P,placeholderColorDisabled:U,color:l,colorHover:l,colorDisabled:a,colorFocus:J(n,{alpha:.1}),groupLabelBorder:"1px solid #0000",border:"1px solid #0000",borderHover:`1px solid ${i}`,borderDisabled:"1px solid #0000",borderFocus:`1px solid ${i}`,boxShadowFocus:`0 0 8px 0 ${J(n,{alpha:.3})}`,loadingColor:n,loadingColorWarning:s,borderWarning:`1px solid ${s}`,borderHoverWarning:`1px solid ${c}`,colorFocusWarning:J(s,{alpha:.1}),borderFocusWarning:`1px solid ${c}`,boxShadowFocusWarning:`0 0 8px 0 ${J(s,{alpha:.3})}`,caretColorWarning:s,loadingColorError:u,borderError:`1px solid ${u}`,borderHoverError:`1px solid ${f}`,colorFocusError:J(u,{alpha:.1}),borderFocusError:`1px solid ${f}`,boxShadowFocusError:`0 0 8px 0 ${J(u,{alpha:.3})}`,caretColorError:u,clearColor:w,clearColorHover:D,clearColorPressed:F,iconColor:X,iconColorDisabled:k,iconColorHover:Q,iconColorPressed:me,suffixTextColor:t}}const Et={name:"Input",common:W,peers:{Scrollbar:et},self:S_};function y_(e){const{boxShadow2:t}=e;return{menuBoxShadow:t}}const E_={name:"AutoComplete",common:W,peers:{InternalSelectMenu:gn,Input:Et},self:y_};function T_(e){const{borderRadius:t,avatarColor:o,cardColor:r,fontSize:n,heightTiny:i,heightSmall:l,heightMedium:a,heightLarge:s,heightHuge:c,modalColor:u,popoverColor:f}=e;return{borderRadius:t,fontSize:n,border:`2px solid ${r}`,heightTiny:i,heightSmall:l,heightMedium:a,heightLarge:s,heightHuge:c,color:Z(r,o),colorModal:Z(u,o),colorPopover:Z(f,o)}}const Bf={name:"Avatar",common:W,self:T_};function P_(){return{gap:"-12px"}}var I_={width:"44px",height:"44px",borderRadius:"22px",iconSize:"26px"};const A_={name:"BackTop",common:W,self(e){const{popoverColor:t,textColor2:o,primaryColorHover:r,primaryColorPressed:n}=e;return{...I_,color:t,textColor:o,iconColor:o,iconColorHover:r,iconColorPressed:n,boxShadow:"0 2px 8px 0px rgba(0, 0, 0, .12)",boxShadowHover:"0 2px 12px 0px rgba(0, 0, 0, .18)",boxShadowPressed:"0 2px 12px 0px rgba(0, 0, 0, .18)"}}},L_={name:"Badge",common:W,self(e){const{errorColorSuppl:t,infoColorSuppl:o,successColorSuppl:r,warningColorSuppl:n,fontFamily:i}=e;return{color:t,colorInfo:o,colorSuccess:r,colorError:t,colorWarning:n,fontSize:"12px",fontFamily:i}}};var w_={fontWeightActive:"400"};function D_(e){const{fontSize:t,textColor3:o,textColor2:r,borderRadius:n,buttonColor2Hover:i,buttonColor2Pressed:l}=e;return{...w_,fontSize:t,itemLineHeight:"1.25",itemTextColor:o,itemTextColorHover:r,itemTextColorPressed:r,itemTextColorActive:r,itemBorderRadius:n,itemColorHover:i,itemColorPressed:l,separatorColor:o}}const R_={name:"Breadcrumb",common:W,self:D_};var F_={paddingTiny:"0 6px",paddingSmall:"0 10px",paddingMedium:"0 14px",paddingLarge:"0 18px",paddingRoundTiny:"0 10px",paddingRoundSmall:"0 14px",paddingRoundMedium:"0 18px",paddingRoundLarge:"0 22px",iconMarginTiny:"6px",iconMarginSmall:"6px",iconMarginMedium:"6px",iconMarginLarge:"6px",iconSizeTiny:"14px",iconSizeSmall:"18px",iconSizeMedium:"18px",iconSizeLarge:"20px",rippleDuration:".6s"};function O_(e){const{heightTiny:t,heightSmall:o,heightMedium:r,heightLarge:n,borderRadius:i,fontSizeTiny:l,fontSizeSmall:a,fontSizeMedium:s,fontSizeLarge:c,opacityDisabled:u,textColor2:f,textColor3:d,primaryColorHover:p,primaryColorPressed:g,borderColor:C,primaryColor:S,baseColor:E,infoColor:T,infoColorHover:v,infoColorPressed:y,successColor:L,successColorHover:w,successColorPressed:D,warningColor:F,warningColorHover:P,warningColorPressed:U,errorColor:X,errorColorHover:k,errorColorPressed:Q,fontWeight:me,buttonColor2:ye,buttonColor2Hover:se,buttonColor2Pressed:ne,fontWeightStrong:de}=e;return{...F_,heightTiny:t,heightSmall:o,heightMedium:r,heightLarge:n,borderRadiusTiny:i,borderRadiusSmall:i,borderRadiusMedium:i,borderRadiusLarge:i,fontSizeTiny:l,fontSizeSmall:a,fontSizeMedium:s,fontSizeLarge:c,opacityDisabled:u,colorOpacitySecondary:"0.16",colorOpacitySecondaryHover:"0.22",colorOpacitySecondaryPressed:"0.28",colorSecondary:ye,colorSecondaryHover:se,colorSecondaryPressed:ne,colorTertiary:ye,colorTertiaryHover:se,colorTertiaryPressed:ne,colorQuaternary:"#0000",colorQuaternaryHover:se,colorQuaternaryPressed:ne,color:"#0000",colorHover:"#0000",colorPressed:"#0000",colorFocus:"#0000",colorDisabled:"#0000",textColor:f,textColorTertiary:d,textColorHover:p,textColorPressed:g,textColorFocus:p,textColorDisabled:f,textColorText:f,textColorTextHover:p,textColorTextPressed:g,textColorTextFocus:p,textColorTextDisabled:f,textColorGhost:f,textColorGhostHover:p,textColorGhostPressed:g,textColorGhostFocus:p,textColorGhostDisabled:f,border:`1px solid ${C}`,borderHover:`1px solid ${p}`,borderPressed:`1px solid ${g}`,borderFocus:`1px solid ${p}`,borderDisabled:`1px solid ${C}`,rippleColor:S,colorPrimary:S,colorHoverPrimary:p,colorPressedPrimary:g,colorFocusPrimary:p,colorDisabledPrimary:S,textColorPrimary:E,textColorHoverPrimary:E,textColorPressedPrimary:E,textColorFocusPrimary:E,textColorDisabledPrimary:E,textColorTextPrimary:S,textColorTextHoverPrimary:p,textColorTextPressedPrimary:g,textColorTextFocusPrimary:p,textColorTextDisabledPrimary:f,textColorGhostPrimary:S,textColorGhostHoverPrimary:p,textColorGhostPressedPrimary:g,textColorGhostFocusPrimary:p,textColorGhostDisabledPrimary:S,borderPrimary:`1px solid ${S}`,borderHoverPrimary:`1px solid ${p}`,borderPressedPrimary:`1px solid ${g}`,borderFocusPrimary:`1px solid ${p}`,borderDisabledPrimary:`1px solid ${S}`,rippleColorPrimary:S,colorInfo:T,colorHoverInfo:v,colorPressedInfo:y,colorFocusInfo:v,colorDisabledInfo:T,textColorInfo:E,textColorHoverInfo:E,textColorPressedInfo:E,textColorFocusInfo:E,textColorDisabledInfo:E,textColorTextInfo:T,textColorTextHoverInfo:v,textColorTextPressedInfo:y,textColorTextFocusInfo:v,textColorTextDisabledInfo:f,textColorGhostInfo:T,textColorGhostHoverInfo:v,textColorGhostPressedInfo:y,textColorGhostFocusInfo:v,textColorGhostDisabledInfo:T,borderInfo:`1px solid ${T}`,borderHoverInfo:`1px solid ${v}`,borderPressedInfo:`1px solid ${y}`,borderFocusInfo:`1px solid ${v}`,borderDisabledInfo:`1px solid ${T}`,rippleColorInfo:T,colorSuccess:L,colorHoverSuccess:w,colorPressedSuccess:D,colorFocusSuccess:w,colorDisabledSuccess:L,textColorSuccess:E,textColorHoverSuccess:E,textColorPressedSuccess:E,textColorFocusSuccess:E,textColorDisabledSuccess:E,textColorTextSuccess:L,textColorTextHoverSuccess:w,textColorTextPressedSuccess:D,textColorTextFocusSuccess:w,textColorTextDisabledSuccess:f,textColorGhostSuccess:L,textColorGhostHoverSuccess:w,textColorGhostPressedSuccess:D,textColorGhostFocusSuccess:w,textColorGhostDisabledSuccess:L,borderSuccess:`1px solid ${L}`,borderHoverSuccess:`1px solid ${w}`,borderPressedSuccess:`1px solid ${D}`,borderFocusSuccess:`1px solid ${w}`,borderDisabledSuccess:`1px solid ${L}`,rippleColorSuccess:L,colorWarning:F,colorHoverWarning:P,colorPressedWarning:U,colorFocusWarning:P,colorDisabledWarning:F,textColorWarning:E,textColorHoverWarning:E,textColorPressedWarning:E,textColorFocusWarning:E,textColorDisabledWarning:E,textColorTextWarning:F,textColorTextHoverWarning:P,textColorTextPressedWarning:U,textColorTextFocusWarning:P,textColorTextDisabledWarning:f,textColorGhostWarning:F,textColorGhostHoverWarning:P,textColorGhostPressedWarning:U,textColorGhostFocusWarning:P,textColorGhostDisabledWarning:F,borderWarning:`1px solid ${F}`,borderHoverWarning:`1px solid ${P}`,borderPressedWarning:`1px solid ${U}`,borderFocusWarning:`1px solid ${P}`,borderDisabledWarning:`1px solid ${F}`,rippleColorWarning:F,colorError:X,colorHoverError:k,colorPressedError:Q,colorFocusError:k,colorDisabledError:X,textColorError:E,textColorHoverError:E,textColorPressedError:E,textColorFocusError:E,textColorDisabledError:E,textColorTextError:X,textColorTextHoverError:k,textColorTextPressedError:Q,textColorTextFocusError:k,textColorTextDisabledError:f,textColorGhostError:X,textColorGhostHoverError:k,textColorGhostPressedError:Q,textColorGhostFocusError:k,textColorGhostDisabledError:X,borderError:`1px solid ${X}`,borderHoverError:`1px solid ${k}`,borderPressedError:`1px solid ${Q}`,borderFocusError:`1px solid ${k}`,borderDisabledError:`1px solid ${X}`,rippleColorError:X,waveOpacity:"0.6",fontWeight:me,fontWeightStrong:de}}const ut={name:"Button",common:W,self(e){const t=O_(e);return t.waveOpacity="0.8",t.colorOpacitySecondary="0.16",t.colorOpacitySecondaryHover="0.2",t.colorOpacitySecondaryPressed="0.12",t}};var N_={titleFontSize:"22px"};function M_(e){const{borderRadius:t,fontSize:o,lineHeight:r,textColor2:n,textColor1:i,textColorDisabled:l,dividerColor:a,fontWeightStrong:s,primaryColor:c,baseColor:u,hoverColor:f,cardColor:d,modalColor:p,popoverColor:g}=e;return{...N_,borderRadius:t,borderColor:Z(d,a),borderColorModal:Z(p,a),borderColorPopover:Z(g,a),textColor:n,titleFontWeight:s,titleTextColor:i,dayTextColor:l,fontSize:o,lineHeight:r,dateColorCurrent:c,dateTextColorCurrent:u,cellColorHover:Z(d,f),cellColorHoverModal:Z(p,f),cellColorHoverPopover:Z(g,f),cellColor:d,cellColorModal:p,cellColorPopover:g,barColor:c}}var k_={paddingSmall:"12px 16px 12px",paddingMedium:"19px 24px 20px",paddingLarge:"23px 32px 24px",paddingHuge:"27px 40px 28px",titleFontSizeSmall:"16px",titleFontSizeMedium:"18px",titleFontSizeLarge:"18px",titleFontSizeHuge:"18px",closeIconSize:"18px",closeSize:"22px"};function H_(e){const{primaryColor:t,borderRadius:o,lineHeight:r,fontSize:n,cardColor:i,textColor2:l,textColor1:a,dividerColor:s,fontWeightStrong:c,closeIconColor:u,closeIconColorHover:f,closeIconColorPressed:d,closeColorHover:p,closeColorPressed:g,modalColor:C,boxShadow1:S,popoverColor:E,actionColor:T}=e;return{...k_,lineHeight:r,color:i,colorModal:C,colorPopover:E,colorTarget:t,colorEmbedded:T,colorEmbeddedModal:T,colorEmbeddedPopover:T,textColor:l,titleTextColor:a,borderColor:s,actionColor:T,titleFontWeight:c,closeColorHover:p,closeColorPressed:g,closeBorderRadius:o,closeIconColor:u,closeIconColorHover:f,closeIconColorPressed:d,fontSizeSmall:n,fontSizeMedium:n,fontSizeLarge:n,fontSizeHuge:n,boxShadow:S,borderRadius:o}}const Wf={name:"Card",common:W,self(e){const t=H_(e),{cardColor:o,modalColor:r,popoverColor:n}=e;return t.colorEmbedded=o,t.colorEmbeddedModal=r,t.colorEmbeddedPopover=n,t}};function $_(){return{dotSize:"8px",dotColor:"rgba(255, 255, 255, .3)",dotColorActive:"rgba(255, 255, 255, 1)",dotColorFocus:"rgba(255, 255, 255, .5)",dotLineWidth:"16px",dotLineWidthActive:"24px",arrowColor:"#eee"}}var B_={sizeSmall:"14px",sizeMedium:"16px",sizeLarge:"18px",labelPadding:"0 8px",labelFontWeight:"400"};function W_(e){const{baseColor:t,inputColorDisabled:o,cardColor:r,modalColor:n,popoverColor:i,textColorDisabled:l,borderColor:a,primaryColor:s,textColor2:c,fontSizeSmall:u,fontSizeMedium:f,fontSizeLarge:d,borderRadiusSmall:p,lineHeight:g}=e;return{...B_,labelLineHeight:g,fontSizeSmall:u,fontSizeMedium:f,fontSizeLarge:d,borderRadius:p,color:t,colorChecked:s,colorDisabled:o,colorDisabledChecked:o,colorTableHeader:r,colorTableHeaderModal:n,colorTableHeaderPopover:i,checkMarkColor:t,checkMarkColorDisabled:l,checkMarkColorDisabledChecked:l,border:`1px solid ${a}`,borderDisabled:`1px solid ${a}`,borderDisabledChecked:`1px solid ${a}`,borderChecked:`1px solid ${s}`,borderFocus:`1px solid ${s}`,boxShadowFocus:`0 0 0 2px ${J(s,{alpha:.3})}`,textColor:c,textColorDisabled:l}}const Tr={name:"Checkbox",common:W,self(e){const{cardColor:t}=e,o=W_(e);return o.color="#0000",o.checkMarkColor=t,o}};function z_(e){const{borderRadius:t,boxShadow2:o,popoverColor:r,textColor2:n,textColor3:i,primaryColor:l,textColorDisabled:a,dividerColor:s,hoverColor:c,fontSizeMedium:u,heightMedium:f}=e;return{menuBorderRadius:t,menuColor:r,menuBoxShadow:o,menuDividerColor:s,menuHeight:"calc(var(--n-option-height) * 6.6)",optionArrowColor:i,optionHeight:f,optionFontSize:u,optionColorHover:c,optionTextColor:n,optionTextColorActive:l,optionTextColorDisabled:a,optionCheckMarkColor:l,loadingColor:l,columnWidth:"180px"}}const U_={name:"Cascader",common:W,peers:{InternalSelectMenu:gn,InternalSelection:fa,Scrollbar:et,Checkbox:Tr,Empty:s_},self:z_},zf={name:"Code",common:W,self(e){const{textColor2:t,fontSize:o,fontWeightStrong:r,textColor3:n}=e;return{textColor:t,fontSize:o,fontWeightStrong:r,"mono-3":"#5c6370","hue-1":"#56b6c2","hue-2":"#61aeee","hue-3":"#c678dd","hue-4":"#98c379","hue-5":"#e06c75","hue-5-2":"#be5046","hue-6":"#d19a66","hue-6-2":"#e6c07b",lineNumberTextColor:n}}};function V_(e){const{fontWeight:t,textColor1:o,textColor2:r,textColorDisabled:n,dividerColor:i,fontSize:l}=e;return{titleFontSize:l,titleFontWeight:t,dividerColor:i,titleTextColor:o,titleTextColorDisabled:n,fontSize:l,textColor:r,arrowColor:r,arrowColorDisabled:n,itemMargin:"16px 0 0 0",titlePadding:"16px 0 0 0"}}const j_={name:"Collapse",common:W,self:V_};function G_(e){const{cubicBezierEaseInOut:t}=e;return{bezier:t}}function K_(e){const{fontSize:t,boxShadow2:o,popoverColor:r,textColor2:n,borderRadius:i,borderColor:l,heightSmall:a,heightMedium:s,heightLarge:c,fontSizeSmall:u,fontSizeMedium:f,fontSizeLarge:d,dividerColor:p}=e;return{panelFontSize:t,boxShadow:o,color:r,textColor:n,borderRadius:i,border:`1px solid ${l}`,heightSmall:a,heightMedium:s,heightLarge:c,fontSizeSmall:u,fontSizeMedium:f,fontSizeLarge:d,dividerColor:p}}const Y_={abstract:Boolean,bordered:{type:Boolean,default:void 0},clsPrefix:String,locale:Object,dateLocale:Object,namespace:String,rtl:Array,tag:{type:String,default:"div"},hljs:Object,katex:Object,theme:Object,themeOverrides:Object,componentOptions:Object,icons:Object,breakpoints:Object,preflightStyleDisabled:Boolean,styleMountTarget:Object,inlineThemeDisabled:{type:Boolean,default:void 0},as:{type:String,validator:()=>(Dx("config-provider","`as` is deprecated, please use `tag` instead."),!0),default:void 0}};var q_=po({name:"ConfigProvider",alias:["App"],props:Y_,setup(e){const t=Ze(Il,null),o=fe(()=>{const{theme:C}=e;if(C===null)return;const S=t?.mergedThemeRef.value;return C===void 0?S:S===void 0?C:Object.assign({},S,C)}),r=fe(()=>{const{themeOverrides:C}=e;if(C!==null){if(C===void 0)return t?.mergedThemeOverridesRef.value;{const S=t?.mergedThemeOverridesRef.value;return S===void 0?C:kr({},S,C)}}}),n=Ks(()=>{const{namespace:C}=e;return C===void 0?t?.mergedNamespaceRef.value:C}),i=Ks(()=>{const{bordered:C}=e;return C===void 0?t?.mergedBorderedRef.value:C}),l=fe(()=>{const{icons:C}=e;return C===void 0?t?.mergedIconsRef.value:C}),a=fe(()=>{const{componentOptions:C}=e;return C!==void 0?C:t?.mergedComponentPropsRef.value}),s=fe(()=>{const{clsPrefix:C}=e;return C!==void 0?C:t?t.mergedClsPrefixRef.value:"n"}),c=fe(()=>{const{rtl:C}=e;if(C===void 0)return t?.mergedRtlRef.value;const S={};for(const E of C)S[E.name]=qr(E),E.peers?.forEach(T=>{T.name in S||(S[T.name]=qr(T))});return S}),u=fe(()=>e.breakpoints||t?.mergedBreakpointsRef.value),f=e.inlineThemeDisabled||t?.inlineThemeDisabled,d=e.preflightStyleDisabled||t?.preflightStyleDisabled,p=e.styleMountTarget||t?.styleMountTarget,g=fe(()=>{const{value:C}=o,{value:S}=r,E=S&&Object.keys(S).length!==0,T=C?.name;return T?E?`${T}-${Sl(JSON.stringify(r.value))}`:T:E?Sl(JSON.stringify(r.value)):""});return Wr(Il,{mergedThemeHashRef:g,mergedBreakpointsRef:u,mergedRtlRef:c,mergedIconsRef:l,mergedComponentPropsRef:a,mergedBorderedRef:i,mergedNamespaceRef:n,mergedClsPrefixRef:s,mergedLocaleRef:fe(()=>{const{locale:C}=e;if(C!==null)return C===void 0?t?.mergedLocaleRef.value:C}),mergedDateLocaleRef:fe(()=>{const{dateLocale:C}=e;if(C!==null)return C===void 0?t?.mergedDateLocaleRef.value:C}),mergedHljsRef:fe(()=>{const{hljs:C}=e;return C===void 0?t?.mergedHljsRef.value:C}),mergedKatexRef:fe(()=>{const{katex:C}=e;return C===void 0?t?.mergedKatexRef.value:C}),mergedThemeRef:o,mergedThemeOverridesRef:r,inlineThemeDisabled:f||!1,preflightStyleDisabled:d||!1,styleMountTarget:p}),{mergedClsPrefix:s,mergedBordered:i,mergedNamespace:n,mergedTheme:o,mergedThemeOverrides:r}},render(){return this.abstract?this.$slots.default?.():vr(this.as||this.tag,{class:`${this.mergedClsPrefix||"n"}-config-provider`},this.$slots.default?.())}});const Uf={name:"Popselect",common:W,peers:{Popover:nr,InternalSelectMenu:gn}};function X_(e){const{boxShadow2:t}=e;return{menuBoxShadow:t}}const Vf={name:"Select",common:W,peers:{InternalSelection:fa,InternalSelectMenu:gn},self:X_};var J_={itemPaddingSmall:"0 4px",itemMarginSmall:"0 0 0 8px",itemMarginSmallRtl:"0 8px 0 0",itemPaddingMedium:"0 4px",itemMarginMedium:"0 0 0 8px",itemMarginMediumRtl:"0 8px 0 0",itemPaddingLarge:"0 4px",itemMarginLarge:"0 0 0 8px",itemMarginLargeRtl:"0 8px 0 0",buttonIconSizeSmall:"14px",buttonIconSizeMedium:"16px",buttonIconSizeLarge:"18px",inputWidthSmall:"60px",selectWidthSmall:"unset",inputMarginSmall:"0 0 0 8px",inputMarginSmallRtl:"0 8px 0 0",selectMarginSmall:"0 0 0 8px",prefixMarginSmall:"0 8px 0 0",suffixMarginSmall:"0 0 0 8px",inputWidthMedium:"60px",selectWidthMedium:"unset",inputMarginMedium:"0 0 0 8px",inputMarginMediumRtl:"0 8px 0 0",selectMarginMedium:"0 0 0 8px",prefixMarginMedium:"0 8px 0 0",suffixMarginMedium:"0 0 0 8px",inputWidthLarge:"60px",selectWidthLarge:"unset",inputMarginLarge:"0 0 0 8px",inputMarginLargeRtl:"0 8px 0 0",selectMarginLarge:"0 0 0 8px",prefixMarginLarge:"0 8px 0 0",suffixMarginLarge:"0 0 0 8px"};function Q_(e){const{textColor2:t,primaryColor:o,primaryColorHover:r,primaryColorPressed:n,inputColorDisabled:i,textColorDisabled:l,borderColor:a,borderRadius:s,fontSizeTiny:c,fontSizeSmall:u,fontSizeMedium:f,heightTiny:d,heightSmall:p,heightMedium:g}=e;return{...J_,buttonColor:"#0000",buttonColorHover:"#0000",buttonColorPressed:"#0000",buttonBorder:`1px solid ${a}`,buttonBorderHover:`1px solid ${a}`,buttonBorderPressed:`1px solid ${a}`,buttonIconColor:t,buttonIconColorHover:t,buttonIconColorPressed:t,itemTextColor:t,itemTextColorHover:r,itemTextColorPressed:n,itemTextColorActive:o,itemTextColorDisabled:l,itemColor:"#0000",itemColorHover:"#0000",itemColorPressed:"#0000",itemColorActive:"#0000",itemColorActiveHover:"#0000",itemColorDisabled:i,itemBorder:"1px solid #0000",itemBorderHover:"1px solid #0000",itemBorderPressed:"1px solid #0000",itemBorderActive:`1px solid ${o}`,itemBorderDisabled:`1px solid ${a}`,itemBorderRadius:s,itemSizeSmall:d,itemSizeMedium:p,itemSizeLarge:g,itemFontSizeSmall:c,itemFontSizeMedium:u,itemFontSizeLarge:f,jumperFontSizeSmall:c,jumperFontSizeMedium:u,jumperFontSizeLarge:f,jumperTextColor:t,jumperTextColorDisabled:l}}const jf={name:"Pagination",common:W,peers:{Select:Vf,Input:Et,Popselect:Uf},self(e){const{primaryColor:t,opacity3:o}=e,r=J(t,{alpha:Number(o)}),n=Q_(e);return n.itemBorderActive=`1px solid ${r}`,n.itemBorderDisabled="1px solid #0000",n}};var Z_={padding:"4px 0",optionIconSizeSmall:"14px",optionIconSizeMedium:"16px",optionIconSizeLarge:"16px",optionIconSizeHuge:"18px",optionSuffixWidthSmall:"14px",optionSuffixWidthMedium:"14px",optionSuffixWidthLarge:"16px",optionSuffixWidthHuge:"16px",optionIconSuffixWidthSmall:"32px",optionIconSuffixWidthMedium:"32px",optionIconSuffixWidthLarge:"36px",optionIconSuffixWidthHuge:"36px",optionPrefixWidthSmall:"14px",optionPrefixWidthMedium:"14px",optionPrefixWidthLarge:"16px",optionPrefixWidthHuge:"16px",optionIconPrefixWidthSmall:"36px",optionIconPrefixWidthMedium:"36px",optionIconPrefixWidthLarge:"40px",optionIconPrefixWidthHuge:"40px"};function ev(e){const{primaryColor:t,textColor2:o,dividerColor:r,hoverColor:n,popoverColor:i,invertedColor:l,borderRadius:a,fontSizeSmall:s,fontSizeMedium:c,fontSizeLarge:u,fontSizeHuge:f,heightSmall:d,heightMedium:p,heightLarge:g,heightHuge:C,textColor3:S,opacityDisabled:E}=e;return{...Z_,optionHeightSmall:d,optionHeightMedium:p,optionHeightLarge:g,optionHeightHuge:C,borderRadius:a,fontSizeSmall:s,fontSizeMedium:c,fontSizeLarge:u,fontSizeHuge:f,optionTextColor:o,optionTextColorHover:o,optionTextColorActive:t,optionTextColorChildActive:t,color:i,dividerColor:r,suffixColor:o,prefixColor:o,optionColorHover:n,optionColorActive:J(t,{alpha:.1}),groupHeaderTextColor:S,optionTextColorInverted:"#BBB",optionTextColorHoverInverted:"#FFF",optionTextColorActiveInverted:"#FFF",optionTextColorChildActiveInverted:"#FFF",colorInverted:l,dividerColorInverted:"#BBB",suffixColorInverted:"#BBB",prefixColorInverted:"#BBB",optionColorHoverInverted:t,optionColorActiveInverted:t,groupHeaderTextColorInverted:"#AAA",optionOpacityDisabled:E}}const da={name:"Dropdown",common:W,peers:{Popover:nr},self(e){const{primaryColorSuppl:t,primaryColor:o,popoverColor:r}=e,n=ev(e);return n.colorInverted=r,n.optionColorActive=J(o,{alpha:.15}),n.optionColorActiveInverted=t,n.optionColorHoverInverted=t,n}};var tv={padding:"8px 14px"};const yi={name:"Tooltip",common:W,peers:{Popover:nr},self(e){const{borderRadius:t,boxShadow2:o,popoverColor:r,textColor2:n}=e;return{...tv,borderRadius:t,boxShadow:o,color:r,textColor:n}}};var ov={radioSizeSmall:"14px",radioSizeMedium:"16px",radioSizeLarge:"18px",labelPadding:"0 8px",labelFontWeight:"400"};const Gf={name:"Radio",common:W,self(e){const{borderColor:t,primaryColor:o,baseColor:r,textColorDisabled:n,inputColorDisabled:i,textColor2:l,opacityDisabled:a,borderRadius:s,fontSizeSmall:c,fontSizeMedium:u,fontSizeLarge:f,heightSmall:d,heightMedium:p,heightLarge:g,lineHeight:C}=e;return{...ov,labelLineHeight:C,buttonHeightSmall:d,buttonHeightMedium:p,buttonHeightLarge:g,fontSizeSmall:c,fontSizeMedium:u,fontSizeLarge:f,boxShadow:`inset 0 0 0 1px ${t}`,boxShadowActive:`inset 0 0 0 1px ${o}`,boxShadowFocus:`inset 0 0 0 1px ${o}, 0 0 0 2px ${J(o,{alpha:.3})}`,boxShadowHover:`inset 0 0 0 1px ${o}`,boxShadowDisabled:`inset 0 0 0 1px ${t}`,color:"#0000",colorDisabled:i,colorActive:"#0000",textColor:l,textColorDisabled:n,dotColorActive:o,dotColorDisabled:t,buttonBorderColor:t,buttonBorderColorActive:o,buttonBorderColorHover:o,buttonColor:"#0000",buttonColorActive:o,buttonTextColor:l,buttonTextColorActive:r,buttonTextColorHover:o,opacityDisabled:a,buttonBoxShadowFocus:`inset 0 0 0 1px ${o}, 0 0 0 2px ${J(o,{alpha:.3})}`,buttonBoxShadowHover:`inset 0 0 0 1px ${o}`,buttonBoxShadow:"inset 0 0 0 1px #0000",buttonBorderRadius:s}}},Kf={name:"Ellipsis",common:W,peers:{Tooltip:yi}};var rv={thPaddingSmall:"8px",thPaddingMedium:"12px",thPaddingLarge:"12px",tdPaddingSmall:"8px",tdPaddingMedium:"12px",tdPaddingLarge:"12px",sorterSize:"15px",resizableContainerSize:"8px",resizableSize:"2px",filterSize:"15px",paginationMargin:"12px 0 0 0",emptyPadding:"48px 0",actionPadding:"8px 12px",actionButtonMargin:"0 8px 0 0"};function nv(e){const{cardColor:t,modalColor:o,popoverColor:r,textColor2:n,textColor1:i,tableHeaderColor:l,tableColorHover:a,iconColor:s,primaryColor:c,fontWeightStrong:u,borderRadius:f,lineHeight:d,fontSizeSmall:p,fontSizeMedium:g,fontSizeLarge:C,dividerColor:S,heightSmall:E,opacityDisabled:T,tableColorStriped:v}=e;return{...rv,actionDividerColor:S,lineHeight:d,borderRadius:f,fontSizeSmall:p,fontSizeMedium:g,fontSizeLarge:C,borderColor:Z(t,S),tdColorHover:Z(t,a),tdColorSorting:Z(t,a),tdColorStriped:Z(t,v),thColor:Z(t,l),thColorHover:Z(Z(t,l),a),thColorSorting:Z(Z(t,l),a),tdColor:t,tdTextColor:n,thTextColor:i,thFontWeight:u,thButtonColorHover:a,thIconColor:s,thIconColorActive:c,borderColorModal:Z(o,S),tdColorHoverModal:Z(o,a),tdColorSortingModal:Z(o,a),tdColorStripedModal:Z(o,v),thColorModal:Z(o,l),thColorHoverModal:Z(Z(o,l),a),thColorSortingModal:Z(Z(o,l),a),tdColorModal:o,borderColorPopover:Z(r,S),tdColorHoverPopover:Z(r,a),tdColorSortingPopover:Z(r,a),tdColorStripedPopover:Z(r,v),thColorPopover:Z(r,l),thColorHoverPopover:Z(Z(r,l),a),thColorSortingPopover:Z(Z(r,l),a),tdColorPopover:r,boxShadowBefore:"inset -12px 0 8px -12px rgba(0, 0, 0, .18)",boxShadowAfter:"inset 12px 0 8px -12px rgba(0, 0, 0, .18)",loadingColor:c,loadingSize:E,opacityLoading:T}}const iv={name:"DataTable",common:W,peers:{Button:ut,Checkbox:Tr,Radio:Gf,Pagination:jf,Scrollbar:et,Empty:rr,Popover:nr,Ellipsis:Kf,Dropdown:da},self(e){const t=nv(e);return t.boxShadowAfter="inset 12px 0 8px -12px rgba(0, 0, 0, .36)",t.boxShadowBefore="inset -12px 0 8px -12px rgba(0, 0, 0, .36)",t}};function lv(e){const{textColorBase:t,opacity1:o,opacity2:r,opacity3:n,opacity4:i,opacity5:l}=e;return{color:t,opacity1Depth:o,opacity2Depth:r,opacity3Depth:n,opacity4Depth:i,opacity5Depth:l}}const av={name:"Icon",common:W,self:lv};var sv={itemFontSize:"12px",itemHeight:"36px",itemWidth:"52px",panelActionPadding:"8px 0"};function cv(e){const{popoverColor:t,textColor2:o,primaryColor:r,hoverColor:n,dividerColor:i,opacityDisabled:l,boxShadow2:a,borderRadius:s,iconColor:c,iconColorDisabled:u}=e;return{...sv,panelColor:t,panelBoxShadow:a,panelDividerColor:i,itemTextColor:o,itemTextColorActive:r,itemColorHover:n,itemOpacityDisabled:l,itemBorderRadius:s,borderRadius:s,iconColor:c,iconColorDisabled:u}}const Yf={name:"TimePicker",common:W,peers:{Scrollbar:et,Button:ut,Input:Et},self:cv};var uv={itemSize:"24px",itemCellWidth:"38px",itemCellHeight:"32px",scrollItemWidth:"80px",scrollItemHeight:"40px",panelExtraFooterPadding:"8px 12px",panelActionPadding:"8px 12px",calendarTitlePadding:"0",calendarTitleHeight:"28px",arrowSize:"14px",panelHeaderPadding:"8px 12px",calendarDaysHeight:"32px",calendarTitleGridTempateColumns:"28px 28px 1fr 28px 28px",calendarLeftPaddingDate:"6px 12px 4px 12px",calendarLeftPaddingDatetime:"4px 12px",calendarLeftPaddingDaterange:"6px 12px 4px 12px",calendarLeftPaddingDatetimerange:"4px 12px",calendarLeftPaddingMonth:"0",calendarLeftPaddingYear:"0",calendarLeftPaddingQuarter:"0",calendarLeftPaddingMonthrange:"0",calendarLeftPaddingQuarterrange:"0",calendarLeftPaddingYearrange:"0",calendarLeftPaddingWeek:"6px 12px 4px 12px",calendarRightPaddingDate:"6px 12px 4px 12px",calendarRightPaddingDatetime:"4px 12px",calendarRightPaddingDaterange:"6px 12px 4px 12px",calendarRightPaddingDatetimerange:"4px 12px",calendarRightPaddingMonth:"0",calendarRightPaddingYear:"0",calendarRightPaddingQuarter:"0",calendarRightPaddingMonthrange:"0",calendarRightPaddingQuarterrange:"0",calendarRightPaddingYearrange:"0",calendarRightPaddingWeek:"0"};function fv(e){const{hoverColor:t,fontSize:o,textColor2:r,textColorDisabled:n,popoverColor:i,primaryColor:l,borderRadiusSmall:a,iconColor:s,iconColorDisabled:c,textColor1:u,dividerColor:f,boxShadow2:d,borderRadius:p,fontWeightStrong:g}=e;return{...uv,itemFontSize:o,calendarDaysFontSize:o,calendarTitleFontSize:o,itemTextColor:r,itemTextColorDisabled:n,itemTextColorActive:i,itemTextColorCurrent:l,itemColorIncluded:J(l,{alpha:.1}),itemColorHover:t,itemColorDisabled:t,itemColorActive:l,itemBorderRadius:a,panelColor:i,panelTextColor:r,arrowColor:s,calendarTitleTextColor:u,calendarTitleColorHover:t,calendarDaysTextColor:r,panelHeaderDividerColor:f,calendarDaysDividerColor:f,calendarDividerColor:f,panelActionDividerColor:f,panelBoxShadow:d,panelBorderRadius:p,calendarTitleFontWeight:g,scrollItemBorderRadius:p,iconColor:s,iconColorDisabled:c}}const dv={name:"DatePicker",common:W,peers:{Input:Et,Button:ut,TimePicker:Yf,Scrollbar:et},self(e){const{popoverColor:t,hoverColor:o,primaryColor:r}=e,n=fv(e);return n.itemColorDisabled=Z(t,o),n.itemColorIncluded=J(r,{alpha:.15}),n.itemColorHover=Z(t,o),n}};var pv={thPaddingBorderedSmall:"8px 12px",thPaddingBorderedMedium:"12px 16px",thPaddingBorderedLarge:"16px 24px",thPaddingSmall:"0",thPaddingMedium:"0",thPaddingLarge:"0",tdPaddingBorderedSmall:"8px 12px",tdPaddingBorderedMedium:"12px 16px",tdPaddingBorderedLarge:"16px 24px",tdPaddingSmall:"0 0 8px 0",tdPaddingMedium:"0 0 12px 0",tdPaddingLarge:"0 0 16px 0"};function mv(e){const{tableHeaderColor:t,textColor2:o,textColor1:r,cardColor:n,modalColor:i,popoverColor:l,dividerColor:a,borderRadius:s,fontWeightStrong:c,lineHeight:u,fontSizeSmall:f,fontSizeMedium:d,fontSizeLarge:p}=e;return{...pv,lineHeight:u,fontSizeSmall:f,fontSizeMedium:d,fontSizeLarge:p,titleTextColor:r,thColor:Z(n,t),thColorModal:Z(i,t),thColorPopover:Z(l,t),thTextColor:r,thFontWeight:c,tdTextColor:o,tdColor:n,tdColorModal:i,tdColorPopover:l,borderColor:Z(n,a),borderColorModal:Z(i,a),borderColorPopover:Z(l,a),borderRadius:s}}const hv={name:"Descriptions",common:W,self:mv};var gv={titleFontSize:"18px",padding:"16px 28px 20px 28px",iconSize:"28px",actionSpace:"12px",contentMargin:"8px 0 16px 0",iconMargin:"0 4px 0 0",iconMarginIconTop:"4px 0 8px 0",closeSize:"22px",closeIconSize:"18px",closeMargin:"20px 26px 0 0",closeMarginIconTop:"10px 16px 0 0"};function Cv(e){const{textColor1:t,textColor2:o,modalColor:r,closeIconColor:n,closeIconColorHover:i,closeIconColorPressed:l,closeColorHover:a,closeColorPressed:s,infoColor:c,successColor:u,warningColor:f,errorColor:d,primaryColor:p,dividerColor:g,borderRadius:C,fontWeightStrong:S,lineHeight:E,fontSize:T}=e;return{...gv,fontSize:T,lineHeight:E,border:`1px solid ${g}`,titleTextColor:t,textColor:o,color:r,closeColorHover:a,closeColorPressed:s,closeIconColor:n,closeIconColorHover:i,closeIconColorPressed:l,closeBorderRadius:C,iconColor:p,iconColorInfo:c,iconColorSuccess:u,iconColorWarning:f,iconColorError:d,borderRadius:C,titleFontWeight:S}}const qf={name:"Dialog",common:W,peers:{Button:ut},self:Cv};function bv(e){const{modalColor:t,textColor2:o,boxShadow3:r}=e;return{color:t,textColor:o,boxShadow:r}}const xv={name:"Modal",common:W,peers:{Scrollbar:et,Dialog:qf,Card:Wf},self:bv},_v={name:"LoadingBar",common:W,self(e){const{primaryColor:t}=e;return{colorError:"red",colorLoading:t,height:"2px"}}};var vv={margin:"0 0 8px 0",padding:"10px 20px",maxWidth:"720px",minWidth:"420px",iconMargin:"0 10px 0 0",closeMargin:"0 0 0 10px",closeSize:"20px",closeIconSize:"16px",iconSize:"20px",fontSize:"14px"};function Sv(e){const{textColor2:t,closeIconColor:o,closeIconColorHover:r,closeIconColorPressed:n,infoColor:i,successColor:l,errorColor:a,warningColor:s,popoverColor:c,boxShadow2:u,primaryColor:f,lineHeight:d,borderRadius:p,closeColorHover:g,closeColorPressed:C}=e;return{...vv,closeBorderRadius:p,textColor:t,textColorInfo:t,textColorSuccess:t,textColorError:t,textColorWarning:t,textColorLoading:t,color:c,colorInfo:c,colorSuccess:c,colorError:c,colorWarning:c,colorLoading:c,boxShadow:u,boxShadowInfo:u,boxShadowSuccess:u,boxShadowError:u,boxShadowWarning:u,boxShadowLoading:u,iconColor:t,iconColorInfo:i,iconColorSuccess:l,iconColorWarning:s,iconColorError:a,iconColorLoading:f,closeColorHover:g,closeColorPressed:C,closeIconColor:o,closeIconColorHover:r,closeIconColorPressed:n,closeColorHoverInfo:g,closeColorPressedInfo:C,closeIconColorInfo:o,closeIconColorHoverInfo:r,closeIconColorPressedInfo:n,closeColorHoverSuccess:g,closeColorPressedSuccess:C,closeIconColorSuccess:o,closeIconColorHoverSuccess:r,closeIconColorPressedSuccess:n,closeColorHoverError:g,closeColorPressedError:C,closeIconColorError:o,closeIconColorHoverError:r,closeIconColorPressedError:n,closeColorHoverWarning:g,closeColorPressedWarning:C,closeIconColorWarning:o,closeIconColorHoverWarning:r,closeIconColorPressedWarning:n,closeColorHoverLoading:g,closeColorPressedLoading:C,closeIconColorLoading:o,closeIconColorHoverLoading:r,closeIconColorPressedLoading:n,loadingColor:f,lineHeight:d,borderRadius:p,border:"0"}}const yv={name:"Message",common:W,self:Sv};var Ev={closeMargin:"16px 12px",closeSize:"20px",closeIconSize:"16px",width:"365px",padding:"16px",titleFontSize:"16px",metaFontSize:"12px",descriptionFontSize:"12px"};function Tv(e){const{textColor2:t,successColor:o,infoColor:r,warningColor:n,errorColor:i,popoverColor:l,closeIconColor:a,closeIconColorHover:s,closeIconColorPressed:c,closeColorHover:u,closeColorPressed:f,textColor1:d,textColor3:p,borderRadius:g,fontWeightStrong:C,boxShadow2:S,lineHeight:E,fontSize:T}=e;return{...Ev,borderRadius:g,lineHeight:E,fontSize:T,headerFontWeight:C,iconColor:t,iconColorSuccess:o,iconColorInfo:r,iconColorWarning:n,iconColorError:i,color:l,textColor:t,closeIconColor:a,closeIconColorHover:s,closeIconColorPressed:c,closeBorderRadius:g,closeColorHover:u,closeColorPressed:f,headerTextColor:d,descriptionTextColor:p,actionTextColor:t,boxShadow:S}}const Pv={name:"Notification",common:W,peers:{Scrollbar:et},self:Tv};function Iv(e){const{textColor1:t,dividerColor:o,fontWeightStrong:r}=e;return{textColor:t,color:o,fontWeight:r}}const Av={name:"Divider",common:W,self:Iv};function Lv(e){const{modalColor:t,textColor1:o,textColor2:r,boxShadow3:n,lineHeight:i,fontWeightStrong:l,dividerColor:a,closeColorHover:s,closeColorPressed:c,closeIconColor:u,closeIconColorHover:f,closeIconColorPressed:d,borderRadius:p,primaryColorHover:g}=e;return{bodyPadding:"16px 24px",borderRadius:p,headerPadding:"16px 24px",footerPadding:"16px 24px",color:t,textColor:r,titleTextColor:o,titleFontSize:"18px",titleFontWeight:l,boxShadow:n,lineHeight:i,headerBorderBottom:`1px solid ${a}`,footerBorderTop:`1px solid ${a}`,closeIconColor:u,closeIconColorHover:f,closeIconColorPressed:d,closeSize:"22px",closeIconSize:"18px",closeColorHover:s,closeColorPressed:c,closeBorderRadius:p,resizableTriggerColorHover:g}}const wv={name:"Drawer",common:W,peers:{Scrollbar:et},self:Lv};var Dv={actionMargin:"0 0 0 20px",actionMarginRtl:"0 20px 0 0"};const Rv={name:"DynamicInput",common:W,peers:{Input:Et,Button:ut},self(){return Dv}};var Fv={gapSmall:"4px 8px",gapMedium:"8px 12px",gapLarge:"12px 16px"};const Xf={name:"Space",self(){return Fv}},Ov={name:"DynamicTags",common:W,peers:{Input:Et,Button:ut,Tag:$f,Space:Xf},self(){return{inputWidth:"64px"}}},Nv={name:"Element",common:W};var Mv={gapSmall:"4px 8px",gapMedium:"8px 12px",gapLarge:"12px 16px"};const kv={name:"Flex",self(){return Mv}},Hv={name:"ButtonGroup",common:W};var $v={feedbackPadding:"4px 0 0 2px",feedbackHeightSmall:"24px",feedbackHeightMedium:"24px",feedbackHeightLarge:"26px",feedbackFontSizeSmall:"13px",feedbackFontSizeMedium:"14px",feedbackFontSizeLarge:"14px",labelFontSizeLeftSmall:"14px",labelFontSizeLeftMedium:"14px",labelFontSizeLeftLarge:"15px",labelFontSizeTopSmall:"13px",labelFontSizeTopMedium:"14px",labelFontSizeTopLarge:"14px",labelHeightSmall:"24px",labelHeightMedium:"26px",labelHeightLarge:"28px",labelPaddingVertical:"0 0 6px 2px",labelPaddingHorizontal:"0 12px 0 0",labelTextAlignVertical:"left",labelTextAlignHorizontal:"right",labelFontWeight:"400"};function Bv(e){const{heightSmall:t,heightMedium:o,heightLarge:r,textColor1:n,errorColor:i,warningColor:l,lineHeight:a,textColor3:s}=e;return{...$v,blankHeightSmall:t,blankHeightMedium:o,blankHeightLarge:r,lineHeight:a,labelTextColor:n,asteriskColor:i,feedbackTextColorError:i,feedbackTextColorWarning:l,feedbackTextColor:s}}const Wv={name:"Form",common:W,self:Bv},zv={name:"GradientText",common:W,self(e){const{primaryColor:t,successColor:o,warningColor:r,errorColor:n,infoColor:i,primaryColorSuppl:l,successColorSuppl:a,warningColorSuppl:s,errorColorSuppl:c,infoColorSuppl:u,fontWeightStrong:f}=e;return{fontWeight:f,rotate:"252deg",colorStartPrimary:t,colorEndPrimary:l,colorStartInfo:i,colorEndInfo:u,colorStartWarning:r,colorEndWarning:s,colorStartError:n,colorEndError:c,colorStartSuccess:o,colorEndSuccess:a}}},Uv={name:"InputNumber",common:W,peers:{Button:ut,Input:Et},self(e){const{textColorDisabled:t}=e;return{iconColorDisabled:t}}};function Vv(){return{inputWidthSmall:"24px",inputWidthMedium:"30px",inputWidthLarge:"36px",gapSmall:"8px",gapMedium:"8px",gapLarge:"8px"}}const jv={name:"InputOtp",common:W,peers:{Input:Et},self:Vv},Gv={name:"Layout",common:W,peers:{Scrollbar:et},self(e){const{textColor2:t,bodyColor:o,popoverColor:r,cardColor:n,dividerColor:i,scrollbarColor:l,scrollbarColorHover:a}=e;return{textColor:t,textColorInverted:t,color:o,colorEmbedded:o,headerColor:n,headerColorInverted:n,footerColor:n,footerColorInverted:n,headerBorderColor:i,headerBorderColorInverted:i,footerBorderColor:i,footerBorderColorInverted:i,siderBorderColor:i,siderBorderColorInverted:i,siderColor:n,siderColorInverted:n,siderToggleButtonBorder:"1px solid transparent",siderToggleButtonColor:r,siderToggleButtonIconColor:t,siderToggleButtonIconColorInverted:t,siderToggleBarColor:Z(o,l),siderToggleBarColorHover:Z(o,a),__invertScrollbar:"false"}}};function Kv(e){const{textColor2:t,cardColor:o,modalColor:r,popoverColor:n,dividerColor:i,borderRadius:l,fontSize:a,hoverColor:s}=e;return{textColor:t,color:o,colorHover:s,colorModal:r,colorHoverModal:Z(r,s),colorPopover:n,colorHoverPopover:Z(n,s),borderColor:i,borderColorModal:Z(r,i),borderColorPopover:Z(n,i),borderRadius:l,fontSize:a}}const Yv={name:"List",common:W,self:Kv},qv={name:"Log",common:W,peers:{Scrollbar:et,Code:zf},self(e){const{textColor2:t,inputColor:o,fontSize:r,primaryColor:n}=e;return{loaderFontSize:r,loaderTextColor:t,loaderColor:o,loaderBorder:"1px solid #0000",loadingColor:n}}},Xv={name:"Mention",common:W,peers:{InternalSelectMenu:gn,Input:Et},self(e){const{boxShadow2:t}=e;return{menuBoxShadow:t}}};function Jv(e,t,o,r){return{itemColorHoverInverted:"#0000",itemColorActiveInverted:t,itemColorActiveHoverInverted:t,itemColorActiveCollapsedInverted:t,itemTextColorInverted:e,itemTextColorHoverInverted:o,itemTextColorChildActiveInverted:o,itemTextColorChildActiveHoverInverted:o,itemTextColorActiveInverted:o,itemTextColorActiveHoverInverted:o,itemTextColorHorizontalInverted:e,itemTextColorHoverHorizontalInverted:o,itemTextColorChildActiveHorizontalInverted:o,itemTextColorChildActiveHoverHorizontalInverted:o,itemTextColorActiveHorizontalInverted:o,itemTextColorActiveHoverHorizontalInverted:o,itemIconColorInverted:e,itemIconColorHoverInverted:o,itemIconColorActiveInverted:o,itemIconColorActiveHoverInverted:o,itemIconColorChildActiveInverted:o,itemIconColorChildActiveHoverInverted:o,itemIconColorCollapsedInverted:e,itemIconColorHorizontalInverted:e,itemIconColorHoverHorizontalInverted:o,itemIconColorActiveHorizontalInverted:o,itemIconColorActiveHoverHorizontalInverted:o,itemIconColorChildActiveHorizontalInverted:o,itemIconColorChildActiveHoverHorizontalInverted:o,arrowColorInverted:e,arrowColorHoverInverted:o,arrowColorActiveInverted:o,arrowColorActiveHoverInverted:o,arrowColorChildActiveInverted:o,arrowColorChildActiveHoverInverted:o,groupTextColorInverted:r}}function Qv(e){const{borderRadius:t,textColor3:o,primaryColor:r,textColor2:n,textColor1:i,fontSize:l,dividerColor:a,hoverColor:s,primaryColorHover:c}=e;return{borderRadius:t,color:"#0000",groupTextColor:o,itemColorHover:s,itemColorActive:J(r,{alpha:.1}),itemColorActiveHover:J(r,{alpha:.1}),itemColorActiveCollapsed:J(r,{alpha:.1}),itemTextColor:n,itemTextColorHover:n,itemTextColorActive:r,itemTextColorActiveHover:r,itemTextColorChildActive:r,itemTextColorChildActiveHover:r,itemTextColorHorizontal:n,itemTextColorHoverHorizontal:c,itemTextColorActiveHorizontal:r,itemTextColorActiveHoverHorizontal:r,itemTextColorChildActiveHorizontal:r,itemTextColorChildActiveHoverHorizontal:r,itemIconColor:i,itemIconColorHover:i,itemIconColorActive:r,itemIconColorActiveHover:r,itemIconColorChildActive:r,itemIconColorChildActiveHover:r,itemIconColorCollapsed:i,itemIconColorHorizontal:i,itemIconColorHoverHorizontal:c,itemIconColorActiveHorizontal:r,itemIconColorActiveHoverHorizontal:r,itemIconColorChildActiveHorizontal:r,itemIconColorChildActiveHoverHorizontal:r,itemHeight:"42px",arrowColor:n,arrowColorHover:n,arrowColorActive:r,arrowColorActiveHover:r,arrowColorChildActive:r,arrowColorChildActiveHover:r,colorInverted:"#0000",borderColorHorizontal:"#0000",fontSize:l,dividerColor:a,...Jv("#BBB",r,"#FFF","#AAA")}}const Zv={name:"Menu",common:W,peers:{Tooltip:yi,Dropdown:da},self(e){const{primaryColor:t,primaryColorSuppl:o}=e,r=Qv(e);return r.itemColorActive=J(t,{alpha:.15}),r.itemColorActiveHover=J(t,{alpha:.15}),r.itemColorActiveCollapsed=J(t,{alpha:.15}),r.itemColorActiveInverted=o,r.itemColorActiveHoverInverted=o,r.itemColorActiveCollapsedInverted=o,r}};var e0={iconSize:"22px"};function t0(e){const{fontSize:t,warningColor:o}=e;return{...e0,fontSize:t,iconColor:o}}const o0={name:"Popconfirm",common:W,peers:{Button:ut,Popover:nr},self:t0};function r0(e){const{infoColor:t,successColor:o,warningColor:r,errorColor:n,textColor2:i,progressRailColor:l,fontSize:a,fontWeight:s}=e;return{fontSize:a,fontSizeCircle:"28px",fontWeightCircle:s,railColor:l,railHeight:"8px",iconSizeCircle:"36px",iconSizeLine:"18px",iconColor:t,iconColorInfo:t,iconColorSuccess:o,iconColorWarning:r,iconColorError:n,textColorCircle:i,textColorLineInner:"rgb(255, 255, 255)",textColorLineOuter:i,fillColor:t,fillColorInfo:t,fillColorSuccess:o,fillColorWarning:r,fillColorError:n,lineBgProcessing:"linear-gradient(90deg, rgba(255, 255, 255, .3) 0%, rgba(255, 255, 255, .5) 100%)"}}const Jf={name:"Progress",common:W,self(e){const t=r0(e);return t.textColorLineInner="rgb(0, 0, 0)",t.lineBgProcessing="linear-gradient(90deg, rgba(255, 255, 255, .3) 0%, rgba(255, 255, 255, .5) 100%)",t}},n0={name:"Rate",common:W,self(e){const{railColor:t}=e;return{itemColor:t,itemColorActive:"#CCAA33",itemSize:"20px",sizeSmall:"16px",sizeMedium:"20px",sizeLarge:"24px"}}};var i0={titleFontSizeSmall:"26px",titleFontSizeMedium:"32px",titleFontSizeLarge:"40px",titleFontSizeHuge:"48px",fontSizeSmall:"14px",fontSizeMedium:"14px",fontSizeLarge:"15px",fontSizeHuge:"16px",iconSizeSmall:"64px",iconSizeMedium:"80px",iconSizeLarge:"100px",iconSizeHuge:"125px",iconColor418:void 0,iconColor404:void 0,iconColor403:void 0,iconColor500:void 0};function l0(e){const{textColor2:t,textColor1:o,errorColor:r,successColor:n,infoColor:i,warningColor:l,lineHeight:a,fontWeightStrong:s}=e;return{...i0,lineHeight:a,titleFontWeight:s,titleTextColor:o,textColor:t,iconColorError:r,iconColorSuccess:n,iconColorInfo:i,iconColorWarning:l}}const a0={name:"Result",common:W,self:l0};var s0={railHeight:"4px",railWidthVertical:"4px",handleSize:"18px",dotHeight:"8px",dotWidth:"8px",dotBorderRadius:"4px"};const c0={name:"Slider",common:W,self(e){const t="0 2px 8px 0 rgba(0, 0, 0, 0.12)",{railColor:o,modalColor:r,primaryColorSuppl:n,popoverColor:i,textColor2:l,cardColor:a,borderRadius:s,fontSize:c,opacityDisabled:u}=e;return{...s0,fontSize:c,markFontSize:c,railColor:o,railColorHover:o,fillColor:n,fillColorHover:n,opacityDisabled:u,handleColor:"#FFF",dotColor:a,dotColorModal:r,dotColorPopover:i,handleBoxShadow:"0px 2px 4px 0 rgba(0, 0, 0, 0.4)",handleBoxShadowHover:"0px 2px 4px 0 rgba(0, 0, 0, 0.4)",handleBoxShadowActive:"0px 2px 4px 0 rgba(0, 0, 0, 0.4)",handleBoxShadowFocus:"0px 2px 4px 0 rgba(0, 0, 0, 0.4)",indicatorColor:i,indicatorBoxShadow:t,indicatorTextColor:l,indicatorBorderRadius:s,dotBorder:`2px solid ${o}`,dotBorderActive:`2px solid ${n}`,dotBoxShadow:""}}};function u0(e){const{opacityDisabled:t,heightTiny:o,heightSmall:r,heightMedium:n,heightLarge:i,heightHuge:l,primaryColor:a,fontSize:s}=e;return{fontSize:s,textColor:a,sizeTiny:o,sizeSmall:r,sizeMedium:n,sizeLarge:i,sizeHuge:l,color:a,opacitySpinning:t}}const f0={name:"Spin",common:W,self:u0};function d0(e){const{textColor2:t,textColor3:o,fontSize:r,fontWeight:n}=e;return{labelFontSize:r,labelFontWeight:n,valueFontWeight:n,valueFontSize:"24px",labelTextColor:o,valuePrefixTextColor:t,valueSuffixTextColor:t,valueTextColor:t}}const p0={name:"Statistic",common:W,self:d0};var m0={stepHeaderFontSizeSmall:"14px",stepHeaderFontSizeMedium:"16px",indicatorIndexFontSizeSmall:"14px",indicatorIndexFontSizeMedium:"16px",indicatorSizeSmall:"22px",indicatorSizeMedium:"28px",indicatorIconSizeSmall:"14px",indicatorIconSizeMedium:"18px"};function h0(e){const{fontWeightStrong:t,baseColor:o,textColorDisabled:r,primaryColor:n,errorColor:i,textColor1:l,textColor2:a}=e;return{...m0,stepHeaderFontWeight:t,indicatorTextColorProcess:o,indicatorTextColorWait:r,indicatorTextColorFinish:n,indicatorTextColorError:i,indicatorBorderColorProcess:n,indicatorBorderColorWait:r,indicatorBorderColorFinish:n,indicatorBorderColorError:i,indicatorColorProcess:n,indicatorColorWait:"#0000",indicatorColorFinish:"#0000",indicatorColorError:"#0000",splitorColorProcess:r,splitorColorWait:r,splitorColorFinish:n,splitorColorError:r,headerTextColorProcess:l,headerTextColorWait:r,headerTextColorFinish:r,headerTextColorError:i,descriptionTextColorProcess:a,descriptionTextColorWait:r,descriptionTextColorFinish:r,descriptionTextColorError:i}}const g0={name:"Steps",common:W,self:h0};var C0={buttonHeightSmall:"14px",buttonHeightMedium:"18px",buttonHeightLarge:"22px",buttonWidthSmall:"14px",buttonWidthMedium:"18px",buttonWidthLarge:"22px",buttonWidthPressedSmall:"20px",buttonWidthPressedMedium:"24px",buttonWidthPressedLarge:"28px",railHeightSmall:"18px",railHeightMedium:"22px",railHeightLarge:"26px",railWidthSmall:"32px",railWidthMedium:"40px",railWidthLarge:"48px"};const b0={name:"Switch",common:W,self(e){const{primaryColorSuppl:t,opacityDisabled:o,borderRadius:r,primaryColor:n,textColor2:i,baseColor:l}=e;return{...C0,iconColor:l,textColor:i,loadingColor:t,opacityDisabled:o,railColor:"rgba(255, 255, 255, .20)",railColorActive:t,buttonBoxShadow:"0px 2px 4px 0 rgba(0, 0, 0, 0.4)",buttonColor:"#FFF",railBorderRadiusSmall:r,railBorderRadiusMedium:r,railBorderRadiusLarge:r,buttonBorderRadiusSmall:r,buttonBorderRadiusMedium:r,buttonBorderRadiusLarge:r,boxShadowFocus:`0 0 8px 0 ${J(n,{alpha:.3})}`}}};var x0={thPaddingSmall:"6px",thPaddingMedium:"12px",thPaddingLarge:"12px",tdPaddingSmall:"6px",tdPaddingMedium:"12px",tdPaddingLarge:"12px"};function _0(e){const{dividerColor:t,cardColor:o,modalColor:r,popoverColor:n,tableHeaderColor:i,tableColorStriped:l,textColor1:a,textColor2:s,borderRadius:c,fontWeightStrong:u,lineHeight:f,fontSizeSmall:d,fontSizeMedium:p,fontSizeLarge:g}=e;return{...x0,fontSizeSmall:d,fontSizeMedium:p,fontSizeLarge:g,lineHeight:f,borderRadius:c,borderColor:Z(o,t),borderColorModal:Z(r,t),borderColorPopover:Z(n,t),tdColor:o,tdColorModal:r,tdColorPopover:n,tdColorStriped:Z(o,l),tdColorStripedModal:Z(r,l),tdColorStripedPopover:Z(n,l),thColor:Z(o,i),thColorModal:Z(r,i),thColorPopover:Z(n,i),thTextColor:a,tdTextColor:s,thFontWeight:u}}const v0={name:"Table",common:W,self:_0};var S0={tabFontSizeSmall:"14px",tabFontSizeMedium:"14px",tabFontSizeLarge:"16px",tabGapSmallLine:"36px",tabGapMediumLine:"36px",tabGapLargeLine:"36px",tabGapSmallLineVertical:"8px",tabGapMediumLineVertical:"8px",tabGapLargeLineVertical:"8px",tabPaddingSmallLine:"6px 0",tabPaddingMediumLine:"10px 0",tabPaddingLargeLine:"14px 0",tabPaddingVerticalSmallLine:"6px 12px",tabPaddingVerticalMediumLine:"8px 16px",tabPaddingVerticalLargeLine:"10px 20px",tabGapSmallBar:"36px",tabGapMediumBar:"36px",tabGapLargeBar:"36px",tabGapSmallBarVertical:"8px",tabGapMediumBarVertical:"8px",tabGapLargeBarVertical:"8px",tabPaddingSmallBar:"4px 0",tabPaddingMediumBar:"6px 0",tabPaddingLargeBar:"10px 0",tabPaddingVerticalSmallBar:"6px 12px",tabPaddingVerticalMediumBar:"8px 16px",tabPaddingVerticalLargeBar:"10px 20px",tabGapSmallCard:"4px",tabGapMediumCard:"4px",tabGapLargeCard:"4px",tabGapSmallCardVertical:"4px",tabGapMediumCardVertical:"4px",tabGapLargeCardVertical:"4px",tabPaddingSmallCard:"8px 16px",tabPaddingMediumCard:"10px 20px",tabPaddingLargeCard:"12px 24px",tabPaddingSmallSegment:"4px 0",tabPaddingMediumSegment:"6px 0",tabPaddingLargeSegment:"8px 0",tabPaddingVerticalLargeSegment:"0 8px",tabPaddingVerticalSmallCard:"8px 12px",tabPaddingVerticalMediumCard:"10px 16px",tabPaddingVerticalLargeCard:"12px 20px",tabPaddingVerticalSmallSegment:"0 4px",tabPaddingVerticalMediumSegment:"0 6px",tabGapSmallSegment:"0",tabGapMediumSegment:"0",tabGapLargeSegment:"0",tabGapSmallSegmentVertical:"0",tabGapMediumSegmentVertical:"0",tabGapLargeSegmentVertical:"0",panePaddingSmall:"8px 0 0 0",panePaddingMedium:"12px 0 0 0",panePaddingLarge:"16px 0 0 0",closeSize:"18px",closeIconSize:"14px"};function y0(e){const{textColor2:t,primaryColor:o,textColorDisabled:r,closeIconColor:n,closeIconColorHover:i,closeIconColorPressed:l,closeColorHover:a,closeColorPressed:s,tabColor:c,baseColor:u,dividerColor:f,fontWeight:d,textColor1:p,borderRadius:g,fontSize:C,fontWeightStrong:S}=e;return{...S0,colorSegment:c,tabFontSizeCard:C,tabTextColorLine:p,tabTextColorActiveLine:o,tabTextColorHoverLine:o,tabTextColorDisabledLine:r,tabTextColorSegment:p,tabTextColorActiveSegment:t,tabTextColorHoverSegment:t,tabTextColorDisabledSegment:r,tabTextColorBar:p,tabTextColorActiveBar:o,tabTextColorHoverBar:o,tabTextColorDisabledBar:r,tabTextColorCard:p,tabTextColorHoverCard:p,tabTextColorActiveCard:o,tabTextColorDisabledCard:r,barColor:o,closeIconColor:n,closeIconColorHover:i,closeIconColorPressed:l,closeColorHover:a,closeColorPressed:s,closeBorderRadius:g,tabColor:c,tabColorSegment:u,tabBorderColor:f,tabFontWeightActive:d,tabFontWeight:d,tabBorderRadius:g,paneTextColor:t,fontWeightStrong:S}}const E0={name:"Tabs",common:W,peers:{Button:ut},self(e){const t=y0(e),{inputColor:o}=e;return t.colorSegment=o,t.tabColorSegment=o,t}};function T0(e){const{textColor1:t,textColor2:o,fontWeightStrong:r,fontSize:n}=e;return{fontSize:n,titleTextColor:t,textColor:o,titleFontWeight:r}}const P0={name:"Thing",common:W,self:T0};var I0={titleMarginMedium:"0 0 6px 0",titleMarginLarge:"-2px 0 6px 0",titleFontSizeMedium:"14px",titleFontSizeLarge:"16px",iconSizeMedium:"14px",iconSizeLarge:"14px"};const A0={name:"Timeline",common:W,self(e){const{textColor3:t,infoColorSuppl:o,errorColorSuppl:r,successColorSuppl:n,warningColorSuppl:i,textColor1:l,textColor2:a,railColor:s,fontWeightStrong:c,fontSize:u}=e;return{...I0,contentFontSize:u,titleFontWeight:c,circleBorder:`2px solid ${t}`,circleBorderInfo:`2px solid ${o}`,circleBorderError:`2px solid ${r}`,circleBorderSuccess:`2px solid ${n}`,circleBorderWarning:`2px solid ${i}`,iconColor:t,iconColorInfo:o,iconColorError:r,iconColorSuccess:n,iconColorWarning:i,titleTextColor:l,contentTextColor:a,metaTextColor:t,lineColor:s}}};var L0={extraFontSizeSmall:"12px",extraFontSizeMedium:"12px",extraFontSizeLarge:"14px",titleFontSizeSmall:"14px",titleFontSizeMedium:"16px",titleFontSizeLarge:"16px",closeSize:"20px",closeIconSize:"16px",headerHeightSmall:"44px",headerHeightMedium:"44px",headerHeightLarge:"50px"};const w0={name:"Transfer",common:W,peers:{Checkbox:Tr,Scrollbar:et,Input:Et,Empty:rr,Button:ut},self(e){const{fontWeight:t,fontSizeLarge:o,fontSizeMedium:r,fontSizeSmall:n,heightLarge:i,heightMedium:l,borderRadius:a,inputColor:s,tableHeaderColor:c,textColor1:u,textColorDisabled:f,textColor2:d,textColor3:p,hoverColor:g,closeColorHover:C,closeColorPressed:S,closeIconColor:E,closeIconColorHover:T,closeIconColorPressed:v,dividerColor:y}=e;return{...L0,itemHeightSmall:l,itemHeightMedium:l,itemHeightLarge:i,fontSizeSmall:n,fontSizeMedium:r,fontSizeLarge:o,borderRadius:a,dividerColor:y,borderColor:"#0000",listColor:s,headerColor:c,titleTextColor:u,titleTextColorDisabled:f,extraTextColor:p,extraTextColorDisabled:f,itemTextColor:d,itemTextColorDisabled:f,itemColorPending:g,titleFontWeight:t,closeColorHover:C,closeColorPressed:S,closeIconColor:E,closeIconColorHover:T,closeIconColorPressed:v}}};function D0(e){const{borderRadiusSmall:t,dividerColor:o,hoverColor:r,pressedColor:n,primaryColor:i,textColor3:l,textColor2:a,textColorDisabled:s,fontSize:c}=e;return{fontSize:c,lineHeight:"1.5",nodeHeight:"30px",nodeWrapperPadding:"3px 0",nodeBorderRadius:t,nodeColorHover:r,nodeColorPressed:n,nodeColorActive:J(i,{alpha:.1}),arrowColor:l,nodeTextColor:a,nodeTextColorDisabled:s,loadingColor:i,dropMarkColor:i,lineColor:o}}const Qf={name:"Tree",common:W,peers:{Checkbox:Tr,Scrollbar:et,Empty:rr},self(e){const{primaryColor:t}=e,o=D0(e);return o.nodeColorActive=J(t,{alpha:.15}),o}},R0={name:"TreeSelect",common:W,peers:{Tree:Qf,Empty:rr,InternalSelection:fa}};var F0={headerFontSize1:"30px",headerFontSize2:"22px",headerFontSize3:"18px",headerFontSize4:"16px",headerFontSize5:"16px",headerFontSize6:"16px",headerMargin1:"28px 0 20px 0",headerMargin2:"28px 0 20px 0",headerMargin3:"28px 0 20px 0",headerMargin4:"28px 0 18px 0",headerMargin5:"28px 0 18px 0",headerMargin6:"28px 0 18px 0",headerPrefixWidth1:"16px",headerPrefixWidth2:"16px",headerPrefixWidth3:"12px",headerPrefixWidth4:"12px",headerPrefixWidth5:"12px",headerPrefixWidth6:"12px",headerBarWidth1:"4px",headerBarWidth2:"4px",headerBarWidth3:"3px",headerBarWidth4:"3px",headerBarWidth5:"3px",headerBarWidth6:"3px",pMargin:"16px 0 16px 0",liMargin:".25em 0 0 0",olPadding:"0 0 0 2em",ulPadding:"0 0 0 2em"};function O0(e){const{primaryColor:t,textColor2:o,borderColor:r,lineHeight:n,fontSize:i,borderRadiusSmall:l,dividerColor:a,fontWeightStrong:s,textColor1:c,textColor3:u,infoColor:f,warningColor:d,errorColor:p,successColor:g,codeColor:C}=e;return{...F0,aTextColor:t,blockquoteTextColor:o,blockquotePrefixColor:r,blockquoteLineHeight:n,blockquoteFontSize:i,codeBorderRadius:l,liTextColor:o,liLineHeight:n,liFontSize:i,hrColor:a,headerFontWeight:s,headerTextColor:c,pTextColor:o,pTextColor1Depth:c,pTextColor2Depth:o,pTextColor3Depth:u,pLineHeight:n,pFontSize:i,headerBarColor:t,headerBarColorPrimary:t,headerBarColorInfo:f,headerBarColorError:p,headerBarColorWarning:d,headerBarColorSuccess:g,textColor:o,textColor1Depth:c,textColor2Depth:o,textColor3Depth:u,textColorPrimary:t,textColorInfo:f,textColorSuccess:g,textColorWarning:d,textColorError:p,codeTextColor:o,codeColor:C,codeBorder:"1px solid #0000"}}const N0={name:"Typography",common:W,self:O0};function M0(e){const{iconColor:t,primaryColor:o,errorColor:r,textColor2:n,successColor:i,opacityDisabled:l,actionColor:a,borderColor:s,hoverColor:c,lineHeight:u,borderRadius:f,fontSize:d}=e;return{fontSize:d,lineHeight:u,borderRadius:f,draggerColor:a,draggerBorder:`1px dashed ${s}`,draggerBorderHover:`1px dashed ${o}`,itemColorHover:c,itemColorHoverError:J(r,{alpha:.06}),itemTextColor:n,itemTextColorError:r,itemTextColorSuccess:i,itemIconColor:t,itemDisabledOpacity:l,itemBorderImageCardError:`1px solid ${r}`,itemBorderImageCard:`1px solid ${s}`}}const k0={name:"Upload",common:W,peers:{Button:ut,Progress:Jf},self(e){const{errorColor:t}=e,o=M0(e);return o.itemColorHoverError=J(t,{alpha:.09}),o}},H0={name:"Watermark",common:W,self(e){const{fontFamily:t}=e;return{fontFamily:t}}};function $0(e){const{borderRadius:t,fontSizeMini:o,fontSizeTiny:r,fontSizeSmall:n,fontWeight:i,textColor2:l,cardColor:a,buttonColor2Hover:s}=e;return{activeColors:["#9be9a8","#40c463","#30a14e","#216e39"],borderRadius:t,borderColor:a,textColor:l,mininumColor:s,fontWeight:i,loadingColorStart:"rgba(0, 0, 0, 0.06)",loadingColorEnd:"rgba(0, 0, 0, 0.12)",rectSizeSmall:"10px",rectSizeMedium:"11px",rectSizeLarge:"12px",borderRadiusSmall:"2px",borderRadiusMedium:"2px",borderRadiusLarge:"2px",xGapSmall:"2px",xGapMedium:"3px",xGapLarge:"3px",yGapSmall:"2px",yGapMedium:"3px",yGapLarge:"3px",fontSizeSmall:r,fontSizeMedium:o,fontSizeLarge:n}}function B0(e){const{primaryColor:t,baseColor:o}=e;return{color:t,iconColor:o}}var W0={extraFontSize:"12px",width:"440px"};function z0(){return{}}var U0={titleFontSize:"18px",backSize:"22px"};function V0(e){const{textColor1:t,textColor2:o,textColor3:r,fontSize:n,fontWeightStrong:i,primaryColorHover:l,primaryColorPressed:a}=e;return{...U0,titleFontWeight:i,fontSize:n,titleTextColor:t,backColor:o,backColorHover:l,backColorPressed:a,subtitleTextColor:r}}const j0=()=>({}),G0={name:"AvatarGroup",common:W,peers:{Avatar:Bf},self:P_},K0={name:"Calendar",common:W,peers:{Button:ut},self:M_},Y0={name:"Carousel",common:W,self:$_},q0={name:"CollapseTransition",common:W,self:G_},X0={name:"ColorPicker",common:W,peers:{Input:Et,Button:ut},self:K_},J0={name:"Row",common:W},Q0={name:"PageHeader",common:W,self:V0},Z0={name:"FloatButton",common:W,self(e){const{popoverColor:t,textColor2:o,buttonColor2Hover:r,buttonColor2Pressed:n,primaryColor:i,primaryColorHover:l,primaryColorPressed:a,baseColor:s,borderRadius:c}=e;return{color:t,textColor:o,boxShadow:"0 2px 8px 0px rgba(0, 0, 0, .12)",boxShadowHover:"0 2px 12px 0px rgba(0, 0, 0, .18)",boxShadowPressed:"0 2px 12px 0px rgba(0, 0, 0, .18)",colorHover:r,colorPressed:n,colorPrimary:i,colorPrimaryHover:l,colorPrimaryPressed:a,textColorPrimary:s,borderRadiusSquare:c}}},eS={name:"IconWrapper",common:W,self:B0},tS={name:"Image",common:W,peers:{Tooltip:yi},self:e=>{const{textColor2:t}=e;return{toolbarIconColor:t,toolbarColor:"rgba(0, 0, 0, .35)",toolbarBoxShadow:"none",toolbarBorderRadius:"24px"}}},oS={name:"Transfer",common:W,peers:{Checkbox:Tr,Scrollbar:et,Input:Et,Empty:rr,Button:ut},self(e){const{iconColorDisabled:t,iconColor:o,fontWeight:r,fontSizeLarge:n,fontSizeMedium:i,fontSizeSmall:l,heightLarge:a,heightMedium:s,heightSmall:c,borderRadius:u,inputColor:f,tableHeaderColor:d,textColor1:p,textColorDisabled:g,textColor2:C,hoverColor:S}=e;return{...W0,itemHeightSmall:c,itemHeightMedium:s,itemHeightLarge:a,fontSizeSmall:l,fontSizeMedium:i,fontSizeLarge:n,borderRadius:u,borderColor:"#0000",listColor:f,headerColor:d,titleTextColor:p,titleTextColorDisabled:g,extraTextColor:C,filterDividerColor:"#0000",itemTextColor:C,itemTextColorDisabled:g,itemColorPending:S,titleFontWeight:r,iconColor:o,iconColorDisabled:t}}},rS={name:"Marquee",common:W,self:z0},nS={name:"QrCode",common:W,self:e=>({borderRadius:e.borderRadius})},iS={name:"Skeleton",common:W,self(e){const{heightSmall:t,heightMedium:o,heightLarge:r,borderRadius:n}=e;return{color:"rgba(255, 255, 255, 0.12)",colorEnd:"rgba(255, 255, 255, 0.18)",borderRadius:n,heightSmall:t,heightMedium:o,heightLarge:r}}},lS={name:"Split",common:W},aS={name:"Equation",common:W,self:j0},sS={name:"FloatButtonGroup",common:W,self(e){const{popoverColor:t,dividerColor:o,borderRadius:r}=e;return{color:t,buttonBorderColor:o,borderRadiusSquare:r,boxShadow:"0 2px 8px 0px rgba(0, 0, 0, .12)"}}},cS={name:"Heatmap",common:W,self(e){return{...$0(e),activeColors:["#0d4429","#006d32","#26a641","#39d353"],mininumColor:"rgba(255, 255, 255, 0.1)",loadingColorStart:"rgba(255, 255, 255, 0.12)",loadingColorEnd:"rgba(255, 255, 255, 0.18)"}}},uS={name:"dark",common:W,Alert:C_,Anchor:__,AutoComplete:E_,Avatar:Bf,AvatarGroup:G0,BackTop:A_,Badge:L_,Breadcrumb:R_,Button:ut,ButtonGroup:Hv,Calendar:K0,Card:Wf,Carousel:Y0,Cascader:U_,Checkbox:Tr,Code:zf,Collapse:j_,CollapseTransition:q0,ColorPicker:X0,DataTable:iv,DatePicker:dv,Descriptions:hv,Dialog:qf,Divider:Av,Drawer:wv,Dropdown:da,DynamicInput:Rv,DynamicTags:Ov,Element:Nv,Empty:rr,Ellipsis:Kf,Equation:aS,Flex:kv,Form:Wv,GradientText:zv,Heatmap:cS,Icon:av,IconWrapper:eS,Image:tS,Input:Et,InputNumber:Uv,InputOtp:jv,LegacyTransfer:oS,Layout:Gv,List:Yv,LoadingBar:_v,Log:qv,Menu:Zv,Mention:Xv,Message:yv,Modal:xv,Notification:Pv,PageHeader:Q0,Pagination:jf,Popconfirm:o0,Popover:nr,Popselect:Uf,Progress:Jf,QrCode:nS,Radio:Gf,Rate:n0,Result:a0,Row:J0,Scrollbar:et,Select:Vf,Skeleton:iS,Slider:c0,Space:Xf,Spin:f0,Statistic:p0,Steps:g0,Switch:b0,Table:v0,Tabs:E0,Tag:$f,Thing:P0,TimePicker:Yf,Timeline:A0,Tooltip:yi,Transfer:w0,Tree:Qf,TreeSelect:R0,Typography:N0,Upload:k0,Watermark:H0,Split:lS,FloatButton:Z0,FloatButtonGroup:sS,Marquee:rS},Ei="".trim().replace(/\/+$/,"");function Zf(e){return`${Ei}${e}`}function fS(){return"/s/"}function dS(e,t){return`${(t?.trim()||location.origin).replace(/\/$/,"")}${fS()}${encodeURIComponent(e)}`}const ed={health:"/api/v1/health",publicConfig:"/api/v1/config",setup:"/setup",shareText:"/share/text",shareFile:"/share/file",shareMetadata:"/share/metadata",shareSelect:"/share/select",shareDownload:"/share/download",chunkInit:"/chunk/upload/init",chunkUpload:(e,t)=>`/chunk/upload/${encodeURIComponent(e)}/${t}`,chunkStatus:e=>`/chunk/upload/status/${encodeURIComponent(e)}`,chunkFinish:e=>`/chunk/upload/complete/${encodeURIComponent(e)}`,chunkCancel:e=>`/chunk/upload/${encodeURIComponent(e)}`,presignInit:"/presign/upload/init",presignProxyUpload:e=>`/presign/upload/proxy/${encodeURIComponent(e)}`,presignConfirm:e=>`/presign/upload/confirm/${encodeURIComponent(e)}`,presignStatus:e=>`/presign/upload/status/${encodeURIComponent(e)}`,presignCancel:e=>`/presign/upload/${encodeURIComponent(e)}`,adminLogin:"/admin/login",adminVerify:"/admin/verify",adminLogout:"/admin/logout",adminDashboard:"/admin/dashboard",adminFileList:"/admin/file/list",adminFileDelete:"/admin/file/delete",adminFileBatchDelete:"/admin/file/batch-delete",adminFileUpdate:"/admin/file/update",adminConfigGet:"/admin/config/get",adminConfigUpdate:"/admin/config/update",adminAuditList:"/admin/audit/list",adminPasswordUpdate:"/admin/settings/password",adminStorageSwitch:"/admin/storage/switch"};function pS(e){return`${Ei}/docs/api/${encodeURIComponent(e)}.md`}function mS(){return`${Ei}/docs/openapi.yaml`}const PT=Object.freeze(Object.defineProperty({__proto__:null,API_BASE:Ei,api:Zf,paths:ed,pickupPageUrl:dS,remoteDocUrl:pS,remoteOpenApiUrl:mS},Symbol.toStringTag,{value:"Module"}));class st extends Error{code;msg;httpStatus;constructor(t,o,r){super(o||`请求失败(${t})`),this.name="ApiError",this.code=t,this.msg=o||`请求失败(${t})`,this.httpStatus=r}}const Ll="fcb_admin_token";function td(){try{return localStorage.getItem(Ll)??""}catch{return""}}function od(e){try{e?localStorage.setItem(Ll,e):localStorage.removeItem(Ll)}catch{}}let pa=null;function hS(e){pa=e}function ma(e,t){const o=new URL(Zf(e),location.origin);if(t)for(const[r,n]of Object.entries(t))n!=null&&`${n}`!=""&&o.searchParams.set(r,`${n}`);return o.toString()}function rd(){const e=td();return e?{Authorization:`Bearer ${e}`}:{}}async function gS(e,t={}){const{method:o="GET",json:r,form:n,formData:i,query:l,timeout:a=3e4,signal:s}=t,c=new AbortController,u=setTimeout(()=>c.abort(new DOMException("请求超时","TimeoutError")),a);s&&s.addEventListener("abort",()=>c.abort(s.reason),{once:!0});const f={...rd()};r!==void 0&&(f["Content-Type"]="application/json");let d;r!==void 0?d=JSON.stringify(r):i?d=i:n&&(d=new URLSearchParams(n).toString(),f["Content-Type"]="application/x-www-form-urlencoded;charset=UTF-8");let p;try{p=await fetch(ma(e,l),{method:o,headers:f,body:d,signal:c.signal})}catch(S){throw S instanceof DOMException&&S.name==="TimeoutError"?new st(0,"请求超时,请检查网络或稍后重试"):new st(0,"网络异常,无法连接服务器")}finally{clearTimeout(u)}if(!(p.headers.get("content-type")??"").includes("application/json")){const S=await p.text().catch(()=>"");throw p.ok?new st(p.status,"响应格式异常(非 JSON)",p.status):new st(p.status,S.slice(0,200)||`请求失败(HTTP ${p.status})`,p.status)}let C;try{C=await p.json()}catch{throw new st(p.status,"响应 JSON 解析失败",p.status)}if(!p.ok||C.code!==200){const S=C.code??p.status;throw(S===401||p.status===401)&&(od(""),pa?.()),new st(S,C.msg||`请求失败(${S})`,p.status)}return C.data}async function IT(e,t={}){const{query:o,timeout:r=12e4}=t,n=new AbortController,i=setTimeout(()=>n.abort(new DOMException("请求超时","TimeoutError")),r);let l;try{l=await fetch(ma(e,o),{method:"GET",headers:rd(),signal:n.signal})}catch{throw new st(0,"网络异常,无法连接服务器")}finally{clearTimeout(i)}const a=l.headers.get("content-type")??"";if(a.includes("application/json"))try{const s=await l.json();throw new st(s.code??l.status,s.msg||"取件失败",l.status)}catch(s){throw s instanceof st?s:new st(l.status,"取件失败",l.status)}if(!l.ok)throw new st(l.status,`取件失败(HTTP ${l.status})`,l.status);return{blob:await l.blob(),contentType:a}}function AT(e,t,o,r=6e5){return new Promise((n,i)=>{const l=new XMLHttpRequest;l.open("POST",ma(e)),l.timeout=r;const a=td();a&&l.setRequestHeader("Authorization",`Bearer ${a}`),l.upload.onprogress=s=>{s.lengthComputable&&o&&o(Math.round(s.loaded/s.total*100))},l.onload=()=>{try{const s=JSON.parse(l.responseText);l.status>=200&&l.status<300&&s.code===200?n(s.data):((s.code===401||l.status===401)&&(od(""),pa?.()),i(new st(s.code??l.status,s.msg||`上传失败(HTTP ${l.status})`,l.status)))}catch{i(new st(l.status,`上传失败(HTTP ${l.status})`,l.status))}},l.onerror=()=>i(new st(0,"网络异常,上传失败")),l.ontimeout=()=>i(new st(0,"上传超时,请重试")),l.send(t)})}const Kn=typeof window<"u",Fo=(e,t=!1)=>t?Symbol.for(e):Symbol(e),CS=(e,t,o)=>bS({l:e,k:t,s:o}),bS=e=>JSON.stringify(e).replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029").replace(/\u0027/g,"\\u0027"),Me=e=>typeof e=="number"&&isFinite(e),xS=e=>id(e)==="[object Date]",wo=e=>id(e)==="[object RegExp]",Ti=e=>ae(e)&&Object.keys(e).length===0,Ge=Object.assign,_S=Object.create,ve=(e=null)=>_S(e);let Ys;const io=()=>Ys||(Ys=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:ve());function qs(e){return e.replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}const vS=Object.prototype.hasOwnProperty;function Ot(e,t){return vS.call(e,t)}const Ae=Array.isArray,Pe=e=>typeof e=="function",K=e=>typeof e=="string",pe=e=>typeof e=="boolean",Ce=e=>e!==null&&typeof e=="object",SS=e=>Ce(e)&&Pe(e.then)&&Pe(e.catch),nd=Object.prototype.toString,id=e=>nd.call(e),ae=e=>{if(!Ce(e))return!1;const t=Object.getPrototypeOf(e);return t===null||t.constructor===Object},yS=e=>e==null?"":Ae(e)||ae(e)&&e.toString===nd?JSON.stringify(e,null,2):String(e);function ES(e,t=""){return e.reduce((o,r,n)=>n===0?o+r:o+t+r,"")}function Pi(e){let t=e;return()=>++t}function TS(e,t){typeof console<"u"&&(console.warn("[intlify] "+e),t&&console.warn(t.stack))}const Tn=e=>!Ce(e)||Ae(e);function wn(e,t){if(Tn(e)||Tn(t))throw new Error("Invalid value");const o=[{src:e,des:t}];for(;o.length;){const{src:r,des:n}=o.pop();Object.keys(r).forEach(i=>{i!=="__proto__"&&(Ce(r[i])&&!Ce(n[i])&&(n[i]=Array.isArray(r[i])?[]:ve()),Tn(n[i])||Tn(r[i])?n[i]=r[i]:o.push({src:r[i],des:n[i]}))})}}function PS(e,t,o){return{line:e,column:t,offset:o}}function Yn(e,t,o){return{start:e,end:t}}const IS=/\{([0-9a-zA-Z]+)\}/g;function ld(e,...t){return t.length===1&&AS(t[0])&&(t=t[0]),(!t||!t.hasOwnProperty)&&(t={}),e.replace(IS,(o,r)=>t.hasOwnProperty(r)?t[r]:"")}const ad=Object.assign,Xs=e=>typeof e=="string",AS=e=>e!==null&&typeof e=="object";function sd(e,t=""){return e.reduce((o,r,n)=>n===0?o+r:o+t+r,"")}const ha={USE_MODULO_SYNTAX:1,__EXTEND_POINT__:2},LS={[ha.USE_MODULO_SYNTAX]:"Use modulo before '{{0}}'."};function wS(e,t,...o){const r=ld(LS[e],...o||[]),n={message:String(r),code:e};return t&&(n.location=t),n}const ie={EXPECTED_TOKEN:1,INVALID_TOKEN_IN_PLACEHOLDER:2,UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER:3,UNKNOWN_ESCAPE_SEQUENCE:4,INVALID_UNICODE_ESCAPE_SEQUENCE:5,UNBALANCED_CLOSING_BRACE:6,UNTERMINATED_CLOSING_BRACE:7,EMPTY_PLACEHOLDER:8,NOT_ALLOW_NEST_PLACEHOLDER:9,INVALID_LINKED_FORMAT:10,MUST_HAVE_MESSAGES_IN_PLURAL:11,UNEXPECTED_EMPTY_LINKED_MODIFIER:12,UNEXPECTED_EMPTY_LINKED_KEY:13,UNEXPECTED_LEXICAL_ANALYSIS:14,UNHANDLED_CODEGEN_NODE_TYPE:15,UNHANDLED_MINIFIER_NODE_TYPE:16,__EXTEND_POINT__:17},DS={[ie.EXPECTED_TOKEN]:"Expected token: '{0}'",[ie.INVALID_TOKEN_IN_PLACEHOLDER]:"Invalid token in placeholder: '{0}'",[ie.UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER]:"Unterminated single quote in placeholder",[ie.UNKNOWN_ESCAPE_SEQUENCE]:"Unknown escape sequence: \\{0}",[ie.INVALID_UNICODE_ESCAPE_SEQUENCE]:"Invalid unicode escape sequence: {0}",[ie.UNBALANCED_CLOSING_BRACE]:"Unbalanced closing brace",[ie.UNTERMINATED_CLOSING_BRACE]:"Unterminated closing brace",[ie.EMPTY_PLACEHOLDER]:"Empty placeholder",[ie.NOT_ALLOW_NEST_PLACEHOLDER]:"Not allowed nest placeholder",[ie.INVALID_LINKED_FORMAT]:"Invalid linked format",[ie.MUST_HAVE_MESSAGES_IN_PLURAL]:"Plural must have messages",[ie.UNEXPECTED_EMPTY_LINKED_MODIFIER]:"Unexpected empty linked modifier",[ie.UNEXPECTED_EMPTY_LINKED_KEY]:"Unexpected empty linked key",[ie.UNEXPECTED_LEXICAL_ANALYSIS]:"Unexpected lexical analysis in token: '{0}'",[ie.UNHANDLED_CODEGEN_NODE_TYPE]:"unhandled codegen node type: '{0}'",[ie.UNHANDLED_MINIFIER_NODE_TYPE]:"unhandled mimifier node type: '{0}'"};function Pr(e,t,o={}){const{domain:r,messages:n,args:i}=o,l=ld((n||DS)[e]||"",...i||[]),a=new SyntaxError(String(l));return a.code=e,t&&(a.location=t),a.domain=r,a}function RS(e){throw e}const Zt=" ",FS="\r",it=` +`,OS="\u2028",NS="\u2029";function MS(e){const t=e;let o=0,r=1,n=1,i=0;const l=D=>t[D]===FS&&t[D+1]===it,a=D=>t[D]===it,s=D=>t[D]===NS,c=D=>t[D]===OS,u=D=>l(D)||a(D)||s(D)||c(D),f=()=>o,d=()=>r,p=()=>n,g=()=>i,C=D=>l(D)||s(D)||c(D)?it:t[D],S=()=>C(o),E=()=>C(o+i);function T(){return i=0,u(o)&&(r++,n=0),l(o)&&o++,o++,n++,t[o]}function v(){return l(o+i)&&i++,i++,t[o+i]}function y(){o=0,r=1,n=1,i=0}function L(D=0){i=D}function w(){const D=o+i;for(;D!==o;)T();i=0}return{index:f,line:d,column:p,peekOffset:g,charAt:C,currentChar:S,currentPeek:E,next:T,peek:v,reset:y,resetPeek:L,skipToPeek:w}}const bo=void 0,kS=".",Js="'",HS="tokenizer";function $S(e,t={}){const o=t.location!==!1,r=MS(e),n=()=>r.index(),i=()=>PS(r.line(),r.column(),r.index()),l=i(),a=n(),s={currentType:14,offset:a,startLoc:l,endLoc:l,lastType:14,lastOffset:a,lastStartLoc:l,lastEndLoc:l,braceNest:0,inLinked:!1,text:""},c=()=>s,{onError:u}=t;function f(m,h,A,...O){const j=c();if(h.column+=A,h.offset+=A,u){const B=o?Yn(j.startLoc,h):null,I=Pr(m,B,{domain:HS,args:O});u(I)}}function d(m,h,A){m.endLoc=i(),m.currentType=h;const O={type:h};return o&&(O.loc=Yn(m.startLoc,m.endLoc)),A!=null&&(O.value=A),O}const p=m=>d(m,14);function g(m,h){return m.currentChar()===h?(m.next(),h):(f(ie.EXPECTED_TOKEN,i(),0,h),"")}function C(m){let h="";for(;m.currentPeek()===Zt||m.currentPeek()===it;)h+=m.currentPeek(),m.peek();return h}function S(m){const h=C(m);return m.skipToPeek(),h}function E(m){if(m===bo)return!1;const h=m.charCodeAt(0);return h>=97&&h<=122||h>=65&&h<=90||h===95}function T(m){if(m===bo)return!1;const h=m.charCodeAt(0);return h>=48&&h<=57}function v(m,h){const{currentType:A}=h;if(A!==2)return!1;C(m);const O=E(m.currentPeek());return m.resetPeek(),O}function y(m,h){const{currentType:A}=h;if(A!==2)return!1;C(m);const O=m.currentPeek()==="-"?m.peek():m.currentPeek(),j=T(O);return m.resetPeek(),j}function L(m,h){const{currentType:A}=h;if(A!==2)return!1;C(m);const O=m.currentPeek()===Js;return m.resetPeek(),O}function w(m,h){const{currentType:A}=h;if(A!==8)return!1;C(m);const O=m.currentPeek()===".";return m.resetPeek(),O}function D(m,h){const{currentType:A}=h;if(A!==9)return!1;C(m);const O=E(m.currentPeek());return m.resetPeek(),O}function F(m,h){const{currentType:A}=h;if(!(A===8||A===12))return!1;C(m);const O=m.currentPeek()===":";return m.resetPeek(),O}function P(m,h){const{currentType:A}=h;if(A!==10)return!1;const O=()=>{const B=m.currentPeek();return B==="{"?E(m.peek()):B==="@"||B==="%"||B==="|"||B===":"||B==="."||B===Zt||!B?!1:B===it?(m.peek(),O()):k(m,!1)},j=O();return m.resetPeek(),j}function U(m){C(m);const h=m.currentPeek()==="|";return m.resetPeek(),h}function X(m){const h=C(m),A=m.currentPeek()==="%"&&m.peek()==="{";return m.resetPeek(),{isModulo:A,hasSpace:h.length>0}}function k(m,h=!0){const A=(j=!1,B="",I=!1)=>{const M=m.currentPeek();return M==="{"?B==="%"?!1:j:M==="@"||!M?B==="%"?!0:j:M==="%"?(m.peek(),A(j,"%",!0)):M==="|"?B==="%"||I?!0:!(B===Zt||B===it):M===Zt?(m.peek(),A(!0,Zt,I)):M===it?(m.peek(),A(!0,it,I)):!0},O=A();return h&&m.resetPeek(),O}function Q(m,h){const A=m.currentChar();return A===bo?bo:h(A)?(m.next(),A):null}function me(m){const h=m.charCodeAt(0);return h>=97&&h<=122||h>=65&&h<=90||h>=48&&h<=57||h===95||h===36}function ye(m){return Q(m,me)}function se(m){const h=m.charCodeAt(0);return h>=97&&h<=122||h>=65&&h<=90||h>=48&&h<=57||h===95||h===36||h===45}function ne(m){return Q(m,se)}function de(m){const h=m.charCodeAt(0);return h>=48&&h<=57}function tt(m){return Q(m,de)}function ft(m){const h=m.charCodeAt(0);return h>=48&&h<=57||h>=65&&h<=70||h>=97&&h<=102}function Re(m){return Q(m,ft)}function Fe(m){let h="",A="";for(;h=tt(m);)A+=h;return A}function Tt(m){S(m);const h=m.currentChar();return h!=="%"&&f(ie.EXPECTED_TOKEN,i(),0,h),m.next(),"%"}function ht(m){let h="";for(;;){const A=m.currentChar();if(A==="{"||A==="}"||A==="@"||A==="|"||!A)break;if(A==="%")if(k(m))h+=A,m.next();else break;else if(A===Zt||A===it)if(k(m))h+=A,m.next();else{if(U(m))break;h+=A,m.next()}else h+=A,m.next()}return h}function gt(m){S(m);let h="",A="";for(;h=ne(m);)A+=h;return m.currentChar()===bo&&f(ie.UNTERMINATED_CLOSING_BRACE,i(),0),A}function We(m){S(m);let h="";return m.currentChar()==="-"?(m.next(),h+=`-${Fe(m)}`):h+=Fe(m),m.currentChar()===bo&&f(ie.UNTERMINATED_CLOSING_BRACE,i(),0),h}function H(m){return m!==Js&&m!==it}function Y(m){S(m),g(m,"'");let h="",A="";for(;h=Q(m,H);)h==="\\"?A+=G(m):A+=h;const O=m.currentChar();return O===it||O===bo?(f(ie.UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER,i(),0),O===it&&(m.next(),g(m,"'")),A):(g(m,"'"),A)}function G(m){const h=m.currentChar();switch(h){case"\\":case"'":return m.next(),`\\${h}`;case"u":return ee(m,h,4);case"U":return ee(m,h,6);default:return f(ie.UNKNOWN_ESCAPE_SEQUENCE,i(),0,h),""}}function ee(m,h,A){g(m,h);let O="";for(let j=0;j{const O=m.currentChar();return O==="{"||O==="%"||O==="@"||O==="|"||O==="("||O===")"||!O||O===Zt?A:(A+=O,m.next(),h(A))};return h("")}function R(m){S(m);const h=g(m,"|");return S(m),h}function $(m,h){let A=null;switch(m.currentChar()){case"{":return h.braceNest>=1&&f(ie.NOT_ALLOW_NEST_PLACEHOLDER,i(),0),m.next(),A=d(h,2,"{"),S(m),h.braceNest++,A;case"}":return h.braceNest>0&&h.currentType===2&&f(ie.EMPTY_PLACEHOLDER,i(),0),m.next(),A=d(h,3,"}"),h.braceNest--,h.braceNest>0&&S(m),h.inLinked&&h.braceNest===0&&(h.inLinked=!1),A;case"@":return h.braceNest>0&&f(ie.UNTERMINATED_CLOSING_BRACE,i(),0),A=N(m,h)||p(h),h.braceNest=0,A;default:{let j=!0,B=!0,I=!0;if(U(m))return h.braceNest>0&&f(ie.UNTERMINATED_CLOSING_BRACE,i(),0),A=d(h,1,R(m)),h.braceNest=0,h.inLinked=!1,A;if(h.braceNest>0&&(h.currentType===5||h.currentType===6||h.currentType===7))return f(ie.UNTERMINATED_CLOSING_BRACE,i(),0),h.braceNest=0,V(m,h);if(j=v(m,h))return A=d(h,5,gt(m)),S(m),A;if(B=y(m,h))return A=d(h,6,We(m)),S(m),A;if(I=L(m,h))return A=d(h,7,Y(m)),S(m),A;if(!j&&!B&&!I)return A=d(h,13,b(m)),f(ie.INVALID_TOKEN_IN_PLACEHOLDER,i(),0,A.value),S(m),A;break}}return A}function N(m,h){const{currentType:A}=h;let O=null;const j=m.currentChar();switch((A===8||A===9||A===12||A===10)&&(j===it||j===Zt)&&f(ie.INVALID_LINKED_FORMAT,i(),0),j){case"@":return m.next(),O=d(h,8,"@"),h.inLinked=!0,O;case".":return S(m),m.next(),d(h,9,".");case":":return S(m),m.next(),d(h,10,":");default:return U(m)?(O=d(h,1,R(m)),h.braceNest=0,h.inLinked=!1,O):w(m,h)||F(m,h)?(S(m),N(m,h)):D(m,h)?(S(m),d(h,12,_(m))):P(m,h)?(S(m),j==="{"?$(m,h)||O:d(h,11,x(m))):(A===8&&f(ie.INVALID_LINKED_FORMAT,i(),0),h.braceNest=0,h.inLinked=!1,V(m,h))}}function V(m,h){let A={type:14};if(h.braceNest>0)return $(m,h)||p(h);if(h.inLinked)return N(m,h)||p(h);switch(m.currentChar()){case"{":return $(m,h)||p(h);case"}":return f(ie.UNBALANCED_CLOSING_BRACE,i(),0),m.next(),d(h,3,"}");case"@":return N(m,h)||p(h);default:{if(U(m))return A=d(h,1,R(m)),h.braceNest=0,h.inLinked=!1,A;const{isModulo:j,hasSpace:B}=X(m);if(j)return B?d(h,0,ht(m)):d(h,4,Tt(m));if(k(m))return d(h,0,ht(m));break}}return A}function z(){const{currentType:m,offset:h,startLoc:A,endLoc:O}=s;return s.lastType=m,s.lastOffset=h,s.lastStartLoc=A,s.lastEndLoc=O,s.offset=n(),s.startLoc=i(),r.currentChar()===bo?d(s,14):V(r,s)}return{nextToken:z,currentOffset:n,currentPosition:i,context:c}}const BS="parser",WS=/(?:\\\\|\\'|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;function zS(e,t,o){switch(e){case"\\\\":return"\\";case"\\'":return"'";default:{const r=parseInt(t||o,16);return r<=55295||r>=57344?String.fromCodePoint(r):"�"}}}function US(e={}){const t=e.location!==!1,{onError:o,onWarn:r}=e;function n(v,y,L,w,...D){const F=v.currentPosition();if(F.offset+=w,F.column+=w,o){const P=t?Yn(L,F):null,U=Pr(y,P,{domain:BS,args:D});o(U)}}function i(v,y,L,w,...D){const F=v.currentPosition();if(F.offset+=w,F.column+=w,r){const P=t?Yn(L,F):null;r(wS(y,P,D))}}function l(v,y,L){const w={type:v};return t&&(w.start=y,w.end=y,w.loc={start:L,end:L}),w}function a(v,y,L,w){t&&(v.end=y,v.loc&&(v.loc.end=L))}function s(v,y){const L=v.context(),w=l(3,L.offset,L.startLoc);return w.value=y,a(w,v.currentOffset(),v.currentPosition()),w}function c(v,y){const L=v.context(),{lastOffset:w,lastStartLoc:D}=L,F=l(5,w,D);return F.index=parseInt(y,10),v.nextToken(),a(F,v.currentOffset(),v.currentPosition()),F}function u(v,y,L){const w=v.context(),{lastOffset:D,lastStartLoc:F}=w,P=l(4,D,F);return P.key=y,L===!0&&(P.modulo=!0),v.nextToken(),a(P,v.currentOffset(),v.currentPosition()),P}function f(v,y){const L=v.context(),{lastOffset:w,lastStartLoc:D}=L,F=l(9,w,D);return F.value=y.replace(WS,zS),v.nextToken(),a(F,v.currentOffset(),v.currentPosition()),F}function d(v){const y=v.nextToken(),L=v.context(),{lastOffset:w,lastStartLoc:D}=L,F=l(8,w,D);return y.type!==12?(n(v,ie.UNEXPECTED_EMPTY_LINKED_MODIFIER,L.lastStartLoc,0),F.value="",a(F,w,D),{nextConsumeToken:y,node:F}):(y.value==null&&n(v,ie.UNEXPECTED_LEXICAL_ANALYSIS,L.lastStartLoc,0,wt(y)),F.value=y.value||"",a(F,v.currentOffset(),v.currentPosition()),{node:F})}function p(v,y){const L=v.context(),w=l(7,L.offset,L.startLoc);return w.value=y,a(w,v.currentOffset(),v.currentPosition()),w}function g(v){const y=v.context(),L=l(6,y.offset,y.startLoc);let w=v.nextToken();if(w.type===9){const D=d(v);L.modifier=D.node,w=D.nextConsumeToken||v.nextToken()}switch(w.type!==10&&n(v,ie.UNEXPECTED_LEXICAL_ANALYSIS,y.lastStartLoc,0,wt(w)),w=v.nextToken(),w.type===2&&(w=v.nextToken()),w.type){case 11:w.value==null&&n(v,ie.UNEXPECTED_LEXICAL_ANALYSIS,y.lastStartLoc,0,wt(w)),L.key=p(v,w.value||"");break;case 5:w.value==null&&n(v,ie.UNEXPECTED_LEXICAL_ANALYSIS,y.lastStartLoc,0,wt(w)),L.key=u(v,w.value||"");break;case 6:w.value==null&&n(v,ie.UNEXPECTED_LEXICAL_ANALYSIS,y.lastStartLoc,0,wt(w)),L.key=c(v,w.value||"");break;case 7:w.value==null&&n(v,ie.UNEXPECTED_LEXICAL_ANALYSIS,y.lastStartLoc,0,wt(w)),L.key=f(v,w.value||"");break;default:{n(v,ie.UNEXPECTED_EMPTY_LINKED_KEY,y.lastStartLoc,0);const D=v.context(),F=l(7,D.offset,D.startLoc);return F.value="",a(F,D.offset,D.startLoc),L.key=F,a(L,D.offset,D.startLoc),{nextConsumeToken:w,node:L}}}return a(L,v.currentOffset(),v.currentPosition()),{node:L}}function C(v){const y=v.context(),L=y.currentType===1?v.currentOffset():y.offset,w=y.currentType===1?y.endLoc:y.startLoc,D=l(2,L,w);D.items=[];let F=null,P=null;do{const k=F||v.nextToken();switch(F=null,k.type){case 0:k.value==null&&n(v,ie.UNEXPECTED_LEXICAL_ANALYSIS,y.lastStartLoc,0,wt(k)),D.items.push(s(v,k.value||""));break;case 6:k.value==null&&n(v,ie.UNEXPECTED_LEXICAL_ANALYSIS,y.lastStartLoc,0,wt(k)),D.items.push(c(v,k.value||""));break;case 4:P=!0;break;case 5:k.value==null&&n(v,ie.UNEXPECTED_LEXICAL_ANALYSIS,y.lastStartLoc,0,wt(k)),D.items.push(u(v,k.value||"",!!P)),P&&(i(v,ha.USE_MODULO_SYNTAX,y.lastStartLoc,0,wt(k)),P=null);break;case 7:k.value==null&&n(v,ie.UNEXPECTED_LEXICAL_ANALYSIS,y.lastStartLoc,0,wt(k)),D.items.push(f(v,k.value||""));break;case 8:{const Q=g(v);D.items.push(Q.node),F=Q.nextConsumeToken||null;break}}}while(y.currentType!==14&&y.currentType!==1);const U=y.currentType===1?y.lastOffset:v.currentOffset(),X=y.currentType===1?y.lastEndLoc:v.currentPosition();return a(D,U,X),D}function S(v,y,L,w){const D=v.context();let F=w.items.length===0;const P=l(1,y,L);P.cases=[],P.cases.push(w);do{const U=C(v);F||(F=U.items.length===0),P.cases.push(U)}while(D.currentType!==14);return F&&n(v,ie.MUST_HAVE_MESSAGES_IN_PLURAL,L,0),a(P,v.currentOffset(),v.currentPosition()),P}function E(v){const y=v.context(),{offset:L,startLoc:w}=y,D=C(v);return y.currentType===14?D:S(v,L,w,D)}function T(v){const y=$S(v,ad({},e)),L=y.context(),w=l(0,L.offset,L.startLoc);return t&&w.loc&&(w.loc.source=v),w.body=E(y),e.onCacheKey&&(w.cacheKey=e.onCacheKey(v)),L.currentType!==14&&n(y,ie.UNEXPECTED_LEXICAL_ANALYSIS,L.lastStartLoc,0,v[L.offset]||""),a(w,y.currentOffset(),y.currentPosition()),w}return{parse:T}}function wt(e){if(e.type===14)return"EOF";const t=(e.value||"").replace(/\r?\n/gu,"\\n");return t.length>10?t.slice(0,9)+"…":t}function VS(e,t={}){const o={ast:e,helpers:new Set};return{context:()=>o,helper:i=>(o.helpers.add(i),i)}}function Qs(e,t){for(let o=0;oZs(o)),e}function Zs(e){if(e.items.length===1){const t=e.items[0];(t.type===3||t.type===9)&&(e.static=t.value,delete t.value)}else{const t=[];for(let o=0;ol;function s(C,S){l.code+=C}function c(C,S=!0){const E=S?r:"";s(n?E+" ".repeat(C):E)}function u(C=!0){const S=++l.indentLevel;C&&c(S)}function f(C=!0){const S=--l.indentLevel;C&&c(S)}function d(){c(l.indentLevel)}return{context:a,push:s,indent:u,deindent:f,newline:d,helper:C=>`_${C}`,needIndent:()=>l.needIndent}}function XS(e,t){const{helper:o}=e;e.push(`${o("linked")}(`),xr(e,t.key),t.modifier?(e.push(", "),xr(e,t.modifier),e.push(", _type")):e.push(", undefined, _type"),e.push(")")}function JS(e,t){const{helper:o,needIndent:r}=e;e.push(`${o("normalize")}([`),e.indent(r());const n=t.items.length;for(let i=0;i1){e.push(`${o("plural")}([`),e.indent(r());const n=t.cases.length;for(let i=0;i{const o=Xs(t.mode)?t.mode:"normal",r=Xs(t.filename)?t.filename:"message.intl";t.sourceMap;const n=t.breakLineCode!=null?t.breakLineCode:o==="arrow"?";":` +`,i=t.needIndent?t.needIndent:o!=="arrow",l=e.helpers||[],a=qS(e,{filename:r,breakLineCode:n,needIndent:i});a.push(o==="normal"?"function __msg__ (ctx) {":"(ctx) => {"),a.indent(i),l.length>0&&(a.push(`const { ${sd(l.map(u=>`${u}: _${u}`),", ")} } = ctx`),a.newline()),a.push("return "),xr(a,e),a.deindent(i),a.push("}"),delete e.helpers;const{code:s,map:c}=a.context();return{ast:e,code:s,map:c?c.toJSON():void 0}};function ty(e,t={}){const o=ad({},t),r=!!o.jit,n=!!o.minify,i=o.optimize==null?!0:o.optimize,a=US(o).parse(e);return r?(i&&GS(a),n&&ur(a),{ast:a,code:""}):(jS(a,o),ey(a,o))}function oy(){typeof __INTLIFY_PROD_DEVTOOLS__!="boolean"&&(io().__INTLIFY_PROD_DEVTOOLS__=!1),typeof __INTLIFY_JIT_COMPILATION__!="boolean"&&(io().__INTLIFY_JIT_COMPILATION__=!1),typeof __INTLIFY_DROP_MESSAGE_COMPILER__!="boolean"&&(io().__INTLIFY_DROP_MESSAGE_COMPILER__=!1)}function Kt(e){return Ce(e)&&Ca(e)===0&&(Ot(e,"b")||Ot(e,"body"))}const cd=["b","body"];function ry(e){return Oo(e,cd)}const ud=["c","cases"];function ny(e){return Oo(e,ud,[])}const fd=["s","static"];function iy(e){return Oo(e,fd)}const dd=["i","items"];function ly(e){return Oo(e,dd,[])}const pd=["t","type"];function Ca(e){return Oo(e,pd)}const md=["v","value"];function Pn(e,t){const o=Oo(e,md);if(o!=null)return o;throw an(t)}const hd=["m","modifier"];function ay(e){return Oo(e,hd)}const gd=["k","key"];function sy(e){const t=Oo(e,gd);if(t)return t;throw an(6)}function Oo(e,t,o){for(let r=0;r{l===void 0?l=a:l+=a},d[1]=()=>{l!==void 0&&(t.push(l),l=void 0)},d[2]=()=>{d[0](),n++},d[3]=()=>{if(n>0)n--,r=4,d[0]();else{if(n=0,l===void 0||(l=py(l),l===!1))return!1;d[1]()}};function p(){const g=e[o+1];if(r===5&&g==="'"||r===6&&g==='"')return o++,a="\\"+g,d[0](),!0}for(;r!==null;)if(o++,i=e[o],!(i==="\\"&&p())){if(s=dy(i),f=No[r],c=f[s]||f.l||8,c===8||(r=c[0],c[1]!==void 0&&(u=d[c[1]],u&&(a=i,u()===!1))))return;if(r===7)return t}}const ec=new Map;function hy(e,t){return Ce(e)?e[t]:null}function gy(e,t){if(!Ce(e))return null;let o=ec.get(t);if(o||(o=my(t),o&&ec.set(t,o)),!o)return null;const r=o.length;let n=e,i=0;for(;ie,by=e=>"",xy="text",_y=e=>e.length===0?"":ES(e),vy=yS;function tc(e,t){return e=Math.abs(e),t===2?e?e>1?1:0:1:e?Math.min(e,2):0}function Sy(e){const t=Me(e.pluralIndex)?e.pluralIndex:-1;return e.named&&(Me(e.named.count)||Me(e.named.n))?Me(e.named.count)?e.named.count:Me(e.named.n)?e.named.n:t:t}function yy(e,t){t.count||(t.count=e),t.n||(t.n=e)}function Ey(e={}){const t=e.locale,o=Sy(e),r=Ce(e.pluralRules)&&K(t)&&Pe(e.pluralRules[t])?e.pluralRules[t]:tc,n=Ce(e.pluralRules)&&K(t)&&Pe(e.pluralRules[t])?tc:void 0,i=E=>E[r(o,E.length,n)],l=e.list||[],a=E=>l[E],s=e.named||ve();Me(e.pluralIndex)&&yy(o,s);const c=E=>s[E];function u(E){const T=Pe(e.messages)?e.messages(E):Ce(e.messages)?e.messages[E]:!1;return T||(e.parent?e.parent.message(E):by)}const f=E=>e.modifiers?e.modifiers[E]:Cy,d=ae(e.processor)&&Pe(e.processor.normalize)?e.processor.normalize:_y,p=ae(e.processor)&&Pe(e.processor.interpolate)?e.processor.interpolate:vy,g=ae(e.processor)&&K(e.processor.type)?e.processor.type:xy,S={list:a,named:c,plural:i,linked:(E,...T)=>{const[v,y]=T;let L="text",w="";T.length===1?Ce(v)?(w=v.modifier||w,L=v.type||L):K(v)&&(w=v||w):T.length===2&&(K(v)&&(w=v||w),K(y)&&(L=y||L));const D=u(E)(S),F=L==="vnode"&&Ae(D)&&w?D[0]:D;return w?f(w)(F,L):F},message:u,type:g,interpolate:p,normalize:d,values:Ge(ve(),l,s)};return S}let sn=null;function Ty(e){sn=e}function Py(e,t,o){sn&&sn.emit("i18n:init",{timestamp:Date.now(),i18n:e,version:t,meta:o})}const Iy=Ay("function:translate");function Ay(e){return t=>sn&&sn.emit(e,t)}const Ly=ha.__EXTEND_POINT__,Wo=Pi(Ly),wy={FALLBACK_TO_TRANSLATE:Wo(),CANNOT_FORMAT_NUMBER:Wo(),FALLBACK_TO_NUMBER_FORMAT:Wo(),CANNOT_FORMAT_DATE:Wo(),FALLBACK_TO_DATE_FORMAT:Wo(),EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER:Wo(),__EXTEND_POINT__:Wo()},bd=ie.__EXTEND_POINT__,zo=Pi(bd),Nt={INVALID_ARGUMENT:bd,INVALID_DATE_ARGUMENT:zo(),INVALID_ISO_DATE_ARGUMENT:zo(),NOT_SUPPORT_NON_STRING_MESSAGE:zo(),NOT_SUPPORT_LOCALE_PROMISE_VALUE:zo(),NOT_SUPPORT_LOCALE_ASYNC_FUNCTION:zo(),NOT_SUPPORT_LOCALE_TYPE:zo(),__EXTEND_POINT__:zo()};function jt(e){return Pr(e,null,void 0)}function ba(e,t){return t.locale!=null?oc(t.locale):oc(e.locale)}let Qi;function oc(e){if(K(e))return e;if(Pe(e)){if(e.resolvedOnce&&Qi!=null)return Qi;if(e.constructor.name==="Function"){const t=e();if(SS(t))throw jt(Nt.NOT_SUPPORT_LOCALE_PROMISE_VALUE);return Qi=t}else throw jt(Nt.NOT_SUPPORT_LOCALE_ASYNC_FUNCTION)}else throw jt(Nt.NOT_SUPPORT_LOCALE_TYPE)}function Dy(e,t,o){return[...new Set([o,...Ae(t)?t:Ce(t)?Object.keys(t):K(t)?[t]:[o]])]}function xd(e,t,o){const r=K(o)?o:_r,n=e;n.__localeChainCache||(n.__localeChainCache=new Map);let i=n.__localeChainCache.get(r);if(!i){i=[];let l=[o];for(;Ae(l);)l=rc(i,l,t);const a=Ae(t)||!ae(t)?t:t.default?t.default:null;l=K(a)?[a]:a,Ae(l)&&rc(i,l,!1),n.__localeChainCache.set(r,i)}return i}function rc(e,t,o){let r=!0;for(let n=0;n`${e.charAt(0).toLocaleUpperCase()}${e.substr(1)}`;function Ny(){return{upper:(e,t)=>t==="text"&&K(e)?e.toUpperCase():t==="vnode"&&Ce(e)&&"__v_isVNode"in e?e.children.toUpperCase():e,lower:(e,t)=>t==="text"&&K(e)?e.toLowerCase():t==="vnode"&&Ce(e)&&"__v_isVNode"in e?e.children.toLowerCase():e,capitalize:(e,t)=>t==="text"&&K(e)?ic(e):t==="vnode"&&Ce(e)&&"__v_isVNode"in e?ic(e.children):e}}let _d;function lc(e){_d=e}let vd;function My(e){vd=e}let Sd;function ky(e){Sd=e}let yd=null;const Hy=e=>{yd=e},$y=()=>yd;let Ed=null;const ac=e=>{Ed=e},By=()=>Ed;let sc=0;function Wy(e={}){const t=Pe(e.onWarn)?e.onWarn:TS,o=K(e.version)?e.version:Oy,r=K(e.locale)||Pe(e.locale)?e.locale:_r,n=Pe(r)?_r:r,i=Ae(e.fallbackLocale)||ae(e.fallbackLocale)||K(e.fallbackLocale)||e.fallbackLocale===!1?e.fallbackLocale:n,l=ae(e.messages)?e.messages:Zi(n),a=ae(e.datetimeFormats)?e.datetimeFormats:Zi(n),s=ae(e.numberFormats)?e.numberFormats:Zi(n),c=Ge(ve(),e.modifiers,Ny()),u=e.pluralRules||ve(),f=Pe(e.missing)?e.missing:null,d=pe(e.missingWarn)||wo(e.missingWarn)?e.missingWarn:!0,p=pe(e.fallbackWarn)||wo(e.fallbackWarn)?e.fallbackWarn:!0,g=!!e.fallbackFormat,C=!!e.unresolving,S=Pe(e.postTranslation)?e.postTranslation:null,E=ae(e.processor)?e.processor:null,T=pe(e.warnHtmlMessage)?e.warnHtmlMessage:!0,v=!!e.escapeParameter,y=Pe(e.messageCompiler)?e.messageCompiler:_d,L=Pe(e.messageResolver)?e.messageResolver:vd||hy,w=Pe(e.localeFallbacker)?e.localeFallbacker:Sd||Dy,D=Ce(e.fallbackContext)?e.fallbackContext:void 0,F=e,P=Ce(F.__datetimeFormatters)?F.__datetimeFormatters:new Map,U=Ce(F.__numberFormatters)?F.__numberFormatters:new Map,X=Ce(F.__meta)?F.__meta:{};sc++;const k={version:o,cid:sc,locale:r,fallbackLocale:i,messages:l,modifiers:c,pluralRules:u,missing:f,missingWarn:d,fallbackWarn:p,fallbackFormat:g,unresolving:C,postTranslation:S,processor:E,warnHtmlMessage:T,escapeParameter:v,messageCompiler:y,messageResolver:L,localeFallbacker:w,fallbackContext:D,onWarn:t,__meta:X};return k.datetimeFormats=a,k.numberFormats=s,k.__datetimeFormatters=P,k.__numberFormatters=U,__INTLIFY_PROD_DEVTOOLS__&&Py(k,o,X),k}const Zi=e=>({[e]:ve()});function xa(e,t,o,r,n){const{missing:i,onWarn:l}=e;if(i!==null){const a=i(e,o,t,n);return K(a)?a:t}else return t}function Fr(e,t,o){const r=e;r.__localeChainCache=new Map,e.localeFallbacker(e,o,t)}function zy(e,t){return e===t?!1:e.split("-")[0]===t.split("-")[0]}function Uy(e,t){const o=t.indexOf(e);if(o===-1)return!1;for(let r=o+1;rVy(o,e)}function Vy(e,t){const o=ry(t);if(o==null)throw an(0);if(Ca(o)===1){const i=ny(o);return e.plural(i.reduce((l,a)=>[...l,cc(e,a)],[]))}else return cc(e,o)}function cc(e,t){const o=iy(t);if(o!=null)return e.type==="text"?o:e.normalize([o]);{const r=ly(t).reduce((n,i)=>[...n,wl(e,i)],[]);return e.normalize(r)}}function wl(e,t){const o=Ca(t);switch(o){case 3:return Pn(t,o);case 9:return Pn(t,o);case 4:{const r=t;if(Ot(r,"k")&&r.k)return e.interpolate(e.named(r.k));if(Ot(r,"key")&&r.key)return e.interpolate(e.named(r.key));throw an(o)}case 5:{const r=t;if(Ot(r,"i")&&Me(r.i))return e.interpolate(e.list(r.i));if(Ot(r,"index")&&Me(r.index))return e.interpolate(e.list(r.index));throw an(o)}case 6:{const r=t,n=ay(r),i=sy(r);return e.linked(wl(e,i),n?wl(e,n):void 0,e.type)}case 7:return Pn(t,o);case 8:return Pn(t,o);default:throw new Error(`unhandled node on format message part: ${o}`)}}const Td=e=>e;let fr=ve();function Pd(e,t={}){let o=!1;const r=t.onError||RS;return t.onError=n=>{o=!0,r(n)},{...ty(e,t),detectError:o}}const jy=(e,t)=>{if(!K(e))throw jt(Nt.NOT_SUPPORT_NON_STRING_MESSAGE);{pe(t.warnHtmlMessage)&&t.warnHtmlMessage;const r=(t.onCacheKey||Td)(e),n=fr[r];if(n)return n;const{code:i,detectError:l}=Pd(e,t),a=new Function(`return ${i}`)();return l?a:fr[r]=a}};function Gy(e,t){if(__INTLIFY_JIT_COMPILATION__&&!__INTLIFY_DROP_MESSAGE_COMPILER__&&K(e)){pe(t.warnHtmlMessage)&&t.warnHtmlMessage;const r=(t.onCacheKey||Td)(e),n=fr[r];if(n)return n;const{ast:i,detectError:l}=Pd(e,{...t,location:!1,jit:!0}),a=el(i);return l?a:fr[r]=a}else{const o=e.cacheKey;if(o){const r=fr[o];return r||(fr[o]=el(e))}else return el(e)}}const uc=()=>"",At=e=>Pe(e);function fc(e,...t){const{fallbackFormat:o,postTranslation:r,unresolving:n,messageCompiler:i,fallbackLocale:l,messages:a}=e,[s,c]=Dl(...t),u=pe(c.missingWarn)?c.missingWarn:e.missingWarn,f=pe(c.fallbackWarn)?c.fallbackWarn:e.fallbackWarn,d=pe(c.escapeParameter)?c.escapeParameter:e.escapeParameter,p=!!c.resolvedMessage,g=K(c.default)||pe(c.default)?pe(c.default)?i?s:()=>s:c.default:o?i?s:()=>s:"",C=o||g!=="",S=ba(e,c);d&&Ky(c);let[E,T,v]=p?[s,S,a[S]||ve()]:Id(e,s,S,l,f,u),y=E,L=s;if(!p&&!(K(y)||Kt(y)||At(y))&&C&&(y=g,L=y),!p&&(!(K(y)||Kt(y)||At(y))||!K(T)))return n?Ii:s;let w=!1;const D=()=>{w=!0},F=At(y)?y:Ad(e,s,T,y,L,D);if(w)return y;const P=Xy(e,T,v,c),U=Ey(P),X=Yy(e,F,U),k=r?r(X,s):X;if(__INTLIFY_PROD_DEVTOOLS__){const Q={timestamp:Date.now(),key:K(s)?s:At(y)?y.key:"",locale:T||(At(y)?y.locale:""),format:K(y)?y:At(y)?y.source:"",message:k};Q.meta=Ge({},e.__meta,$y()||{}),Iy(Q)}return k}function Ky(e){Ae(e.list)?e.list=e.list.map(t=>K(t)?qs(t):t):Ce(e.named)&&Object.keys(e.named).forEach(t=>{K(e.named[t])&&(e.named[t]=qs(e.named[t]))})}function Id(e,t,o,r,n,i){const{messages:l,onWarn:a,messageResolver:s,localeFallbacker:c}=e,u=c(e,r,o);let f=ve(),d,p=null;const g="translate";for(let C=0;Cr);return c.locale=o,c.key=t,c}const s=l(r,qy(e,o,n,r,a,i));return s.locale=o,s.key=t,s.source=r,s}function Yy(e,t,o){return t(o)}function Dl(...e){const[t,o,r]=e,n=ve();if(!K(t)&&!Me(t)&&!At(t)&&!Kt(t))throw jt(Nt.INVALID_ARGUMENT);const i=Me(t)?String(t):(At(t),t);return Me(o)?n.plural=o:K(o)?n.default=o:ae(o)&&!Ti(o)?n.named=o:Ae(o)&&(n.list=o),Me(r)?n.plural=r:K(r)?n.default=r:ae(r)&&Ge(n,r),[i,n]}function qy(e,t,o,r,n,i){return{locale:t,key:o,warnHtmlMessage:n,onError:l=>{throw i&&i(l),l},onCacheKey:l=>CS(t,o,l)}}function Xy(e,t,o,r){const{modifiers:n,pluralRules:i,messageResolver:l,fallbackLocale:a,fallbackWarn:s,missingWarn:c,fallbackContext:u}=e,d={locale:t,modifiers:n,pluralRules:i,messages:p=>{let g=l(o,p);if(g==null&&u){const[,,C]=Id(u,p,t,a,s,c);g=l(C,p)}if(K(g)||Kt(g)){let C=!1;const E=Ad(e,p,t,g,p,()=>{C=!0});return C?uc:E}else return At(g)?g:uc}};return e.processor&&(d.processor=e.processor),r.list&&(d.list=r.list),r.named&&(d.named=r.named),Me(r.plural)&&(d.pluralIndex=r.plural),d}function dc(e,...t){const{datetimeFormats:o,unresolving:r,fallbackLocale:n,onWarn:i,localeFallbacker:l}=e,{__datetimeFormatters:a}=e,[s,c,u,f]=Rl(...t),d=pe(u.missingWarn)?u.missingWarn:e.missingWarn;pe(u.fallbackWarn)?u.fallbackWarn:e.fallbackWarn;const p=!!u.part,g=ba(e,u),C=l(e,n,g);if(!K(s)||s==="")return new Intl.DateTimeFormat(g,f).format(c);let S={},E,T=null;const v="datetime format";for(let w=0;w{Ld.includes(s)?l[s]=o[s]:i[s]=o[s]}),K(r)?i.locale=r:ae(r)&&(l=r),ae(n)&&(l=n),[i.key||"",a,i,l]}function pc(e,t,o){const r=e;for(const n in o){const i=`${t}__${n}`;r.__datetimeFormatters.has(i)&&r.__datetimeFormatters.delete(i)}}function mc(e,...t){const{numberFormats:o,unresolving:r,fallbackLocale:n,onWarn:i,localeFallbacker:l}=e,{__numberFormatters:a}=e,[s,c,u,f]=Fl(...t),d=pe(u.missingWarn)?u.missingWarn:e.missingWarn;pe(u.fallbackWarn)?u.fallbackWarn:e.fallbackWarn;const p=!!u.part,g=ba(e,u),C=l(e,n,g);if(!K(s)||s==="")return new Intl.NumberFormat(g,f).format(c);let S={},E,T=null;const v="number format";for(let w=0;w{wd.includes(s)?l[s]=o[s]:i[s]=o[s]}),K(r)?i.locale=r:ae(r)&&(l=r),ae(n)&&(l=n),[i.key||"",a,i,l]}function hc(e,t,o){const r=e;for(const n in o){const i=`${t}__${n}`;r.__numberFormatters.has(i)&&r.__numberFormatters.delete(i)}}oy();const Jy="9.14.4";function Qy(){typeof __VUE_I18N_FULL_INSTALL__!="boolean"&&(io().__VUE_I18N_FULL_INSTALL__=!0),typeof __VUE_I18N_LEGACY_API__!="boolean"&&(io().__VUE_I18N_LEGACY_API__=!0),typeof __INTLIFY_JIT_COMPILATION__!="boolean"&&(io().__INTLIFY_JIT_COMPILATION__=!1),typeof __INTLIFY_DROP_MESSAGE_COMPILER__!="boolean"&&(io().__INTLIFY_DROP_MESSAGE_COMPILER__=!1),typeof __INTLIFY_PROD_DEVTOOLS__!="boolean"&&(io().__INTLIFY_PROD_DEVTOOLS__=!1)}const Zy=wy.__EXTEND_POINT__,eo=Pi(Zy);eo(),eo(),eo(),eo(),eo(),eo(),eo(),eo(),eo();const Dd=Nt.__EXTEND_POINT__,pt=Pi(Dd),$e={UNEXPECTED_RETURN_TYPE:Dd,INVALID_ARGUMENT:pt(),MUST_BE_CALL_SETUP_TOP:pt(),NOT_INSTALLED:pt(),NOT_AVAILABLE_IN_LEGACY_MODE:pt(),REQUIRED_VALUE:pt(),INVALID_VALUE:pt(),CANNOT_SETUP_VUE_DEVTOOLS_PLUGIN:pt(),NOT_INSTALLED_WITH_PROVIDE:pt(),UNEXPECTED_ERROR:pt(),NOT_COMPATIBLE_LEGACY_VUE_I18N:pt(),BRIDGE_SUPPORT_VUE_2_ONLY:pt(),MUST_DEFINE_I18N_OPTION_IN_ALLOW_COMPOSITION:pt(),NOT_AVAILABLE_COMPOSITION_IN_LEGACY:pt(),__EXTEND_POINT__:pt()};function ze(e,...t){return Pr(e,null,void 0)}const Ol=Fo("__translateVNode"),Nl=Fo("__datetimeParts"),Ml=Fo("__numberParts"),Rd=Fo("__setPluralRules"),Fd=Fo("__injectWithOption"),kl=Fo("__dispose");function cn(e){if(!Ce(e)||Kt(e))return e;for(const t in e)if(Ot(e,t))if(!t.includes("."))Ce(e[t])&&cn(e[t]);else{const o=t.split("."),r=o.length-1;let n=e,i=!1;for(let l=0;l{if("locale"in a&&"resource"in a){const{locale:s,resource:c}=a;s?(l[s]=l[s]||ve(),wn(c,l[s])):wn(c,l)}else K(a)&&wn(JSON.parse(a),l)}),n==null&&i)for(const a in l)Ot(l,a)&&cn(l[a]);return l}function Od(e){return e.type}function Nd(e,t,o){let r=Ce(t.messages)?t.messages:ve();"__i18nGlobal"in o&&(r=Ai(e.locale.value,{messages:r,__i18n:o.__i18nGlobal}));const n=Object.keys(r);n.length&&n.forEach(i=>{e.mergeLocaleMessage(i,r[i])});{if(Ce(t.datetimeFormats)){const i=Object.keys(t.datetimeFormats);i.length&&i.forEach(l=>{e.mergeDateTimeFormat(l,t.datetimeFormats[l])})}if(Ce(t.numberFormats)){const i=Object.keys(t.numberFormats);i.length&&i.forEach(l=>{e.mergeNumberFormat(l,t.numberFormats[l])})}}}function gc(e){return je(pn,null,e,0)}const Cc="__INTLIFY_META__",bc=()=>[],eE=()=>!1;let xc=0;function _c(e){return((t,o,r,n)=>e(o,r,Ao()||void 0,n))}const tE=()=>{const e=Ao();let t=null;return e&&(t=Od(e)[Cc])?{[Cc]:t}:null};function _a(e={},t){const{__root:o,__injectWithOption:r}=e,n=o===void 0,i=e.flatJson,l=Kn?mt:Yl,a=!!e.translateExistCompatible;let s=pe(e.inheritLocale)?e.inheritLocale:!0;const c=l(o&&s?o.locale.value:K(e.locale)?e.locale:_r),u=l(o&&s?o.fallbackLocale.value:K(e.fallbackLocale)||Ae(e.fallbackLocale)||ae(e.fallbackLocale)||e.fallbackLocale===!1?e.fallbackLocale:c.value),f=l(Ai(c.value,e)),d=l(ae(e.datetimeFormats)?e.datetimeFormats:{[c.value]:{}}),p=l(ae(e.numberFormats)?e.numberFormats:{[c.value]:{}});let g=o?o.missingWarn:pe(e.missingWarn)||wo(e.missingWarn)?e.missingWarn:!0,C=o?o.fallbackWarn:pe(e.fallbackWarn)||wo(e.fallbackWarn)?e.fallbackWarn:!0,S=o?o.fallbackRoot:pe(e.fallbackRoot)?e.fallbackRoot:!0,E=!!e.fallbackFormat,T=Pe(e.missing)?e.missing:null,v=Pe(e.missing)?_c(e.missing):null,y=Pe(e.postTranslation)?e.postTranslation:null,L=o?o.warnHtmlMessage:pe(e.warnHtmlMessage)?e.warnHtmlMessage:!0,w=!!e.escapeParameter;const D=o?o.modifiers:ae(e.modifiers)?e.modifiers:{};let F=e.pluralRules||o&&o.pluralRules,P;P=(()=>{n&&ac(null);const I={version:Jy,locale:c.value,fallbackLocale:u.value,messages:f.value,modifiers:D,pluralRules:F,missing:v===null?void 0:v,missingWarn:g,fallbackWarn:C,fallbackFormat:E,unresolving:!0,postTranslation:y===null?void 0:y,warnHtmlMessage:L,escapeParameter:w,messageResolver:e.messageResolver,messageCompiler:e.messageCompiler,__meta:{framework:"vue"}};I.datetimeFormats=d.value,I.numberFormats=p.value,I.__datetimeFormatters=ae(P)?P.__datetimeFormatters:void 0,I.__numberFormatters=ae(P)?P.__numberFormatters:void 0;const M=Wy(I);return n&&ac(M),M})(),Fr(P,c.value,u.value);function X(){return[c.value,u.value,f.value,d.value,p.value]}const k=fe({get:()=>c.value,set:I=>{c.value=I,P.locale=c.value}}),Q=fe({get:()=>u.value,set:I=>{u.value=I,P.fallbackLocale=u.value,Fr(P,c.value,I)}}),me=fe(()=>f.value),ye=fe(()=>d.value),se=fe(()=>p.value);function ne(){return Pe(y)?y:null}function de(I){y=I,P.postTranslation=I}function tt(){return T}function ft(I){I!==null&&(v=_c(I)),T=I,P.missing=v}const Re=(I,M,te,ce,Ee,ot)=>{X();let Ue;try{__INTLIFY_PROD_DEVTOOLS__,n||(P.fallbackContext=o?By():void 0),Ue=I(P)}finally{__INTLIFY_PROD_DEVTOOLS__,n||(P.fallbackContext=void 0)}if(te!=="translate exists"&&Me(Ue)&&Ue===Ii||te==="translate exists"&&!Ue){const[Mo,wi]=M();return o&&S?ce(o):Ee(Mo)}else{if(ot(Ue))return Ue;throw ze($e.UNEXPECTED_RETURN_TYPE)}};function Fe(...I){return Re(M=>Reflect.apply(fc,null,[M,...I]),()=>Dl(...I),"translate",M=>Reflect.apply(M.t,M,[...I]),M=>M,M=>K(M))}function Tt(...I){const[M,te,ce]=I;if(ce&&!Ce(ce))throw ze($e.INVALID_ARGUMENT);return Fe(M,te,Ge({resolvedMessage:!0},ce||{}))}function ht(...I){return Re(M=>Reflect.apply(dc,null,[M,...I]),()=>Rl(...I),"datetime format",M=>Reflect.apply(M.d,M,[...I]),()=>nc,M=>K(M))}function gt(...I){return Re(M=>Reflect.apply(mc,null,[M,...I]),()=>Fl(...I),"number format",M=>Reflect.apply(M.n,M,[...I]),()=>nc,M=>K(M))}function We(I){return I.map(M=>K(M)||Me(M)||pe(M)?gc(String(M)):M)}const Y={normalize:We,interpolate:I=>I,type:"vnode"};function G(...I){return Re(M=>{let te;const ce=M;try{ce.processor=Y,te=Reflect.apply(fc,null,[ce,...I])}finally{ce.processor=null}return te},()=>Dl(...I),"translate",M=>M[Ol](...I),M=>[gc(M)],M=>Ae(M))}function ee(...I){return Re(M=>Reflect.apply(mc,null,[M,...I]),()=>Fl(...I),"number format",M=>M[Ml](...I),bc,M=>K(M)||Ae(M))}function ue(...I){return Re(M=>Reflect.apply(dc,null,[M,...I]),()=>Rl(...I),"datetime format",M=>M[Nl](...I),bc,M=>K(M)||Ae(M))}function b(I){F=I,P.pluralRules=F}function _(I,M){return Re(()=>{if(!I)return!1;const te=K(M)?M:c.value,ce=$(te),Ee=P.messageResolver(ce,I);return a?Ee!=null:Kt(Ee)||At(Ee)||K(Ee)},()=>[I],"translate exists",te=>Reflect.apply(te.te,te,[I,M]),eE,te=>pe(te))}function x(I){let M=null;const te=xd(P,u.value,c.value);for(let ce=0;ce{s&&(c.value=I,P.locale=I,Fr(P,c.value,u.value))}),St(o.fallbackLocale,I=>{s&&(u.value=I,P.fallbackLocale=I,Fr(P,c.value,u.value))}));const B={id:xc,locale:k,fallbackLocale:Q,get inheritLocale(){return s},set inheritLocale(I){s=I,I&&o&&(c.value=o.locale.value,u.value=o.fallbackLocale.value,Fr(P,c.value,u.value))},get availableLocales(){return Object.keys(f.value).sort()},messages:me,get modifiers(){return D},get pluralRules(){return F||{}},get isGlobal(){return n},get missingWarn(){return g},set missingWarn(I){g=I,P.missingWarn=g},get fallbackWarn(){return C},set fallbackWarn(I){C=I,P.fallbackWarn=C},get fallbackRoot(){return S},set fallbackRoot(I){S=I},get fallbackFormat(){return E},set fallbackFormat(I){E=I,P.fallbackFormat=E},get warnHtmlMessage(){return L},set warnHtmlMessage(I){L=I,P.warnHtmlMessage=I},get escapeParameter(){return w},set escapeParameter(I){w=I,P.escapeParameter=I},t:Fe,getLocaleMessage:$,setLocaleMessage:N,mergeLocaleMessage:V,getPostTranslationHandler:ne,setPostTranslationHandler:de,getMissingHandler:tt,setMissingHandler:ft,[Rd]:b};return B.datetimeFormats=ye,B.numberFormats=se,B.rt=Tt,B.te=_,B.tm=R,B.d=ht,B.n=gt,B.getDateTimeFormat=z,B.setDateTimeFormat=m,B.mergeDateTimeFormat=h,B.getNumberFormat=A,B.setNumberFormat=O,B.mergeNumberFormat=j,B[Fd]=r,B[Ol]=G,B[Nl]=ue,B[Ml]=ee,B}function oE(e){const t=K(e.locale)?e.locale:_r,o=K(e.fallbackLocale)||Ae(e.fallbackLocale)||ae(e.fallbackLocale)||e.fallbackLocale===!1?e.fallbackLocale:t,r=Pe(e.missing)?e.missing:void 0,n=pe(e.silentTranslationWarn)||wo(e.silentTranslationWarn)?!e.silentTranslationWarn:!0,i=pe(e.silentFallbackWarn)||wo(e.silentFallbackWarn)?!e.silentFallbackWarn:!0,l=pe(e.fallbackRoot)?e.fallbackRoot:!0,a=!!e.formatFallbackMessages,s=ae(e.modifiers)?e.modifiers:{},c=e.pluralizationRules,u=Pe(e.postTranslation)?e.postTranslation:void 0,f=K(e.warnHtmlInMessage)?e.warnHtmlInMessage!=="off":!0,d=!!e.escapeParameterHtml,p=pe(e.sync)?e.sync:!0;let g=e.messages;if(ae(e.sharedMessages)){const w=e.sharedMessages;g=Object.keys(w).reduce((F,P)=>{const U=F[P]||(F[P]={});return Ge(U,w[P]),F},g||{})}const{__i18n:C,__root:S,__injectWithOption:E}=e,T=e.datetimeFormats,v=e.numberFormats,y=e.flatJson,L=e.translateExistCompatible;return{locale:t,fallbackLocale:o,messages:g,flatJson:y,datetimeFormats:T,numberFormats:v,missing:r,missingWarn:n,fallbackWarn:i,fallbackRoot:l,fallbackFormat:a,modifiers:s,pluralRules:c,postTranslation:u,warnHtmlMessage:f,escapeParameter:d,messageResolver:e.messageResolver,inheritLocale:p,translateExistCompatible:L,__i18n:C,__root:S,__injectWithOption:E}}function Hl(e={},t){{const o=_a(oE(e)),{__extender:r}=e,n={id:o.id,get locale(){return o.locale.value},set locale(i){o.locale.value=i},get fallbackLocale(){return o.fallbackLocale.value},set fallbackLocale(i){o.fallbackLocale.value=i},get messages(){return o.messages.value},get datetimeFormats(){return o.datetimeFormats.value},get numberFormats(){return o.numberFormats.value},get availableLocales(){return o.availableLocales},get formatter(){return{interpolate(){return[]}}},set formatter(i){},get missing(){return o.getMissingHandler()},set missing(i){o.setMissingHandler(i)},get silentTranslationWarn(){return pe(o.missingWarn)?!o.missingWarn:o.missingWarn},set silentTranslationWarn(i){o.missingWarn=pe(i)?!i:i},get silentFallbackWarn(){return pe(o.fallbackWarn)?!o.fallbackWarn:o.fallbackWarn},set silentFallbackWarn(i){o.fallbackWarn=pe(i)?!i:i},get modifiers(){return o.modifiers},get formatFallbackMessages(){return o.fallbackFormat},set formatFallbackMessages(i){o.fallbackFormat=i},get postTranslation(){return o.getPostTranslationHandler()},set postTranslation(i){o.setPostTranslationHandler(i)},get sync(){return o.inheritLocale},set sync(i){o.inheritLocale=i},get warnHtmlInMessage(){return o.warnHtmlMessage?"warn":"off"},set warnHtmlInMessage(i){o.warnHtmlMessage=i!=="off"},get escapeParameterHtml(){return o.escapeParameter},set escapeParameterHtml(i){o.escapeParameter=i},get preserveDirectiveContent(){return!0},set preserveDirectiveContent(i){},get pluralizationRules(){return o.pluralRules||{}},__composer:o,t(...i){const[l,a,s]=i,c={};let u=null,f=null;if(!K(l))throw ze($e.INVALID_ARGUMENT);const d=l;return K(a)?c.locale=a:Ae(a)?u=a:ae(a)&&(f=a),Ae(s)?u=s:ae(s)&&(f=s),Reflect.apply(o.t,o,[d,u||f||{},c])},rt(...i){return Reflect.apply(o.rt,o,[...i])},tc(...i){const[l,a,s]=i,c={plural:1};let u=null,f=null;if(!K(l))throw ze($e.INVALID_ARGUMENT);const d=l;return K(a)?c.locale=a:Me(a)?c.plural=a:Ae(a)?u=a:ae(a)&&(f=a),K(s)?c.locale=s:Ae(s)?u=s:ae(s)&&(f=s),Reflect.apply(o.t,o,[d,u||f||{},c])},te(i,l){return o.te(i,l)},tm(i){return o.tm(i)},getLocaleMessage(i){return o.getLocaleMessage(i)},setLocaleMessage(i,l){o.setLocaleMessage(i,l)},mergeLocaleMessage(i,l){o.mergeLocaleMessage(i,l)},d(...i){return Reflect.apply(o.d,o,[...i])},getDateTimeFormat(i){return o.getDateTimeFormat(i)},setDateTimeFormat(i,l){o.setDateTimeFormat(i,l)},mergeDateTimeFormat(i,l){o.mergeDateTimeFormat(i,l)},n(...i){return Reflect.apply(o.n,o,[...i])},getNumberFormat(i){return o.getNumberFormat(i)},setNumberFormat(i,l){o.setNumberFormat(i,l)},mergeNumberFormat(i,l){o.mergeNumberFormat(i,l)},getChoiceIndex(i,l){return-1}};return n.__extender=r,n}}const va={tag:{type:[String,Object]},locale:{type:String},scope:{type:String,validator:e=>e==="parent"||e==="global",default:"parent"},i18n:{type:Object}};function rE({slots:e},t){return t.length===1&&t[0]==="default"?(e.default?e.default():[]).reduce((r,n)=>[...r,...n.type===qe?n.children:[n]],[]):t.reduce((o,r)=>{const n=e[r];return n&&(o[r]=n()),o},ve())}function Md(e){return qe}const nE=po({name:"i18n-t",props:Ge({keypath:{type:String,required:!0},plural:{type:[Number,String],validator:e=>Me(e)||!isNaN(e)}},va),setup(e,t){const{slots:o,attrs:r}=t,n=e.i18n||Sa({useScope:e.scope,__useComponent:!0});return()=>{const i=Object.keys(o).filter(f=>f!=="_"),l=ve();e.locale&&(l.locale=e.locale),e.plural!==void 0&&(l.plural=K(e.plural)?+e.plural:e.plural);const a=rE(t,i),s=n[Ol](e.keypath,a,l),c=Ge(ve(),r),u=K(e.tag)||Ce(e.tag)?e.tag:Md();return vr(u,c,s)}}}),vc=nE;function iE(e){return Ae(e)&&!K(e[0])}function kd(e,t,o,r){const{slots:n,attrs:i}=t;return()=>{const l={part:!0};let a=ve();e.locale&&(l.locale=e.locale),K(e.format)?l.key=e.format:Ce(e.format)&&(K(e.format.key)&&(l.key=e.format.key),a=Object.keys(e.format).reduce((d,p)=>o.includes(p)?Ge(ve(),d,{[p]:e.format[p]}):d,ve()));const s=r(e.value,l,a);let c=[l.key];Ae(s)?c=s.map((d,p)=>{const g=n[d.type],C=g?g({[d.type]:d.value,index:p,parts:s}):[d.value];return iE(C)&&(C[0].key=`${d.type}-${p}`),C}):K(s)&&(c=[s]);const u=Ge(ve(),i),f=K(e.tag)||Ce(e.tag)?e.tag:Md();return vr(f,u,c)}}const lE=po({name:"i18n-n",props:Ge({value:{type:Number,required:!0},format:{type:[String,Object]}},va),setup(e,t){const o=e.i18n||Sa({useScope:e.scope,__useComponent:!0});return kd(e,t,wd,(...r)=>o[Ml](...r))}}),Sc=lE,aE=po({name:"i18n-d",props:Ge({value:{type:[Number,Date],required:!0},format:{type:[String,Object]}},va),setup(e,t){const o=e.i18n||Sa({useScope:e.scope,__useComponent:!0});return kd(e,t,Ld,(...r)=>o[Nl](...r))}}),yc=aE;function sE(e,t){const o=e;if(e.mode==="composition")return o.__getInstance(t)||e.global;{const r=o.__getInstance(t);return r!=null?r.__composer:e.global.__composer}}function cE(e){const t=l=>{const{instance:a,modifiers:s,value:c}=l;if(!a||!a.$)throw ze($e.UNEXPECTED_ERROR);const u=sE(e,a.$),f=Ec(c);return[Reflect.apply(u.t,u,[...Tc(f)]),u]};return{created:(l,a)=>{const[s,c]=t(a);Kn&&e.global===c&&(l.__i18nWatcher=St(c.locale,()=>{a.instance&&a.instance.$forceUpdate()})),l.__composer=c,l.textContent=s},unmounted:l=>{Kn&&l.__i18nWatcher&&(l.__i18nWatcher(),l.__i18nWatcher=void 0,delete l.__i18nWatcher),l.__composer&&(l.__composer=void 0,delete l.__composer)},beforeUpdate:(l,{value:a})=>{if(l.__composer){const s=l.__composer,c=Ec(a);l.textContent=Reflect.apply(s.t,s,[...Tc(c)])}},getSSRProps:l=>{const[a]=t(l);return{textContent:a}}}}function Ec(e){if(K(e))return{path:e};if(ae(e)){if(!("path"in e))throw ze($e.REQUIRED_VALUE,"path");return e}else throw ze($e.INVALID_VALUE)}function Tc(e){const{path:t,locale:o,args:r,choice:n,plural:i}=e,l={},a=r||{};return K(o)&&(l.locale=o),Me(n)&&(l.plural=n),Me(i)&&(l.plural=i),[t,a,l]}function uE(e,t,...o){const r=ae(o[0])?o[0]:{},n=!!r.useI18nComponentName;(!pe(r.globalInstall)||r.globalInstall)&&([n?"i18n":vc.name,"I18nT"].forEach(l=>e.component(l,vc)),[Sc.name,"I18nN"].forEach(l=>e.component(l,Sc)),[yc.name,"I18nD"].forEach(l=>e.component(l,yc))),e.directive("t",cE(t))}function fE(e,t,o){return{beforeCreate(){const r=Ao();if(!r)throw ze($e.UNEXPECTED_ERROR);const n=this.$options;if(n.i18n){const i=n.i18n;if(n.__i18n&&(i.__i18n=n.__i18n),i.__root=t,this===this.$root)this.$i18n=Pc(e,i);else{i.__injectWithOption=!0,i.__extender=o.__vueI18nExtend,this.$i18n=Hl(i);const l=this.$i18n;l.__extender&&(l.__disposer=l.__extender(this.$i18n))}}else if(n.__i18n)if(this===this.$root)this.$i18n=Pc(e,n);else{this.$i18n=Hl({__i18n:n.__i18n,__injectWithOption:!0,__extender:o.__vueI18nExtend,__root:t});const i=this.$i18n;i.__extender&&(i.__disposer=i.__extender(this.$i18n))}else this.$i18n=e;n.__i18nGlobal&&Nd(t,n,n),this.$t=(...i)=>this.$i18n.t(...i),this.$rt=(...i)=>this.$i18n.rt(...i),this.$tc=(...i)=>this.$i18n.tc(...i),this.$te=(i,l)=>this.$i18n.te(i,l),this.$d=(...i)=>this.$i18n.d(...i),this.$n=(...i)=>this.$i18n.n(...i),this.$tm=i=>this.$i18n.tm(i),o.__setInstance(r,this.$i18n)},mounted(){},unmounted(){const r=Ao();if(!r)throw ze($e.UNEXPECTED_ERROR);const n=this.$i18n;delete this.$t,delete this.$rt,delete this.$tc,delete this.$te,delete this.$d,delete this.$n,delete this.$tm,n.__disposer&&(n.__disposer(),delete n.__disposer,delete n.__extender),o.__deleteInstance(r),delete this.$i18n}}}function Pc(e,t){e.locale=t.locale||e.locale,e.fallbackLocale=t.fallbackLocale||e.fallbackLocale,e.missing=t.missing||e.missing,e.silentTranslationWarn=t.silentTranslationWarn||e.silentFallbackWarn,e.silentFallbackWarn=t.silentFallbackWarn||e.silentFallbackWarn,e.formatFallbackMessages=t.formatFallbackMessages||e.formatFallbackMessages,e.postTranslation=t.postTranslation||e.postTranslation,e.warnHtmlInMessage=t.warnHtmlInMessage||e.warnHtmlInMessage,e.escapeParameterHtml=t.escapeParameterHtml||e.escapeParameterHtml,e.sync=t.sync||e.sync,e.__composer[Rd](t.pluralizationRules||e.pluralizationRules);const o=Ai(e.locale,{messages:t.messages,__i18n:t.__i18n});return Object.keys(o).forEach(r=>e.mergeLocaleMessage(r,o[r])),t.datetimeFormats&&Object.keys(t.datetimeFormats).forEach(r=>e.mergeDateTimeFormat(r,t.datetimeFormats[r])),t.numberFormats&&Object.keys(t.numberFormats).forEach(r=>e.mergeNumberFormat(r,t.numberFormats[r])),e}const dE=Fo("global-vue-i18n");function pE(e={},t){const o=__VUE_I18N_LEGACY_API__&&pe(e.legacy)?e.legacy:__VUE_I18N_LEGACY_API__,r=pe(e.globalInjection)?e.globalInjection:!0,n=__VUE_I18N_LEGACY_API__&&o?!!e.allowComposition:!0,i=new Map,[l,a]=mE(e,o),s=Fo("");function c(d){return i.get(d)||null}function u(d,p){i.set(d,p)}function f(d){i.delete(d)}{const d={get mode(){return __VUE_I18N_LEGACY_API__&&o?"legacy":"composition"},get allowComposition(){return n},async install(p,...g){if(p.__VUE_I18N_SYMBOL__=s,p.provide(p.__VUE_I18N_SYMBOL__,d),ae(g[0])){const E=g[0];d.__composerExtend=E.__composerExtend,d.__vueI18nExtend=E.__vueI18nExtend}let C=null;!o&&r&&(C=yE(p,d.global)),__VUE_I18N_FULL_INSTALL__&&uE(p,d,...g),__VUE_I18N_LEGACY_API__&&o&&p.mixin(fE(a,a.__composer,d));const S=p.unmount;p.unmount=()=>{C&&C(),d.dispose(),S()}},get global(){return a},dispose(){l.stop()},__instances:i,__getInstance:c,__setInstance:u,__deleteInstance:f};return d}}function Sa(e={}){const t=Ao();if(t==null)throw ze($e.MUST_BE_CALL_SETUP_TOP);if(!t.isCE&&t.appContext.app!=null&&!t.appContext.app.__VUE_I18N_SYMBOL__)throw ze($e.NOT_INSTALLED);const o=hE(t),r=CE(o),n=Od(t),i=gE(e,n);if(__VUE_I18N_LEGACY_API__&&o.mode==="legacy"&&!e.__useComponent){if(!o.allowComposition)throw ze($e.NOT_AVAILABLE_IN_LEGACY_MODE);return vE(t,i,r,e)}if(i==="global")return Nd(r,e,n),r;if(i==="parent"){let s=bE(o,t,e.__useComponent);return s==null&&(s=r),s}const l=o;let a=l.__getInstance(t);if(a==null){const s=Ge({},e);"__i18n"in n&&(s.__i18n=n.__i18n),r&&(s.__root=r),a=_a(s),l.__composerExtend&&(a[kl]=l.__composerExtend(a)),_E(l,t,a),l.__setInstance(t,a)}return a}function mE(e,t,o){const r=Wl();{const n=__VUE_I18N_LEGACY_API__&&t?r.run(()=>Hl(e)):r.run(()=>_a(e));if(n==null)throw ze($e.UNEXPECTED_ERROR);return[r,n]}}function hE(e){{const t=Ze(e.isCE?dE:e.appContext.app.__VUE_I18N_SYMBOL__);if(!t)throw ze(e.isCE?$e.NOT_INSTALLED_WITH_PROVIDE:$e.UNEXPECTED_ERROR);return t}}function gE(e,t){return Ti(e)?"__i18n"in t?"local":"global":e.useScope?e.useScope:"local"}function CE(e){return e.mode==="composition"?e.global:e.global.__composer}function bE(e,t,o=!1){let r=null;const n=t.root;let i=xE(t,o);for(;i!=null;){const l=e;if(e.mode==="composition")r=l.__getInstance(i);else if(__VUE_I18N_LEGACY_API__){const a=l.__getInstance(i);a!=null&&(r=a.__composer,o&&r&&!r[Fd]&&(r=null))}if(r!=null||n===i)break;i=i.parent}return r}function xE(e,t=!1){return e==null?null:t&&e.vnode.ctx||e.parent}function _E(e,t,o){pi(()=>{},t),Ql(()=>{const r=o;e.__deleteInstance(t);const n=r[kl];n&&(n(),delete r[kl])},t)}function vE(e,t,o,r={}){const n=t==="local",i=Yl(null);if(n&&e.proxy&&!(e.proxy.$options.i18n||e.proxy.$options.__i18n))throw ze($e.MUST_DEFINE_I18N_OPTION_IN_ALLOW_COMPOSITION);const l=pe(r.inheritLocale)?r.inheritLocale:!K(r.locale),a=mt(!n||l?o.locale.value:K(r.locale)?r.locale:_r),s=mt(!n||l?o.fallbackLocale.value:K(r.fallbackLocale)||Ae(r.fallbackLocale)||ae(r.fallbackLocale)||r.fallbackLocale===!1?r.fallbackLocale:a.value),c=mt(Ai(a.value,r)),u=mt(ae(r.datetimeFormats)?r.datetimeFormats:{[a.value]:{}}),f=mt(ae(r.numberFormats)?r.numberFormats:{[a.value]:{}}),d=n?o.missingWarn:pe(r.missingWarn)||wo(r.missingWarn)?r.missingWarn:!0,p=n?o.fallbackWarn:pe(r.fallbackWarn)||wo(r.fallbackWarn)?r.fallbackWarn:!0,g=n?o.fallbackRoot:pe(r.fallbackRoot)?r.fallbackRoot:!0,C=!!r.fallbackFormat,S=Pe(r.missing)?r.missing:null,E=Pe(r.postTranslation)?r.postTranslation:null,T=n?o.warnHtmlMessage:pe(r.warnHtmlMessage)?r.warnHtmlMessage:!0,v=!!r.escapeParameter,y=n?o.modifiers:ae(r.modifiers)?r.modifiers:{},L=r.pluralRules||n&&o.pluralRules;function w(){return[a.value,s.value,c.value,u.value,f.value]}const D=fe({get:()=>i.value?i.value.locale.value:a.value,set:x=>{i.value&&(i.value.locale.value=x),a.value=x}}),F=fe({get:()=>i.value?i.value.fallbackLocale.value:s.value,set:x=>{i.value&&(i.value.fallbackLocale.value=x),s.value=x}}),P=fe(()=>i.value?i.value.messages.value:c.value),U=fe(()=>u.value),X=fe(()=>f.value);function k(){return i.value?i.value.getPostTranslationHandler():E}function Q(x){i.value&&i.value.setPostTranslationHandler(x)}function me(){return i.value?i.value.getMissingHandler():S}function ye(x){i.value&&i.value.setMissingHandler(x)}function se(x){return w(),x()}function ne(...x){return i.value?se(()=>Reflect.apply(i.value.t,null,[...x])):se(()=>"")}function de(...x){return i.value?Reflect.apply(i.value.rt,null,[...x]):""}function tt(...x){return i.value?se(()=>Reflect.apply(i.value.d,null,[...x])):se(()=>"")}function ft(...x){return i.value?se(()=>Reflect.apply(i.value.n,null,[...x])):se(()=>"")}function Re(x){return i.value?i.value.tm(x):{}}function Fe(x,R){return i.value?i.value.te(x,R):!1}function Tt(x){return i.value?i.value.getLocaleMessage(x):{}}function ht(x,R){i.value&&(i.value.setLocaleMessage(x,R),c.value[x]=R)}function gt(x,R){i.value&&i.value.mergeLocaleMessage(x,R)}function We(x){return i.value?i.value.getDateTimeFormat(x):{}}function H(x,R){i.value&&(i.value.setDateTimeFormat(x,R),u.value[x]=R)}function Y(x,R){i.value&&i.value.mergeDateTimeFormat(x,R)}function G(x){return i.value?i.value.getNumberFormat(x):{}}function ee(x,R){i.value&&(i.value.setNumberFormat(x,R),f.value[x]=R)}function ue(x,R){i.value&&i.value.mergeNumberFormat(x,R)}const b={get id(){return i.value?i.value.id:-1},locale:D,fallbackLocale:F,messages:P,datetimeFormats:U,numberFormats:X,get inheritLocale(){return i.value?i.value.inheritLocale:l},set inheritLocale(x){i.value&&(i.value.inheritLocale=x)},get availableLocales(){return i.value?i.value.availableLocales:Object.keys(c.value)},get modifiers(){return i.value?i.value.modifiers:y},get pluralRules(){return i.value?i.value.pluralRules:L},get isGlobal(){return i.value?i.value.isGlobal:!1},get missingWarn(){return i.value?i.value.missingWarn:d},set missingWarn(x){i.value&&(i.value.missingWarn=x)},get fallbackWarn(){return i.value?i.value.fallbackWarn:p},set fallbackWarn(x){i.value&&(i.value.missingWarn=x)},get fallbackRoot(){return i.value?i.value.fallbackRoot:g},set fallbackRoot(x){i.value&&(i.value.fallbackRoot=x)},get fallbackFormat(){return i.value?i.value.fallbackFormat:C},set fallbackFormat(x){i.value&&(i.value.fallbackFormat=x)},get warnHtmlMessage(){return i.value?i.value.warnHtmlMessage:T},set warnHtmlMessage(x){i.value&&(i.value.warnHtmlMessage=x)},get escapeParameter(){return i.value?i.value.escapeParameter:v},set escapeParameter(x){i.value&&(i.value.escapeParameter=x)},t:ne,getPostTranslationHandler:k,setPostTranslationHandler:Q,getMissingHandler:me,setMissingHandler:ye,rt:de,d:tt,n:ft,tm:Re,te:Fe,getLocaleMessage:Tt,setLocaleMessage:ht,mergeLocaleMessage:gt,getDateTimeFormat:We,setDateTimeFormat:H,mergeDateTimeFormat:Y,getNumberFormat:G,setNumberFormat:ee,mergeNumberFormat:ue};function _(x){x.locale.value=a.value,x.fallbackLocale.value=s.value,Object.keys(c.value).forEach(R=>{x.mergeLocaleMessage(R,c.value[R])}),Object.keys(u.value).forEach(R=>{x.mergeDateTimeFormat(R,u.value[R])}),Object.keys(f.value).forEach(R=>{x.mergeNumberFormat(R,f.value[R])}),x.escapeParameter=v,x.fallbackFormat=C,x.fallbackRoot=g,x.fallbackWarn=p,x.missingWarn=d,x.warnHtmlMessage=T}return Jl(()=>{if(e.proxy==null||e.proxy.$i18n==null)throw ze($e.NOT_AVAILABLE_COMPOSITION_IN_LEGACY);const x=i.value=e.proxy.$i18n.__composer;t==="global"?(a.value=x.locale.value,s.value=x.fallbackLocale.value,c.value=x.messages.value,u.value=x.datetimeFormats.value,f.value=x.numberFormats.value):n&&_(x)}),b}const SE=["locale","fallbackLocale","availableLocales"],Ic=["t","rt","d","n","tm","te"];function yE(e,t){const o=Object.create(null);return SE.forEach(n=>{const i=Object.getOwnPropertyDescriptor(t,n);if(!i)throw ze($e.UNEXPECTED_ERROR);const l=we(i.value)?{get(){return i.value.value},set(a){i.value.value=a}}:{get(){return i.get&&i.get()}};Object.defineProperty(o,n,l)}),e.config.globalProperties.$i18n=o,Ic.forEach(n=>{const i=Object.getOwnPropertyDescriptor(t,n);if(!i||!i.value)throw ze($e.UNEXPECTED_ERROR);Object.defineProperty(e.config.globalProperties,`$${n}`,i)}),()=>{delete e.config.globalProperties.$i18n,Ic.forEach(n=>{delete e.config.globalProperties[`$${n}`]})}}Qy();__INTLIFY_JIT_COMPILATION__?lc(Gy):lc(jy);My(gy);ky(xd);if(__INTLIFY_PROD_DEVTOOLS__){const e=io();e.__INTLIFY__=!0,Ty(e.__INTLIFY_DEVTOOLS_GLOBAL_HOOK__)}const EE={app:{tagline:"文件快传",description:"开箱即用的文件快传系统"},nav:{home:"分享",docs:"API 文档",openapi:"OpenAPI",admin:"管理后台",homeTitle:"{name} — 首页",mainNav:"主导航"},theme:{label:"主题",light:"浅色",dark:"深色",system:"跟随系统"},lang:{label:"语言"},footer:{linkNav:"页脚链接",docs:"API 文档",openapi:"OpenAPI",admin:"管理后台",copyright:"© {year} {name}"},notify:{title:"系统通知",close:"知道了"},common:{loading:"加载中…",cancel:"取消",save:"保存",search:"搜索",refresh:"刷新",copy:"复制",copied:"已复制",copyFailed:"复制失败",close:"关闭",actions:"操作",all:"全部",query:"查询",reset:"重置",previousPage:"上一页",nextPage:"下一页",pagerInfo:"共 {total} 条 · 第 {page}/{pages} 页",text:"文本",file:"文件",success:"成功",failed:"失败",denied:"拒绝",none:"-"},time:{forever:"永久有效",permanent:"永久",expired:"已过期",lessThanMinute:"不足 1 分钟",minutes:"{n} 分钟",hoursMinutes:"{h} 小时 {m} 分",daysHours:"{d} 天 {h} 小时"},expireStyle:{day:"天",hour:"小时",minute:"分钟",count:"次数",forever:"永久"},home:{heroTitle:"{name} · 文件快传",heroDesc:"无需注册,文本文件一键分享,取件码即可领取",pickupPlaceholder:"输入取件码直接领取",pickupButton:"取 件",pickupRequired:"请输入取件码",tabText:"分享文本",tabFile:"分享文件",textContent:"文本内容",textPlaceholder:"粘贴要分享的文本、代码片段…",textBytes:"{bytes} / 222 KB(超出请改用文件分享)",textTooLong:"内容过多(超过 222KB),建议采用文件形式分享",textRequired:"请输入要分享的文本内容",customCode:"自定义提取码(可选)",customCodeHint:"留空随机生成;4-8 位字母或数字",customCodeInvalid:"提取码须为 4-8 位字母或数字",customCodeTaken:"该提取码已被占用,请换一个",generateCode:"生成取件码",fileRequired:"请选择要分享的文件",fileTooLarge:"文件大小超过限制(最大 {size})",chunkedUploading:"分片上传中",uploading:"上传中",uploadingDots:"上传中…",uploadAndShare:"上传并生成取件码",uploadDisabled:"管理员已关闭访客上传功能,如需分享请联系管理员",shareAnother:"再分享一个",textShared:"文本分享成功",fileShared:"文件分享成功",uploadCancelled:"上传已取消",shareFailed:"分享失败,请稍后重试",uploadFailed:"上传失败,请重试",rateLimited:"操作过于频繁,请稍后再试",notInitialized:"系统尚未初始化,请管理员先完成初始化配置"},result:{badge:"分享成功",code:"取件码",link:"取件链接",copyLink:"复制链接",copyLinkCode:"复制链接和提取码",copyCode:"复制取件码",codeCopied:"取件码已复制",linkCopied:"取件链接已复制",linkCodeCopied:"链接和提取码已复制",clickCopyCode:"点击复制提取码",expires:"有效期:{value}",forever:"永久",hint:"把取件码或链接发给对方,对方在首页输入取件码即可领取。",copyFailed:"复制失败,请手动选择复制"},pickup:{emptyCode:"取件码为空",querying:"正在查询取件码 {code} …",failed:"取件失败",failedDefault:"取件失败,请稍后重试",notFound:"取件码不存在或分享已过期",confirmHint:"请确认取件码是否正确,或联系分享人重新发送",retryPlaceholder:"输入其他取件码",retryButton:"重新取件",remainingUnlimited:"不限次数",remainingCount:"剩余 {n} 次",expireAt:"过期时间:{time}",loadingText:"正在获取内容…",copyContent:"复制内容",downloadTxt:"下载为 .txt",downloaded:"下载完成",downloadFailed:"下载失败,请重试",copied:"内容已复制",sizeUsed:"大小 {size} · 已被领取 {n} 次",downloading:"下载中 {percent}%",downloadFile:"下载文件({size})"},expire:{value:"数值",label:"有效期",foreverOption:"永久有效",countOption:"按次数",countHint:"分享在被领取指定次数后失效",timeHint:"有效期 {value} {unit}",foreverHint:"分享将一直有效,直到管理员删除",maxSecondsHint:"最长 {value}",maxCountHint:"最多 {n} 次"},drop:{aria:"选择或拖拽文件",zone:"点击选择或拖拽文件到此处",maxSize:"单文件最大 {size}",noLimit:"上传后自动生成取件码",remove:"移除",tooLarge:"文件大小 {size} 超过限制 {limit}",typeHint:"仅支持 {types}"},docs:{searchPlaceholder:"检索文档内容…",notGenerated:"文档尚未生成",buildHint:"构建时将从 docs/api/*.md 自动收录",noMatch:"没有匹配的章节",tocTitle:"本页目录",loading:"加载文档…",preparing:"API 文档筹备中",preparingHint:"文档源位于项目 docs/api/ 目录(每个 .md 一级标题作为章节名)。重新构建前端后,文档将内嵌到页面中离线可用。",emptyContent:"文档内容为空",loadFailed:"文档「{title}」加载失败",sidebar:"文档章节"},openapi:{title:"OpenAPI 3.0 接口规范",statusOk:"加载成功",statusError:"规范加载失败",statusLoading:"加载中…",source:"来源:{source}",sourceEmbedded:"构建内嵌 docs/openapi.yaml",notAvailable:"openapi.yaml 尚未生成或无法访问",notAvailableHint:"规范文件位于项目 docs/openapi.yaml。重新构建前端会将其内嵌;也可将文件部署到 {url} 供运行时加载。"},notFound:{title:"页面不存在",desc:"你访问的地址可能已变更",back:"回到首页"},admin:{login:{title:"管理员登录",subtitle:"{name} · 管理后台",password:"管理员密码",passwordPlaceholder:"请输入管理员密码",submit:"登 录",wrongPassword:"密码错误",failed:"登录失败,请稍后重试",required:"请输入管理员密码",hint:"密码由部署方在环境变量或系统设置中配置;连续输错会触发 IP 限流保护。"},nav:{title:"管理后台",files:"文件管理",audit:"审计日志",settings:"系统设置",logout:"退出登录",menu:"后台菜单",loggedOut:"已退出登录"},files:{title:"文件管理",totalRecords:"共 {total} 个分享记录",searchPlaceholder:"搜索取件码 / 文件名",batchDelete:"批量删除",batchDeleteWithCount:"批量删除({count})",deleteSelectedTitle:"删除选中的 {count} 项",selectFirst:"先勾选要删除的行",loading:"加载中…",empty:"暂无分享记录",loadFailed:"文件列表加载失败",colCode:"取件码",colName:"名称",colType:"类型",colSize:"大小",colUsed:"已领取",colRemaining:"剩余",colExpireAt:"过期时间",colStatus:"状态",colCreatedAt:"创建时间",remainingUnlimited:"不限",remainingCount:"{n} 次",statusValid:"有效",statusExpired:"已过期",copyCode:"复制码",copyLink:"复制链接",edit:"编辑",fetchText:"取内容",delete:"删除",confirmDelete:"确认删除分享「{name}」?该操作不可恢复。",confirmBatchDelete:"确认删除选中的 {count} 个分享?该操作不可恢复。",deleteSuccess:"删除成功",batchDeleteSuccess:"批量删除成功",deleteFailed:"删除失败",batchDeleteFailed:"批量删除失败",nothingChanged:"没有修改任何字段",updateSuccess:"更新成功",updateFailed:"更新失败",fetchTextFailed:"内容获取失败(分享可能已过期)",linkCopied:"取件链接已复制",codeCopied:"取件码已复制",editModalTitle:"编辑分享",expireAtHint:"过期时间(留空表示永久)",expireCountHint:"剩余可领取次数(-1 表示不限)"},audit:{title:"审计日志",subtitle:"记录上传 / 下载动作:时间、IP、UA、设备、结果、字节数与耗时",action:"动作",result:"结果",actionUpload:"上传",actionDownload:"下载",filterIp:"IP",filterStart:"开始时间",filterEnd:"结束时间",empty:"暂无审计记录(审计仅记录上传 / 下载动作)",loadFailed:"审计日志加载失败",colTime:"时间",colAction:"动作",colResult:"结果",colFile:"文件",colCode:"取件码",colBytes:"字节数",colIp:"IP",colDevice:"设备",colDuration:"耗时",colUaError:"UA / 错误"},settings:{title:"系统设置",subtitle:"站点名称与 Logo(自定义优先,留空恢复内置默认)",restoreDefaults:"恢复默认值",restoreDefaultsDone:"已填回默认值,点击保存生效",loading:"加载配置中…",loadFailed:"配置读取失败",sectionBasic:"基本",siteName:"站点名称 site_name",siteNameHint:"显示在导航栏、登录页与浏览器标题",siteDomain:"网站对外域名",siteDomainHint:"http(s)://域名[:端口],不带路径;留空则分享链接用当前访问地址",sectionLogo:"导航 Logo",logoUrl:"Logo 图片地址 logo_url",uploadImage:"上传图片",logoHint:"支持填写 URL 或上传本地图片(≤256KB,转存为内嵌数据);留空使用内置默认",imageTooLarge:"图片超过 256KB,请压缩后重试或直接填写图片 URL",imageLoaded:"图片已载入,点击保存后全站生效",imageReadFailed:"图片读取失败",navPreview:"导航栏实际效果:",sectionFavicon:"浏览器图标 Favicon",faviconUrl:"Favicon 地址 favicon_url",faviconHint:"建议使用 PNG/ICO 方形图标;留空使用内置默认",faviconPreviewHint:"浏览器标签页图标(保存后刷新页面生效)",saveAll:"保存设置(全站生效)",saved:"设置已保存,全站生效",saveFailed:"保存失败",sectionPassword:"修改管理员密码",passwordHint:"保存后所有已登录会话失效,需重新登录",oldPassword:"旧密码",newPassword:"新密码(至少 6 位)",confirmPassword:"确认新密码",pwdRequired:"请填写旧密码与新密码",pwdTooShort:"新密码至少 6 位",pwdMismatch:"两次输入的新密码不一致",pwdChanged:"密码已修改,请使用新密码重新登录",pwdChangeFailed:"修改失败",pwdWrong:"旧密码错误",sectionBackground:"背景图",backgroundUrl:"背景图地址 background_url",backgroundHint:"支持 http(s) 图片地址、data:image 图片或站内相对路径(≤2048 字符);留空使用主题默认",sectionFooter:"页脚",footerText:"页脚文案 footer_text",footerTextHint:"展示在页面底部,支持纯文本(≤2000 字符);留空显示默认标语",footerBeian:"备案号 footer_beian",footerBeianHint:"如 京ICP备2024xxxxxx号-1(≤128 字符)",sectionNotify:"系统通知",notifyEnabled:"启用右上角通知 notify_enabled",notifyTitle:"通知标题 notify_title",notifyTitleHint:"留空显示默认标题「系统通知」(≤128 字符)",notifyContent:"通知内容 notify_content",notifyContentHint:"支持 等受控 HTML(≤2000 字符)",sectionSavePolicy:"保存策略",maxSaveSeconds:"最长保存秒数 max_save_seconds",maxSaveSecondsHint:"0 = 不限制(服务端默认 7 天兜底),最大 {max} 秒(365 天)",maxSaveCount:"最大可取次数 max_save_count",maxSaveCountHint:"0 = 不限制,最大 {max} 次",sectionStorage:"存储策略",maxFileSize:"单文件上限 max_file_size(字节)",maxFileSizeHint:"0 = 回落 uploadSize(当前 {fallback}),最大 {max} 字节(10 GiB)",allowedFileTypes:"允许类型 allowed_file_types",allowedFileTypesHint:"逗号分隔:扩展名(jpg)或 MIME(image/*),* 不限制",sectionUploadRate:"上传频率限制",uploadCount:"窗口内允许上传次数 uploadCount",uploadCountHint:"最小 1,最大 {max}",uploadMinute:"频率窗口(分钟)uploadMinute",uploadMinuteHint:"最小 1,最大 {max}",unitHour:"小时",unitDay:"天",unitMB:"MB",unitGB:"GB",maxSaveTime:"最长保存时间 max_save_seconds",maxSaveTimeHint:"0 = 不限制(服务端默认 7 天兜底),最大 365 天",saveTimeUnlimited:"不限制(0)",maxFileSizeFriendly:"单文件上限 max_file_size",maxFileSizeHintV3:"0 = 回落 uploadSize(当前 {fallback}),最大 10 GB",sizeUnlimited:"不限制(0)",sectionEngine:"存储引擎",engineCurrent:"当前引擎",engineLocal:"本地存储",engineWebdav:"WebDAV",engineS3:"S3 对象存储",engineSwitch:"切换到该引擎",engineSwitching:"切换中…",engineSwitchOk:"存储引擎已切换为 {engine}",engineSwitchFail:"切换失败(已保持原引擎)",engineParamsTitle:"引擎参数",engineParamsSaved:"引擎参数已保存",localRoot:"存储根目录 local_storage_path",localRootHint:"留空 = 系统默认数据目录;修改后对新写入生效",webdavUrl:"服务地址 webdav_url",webdavUrlHint:"如 https://dav.example.com/dav/",webdavRoot:"远端根目录 webdav_root_path",webdavRootHint:"远端起始目录(不存在会自动逐级创建)",webdavUser:"用户名 webdav_username",webdavPass:"密码 webdav_password",secretKeepHint:"留空或 ****** = 不修改",s3Endpoint:"端点 s3_endpoint_url",s3EndpointHint:"如 https://s3.example.com:9000(AWS 官方可留空)",s3Bucket:"存储桶 s3_bucket_name",s3Region:"区域 s3_region_name",s3Ak:"AccessKeyID s3_access_key_id",s3Sk:"SecretAccessKey s3_secret_access_key",s3Token:"会话令牌 aws_session_token(可选)",s3Style:"寻址样式 s3_addressing_style",styleAuto:"auto(自动)",stylePath:"path(路径式,MinIO 常用)",styleVirtual:"virtual(虚拟主机式)",engineParamsSave:"保存引擎参数",approxSize:"≈ {size}"}}},TE={app:{tagline:"File Drop",description:"A ready-to-use file sharing service"},nav:{home:"Share",docs:"API Docs",openapi:"OpenAPI",admin:"Admin",homeTitle:"{name} — Home",mainNav:"Main navigation"},theme:{label:"Theme",light:"Light",dark:"Dark",system:"System"},lang:{label:"Language"},footer:{linkNav:"Footer links",docs:"API Docs",openapi:"OpenAPI",admin:"Admin",copyright:"© {year} {name}"},notify:{title:"System Notice",close:"Got it"},common:{loading:"Loading…",cancel:"Cancel",save:"Save",search:"Search",refresh:"Refresh",copy:"Copy",copied:"Copied",copyFailed:"Copy failed",close:"Close",actions:"Actions",all:"All",query:"Query",reset:"Reset",previousPage:"Previous",nextPage:"Next",pagerInfo:"{total} records · page {page}/{pages}",text:"Text",file:"File",success:"Success",failed:"Failed",denied:"Denied",none:"-"},time:{forever:"Never expires",permanent:"Permanent",expired:"Expired",lessThanMinute:"less than a minute",minutes:"{n} min",hoursMinutes:"{h} h {m} min",daysHours:"{d} d {h} h"},expireStyle:{day:"Days",hour:"Hours",minute:"Minutes",count:"Times",forever:"Forever"},home:{heroTitle:"{name} · File Drop",heroDesc:"No signup — share text or files and hand over a pickup code",pickupPlaceholder:"Enter a pickup code",pickupButton:"Pick up",pickupRequired:"Please enter a pickup code",tabText:"Share text",tabFile:"Share file",textContent:"Text content",textPlaceholder:"Paste the text or code snippet to share…",textBytes:"{bytes} / 222 KB (use file sharing for larger content)",textTooLong:"Content too long (over 222KB) — please share it as a file instead",textRequired:"Enter the text to share",customCode:"Custom pickup code (optional)",customCodeHint:"Leave empty for random; 4-8 letters/digits",customCodeInvalid:"Pickup code must be 4-8 letters or digits",customCodeTaken:"This pickup code is already taken",generateCode:"Generate code",fileRequired:"Please choose a file to share",fileTooLarge:"File exceeds the size limit (max {size})",chunkedUploading:"Chunked upload",uploading:"Uploading",uploadingDots:"Uploading…",uploadAndShare:"Upload & generate code",uploadDisabled:"Guest uploads are disabled. Please contact the administrator if you need to share.",shareAnother:"Share another one",textShared:"Text shared",fileShared:"File shared",uploadCancelled:"Upload cancelled",shareFailed:"Share failed, please try again later",uploadFailed:"Upload failed, please retry",rateLimited:"Too many requests, please slow down",notInitialized:"System is not initialized yet. An administrator must finish the setup first."},result:{badge:"Shared",code:"Pickup code",link:"Pickup link",copyLink:"Copy link",copyLinkCode:"Copy link & code",copyCode:"Copy code",codeCopied:"Pickup code copied",linkCopied:"Pickup link copied",linkCodeCopied:"Link and code copied",clickCopyCode:"Click to copy pickup code",expires:"Expires in: {value}",forever:"Forever",hint:"Send the code or link to the recipient; they can pick it up from the home page.",copyFailed:"Copy failed — please select the text manually"},pickup:{emptyCode:"Pickup code is empty",querying:"Looking up code {code} …",failed:"Pickup failed",failedDefault:"Pickup failed, please try again later",notFound:"Code not found or the share has expired",confirmHint:"Double-check the code, or ask the sender to share it again",retryPlaceholder:"Enter another pickup code",retryButton:"Try again",remainingUnlimited:"Unlimited",remainingCount:"{n} left",expireAt:"Expires: {time}",loadingText:"Fetching content…",copyContent:"Copy content",downloadTxt:"Download as .txt",downloaded:"Download complete",downloadFailed:"Download failed, please retry",copied:"Content copied",sizeUsed:"Size {size} · picked up {n} times",downloading:"Downloading {percent}%",downloadFile:"Download ({size})"},expire:{value:"Amount",label:"Expires in",foreverOption:"Never expires",countOption:"After N pickups",countHint:"The share becomes invalid after the given number of pickups",timeHint:"Valid for {value} {unit}",foreverHint:"The share stays valid until an administrator deletes it",maxSecondsHint:"At most {value}",maxCountHint:"At most {n} pickups"},drop:{aria:"Choose or drop a file",zone:"Click to choose or drop a file here",maxSize:"Max {size} per file",noLimit:"A pickup code is generated after upload",remove:"Remove",tooLarge:"File size {size} exceeds the limit {limit}",typeHint:"Allowed types: {types}"},docs:{searchPlaceholder:"Search documentation…",notGenerated:"Docs not generated yet",buildHint:"They are collected from docs/api/*.md at build time",noMatch:"No matching sections",tocTitle:"On this page",loading:"Loading document…",preparing:"API docs are on the way",preparingHint:"Sources live in the project docs/api/ directory (each .md is one section). Rebuild the frontend to embed them for offline use.",emptyContent:"Document is empty",loadFailed:'Failed to load document "{title}"',sidebar:"Documentation sections"},openapi:{title:"OpenAPI 3.0 Specification",statusOk:"Loaded",statusError:"Failed to load spec",statusLoading:"Loading…",source:"Source: {source}",sourceEmbedded:"Embedded docs/openapi.yaml at build time",notAvailable:"openapi.yaml is not generated or cannot be accessed",notAvailableHint:"The spec file lives in the project docs/openapi.yaml. Rebuilding the frontend embeds it; you can also deploy it to {url} for runtime loading."},notFound:{title:"Page not found",desc:"The address may have changed",back:"Back home"},admin:{login:{title:"Administrator Sign-in",subtitle:"{name} · Admin Console",password:"Admin password",passwordPlaceholder:"Enter the admin password",submit:"Sign in",wrongPassword:"Incorrect password",failed:"Sign-in failed, please try again later",required:"Please enter the admin password",hint:"The password is configured by the deployer via env vars or system settings; repeated failures trigger IP rate limiting."},nav:{title:"Admin",files:"Files",audit:"Audit Log",settings:"Settings",logout:"Sign out",menu:"Admin menu",loggedOut:"Signed out"},files:{title:"File Management",totalRecords:"{total} shares in total",searchPlaceholder:"Search code / file name",batchDelete:"Delete selected",batchDeleteWithCount:"Delete selected ({count})",deleteSelectedTitle:"Delete {count} selected items",selectFirst:"Select rows first",loading:"Loading…",empty:"No shares yet",loadFailed:"Failed to load the file list",colCode:"Code",colName:"Name",colType:"Type",colSize:"Size",colUsed:"Picked",colRemaining:"Remaining",colExpireAt:"Expires",colStatus:"Status",colCreatedAt:"Created",remainingUnlimited:"∞",remainingCount:"{n} left",statusValid:"Active",statusExpired:"Expired",copyCode:"Copy code",copyLink:"Copy link",edit:"Edit",fetchText:"Fetch text",delete:"Delete",confirmDelete:'Delete "{name}"? This cannot be undone.',confirmBatchDelete:"Delete {count} selected shares? This cannot be undone.",deleteSuccess:"Deleted",batchDeleteSuccess:"Batch deleted",deleteFailed:"Delete failed",batchDeleteFailed:"Batch delete failed",nothingChanged:"Nothing changed",updateSuccess:"Updated",updateFailed:"Update failed",fetchTextFailed:"Failed to fetch content (the share may have expired)",linkCopied:"Pickup link copied",codeCopied:"Pickup code copied",editModalTitle:"Edit share",expireAtHint:"Expires at (leave empty for never)",expireCountHint:"Remaining pickups (-1 for unlimited)"},audit:{title:"Audit Log",subtitle:"Upload / download events: time, IP, UA, device, result, bytes and duration",action:"Action",result:"Result",actionUpload:"Upload",actionDownload:"Download",filterIp:"IP",filterStart:"Start time",filterEnd:"End time",empty:"No audit records yet (only upload / download actions are recorded)",loadFailed:"Failed to load the audit log",colTime:"Time",colAction:"Action",colResult:"Result",colFile:"File",colCode:"Code",colBytes:"Bytes",colIp:"IP",colDevice:"Device",colDuration:"Duration",colUaError:"UA / Error"},settings:{title:"System Settings",subtitle:"Site name and branding (custom values win; leave empty to restore built-in defaults)",restoreDefaults:"Restore defaults",restoreDefaultsDone:"Defaults filled in — click save to apply",loading:"Loading settings…",loadFailed:"Failed to load settings",sectionBasic:"Basic",siteName:"Site name · site_name",siteNameHint:"Shown in the nav bar, login page and browser title",siteDomain:"Public site domain",siteDomainHint:"http(s)://host[:port], no path; leave empty to use the current address in share links",sectionLogo:"Nav logo",logoUrl:"Logo image URL · logo_url",uploadImage:"Upload image",logoHint:"Enter a URL or upload a local image (≤256KB, stored inline); leave empty for the built-in default",imageTooLarge:"Image exceeds 256KB — compress it or paste an image URL instead",imageLoaded:"Image loaded — click save to apply site-wide",imageReadFailed:"Failed to read the image",navPreview:"Nav bar preview:",sectionFavicon:"Browser favicon",faviconUrl:"Favicon URL · favicon_url",faviconHint:"Use a square PNG/ICO; leave empty for the built-in default",faviconPreviewHint:"Browser tab icon (applied after saving and refreshing)",saveAll:"Save settings (applies site-wide)",saved:"Settings saved site-wide",saveFailed:"Save failed",sectionPassword:"Change admin password",passwordHint:"After saving, all signed-in sessions are invalidated and you must sign in again",oldPassword:"Old password",newPassword:"New password (at least 6 characters)",confirmPassword:"Confirm new password",pwdRequired:"Please fill in the old and new passwords",pwdTooShort:"The new password must be at least 6 characters",pwdMismatch:"The two passwords do not match",pwdChanged:"Password changed — please sign in again with the new password",pwdChangeFailed:"Change failed",pwdWrong:"Old password is incorrect",sectionBackground:"Background image",backgroundUrl:"Background URL · background_url",backgroundHint:"http(s) image URL, data:image image or site-relative path (≤2048 chars); leave empty for the theme default",sectionFooter:"Footer",footerText:"Footer text · footer_text",footerTextHint:"Shown at the page bottom as plain text (≤2000 chars); leave empty for the default tagline",footerBeian:"ICP filing number · footer_beian",footerBeianHint:"e.g. 京ICP备2024xxxxxx号-1 (≤128 chars)",sectionNotify:"System notice",notifyEnabled:"Show floating notice · notify_enabled",notifyTitle:"Notice title · notify_title",notifyTitleHint:'Leave empty for the default title "System Notice" (≤128 chars)',notifyContent:"Notice content · notify_content",notifyContentHint:"Controlled HTML such as is allowed (≤2000 chars)",sectionSavePolicy:"Save policy",maxSaveSeconds:"Max save seconds · max_save_seconds",maxSaveSecondsHint:"0 = unlimited (server default 7-day fallback), max {max} seconds (365 days)",maxSaveCount:"Max pickup count · max_save_count",maxSaveCountHint:"0 = unlimited, max {max}",sectionStorage:"Storage policy",maxFileSize:"Max file size · max_file_size (bytes)",maxFileSizeHint:"0 = fall back to uploadSize (currently {fallback}), max {max} bytes (10 GiB)",allowedFileTypes:"Allowed types · allowed_file_types",allowedFileTypesHint:"Comma separated: extensions (jpg) or MIME (image/*); * means no limit",sectionUploadRate:"Upload rate limit",uploadCount:"Uploads per window · uploadCount",uploadCountHint:"Min 1, max {max}",uploadMinute:"Window length (minutes) · uploadMinute",uploadMinuteHint:"Min 1, max {max}",unitHour:"Hour(s)",unitDay:"Day(s)",unitMB:"MB",unitGB:"GB",maxSaveTime:"Max save time · max_save_seconds",maxSaveTimeHint:"0 = unlimited (server default 7-day fallback), max 365 days",saveTimeUnlimited:"Unlimited (0)",maxFileSizeFriendly:"Max file size · max_file_size",maxFileSizeHintV3:"0 = fall back to uploadSize (current {fallback}), max 10 GB",sizeUnlimited:"Unlimited (0)",sectionEngine:"Storage engine",engineCurrent:"Current engine",engineLocal:"Local storage",engineWebdav:"WebDAV",engineS3:"S3 object storage",engineSwitch:"Switch to this engine",engineSwitching:"Switching…",engineSwitchOk:"Storage engine switched to {engine}",engineSwitchFail:"Switch failed (previous engine kept)",engineParamsTitle:"Engine parameters",engineParamsSaved:"Engine parameters saved",localRoot:"Storage root · local_storage_path",localRootHint:"Empty = system default data directory; applies to new writes",webdavUrl:"Server URL · webdav_url",webdavUrlHint:"e.g. https://dav.example.com/dav/",webdavRoot:"Remote root · webdav_root_path",webdavRootHint:"Remote base directory (created recursively if missing)",webdavUser:"Username · webdav_username",webdavPass:"Password · webdav_password",secretKeepHint:"Empty or ****** = keep unchanged",s3Endpoint:"Endpoint · s3_endpoint_url",s3EndpointHint:"e.g. https://s3.example.com:9000 (leave empty for AWS)",s3Bucket:"Bucket · s3_bucket_name",s3Region:"Region · s3_region_name",s3Ak:"AccessKeyID · s3_access_key_id",s3Sk:"SecretAccessKey · s3_secret_access_key",s3Token:"Session token · aws_session_token (optional)",s3Style:"Addressing style · s3_addressing_style",styleAuto:"auto",stylePath:"path (typical for MinIO)",styleVirtual:"virtual-hosted",engineParamsSave:"Save engine parameters",approxSize:"≈ {size}"}}},Hd="fcb_locale";function PE(){try{const e=localStorage.getItem(Hd);return e==="zh-CN"||e==="en-US"?e:null}catch{return null}}function IE(){const e=PE();return e||(((typeof navigator<"u"?navigator.language:"en")??"en").toLowerCase().startsWith("zh")?"zh-CN":"en-US")}function AE(e){try{localStorage.setItem(Hd,e)}catch{}}const Ir=pE({legacy:!1,locale:IE(),fallbackLocale:"zh-CN",messages:{"zh-CN":EE,"en-US":TE},missingWarn:!1,fallbackWarn:!1});function Ac(){return Ir.global.locale.value??"zh-CN"}function LT(e){Ir.global.locale.value=e,document.documentElement.lang=e,AE(e)}Ir.global.t;function vo(e,t){return Ir.global.t(e,t??{})}function wT(e){if(e==null||Number.isNaN(e))return"-";if(e<1024)return`${e} B`;const t=["KB","MB","GB","TB"];let o=e,r=-1;do o/=1024,r++;while(o>=1024&&r=100?0:1)} ${t[r]}`}function DT(e){if(!e)return"-";const t=new Date(e);if(Number.isNaN(t.getTime()))return String(e);const o=r=>`${r}`.padStart(2,"0");return`${t.getFullYear()}-${o(t.getMonth()+1)}-${o(t.getDate())} ${o(t.getHours())}:${o(t.getMinutes())}:${o(t.getSeconds())}`}function RT(e){if(!e)return vo("time.forever");const t=new Date(e).getTime();if(Number.isNaN(t))return vo("time.forever");const o=t-Date.now();if(o<=0)return vo("time.expired");const r=Math.floor(o/6e4);if(r<1)return vo("time.lessThanMinute");if(r<60)return vo("time.minutes",{n:r});const n=Math.floor(r/60);if(n<24)return vo("time.hoursMinutes",{h:n,m:r%60});const i=Math.floor(n/24);return vo("time.daysHours",{d:i,h:n%24})}function FT(e){return e==null?"-":e<1e3?`${e} ms`:`${(e/1e3).toFixed(2)} s`}const LE=[{value:"day",label:"day"},{value:"hour",label:"hour"},{value:"minute",label:"minute"},{value:"count",label:"count"},{value:"forever",label:"forever"}];function OT(e){const t=LE.find(o=>o.value===e);return t?vo(`expireStyle.${t.value}`):e}function NT(e){if(!e)return null;const t=/filename\*=(?:UTF-8'')?([^;]+)/i.exec(e);if(t)try{return decodeURIComponent(t[1].replace(/["']/g,"").trim())}catch{}const o=/filename="?([^";]+)"?/i.exec(e);return o?o[1]:null}function MT(e,t){const o=URL.createObjectURL(e),r=document.createElement("a");r.href=o,r.download=t,document.body.appendChild(r),r.click(),r.remove(),setTimeout(()=>URL.revokeObjectURL(o),5e3)}async function kT(e){try{return await navigator.clipboard.writeText(e),!0}catch{try{const t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",document.body.appendChild(t),t.select();const o=document.execCommand("copy");return t.remove(),o}catch{return!1}}}async function HT(e){const t=await crypto.subtle.digest("SHA-256",e);return Array.from(new Uint8Array(t)).map(o=>o.toString(16).padStart(2,"0")).join("")}function De(e,t){for(const o of t)if(e&&typeof e=="object"&&o in e&&e[o]!==void 0&&e[o]!==null)return e[o]}const wE="/assets/logo-CBe6oOaL.svg",DE="/assets/favicon-Dl6ZLL7S.png",RE=wE,FE=DE,tl="文件快传";function ol(e,t=!0){return e==null?t:typeof e=="boolean"?e:typeof e=="number"?e!==0:String(e)!=="0"&&String(e)!=="false"&&String(e)!==""}function Lc(e){return Array.isArray(e)?e.map(t=>String(t).trim()).filter(Boolean):typeof e=="string"?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function ar(e,t){const o=Number(e);return Number.isFinite(o)?o:t}const OE=Xu("config",{state:()=>({loaded:!1,loading:!1,siteName:tl,siteDomain:"",description:"",explain:"",uploadSize:10*1024*1024,allowedFileTypes:[],expireStyle:["day","hour","minute","forever","count"],enableChunk:!1,openUpload:!0,notifyEnabled:!1,notifyTitle:"",notifyContent:"",logoUrl:"",faviconUrl:"",backgroundUrl:"",footerText:"",footerBeian:"",maxFileSize:0,maxSaveSeconds:0,maxSaveCount:0,uploadCount:0,uploadMinute:0}),getters:{displayLogoUrl:e=>e.logoUrl?.trim()?e.logoUrl:RE,displayFaviconUrl:e=>e.faviconUrl?.trim()?e.faviconUrl:FE,displayName:e=>e.siteName?.trim()?e.siteName:tl,shareLinkBase:e=>e.siteDomain?.trim()?e.siteDomain.trim().replace(/\/$/,""):location.origin,effectiveMaxFileSize(){return this.maxFileSize>0?this.maxFileSize:this.uploadSize}},actions:{async load(){this.loading=!0;try{const e=await gS(ed.publicConfig,{timeout:8e3}),t=De(e,["config"])??e;this.siteName=String(De(t,["name","site_name","siteName"])??tl),this.siteDomain=String(De(t,["site_domain","siteDomain"])??"").trim(),this.description=String(De(t,["description"])??""),this.explain=String(De(t,["explain","page_explain"])??""),this.uploadSize=ar(De(t,["uploadSize","upload_size"]),10*1024*1024),this.allowedFileTypes=Lc(De(t,["allowedFileTypes","allowed_file_types"]));const o=Lc(De(t,["expireStyle","expire_style"]));o.length&&(this.expireStyle=o),this.enableChunk=ol(De(t,["enableChunk","enable_chunk"]),!1),this.openUpload=ol(De(t,["openUpload","open_upload"]),!0),this.notifyTitle=String(De(t,["notify_title","notifyTitle"])??""),this.notifyContent=String(De(t,["notify_content","notifyContent"])??""),this.notifyEnabled=ol(De(t,["notify_enabled","notifyEnabled"]),!1),this.backgroundUrl=String(De(t,["background_url","backgroundUrl"])??"").trim(),this.footerText=String(De(t,["footer_text","footerText"])??""),this.footerBeian=String(De(t,["footer_beian","footerBeian"])??""),this.maxFileSize=ar(De(t,["max_file_size","maxFileSize","maxFileSize"]),0),this.maxSaveSeconds=ar(De(t,["max_save_seconds","maxSaveSeconds"]),0),this.maxSaveCount=ar(De(t,["max_save_count","maxSaveCount"]),0),this.uploadCount=ar(De(t,["uploadCount","upload_count"]),0),this.uploadMinute=ar(De(t,["uploadMinute","upload_minute"]),0),this.logoUrl=String(De(t,["logo_url","logoUrl"])??"").trim(),this.faviconUrl=String(De(t,["favicon_url","faviconUrl"])??"").trim(),this.loaded=!0,this.applyToDocument()}catch{}finally{this.loading=!1}},applyToDocument(){let e=document.querySelector('link[rel="icon"]');e||(e=document.createElement("link"),e.rel="icon",document.head.appendChild(e)),e.href=this.displayFaviconUrl}}}),$T=["light","dark","system"],$d="fcb_theme_mode";function NE(){try{const e=localStorage.getItem($d);return e==="light"||e==="dark"||e==="system"?e:null}catch{return null}}function ME(e){try{localStorage.setItem($d,e)}catch{}}function Bd(){return typeof matchMedia=="function"&&matchMedia("(prefers-color-scheme: dark)").matches}const Do=mt(NE()??"system"),qn=mt(Do.value==="system"?Bd()?"dark":"light":Do.value);let wc=!1;function kE(){if(wc||typeof matchMedia!="function")return;wc=!0;const e=matchMedia("(prefers-color-scheme: dark)");e.addEventListener?.("change",()=>{Do.value==="system"&&(qn.value=e.matches?"dark":"light")})}function HE(){kE(),qn.value=Do.value==="system"?Bd()?"dark":"light":Do.value,document.documentElement.dataset.theme=qn.value}St(Do,HE,{immediate:!0});function $E(e){Do.value=e,ME(e)}function BE(){return{mode:Do,resolved:qn,setMode:$E}}let WE=0;const zE=Xu("toast",{state:()=>({items:[]}),actions:{push(e,t="info",o=3200){const r=++WE;this.items.push({id:r,type:t,text:e}),this.items.length>4&&this.items.shift(),setTimeout(()=>this.dismiss(r),o)},success(e){this.push(e,"success")},error(e){this.push(e,"error",4200)},info(e){this.push(e,"info")},dismiss(e){this.items=this.items.filter(t=>t.id!==e)}}}),UE={class:"toast-host","aria-live":"polite"},VE=["onClick"],jE={class:"toast-icon","aria-hidden":"true"},GE=po({__name:"ToastHost",setup(e){const t=zE();return(o,r)=>(Ft(),hr("div",UE,[(Ft(!0),hr(qe,null,tm(bt(t).items,n=>(Ft(),hr("div",{key:n.id,class:ii(["toast",`toast-${n.type}`]),role:"status",onClick:i=>bt(t).dismiss(n.id)},[Rt("span",jE,Dn(n.type==="success"?"✅":n.type==="error"?"⚠️":"ℹ️"),1),Rt("span",null,Dn(n.text),1)],10,VE))),128))]))}}),KE=["aria-label"],YE={class:"notify-head"},qE={class:"notify-title-text"},XE=["title","aria-label"],JE=["innerHTML"],QE=po({__name:"NotifyPop",props:{title:{},content:{}},emits:["close"],setup(e,{emit:t}){const o=t;return(r,n)=>(Ft(),hr("aside",{class:"notify-pop",role:"dialog","aria-live":"polite","aria-label":e.title||r.$t("notify.title")},[Rt("div",YE,[n[1]||(n[1]=Rt("span",{"aria-hidden":"true"},"🔔",-1)),Rt("span",qE,Dn(e.title||r.$t("notify.title")),1),Rt("button",{class:"notify-close",type:"button",title:r.$t("notify.close"),"aria-label":r.$t("notify.close"),onClick:n[0]||(n[0]=i=>o("close"))}," ✕ ",8,XE)]),Rt("div",{class:"notify-content",innerHTML:e.content},null,8,JE)],8,KE))}}),Wd=(e,t)=>{const o=e.__vccOpts||e;for(const[r,n]of t)o[r]=n;return o},ZE=Wd(QE,[["__scopeId","data-v-6154d8f4"]]),eT={class:"app-root"},tT={key:1,class:"app-bg-tint","aria-hidden":"true"},Dc="fcb_notify_read",oT=po({__name:"App",setup(e){const t=OE(),o=kg(),{resolved:r}=BE();St(Ac,d=>{document.documentElement.lang=d},{immediate:!0}),St([()=>o.fullPath,Ac,()=>t.displayName],()=>{const d=o.meta.titleKey,p=typeof d=="string"?Ir.global.t(d):t.displayName;document.title=`${p} · ${t.displayName}`},{immediate:!0});const n=fe(()=>r.value==="dark"?uS:null),i=fe(()=>r.value==="dark"?{common:{primaryColor:"#7d95ff",primaryColorHover:"#98abff",primaryColorPressed:"#6c86f5",primaryColorSuppl:"#98abff"}}:{common:{primaryColor:"#4f6ef7",primaryColorHover:"#3d5bf0",primaryColorPressed:"#4359e0",primaryColorSuppl:"#3d5bf0"}}),l=fe(()=>!!t.backgroundUrl.trim()),a=mt(!1),s=mt(!1);function c(){return`${t.notifyEnabled}|${t.notifyTitle}|${t.notifyContent}`}function u(){try{s.value=localStorage.getItem(Dc)===c()}catch{s.value=!1}}function f(){a.value=!1,s.value=!0;try{localStorage.setItem(Dc,c())}catch{}}return St(()=>[t.loaded,c()],()=>{const d=s.value;u(),!(!d&&s.value)&&t.loaded&&t.notifyEnabled&&t.notifyContent.trim()&&!s.value&&(a.value=!0)}),pi(()=>{t.load(),u(),t.loaded&&t.notifyEnabled&&t.notifyContent.trim()&&!s.value&&(a.value=!0)}),(d,p)=>{const g=Qp("RouterView");return Ft(),Zr(bt(q_),{theme:n.value,"theme-overrides":i.value,"inline-theme-disabled":""},{default:du(()=>[Rt("div",eT,[p[0]||(p[0]=Rt("div",{class:"app-ambient","aria-hidden":"true"},null,-1)),l.value?(Ft(),hr("div",{key:0,class:"app-bg","aria-hidden":"true",style:ni({backgroundImage:`url(${bt(t).backgroundUrl})`})},null,4)):Ln("",!0),l.value?(Ft(),hr("div",tT)):Ln("",!0),a.value?(Ft(),Zr(ZE,{key:2,title:bt(t).notifyTitle,content:bt(t).notifyContent,onClose:f},null,8,["title","content"])):Ln("",!0),je(GE),je(g)])]),_:1},8,["theme","theme-overrides"])}}}),rT=Wd(oT,[["__scopeId","data-v-b2dd3b97"]]),nT="modulepreload",iT=function(e){return"/"+e},Rc={},Dt=function(t,o,r){let n=Promise.resolve();if(o&&o.length>0){let s=function(c){return Promise.all(c.map(u=>Promise.resolve(u).then(f=>({status:"fulfilled",value:f}),f=>({status:"rejected",reason:f}))))};document.getElementsByTagName("link");const l=document.querySelector("meta[property=csp-nonce]"),a=l?.nonce||l?.getAttribute("nonce");n=s(o.map(c=>{if(c=iT(c),c in Rc)return;Rc[c]=!0;const u=c.endsWith(".css"),f=u?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${c}"]${f}`))return;const d=document.createElement("link");if(d.rel=u?"stylesheet":nT,u||(d.as="script"),d.crossOrigin="",d.href=c,a&&d.setAttribute("nonce",a),document.head.appendChild(d),u)return new Promise((p,g)=>{d.addEventListener("load",p),d.addEventListener("error",()=>g(new Error(`Unable to preload CSS for ${c}`)))})}))}function i(l){const a=new Event("vite:preloadError",{cancelable:!0});if(a.payload=l,window.dispatchEvent(a),!a.defaultPrevented)throw l}return n.then(l=>{for(const a of l||[])a.status==="rejected"&&i(a.reason);return t().catch(i)})},lT=mg(),Xn=Mg({history:lT,routes:[{path:"/",name:"home",component:()=>Dt(()=>import("./HomeView-D38U_AAJ.js"),__vite__mapDeps([0,1,2,3,4,5])),meta:{titleKey:"nav.home"}},{path:"/s/:code",name:"pickup",component:()=>Dt(()=>import("./PickupView-ClAAhXsM.js"),__vite__mapDeps([6,1,2,3,4])),meta:{titleKey:"nav.home"}},{path:"/admin/login",name:"admin-login",component:()=>Dt(()=>import("./LoginView-BnVXNxFO.js"),__vite__mapDeps([7,1,2,3,8,9,10])),meta:{titleKey:"admin.nav.title"}},{path:"/admin",component:()=>Dt(()=>import("./AdminLayout-DJ2FR9f-.js"),__vite__mapDeps([11,2,8,9])),meta:{requiresAuth:!0,titleKey:"admin.nav.title"},children:[{path:"",redirect:{name:"admin-files"}},{path:"files",name:"admin-files",component:()=>Dt(()=>import("./FilesView-ZJ5eqPp5.js"),__vite__mapDeps([12,9,4,13])),meta:{titleKey:"admin.nav.files"}},{path:"audit",name:"admin-audit",component:()=>Dt(()=>import("./AuditView-BHN4e5xt.js"),__vite__mapDeps([14,9,13,15])),meta:{titleKey:"admin.nav.audit"}},{path:"settings",name:"admin-settings",component:()=>Dt(()=>import("./SettingsView-CjSvbH8z.js"),__vite__mapDeps([16,9,8,17])),meta:{titleKey:"admin.nav.settings"}}]},{path:"/docs",name:"docs",component:()=>Dt(()=>import("./DocsView-Ds78oqBY.js"),__vite__mapDeps([18,1,2,3,19,20])),meta:{titleKey:"nav.docs"}},{path:"/docs/:slug",name:"docs-detail",component:()=>Dt(()=>import("./DocsView-Ds78oqBY.js"),__vite__mapDeps([18,1,2,3,19,20])),meta:{titleKey:"nav.docs"}},{path:"/openapi",name:"openapi",component:()=>Dt(()=>import("./OpenApiView-4SbIV1Bx.js"),__vite__mapDeps([21,1,2,3,22,19,23])),meta:{titleKey:"nav.openapi"}},{path:"/:pathMatch(.*)*",name:"not-found",component:()=>Dt(()=>import("./NotFoundView-CbfqSXyJ.js"),__vite__mapDeps([24,1,2,3])),meta:{titleKey:"notFound.title"}}],scrollBehavior(e,t,o){return o||(e.hash?{el:e.hash,behavior:"smooth"}:{top:0})}});Xn.beforeEach(e=>{if(e.meta.requiresAuth&&!localStorage.getItem("fcb_admin_token"))return{name:"admin-login",query:{redirect:e.fullPath}}});hS(()=>{const e=Xn.currentRoute.value;e.name!=="admin-login"&&Xn.push({name:"admin-login",query:{redirect:e.fullPath}})});const Li=Sh(rT);Li.use(Th());Li.use(Ir);Li.use(Xn);Li.mount("#app");export{FT as $,du as A,CT as B,sT as C,pT as D,LE as E,qe as F,je as G,st as H,pi as I,St as J,DT as K,RT as L,MT as M,kg as N,Qp as O,Su as P,fT as Q,fn as R,De as S,uT as T,IT as U,Zf as V,td as W,NT as X,AT as Y,mT as Z,Wd as _,OE as a,Ze as a0,Yl as a1,Il as a2,Bx as a3,Jl as a4,Vs as a5,Nx as a6,en as a7,Je as a8,pn as a9,Ac as aA,LT as aB,BE as aC,PT as aD,Io as aa,ET as ab,cT as ac,Sl as ad,Ds as ae,ll as af,TT as ag,Wr as ah,dT as ai,ua as aj,xT as ak,_T as al,aT as am,n_ as an,C0 as ao,J as ap,vT as aq,ST as ar,c_ as as,yT as at,Dm as au,Xu as av,od as aw,mS as ax,Dt as ay,$T as az,Rt as b,hr as c,po as d,ii as e,bt as f,Ln as g,fe as h,zE as i,mt as j,kT as k,OT as l,gT as m,ni as n,Ft as o,dS as p,wm as q,tm as r,wT as s,Dn as t,Sa as u,gS as v,hT as w,ed as x,HT as y,Zr as z}; diff --git a/server/web/dist/assets/logo-CBe6oOaL.svg b/server/web/dist/assets/logo-CBe6oOaL.svg new file mode 100644 index 0000000..36448b4 --- /dev/null +++ b/server/web/dist/assets/logo-CBe6oOaL.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/server/web/dist/assets/markdown-B5D8JARp.js b/server/web/dist/assets/markdown-B5D8JARp.js new file mode 100644 index 0000000..1f19316 --- /dev/null +++ b/server/web/dist/assets/markdown-B5D8JARp.js @@ -0,0 +1,60 @@ +function Q(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var R=Q();function ie(r){R=r}var _={exec:()=>null};function p(r,e=""){let n=typeof r=="string"?r:r.source,s={replace:(t,i)=>{let l=typeof i=="string"?i:i.source;return l=l.replace(x.caret,"$1"),n=n.replace(t,l),s},getRegex:()=>new RegExp(n,e)};return s}var be=(()=>{try{return!!new RegExp("(?<=1)(?/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] /,listReplaceTask:/^\[[ xX]\] +/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:r=>new RegExp(`^( {0,3}${r})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:r=>new RegExp(`^ {0,${Math.min(3,r-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),hrRegex:r=>new RegExp(`^ {0,${Math.min(3,r-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:r=>new RegExp(`^ {0,${Math.min(3,r-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:r=>new RegExp(`^ {0,${Math.min(3,r-1)}}#`),htmlBeginRegex:r=>new RegExp(`^ {0,${Math.min(3,r-1)}}<(?:[a-z].*>|!--)`,"i")},we=/^(?:[ \t]*(?:\n|$))+/,me=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,ye=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,P=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,$e=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,H=/(?:[*+-]|\d{1,9}[.)])/,ae=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,oe=p(ae).replace(/bull/g,H).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),Re=p(ae).replace(/bull/g,H).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),N=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,Se=/^[^\n]+/,j=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,Te=p(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",j).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),ze=p(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,H).getRegex(),C="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",G=/|$))/,Ae=p("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",G).replace("tag",C).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),ce=p(N).replace("hr",P).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",C).getRegex(),_e=p(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",ce).getRegex(),F={blockquote:_e,code:me,def:Te,fences:ye,heading:$e,hr:P,html:Ae,lheading:oe,list:ze,newline:we,paragraph:ce,table:_,text:Se},ee=p("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",P).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",C).getRegex(),Pe={...F,lheading:Re,table:ee,paragraph:p(N).replace("hr",P).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",ee).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",C).getRegex()},Ie={...F,html:p(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",G).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:_,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:p(N).replace("hr",P).replace("heading",` *#{1,6} *[^ +]`).replace("lheading",oe).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},Le=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,ve=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,he=/^( {2,}|\\)\n(?!\s*$)/,Be=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\`+)[^`]+\k(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-",be?"(?`+)[^`]+\k(?!`)/).replace("html",/<(?! )[^<>]*?>/).getRegex(),ge=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,De=p(ge,"u").replace(/punct/g,E).getRegex(),Me=p(ge,"u").replace(/punct/g,ue).getRegex(),ke="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",Oe=p(ke,"gu").replace(/notPunctSpace/g,pe).replace(/punctSpace/g,W).replace(/punct/g,E).getRegex(),Qe=p(ke,"gu").replace(/notPunctSpace/g,qe).replace(/punctSpace/g,Ee).replace(/punct/g,ue).getRegex(),He=p("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,pe).replace(/punctSpace/g,W).replace(/punct/g,E).getRegex(),Ne=p(/\\(punct)/,"gu").replace(/punct/g,E).getRegex(),je=p(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),Ge=p(G).replace("(?:-->|$)","-->").getRegex(),Fe=p("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",Ge).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),L=/(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+[^`]*?`+(?!`)|[^\[\]\\`])*?/,We=p(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]*(?:\n[ \t]*)?)(title))?\s*\)/).replace("label",L).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),fe=p(/^!?\[(label)\]\[(ref)\]/).replace("label",L).replace("ref",j).getRegex(),de=p(/^!?\[(ref)\](?:\[\])?/).replace("ref",j).getRegex(),Xe=p("reflink|nolink(?!\\()","g").replace("reflink",fe).replace("nolink",de).getRegex(),te=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,X={_backpedal:_,anyPunctuation:Ne,autolink:je,blockSkip:Ze,br:he,code:ve,del:_,emStrongLDelim:De,emStrongRDelimAst:Oe,emStrongRDelimUnd:He,escape:Le,link:We,nolink:de,punctuation:Ce,reflink:fe,reflinkSearch:Xe,tag:Fe,text:Be,url:_},Ue={...X,link:p(/^!?\[(label)\]\((.*?)\)/).replace("label",L).getRegex(),reflink:p(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",L).getRegex()},D={...X,emStrongRDelimAst:Qe,emStrongLDelim:Me,url:p(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol",te).replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:p(/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},re=r=>Ke[r];function y(r,e){if(e){if(x.escapeTest.test(r))return r.replace(x.escapeReplace,re)}else if(x.escapeTestNoEncode.test(r))return r.replace(x.escapeReplaceNoEncode,re);return r}function ne(r){try{r=encodeURI(r).replace(x.percentDecode,"%")}catch{return null}return r}function se(r,e){let n=r.replace(x.findPipe,(i,l,o)=>{let a=!1,h=l;for(;--h>=0&&o[h]==="\\";)a=!a;return a?"|":" |"}),s=n.split(x.splitPipe),t=0;if(s[0].trim()||s.shift(),s.length>0&&!s.at(-1)?.trim()&&s.pop(),e)if(s.length>e)s.splice(e);else for(;s.length0?-2:-1}function le(r,e,n,s,t){let i=e.href,l=e.title||null,o=r[1].replace(t.other.outputLinkReplace,"$1");s.state.inLink=!0;let a={type:r[0].charAt(0)==="!"?"image":"link",raw:n,href:i,title:l,text:o,tokens:s.inlineTokens(o)};return s.state.inLink=!1,a}function Ye(r,e,n){let s=r.match(n.other.indentCodeCompensation);if(s===null)return e;let t=s[1];return e.split(` +`).map(i=>{let l=i.match(n.other.beginningSpace);if(l===null)return i;let[o]=l;return o.length>=t.length?i.slice(t.length):i}).join(` +`)}var v=class{options;rules;lexer;constructor(r){this.options=r||R}space(r){let e=this.rules.block.newline.exec(r);if(e&&e[0].length>0)return{type:"space",raw:e[0]}}code(r){let e=this.rules.block.code.exec(r);if(e){let n=e[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:e[0],codeBlockStyle:"indented",text:this.options.pedantic?n:z(n,` +`)}}}fences(r){let e=this.rules.block.fences.exec(r);if(e){let n=e[0],s=Ye(n,e[3]||"",this.rules);return{type:"code",raw:n,lang:e[2]?e[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):e[2],text:s}}}heading(r){let e=this.rules.block.heading.exec(r);if(e){let n=e[2].trim();if(this.rules.other.endingHash.test(n)){let s=z(n,"#");(this.options.pedantic||!s||this.rules.other.endingSpaceChar.test(s))&&(n=s.trim())}return{type:"heading",raw:e[0],depth:e[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(r){let e=this.rules.block.hr.exec(r);if(e)return{type:"hr",raw:z(e[0],` +`)}}blockquote(r){let e=this.rules.block.blockquote.exec(r);if(e){let n=z(e[0],` +`).split(` +`),s="",t="",i=[];for(;n.length>0;){let l=!1,o=[],a;for(a=0;a1,t={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");let i=this.rules.other.listItemRegex(n),l=!1;for(;r;){let a=!1,h="",c="";if(!(e=i.exec(r))||this.rules.block.hr.test(r))break;h=e[0],r=r.substring(h.length);let k=e[2].split(` +`,1)[0].replace(this.rules.other.listReplaceTabs,q=>" ".repeat(3*q.length)),u=r.split(` +`,1)[0],d=!k.trim(),f=0;if(this.options.pedantic?(f=2,c=k.trimStart()):d?f=e[1].length+1:(f=e[2].search(this.rules.other.nonSpaceChar),f=f>4?1:f,c=k.slice(f),f+=e[1].length),d&&this.rules.other.blankLine.test(u)&&(h+=u+` +`,r=r.substring(u.length+1),a=!0),!a){let q=this.rules.other.nextBulletRegex(f),K=this.rules.other.hrRegex(f),V=this.rules.other.fencesBeginRegex(f),Y=this.rules.other.headingBeginRegex(f),xe=this.rules.other.htmlBeginRegex(f);for(;r;){let Z=r.split(` +`,1)[0],S;if(u=Z,this.options.pedantic?(u=u.replace(this.rules.other.listReplaceNesting," "),S=u):S=u.replace(this.rules.other.tabCharGlobal," "),V.test(u)||Y.test(u)||xe.test(u)||q.test(u)||K.test(u))break;if(S.search(this.rules.other.nonSpaceChar)>=f||!u.trim())c+=` +`+S.slice(f);else{if(d||k.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||V.test(k)||Y.test(k)||K.test(k))break;c+=` +`+u}!d&&!u.trim()&&(d=!0),h+=Z+` +`,r=r.substring(Z.length+1),k=S.slice(f)}}t.loose||(l?t.loose=!0:this.rules.other.doubleBlankLine.test(h)&&(l=!0));let b=null,J;this.options.gfm&&(b=this.rules.other.listIsTask.exec(c),b&&(J=b[0]!=="[ ] ",c=c.replace(this.rules.other.listReplaceTask,""))),t.items.push({type:"list_item",raw:h,task:!!b,checked:J,loose:!1,text:c,tokens:[]}),t.raw+=h}let o=t.items.at(-1);if(o)o.raw=o.raw.trimEnd(),o.text=o.text.trimEnd();else return;t.raw=t.raw.trimEnd();for(let a=0;ak.type==="space"),c=h.length>0&&h.some(k=>this.rules.other.anyLine.test(k.raw));t.loose=c}if(t.loose)for(let a=0;a({text:o,tokens:this.lexer.inline(o),header:!1,align:i.align[a]})));return i}}lheading(r){let e=this.rules.block.lheading.exec(r);if(e)return{type:"heading",raw:e[0],depth:e[2].charAt(0)==="="?1:2,text:e[1],tokens:this.lexer.inline(e[1])}}paragraph(r){let e=this.rules.block.paragraph.exec(r);if(e){let n=e[1].charAt(e[1].length-1)===` +`?e[1].slice(0,-1):e[1];return{type:"paragraph",raw:e[0],text:n,tokens:this.lexer.inline(n)}}}text(r){let e=this.rules.block.text.exec(r);if(e)return{type:"text",raw:e[0],text:e[0],tokens:this.lexer.inline(e[0])}}escape(r){let e=this.rules.inline.escape.exec(r);if(e)return{type:"escape",raw:e[0],text:e[1]}}tag(r){let e=this.rules.inline.tag.exec(r);if(e)return!this.lexer.state.inLink&&this.rules.other.startATag.test(e[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(e[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(e[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(e[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:e[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:e[0]}}link(r){let e=this.rules.inline.link.exec(r);if(e){let n=e[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let i=z(n.slice(0,-1),"\\");if((n.length-i.length)%2===0)return}else{let i=Ve(e[2],"()");if(i===-2)return;if(i>-1){let l=(e[0].indexOf("!")===0?5:4)+e[1].length+i;e[2]=e[2].substring(0,i),e[0]=e[0].substring(0,l).trim(),e[3]=""}}let s=e[2],t="";if(this.options.pedantic){let i=this.rules.other.pedanticHrefTitle.exec(s);i&&(s=i[1],t=i[3])}else t=e[3]?e[3].slice(1,-1):"";return s=s.trim(),this.rules.other.startAngleBracket.test(s)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?s=s.slice(1):s=s.slice(1,-1)),le(e,{href:s&&s.replace(this.rules.inline.anyPunctuation,"$1"),title:t&&t.replace(this.rules.inline.anyPunctuation,"$1")},e[0],this.lexer,this.rules)}}reflink(r,e){let n;if((n=this.rules.inline.reflink.exec(r))||(n=this.rules.inline.nolink.exec(r))){let s=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," "),t=e[s.toLowerCase()];if(!t){let i=n[0].charAt(0);return{type:"text",raw:i,text:i}}return le(n,t,n[0],this.lexer,this.rules)}}emStrong(r,e,n=""){let s=this.rules.inline.emStrongLDelim.exec(r);if(!(!s||s[3]&&n.match(this.rules.other.unicodeAlphaNumeric))&&(!(s[1]||s[2])||!n||this.rules.inline.punctuation.exec(n))){let t=[...s[0]].length-1,i,l,o=t,a=0,h=s[0][0]==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(h.lastIndex=0,e=e.slice(-1*r.length+t);(s=h.exec(e))!=null;){if(i=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!i)continue;if(l=[...i].length,s[3]||s[4]){o+=l;continue}else if((s[5]||s[6])&&t%3&&!((t+l)%3)){a+=l;continue}if(o-=l,o>0)continue;l=Math.min(l,l+o+a);let c=[...s[0]][0].length,k=r.slice(0,t+s.index+c+l);if(Math.min(t,l)%2){let d=k.slice(1,-1);return{type:"em",raw:k,text:d,tokens:this.lexer.inlineTokens(d)}}let u=k.slice(2,-2);return{type:"strong",raw:k,text:u,tokens:this.lexer.inlineTokens(u)}}}}codespan(r){let e=this.rules.inline.code.exec(r);if(e){let n=e[2].replace(this.rules.other.newLineCharGlobal," "),s=this.rules.other.nonSpaceChar.test(n),t=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return s&&t&&(n=n.substring(1,n.length-1)),{type:"codespan",raw:e[0],text:n}}}br(r){let e=this.rules.inline.br.exec(r);if(e)return{type:"br",raw:e[0]}}del(r){let e=this.rules.inline.del.exec(r);if(e)return{type:"del",raw:e[0],text:e[2],tokens:this.lexer.inlineTokens(e[2])}}autolink(r){let e=this.rules.inline.autolink.exec(r);if(e){let n,s;return e[2]==="@"?(n=e[1],s="mailto:"+n):(n=e[1],s=n),{type:"link",raw:e[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}url(r){let e;if(e=this.rules.inline.url.exec(r)){let n,s;if(e[2]==="@")n=e[0],s="mailto:"+n;else{let t;do t=e[0],e[0]=this.rules.inline._backpedal.exec(e[0])?.[0]??"";while(t!==e[0]);n=e[0],e[1]==="www."?s="http://"+e[0]:s=e[0]}return{type:"link",raw:e[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}inlineText(r){let e=this.rules.inline.text.exec(r);if(e){let n=this.lexer.state.inRawBlock;return{type:"text",raw:e[0],text:e[0],escaped:n}}}},w=class M{tokens;options;state;tokenizer;inlineQueue;constructor(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||R,this.options.tokenizer=this.options.tokenizer||new v,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let n={other:x,block:I.normal,inline:T.normal};this.options.pedantic?(n.block=I.pedantic,n.inline=T.pedantic):this.options.gfm&&(n.block=I.gfm,this.options.breaks?n.inline=T.breaks:n.inline=T.gfm),this.tokenizer.rules=n}static get rules(){return{block:I,inline:T}}static lex(e,n){return new M(n).lex(e)}static lexInline(e,n){return new M(n).inlineTokens(e)}lex(e){e=e.replace(x.carriageReturn,` +`),this.blockTokens(e,this.tokens);for(let n=0;n(t=l.call({lexer:this},e,n))?(e=e.substring(t.raw.length),n.push(t),!0):!1))continue;if(t=this.tokenizer.space(e)){e=e.substring(t.raw.length);let l=n.at(-1);t.raw.length===1&&l!==void 0?l.raw+=` +`:n.push(t);continue}if(t=this.tokenizer.code(e)){e=e.substring(t.raw.length);let l=n.at(-1);l?.type==="paragraph"||l?.type==="text"?(l.raw+=(l.raw.endsWith(` +`)?"":` +`)+t.raw,l.text+=` +`+t.text,this.inlineQueue.at(-1).src=l.text):n.push(t);continue}if(t=this.tokenizer.fences(e)){e=e.substring(t.raw.length),n.push(t);continue}if(t=this.tokenizer.heading(e)){e=e.substring(t.raw.length),n.push(t);continue}if(t=this.tokenizer.hr(e)){e=e.substring(t.raw.length),n.push(t);continue}if(t=this.tokenizer.blockquote(e)){e=e.substring(t.raw.length),n.push(t);continue}if(t=this.tokenizer.list(e)){e=e.substring(t.raw.length),n.push(t);continue}if(t=this.tokenizer.html(e)){e=e.substring(t.raw.length),n.push(t);continue}if(t=this.tokenizer.def(e)){e=e.substring(t.raw.length);let l=n.at(-1);l?.type==="paragraph"||l?.type==="text"?(l.raw+=(l.raw.endsWith(` +`)?"":` +`)+t.raw,l.text+=` +`+t.raw,this.inlineQueue.at(-1).src=l.text):this.tokens.links[t.tag]||(this.tokens.links[t.tag]={href:t.href,title:t.title},n.push(t));continue}if(t=this.tokenizer.table(e)){e=e.substring(t.raw.length),n.push(t);continue}if(t=this.tokenizer.lheading(e)){e=e.substring(t.raw.length),n.push(t);continue}let i=e;if(this.options.extensions?.startBlock){let l=1/0,o=e.slice(1),a;this.options.extensions.startBlock.forEach(h=>{a=h.call({lexer:this},o),typeof a=="number"&&a>=0&&(l=Math.min(l,a))}),l<1/0&&l>=0&&(i=e.substring(0,l+1))}if(this.state.top&&(t=this.tokenizer.paragraph(i))){let l=n.at(-1);s&&l?.type==="paragraph"?(l.raw+=(l.raw.endsWith(` +`)?"":` +`)+t.raw,l.text+=` +`+t.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=l.text):n.push(t),s=i.length!==e.length,e=e.substring(t.raw.length);continue}if(t=this.tokenizer.text(e)){e=e.substring(t.raw.length);let l=n.at(-1);l?.type==="text"?(l.raw+=(l.raw.endsWith(` +`)?"":` +`)+t.raw,l.text+=` +`+t.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=l.text):n.push(t);continue}if(e){let l="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(l);break}else throw new Error(l)}}return this.state.top=!0,n}inline(e,n=[]){return this.inlineQueue.push({src:e,tokens:n}),n}inlineTokens(e,n=[]){let s=e,t=null;if(this.tokens.links){let a=Object.keys(this.tokens.links);if(a.length>0)for(;(t=this.tokenizer.rules.inline.reflinkSearch.exec(s))!=null;)a.includes(t[0].slice(t[0].lastIndexOf("[")+1,-1))&&(s=s.slice(0,t.index)+"["+"a".repeat(t[0].length-2)+"]"+s.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(t=this.tokenizer.rules.inline.anyPunctuation.exec(s))!=null;)s=s.slice(0,t.index)+"++"+s.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let i;for(;(t=this.tokenizer.rules.inline.blockSkip.exec(s))!=null;)i=t[2]?t[2].length:0,s=s.slice(0,t.index+i)+"["+"a".repeat(t[0].length-i-2)+"]"+s.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);s=this.options.hooks?.emStrongMask?.call({lexer:this},s)??s;let l=!1,o="";for(;e;){l||(o=""),l=!1;let a;if(this.options.extensions?.inline?.some(c=>(a=c.call({lexer:this},e,n))?(e=e.substring(a.raw.length),n.push(a),!0):!1))continue;if(a=this.tokenizer.escape(e)){e=e.substring(a.raw.length),n.push(a);continue}if(a=this.tokenizer.tag(e)){e=e.substring(a.raw.length),n.push(a);continue}if(a=this.tokenizer.link(e)){e=e.substring(a.raw.length),n.push(a);continue}if(a=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(a.raw.length);let c=n.at(-1);a.type==="text"&&c?.type==="text"?(c.raw+=a.raw,c.text+=a.text):n.push(a);continue}if(a=this.tokenizer.emStrong(e,s,o)){e=e.substring(a.raw.length),n.push(a);continue}if(a=this.tokenizer.codespan(e)){e=e.substring(a.raw.length),n.push(a);continue}if(a=this.tokenizer.br(e)){e=e.substring(a.raw.length),n.push(a);continue}if(a=this.tokenizer.del(e)){e=e.substring(a.raw.length),n.push(a);continue}if(a=this.tokenizer.autolink(e)){e=e.substring(a.raw.length),n.push(a);continue}if(!this.state.inLink&&(a=this.tokenizer.url(e))){e=e.substring(a.raw.length),n.push(a);continue}let h=e;if(this.options.extensions?.startInline){let c=1/0,k=e.slice(1),u;this.options.extensions.startInline.forEach(d=>{u=d.call({lexer:this},k),typeof u=="number"&&u>=0&&(c=Math.min(c,u))}),c<1/0&&c>=0&&(h=e.substring(0,c+1))}if(a=this.tokenizer.inlineText(h)){e=e.substring(a.raw.length),a.raw.slice(-1)!=="_"&&(o=a.raw.slice(-1)),l=!0;let c=n.at(-1);c?.type==="text"?(c.raw+=a.raw,c.text+=a.text):n.push(a);continue}if(e){let c="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(c);break}else throw new Error(c)}}return n}},B=class{options;parser;constructor(r){this.options=r||R}space(r){return""}code({text:r,lang:e,escaped:n}){let s=(e||"").match(x.notSpaceStart)?.[0],t=r.replace(x.endingNewline,"")+` +`;return s?'
    '+(n?t:y(t,!0))+`
    +`:"
    "+(n?t:y(t,!0))+`
    +`}blockquote({tokens:r}){return`
    +${this.parser.parse(r)}
    +`}html({text:r}){return r}def(r){return""}heading({tokens:r,depth:e}){return`${this.parser.parseInline(r)} +`}hr(r){return`
    +`}list(r){let e=r.ordered,n=r.start,s="";for(let l=0;l +`+s+" +`}listitem(r){let e="";if(r.task){let n=this.checkbox({checked:!!r.checked});r.loose?r.tokens[0]?.type==="paragraph"?(r.tokens[0].text=n+" "+r.tokens[0].text,r.tokens[0].tokens&&r.tokens[0].tokens.length>0&&r.tokens[0].tokens[0].type==="text"&&(r.tokens[0].tokens[0].text=n+" "+y(r.tokens[0].tokens[0].text),r.tokens[0].tokens[0].escaped=!0)):r.tokens.unshift({type:"text",raw:n+" ",text:n+" ",escaped:!0}):e+=n+" "}return e+=this.parser.parse(r.tokens,!!r.loose),`
  • ${e}
  • +`}checkbox({checked:r}){return"'}paragraph({tokens:r}){return`

    ${this.parser.parseInline(r)}

    +`}table(r){let e="",n="";for(let t=0;t${s}`),` + +`+e+` +`+s+`
    +`}tablerow({text:r}){return` +${r} +`}tablecell(r){let e=this.parser.parseInline(r.tokens),n=r.header?"th":"td";return(r.align?`<${n} align="${r.align}">`:`<${n}>`)+e+` +`}strong({tokens:r}){return`${this.parser.parseInline(r)}`}em({tokens:r}){return`${this.parser.parseInline(r)}`}codespan({text:r}){return`${y(r,!0)}`}br(r){return"
    "}del({tokens:r}){return`${this.parser.parseInline(r)}`}link({href:r,title:e,tokens:n}){let s=this.parser.parseInline(n),t=ne(r);if(t===null)return s;r=t;let i='
    ",i}image({href:r,title:e,text:n,tokens:s}){s&&(n=this.parser.parseInline(s,this.parser.textRenderer));let t=ne(r);if(t===null)return y(n);r=t;let i=`${n}{let l=t[i].flat(1/0);n=n.concat(this.walkTokens(l,e))}):t.tokens&&(n=n.concat(this.walkTokens(t.tokens,e)))}}return n}use(...r){let e=this.defaults.extensions||{renderers:{},childTokens:{}};return r.forEach(n=>{let s={...n};if(s.async=this.defaults.async||s.async||!1,n.extensions&&(n.extensions.forEach(t=>{if(!t.name)throw new Error("extension name required");if("renderer"in t){let i=e.renderers[t.name];i?e.renderers[t.name]=function(...l){let o=t.renderer.apply(this,l);return o===!1&&(o=i.apply(this,l)),o}:e.renderers[t.name]=t.renderer}if("tokenizer"in t){if(!t.level||t.level!=="block"&&t.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let i=e[t.level];i?i.unshift(t.tokenizer):e[t.level]=[t.tokenizer],t.start&&(t.level==="block"?e.startBlock?e.startBlock.push(t.start):e.startBlock=[t.start]:t.level==="inline"&&(e.startInline?e.startInline.push(t.start):e.startInline=[t.start]))}"childTokens"in t&&t.childTokens&&(e.childTokens[t.name]=t.childTokens)}),s.extensions=e),n.renderer){let t=this.defaults.renderer||new B(this.defaults);for(let i in n.renderer){if(!(i in t))throw new Error(`renderer '${i}' does not exist`);if(["options","parser"].includes(i))continue;let l=i,o=n.renderer[l],a=t[l];t[l]=(...h)=>{let c=o.apply(t,h);return c===!1&&(c=a.apply(t,h)),c||""}}s.renderer=t}if(n.tokenizer){let t=this.defaults.tokenizer||new v(this.defaults);for(let i in n.tokenizer){if(!(i in t))throw new Error(`tokenizer '${i}' does not exist`);if(["options","rules","lexer"].includes(i))continue;let l=i,o=n.tokenizer[l],a=t[l];t[l]=(...h)=>{let c=o.apply(t,h);return c===!1&&(c=a.apply(t,h)),c}}s.tokenizer=t}if(n.hooks){let t=this.defaults.hooks||new A;for(let i in n.hooks){if(!(i in t))throw new Error(`hook '${i}' does not exist`);if(["options","block"].includes(i))continue;let l=i,o=n.hooks[l],a=t[l];A.passThroughHooks.has(i)?t[l]=h=>{if(this.defaults.async&&A.passThroughHooksRespectAsync.has(i))return(async()=>{let k=await o.call(t,h);return a.call(t,k)})();let c=o.call(t,h);return a.call(t,c)}:t[l]=(...h)=>{if(this.defaults.async)return(async()=>{let k=await o.apply(t,h);return k===!1&&(k=await a.apply(t,h)),k})();let c=o.apply(t,h);return c===!1&&(c=a.apply(t,h)),c}}s.hooks=t}if(n.walkTokens){let t=this.defaults.walkTokens,i=n.walkTokens;s.walkTokens=function(l){let o=[];return o.push(i.call(this,l)),t&&(o=o.concat(t.call(this,l))),o}}this.defaults={...this.defaults,...s}}),this}setOptions(r){return this.defaults={...this.defaults,...r},this}lexer(r,e){return w.lex(r,e??this.defaults)}parser(r,e){return m.parse(r,e??this.defaults)}parseMarkdown(r){return(e,n)=>{let s={...n},t={...this.defaults,...s},i=this.onError(!!t.silent,!!t.async);if(this.defaults.async===!0&&s.async===!1)return i(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof e>"u"||e===null)return i(new Error("marked(): input parameter is undefined or null"));if(typeof e!="string")return i(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(e)+", string expected"));if(t.hooks&&(t.hooks.options=t,t.hooks.block=r),t.async)return(async()=>{let l=t.hooks?await t.hooks.preprocess(e):e,o=await(t.hooks?await t.hooks.provideLexer():r?w.lex:w.lexInline)(l,t),a=t.hooks?await t.hooks.processAllTokens(o):o;t.walkTokens&&await Promise.all(this.walkTokens(a,t.walkTokens));let h=await(t.hooks?await t.hooks.provideParser():r?m.parse:m.parseInline)(a,t);return t.hooks?await t.hooks.postprocess(h):h})().catch(i);try{t.hooks&&(e=t.hooks.preprocess(e));let l=(t.hooks?t.hooks.provideLexer():r?w.lex:w.lexInline)(e,t);t.hooks&&(l=t.hooks.processAllTokens(l)),t.walkTokens&&this.walkTokens(l,t.walkTokens);let o=(t.hooks?t.hooks.provideParser():r?m.parse:m.parseInline)(l,t);return t.hooks&&(o=t.hooks.postprocess(o)),o}catch(l){return i(l)}}}onError(r,e){return n=>{if(n.message+=` +Please report this to https://github.com/markedjs/marked.`,r){let s="

    An error occurred:

    "+y(n.message+"",!0)+"
    ";return e?Promise.resolve(s):s}if(e)return Promise.reject(n);throw n}}},$=new et;function g(r,e){return $.parse(r,e)}g.options=g.setOptions=function(r){return $.setOptions(r),g.defaults=$.defaults,ie(g.defaults),g};g.getDefaults=Q;g.defaults=R;g.use=function(...r){return $.use(...r),g.defaults=$.defaults,ie(g.defaults),g};g.walkTokens=function(r,e){return $.walkTokens(r,e)};g.parseInline=$.parseInline;g.Parser=m;g.parser=m.parse;g.Renderer=B;g.TextRenderer=U;g.Lexer=w;g.lexer=w.lex;g.Tokenizer=v;g.Hooks=A;g.parse=g;g.options;g.setOptions;g.use;g.walkTokens;g.parseInline;m.parse;w.lex;export{g as k}; diff --git a/server/web/dist/assets/share-CqDDLJWI.js b/server/web/dist/assets/share-CqDDLJWI.js new file mode 100644 index 0000000..6be96a6 --- /dev/null +++ b/server/web/dist/assets/share-CqDDLJWI.js @@ -0,0 +1 @@ +import{v as x,S as s,U as m,V as g,x as u,W as w,H as i,X as f,Y as h}from"./index-DYsKpclu.js";function T(n,t,r,a=""){return x(u.shareText,{method:"POST",form:{text:n,expire_value:String(t),expire_style:r,code:a}})}function y(n,t,r,a,o=""){const e=new FormData;return e.append("file",n),e.append("expire_value",String(t)),e.append("expire_style",r),o&&e.append("code",o),h(u.shareFile,e,a)}async function S(n){const t=await x(u.shareMetadata,{query:{code:n}}),r=!!(s(t,["is_text","isText"])??s(t,["type"])==="text");return{code:String(s(t,["code"])??n),name:String(s(t,["name"])??"未命名"),size:Number(s(t,["size"])??0),type:r?"text":"file",isText:r,createdAt:s(t,["created_at","createdAt"])??null,expiredAt:s(t,["expired_at","expires_at","expiredAt","expiresAt"])??null,expiredCount:s(t,["expired_count","expiredCount","remaining_downloads","remainingDownloads"])??null,usedCount:Number(s(t,["used_count","usedCount"])??0),remainingDownloads:s(t,["remaining_downloads","remainingDownloads","expired_count","expiredCount"])??null}}async function b(n){const{blob:t}=await m(u.shareSelect,{query:{code:n}});return t.text()}function A(n,t){return new Promise((r,a)=>{const o=new URL(g(u.shareSelect),location.origin);o.searchParams.set("code",n);const e=new XMLHttpRequest;e.open("GET",o.toString()),e.timeout=6e5,e.responseType="blob";const l=w();l&&e.setRequestHeader("Authorization",`Bearer ${l}`),e.onprogress=p=>{p.lengthComputable&&t&&t(Math.round(p.loaded/p.total*100))},e.onload=()=>{if((e.getResponseHeader("content-type")??"").includes("application/json")){const d=new FileReader;d.onload=()=>{try{const c=JSON.parse(String(d.result));a(new i(c.code??e.status,c.msg||"取件失败",e.status))}catch{a(new i(e.status,"取件失败",e.status))}},d.readAsText(e.response);return}e.status>=200&&e.status<300?r({blob:e.response,filename:f(e.getResponseHeader("content-disposition"))}):a(new i(e.status,`取件失败(HTTP ${e.status})`,e.status))},e.onerror=()=>a(new i(0,"网络异常,取件失败")),e.ontimeout=()=>a(new i(0,"下载超时,请重试")),e.send()})}export{y as a,A as b,S as g,b as p,T as s}; diff --git a/server/web/dist/assets/swagger-CqkleIqs.js b/server/web/dist/assets/swagger-CqkleIqs.js new file mode 100644 index 0000000..dbbfc19 --- /dev/null +++ b/server/web/dist/assets/swagger-CqkleIqs.js @@ -0,0 +1 @@ +function e(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}export{e as g}; diff --git a/server/web/dist/index.html b/server/web/dist/index.html new file mode 100644 index 0000000..583145b --- /dev/null +++ b/server/web/dist/index.html @@ -0,0 +1,36 @@ + + + + + + + + + + + 文件快传 · 文件快传 + + + + + +
    + + diff --git a/server/web/web.go b/server/web/web.go new file mode 100644 index 0000000..0ee8980 --- /dev/null +++ b/server/web/web.go @@ -0,0 +1,21 @@ +// Package web 提供前端 SPA 的 go:embed 嵌入。 +// +// dist/ 由前端任务(vue-dev)构建产出;deploy 构建脚本会把仓库根 +// web/dist 拷贝到本目录后再编译(Dockerfile 已处理)。 +// 本目录内的 index.html 是构建产物的一部分,随产物更新覆盖。 +package web + +import ( + "embed" + "io/fs" +) + +// distFS 嵌入 dist 全部产物。 +// +//go:embed all:dist +var distFS embed.FS + +// Dist 返回嵌入的前端文件系统(根为 dist/)。 +func Dist() (fs.FS, error) { + return fs.Sub(distFS, "dist") +} diff --git a/web-embed/.gitkeep b/web-embed/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..b518b92 --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +*.local +.DS_Store diff --git a/web/README.md b/web/README.md new file mode 100644 index 0000000..e7c59be --- /dev/null +++ b/web/README.md @@ -0,0 +1,79 @@ +# 文件快传 前端(web/) + +Vue 3 + Vite + TypeScript 单页应用:分享 / 取件 / 管理后台(文件管理、审计日志、系统设置)/ 站内文档。 + +v2(任务 t3):液态玻璃主题(亮/暗/跟随系统)、中英双语(vue-i18n)、本地 Logo/favicon 打包资源、 +背景图 / 页脚自定义 / 系统通知(读 `GET /api/v1/config` 下发的 v2 字段)、上传页策略动态渲染、 +管理设置页全量策略表单(PATCH `/admin/config/update`)。 + +## 页面 + +| 路由 | 说明 | +|---|---| +| `/` | 首页:文本/文件分享 Tab + 快速取件(有效期选项/大小/类型过滤按 config 动态渲染) | +| `/s/:code` | 取件页:文本展示 / 文件下载(带进度) | +| `/admin/login` | 管理员登录 | +| `/admin/files` | 文件管理:列表/搜索/编辑/删除/批量删除 | +| `/admin/audit` | 审计日志:动作/结果/IP/时间范围过滤 | +| `/admin/settings` | 系统设置:站点名称、Logo/favicon、背景图、页脚、通知、保存/存储策略、上传频率、管理员密码 | +| `/docs` | 站内 API 文档(渲染 `docs/api/*.md`,构建时内嵌 + 运行时回退) | +| `/openapi` | Swagger UI(渲染 `docs/openapi.yaml`) | + +## 主题与语言 + +- 主题三态(导航栏切换 + `localStorage` 键 `fcb_theme_mode`):浅色 / 深色 / 跟随系统(默认,`prefers-color-scheme`)。 + 实际主题写入 ``;Naive UI 组件接收 `darkTheme`。`index.html` 内联脚本做首帧防闪烁。 +- 语言(导航栏切换 + `localStorage` 键 `fcb_locale`):默认 `navigator.language`(zh 开头 → zh-CN,其余 → en-US)。 + 文案在 `src/locales/`,新增文案先加 zh-CN 再同步 en-US。 + +## v2 配置字段(消费 `GET /api/v1/config`,管理端经 `PATCH /admin/config/update` 写回) + +`background_url`、`footer_text`、`footer_beian`、`notify_enabled/notify_title/notify_content`、 +`max_file_size`、`max_save_seconds`、`max_save_count`、`allowedFileTypes`、`expireStyle`、 +`uploadSize`、`uploadCount/uploadMinute`、`site_name/logo_url/favicon_url`。 +策略最终由服务端强制(403/400 中文错误);前端只做体验层提示与动态选项渲染。 + +## 命令 + +```bash +npm ci # 安装依赖 +npm run dev # 开发服务器(默认代理 /api /share /chunk /presign /admin 到 http://127.0.0.1:8466) +npm run build # 类型检查 + 产出 dist/(后端 embed 用) +npm run preview # 预览构建产物 +``` + +## 环境变量(.env.local) + +| 变量 | 说明 | +|---|---| +| `VITE_API_BASE` | 后端 API 根地址;默认空 = 同源(embed 部署)。如 `http://127.0.0.1:8466` | +| `VITE_MOCK` | 置 `1` 启用内置 mock(无需后端) | +| `VITE_ROUTER_MODE` | `history`(默认,后端 NoRoute 已做 SPA 回退)/ `hash`(后端无回退时) | + +## API 契约(最终版,经队长终审裁定) + +**业务 API 挂根路径;公共端点 `/api/v1/health`、`/api/v1/config` 例外。** 权威依据:`docs/api/*.md`(tech-writer 13 篇,概述 00-overview.md)与 `docs/openapi.yaml`,本节与其 100% 对齐;变更须同步文档并知会 go-api + tech-writer。 +- 统一响应 `{"code":200,"msg":"ok","data":...}`,HTTP 状态 = code;401 未登录(连续失败 423)、423 限流、428 未初始化。 +- 取件:`GET /share/metadata?code=` 不消耗次数;`GET /share/select?code=` 会**消耗**取件次数(文本 text/plain,文件流 200/206 支持 Range),文件名读 Content-Disposition。前端只在真正取件时调用 select。 +- 分片:`POST /chunk/upload/init`(JSON)→ `POST /chunk/upload/{uploadID}/{chunkIndex}`(multipart 仅 chunk 字段)→ `GET /chunk/upload/status/{uploadID}` → `POST /chunk/upload/complete/{uploadID}`;取消 `DELETE /chunk/upload/{uploadID}`。 +- 管理端:`POST /admin/login`、`GET /admin/verify`、`DELETE /admin/file/delete`、`POST /admin/file/batch-delete`、`PATCH /admin/file/update`、`PATCH|POST /admin/config/update`、`GET /admin/audit/list`(参数 page/size/action/result/ip/start_time/end_time,ISO 8601)、`PATCH /admin/settings/password`。 + +## 嵌入约定(go:embed) + +- 构建产物输出到 `web/dist/`(入口 `web/dist/index.html`)。**注意**:`web/` 在 Go module(server/)之外,go:embed 无法直接引用 —— go-api 需在构建脚本中拷贝(如 `cp -a web/dist server/web/dist`)或以 `os.DirFS` 方式挂载;`base: '/'` + history 路由配合其 NoRoute SPA 回退(静态资源优先、GET+text/html 回 index.html、/api/* JSON 404)。 +- 如需改输出目录,调整 `vite.config.ts` 的 `build.outDir`。 + +## Logo 自定义(v2:本地资源兜底) + +- 默认导航 Logo:`src/assets/brand/logo.svg`(构建内嵌;`DEFAULT_LOGO_URL`) +- 默认 favicon:`src/assets/brand/favicon.png`(`index.html` + `DEFAULT_FAVICON_URL`) +- 运行时优先读取公共配置 `logo_url` / `favicon_url` / `site_name`(管理端「系统设置」保存后立即全站生效),空值回退本地默认;不再依赖远程 URL。 + +## API 客户端 + +统一封装见 `src/api/paths.ts`(全部路径一处可改)与 `src/api/http.ts`(`{code,msg,data}` 解包、Bearer 认证、超时、401/428 处理)。 + +## 文档页约定(与 tech-writer) + +- `docs/api/*.md`:每个文件一个章节,**一级标题(`# 标题`)即侧边栏章节名**;文件名(去掉 .md)作为路由 slug。构建时通过 `import.meta.glob` 内嵌原文,后端如挂载 `/docs/api/index.json`(`[{slug,title}]`)可补充远端章节。 +- `docs/openapi.yaml`:构建时内嵌给 Swagger UI;后端也可直接静态托管 `/docs/openapi.yaml`。 diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..69a26e6 --- /dev/null +++ b/web/index.html @@ -0,0 +1,35 @@ + + + + + + + + + + + 文件快传 · 文件快传 + + + +
    + + + diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100644 index 0000000..538626d --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,1960 @@ +{ + "name": "filecodebox-web", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "filecodebox-web", + "version": "1.0.0", + "dependencies": { + "marked": "^16.3.0", + "naive-ui": "^2.45.3", + "pinia": "^3.0.3", + "swagger-ui-dist": "^5.28.1", + "vue": "^3.5.13", + "vue-i18n": "^9.14.4", + "vue-router": "^4.5.0" + }, + "devDependencies": { + "@types/node": "^22.10.0", + "@vitejs/plugin-vue": "^6.0.0", + "typescript": "~5.8.3", + "vite": "^7.0.0", + "vue-tsc": "^3.0.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==", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@css-render/plugin-bem": { + "version": "0.15.14", + "resolved": "https://registry.npmmirror.com/@css-render/plugin-bem/-/plugin-bem-0.15.14.tgz", + "integrity": "sha512-QK513CJ7yEQxm/P3EwsI+d+ha8kSOcjGvD6SevM41neEMxdULE+18iuQK6tEChAWMOQNQPLG/Rw3Khb69r5neg==", + "license": "MIT", + "peerDependencies": { + "css-render": "~0.15.14" + } + }, + "node_modules/@css-render/vue3-ssr": { + "version": "0.15.14", + "resolved": "https://registry.npmmirror.com/@css-render/vue3-ssr/-/vue3-ssr-0.15.14.tgz", + "integrity": "sha512-//8027GSbxE9n3QlD73xFY6z4ZbHbvrOVB7AO6hsmrEzGbg+h2A09HboUyDgu+xsmj7JnvJD39Irt+2D0+iV8g==", + "license": "MIT", + "peerDependencies": { + "vue": "^3.0.11" + } + }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/core-base": { + "version": "9.14.4", + "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.14.4.tgz", + "integrity": "sha512-vtZCt7NqWhKEtHa3SD/322DlgP5uR9MqWxnE0y8Q0tjDs9H5Lxhss+b5wv8rmuXRoHKLESNgw9d+EN9ybBbj9g==", + "license": "MIT", + "dependencies": { + "@intlify/message-compiler": "9.14.4", + "@intlify/shared": "9.14.4" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "9.14.4", + "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.14.4.tgz", + "integrity": "sha512-vcyCLiVRN628U38c3PbahrhbbXrckrM9zpy0KZVlDk2Z0OnGwv8uQNNXP3twwGtfLsCf4gu3ci6FMIZnPaqZsw==", + "license": "MIT", + "dependencies": { + "@intlify/shared": "9.14.4", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "9.14.4", + "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.14.4.tgz", + "integrity": "sha512-P9zv6i1WvMc9qDBWvIgKkymjY2ptIiQ065PjDv7z7fDqH3J/HBRBN5IoiR46r/ujRcU7hCuSIZWvCAFCyuOYZA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "license": "MIT" + }, + "node_modules/@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", + "license": "Apache-2.0" + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz", + "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz", + "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz", + "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz", + "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz", + "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz", + "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz", + "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz", + "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz", + "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz", + "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz", + "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz", + "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz", + "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz", + "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz", + "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz", + "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz", + "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz", + "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz", + "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz", + "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz", + "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz", + "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz", + "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz", + "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz", + "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.0" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.25", + "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.25.tgz", + "integrity": "sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==", + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, + "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/@vitejs/plugin-vue": { + "version": "6.0.8", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz", + "integrity": "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.42", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.42.tgz", + "integrity": "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.42", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.42", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.42.tgz", + "integrity": "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.42", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz", + "integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.42", + "@vue/compiler-dom": "3.5.42", + "@vue/compiler-ssr": "3.5.42", + "@vue/shared": "3.5.42", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.42", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.42.tgz", + "integrity": "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.10", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.10.tgz", + "integrity": "sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.10" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.10", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.10.tgz", + "integrity": "sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.10", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.10", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.10.tgz", + "integrity": "sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/language-core": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-3.3.11.tgz", + "integrity": "sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.2.1", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.4" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.42", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.42.tgz", + "integrity": "sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.42", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.42.tgz", + "integrity": "sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.42", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.42.tgz", + "integrity": "sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.42", + "@vue/runtime-core": "3.5.42", + "@vue/shared": "3.5.42", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.42", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.42.tgz", + "integrity": "sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.42", + "@vue/runtime-dom": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.42", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.42.tgz", + "integrity": "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==", + "license": "MIT" + }, + "node_modules/alien-signals": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-3.2.1.tgz", + "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "license": "MIT" + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/copy-anything": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-4.1.0.tgz", + "integrity": "sha512-ufbM3smX/Jbnpk5wcQjzd1MgBpzmqfNETUAyZNrGwU9foRlyHoGzMMBBCRzEhQLBjZfFDE1W2ufPXX2vdWkV8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/css-render": { + "version": "0.15.14", + "resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.14.tgz", + "integrity": "sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "~0.8.0", + "csstype": "~3.0.5" + } + }, + "node_modules/css-render/node_modules/csstype": { + "version": "3.0.11", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.0.11.tgz", + "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/date-fns": { + "version": "4.4.0", + "resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-4.4.0.tgz", + "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/date-fns-tz": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/date-fns-tz/-/date-fns-tz-3.2.0.tgz", + "integrity": "sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==", + "license": "MIT", + "peerDependencies": { + "date-fns": "^3.0.0 || ^4.0.0" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/evtd": { + "version": "0.2.4", + "resolved": "https://registry.npmmirror.com/evtd/-/evtd-0.2.4.tgz", + "integrity": "sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "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/highlight.js": { + "version": "11.12.0", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.12.0.tgz", + "integrity": "sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmmirror.com/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/naive-ui": { + "version": "2.45.3", + "resolved": "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.45.3.tgz", + "integrity": "sha512-D83gJ+6zUdkQ5DuUbmlfyaM1t8pV5WMPa6KmL9mByqQuiL12qBdA0n/iEIth4dQw1x/duNGpOS2anpt5FKO1kw==", + "license": "MIT", + "dependencies": { + "@css-render/plugin-bem": "^0.15.14", + "@css-render/vue3-ssr": "^0.15.14", + "@types/lodash": "^4.17.20", + "@types/lodash-es": "^4.17.12", + "async-validator": "^4.2.5", + "css-render": "^0.15.14", + "csstype": "^3.1.3", + "date-fns": "^4.1.0", + "date-fns-tz": "^3.2.0", + "evtd": "^0.2.4", + "highlight.js": "^11.8.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "seemly": "^0.3.10", + "treemate": "^0.3.11", + "vdirs": "^0.1.8", + "vooks": "^0.2.12", + "vueuc": "^0.4.66" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinia": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.4.tgz", + "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.7" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.5.0", + "vue": "^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "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.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.63.1.tgz", + "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.1", + "@rollup/rollup-android-arm64": "4.63.1", + "@rollup/rollup-darwin-arm64": "4.63.1", + "@rollup/rollup-darwin-x64": "4.63.1", + "@rollup/rollup-freebsd-arm64": "4.63.1", + "@rollup/rollup-freebsd-x64": "4.63.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", + "@rollup/rollup-linux-arm-musleabihf": "4.63.1", + "@rollup/rollup-linux-arm64-gnu": "4.63.1", + "@rollup/rollup-linux-arm64-musl": "4.63.1", + "@rollup/rollup-linux-loong64-gnu": "4.63.1", + "@rollup/rollup-linux-loong64-musl": "4.63.1", + "@rollup/rollup-linux-ppc64-gnu": "4.63.1", + "@rollup/rollup-linux-ppc64-musl": "4.63.1", + "@rollup/rollup-linux-riscv64-gnu": "4.63.1", + "@rollup/rollup-linux-riscv64-musl": "4.63.1", + "@rollup/rollup-linux-s390x-gnu": "4.63.1", + "@rollup/rollup-linux-x64-gnu": "4.63.1", + "@rollup/rollup-linux-x64-musl": "4.63.1", + "@rollup/rollup-openbsd-x64": "4.63.1", + "@rollup/rollup-openharmony-arm64": "4.63.1", + "@rollup/rollup-win32-arm64-msvc": "4.63.1", + "@rollup/rollup-win32-ia32-msvc": "4.63.1", + "@rollup/rollup-win32-x64-gnu": "4.63.1", + "@rollup/rollup-win32-x64-msvc": "4.63.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/seemly": { + "version": "0.3.10", + "resolved": "https://registry.npmmirror.com/seemly/-/seemly-0.3.10.tgz", + "integrity": "sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==", + "license": "MIT" + }, + "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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/swagger-ui-dist": { + "version": "5.32.14", + "resolved": "https://registry.npmmirror.com/swagger-ui-dist/-/swagger-ui-dist-5.32.14.tgz", + "integrity": "sha512-nOA2pSQhcmODMUQZpJHYKNuwniDUqcOWGNaSCOoZv12FdOSJ9JxV95HtyRGNMqEBj6h6lCNTy20TgZDYTSuUIg==", + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/treemate": { + "version": "0.3.11", + "resolved": "https://registry.npmmirror.com/treemate/-/treemate-0.3.11.tgz", + "integrity": "sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "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/vdirs": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/vdirs/-/vdirs-0.1.8.tgz", + "integrity": "sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==", + "license": "MIT", + "dependencies": { + "evtd": "^0.2.2" + }, + "peerDependencies": { + "vue": "^3.0.11" + } + }, + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmmirror.com/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vooks": { + "version": "0.2.12", + "resolved": "https://registry.npmmirror.com/vooks/-/vooks-0.2.12.tgz", + "integrity": "sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==", + "license": "MIT", + "dependencies": { + "evtd": "^0.2.2" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vscode-uri": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.42", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.42.tgz", + "integrity": "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.42", + "@vue/compiler-sfc": "3.5.42", + "@vue/runtime-dom": "3.5.42", + "@vue/server-renderer": "3.5.42", + "@vue/shared": "3.5.42" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-i18n": { + "version": "9.14.4", + "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.14.4.tgz", + "integrity": "sha512-B934C8yUyWLT0EMud3DySrwSUJI7ZNiWYsEEz2gknTthqKiG4dzWE/WSa8AzCuSQzwBEv4HtG1jZDhgzPfWSKQ==", + "license": "MIT", + "dependencies": { + "@intlify/core-base": "9.14.4", + "@intlify/shared": "9.14.4", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-i18n/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/vue-router": { + "version": "4.6.4", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/vue-tsc": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-3.3.11.tgz", + "integrity": "sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.3.11" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/vueuc": { + "version": "0.4.66", + "resolved": "https://registry.npmmirror.com/vueuc/-/vueuc-0.4.66.tgz", + "integrity": "sha512-hHrZl+0AsrUvqYPBXSXQ2QaSrO3razjeUseLAhJWlOS/Hdfw/Y2MU05fOTP1qn1A3121gMxsHLaEz2V6CkWdTg==", + "license": "MIT", + "dependencies": { + "@css-render/vue3-ssr": "^0.15.10", + "@juggle/resize-observer": "^3.3.1", + "css-render": "^0.15.10", + "evtd": "^0.2.4", + "seemly": "^0.3.6", + "vdirs": "^0.1.4", + "vooks": "^0.2.4" + }, + "peerDependencies": { + "vue": "^3.0.11" + } + } + } +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..1c42b26 --- /dev/null +++ b/web/package.json @@ -0,0 +1,29 @@ +{ + "name": "filecodebox-web", + "private": true, + "version": "1.0.0", + "type": "module", + "description": "FileCodeBox Go 版前端:分享 / 取件 / 管理后台 / 审计日志 / 站内文档", + "scripts": { + "dev": "vite", + "build": "vue-tsc --noEmit && vite build", + "preview": "vite preview", + "typecheck": "vue-tsc --noEmit" + }, + "dependencies": { + "marked": "^16.3.0", + "naive-ui": "^2.45.3", + "pinia": "^3.0.3", + "swagger-ui-dist": "^5.28.1", + "vue": "^3.5.13", + "vue-i18n": "^9.14.4", + "vue-router": "^4.5.0" + }, + "devDependencies": { + "@types/node": "^22.10.0", + "@vitejs/plugin-vue": "^6.0.0", + "typescript": "~5.8.3", + "vite": "^7.0.0", + "vue-tsc": "^3.0.0" + } +} diff --git a/web/src/App.vue b/web/src/App.vue new file mode 100644 index 0000000..b589337 --- /dev/null +++ b/web/src/App.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/web/src/api/admin.ts b/web/src/api/admin.ts new file mode 100644 index 0000000..1d94f6d --- /dev/null +++ b/web/src/api/admin.ts @@ -0,0 +1,157 @@ +/** 管理后台 API(对齐 t3 契约:login/verify/logout/file list/update/delete/batch-delete/config/audit) */ +import { request } from './http' +import { paths } from './paths' +import type { + AdminConfig, + AdminFileListResult, + AdminFileUpdatePayload, + AdminLoginResult, + AuditListResult, +} from './types' +import { pick } from '@/utils/format' + +/** 管理员登录(密码错误 → 401 ApiError) */ +export function adminLogin(password: string): Promise { + return request(paths.adminLogin, { method: 'POST', json: { password } }) +} + +export function adminVerify(): Promise> { + return request>(paths.adminVerify) +} + +export async function adminLogout(): Promise { + await request(paths.adminLogout, { method: 'POST' }) +} + +export interface AdminFileListQuery { + page: number + size: number + keyword?: string +} + +/** 文件列表(宽松解析:兼容 data 字段或 list 字段、total 顶层) */ +export async function adminFileList(q: AdminFileListQuery): Promise { + const raw = await request>(paths.adminFileList, { + query: { page: q.page, size: q.size, keyword: q.keyword || undefined }, + }) + const rows = (pick(raw, ['data', 'list', 'items', 'files']) ?? []) as Record[] + const total = Number(pick(raw, ['total', 'count']) ?? rows.length) + return { + page: Number(pick(raw, ['page']) ?? q.page), + size: Number(pick(raw, ['size']) ?? q.size), + total, + data: rows.map((r) => ({ + id: Number(pick(r, ['id']) ?? 0), + code: String(pick(r, ['code']) ?? ''), + name: String(pick(r, ['name']) ?? `${pick(r, ['prefix']) ?? ''}${pick(r, ['suffix']) ?? ''}`), + suffix: String(pick(r, ['suffix']) ?? ''), + size: Number(pick(r, ['size']) ?? 0), + isText: Boolean(pick(r, ['isText', 'is_text']) ?? false), + expiredAt: (pick(r, ['expiredAt', 'expired_at', 'expires_at']) as string) ?? null, + expiredCount: (pick(r, ['expiredCount', 'expired_count']) as number) ?? null, + usedCount: Number(pick(r, ['usedCount', 'used_count']) ?? 0), + createdAt: (pick(r, ['createdAt', 'created_at']) as string) ?? null, + isExpired: Boolean(pick(r, ['isExpired', 'is_expired']) ?? false), + })), + } +} + +/** 删除单个文件 */ +export async function adminFileDelete(id: number): Promise { + await request(paths.adminFileDelete, { method: 'DELETE', json: { id } }) +} + +/** 批量删除(契约:POST /admin/file/batch-delete,body {ids:number[]}) */ +export async function adminFileBatchDelete(ids: number[]): Promise { + await request(paths.adminFileBatchDelete, { method: 'POST', json: { ids } }) +} + +/** 更新分享(code/过期时间/次数;go-api 定稿 PATCH /admin/file/update) */ +export async function adminFileUpdate(payload: AdminFileUpdatePayload): Promise { + await request(paths.adminFileUpdate, { method: 'PATCH', json: payload }) +} + +/** 读取管理端配置(KV 全量,含 site_name/logo_url/favicon_url) */ +export async function adminConfigGet(): Promise { + const raw = await request(paths.adminConfigGet) + // 兼容:后端可能直接返回 KV 对象,或包一层 config/data + if (raw && typeof raw === 'object' && !Array.isArray(raw)) { + const obj = raw as Record + const inner = (pick>(obj, ['config', 'data', 'settings']) ?? obj) as Record + return inner + } + return {} +} + +/** 更新管理端配置(PATCH 部分字段 JSON;提交后全站生效,含 site_name/logo_url/favicon_url) */ +export async function adminConfigUpdate(patch: Record): Promise { + await request(paths.adminConfigUpdate, { method: 'PATCH', json: patch }) +} + +/** v3 存储引擎热切换:成功返回当前引擎名;失败(503)服务端保持原引擎并抛 ApiError */ +export async function adminStorageSwitch(engine: 'local' | 's3' | 'webdav'): Promise { + const data = await request<{ ok: boolean; engine: string }>(paths.adminStorageSwitch, { + method: 'POST', + json: { engine }, + }) + return String(data?.engine ?? engine) +} + +/** 修改管理员密码(成功后旧 token 全部失效,需重新登录) */ +export async function adminPasswordUpdate(oldPassword: string, newPassword: string): Promise { + await request(paths.adminPasswordUpdate, { + method: 'PATCH', + json: { old_password: oldPassword, new_password: newPassword }, + }) +} + +export interface AuditListQuery { + page: number + size: number + action?: string + result?: string + ip?: string + /** ISO 或 YYYY-MM-DD;由调用方格式化 */ + startTime?: string + endTime?: string +} + +/** 审计日志列表(宽松解析兼容 data/list 字段与 snake_case/camelCase 行字段) */ +export async function adminAuditList(q: AuditListQuery): Promise { + const raw = await request>(paths.adminAuditList, { + query: { + page: q.page, + size: q.size, + action: q.action || undefined, + result: q.result || undefined, + ip: q.ip || undefined, + start_time: q.startTime || undefined, + end_time: q.endTime || undefined, + }, + }) + const rows = (pick(raw, ['data', 'list', 'items', 'logs']) ?? []) as Record[] + const total = Number(pick(raw, ['total', 'count']) ?? rows.length) + return { + page: Number(pick(raw, ['page']) ?? q.page), + size: Number(pick(raw, ['size']) ?? q.size), + total, + data: rows.map((r, idx) => ({ + id: Number(pick(r, ['id']) ?? idx + 1), + action: String(pick(r, ['action']) ?? ''), + result: String(pick(r, ['result']) ?? ''), + fileCode: String(pick(r, ['file_code', 'fileCode', 'code']) ?? ''), + fileName: String(pick(r, ['file_name', 'fileName', 'name']) ?? ''), + sizeBytes: (pick(r, ['size_bytes', 'sizeBytes', 'size']) as number) ?? null, + transferredBytes: (pick(r, ['transferred_bytes', 'transferredBytes', 'bytes']) as number) ?? null, + ip: String(pick(r, ['ip', 'client_ip', 'clientIp']) ?? ''), + userAgent: String(pick(r, ['user_agent', 'userAgent']) ?? ''), + deviceOs: String(pick(r, ['device_os', 'deviceOs', 'os']) ?? ''), + deviceBrowser: String(pick(r, ['device_browser', 'deviceBrowser', 'browser']) ?? ''), + deviceType: String(pick(r, ['device_type', 'deviceType']) ?? ''), + actor: String(pick(r, ['actor']) ?? ''), + errorMsg: String(pick(r, ['error_msg', 'errorMsg', 'error']) ?? ''), + durationMs: (pick(r, ['duration_ms', 'durationMs', 'duration']) as number) ?? null, + createdAt: (pick(r, ['created_at', 'createdAt', 'time']) as string) ?? null, + })), + } +} diff --git a/web/src/api/chunk.ts b/web/src/api/chunk.ts new file mode 100644 index 0000000..abd6812 --- /dev/null +++ b/web/src/api/chunk.ts @@ -0,0 +1,58 @@ +/** 分片上传 API(对齐 t3 契约:init / upload / status / finish / cancel) */ +import { request } from './http' +import { paths } from './paths' +import type { ChunkFinishResult, ChunkInitResult, ChunkStatusResult } from './types' + +/** 初始化分片上传会话(file_hash 用于断点续传匹配) */ +export function initChunkUpload( + fileName: string, + fileSize: number, + chunkSize: number, + fileHash: string, +): Promise { + return request(paths.chunkInit, { + method: 'POST', + json: { file_name: fileName, file_size: fileSize, chunk_size: chunkSize, file_hash: fileHash }, + timeout: 60_000, + }) +} + +/** + * 上传单个分片:路径式 /chunk/upload/{uploadID}/{chunkIndex},multipart 字段 chunk。 + * 兼容处理:同时附 upload_id / chunk_index 表单字段(后端按表单读取时可用,按路径 + * 参数读取时忽略无副作用)。 + */ +export async function uploadOneChunk(uploadId: string, index: number, blob: Blob): Promise { + const fd = new FormData() + fd.append('upload_id', uploadId) + fd.append('chunk_index', String(index)) + fd.append('chunk', blob, `chunk-${index}`) + await request<{ chunk_hash?: string }>(paths.chunkUpload(uploadId, index), { + method: 'POST', + formData: fd, + timeout: 120_000, + }) +} + +export function chunkStatus(uploadId: string): Promise { + return request(paths.chunkStatus(uploadId), { timeout: 60_000 }) +} + +/** 合并分片并创建分享 */ +export function finishChunkUpload( + uploadId: string, + expireValue: number, + expireStyle: string, + code = '', +): Promise { + return request(paths.chunkFinish(uploadId), { + method: 'POST', + json: { expire_value: expireValue, expire_style: expireStyle, code }, + timeout: 300_000, + }) +} + +/** 取消上传并清理分片 */ +export async function cancelChunkUpload(uploadId: string): Promise { + await request(paths.chunkCancel(uploadId), { method: 'DELETE', timeout: 60_000 }) +} diff --git a/web/src/api/http.ts b/web/src/api/http.ts new file mode 100644 index 0000000..a5e8891 --- /dev/null +++ b/web/src/api/http.ts @@ -0,0 +1,209 @@ +/** + * HTTP 客户端:统一处理 {code,msg,data} 响应封装、Bearer 认证、超时与错误归一。 + * 业务层拿到的永远是 data,非 200 一律抛 ApiError。 + * paths.ts 中的路径均为完整路径(业务接口根路径 /share /chunk /admin,公共 /api/v1)。 + */ +import { api } from './paths' + +export class ApiError extends Error { + /** 业务错误码(响应体 code)或 HTTP 状态码 */ + code: number + msg: string + httpStatus?: number + + constructor(code: number, msg: string, httpStatus?: number) { + super(msg || `请求失败(${code})`) + this.name = 'ApiError' + this.code = code + this.msg = msg || `请求失败(${code})` + this.httpStatus = httpStatus + } +} + +const TOKEN_KEY = 'fcb_admin_token' + +export function getToken(): string { + try { + return localStorage.getItem(TOKEN_KEY) ?? '' + } catch { + return '' + } +} + +export function setToken(token: string): void { + try { + if (token) localStorage.setItem(TOKEN_KEY, token) + else localStorage.removeItem(TOKEN_KEY) + } catch { + /* localStorage 不可用时忽略 */ + } +} + +/** 401 时的全局处理(由 router 注入:跳转管理登录页) */ +let unauthorizedHandler: (() => void) | null = null +export function setUnauthorizedHandler(fn: () => void): void { + unauthorizedHandler = fn +} + +export interface RequestOptions { + method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' + /** JSON 请求体 */ + json?: unknown + /** application/x-www-form-urlencoded 请求体 */ + form?: Record + /** multipart/form-data 请求体(Content-Type 由浏览器自动设置) */ + formData?: FormData + /** URL 查询参数 */ + query?: Record + /** 超时毫秒,默认 30s;上传等长任务可调大 */ + timeout?: number + signal?: AbortSignal +} + +interface Envelope { + code: number + msg?: string + data: T +} + +function buildUrl(path: string, query?: RequestOptions['query']): string { + const url = new URL(api(path), location.origin) + if (query) { + for (const [k, v] of Object.entries(query)) { + if (v !== undefined && v !== null && `${v}` !== '') url.searchParams.set(k, `${v}`) + } + } + return url.toString() +} + +function authHeaders(): Record { + const token = getToken() + return token ? { Authorization: `Bearer ${token}` } : {} +} + +/** 发起请求并解包 {code,msg,data}。code!==200 或 HTTP 非 2xx → ApiError */ +export async function request(path: string, opts: RequestOptions = {}): Promise { + const { method = 'GET', json, form, formData, query, timeout = 30_000, signal } = opts + const controller = new AbortController() + const timer = setTimeout(() => controller.abort(new DOMException('请求超时', 'TimeoutError')), timeout) + if (signal) signal.addEventListener('abort', () => controller.abort(signal.reason), { once: true }) + + const headers: Record = { ...authHeaders() } + if (json !== undefined) headers['Content-Type'] = 'application/json' + let body: BodyInit | undefined + if (json !== undefined) body = JSON.stringify(json) + else if (formData) body = formData + else if (form) { + // v3.1.1 根因修复:URLSearchParams.toString() 产出的是字符串 body, + // fetch 对字符串默认 Content-Type: text/plain —— 后端无法按表单解析, + // 文本分享被静默存成空(或被空文本校验拒绝)。必须显式声明 urlencoded。 + body = new URLSearchParams(form).toString() + headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8' + } + + let res: Response + try { + res = await fetch(buildUrl(path, query), { method, headers, body, signal: controller.signal }) + } catch (e) { + if (e instanceof DOMException && e.name === 'TimeoutError') throw new ApiError(0, '请求超时,请检查网络或稍后重试') + throw new ApiError(0, '网络异常,无法连接服务器') + } finally { + clearTimeout(timer) + } + + // 非 JSON 响应(如裸文本错误页) + const contentType = res.headers.get('content-type') ?? '' + if (!contentType.includes('application/json')) { + const text = await res.text().catch(() => '') + if (!res.ok) throw new ApiError(res.status, text.slice(0, 200) || `请求失败(HTTP ${res.status})`, res.status) + throw new ApiError(res.status, '响应格式异常(非 JSON)', res.status) + } + + let env: Envelope + try { + env = (await res.json()) as Envelope + } catch { + throw new ApiError(res.status, '响应 JSON 解析失败', res.status) + } + + if (!res.ok || env.code !== 200) { + const code = env.code ?? res.status + if (code === 401 || res.status === 401) { + setToken('') + unauthorizedHandler?.() + } + throw new ApiError(code, env.msg || `请求失败(${code})`, res.status) + } + return env.data +} + +/** + * 取件接口专用:/share/select 返回的是纯文本或文件流(而非 JSON 封装)。 + * 错误时后端仍可能返回 JSON 封装 → 统一转成 ApiError。 + */ +export async function requestRaw( + path: string, + opts: { query?: Record; timeout?: number } = {}, +): Promise<{ blob: Blob; contentType: string }> { + const { query, timeout = 120_000 } = opts + const controller = new AbortController() + const timer = setTimeout(() => controller.abort(new DOMException('请求超时', 'TimeoutError')), timeout) + let res: Response + try { + res = await fetch(buildUrl(path, query), { method: 'GET', headers: authHeaders(), signal: controller.signal }) + } catch { + throw new ApiError(0, '网络异常,无法连接服务器') + } finally { + clearTimeout(timer) + } + + const contentType = res.headers.get('content-type') ?? '' + if (contentType.includes('application/json')) { + try { + const env = (await res.json()) as Envelope + throw new ApiError(env.code ?? res.status, env.msg || '取件失败', res.status) + } catch (e) { + if (e instanceof ApiError) throw e + throw new ApiError(res.status, '取件失败', res.status) + } + } + if (!res.ok) throw new ApiError(res.status, `取件失败(HTTP ${res.status})`, res.status) + return { blob: await res.blob(), contentType } +} + +/** 带上传进度的 POST(XMLHttpRequest,fetch 无法拿到上传进度) */ +export function uploadWithProgress( + path: string, + form: FormData, + onProgress?: (percent: number) => void, + timeout = 600_000, +): Promise { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest() + xhr.open('POST', buildUrl(path)) + xhr.timeout = timeout + const token = getToken() + if (token) xhr.setRequestHeader('Authorization', `Bearer ${token}`) + xhr.upload.onprogress = (e) => { + if (e.lengthComputable && onProgress) onProgress(Math.round((e.loaded / e.total) * 100)) + } + xhr.onload = () => { + try { + const env = JSON.parse(xhr.responseText) as Envelope + if (xhr.status >= 200 && xhr.status < 300 && env.code === 200) resolve(env.data) + else { + if (env.code === 401 || xhr.status === 401) { + setToken('') + unauthorizedHandler?.() + } + reject(new ApiError(env.code ?? xhr.status, env.msg || `上传失败(HTTP ${xhr.status})`, xhr.status)) + } + } catch { + reject(new ApiError(xhr.status, `上传失败(HTTP ${xhr.status})`, xhr.status)) + } + } + xhr.onerror = () => reject(new ApiError(0, '网络异常,上传失败')) + xhr.ontimeout = () => reject(new ApiError(0, '上传超时,请重试')) + xhr.send(form) + }) +} diff --git a/web/src/api/paths.ts b/web/src/api/paths.ts new file mode 100644 index 0000000..055c33a --- /dev/null +++ b/web/src/api/paths.ts @@ -0,0 +1,89 @@ +/** + * 统一 API 路径契约(最终版,经队长终审裁定): + * + * ⚠️ 业务 API 挂**根路径**(与参考实现及 t1 审计中间件路由分类器一致): + * /share/*、/chunk/*、/presign/*、/admin/*、/setup —— 一律不带 /api/v1 前缀! + * 仅以下两个公共端点带 /api/v1 前缀(t1 冻结):/api/v1/health、/api/v1/config。 + * 若业务路径误加前缀,线上将全部 404 且审计分类失效。 + * + * - baseURL 通过 VITE_API_BASE 配置;为空表示同源部署(后端 embed dist/ + NoRoute SPA 回退) + * - 若后端调整路径,只需改本文件 + */ + +/** API 根地址(去掉末尾斜杠)。为空 = 同源。 */ +export const API_BASE: string = (import.meta.env.VITE_API_BASE ?? '').trim().replace(/\/+$/, '') + +/** 拼接完整接口地址(path 为含前缀的完整路径:业务路径以 / 开头直接拼根) */ +export function api(path: string): string { + return `${API_BASE}${path}` +} + +/** 取件页站内路由(router hash/history 模式自适应) */ +function routerBase(): string { + return (import.meta.env.VITE_ROUTER_MODE ?? 'history') === 'history' ? '/s/' : '/#/s/' +} + +export function pickupPageUrl(code: string, base?: string): string { + // v3.1:配置了站点对外域名时用之(内网访问也能生成公网链接),否则当前地址 + const origin = (base?.trim() || location.origin).replace(/\/$/, '') + return `${origin}${routerBase()}${encodeURIComponent(code)}` +} + +/** + * 全部业务端点(最终版)。 + * 与 docs/openapi.yaml 及 docs/api/*.md(tech-writer 权威文档)逐项核对一致;变更须同步文档。 + */ +export const paths = { + // 公共(/api/v1 前缀,t1 冻结的两个例外) + health: '/api/v1/health', + publicConfig: '/api/v1/config', + + // 初始化(HTML 向导/表单,整页跳转用;未初始化时其余接口一律 428) + setup: '/setup', // GET 向导 / POST 表单提交 + + // 分享(根路径;select 会消耗取件次数:仅真正取件时调用,勿做预览轮询) + shareText: '/share/text', // POST form: text, expire_value, expire_style + shareFile: '/share/file', // POST multipart: file, expire_value, expire_style + shareMetadata: '/share/metadata', // GET ?code=(不消耗次数) + shareSelect: '/share/select', // GET ?code=(文本 text/plain / 文件流 200|206 Range) + shareDownload: '/share/download', // GET ?key=&code=(备忘) + + // 分片上传(根路径) + chunkInit: '/chunk/upload/init', // POST JSON {file_name, chunk_size, file_size, file_hash} + chunkUpload: (uploadId: string, index: number) => + `/chunk/upload/${encodeURIComponent(uploadId)}/${index}`, // POST multipart: chunk + chunkStatus: (uploadId: string) => `/chunk/upload/status/${encodeURIComponent(uploadId)}`, // GET + chunkFinish: (uploadId: string) => `/chunk/upload/complete/${encodeURIComponent(uploadId)}`, // POST JSON {expire_value, expire_style} + chunkCancel: (uploadId: string) => `/chunk/upload/${encodeURIComponent(uploadId)}`, // DELETE + + // 预签名直传(备忘,当前前端版本未使用) + presignInit: '/presign/upload/init', + presignProxyUpload: (uploadId: string) => `/presign/upload/proxy/${encodeURIComponent(uploadId)}`, + presignConfirm: (uploadId: string) => `/presign/upload/confirm/${encodeURIComponent(uploadId)}`, + presignStatus: (uploadId: string) => `/presign/upload/status/${encodeURIComponent(uploadId)}`, + presignCancel: (uploadId: string) => `/presign/upload/${encodeURIComponent(uploadId)}`, + + // 管理后台(根路径) + adminLogin: '/admin/login', // POST JSON {password};失败 401,连续失败 423 + adminVerify: '/admin/verify', // GET + adminLogout: '/admin/logout', // POST + adminDashboard: '/admin/dashboard', // GET(备忘) + adminFileList: '/admin/file/list', // GET ?page&size&keyword + adminFileDelete: '/admin/file/delete', // DELETE {id} + adminFileBatchDelete: '/admin/file/batch-delete', // POST {ids:number[]} + adminFileUpdate: '/admin/file/update', // PATCH {id, code?, expired_at?, expired_count?} + adminConfigGet: '/admin/config/get', // GET(admin_token 屏蔽、jwt_secret 不下发) + adminConfigUpdate: '/admin/config/update', // PATCH 部分字段 JSON(POST 亦可) + adminAuditList: '/admin/audit/list', // GET ?page&size&action&result&ip&start_time&end_time(ISO 8601) + adminPasswordUpdate: '/admin/settings/password', // PATCH {old_password,new_password}(轮换 jwt_secret,旧 token 全失效) + adminStorageSwitch: '/admin/storage/switch', // v3 POST {engine}(热切换,健康检查通过才生效) +} as const + +/** 站内文档与 OpenAPI 资源地址(构建时嵌入优先,运行时回退) */ +export function remoteDocUrl(slug: string): string { + return `${API_BASE}/docs/api/${encodeURIComponent(slug)}.md` +} + +export function remoteOpenApiUrl(): string { + return `${API_BASE}/docs/openapi.yaml` +} diff --git a/web/src/api/share.ts b/web/src/api/share.ts new file mode 100644 index 0000000..3af1580 --- /dev/null +++ b/web/src/api/share.ts @@ -0,0 +1,106 @@ +/** 分享相关 API(对齐 t3 契约:POST /share/text/、POST /share/file/、GET /share/metadata、GET /share/select) */ +import { request, requestRaw, uploadWithProgress, ApiError, getToken } from './http' +import { api, paths } from './paths' +import type { ShareFileResult, ShareMetadata, ShareTextResult } from './types' +import { filenameFromDisposition, pick } from '@/utils/format' + +/** 分享文本(对齐参考:表单字段 text / expire_value / expire_style) */ +export function shareText(text: string, expireValue: number, expireStyle: string, code = ''): Promise { + return request(paths.shareText, { + method: 'POST', + form: { text, expire_value: String(expireValue), expire_style: expireStyle, code }, + }) +} + +/** 直传分享文件(multipart),带进度回调 */ +export function shareFile( + file: File, + expireValue: number, + expireStyle: string, + onProgress?: (percent: number) => void, + code = '', +): Promise { + const fd = new FormData() + fd.append('file', file) + fd.append('expire_value', String(expireValue)) + fd.append('expire_style', expireStyle) + if (code) fd.append('code', code) + return uploadWithProgress(paths.shareFile, fd, onProgress) +} + +/** 查询分享元信息(不消耗取件次数) */ +export async function getMetadata(code: string): Promise { + const raw = await request>(paths.shareMetadata, { query: { code } }) + const isText = Boolean( + pick(raw, ['is_text', 'isText']) ?? pick(raw, ['type']) === 'text', + ) + return { + code: String(pick(raw, ['code']) ?? code), + name: String(pick(raw, ['name']) ?? '未命名'), + size: Number(pick(raw, ['size']) ?? 0), + type: isText ? 'text' : 'file', + isText, + createdAt: (pick(raw, ['created_at', 'createdAt']) as string) ?? null, + expiredAt: (pick(raw, ['expired_at', 'expires_at', 'expiredAt', 'expiresAt']) as string) ?? null, + expiredCount: + (pick(raw, ['expired_count', 'expiredCount', 'remaining_downloads', 'remainingDownloads']) as number) ?? + null, + usedCount: Number(pick(raw, ['used_count', 'usedCount']) ?? 0), + remainingDownloads: + (pick(raw, ['remaining_downloads', 'remainingDownloads', 'expired_count', 'expiredCount']) as number) ?? + null, + } +} + +/** 取件:文本 → 返回文本内容(错误统一抛 ApiError) */ +export async function pickupText(code: string): Promise { + const { blob } = await requestRaw(paths.shareSelect, { query: { code } }) + return blob.text() +} + +/** 用 XHR 拉取文件 Blob(带下载进度);文件名优先取 Content-Disposition */ +export function pickupFileWithProgress( + code: string, + onProgress?: (percent: number) => void, +): Promise<{ blob: Blob; filename: string | null }> { + return new Promise((resolve, reject) => { + const url = new URL(api(paths.shareSelect), location.origin) + url.searchParams.set('code', code) + const xhr = new XMLHttpRequest() + xhr.open('GET', url.toString()) + xhr.timeout = 600_000 + xhr.responseType = 'blob' + const token = getToken() + if (token) xhr.setRequestHeader('Authorization', `Bearer ${token}`) + xhr.onprogress = (e) => { + if (e.lengthComputable && onProgress) onProgress(Math.round((e.loaded / e.total) * 100)) + } + xhr.onload = () => { + const ct = xhr.getResponseHeader('content-type') ?? '' + if (ct.includes('application/json')) { + const reader = new FileReader() + reader.onload = () => { + try { + const env = JSON.parse(String(reader.result)) as { code?: number; msg?: string } + reject(new ApiError(env.code ?? xhr.status, env.msg || '取件失败', xhr.status)) + } catch { + reject(new ApiError(xhr.status, '取件失败', xhr.status)) + } + } + reader.readAsText(xhr.response as Blob) + return + } + if (xhr.status >= 200 && xhr.status < 300) { + resolve({ + blob: xhr.response as Blob, + filename: filenameFromDisposition(xhr.getResponseHeader('content-disposition')), + }) + } else { + reject(new ApiError(xhr.status, `取件失败(HTTP ${xhr.status})`, xhr.status)) + } + } + xhr.onerror = () => reject(new ApiError(0, '网络异常,取件失败')) + xhr.ontimeout = () => reject(new ApiError(0, '下载超时,请重试')) + xhr.send() + }) +} diff --git a/web/src/api/types.ts b/web/src/api/types.ts new file mode 100644 index 0000000..6e67be1 --- /dev/null +++ b/web/src/api/types.ts @@ -0,0 +1,135 @@ +/** 前后端数据结构(宽松定义:兼容 camelCase / snake_case 由各 normalizer 归一) */ + +export interface PublicConfig { + name: string + description: string + explain: string + uploadSize: number + allowedFileTypes: string[] + expireStyle: string[] + enableChunk: boolean + openUpload: boolean + notifyTitle: string + notifyContent: string + logoUrl: string + faviconUrl: string +} + +export interface ShareMetadata { + code: string + name: string + size: number + type: 'text' | 'file' + isText: boolean + createdAt: string | null + expiredAt: string | null + /** -1 / null = 不限次数;>0 = 剩余次数 */ + expiredCount: number | null + usedCount: number + remainingDownloads: number | null +} + +export interface ShareTextResult { + code: string +} + +export interface ShareFileResult { + code: string + name: string +} + +export interface ChunkInitResult { + existed?: boolean + upload_id: string + chunk_size: number + total_chunks: number + uploaded_chunks: number[] +} + +export interface ChunkStatusResult { + upload_id: string + file_name?: string + file_size?: number + chunk_size: number + total_chunks: number + uploaded_chunks: number[] + progress?: number +} + +export interface ChunkFinishResult { + code: string + name: string +} + +export interface AdminFileInfo { + id: number + code: string + name: string + suffix: string + size: number + isText: boolean + expiredAt: string | null + /** -1 / null = 永久 */ + expiredCount: number | null + usedCount: number + createdAt: string | null + isExpired: boolean +} + +export interface AdminFileListResult { + page: number + size: number + total: number + data: AdminFileInfo[] +} + +export interface AdminFileUpdatePayload { + id: number + code?: string + prefix?: string + suffix?: string + expired_at?: string + expired_count?: number +} + +export interface AdminConfig { + /** 直接透传后端 KV(键名以 /admin/config/get 实际返回为准,宽松处理) */ + [key: string]: unknown +} + +export type AuditAction = 'upload' | 'download' | string +export type AuditResult = 'success' | 'failed' | 'denied' | string + +export interface AuditLogEntry { + id: number + action: AuditAction + result: AuditResult + fileCode: string + fileName: string + sizeBytes: number | null + transferredBytes: number | null + ip: string + userAgent: string + deviceOs: string + deviceBrowser: string + deviceType: string + actor: string + errorMsg: string + durationMs: number | null + createdAt: string | null +} + +export interface AuditListResult { + page: number + size: number + total: number + data: AuditLogEntry[] +} + +export interface AdminLoginResult { + token: string + token_type?: string + expires_in?: number + expires_at?: number + username?: string +} diff --git a/web/src/assets/brand/favicon.png b/web/src/assets/brand/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..43b873ab823e6a9e4c9e91b5883e74f78b3d0c78 GIT binary patch literal 61232 zcmdqIi9gie7eB64Dj^Bki=?uJgcwUip%5|leaU3c3??RJ-(?qL-?y(Kidu=wU3|DR0HrgIW_Q6HTDpuP)boW75o)){y^QRPRWNIiCED}N?) z`Q7XD5AM3$;43egck4X&%%ZDCSr5OudhChZFO%`%Zo+$GX0qVx^|VxgBL9E%6zrm>uOdhX$sZ z=RYirci6t4ssDNBSo$9_?|)^MgqEm?pk% zGce86@_?y|t(FUnD1RfXy$2(2GzD14OmAfdm`tnxu>WBi*S_pP#Z*)!4=U*(0{?zy z-sA8H1wZZ^yh5gyuNd&Ah6v9Ozx~JJFXkD$26mgwcbxPYu=bn6P{}DKoWN-$t17MzI^p* z_m^H4#*WrT#WzL8)_htsJm_0q3r#4QuKf8dgG}`)ZXM-l_;GQ$*XkPY67}x5wJclsW(n&wv(wZ}?LlzfeYeP1kS>|zr{)u_ZITiG;{MXyxt{(C~}cZaLnFZ1>pL`(NwbT-O7?t=0mB; z<|u64?so=jI$<)!X!Wi))p;_LhId^SN%=msPu9V8PTKRhgpMLVt4Q&GM5(X5w}ofx zEVs%8zz~$(hT%b*|8(ibSI{zfT2tc59Oo0Q>-b@86W@ z_`aZwBfvU*nE8GCyc^1;>)EOZx^yGfn2k(udv@7jTLU{;>%kwYOb|?w0JJx6EA{W{ zEu|;VN7=Z|&s09y9hk3q*Yzw3WkU<-I@92SZym8?AQRBow(Zq3I}r`*)b?7#B;z^= zCqKfyLxp3F#mLi|VuWKR)9P}k1!TB|meQyS?Ge+uNsHyeVbwqPp9bt&$Fji$*uOSk z=Xotn#Rl!4ulK>@#H=T-lNlu1kk+x`Y*2xj3K#sdqjYp z29^yfbfSLxEHvMX7Al}_R2+-Renn|`-ycjjLy`5?J1`5(JD8r36uHkiIX{NFhD(^Xri2Wh z3N8VfnT)iwggzk$ucla20vjF>3|l;!-M0SBp7Mr(D99OL?^ zF#;%18|cqZJ<-cl>ZT&Gj$F(5r*&d3?0cOVX}umjv4P}gSW2&8O(dNcB#Zd|m<>8W zk|s;?7cXCFD?$kfT#*E00 zPuT09Yj(sKCEcbP%RlErmG;j@O>x38KCS52U*}izHc>rdH6qp)`pev^)5)Hpy*{+%{A7# zY2f@y{|B`|-xTPWd3-&Iu}t_*x5602Y!>Oy^pBZ*hY+#lR0{&zkn8;%{v53j0K0$U z{~yPYk~wXImKsEjdK6`)z1gd?nb7}0Wi&+Fu0Izr(634cOg`FgiTMwn@@ly1h8eIy zV@v-08?OJ{tX`Z{nf7mNlyy=J&I0=9L>2&0K3@R0(2HR%olk#G_G6u9=e4xiP5`o^ z9_opWkxcUbJ*>$^K!eDHZfP#5v;PG`{^aT@_GhwXDD`Ld#J7}+|5x2gxgTUJ0QW#h z{Bxyr3&@de7r3s%S-kR+?nct=>wl4yf6LGxP@dG*v3g39`=9vt?q|gB$-Y=o1={{X z-!anhS>*rB0!)|&S5?@FS6@1^Jq!GQqw{2M9P4KVgB~5T{0~sw{e{@YFCx%ZTD0JD?>@`g> z%fpha|1f$#``t%^`U%sT&G;vTT}|+Ry3`B)7V|n*gP}aEx9nAEb`l&jKx9Y-44uwS z=PLnsvsb1Gn#%mgLtuLK2T59&Ze19Nx>fN?z?6ymqYcK%HEdwf^v5M=l6d&?hpKP@L7k}e78{hVtYGhAyN z0vtvl&D9MSn2(-2|%LbJR)-3spZ>3JP?c`#-zbVoYZfavipjCS(3zOn(}^a za0qaM)-Zyn17`0&ygEA-N=oAb7C(Ah`v#1a2A@DNnk#eGMmuWWgYLV|TRr5B70SEh z6>ya+O24g1F=tn&zN6A}T+T+%_~FxU-B(tSWM?c3uCs0@}tIO6F26?tIov#GAk(=FPWw9uqRbdNxw@O0fCPy!!`ir z8fxMB2PJdo7SAV10fObf(3iFRVG;5{+2(tM7W;o6?r=uTJrJi?J?vl4c=P%wGYI8D z8xr#uMio1E8$|b|gMg+AadMCCof z+8XbRa-?ktR0SNKIDZ@cI-%yT;cc${{WT}_XMeule;2;@FB+FK;rT!3#f$_Dc^d*l zJiFrmvi#j>2=Q+``FMT2PhsDA3;u>M0z{9kIy~U2iZvdWW|8l#)6a(+dsDGZR)TFZ zJsY#Z`Ov2tp6}89mHJEc$+i_fABcNZtmp0G#4oKSkbo-Pm0c;Fx|v?7RZbyNg2#zG z6bY==jO_i*2;%9e{Xoaa^xJd>DcKNsHH(%S0OiyGDs&n0TV9~sYQmZv2Oy)y*`xZ0 zR>hk<n|}$go@cg;$$$5TX>_V|2E6Wol6vWLRO*+E)&*q7 z$@2<=%gP~P;aNHDHcSr?Sn<7dxV}Q0T=P=b+Y#m}(Sadc>#Ra^_eh!571XoeW_vmS z>xae=q^yR3P^p!}+g1~c5*$oF#f>~1KoidokI)oPZu?=Th4`^d`;A$Hc)N zd2Q#tlvOb+OnkWpxB2}1*sn3F;;60#Ui?7ASXT3#<{D!cTqTSXdk9pnpDxV)ILQUJ z`|Yr>YSB$&#g14>&EBgWqadA=c;d(FqOO{f7IBmT4a!$BReCfx*8vqj4qcTd-`O=O zPcyK&r5UZifn~=_reV4g4VS2eE0yRU{1!t8T-C1FRv49}QtWV&5@;>m1zZgLha4=@ zEx_VO!IBGAw)qF8r5U)iHMH~nlfj1|2r1fYE?E0OW7X~>V4FS~5(8!y#fJd4`X|KXybWBgA z1v24v`Hx!@Vc|D7)>t1HH9skpHGmERkfE*_^Z1L;wsnYLnG?mE5;=s_4GQcU0@?h`Lun)$m|+5r9CZNGe#ZGmJv>rXguMG-?) zS%##HK323nYY0@^h)!i_t$IF!0zL&$|4`;2lrFSZNq%^}cMDkg%c>?8^XCea@CxUH zx!cFwZyaxyq72G|xNa=r{qtREZHoV{vMIdRbKF568@F{Fx9=^G10A~vDE*0{N$x|o za|w}JrAP@tMxcmMb;5Z;x?=m84nLK@Aw1TotnC@=TKT7#FwD6v9^_k-XrP|^xp@L) zx4~$OI>KblbDWdZEg^NoQpt9RtMb*XFzu#^cMIeo_)SZFk`c>U@{|Rlo;o&V0UxP$P* z=Tmuc*2NuX^LG1CPM6DCfTo{zW=6F>(BjX45b^j*EC);WwN64&G|EwH>yl@*Hj) z2iknIio64WGQ0i2Et1IIgUEEtc82SrH19p^|M6nsj9r&^z3~#YoYngh!}-)7tsX$r z%qXzq!snd458+F6eS;dtT`x|TbJp5a57n`YK(jzIq2aTC0_@^8?+P>YNzh<)t2Ypl z_X7Qcx$axxgC$L9|AsK8#jK}39$aC4V0EjqaDX1(L3ggAixD_F9Cz*?hCWrf+JgIB zHAP);5=R?KxVAh$!10(MkS?uqRk{P}70!`_(S+d08s({ z^w=qBmA}&KEglu$pQn&A-R|zI26kKw*e+YYy1WseTBO{f=v1#aC$rO4a%_d~sS)|w zQqK?e;F=Bu)^p0gSGTBvw|(JvY0!z}7#*Y}pyA{l`H{Ypn{JDkPy`cQJ zB~QqHg4s9vDGf1y0DwHT68y-!_7|3%(9)6 z{=CGxlQUWj9!$w|xR(0kgkDJY_w(C{8)bQ}0I?9w1?v4bVm2pHT9eZvDbs&YPjf!n znB6_2vX}omBl30P>6Z@X9wBlIT002;kdnt=AKCT_SqPo)E_v@)q)vx;I_u) z3)BH8GgzMUJpR*^KTZ&1edlCwj)dWwx714!;G-Wr%X)jTo>HLO`KfS)Rj9GX?XKyP z1;&E8k;gySi+>yFxP06SVOps#It$vPy(O!XO3%jui&&yFyp3odX8+p4e_i0?kGy^m z&=CTZ(djQ+622O?g!u6s`BvCrj%;+aeKSKIZ za*LjqtRScF(1~e7>^S0{>9a~S}qaZ zNMr@Vf<|?whaOgY{>RMF_i|}3zYf=5TmzWHFa8sftg(}Yf9RHF|8Nbg#ekT^gIX?p zt`~*^MQy~TadD%%M*x&l=NA=Z9#Q?6S&)C!QZhsUx>%zJg8`;J2-!!;a@`$C;=->< z7KWR4>ujZ`1kUGfCzfpGh{yYg)1euf8dga8^CyW2i83C~Pp+Q+rXq9oRf)CVZ_%GU zmY7|pTb2_-YS=ge)#~#71icHft>4;>WrW&`;UcSC1E%zs6UK)w@ukONh}TvzH>Tea zYT^@Uje6+-E%71ER>hGRhJhLYOj)QuxIAFnUwMuOV5L4RRV;^v3FW{oxJGSg zx|XG1d41+NDgV>M)a)^H%??LHPDy;d>BZPq_?pKv)JmX4_;OjaY(Tpr8J)Awc++zf z@^9z5SZ@t%EJXO{2tTE}r0kYfv;FRecqP!m8}4b(>Qp(Ob(k%e_$^MZ<}iD5Z;GQw z+Bz?<6eu?~&U|r9)H?!O8dBXHTanAqkQRxGK44o%S+-815s%NVARZy4ne~~Py00xq z9DS)bcWU44DP@*sfJsQlwu%9a_Yc0*bA?DNt+}-QsjcP5>22Uc1~I5B=MTV%etc*ZKN=CFWXsd^rA|uKW_7SQ zRlVWU*ptAH8kOE#;9Q`w@HBRY7YL|Vf8pOG2S4spA@1)#US$W8K7E<`m9S-@?Z{<1 zmo2NY(5{*2PhU4S-iAHtWy%*y6P;k!bWNC_w`KYR!)-|2GWOD_8)gx@ln)1DiQ+2*kX7F;KmxTOQDg`#zLQFgB_-Qco8K;Bws|#m!X+d9F^`Crwi5{a1i@^ zo>;_Z)1I>c>bnG!yGL;+DU+SuZYJ_H<=&I4<-hd)Zs3xfDJb3#a`iNjLdwU=ZmFE8 zma2nAsi>rt-4eFE*yK4i9#0QvED70`R@sHIB8Zn)*fpsrKF$7X{BeYAEzt79zU@%c z;x0%%iuiC}86p3my04U(qU!*-r5rIX()AmTr=S6Dmbed2tH9yO_UAII@IJrt>%EJY zwjME?hHO<1rkm7a%DohiVklMDvmlGBS=I<9FrhRDF?vvF+OxLG@kq#dv}k3eGJ>>e zdlKi_AL3T;qSs}&u7*ojz~%s^KD>SzLO2O=@jnT@iZgeOqPu^nH{s?BQ~5hc+flc0 zisqs^3E48hrd|2#e!qSK7RD+en?ByMp6_NM;Wx>+z$G3bTBh%6V!Fv+U+XLcWH|TC zS1;3qk6?IS9?Mn?yq~&zrS*RPO0Bcs~5G}97Qur;|f97)DPG3Sk ze61hrQ!bzKhzi&TZKr(a4CAYMG8)TV0|Rn1iLm?i@RO|i$Z~hgX?x^N3&QbBW`>Lt z2KFb?1;zs1SlRbOhltU-k+NHc9%sP4;;Z|$A|xQ=mtI)bsV3kAB2>zyJhAYcHk*?> zi+y>~fn1@~AF*jH%YQU?U@*Thv_?6Tk7Jk1=7Y{kz0Z5I>Ta!*3&izMY#*Asl{y_<*|pR9p|`{91Q+DXRDKpZJQW~3dRvqma*S|@O1 z%z*yld2ktSKZj{~a||CGSWf``)|w@)(Q1x6mW7Lx{#@oi*bko7a~;RF+60ClDjwnm zE)Vf{pvt>j&J2ubQK_iXQIt9$O=rD}%Z0KB}&(Vz+O&w6f0(Wh}`ql6!V& zU7!#ixFZQjw3g+C1yNrQHSv28ut4`)2s&w8-`L75x~D7bRR8Vi&BZp0&>Uh}Kpv{)@ctVH|L0u*s~K*SU~{}fB;AsBm_*W7HJAMG%od%!{WitmsrAb&Tw4!1OYZGp z^w@auIDJ@J=2@+9Ec(@6Zg}B~eDq=ECFZyA-bEd9u)(pcEVmmMWtiCxS$96&R zQ35?(X%A0xRC#suG8Bm28=9(X&6t>e08*#2KK9$n~fjJ@7r4p!q$ zL4W;S^_}jYeN`5i18#GlBXTCNqcR(E%owWP)6?+QtR>`B#;uWR4$H+LaJv+1w1998O^VKxxTYua6>Cqo!5z2y*Y)XULUn5Q_jQPrJzlXPd10S8K| z*tP(oeeYVD#fj2hozU}1kNT`1o;(2<=HcUxXD^RDu6HFNc3MIXRfthvje)QpRr>Z- zcAj~-Wtn;<@M)_W^Luh(L`|^7HPu)7wGGifsWEi?`0S{J!+i1EoU@O(<%grZ3&%G} zG|i~y2?I-eD>hHPHcHR7dOXxG+`FR7Ptu&$=3~g#d-4(U$rHGE&P8q6n0AAgK7UVU ze%_2it_R%`PN#`jx%~SH)Ni+~qU*OG{cz+_Yo|uL^ar-zaY}JF^0EPD8c@F-&y__e z&ZCSdqaOgg^sto0V>M&t>vAVTZ2yB_xOrM8+?ovjn4E%WvFVzj0Xw~JpRYvmJTCMP z*HQ#V4oEiH#B!93O-Tu2k%{Kk<4r$QqmZHn`TZ{XjS~wIdUjcd= z1Mq4b9bE6~`@zwS+H$F(qoi0xddRx2WQr7XEkrgm3V+;b1GbK5AGocdbp<@lB6qe5 zJkD8pC>~j_qiP0+(lM%DZJm5gcX;@{1zyWFb4w$1XGy=PS@iVHJ_aogN(J;fa!|;7 zCnIJ1*q+D|X;y0g2F@ijKLx~7M`p_DLJPoq;sDyPimrTBml6q!6Fs0kwu0I&sG?uI z2*`GKnmn)sooO!95MZ7b^|V;2>W8Lac$`Ew-a3*2NGwWIZ5^M8PtO5OSC7`C?Iv?j z8y7UxWX5l&J_5Dp)qB|9m@(6_#|B%3nsy%)O4oasG(|S?e}#Fq9ibcVSSCnh=S8fs zqvRusnBi9V$zCtMaJP1j-Q(JVUQT}89k=kw_%cNNS{nsL-(P-%1PLppLq*3~X{ouW z(~HsqM*VkbrTzx&rVzJ8MIM)Lx{a1wMsmHtWCdq%ZTW?D^$b>yxwpTK1TP&&aJ;ec zb>ZPm{E}00lp=eL0(GPOI-zD`ni=wYu6y@xQM6A zp>`To2n?%STq@q{=_de6A7StQpvBR0pHa4PScrC$^~4p5^dFvzM=gh~M*;{`iRTcG zhD~fPXivC;kZn@cKBh8{cd1zHm}-)4fgArvkpIS?lf~KpKfN6UUEj^+Ed=)st_dX9P;Sb3R|$h|HLX~ z5jwHNE`4}taCrDwqLH37TcK?n`7~2GQlhxaI+DvJC7G5G2#izv8t1r?A<-d;Qz7lv z3z~zrpr-8yg~G-B%|WMpPT&>=qwKU{E`O9qXDfKee|O%4NBJk4NHZ~fS;*vQOyfaX zPgE+a>D_$`Q>A$fmj%Qm%%klH(?m}a8FjP%NN)*I9DS*@Q8`ki#hPB`bR9--jc+gs zp1AAD#h4Xmn+xt~#AyLS`0o6`k z&!)E9k8$iY>M|>k`x#&bbFEM>L&vgJ7_di#FnCv!_E7MKn^huE0R zX}a~cZ*t?S)^!3OM@wXK)U?;#Kwy0NGUtg`3O0Ui=2|8LkJQ>1-Xj5}G{AJ8 z+y<|W(#AdJlD3tqP<6`t0i*b;2P@mWwU2HG9*B3b4Ye%L5WG0lXQ-O!1y`;>Hr(%B zEmbaH5mE5u_G-EKSA(683P-08mt+8HQ|PYGL!I&iUWKvWUAO`XY&3s0lS+z` zR8)rcQfLR8hBld#wC93K$F}_2%r(Gif0cLCh$sEgl9Pq6eRS;VHvM*4oWS0tMAq_& zOqJts5?Tw~0F4wMmgD%leH)wS;es_9L^i1;?;cKCR_ z&)Z7*h+wMmZ$6cLtpta{0k$V5Be&h5No~vPEex79FgraUdCeZg)~j%$`*>?C`#4Q; z3R0J+T;Qw2!4vdrYRDj6R7avP$Rdw4!!SN?)3x=C@;YWFnCE2UOg#7f+0WCvtTl6K zf$m+Sq4uZOF1l1qJtfi*r-ep#(SO8BOMU-#9aLJD!OX%7qDv zUlw;<*7sTl6&v%wQ-Uo+)4k+k->)=Ap`Zm0>!nD00Z>*EO(@q*>&SD?Gt$L?MYeI~c%a~ZB<3_Q7yKs1k3 z8r7hs4i0LE*G!d;=}O;aPaL5++zSmK6yKB(1w!rK%C`TK)7r2YV#}Pm`6bH)cseJewdUGcty^8fv87y{dir8q5dMeFx4TlJ zaNd0XXiuriA>tCbL<6E5O+1;n-F#nqBD|GBZ^DcFDEne(qX>6q!pWl8s|YVqkas*^ zVbcWL*r@;r!9=TJwX%tElF(pf?&cJI{PVdl-^00$0*<&@irt&Ye4og=pZVX=?*vRB z+HCY|2{=swY}@P$87*%#iJ;m7O3$9=py_28lbxrq{D`_U4|@TD%bH)If6C%PP?T7CRPc(Eo662NnJy*^rHfL|Djx=z$I_Pa zsSJB2P{KEfrb}86CaLr_7J^%E93Z>!G>%4`gPG>IT(B47!ENpr< z*Fx~>*;=q&Z4V~`9=<&Pj-z+5ade}Wh?uMVWX@|;DkAA|IryV_o{!F>p*}C+T*6mL)yMt=O zEXNQO&ON1d=x!Dh^oKISOH-)KxvL7+z%x}c>}C^Lr?{lOhibhyqeTjToF_G~8ezYP zV|+{-%FvaB&N>1yCC;o$R`KB0FJsDk7q1X738qxbfzt0AUs@y!?jlWKIucfqfh+Vw zrLn05<>hdK22`bLUajnlS7cDUi?Qcy7CRGO_o@#}=d57oBlgjZmhqK#)gr4%k=4Wd z8$nM5&{1og>-mz=SdWl4+>FTDVujn=+B-f39j|-^kG54e zYy6Z~VE+mt3M6C`D4mIsjQyKbF*5t7{bj*={$5E=m#=^(vpWO*nl~*q0eH$m?_^+R z=uu*CvBDm577tp_XFw(%J%Q;o^)ZYoZC$VKRZs%01{bEefD64f0e3AhN-K6D6;R}el1vKUl0`pLN zA~9O-{_=W@SctIV5{7$hVvM32wP()NdN(PLI)sthaXragjZ^gN8OxF_$AiG~iF%pN%E zqWyEP{1((I(?U+$eHK17f9cG*Ra%Hff-zf^`plib?W1fUkrH*_^~v?bBly{YdQ97A zAffSBZ1ad`;-qxfVq_5J#%o&MO?-v36BOI+%vSgY&bpX)Ez-Gj)@SuE*J4s3DT#Fl zd+k+_?1|uhDgHvv8<=y3<*xw;SF6}oOtEtuS$`jCFC1;m6Q#s1u@+H)a2GZ{xUM^( z09S8%{T^0jkVQU38!YWjpg#6otI>P;Q?|eHuFmRk9kt#v=8;7&Bt*u2O=PVII43xu z<3Ea?vrd(3ADxYGXrBqRm}IvnuK&ARJ;g zjRx@pEt3-qg)gHMcZWDfO6d_x#pCyJ=U+k{`{~5zIR{R^5selfuiLa&HPL7$=y&bx z=#O@+yo+hU8va2>#>b3=WgdB}Eb&ujeIv*-M1d zO$V@j;>!h5x2dCsG@+W&I?T8dBcx0|h-;X)s9*ne$e3w=^zi6~-Ueq*fwIa2c@1^787!=}m zeasOC6hc-LHwx9+M}I{W)(){llh{Yk@ssQy&_-5hco)c3PjTs`JcX$r`;jGNu4?J& zAa1yq%VbXEMCSAiagH$w?#tYH9Sqb*c9De&)1wys1{=vthASNw22hir)qb~Tky(Cp zt|qU=qmH)C$AL}vg7y-R#QDWvl7w%?zkcBpN0iBQ#Zgo$OFl3*>VL!6hYx0pWXSWI zZZWA6991LPN7|$b&I#Q{N5nH~t(igY5};r|i4^@=&N0VmBy(o6)Fk2cI!FWRV4l>z z9R)a(L2?_C!H=)BdG_I}H?@Y3v!;IRJUrQq)d1cm( zYEv5u5VE|p#E3$<{35|pt+h*ijgX^Bh*OzrgPrKhdNMtrW7tvyLs$LI|tB z-+s{bn)m?MLF+(eFOT3GcyKfiDZqG*3*Da&6>zA>@TE$#<^pR}5ru)}qp3J4wFf~u9AyxZf=3-sD$we4cgK#3M4 zXCUgf5K<%ozyLG+O~+SQ1{;>l10e-pY)Dr^q;gFZIlh&F3Y(Pnz!P?kRsHXF@@4g& z9VhJ7C4V)x9rW8%-N6-$ z$*Zq$5k+T0IZPc~`CusD^yjrQ5VEO?ep%Y(&O@No&7_A4)LURL&cW>5Mn-JwEmophKMSwn&mA21 z_&mi&yBbXz)A}~?tQ&p5$hZ!-dSO>b+5zY}tZfm^v5O{O>i@cp#*yk{42t+m9gV$W z^83NmW54-&evp!RV3gQ&nDK{&qzo2wzX@e5p_c`9&`l#@#*+~F? zJY0E=Nvi4kvRlU}aGIngv18jxUqBglbqv>Bsx?k0zj4Bz7vKOufg?0|kH{V*o!UeouaXyaRc)vmun3g&B+96HAgUmYXM ziCTIB(tp@SnkriO?Yn?OpKa-Lc4R|g(>QR4V1hE;LTD?_1ZOp~z5ts3EZXx{d%Kyu z(2pF)=j_#?k@Vv|zWlk4F~%`DR_@=&~C4wqJ_&I z*bfeePp6>C8+xE!UDI`|y)9r`1-E0Mryd@Hk0NXZ=IqrS>92^Jd0Ek+ZzP3RxY0Z) z3plJ`DZ{vQjD|7Nenp>sdPu!<%RH6cicZ>c)dpLQ-E0?bWADC;W+`SXt@Fwu-lJzO zdFx|C{KTKzJa84kt_2@d+ItZ0{4ATl)Teyz^^$wXm?`Jku~lRcrRdb+Dx?Eq%kKUi zcoCvv8S|&FKn$7TsJU)6HP5LRSoAclQ`9M%)YM)LSb6#q>O?=@=gW_5dIt4;BBLjB ztcdlO2TN0x)*TsP!*L3<}^KVKO2m$?3(C3YZUUmP1RL)&h{B8m6-M2xV)c`ATH}7-ifaJ8LwkHa@l(> z{^uNIWe9l1wnz}!)SlFB!%vC;z)-f8Ii`299WnoHd!Au+!)ij224?_8J+3=WL%4U2 zVME|gk+T;5^*t;YCc&TDe$U;0se>0cQ2|R~Me{Mn6iTT}w~^ij{RTAZ34`vuqlWwk zGsh`C{o7VPz$xU>^u|a$d;e?Cg1VJ>A2PxisEBy0CW0~Gri~Qi54@rqRjaK9g1NE- zj2O;1V`lN7+MYr2B?h^>Qj4p$##eSDMvBwmC7bHab<_5~Cfou@N?Y#Z^VsSs=PADM zC$>gS+IznkcB3f)!*htq?z|OpPs3kY&oS!FJC23wk0OC>&cEX&p5z(FNGJjoG_}WR zo>}2wExPRaIMi)h8EO0D1~n`EEKvRff{PUdo}~s8U+ZzhmtyVjmnvH!Y_?85x+8RW zh{=FtX+g9RTLgcG$vyb5;GJTSSLJzoU0T>6`NWmJ_9}ot21k z??{7RRJl}cU80zq8;D|K+KQJznj)YkEu{uM_8hUh=3eW7oz{5PaqidVjFGD;A0`w8 zw!N@v?%2q?^fYmlc}ozS@{A~~iKxK0DnPaM9&CdR>Btw$m~;D#ft5`j2|;gkpM;_A z*&30QDR=N%dNSKqf&M0t#V}hw-rtKRw%qV~zGMd%u%B3|kFi&HA}4k_m0TGawRr&! z&yaG(9j8PM)({xGb1c@-&R>_ke_cyLD#&)R0#ajMcOz+{{)YaRO2BQZJZ;MT8X(J9 zkC%!?51p=vb%?2?2EWgD7pOE?#(#FN2io9b$olE^CZU?Frvl$oN5`cY^q=qKUUYR0 zmHhq=_&TH?vGFm`ItZf7vBGCx+BySH;_)#~4JYRgF~ig?Vk#X`8%rf$!&ix^k>9xP z9`~i++{r3U!)Er$HCt{mLB#jqrvpFotj0_EqhFm^{z!fqf_^G0M^}uA>%OL+?xaow zSgT?9c#KC3v$mM@z1((6G|(Eqc!wUd4D3%4$5$!K6_^^v|FFJUL*O-8x?FpKV6;#9 zf%!{zvwO$eL0sQ+_Gz_XQqG-~X$i*Y8a(?1x@1#LQma;~yYIwWM+UtdV%vgKw`1-- zx|Ywi)-%e$X4QNgQOq7%l3`(3Du$G@@|BT!FS(eMrba4PV?Vut$+p7(*pcBAT^x43 zmU7Q!LjB|yH`*!asLxjnIg7cIe$TB_EG-W7Qz;ey{TXKH99J?cvc zBwa9iR`~e##b4$Qz@aFXG(?=kI|LKon>BoJd{t-;d;_4yYFW^shnQ00TDRjpDlz8z z8p5Sf0tnjgIbF}iLj~Y3OI)w&Sgc4HQtw>V>XLe4FA!zKBX z%gQ!>Q<%Er{1r^`^Rueb}HeL3`rY(XM@XU_iwNN zoOqb`eZ^kQipqNbiq@Hw-76cDXZuOtle$g#;LcazOMvo+lCQ4v-@usE@K#DpHdDnU z%_n;&pmWQwoek!yEQ{ITZ{+(CdI_Ar z)onJS=6RHw@Bl-sGIR3Z!p}K!y~9+A`Dg4^KuXGZY5sgrX&F*++_rPw&6E51=C-Tk zBA^&z^zyX6#&sA#lqL#is}ala89hHgw$wed9%6)lEEXxx+pY^eBq>bZp#m_Z!Hs%6 zEe}bt#Gb{1P=4O706;Uwm9~UW+IQDut9iv>*ygjZ*mGhb&)9YDu15uT1Q0ju{O#0i zLL6T1ira{%dkFO=^)Fik&!yVw272#k7SveT^q+51n7m6>vem!%r}Lq`nvJp*-9vLU zyG-0Y|G1!wsTYr0NA1+CHj0mQ$6Y<28*;n37>a6tC6S76IRy7t>0@?7{mY3Pwmw$0 zjmd{cKQzW=2EJjncr;8f_YN+@rkPIAG{7tk(_=Wwmn ztb?YtcuN*npNDT|g-WJ27)VjZwoNEqp*8G}_e)=VUVF<8qec+?{=^HB-59ixzT$~KKx7Y6F zpj@R=r-2}|@!hb1_P>Ror8T!AmHr-oLRr$@Sg9R~>#K%sU>z)z;8UHVz_&(q(3cFl zDxA_^-ceIC^8P>oFDoBo99Eh`{)&lg`;A*I+aIR2h-A78xG4g29C40;jIfUBPSMJW zmcYa4Qyt#JbA=g#NnRNzoVSUIUw{QgA67)gyGGco=?vfmv^%LaH)gOB=#;oq0+ zUge&B{K2@gJmM1}o-{Y~Bwm*Lcw55ms%8{UyPIE-I3aQ?g=UW9y*j! zNl$-Z_CAGqAA8k3u{%#cBu#aSG52x4Xl(}KKi9KdCfg18n`sGfZqe_FnSadNGS$U!(ac%Ca%m|=`DKJ$C! z7Nx$Cqn0q6zJgKXB6EL{+x6~E_s`^hV!X-OJrx4@h%e9Uwl=|IFUO&$O6XXOfo@RY zXs@QczpEqiYM#NgU{8{J4WgYdsb1qF#`@KH`HNGX!_zqva^@KJyJFjqe~VT}SVLa| z%xlQ0t2F1O7K9w6#Accr@uX%naPA&i)!Thj->u>uVW^N3cKHeb=mh|8s!`t=v*I)< zo7bamfsHxmoNUlFEV5FqF)2z_(zA1i)fGTxSS|q+&AR~aI@ zjSYO&I@e@d8(}Rwlibf9OVSS>VVT%@iz5cdjCo|Sk1na(AoT|7&q=b-7RSu*LnGqK zA$mjpeLs`cHxfWWe%@}kg_7TCj()9!Sf4ek=5abg3dEwl6%0ikx=&M-I;84UX0#ad zEaX0x*v^|Bv-61|ig#|>q~CC`eD}(0mZ4@qfuIDXNv*$DgLoYfh5YjU!-`SzP4SFa z%zF;CJnM;ae;6!Wb5E=cQTNr!&*9hLR{ytG-^4yq z?dwnyt#034Hj~+XoLu_sl^&z+o5sf|toU~bjV6D|;tCAw{Tjm2_wlT5`g6>Rnv~#V zsU`AsD;Q~Oa#YTpRIk5h`&i26@Z8O6Zl6aL@&gP{27KLQwm&x14=<}{!>#FVo1d{i z<{j}#zG(x|9>&++6PxHOg~crD!rnlC?p;x@`e%YbNnsdC`m7N}IbxpSVmP2bc7Ojr95y#Cq ziZtN!)s?j1{CBhlUz0upoA#Az0s#>PnGwog>F`HSJ{Wn5JT(U#NfwY1 z75^o^Tpc`0n)i~Q33lMX^kcpLKdQbuuBzqz`Urx7pppU-lF}e09n#$;p&%hhcMC^A zx)f=U?vUxA`?>#Ib@rYo)>_XqGn+vnqDGNC?-O{6Y{&|F ze`43C4+E71mMLEwC4R$jv7_MKJqs3U@%g5L73v--N#v`ai%nK%sv~kEzg{xGp><%7 zYc4hwJP%%yUGUFtSzSmd{(aG$;Q2J?M;p3hRao|RoZFk~=Y;msX&uRD!t}ou(@mMZ^S~^n&Vr7Sn_bVD$;^2I5?-}<21O;p<8}`k zR%dTkg=ZVdUm5;lHujq7qw`=EyZpM_lH3x<5{G5Fy=A&7)j3;F9{Z;4H=UExPgcog z_Bfr7w$nYC5Q04WYooiBwbqJIGDI6o{OAJQBL&!fqtPwXqrsERNP6fe)_Mxqp8$RB zhaR@Y(d$&V{piSK-S=W`@zb>^u!lrW)>}Q0mB%m1S7FL5MBUJQ!$A}q(Tu4_H)Lz9 z6+jH=xqra5?@{Q^=SH3K^ka27BGf`Dad?B|&h}WAtg3Y}UjF5?3*(CnOp;2>PPJ3? z;cUkQT~%ZYi^*!y!3o`~BzSfCfz|57O>SEAR7OwOjilvg%;4jEA#Xy(5KG+B3kza- zvM9rut9gj=h7?o({1gg)2jfz+)rC^+1CPYKtI9E z6`5Uf zh?gA8iAOl~P6>%!>Ts@y*p}1n+~+)r_#*>?2_6p|2^vH|# z*n}>Qe)j&#_-Od9Mk0>ER>!Qwi8}PG7B-i?2T1`^@WRc5;_tJ`d7!!u+rDU*)HJN& ziRN$D|Exc(cAms%;B^V8|XD-r+uh+wJ*{XYIaKA2eWY%1)a zQK0Tn&`h6M8+anJkG-!bk94%-o2pY_%0(z8#x8MXVkXiLF=B_Tot>l@K1(%D%>bE+ zof(wz4%8+3KJq9v@kR6V=9l@uop*KCT2}fP0pZBf#a<4qvFWRS=6V<>B%+ul(slOb zOafa4J@>%XT+lhlZH$wXE$+~j9?89QeRSO});OXZXDL{y&u-!GZ?KoB$%qF z3?E9@Eb;pcEyy!uRr7Q5d!$sQdlY*V5ioAMdhP)(j(tuuBWCGgH_Ozzpt{^TIX;WT z_~3zPO@v@7<6t_M1%cHpFYud>UXh)igC0OqG@EjD8m&Q41$3e%rb$VE1 zy9vre;oz}^6V?3}1z>TshG^|s2CynH6-4SUQ7utfz9!Kz!^XcK+sW7SC2ytPjmr6k z;~hs8$M+5c{8ih#PR0bqi84)L0F;H==%=aT@C+)jZ_1_Ir~lpSix(8}@5O#O!3pvS z!K%S=6osiv!3mi?PkNs8eoqS`BwO6O$zPsG7w(uyujZQ#l4d&A`lY8AN0KiVQ<653 ze%RKO+?d>Br7>n=j4Q!MYX3*owC2KT>WCoUet1qWA{^{?I>eCFBG0l$zr6~R<5J8j zc@6>J&;`e=deDy!gl1e33Td97Dhd93JNm=aF|O!DbLg!D8O~+yL$$LHFfy|!2D#q* znCwfeeL&pec5H7Ti{+d#my!Y|sHK_NJhypiXojPgGgO)mmUvuz`=x*o^^+tz+%L))U;Ja5dHRBqcil=sjWp}!y^tIJ^V}f zOYfEzt?`Vaa9e{g_Y)X2+@notb`yPrPruFEHeg zN4dwV!kXXvVs&)z9P3A#iZo&nZuu-mml#+OUl(!w<~XZvFJRqV~Y_7xI^foav08UhS>xv2O3r5woxV!qc3+>2+YV(D|h&+0C$Of}V{>vA40({Si0y z`RUy~M|O@_7X{_Cj$7fwyDNpCSbFg|gR;ihRD<~9$JwUeiFPFvy`(~Z&oOcBzwTfM zE_aA#beQC2OmZ}fInXodt0|;YcjZycV&y~vR>{l6vzbMdB*qiJ ztfv}LE!4vT8-CV_@y8a!0AykA@X>RLwSGnIO#|J+M7R)_Vy~d4$0jcFJv^3Y zWL3ifoT)naet;O_d~lWTu{*Xx&Q|dv#=}>}V4onT8)4{CG`BP^0!&V2E!@!REHS9E zWJDr0)$<#eqp&hM`Es)8 zoSN@`g6BHp+&@+H=GQWla?EV36BF;o3X0*7=V1OOnV*QD{eMGbXV3xkL9!_Xaq?XB_<+!fG~RK8skTD%e^iPSk9tby_(r zO^@amJ@qI7Csc~+rx!A|k9fILEDRGS4^75RCkASn#2tkA z*vk#oDPj$*j)HRX6Ap_K6YJL9OsX6hk+xc2-@6$J33WFjoX)J~Gv^2K#>gP{`zb*rVa9-Cu$xer1vQomN94_PIc*)V=bb70;KDlZS@B zBFmE^8zwt_#iuqS#U&o5l{Q^e9aK~0n0ikuMkfaqaL>v!HZQ{$>P9=c>qd!>Fto@$ zlN|eXVv~M`rDi-;erZk8-|H=3qa6N%ei?_fzlwF9h~|J#>TTZ@-@PXxMtdHUhIONO zW51SsYC;!~+X}b~QtRSH(g&H`0{V`91_}mAQC#op4o&=w{kBxl>qh%cbt%uk*}-mYEyMO7TS+1W#iiG8rQ?1j&n71Ug_1#1Ys!S7P-;?WXY3W2-~AnF7ronC z0(CyVx`Z-3ysjr(_djg352NSq*b=B;CVAeS`GK}Bok^aURJh-@q*H29qiq^i*Fw?X zYoMI!`KFcM#yaz?#IAIn^v8?vQGy1~1Y)bivFNwdFX2pz2#cDB*Gc~_(kz zSIpTiFR7j6elk8;uN+S5=$j!-c>Ot_Mc##BHRGeDlia;ruDBnb!fh`&I0iv`{i?*d)6K>v_2dU-TpFCa?&vduJNwdOPFcE&++ffCTh#^S zFm?sE)c)JufNc#E?B8}cqw%|>c~*A`$4xn`s%=PlmnYtm_xcS^#PwDgvJ9JRg2;US ze%|oO%*r90KqTp8dr;GZQCQHbPe|Ob!cO5SF4k(V7n>xIDob@<5Sa1`JpyCM z6PwDavgF+=@daYSBCTl9L*{IT@6c}4A|;UQZO7L{$F!^xgD)|or;5M|k$IsVA%smS zU`tbw(Lnl^hQ4QUvDYxA*k-ADIBD?*klx`bekEe7sr7h?nB110(ai&7s7|*bM}&4S zqIH{fHfz$Pw=F)Y|JQPAKTQnxa`R|!OM(bt8n@o1Kej;J;l*w8yK&&AGTEtp>1{w8 z@-iM0f?9n~oR)50n@ZuC@Fc6ZHl==};lz&Th6wJg4U>Jd^_Zz7@{z1DlEV%=d@@xK6@-FWGuU@}5z2rrjC?`J}f&qy;7$jp$g)Yts6GrtV;`M<|I zmYT*8^DYSoI_LPNbte|zOwtjNF|aKQI%0ofAo!M`+mGRAX#|jppG-a*;aWbE`mXrM znUXib!NQIEnM%Ij5*<@tDQm3IS$D80xNv-!b)$A^ST=fV>$83TvR!mE`BY&vzQK2K zy?~o~q43rjCgo^+vp1bp&s0d03S!!6(P52B+==^|_V?fO$B;m+l8abdlkw__%&hM% zi6PvhM)NmEyx@36@)#bm9Iiq$c9HcjJl4*&Ifa9bj-n_$zks_TW_Aqh^W~-c^iof| zipEcT zC972Lw-y^tDKGWXZOX()%c&1cBYEe2&2hhx^wOL59=YtDQA#Y%t|PcH&`gVuG<7+B z@UbK!>7se&BfD!wX_~(_P+4;+KNC8;AaB`cX|oq)j)8{}6LmT|N~?|C+<0ikW0b(L zK)9y;8%b*0dvEvf#R>DIoXjH0safyQsoE5mkJr83CNEA=x(CyavAz_}5qG|S6YC7? zkKXmR)X52Ob)C1?p|s=;4kMPYg72i6Iu>iorwL?g_AJdWeToYSc$k?7lWh zP1xJE-4#BXlxKzE^4vIs&E!k&i{8;w2)-liC;q;<47D;O}9Ipv?#o0}EK`SE}7DK=^| zdOwssI`(6iL${EdTFmf+U0=9C0>!dIpmv3`h;!1MjW>I@Ou^P+N5v$Bu4Er_T>o8NI=K3BJFM5tX{^C@>DI|RM!)9luM{k!-C_-$Va9l+F!#D2K_Z&}a)Ru3S%V6v7 zWap&Fg_n zb>M>MBdzt!g3H9)4dy)|X;J0#X*-82<*YBZu)X$k>c->@#v+C-ZYH8hu7QNnQ$1mb482iIVOE8Y(>6rR* z0aNs?h27(YSmcw07e|;x#G$^0%B7^3Crw=w{IR~jQtJ($Et+l~)tz|@@9|4)Z~q$sFBXGdL6EnT5La2ZMVk=N|Ls7kIKH@H_;|F7VLE&s8O5<+^y0|Ca!ruZ zMT@#cIcb+vyIxq-;r%z6&XAUPo~O4>&SfT_^o+8+1iA~Fx27T57>5k+A+xh{g*}g` zt93dbWu6u*b4u=L3wvxHeZ9M@X`*%ZYHi_mOiN}g&r``0dOKBL7V3)HEN*eGdU-wJ znehtx@gAco~<6{%m24S(Ln9~4Q9G~4UF)R=eXQ+dKa<7EV9y^ZBmhi>IC#2;ka*@_>V>}2m( zQSs3Xy+xn}2A55xKgZyQ#xBc_L(0;j z84G^ftN}p_Nuv7)Sw-VsrW2OcU)RaY3muOUB|Z;+wUn6^9W4_xrAiCCHq4~<;WrB} zE#JJqF60w4C&Vb^em&${eE1j`bCY>s${@X0VE7o^eeBN5F)mwr(esOlUcYB!5W|3< z;_`uekyArXk86>_(_YLyhC>VoehTIN!@(6T@&g(lbta0mnDY6C{4tnuF5i##YwFEU z4;CT~CTvopy1PES*y}C+I)iKZHDsm(zq;(UjDB1B%qsV84qy|y0Osd!C;NT5TKhQ6 z10AehNrz?|M-`8}et6~M=#YkkBso9s{9gCgYE(^X-856mNz_4_aQkt$FVT+(<#vN- z>F=Bq7l@fI$bHIVn~elxsuRuWO1*S;S$rfXEkA`X z9y*guxCmj=x31i*;v=~=$qK6H+%?&I^@^P~XV&~PXU>T&%d%-Rtdfof-z-ymjJPxV zcw4uu!0NVWY5j;^W9Y2T<(zOSg@Nh9a3bRN*zVGkiICFtehxIg6+P_94&@^LsaO8f zZ6djyGX(O>3oI*lA1hlJpnHw!V(%)?Wfyfyt7%mfblHb&E9vg){usDZ*-DYp`(w8y zr^qevQe3vVHST_|z;Q==JVA@xGS>9F5y20_W7mJBGtrM7gV>Ta?_wkQ(RE77+b5$} zz4XOPTaU$ml9VNqx~Hs9vK-~c4jmsco!y|^^V*)vY-K1sF03R-s~zsYypji+TC$kF z8sXIHd54J*UUjy{skc8pp6i@fR1&us`6p9Hq3_b`GNt|^x{k2AC0eN=>|^!K(lDsm z&oL;D`x!TB)1!OXN$WUDy1lov_j;@Lno33{6F<$5zxc{S%C;I{W&E0q$7y~$FlVn@ zYj1tn#nEaa;^({QZ}ds>28n0lMQw2#o>T^}goI3aDRP7{um2LY!`4qIU*J_KWqV!X zU~^Ate0N#xc(7(4OdD(Dm z^isfw+E>z#pB6TR{psvO?>jPI9_uc=mkoR$s*@sJOjVbXeyX{6nebEmWN0`RF>yKT zD-v?Ox2~zm74>OBcP$nVjqi@jP*u|5ZmxjGqLL7-tA!}#(}LrR+Ph(*IA4(aZs>-) zI&Qo?2Co|5_?ppM^R`sm72oF_=DECZa~#Iq^YERpF?|1Np|)v_9U{xbCjfWi0OZUYtA^J`)jadbN1?*BBazov&ZCH>C9OvMDLeA1{6A zDq500mi{OWdKoWts{2tbvuTgoB{5kaapn850B6xB$pQ^KUD*^D7gYR_Z-I3P(eho~ zHD_9Z3o?g`M_<8)(1G zgv}nbH}p^)J>P48;vMK|m`z`k?QS#BZRdY%i9KZelDd>$9xQSN4J2fXmV^8 zKmQqq#uwFy*||Xnw$z51U3>4uaklnh{~Uaf@3qjb(Oe$xu?Yxj@cYo~v;5+E|Cf49 z*z1m}pM_GJow6p!OG34^tsMc$+yg%nKP4?k5G?)vtcLl6(<^s~qd_;@s0)x|7`wZ( z$>g}RYZnJxQ@_C@jK4Dnb6vK#ryc3gY*r=VCY659fX;l0Y05a$nBt{K9R=^2ic;Xg z0hjdXglIJHhHx~v)3UPK*^B9-o4D-X1`d+xbin3!$bNTiEWOP4a-{YU^bmZGE%B1c z=fNnra)~)@;a2WmxVpVP9E;T74|y@@wV?jW_?$BCk*MpA@{6#=qz}*CBqNQ+Nh~H4 z7`cRBclm=`t!foGJmd-n*wq8fNd4|R)nM+BGquW*GIf^9jrS6s2%LCoTeaHQC=n!@ z7XeY$0ynxkY1ZFKi~mH*yD*PQ#5+}&_Yz+^)Gd;TXxUjnCao>Tmxg}C} zx1zlM3hY_OiD%JySCd(i$2^Y zS`Shi19up;;{3)bk;~m!s!?}wBq!pntsU?1R3e;qL{sSjt>d8ag1Sk-(m6DfKpn%l z%=j(`Vi8=oQ6|6YweZr!g_E~>XPOF)@2G|39t&5g9KkO5Z5i*gL5Y#Z>gDXb*~_wG z`?%{oyM=d)jEPIQkB;sn&kEe_`YbXGe;%)EqZ7NftSz87Zwj2NrrRg`FjK>X!t{Du zh*`M4uj~plL|}0UTV@)czu5P+e+s=_*o|uw0z4jP&QxF1bUZa{D8+ zl`DAb*N)W*GF=wL4#vqJkNpTwIzIHt|BeGFVwzVeye%?U44(zLsiq)sZvG61Zf5=AK z_AKPk07R7N?B3hzk@r^BB(7d(lX`&Osun)pYtnTSspIbx_=ELYW=DN^|4;DJ4ZjB$ z$I0U7p9TZE-rxDccaMRqgoFX1EO8h8^&LDsZ|Z=PAgPxU79YiV;c7SMwhy19IP6!K&&Ts)cmcn8Pg=O3eu&$;3n z%&w1M1K{aL42$R$q=gCWGr4nop0T$)d%Vc~Tk;CJXiawV3Ndv+eZZV^_KBDGy3Ahl z?xYViA*>B-LO0o&`$+njb_giJMEzT0*!3*V?xMfa4jNsJRyz=X_ZarNBpI1sE`Bbv z=8MVR8TnbJd~3E{YNu5%kM}rpJdXoLY1&}|eDaMXTu&NU4)}eX6u5aJaLjswMqGx$#Ux~1A z4VhEh&qiG8yCqi*uE^x?z8^F;SupOpRW5@Q5-u$jf-IdYVS~wB9@*vFvv?fG^qCVu zRRPg;=fb%1NBqqBg7yrTn&l*?o~1soS`n|be7Hp=!tR(!h&sETTdzQQ=N`j|FCUoB zt^6JXPcxs|E3buJ4n51{s*1~=tuJ=@7u%1k(~G0k&aESj!ZJHfkeMBU{iN#_uM`!EW*Jh~&}!{Cs7A#&L- z7&w&xKfau@c;GQ@SAOBp+Je_goX8$OK#vgphCD2q=Xr9Fwq+koPv4OA0{a3@yJ}<*QLwMP4mna>9aLMuy-|XZ6$Vht z+KeS)cpmjZ3vQJ^r?!j-Bt;$xMl=;WgyFAW!f~VZvmZTDEAaKM)Je-NA<{L@(rBS} z*MlWFz+PiY$q(Mh>{#(wn`Ts`S#DTqIBTdybYZ$E6o{J9P~@Qn%BK>MrMFxj{h}lt&NgfewVEkF# zg~CNJ*IH`B-re)m%RPk@-<7$xD*S-Q7wNRkO}yu5PknBE$f!cpy~*A*>)pPkHr5{^ zQQmByrE__8+8*t7>L55=VC7LFVU=|KEgI?jqE_b;4cb*taaq}HbFS4X&A=3u&+O<_ zMPMnYyvuXEAw-={~^JK{1(7IT;{jpyBktw^2~qEUCOY(yJZhSyWL z(o=od&QMM<*WvHQ93!#3Qpu&C=$%g(TB(ti(?`qPFZ292#^>x6tu;Mdqa@W(k(kI6 z*lR;xy%0kYwSpO$sSg3tAAN2*+dOx#B+<85yp@%eNDc`eVpmnxDuo!d9m`grI`E?0k!c0yF6^rD$D%Bb(6CyIJgtm=7 zvSBBm6!yA>M?Z`;XRBWv9bUKxW|2p}GVE&QEi$un&@ZIECO5yw|ofZjN**{S`q>{4N^N2@Z{J-@sBKg28;P^YBxor_v-bF}0B| zxohJxnDX~;3=llYUTOHOgczl)PM2^Z*6+5G)!FGkRawS|iSmGd(_gz_eiWFj+qh6r zJEz8lvJT%s5@j8$qY;`p8(Nt;x`6GrDIP#aM0>cDr~II4fO&z2MxC5Xs!? zrvfQC2~4p_t&eyUfM@VGv}SBHM$i1gQ^J5Hy}(9T)Llp!k=*fF4az4UbU%_##2^ zP2#-mJ!)1VnG|Ihj2N50LWFL6Q2bO#I4HCJ0evEUDYY2KiUcntj7)*wDn6*Qv8#fJ zdM9F+9D=`uiAn*iQ16kmRL+>#X{MOL*A-Fmd_ezA6%1{=pG#qy!IOeH*5=@J?T=wP z5k4|gnE@aHi%_C7lTe9=H(&Lj>FO7wLtK{3Tm0h?^Cd9s5apqV+sx$zI z)6C#Z!Y~}zK-)7u{?LCTL1v|EP$a_q9{r@LRRZI?0q2`Xs!2-l^(anH+JBb!(QFSI zpPn-y_yQIOE$y7mwk3pK$6pa=s4X;P~nlc;VG58k%nvEC_U1YeeAW$}JEe zF8102o4otI!(-9$_n7;q|A5qqI_;0wnZRohj(Sx%fDP;s31h#HvOhTsJ9tW|ux%uK z>*HTXK0k1+E`IXNZp73syOj2UYHD!a{tXyq=EuDr*KW_sAFY|~A*77`J?oC=9HHA^ z;i;J4Ijh8=XV{s@K{S|S@iw0rNTZoxB0;N`dBt@}0QbP+`Cuf7FMKh&7U)njF7a&$4=!&}ketICJrw?TSlv?-SSJu4HoI274eo{g(hZkZtb~clw zJQiPb0#bUtrPluyP$`P6zfSA@0)>WM;9G_Qf=4TFE4M$`gbv^tSi%lQqAUpdW5L!2 z{|N>iUHtHU>4ZFMM8dRDCSDNW6hQZLN4ltRXmT@j6m!xAJux8W06OumNnKlUoR4<~ zF9EUi`xDF7Rsj_2oz4Fc0C-(Uqw`GM|Jcz=h0cpgTRCtnMko{HIm1eD2{vb_tQkV>Aw%U_q#xaGzv2?oHvTC^selRHNe5<`}( z^!2EZMVHx`lV_uxcjNShLgYU_?3oF?-{`xqU?81g+?v9og5n?g^v3GHIkn)0ybTE` z-I?vvXKF*k!#yKS2gt%c1#O};4I^e_g5V!4&-NvYO=bR(Tb^1Q6uJ%?D#oyBh{o5h zU1C1`CSmAi0P?Bz9j)Bi~Ks~ur0_<>ZO?$b1hxyhn-~A43k1BIuI0Q==j10g> zZQu&TIDfL@K96d#B>q)NelbnJkf=4AH6(~hJ8k`9xD(yXe5c$=! z@+)Yg8hva%QX3<#VmglIf>478c^!jUaSO5$c{~gi6sW}QRg&t% zDHTC)Fn7|FAW)^7H4G39W|gu-g5zzFM0iG#|$9CfIII&gx9#b{<`8_!xLJmHjV|E#Op!+pGnW{Fvcx- zYfmmnpfHVX_f9@(sx6b5>&HBs7zAi%1_*#TA(nfS6{i6|eC#1Zn0$J!kRd%e4=pQX zc6HwDoxoR@A(pnz-h>UPrtzM00)>CXj*2D$X`Fw=$$7%*fo_&ts*&^g)&^GIKR{fE zGkuk6Xd_5_dllT&StWuz9kwR(A6JdzPb(8VH$(;i3)oqoKr{fJUDRC#nmJ3Py@>Rz za^K^}y;r0+1@&|7;Mvntc-AT-#gCJx-se7>lK*4+SRc@4;|2WtmQ`S!`;9dj9d>gM zUzESg$|3_q3N_BxyByPr#eFpI_% zBLM;RvHhJ5=3_*KDzJ^wHkSQ3+p%es(F}$pHXwh~~ zzFo_x)X+8(iGzZLw~sZPCzLBhN|nlT{dTUA5)eidSk3S0i}8ThKX-fh&vuAzcAAE1 zMnZA*5fY$B16!sZ-kXAJzLBt5)U8eRjJw+`>= z699mcNGSiceNz?8v6c7$3es7^m}DUWk|hrC@Q4R{06u8pG_akq11yddAg{V%Am#P4oF#Z`fq+n1n%iy zff24Fhrwb1bTUGi+9SYmOitxo$nnC@t1N}?pgYvT$4-|(j1noefUZ=cgHe$27HnX! z;MvI-C^Z&PQm^!z>0Vg@2gNYhb>W=G}009 z`xoUtR2Kk+YZ(&cA{Wp1TA`IwhT$N8X-0g}|Bcha7`Xv5&b1SY(xbu=Y6yN>4NZE5 zk`p$i;!yVTfcF1INFNwK^RM{?c=FkN2uSpnn`1C{gbHI3iBZK1IFhGBFv^;PN}HQ+ z*4Ci-U$y(ZQwQbYyZuR(URHWzbJ=gge;MHaJbUxgpp9LzMyfO*O?ifeXrbiIZy>d{ z|5BUIdBF&=k>oE`+79+1TSrfj@It1lYQno(H6H~LV?-J=Qik&W#+n$BH_>5EpBRe_ z#izj1{D9%{kmXY{t>x8q8T~T5C0mrEth|+$b7)V`uNOX5B`3LVWlLX5Ywza@VS-qn zc4e^L|2fEQK^cFkwxX7yC}i4w^LUTvkeqwuDYU>FyFk8Em$C-n3hs>)2bzNdvG4Kh9^K~zOqJi67Z=&ttt8c+K^(cNQk&PReFJthl;?%H&wndz(Z6( zlpqMA3=R2I$T(6>S02ruLMfWP*N=;c$pcL}sD?!a8g!^2Ycl4}iWXXH>LiR6(7`9Z zLq#-hAq=wOt`>!uyG6R5H=8?4@Sh?I9?PKtxq(6ftp2>Sh=SGOSea#2u`E~#h*g~k z->{U`(3eP2g25Ox8ToE_RMxh#m8=zNEO8fhYpdj#B{hM*^9opMP_tO|Q=36B@ZdDWAs4%br5lzu3 z%=uf^kgGhkfpdlV>VSjyg#Emp2Zvq*xXjZKrV(TLBe^M_O|)RmO9UFIpf&`jqMkj6%;V3~ z5@G$%QVmAh{~4^e>5LkD9p}Y}+|$_F+lwFly34FFpLrRcHG=0Q(dH^vK^^&eJu1R0C?0eWJR ze&dXp%UrjCh;t`!W+<1A1L|`%ZX|(r8>;B3fVbIuu8_e@*He{R%C1V@GmJ#NwOWch z*a%9`?-O=tdkCs;5IQcN{$(y4V(z*Ms3@PeWmM#i|DnSo`tQaK2!_}W52Oj2#lo@B zXe+4xz_(@pjuth4|BMW@@g$&)NAa9{TleJFjW%vb?HSvMPY^WZ1emau$@S03zXpRn zaai~9#oZ#@%Br{QM?w&m*dNPhCVqjIM=74?1YqE+XWGw40xlY_f@SPZ1e1kyZd=v7 zks>4txL#?^f5F)&eklhUcpgxiaNta@f3@$7<8pb5%^698fs_ZnqDFrZFKT|4tO%+r z*t;45>xd}*?7jQmfmbi()|~ud&v&T3Epbs*_)A($257H9oYo+m2{$j?Ow9L?9%5q_ zYn%CS1*v(KW}y-524M`Z`6Csm@}&3lHBo*y`@Xx{Yp79_3*`yZT>k%}Iem!^0`%WN zu^nENjis586z=*e{k5^xxamUguQN(DZQKW79}VbR`%c{nvx+YcfcSujbXwvUR7KMC zUx#1<)^sS4fE`Y3Jyd<2{XjB8Gi?uKYI41l9;_z*`U0QrML~LAI8yVhntZMUqWRfA z*aug61103OlK3U&pWQA-wu2?md_23#3Jxz6dPNeUv|~b98fXyp*Tcm_F}~1^7ijs5KDUH~nlxQMvB#p~Oi z69^i}0#FKC5*q((0iuL5*K0H~ge#E{eFR^9@Lz9g_-Do@ruN}g6g`oVgBE_6kXI!il@(EiYI42F6QBg8 zIw*U;v1Qu+Zg2ksJZ8{&D4;ewn4Px4E(%H=KqDl>>SUxevk?#=4{^xJ`oCdC^V8CQ zV+3-ExrizoaY}Hjl{l#OyGj0I5x&P^?EgSRd)Kh7{TJF(xE&dk$g{^#XV>NF3^40t7bqHvQ}^)Gh(v(rn;`Mm{WmHJ{}v$kNrVTTrEx)SJsKc0F3+Og(9dOwx(KfO{CR4kz`6^Neq@;wMo9j^C> zm$Fn-C4f^8YyJxZjZ=-xe{qGHz%6-HqhTbhlol&>>;SAqb0zzq!s=cKVlc~n9UK~f z;U|5e&~Pmk;Z#Uesvx*7B2gVCw2>jJGEMilXUzRMt7mT{nxgia^;XFu5Uevu0bA?T zK$(Ds-f;INyzl3D6Q^<>%^d7$Ndy}4Cqmf>$j18$NK<k&L%5p%1pdbbuygID<$KWD3S2Z!}mL%$*TJ;$3I>x5E8&O!hgsV15j( zr)t_p_ERMxo%bI~g2-Nb4~$Lsg8ygtv-1|7GGuoJ><6*u;ie0vO%n5 z`9XEQ`oni@a2z@KA39Q9AEQ9`(yk*aJt*Ca)0^cZ_|m7o(>`$Zv|<*N52!s3+1Efx z2*V{A=)MU10wDkTia{aq0fpp;VfG{O|BGDxRxH#!#2I-pVWoi8>^q-uKs}ZE|6sQ% zX(dg&((hj?wv$)3=RYLqPIe=F{v)bCdDx@~V6v)>3U>+!H;S#V`agBjpq@bCwQa=t zb`A=!`!0~?rGph_+!htI;DvfA&xqKVbdk_z%r;kjDB2{yZf8NG-|q=PiGNIi!ih8} zRH!|ZYVGf);N?&qgy5Z#x{8Re;0Ua%jIBFe|l&4T{}f zsBdY%Y^D<{Se5!Yiu{8%NTU6XlZshXqb3M~2-czypwbVNfXyusZJ32*4CMi>K*Rmp z%QyV$x$Xh?^=W&}6dU;ZNw65ICh%`l8R^W=6QLsh`5zI=nnLP6fbp*&?EnI+G9d*P zh`Zv;6kovx-NO_(?x5)GtMtF!|1;0w-_!pL_rNZR1Z!r|M!DUoU!n4ovJ2)9?_$5n z4*xIMk+vjH{$N}6`N%3)?Or(Rl}fyK7tt8m)#R#zknp`W@_5%A_Xf2--V7MUgOZc^ z8uZ3K_NmPg5|Xn8Gh-t3IBv=Qxoe{b^yh-MKHlh2Pr7b zl&RWh^xlLoSLuO0N7=>y+0$?T`$M`277ldq2afX$K#vF81*_`9gX0CTb_1;=8thf6+G81i6eSszOWQ`kS=nnhzkS0K@anUn z+bUEc;5#6&iwk-cfE0$S;bV(gu$!pChven0XWKp!G7*< zK7qoAYQ0JH&*ypn*zF~9JdaD*aVZ)6un&-NT?VxB5Z81BHU1fr+s6IyTmWS_7R58M z@?zQK09;l5N*4`(u%V2BdV3+ia>jOlJ2=^VJP@*vwSVAD^Y9FOtPjOT2^5{jc(fUh zyJVxsq6Z#Mb~X+%4K_mylycifn3@v#Q(*HL0lp86^fTJolKsFu zce1+IW!wx1YaX@i6#qIm0N-)|D(R>QrZn~P{IxvjAkOYM9C)Zv_oL!)c!*|+jP5syJw7T4BP`GI(M__PfYm1 z;gi%{!^67)#{qb{Dvc415%nYiz041NsK?*jgr#e{WfOf$SIORPV{#FC^d0TA1MxB_ zl}w*^Zt2F#4LFku({ftpS^K-I;Xbr%mL1&@F1^7U7mvob$xzE2{YR^!f%xCw?5|K#9sPe6hZyNXYv1-`P{0$pv#^q@r zdIewvT*~+y-N4#57TY~~fhX1C#N~+9Z_@0ogBwwCd;gsK#|#?yK}oFXKDH8y=RUh~ zg!tY|M05#8f+EL;jJ}hDY(5dk0sS<0`mv;t6?j_xao=^TTNwAF&6$ z0;<`(>J+#z*n{ozz>Y&&fdchIt>gK_?kM!Vz0exEv1GA1`JRfIR>mbm#3c>Ptug&* zE+9~^HvAZaY5W|)bb*JJyj(Hh;qTNvPfCtRrhhae9EZ`N%-(WjMTlQ&bPslOCgWnc zjrC0*r;bV`ZSCtQ_M0uHCGPB&*J1B|{p>1BzcYBK)@?F35}&J1!3u*Rcjh#LQo4?2 z+9-Nq?i8d8yo6q!sTdf-Sr7P^XXk$1&3Z_ZF8l%pYoKK&|8~|M&{I?Mv1WUzu9r>l z8``PwPQi8X@uO1%29eDdj9zl=^|RxjOzd;xU4~jM72H!_G--2a{?-Td1#jk7{Jg%XQ)cc9oojC>`7+4Ii@H(X-{86)z zkwc2!FlC&IR70@CiXLC+M*dQ#{B3q{$T|~+#3GGJYKh8it;{=Pqc z5Kxg4328~C5n*&F5>g@{APoY_fYGB|{65d`&-b(4 zbMHO(yiVME?mcY5G!W>K!0|j9 zoXG1F;CY$ZkTZ@78m#t38Y*e0(BqW}LXWADn|I19!Q?#CduNL{F&V;{9(^D#?>M&I zPv~sl!05wy|GOj`kX75Q3zClLLkbg~>oa&mUv*Lc4QbE|B6MhK%AJ%;hItt-wfRE| zh`H0}MlW_bttaH&*e1&z`Pl((UW9UQee#*G3!&L?WjK)h$;PS|atr?6I2pGN-dHp~ zHY+j3Um+;*WsnpRI{gI86QBY0o#zq~@F(&G$t$Pe3mgf;r6QWta1!Bu6l86l&vZ@s z;!PdHbZWLg1unlk&*w&Dc&wg%o8tl6ltw%SXZ4F!sd4h4)aJ{3R!(?!hM^I??>yc} zClC68B+Uazi2Fe_6!Qd9C zgCM${uKfmzH@$G6J3IypMx*J+;S=!YkN8<4*_a5>qQR~+PQo%9(R0G^rvGSOH{|S$ z5~%y}oSn*cU76l{a39H&mJQ8^<-?$AB>MHj0(Wg+w3TrEm`mL_BDVzk{>|vqb`bJ8 z(h!Bk-D-F+Vk0O3qsEOY^EZw^)XgKf{Gn5E4eq~kePf``cO?9_4{Y(^{sh~}IGCN4 zgUqu3J9ig%Pv1(HS_n1k!2LTbEp@xgyR0EKemk$%MjngS@5TF1Y*g^9?9QysO%}bN z$U@{lvv)?uSsOmKwg34kNhOOv%h@9(&R8@NQZfzX`_mIGzybIzK?HX^_<~FWnYa0> zoz@V*0txVzR}RKyzN3*^!}T%NSr?NV`P(K~s)4)V{0x7(^tvkz(O&`5U1!^y=?yvU z1R<_RuxqqS0;LGvzPO*vc^TMzo#Wy4GYcB=$_n!e-~W=uEeAM1`C;D;+_VoIr@>yM zTgE#%^;lu`^snI856{XpE8D#oU7R%)u=m_kfJ_-n6sN;?U$QQQkp`q^<@BjG_GWD} zD_sqdp4N%zS*u}TO`9zA+841d<3iHxdZ97vtVa8X;}=-?yYs+bn*D2{))=bmWO z8i%$f@qM(#U0~ZTT$CU!>UFp{fNW0O6KZ=_Xk*VYd>1`j?Kfbw*E%eerI1O7=Xt-e zh#kKGWc=&#FP{1rAoHvXLu7iknqT9=OrJJtS!Apw3Bw*DM|!#c{hD7&_g-c7N|tOK zdbY9QdH4!sA3ameWYvZ)-=N~m4AmXGradcrbXLx{A#N(=;IU5>17MHk#y|f#?j?FQ zEFe7A^f{;prJqX11xERt!M%|Drg5?+r;50{M(&^RpA)V;K4PR*9$~^tG_w-xxUYd;` zhiCRDJrnHAh9{0eLz01~eT!pONuBc3Lk|NofNjxeA~g5hh*bHSnw|#IV5gVbVf`DOc4s`@_>P6>jqPgT7Z>6Tv0NIDM&C3W>;tIJ zDkgUgy$(y=Ma8raAalN_^TvPowE6=k@wdMxcO5A5mtv#|LPU);`m^|pRG{Q$_S-pH zfraQlV7qYXz!Vdn1Y(O3yBiI{KJ>-kA|(N?S`>g}PC>CZ)>=VQlZ5d5N&cpyDfD?V z?0Ax-WyH*KZFyJJg%?W5v^&T-mjvqMY`Au#5l7KN^VedkO=h-_1+g4Wo0}16i9zxF z3n_~0F&{9hB+UQr_1}T!IY0B|O`+C!H1Z}9sE+3xh<=YWbUO`s-j`XBn6`c{Ohu4V zkn~rKIFMm~VnMC#p6{2EmLx3-qoP72~R4@>eBH6bO{4i=Mn#@FVY=H87y58yt|ANdkCQ9*nSp9 z)gO5Byl^END`jgr(1&T;c=;}%;xo$Zl)35y_~t*)EYIHhq#FeyxEV2*tx?&w5q?%< z5*jH!EJB)N;(#aVSe_ybw~K>Pc2`{Y=MMw?M|{>b`)8bYdr(yt-a#0iv*=K#;gIt% z6>3~GFH@G2Zfh=!q^Gfr=SQ=m7dDK|l01-i2oHhMXE+EukvYFW_dsoA`hCuX=P$^A zlWQ;yN}aKux&_X^gA3O2JubU6A8^7cHtVdD);>am4bYhL2OFHeQddtYZ=_+D-_N^jB=L38urI zz0CS%E#Y2&c%B2xCpcPCPp~z z(a|5TS*}!&UANQ0f_N%4P55V{BH=?0F!_jTrE-YCxWvD(MW^v5Rw?IlWBznZp0WNd z6U||-KRr#sBcuKk-0t5}wel?edDceIJcX=4m1#kTECn?jLIPT07$xOLMa^|3$`0!^8bx*k6Hh|1@=Fq-9CT z9sTn6;3km}9QE)ldZfQ(xHW{Cd0h>uSz%D+M%Zd&-q2wgdfGQK0C>U0d7bPS6>Hfh z&eZ{DG4)W?xMo~2UiTaZRIrWdLJ97g-kG&TT^|QOLWR_W{LZSPRktJc?oi+Us8;VE zkIq>1fwWImxnXS0O6Uv;sezYFT#1q>o+miX&tY8o-@e`0gE1-kErY+oFod~}!{4p1 zE3&A=k}M~#TR6Ll2NHjHKw4{;lY+Krb?@0vkxtpWf_aUp%1)fhO0#JgR{izJqQ^N- z%J2MX$zH3)VUN-Jr|BM3#`?$<-zC(t(VSqM-oQ;Xuuk8gYGg)E#s{Bn4)xk)gs2}# zS5Tz@}-Pu{&EyFb`Flxnl0 zXqqEU4_3WfI|$KyuC=A|qnIljLOw9m8susHE(k_aHN`+n!<7!j+kXj{i1+Lj(YJhZ zMLRd&E4AKvxA?Nb5JjRnhOsBx5?7Xa$;O5~{|S`X5PxH*CyewQ+?OKZX&=iRUShag zc>xoAJHBZ~dFFf+--I+?2S^*S)eFmBVJos6hkDafQw(v-5uyK@s4;q2q-~HFSO#>% zyMCc5ljtc<34DK=g+MZV8ofP;m{oM{n*4hHDJxp3U>Os0B)|5|1=k~QGCt2=Uq&xl z|D(jBB?EbTwKOJz(TM)HTvPolX`qoO9(fa3ySoiKc z;7kLDxQ55~hZ1Y_-kyz9PTKmXObFzx@SIp+S(qmb?82<<5Ot~077Y@7c4EFTvaFoN z++8tSn&@Lq?nMp*uF(Ojl-cc{VrUo*8hc-Epbe_}jfXJ1!MRY zTp}M0PE2j(PU$;UVq56<6tB6yY9w2)i}+bi5BW-a<^{_DmLK}@8yAb z=|2meACY*4+3xsfT&p;7{2 zJ-b#sXU<#fn(T5%2zrl3MdCdLgvlBmeFA;gE7A`cmQvxKjgiz@Z3!%DCH&=SU4s%RM z@@kdo$1K?-7{AN~^R7j{zW)DQfXDPSiF3>rHb561muS=XcMp?c5`A>l3k*|V-f|8& zbQ5Wh*_a&qoi4E?rv_()lcc+D{UQ>cYaoqOXjDA0=VTKA2i|X_Xf*Gx|LK8YugPj} zSt3hISyamcbhC>0-#-8QmH50Du#JGN>|i{KsN;D!vYE6;I&YwNWtcry3}?L^RQp&j zAc217$p%qX)WR#y-(N5XP4tnylaYNifeu!Nw16{3{SavkYle!y+=jXVy6exLgZmikGUk zOIFi?n=)m(%NI*n@?L?TbaY;4(zDs@TO65Nyhi(xyIh#^gQBeMee&s44SRfU@lwiKmG>+tO1005~#JwJ)tA;b%-}-RKNxK?&nK4tCj|v-Rm*9zgy8E``B%s(8Y&8T*168 zOFXH7+B6w~u*36!rC?scEDdApN`{r`zxph6q7T5kpRE_Yhic`+hq=bsZGdS2H?cjc z0Vq_Pe&8%--hh0n(OPmO1hzG4 zJ99WpSEKd`a4&M;7<-sEBm$_FY2+jzI3WXC27X;)FC5;8DdnYZ6JAIbL?(dcytTxy zjF9Ta0v$qfylpXnSK!@Jv5EJ+LyT$r3Xuy*-Um&MvYgq`fqkNTs-nS(oAY*Y$hp}K zUAl1R;B9wO{v3XB{mJ^0W1uS8>rere%hEmLCmUa$qhRmquk=$|gcfxoF&-@LTF^$r z1Y=vPn^AM9)9m5dd{H3^>QQW1;v%?d-c?PCS%RxLbxmd_Y|)>a9Lf%y1?sO|3(F`N zyI0!ach~B(OXD^~mp+yFssCIv*26ND0xs;Wagm{`^5qMI5X?M_I~$QU)lM6Dp65os zq0!&c;ZSC}}vhMwjhE|n1h^}B>kWL9Q`4ygpvGhXQyzT#lxYi)Fv*H24YaXEmDO#qveN3mS4!XvbysSX_Ij@|3#z}9IiHNo<4tY zQ9RqrgCTsmk9KI{xfl$Stc^zZQha1!#iE6Ff4%!~6(#U7ayLjrt_qOLe0$^(2K$ZtIThhZr2SZ<0)o=4|;Fg&Z_~4Zk zA0L^pTFADgrr+T^3mPEV8lVAFl9-y(9E{OtO?HmCA=`a2iL9mY!|u-#e!)&VsS#pk zo|g%q(+La&Mn;ou`aJ#)qv=VP%mcePk|xS~?urj*E^%Ux3{gidVdA}s#divujoMo( zAE05+Pr-tuQwoV;yjBJQR>hF4a zu%HRJQ|O-o z&ucf&|F$%4WgkBJ@3|`J(g4rz2H}HE6F%foT2m4_k;{^1fd81wbSlZTmx8BZrN}{z z$v*@}UCwBAIQ?xneJRXK$4C8CNk%^U0%3%;JAK=8b(2$J@V!hN^$15AZakLRg3CUY z>;H)l{uo0h;H`a8a7Us|p9(;4ewh;5MhZ!!x0q1;=PYRO#pJL*%anUSKsoX5=hE>T z;}Tfi(txK=q!R%;F;obAI?2IQYH*6}0xfuJ&&AF0y%}X_z)?!{W_3%$-d!F_NhLsx zFeo1GFcy{|mK?jopymc6s&_^dt%xAv!!Sgzu0W89+lS+b2oH1XSBd7<39gf>kU4f~ zl_sG0%sr(KMjxmFa_({Wo+X=`)xou?Farr`DiX%N8oIu+V`o@=nFPNvZ;hD}3AdQv zpDTIaf}fKkH*<7hBVo~zg}cn()O z1^O20${K^iy{#sYv)^d-I&ui;dlhY^xV+9+S{60yWN^yo(q5!MI@WB8ftdN z^deg6)*jgDPtAJ}C|>yK0thu=g%Dp5ra=UrG@rf>ztxz}==+88>(uy*{FpeFTyouc z2NU{Q+V9u8j*K7CAry;L= z6Hio-$-#!3&OoK*f^<@COPfI*X^$X{xZs01xl+GO3RS0IBL2rukY{}wxuyl?c8tv* z3wMvmrwG;+OWV{6cj-s^=_x!(gX(wN%lg7j@W0sd>TA8ZV<$31rUu83Pc)q(A#Fbp zUjlwG@E%1fu?bR8 zQN31jWtu9J&(?PT%eRmL&i;;lU^`UORb%*bB_-c2&pXTgdBT%D)z0O&>ho(0%r{Y} zi}drYkZosD(FoYC<1$AW?Zf1sHWHWVXmeB|&%DS8^Nld$Pg04i zQSmy1rIWXADS{U49#2$ERp&N(pm09ye0}W!a)Hjf8FevKvlTe5<8L`sJI#m~V)n+r zO%hT?iX2Am!ihN7`g9$&IEW5r+sXdXP$>7ZLbV5DliP&mpr!)Yd9J29c@5zHgOfg^ z16cZaYH*dec-DU|$T@!vnALs)I$9c5xb?1w*-(L~Ab51$`n>75Tw2huyo$k6BchK1 z=GTZSU3s?C7+2oZgZ~77;j?#?Z+74_90BNrX|qC^%PJAsHdmJXQ6!={`_t{G0}{1v zNg`W}2#!&|eu*i6w%n_oqOe&?3!$~OU1 zl$A+)WfaNwYi73>&jGv?Hw^cOYy-rjr7yC-K@B?t7``VCuN-^)axa-^{jDo&_}TJm z?@s!=yf{iK_!W{Dg;DW8c%srDC_Af!Bl5{NPTMRG7Uvp5p)@Ax zZi;GpgEpRQnE8n~uJuQ{ro6T&6!GG|jj6h#5Lt~SpcnJCPERDYI^f360}4dyeq87J zS(n}O-tJnUZWOPv$ujS9j}Z~umN=@iwQoccg$4Lhy5vnK{ua3g z_df5IuGl6(31NWQN^h;9gW1eAC$CRV$YIge^r7e8oHIQ=Aeiu%y7%&~VKm zbbj3O)zITm_%@Cq%FP+Ekf-DxL?;{2`NeV9gG6n{M46~3qqcWt)T7(uUjd4<(!hy} zNjmXV3fco^GI4jZb@KH%S9e6yr#d@NhF0gc&-r~*;yW3P(0j9|`Nr7PuGH?-|20f) z$m+t2sw=rxedbT=&mO^mvK-5`8Px-x`}`Z>nTX)EzpF+Kc&c(YX&>32+U*uj_w}s2 zH*<}`P_8+M=;&OwclFbK@l-lb0HX`8A6OxdJ!~G3ZikrEzJ}ML!wuaH|9Y}lGlGqd zN1~T$LM4-hv2nqCnJ@s;EqAL+TY4_a@=7b?@dt|O_F7zL2c6f?5wX(k^WF3RTDo|c zd8vEwOA9h%^_ka6*JBjo@%{IWB({8tcjYJPNRr*2Y~)nqBtwQ=hW6gV44r`*?2FyM z;x-0z?K%ftj~i0!&93yW87)CP2@Fx=Z@1>e;VN6dT@>~3H*Ln{C+bXpX7gjWm!q_z zv~U$jDsMvz*itGjxg_#LobfG5En^fCgJs86x7bkB-w_*yFaFTcN(PZec*bbSnnhVe z{VUdT0JCS5PMDxRqvjR4QUVAe1(C{6#gCO{CsnudMgBzL#4oCh5EH+?os*jK4-Ebr z%u(7=+W)$XrZYn`O4P&t^Nl{KqcSH&k@_=XCL*_UT*v!uaboui_!2n+&FmEG5qpPY zhha%zv@;GDw76&WXkuEwSN*6`%p2~&nWXLV0}Do3z}tAz4{WA|E*2~N_3lso&*>aE zX`#jUY|VZoNvl4ynh?Tn1E1N@+OQ8Ys71G=GA;TM87tuwlHw{b4K)yu}5H&@4|Z>jF&O!@RcyC8c)d_u@}gYo~H zjQ_r{{`T74{QEm4bUw3HlzCMA*Q01z6{vlm&OwN)hJ;PG2yX2ZaKG}eQ?FCwfD%S` z!;sG-Q6b{Rx=y?vsgQ=>TOo>`0Vo66Q_++gEO~Urnee|nk3MSZnsQ_NIj?!wMMdIv z=^8%%^m?<~_trH_);Zzcg*v+gQLVs)+oF)jsl#ow67JLQU<=0xqtfIllf{3YPx*+s#Y&Dc~@!8|z6HXAXxEkBNXE0h=ap2S0 z0MoN)x)f=j4lEad8OrXYn0U}+@Zwr*i>~)N#lUEw>?xZB7wZHaTK3=qSIx>cXK3!{ zf^}o%^&>@H1#8IVy=xK6W`$qD3p?klbrM_K@&Drk<(%+NMp$>qMNx!lbD~g`P!yfA z772>&ZB%iXPU*G#ftv=qByJ8*y__U56LS&FCJ!DsldF>yFMzi*!lLNV)V?VvlC>f^ zilX5Vksjzw%^ja{_`>zE@I99I<%1O0z)WRV($6zvQ{jtix~93UCt=^W%+4cSH59CC zYpYRgfNkiRgTg^9B9@l|=3a`0g^Xw6iy*>HrKR?XI==8T`mPp56^3QVUNYR){u@iJ z=6K@bZpKX41%N4jo1q4RQ%4jpVV7yH=LPX&GIoFb3Q@Hr3wNA(H{u`I_7rB?I1Q=y zabkp4qJ&f1Zp0j);j!4_RQtkSrm`2QjDnIwsG;n5KrdTD2EMN3uhDR2gV}XvFZ#6e zHrG)$zj{6I+TsPPg**PdND;wTRBa?)uXS&9PZx79-4s8%_9XMg5~maZp`NT&;>Tj6 zrP3X1%YOFxsM^&$xwzobIXU3@CS%)gyjUuu`w1)lx(C*cOA99yu$##ht_aiH!s1=u zg*zG6L53wtCjGnP`m#GjEJF%vsdQK5l}sDuTjr)-=&ic6Wyx==Mcy0# z3GUNSB5pLi;u!eeyH$y4Cea#mP#z!rZsya@-A!1`mfQ@TA)}H2M4E$Y&QbTqu{)nA zu5<5UE*}xi%w_&it?JAt?H1RSuMVP+^mip(I1Eu?w@W*Jsk-`CtZ>)W-pslPo^~9M zlwHCSx8+D@!yvvAMs*D$kn8I1tV_okL)7xkD*^%)y z;ifJ%cdgw<3-0fiPF1ZNJ%>ZW$I2nm?q9#)A$y^%n%=jR1H*w3<2T?8GTqyVOGWmC zU%`(vDUFLDThBh+O!!}k8FnD0JCLGHch(m4;>9a3AgA#L0^#=2KCjT|$19wACW5i? zcwe&(y|b_~a_p~&AWcCP;>>;rBMbEL%GRVh7dJSBFaNBqFWy~vdJetIaMK6^jodEW zg_*YM-N4nQc!_2%5PJUXg9~o6_Rlh`Ahz`!GO>`lni1R^Lc=BWkU9--S(b#by6RPKEYLb|95;41tgy#gT-^3d#u{sjF7|9|I zKN0;pg}7kS89A#~BZmX!t?#1cyEc^YY~9VaV?JBSI^KDColTcK$lW+ra+0Dx43Z@V zwIs(tKySPjHK%<$~wWUlIIOsu3TOgcNh%%uEGKzSeL z+<4FT`ND^!K+jDT-79j~>C~}#;vqbt`@@V|9>iv3M{+g|m9+jb0ZO@*#Fe9+QFZ(O z1kbi}GRb*&rk7I{{y67w+m+qB#q3tS+y8~sHlw~GbGca~pn;D)ibtgRS`ywUHrsM+yfshtMunO1qh=N=6B_T&bVzAjmRreok+P>JfI^Ppt;t{k<9uk8Z^fL#I$)veUcm2vfhY~ z-YIq4lwB)FoUWTccp$mqradC_Jn-*o1P zbPqP^PPTnQMQT6n4^FWTG;=d2$7JYTvkowiKZHn>!D7jU#yWbBB_c?*sm20&IS|>w zmOJT!8iF0;a6n#6b7w)p#OAT1R+26uM&mY@J3sIJSjvkMTQ&kuyE|pB9Qw~Q>$O*u ztcT*qJ7rqOrKYcQoWeb}R{cg-gX8_Q{F;gfKjU93Pft*K@*CCye$cKfMJ*pqSd-ZhydSh z{=?@a9;Kay>*d9MLKjzT%?h*{U@sCjrjP1KSz= zGn@Wh@IsdJ1A#v_|=DFncPv~c4tyN15`A+*#gkH>4N_Bj2j{tNoJGB3- ztY(w;WL5tr62eQZiw-S5ZJ+>c6#>3Az4gC)Zjp$uiw-;dqfR8!lu|q>aX_Nc%|!(H zIBs0mo`JIBBDg)3(DK!sytCIwgMV!8#2oXTY|h4fcG*mES#Y87tQbnKZ5{c64$@`y zLr2CMS84fWr^@M$&aQ-prEcW-lv9uyeSE8%ybE$=6&3%g8$+&rAO2T;#-#XWEMQIN zqJYe|uYu#QmW(KZCsbEjc6j z)6gfr_*B5UBHbC5+WzeCRxZin`4va!IYUNXv(c25*T5tpK3$feG!aCKqp+dUHLUn_ z8KEcoW9aGfU~bbLJMFN*)wd(ri9X{sZaPXe1Mf)FM!6gO;8hs&(t~=)*i&tk6+GZ` zxWpk%y(_LlPM6@6s^b{7uBRh?t&3%Ye8NK=v<=}oN9Q)fPY}r-opEwZx`aiW`0-#L z?G!+_;4}=z*Fv70^2{yxQCD03`<|(@vmW-2M*i5)S?(NNywpn5F0-l{nJzZs@Rd3j>^$Q@>K@xB7)A zN}*jmNOp1cvJ+R57@Bl)lIfcA)vILELTht1PZRe@@2uRhbjeFV2jo2cpe|moNxNlo zu4l64A@Q)2*1=0e5=ceM*tre z;S|v(jipnrD6}=9muErZq)@M}3FwlNCDAQ&DED?R@4fLPq@D zWK&sdb7;#zm06TC5#p0~&o@%=C6NibeyvM?`!J&b^i6=UvxdR$6;e5vfY_Z}x2>#I zzOZH?^~gF$(=j*gjwA_9G2J{s+1QR+byySW<5^x>IpV$L^og^&K6>o2DO@|J0Ny>k z#Z4*tXDzgN@{p;iJ9zFHSWuk;YVB?o1 z1nY9ikKUvDU=9StM^6bjD71}gUDX$X*5Gz!6}LwN^+vb(m83GbVb<4t^qFiI;t$#= zMK(R6ck0VCy|gdo8b)q$fyOS@V4OV_!|oe(Hs~L{{AXm<&53p9x+^Z z@%RsO`_o*I4v?~0d|Q+YZq)q>iG$xeH)&R1y2N%Xkkuox_NG6JvL)Qh9cJc zU$*J%c;;V9gyBjo710^bmK|`Jh+mDrjV$hkjv(~N0EPB?hLVxocLM=f#qGgBJqZ=? zGmFyZ;v|P0m)~BF4fCmFV_iq8gL?0-3|0Nt7t5ZWzTcu7A7hP~q>JI4P)_2yn3LN$ z31~=q>sI*8*mS`k;<4%d5&fZC0LD#4CLPWv?4UyzDoyo~N9 zos;kUx0jKB+h4v5s`uEL-?`{(($0MEfPR6$&_;hEyj-G4lX437cWGAd^<~X@4;RXT zQ!I~kwC=|~A?hINca3>XN@_}(DC+y6Ko)#b4|)VEAWoKlj-GUs(m=65{;7_l_b7G0 zovQ(8`DEs+ZwDok%VGM0e#IXREX%2e7c<*7fJgoc02jx+!#`bRDUWIHSRqe&#n~p~ z@S(4%R$S9q#a0b8nGPRjLkQtDERBV+;;8aVKN{c1I~s0~ZMymELtC6V3zQxa~Kj5!TXT9qt~pJPJunIHsBhyr`g9^h(x(PkQtK zYmwbNCISvS`95=TW`kb79+tmb`+m)A|9>vP-8;JU&MJlpVkURb9{R^$&%*k8C;t~X zaU^|!pd7S%VBetC{z`A7THJp!y=DO@d=YlR1}kk!?Sum%nKcKvDJw_RC3?GG&r8VRDrd$=tISb90X=d;kt~ zy4Qa$TgY)bD$r4wzxyXQe~}BjN5FA}?32W(7#V{W#K@1$uQ(WHXn_hQI`SDKz(8nK ze?H3LJ?ER~)2Vk!v%8?J{`DuHa@mK^MXuCN-e1vwFg<}tLL0^dmdG=4sJbpYob2?u zuNtJYV3*3qqH0 zBlN2*QA#-EFt_>V_&0~0=&2!&9fp$;d8gKRTwYOnUc;fq0aaukglI?OIkKbbl16tS zH3;iZ=r$?ep$}>1AHiQd&k9=BO4Kd7wSbs$7}CXbzt?#R5^*MAl^Evpd%+0edP0@1kR;>q z#q>Dx=rxOMW(55^DosIWA_coUJDJ;ysc3zLm#zVN5-S09`-&MFt=;ZLdsJk`H}&j$ z4`7JB?rqBu#Gf_Yh5QCZ3}VPN<#E$HDp}U7T6rCzuRfg{3?8%AT0H`^f5rK6zG^Wd z`$_7VX@n(Xu4HQ}+t|kiAlK;vuS`EBQe(dkWL)0Lmmj+qwM0&|*jV`ajsxO>yT(tc zr4IvmQD!E)RpL;1u@ydVOWJ}sKqQ;yHS3Jqo1C=fm0h@!PH~mbrbHhwWL-pm-)hSS zaapj>+o^(=T{+6kmrG(qbTOab?{*LCN~ET7Gx9+^LahA3dfe5d z?z4wGbotMC39+bPaJ3_#fDjUYuoBih%N6CvT@rD{*>=!^kj^Wc>Z*S&n5~zg?5F-Y z9p{S)f@!R?xgj2*m!eTpWpKw0J+)$td|uff?5~|&T6b_mN!CCRiGK^?sAem|!-KSf zQugk@<$`;-(nV^ge}2;b&})(PVRLw8B~r6hQfh9@*LESZ{H@RZ#a=;PMEd!h>c-kf zV*3X%loN3GiBKTpJ6~D{*YknA2sjP2LBqRs`vLT;3PTLbDX*b-Mc57pawH?I`Quys zKCkCPtWnPxg6i*qUNtd$^k~w>ajBb&Dt#TQXZM4QB0%ZG-&O?!z5{|^hp0qU!B#44F}Joi zz-T9OCwhcB(%AFYA+r7P5+-*lo%Y!Wp7VsW_t4II__D>B140qL!9(a16?zwwJ z&(9u~fR4(S&Ahtwc0l?4>zodkk8=l!zF(=Y99!e>vMLHKg)IYa*7z^GQg+6SSrMO3 z$}C+0FB3ZHvA;dK?q26qCcdf?=c85)zU!&>%aaD3MAE+_$tHK$#mlVqj<{D#?t7n1 z^;myULY(mc8X$5Oi4=>fxI!g0p~k2Ef`afvhSODd4koW7fQ` z)bfaX@0iwU+r-=@Wtu|3A^4G(OFgfpfVHR)y&Z?%>0fOn4T|233QS#B;g$zb$z{R{ z+9nYK%VFm3KTNpK5+td=^7JvhcJ9qmS2fd9xL0|%yD0%M?FBIZQmU6jY7dQ?LzeO2Z&q*)2LrAr7%p; zSJyDh>J$nHqmdDLZavz;=~UjPuc2QE*D-#x=w4Gd7Xi$qLsBnO^3GkB!P(oAgHPti zU;&elR{U_WC%`BBQZ!uVS51B4*>G?@utUVepM+h&sFdku%uZ*m@_7I2*g~;%cHwv6 zGE-akzmRiTN|{I3tIcD!_;S?Dx{SI{;tgVuUIre(z$t;FioBMr^ztgmRoVZt< z@0WUxOLw^WUgzdx>oniq=TZK7aozpXL-5;KnLq@W45!qsN2(IGu9*|SMV}4ov5eQuh)swSfYuAMD@mK?~O{ej#SPA_XTf{A7Mh_GQMd(lx)&KuB8p%SP3-2G-i) zOMEzIoMvRAgrcZbMqd@f>Eq^}_8!~kl~G!PV~m$PJ}LSef}aP4hJD+3x{{G_DXj{I zP-^0K{y?a$wXS!|2bsIH{Hh7DA0CO4W%vm`In!nB0(MvXasLZpz7Z^JZJBVI0dRU*H(?gFvokBr#@L7e_U^C|w*AC9C3b#KVA%zWHWW9L@%bgNV!~`d& zNB@gai^#V#CMLKd|HyEx91B?ZR96aNI47!OG&f?tPN{xh7t-gHUh1?yc_g0vpQ7$w z*Vb3idd2q(1C_*8?P#4Sve^NgSFhjkM5F##^1xnAN`eR$H=Vhph5GIh65$bY^SCrL zy_@emoZBar=&SMsee4=ELeH^>&ra}$&>W@VMEvfgEb({RqGrM{ALfI1Te#BJs)-XZ z{9y`Zt_x?mbPpd!7=7`nK}e*QvWpqR&BEfHEGvKXKi zI+(pYh=!wsn-_WeMI8qQA(KJHVJboff5EU;gVh%dMxA=N+UZh{+fS{ zr!0HrnWu-`(%K!hz}2n}UoCN)s$(d0EAQKCbg-mPxkt#=9)G;T$q!9cr33w<_jLCq z^$PQ)r_cw^IzdE|Cgy>GpxUq=t?D4V1T(zb1l@qaSdK*8r4D}(BL5f#-N?LF{*=)$J==jtz zT;aIzrNz2rLIL16ic0|ouPP5i$7DJ z6Lq8?bpkx&@$gd96|GZ3leH^0{{yb7D>ekRON0<(r0x&pEUob*n(~Br|JL&J3WC|+ zx;(nRiNlR^WAXBg>2nYSrjC=5cHFxMBzOV*#H4rb#5P6i58}lX*EI9Wz~cf7<)A(M z>%~f9iv8ai3bv^4PtQ+?#OR%5MDnu89V+`zL<@V%*S5@YPm-%0vrp4AiLA0J4QB-Z zsVNICLDh|7F>Z*xLB=*&r#>EnY?1Fb%(QWE<@{qxoI}W`D<(nJtlsB_ZkF_s_c$lU z!;3$D1fr>TEbs_f{SkrNAaPjpb0W408EO&o);KI*SgeqoNH6~5=zJv1QbeKT7hSx| z)TGTQ>3$g(I@cMx13p2}Fi zx(%ui>*AAo;eZ|Q{`o!Ss%Go;uH&P-0?CmooZ;>g9taNgXj+U(HGez^XEVXRt}0`6 zOTj$^P@ZnwfF?2et=^(FaQEJBueNE3om%HG34sI@&s6Dw2>< z#Rjk2pUyoScj#6VcO)_#i+uAV6h^S{iiBd;UCxssh26<%X+tHww~H`;#f21(7Q>>~CkueP(^=x0 z;pXp*k&;Ku)PTF8`*qqXV6W&+>J^dOX;YsNirlsb0026KuBxTXB< z9P6%gFKS`t>{oH-luz#&iskf51?|HEK|uzfl`2Lnl6ML31z1&TfyckdqtV34MPM>hl+E+6p489m zW)e`Jwp-EQ1NSxEslG+paZa2d>EmYi(-929!~3}Q@7y@$uE_&kyTp*{5&W}Ra|r{s z!X2jnPD;7W!@AGg+C33~KTW!kYZ&6LJHj1NbPxHeEMasBYvB3oN4QEbuU*P+dqzmY z(--aJyvbun1JnIKv|t+d!}0Kxp{t9u0)Y0Bh$_H)f13JZd6m5`D!?5L{=%wxeDm8D z%w0sLv;x@60yvc17oP5)-hh61dw_<%@KKwzy?{3XeSku2Lh+&4bU1^?j% zh!1rwVS1LtXuZ}qkiJgD0`WBuIS-m#WEtzj(`AF}85u$Fl(3ZQ8W}N{Gno28?^lgW z{gYEY`f*s;GobL*+_VhtO1{|*z4KEU92}UJlxaQLYE@c{6zS56igph&Mvo4bmTK3o zzI-$JvrK{=Uo*GY6zDi~mxR6h=-Hy~uoNH^ohnXr^@D7%w+}pN%#9PggU)^Jo{?-W z69+Z`HQ|arL36ejdg|kueSx8e<&{gujU|da)PE8JpQuv4BVRBiM_MqAOeI~YJP@Q z6XMG4*#W^$r&|$;{&Do=l%RI!y)KS`j(9eeP2>$*YD*a%FSFBEdDjFKi{wSm- z<8y(l=NmOSol8pXrBySQZW)L5bak*S%d*7P({w&R#2W88;mY*EFaI8KiXL^mr3GC1 zI{n$JiB;;J?PH3W{tqulb!nfdXT)SII=l*CgA)O7H^g;t;*AOsC*NFnx^K#1c93XE z$6Q!qs}IwGza=ZY^8dPf@<*t{1wH$xc~9 zJvD_f7$ww0_A++k86*rM``BWPj4c|=V7~W5@B2r5=g0H8=iGD8dheZk&*c-$VXZI3 zHJ@!fWb5>-8&c(4GwI@3a#=C8H4ils8S3XP5RSS+LHy(Brf1TX_pT4kc>b*3v*YKu zVm7w(=N;LAER8b0fh2P9%@=3)Rb5T7ip90JPlw_;*&2OURwBh=FgjRHk1-lbhDG;V z5)0_kZtNv$3P01nX0XA2;$h*U``3+Z=_mXHqC?eMWv1|t{50_IzXtH+VuUa^u9ElO z?g3e6Sx_6`Z=2OjA}4kSY7eyq-dhucTC$F*7`gYYOySUnFK<6)&*~u#fOEp4)h5G3 zrTLVa(&BtGBeYdNhdRH{dLH>~f!4LKX;A2OsuLQh4G|scXhsTd&faJUOH$_Rb0jxi zPx)gw=6-(AzpXAsveTPaI*z-|>Wnem2F~ipn;Kb?SAmOFy;x{%U!XV9CEaGFV+JJ! zVWCEzAOm&Q z2&Ax5$Myxi`VO8{I;Z6{KQU&1YAow>7qRX&AGIm1(LY;1@gpzk81}TrP-IkqV*?Ub zBrN`N2kdVo_a^V;Ub<~<>n`QRo$RcF_+BwMleTc$%}3$;^Y`Bu+K-P-SYI#)Th$-J z#g5Wh!lRbDGbkkWy63#yrHK5FTYu_HJ^l;rkZ-fD?6Y3=oX-yi8@YpWU!8n{R=ZPs zVK>Cx>(yktAGz=d!h+b*5(j|au3j_4yKFpgNC%!+j+(FfdSd zx3Ke~A93m$a$49aPRyy1N)tB-;(>+UN6Z8jkqw8B$OhExKq*19lcJD=D30R!zR--U zEqi}@(3hDpip|EdOOys*=f~jDXFxQo7uV9(UOv1gZ&47T(Fd<@9cm!kGRZ&exhIB3 zESaF(qK#WP51sz{A2w0g7J+(bm_ro{6wee>&JGD#@NA4$UAlmaN_ay22pnC3)*h+Z zJ$VxssdyMwVU_aQJvuR{0>dDgaEx1M)z*V7_oJqJb%z>SgC0XFsa@ZR>gjY}SsEVM zZ^=2#$_CjQkvX(uwmodjUQgbQeGgZ(4;Q)F@ffij63P8wlW91Q4EoxS|HOi+`-N&4 zmdzt@4FWukZLj0U9229z%o@VtTK0@l1Wy*xrQt*1(%txmChL zpD4nCB#nq3k#aI>IR$O}6IjJ6QM;(O47d&b(ZTXN_2#ziWGC#WIz3 z;04lc)_82M^gTD~kN9c0m=)RZsQAu%izf(MM)*eKS5-{=cECdWSgejQfz31AR=Bbq@Cv?qG3~!V zmNVI%nI`GRw#V^uu@&5$R6a{vr`vKSc7oapDJh*b7dAE@n)IFpeHX57K6ogFtJox- zw3R~3&-0QxA8P@YSmE_i%TU( zNPN8oGzm*R5NO_X=@7D^uJ@In^rAN|YAqWj^EIs|sea_b=yQGCguvmY$-j4bDOJTT zE<2PgH% z2GjL?UE;xpp76usSy1{1#@t-a_hE?QR0N~p)jMR$6Czr^6|N3-h-+E1T4uWbvj13M zaNQDZYPD0a>Zn|~_I1bb@As>z&=g3LKDcREPZKH=p6QQgB5}OKl3A|yJ_ls+nTq8X z;p~w)V}~>aoXrjNlhm^Ca^{yu4>B|&bPBDm1BUkt2h3lv6||=9a~a3KB#GRvr^tNa zPdM(wX}z2*U3@)-B6v=8ryyX$aYNJ8+oKYcoT|NktDj?pQyCUQdp+BMax;mOGybL1 z#N55Nx}t=S>!P3c^$5kjU0fTj9o6TJbtJF@kE`bGL7f4$ifZN6yf`sBNuwXmXD4nr zJX89#2@bcXXb?KZZ+^+;Fmz@vsct602l{WZD2RQZuc_40Kv9gF)QJIKAf*g4A)~JJ zQMvn%g9iu_G4I1mYkjib=>0|g_s4`3p@c1jSLb`0(W-T^n4Nwc#Hn&39LQ(0IckP- z<%X^{3DMa?s>?9K2vv?2^4&s8BC*M;<}p9e=P-^=XO|{V<~?6EB$tAYU|OH2hHtaN z0AnMOu}P)^$vp0;qQepN^D`~SOR}4OX?qm+8b-x7RlGnGeyuGE<&5|tfVY1x+i(|h z8WH3Lb8hMI<7DLJAP$f^CLlPvZ+t}J>t|79RuNzDIW+AbF;q-y)8~~ZRS!_YFqpp9 ziC;T$yk-ZSMikvFO4VJ&EM((bD7MKX0a{KYcl#C3!ky4wvE+z0xLqRA$*n6*x)`i> z0jDK6Pfj#`>PUc1@eWiZ<8i8HGx8h&QPZ%ayh5dW?J+GX35SJ{8zx z;2d;qfVqoI=!wCvDxmTNN%ngO#l?kf`%xcgwGuAb7G^2=C{MnA4|r+I@ZQhQG;pxKb|W=Z8L(SFws-w}h;{U;*%OsS#PUU%ktLEo zCvxVBS}+PcbQ{`rrLT(GB)sX0HNkmfQ{2p}4hT+hzB|PBx}L-*A&Xe6QJ6tIyV8L$J|`<0DAn+l8e`RAKUo z*Y`t$$WLVaiuVFxsELv~xvo5J253ZUSX+!HQ5pOd7Qu8BK*l9w$~NXqu5t!8rm0ih z9gWa?yKgUe;Ab3llhRAWQ85lPCy)hv)o%K_(RpThyx2qdwNh1&Ybrm|LogzQhzHr= zm1UULt&;VVV@up~tT2w9u8Rge+TgCeKx~j=jF0O)Wxm4kN-xjtxCfT~Jc*{oa|-31 zK(R1^ zq~~WDQ{|l?bBAgaOF(+q@{aH29k5K{kdJHd$n3;j3`;Am#D`508avYV)TN6qaT-`Eti5}W4u-hz zui(NP#&?6p%2_IHhqG(4Hjutq&H|OUd>>M0xY2BFFiWi!TOlV&`r+TBkEjMEx-Ey8 zULD4JXM>d?%;fu0%?(y{Gj(d%)~GiqWKSFB+ez}mCk{7H=g1>}%>s_w%WvH@+y7*9 z&HERX#X5%z|EUgoki8SaJ&X~G$PB^`-|u%1k666?pqPX0N#m;<6eVwP6_6fl)#*Cm zDV#C%bEeFL8XLaCO2;HyS}9^@MZeiE&njZM zK{q{ZmN%}YY%WiMCm&(3uPwJWwQ+0H^cvyQHdl}pTzMYkS#Tw^fI~YB*Vr(;wK?Pa z*gYj;F&=6yy^|=T8D#D=mqnh_`?xvOw1+K}?f!vna^Oy|0IT}Jm{q|uCL>q-lWC1_ zMDK>FePI1ic?guH<}J?aoMBbxKWdiFs8X)J#jzC4p2N(*rKGT$D`Cvj!9&>Rr|y?> zL_na+;$(wN3M&pkxGXEYbX;LLnC>h*=c*dV*1f^nL`$vR2WQpWSrznXvB(Iqpp4ac z^BB5s+$;%_d&%-f4*mAr$^}mk`DfVH>Qm07T?ZDL!#cVCA08mO-M9f-oQk#CmyTSQ z(E%%fAlc0d%gF5W_5aK5B{9gg?s!xAy}q9sg`B)DU4E&t#0bY>(nk_n6r(g#{ZVX1 z0r9qPxY;)sV4h1b4IrIpt#*&5`zH1VSuzgDecXK7@Fjz>x(s)ro$;EeFhW(FLbnZH zeAQGvIlT#@HB8?-oPLccml#VgeP2anY%V+BPh)&JHiMUtqG=YCbkBM3ljyM?o+g68 z@v)p62vE`kQeK@pQZZMjIi#I`esKEpx0nBZtKBfacr@TH)DXe(IpAUQ~)YYhbg4eVykk^8wCXOiXIQzkAfz3uMyydG@Lp+|Q1;NAUUOvttA;XbP=7t+%Hg|;(ZnFr}2a?j}OM-L`W zvV=fHg0)a*^wb7d99&Ud_#@humSb1vtUD*aiYP4{8%vA0Fs7(?(NZDmSLF!5?J|Ao z9sl&JQAGF1Fl0}?0!|Z_|IYmoUrqSYb`xBln(NXVJqFHd=SRCyc1JS4sN3eV@Qf$Y z|7LPPa~MO?ZyGLdUSdA9+CGp@a&0A5c~V201i*Rh++5cWVAc#KZq{!qip-OP+&cn! zXXtCPXd_S0f}hsa$Nm5x+cjxlP)rhYe+*<1j^!r-46G~6gKx85Q7l!$MZNQQljTA% zN46RZvv)`FBk=59cSm}>FG0i?OTiAMZN5b_^;L}PPW1xa^$`{u=n}$PdrI z>D#T|S-QB@xYJ_uBr*?Um5~!RA(0Ua8Fi9Gz=S5oia-Xpty*8{z1{|r7+-K(4TD(PYhe5!DDG7d`oRMigL?n<^aPbAaZduPDiq52gkaxXupZ1SS z)`|4W+@-*oSpW>{|1KY*y2VmYXlISyEhx6@2Z+dFZN|P()qjd4SuGqaKLLQDY_&vE z9W!<^$Zue;9PM{Y=UnY4iclm>*J+)YRK_`s^*&J z3Y1;u4E9w<{STC|)s6qY-XoO}{{>FOCIMm0fOe1n2hwwd2DpaGtIB`#n;pB=_ggZz z=79(CZOgYLyKw!=Z{XdIylwT)6eddi3rXUwdOLX}xLkHiJn9KmFJt83J#rSs2IGBpF?<+Oj>wsPah+Q>lVaZtwZ!|!KA&m4Yx8DV_;+hCC}qfqQggzF^uBI)blt1(KM++kV4i>53Y_|y zZ#O`K1IR_ot-q0btb7jP&5`+H@4`OX-avodX3DyF8MB3ck;i{q!p>?55)k%urEBf1 zPV$x}CAC_IOp_{A{8JmVFg>5ap_2A3bR!@UDM%uV@50=-M{bk@<`&6Su9kS9fC*dEI$xydId63^jN%yfx*^Id9Oa#Z6%bS@{4VF8y~~*akY7WVHrx>=~47W z6@pbcp-B$kbH6*3&Uv^+6&4hpYD*w5bU$!aSL$=^*pebseJJ3a+QS}Ie}|uIy(ZIN zIS#f>XSM~mc0qA9MlFGNSOpYB9adK=8^_QyA!cBmcbiF4@yAiBE_P#^a{vortkC~T z5sCw~qHrAvCt&)&?x=f&&%S^-3T;fbQu#9eswoGfc*}ShqF{rpSibSq!V`t>xoT$J z^JRR@>4%~*^rg4F;l(KtC4VXJiF6=iI+Q#@`=ATNrjb{)aA{U!k7hvSTNtbX0cNIZ z2}5s!q>R12zCJW3UxntvFx!j{&S@8UI(1z64>+^wIMKm&3Ri9lEpB0qyuu}Pzw9-m zBgj0{w#)zs7QL{Xr(^6Q-)8`>qUj_?JIuVR=z>0^2Z2b3#GB@I=~1M#(juCUyRPJc zoeY@1idur0JYJ_%#UIl??(f;e>%s;TG8f4H^(WOHS|7uk&UWaIN@=}}NITV`0}+!k zvzsa$C2Ap`DU^7dffo8_5^a z0~Ng21{V8=q!#^3OZg0!by1szNb}!NnNG>gG@rk%f6t4BP7SPaCL%l|;si z$#ZrK*D&Jo1nnce(OXIvS6F#k)M1BEaxrzsC=SWT*E@X%7Z@}2p@G;SLBzK&njDfx@?c8f@dgQS+yy zfv0H@d6fi?Gj~1{)0OHt{d4`AW!cO2qSc|!rO=+FfwLaQ5&&-4Ol++6S z;`mw8$XvnC)Hl|`dhYJqR58lcE1%~PG_??pRv}Z7V6iU!{#3uYuNOoV_Q|nw(qVL< zA;x$)+o`U6uVk&S$ymE*77%~8$Nu>_3#8aa+sil6m{ zTmjbq5#)ZA4zCi7Vo{yD-UBm@NwL`3y~8K+Z~AF$#U_}EYJWwd+CyyeP`m~f-)rML zQXiyJHctn^?*~>IB9e|^4bdHnCNoqDPq;EY#}M|O3xBATF}F7FPC`i*cCKP!(fR~_ zPmL6OPb|Q`Nw6x(tSFpkduT3F>rQ?C=W*M&)Gen9onk#U!#9kTHjTgObnv`G!pN7O zLmsAU&GG-BzS%cQ^|Kv)hf!P)i#ruaQlsfvEVn`P8kkJ|ihqTP*TNqAz>aB$pX=P0 zO~xdA)8p*N%7x4dpc&Sb#fk#K!taUN;WQcip5asRi(JEoHncvli}Dc2ME;-sWX^0# XbaT{ga>AQGKtnSz#v0`r*o6NdkJicW literal 0 HcmV?d00001 diff --git a/web/src/assets/brand/logo.svg b/web/src/assets/brand/logo.svg new file mode 100644 index 0000000..36448b4 --- /dev/null +++ b/web/src/assets/brand/logo.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/src/components/AppModal.vue b/web/src/components/AppModal.vue new file mode 100644 index 0000000..9d1be56 --- /dev/null +++ b/web/src/components/AppModal.vue @@ -0,0 +1,29 @@ + + + diff --git a/web/src/components/ExpirePicker.vue b/web/src/components/ExpirePicker.vue new file mode 100644 index 0000000..eb57cb0 --- /dev/null +++ b/web/src/components/ExpirePicker.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/web/src/components/FileDrop.vue b/web/src/components/FileDrop.vue new file mode 100644 index 0000000..7a628a7 --- /dev/null +++ b/web/src/components/FileDrop.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/web/src/components/NotifyPop.vue b/web/src/components/NotifyPop.vue new file mode 100644 index 0000000..af41487 --- /dev/null +++ b/web/src/components/NotifyPop.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/web/src/components/PageShell.vue b/web/src/components/PageShell.vue new file mode 100644 index 0000000..20c594c --- /dev/null +++ b/web/src/components/PageShell.vue @@ -0,0 +1,81 @@ + + + + + diff --git a/web/src/components/Pager.vue b/web/src/components/Pager.vue new file mode 100644 index 0000000..d3c8eb8 --- /dev/null +++ b/web/src/components/Pager.vue @@ -0,0 +1,36 @@ + + + diff --git a/web/src/components/ResultCard.vue b/web/src/components/ResultCard.vue new file mode 100644 index 0000000..5883a3f --- /dev/null +++ b/web/src/components/ResultCard.vue @@ -0,0 +1,96 @@ + + + + + diff --git a/web/src/components/SiteNav.vue b/web/src/components/SiteNav.vue new file mode 100644 index 0000000..aa629dd --- /dev/null +++ b/web/src/components/SiteNav.vue @@ -0,0 +1,74 @@ + + + diff --git a/web/src/components/ToastHost.vue b/web/src/components/ToastHost.vue new file mode 100644 index 0000000..78bf9ef --- /dev/null +++ b/web/src/components/ToastHost.vue @@ -0,0 +1,21 @@ + + + diff --git a/web/src/docs/docsSource.ts b/web/src/docs/docsSource.ts new file mode 100644 index 0000000..6211e09 --- /dev/null +++ b/web/src/docs/docsSource.ts @@ -0,0 +1,88 @@ +/** + * 文档内容源(需求⑤:站内 /docs 页面渲染 docs/api/*.md) + * 双通道: + * A. 构建时嵌入 —— import.meta.glob 静态导入 ../docs/api/*.md 原文(embed 部署/离线可用); + * 文档由 tech-writer 任务产出,写入 docs/api/ 后重新构建前端即生效。 + * B. 运行时 fetch —— GET ${API_BASE}/docs/api/.md(后端若挂载 docs 目录则优先最新内容)。 + * 约定:每个 md 的一级标题(# ...)作为侧边栏章节名;排序按 ORDER 列表(tech-writer 文件命名对齐)。 + */ + +// 注意:本文件位于 web/src/docs/,文档源在项目根 docs/api/ → 相对路径需上溯三级。 +// docs/ 目录由 tech-writer 任务产出;为空时 glob 返回空对象,/docs 页展示空态。 +const markdownFiles = import.meta.glob('../../../docs/api/*.md', { + query: '?raw', + import: 'default', + eager: true, +}) as Record + +export interface DocEntry { + slug: string + title: string + /** 构建时嵌入的 markdown 原文;无则运行时 fetch */ + embedded: string | null + /** 侧边栏排序权重 */ + order: number +} + +/** 侧边栏章节顺序(对齐 t4 交付清单;未列出的文件按文件名排序追加在最后) */ +const ORDER = ['README', 'share', 'chunk', 'presign', 'admin', 'audit', 'errors', 'config', 'logo', 'webdav', 'env'] + +function slugFromPath(p: string): string { + const base = p.split('/').pop() ?? p + return base.replace(/\.md$/, '') +} + +function titleOf(md: string, slug: string): string { + const m = /^#\s+(.+)$/m.exec(md) + return m ? m[1].trim() : slug +} + +export const docs: DocEntry[] = Object.entries(markdownFiles) + .map(([path, content]) => { + const slug = slugFromPath(path) + const orderIdx = ORDER.indexOf(slug) + return { + slug, + title: titleOf(content, slug), + embedded: content, + order: orderIdx >= 0 ? orderIdx : 100 + slug.localeCompare(''), + } + }) + .sort((a, b) => a.order - b.order || a.slug.localeCompare(b.slug)) + +export const hasEmbeddedDocs = docs.length > 0 + +/** OpenAPI 规范:构建时嵌入(yaml 原文),供 /openapi 页 Swagger UI 使用 */ +const openApiFiles = import.meta.glob('../../../docs/openapi.yaml', { + query: '?raw', + import: 'default', + eager: true, +}) as Record + +export const embeddedOpenApiYaml: string | null = Object.values(openApiFiles)[0] ?? null + +/** 取单篇文档内容:优先嵌入,其次运行时 fetch */ +export async function loadDoc(doc: DocEntry): Promise { + if (doc.embedded !== null) return doc.embedded + const { API_BASE } = await import('@/api/paths') + const url = `${API_BASE}/docs/api/${encodeURIComponent(doc.slug)}.md` + const res = await fetch(url) + if (!res.ok) throw new Error(`文档加载失败(HTTP ${res.status})`) + return res.text() +} + +/** 运行时扫描远端文档列表(后端挂载 docs/api/ 时可见嵌入集之外的新文档) */ +export async function discoverRemoteDocs(): Promise { + try { + const { API_BASE } = await import('@/api/paths') + const res = await fetch(`${API_BASE}/docs/api/index.json`) + if (!res.ok) return [] + const list = (await res.json()) as { slug: string; title?: string }[] + const known = new Set(docs.map((d) => d.slug)) + return list + .filter((d) => d.slug && !known.has(d.slug)) + .map((d) => ({ slug: d.slug, title: d.title ?? d.slug, embedded: null, order: 200 })) + } catch { + return [] + } +} diff --git a/web/src/i18n/index.ts b/web/src/i18n/index.ts new file mode 100644 index 0000000..1de778f --- /dev/null +++ b/web/src/i18n/index.ts @@ -0,0 +1,67 @@ +/** + * vue-i18n 装配(需求⑩): + * - legacy:false(Composition API 模式) + * - 默认语言:navigator.language(zh 开头 → zh-CN,其余 → en-US) + * - localStorage 记忆用户选择(键 fcb_locale) + */ +import { createI18n } from 'vue-i18n' +import zhCN from '@/locales/zh-CN' +import enUS from '@/locales/en-US' + +export type Locale = 'zh-CN' | 'en-US' +export const LOCALES: Locale[] = ['zh-CN', 'en-US'] + +const STORAGE_KEY = 'fcb_locale' + +function readStoredLocale(): Locale | null { + try { + const v = localStorage.getItem(STORAGE_KEY) + return v === 'zh-CN' || v === 'en-US' ? v : null + } catch { + return null + } +} + +/** 需求⑩:默认跟随 navigator.language —— zh 开头 → zh-CN,其余 → en-US */ +function detectLocale(): Locale { + const saved = readStoredLocale() + if (saved) return saved + const lang = (typeof navigator !== 'undefined' ? navigator.language : 'en') ?? 'en' + return lang.toLowerCase().startsWith('zh') ? 'zh-CN' : 'en-US' +} + +function persist(locale: Locale): void { + try { + localStorage.setItem(STORAGE_KEY, locale) + } catch { + /* localStorage 不可用时忽略 */ + } +} + +export const i18n = createI18n({ + legacy: false, + locale: detectLocale(), + fallbackLocale: 'zh-CN', + messages: { + 'zh-CN': zhCN, + 'en-US': enUS, + }, + // 生产环境关闭缺失告警,避免控制台噪音 + missingWarn: false, + fallbackWarn: false, +}) + +/** 当前语言(响应式 Composer) */ +export function currentLocale(): Locale { + return (i18n.global.locale.value ?? 'zh-CN') as Locale +} + +/** 切换语言并记忆(需求⑩:导航栏切换器 + localStorage) */ +export function setLocale(locale: Locale): void { + i18n.global.locale.value = locale + document.documentElement.lang = locale + persist(locale) +} + +/** 全局 t(非组件上下文:router/store/utils 用) */ +export const tGlobal = i18n.global.t diff --git a/web/src/locales/en-US.ts b/web/src/locales/en-US.ts new file mode 100644 index 0000000..0c8a02a --- /dev/null +++ b/web/src/locales/en-US.ts @@ -0,0 +1,409 @@ +/** + * English catalog. Key structure mirrors zh-CN.ts; + * add new copy to zh-CN first, then sync here. + */ +export default { + app: { + tagline: 'File Drop', + description: 'A ready-to-use file sharing service', + }, + nav: { + home: 'Share', + docs: 'API Docs', + openapi: 'OpenAPI', + admin: 'Admin', + homeTitle: '{name} — Home', + mainNav: 'Main navigation', + }, + theme: { + label: 'Theme', + light: 'Light', + dark: 'Dark', + system: 'System', + }, + lang: { + label: 'Language', + }, + footer: { + linkNav: 'Footer links', + docs: 'API Docs', + openapi: 'OpenAPI', + admin: 'Admin', + copyright: '© {year} {name}', + }, + notify: { + title: 'System Notice', + close: 'Got it', + }, + common: { + loading: 'Loading…', + cancel: 'Cancel', + save: 'Save', + search: 'Search', + refresh: 'Refresh', + copy: 'Copy', + copied: 'Copied', + copyFailed: 'Copy failed', + close: 'Close', + actions: 'Actions', + all: 'All', + query: 'Query', + reset: 'Reset', + previousPage: 'Previous', + nextPage: 'Next', + pagerInfo: '{total} records · page {page}/{pages}', + text: 'Text', + file: 'File', + success: 'Success', + failed: 'Failed', + denied: 'Denied', + none: '-', + }, + time: { + forever: 'Never expires', + permanent: 'Permanent', + expired: 'Expired', + lessThanMinute: 'less than a minute', + minutes: '{n} min', + hoursMinutes: '{h} h {m} min', + daysHours: '{d} d {h} h', + }, + expireStyle: { + day: 'Days', + hour: 'Hours', + minute: 'Minutes', + count: 'Times', + forever: 'Forever', + }, + home: { + heroTitle: '{name} · File Drop', + heroDesc: 'No signup — share text or files and hand over a pickup code', + pickupPlaceholder: 'Enter a pickup code', + pickupButton: 'Pick up', + pickupRequired: 'Please enter a pickup code', + tabText: 'Share text', + tabFile: 'Share file', + textContent: 'Text content', + textPlaceholder: 'Paste the text or code snippet to share…', + textBytes: '{bytes} / 222 KB (use file sharing for larger content)', + textTooLong: 'Content too long (over 222KB) — please share it as a file instead', + textRequired: 'Enter the text to share', + + customCode: 'Custom pickup code (optional)', + + customCodeHint: 'Leave empty for random; 4-8 letters/digits', + + customCodeInvalid: 'Pickup code must be 4-8 letters or digits', + + customCodeTaken: 'This pickup code is already taken', + generateCode: 'Generate code', + fileRequired: 'Please choose a file to share', + fileTooLarge: 'File exceeds the size limit (max {size})', + chunkedUploading: 'Chunked upload', + uploading: 'Uploading', + uploadingDots: 'Uploading…', + uploadAndShare: 'Upload & generate code', + uploadDisabled: 'Guest uploads are disabled. Please contact the administrator if you need to share.', + shareAnother: 'Share another one', + textShared: 'Text shared', + fileShared: 'File shared', + uploadCancelled: 'Upload cancelled', + shareFailed: 'Share failed, please try again later', + uploadFailed: 'Upload failed, please retry', + rateLimited: 'Too many requests, please slow down', + notInitialized: 'System is not initialized yet. An administrator must finish the setup first.', + }, + result: { + badge: 'Shared', + code: 'Pickup code', + link: 'Pickup link', + copyLink: 'Copy link', + copyLinkCode: 'Copy link & code', + copyCode: 'Copy code', + codeCopied: 'Pickup code copied', + linkCopied: 'Pickup link copied', + linkCodeCopied: 'Link and code copied', + clickCopyCode: 'Click to copy pickup code', + expires: 'Expires in: {value}', + forever: 'Forever', + hint: 'Send the code or link to the recipient; they can pick it up from the home page.', + copyFailed: 'Copy failed — please select the text manually', + }, + pickup: { + emptyCode: 'Pickup code is empty', + querying: 'Looking up code {code} …', + failed: 'Pickup failed', + failedDefault: 'Pickup failed, please try again later', + notFound: 'Code not found or the share has expired', + confirmHint: 'Double-check the code, or ask the sender to share it again', + retryPlaceholder: 'Enter another pickup code', + retryButton: 'Try again', + remainingUnlimited: 'Unlimited', + remainingCount: '{n} left', + expireAt: 'Expires: {time}', + loadingText: 'Fetching content…', + copyContent: 'Copy content', + downloadTxt: 'Download as .txt', + downloaded: 'Download complete', + downloadFailed: 'Download failed, please retry', + copied: 'Content copied', + sizeUsed: 'Size {size} · picked up {n} times', + downloading: 'Downloading {percent}%', + downloadFile: 'Download ({size})', + }, + expire: { + value: 'Amount', + label: 'Expires in', + foreverOption: 'Never expires', + countOption: 'After N pickups', + countHint: 'The share becomes invalid after the given number of pickups', + timeHint: 'Valid for {value} {unit}', + foreverHint: 'The share stays valid until an administrator deletes it', + maxSecondsHint: 'At most {value}', + maxCountHint: 'At most {n} pickups', + }, + drop: { + aria: 'Choose or drop a file', + zone: 'Click to choose or drop a file here', + maxSize: 'Max {size} per file', + noLimit: 'A pickup code is generated after upload', + remove: 'Remove', + tooLarge: 'File size {size} exceeds the limit {limit}', + typeHint: 'Allowed types: {types}', + }, + docs: { + searchPlaceholder: 'Search documentation…', + notGenerated: 'Docs not generated yet', + buildHint: 'They are collected from docs/api/*.md at build time', + noMatch: 'No matching sections', + tocTitle: 'On this page', + loading: 'Loading document…', + preparing: 'API docs are on the way', + preparingHint: + 'Sources live in the project docs/api/ directory (each .md is one section). Rebuild the frontend to embed them for offline use.', + emptyContent: 'Document is empty', + loadFailed: 'Failed to load document "{title}"', + sidebar: 'Documentation sections', + }, + openapi: { + title: 'OpenAPI 3.0 Specification', + statusOk: 'Loaded', + statusError: 'Failed to load spec', + statusLoading: 'Loading…', + source: 'Source: {source}', + sourceEmbedded: 'Embedded docs/openapi.yaml at build time', + notAvailable: 'openapi.yaml is not generated or cannot be accessed', + notAvailableHint: + 'The spec file lives in the project docs/openapi.yaml. Rebuilding the frontend embeds it; you can also deploy it to {url} for runtime loading.', + }, + notFound: { + title: 'Page not found', + desc: 'The address may have changed', + back: 'Back home', + }, + admin: { + login: { + title: 'Administrator Sign-in', + subtitle: '{name} · Admin Console', + password: 'Admin password', + passwordPlaceholder: 'Enter the admin password', + submit: 'Sign in', + wrongPassword: 'Incorrect password', + failed: 'Sign-in failed, please try again later', + required: 'Please enter the admin password', + hint: 'The password is configured by the deployer via env vars or system settings; repeated failures trigger IP rate limiting.', + }, + nav: { + title: 'Admin', + files: 'Files', + audit: 'Audit Log', + settings: 'Settings', + logout: 'Sign out', + menu: 'Admin menu', + loggedOut: 'Signed out', + }, + files: { + title: 'File Management', + totalRecords: '{total} shares in total', + searchPlaceholder: 'Search code / file name', + batchDelete: 'Delete selected', + batchDeleteWithCount: 'Delete selected ({count})', + deleteSelectedTitle: 'Delete {count} selected items', + selectFirst: 'Select rows first', + loading: 'Loading…', + empty: 'No shares yet', + loadFailed: 'Failed to load the file list', + colCode: 'Code', + colName: 'Name', + colType: 'Type', + colSize: 'Size', + colUsed: 'Picked', + colRemaining: 'Remaining', + colExpireAt: 'Expires', + colStatus: 'Status', + colCreatedAt: 'Created', + remainingUnlimited: '∞', + remainingCount: '{n} left', + statusValid: 'Active', + statusExpired: 'Expired', + copyCode: 'Copy code', + copyLink: 'Copy link', + edit: 'Edit', + fetchText: 'Fetch text', + delete: 'Delete', + confirmDelete: 'Delete "{name}"? This cannot be undone.', + confirmBatchDelete: 'Delete {count} selected shares? This cannot be undone.', + deleteSuccess: 'Deleted', + batchDeleteSuccess: 'Batch deleted', + deleteFailed: 'Delete failed', + batchDeleteFailed: 'Batch delete failed', + nothingChanged: 'Nothing changed', + updateSuccess: 'Updated', + updateFailed: 'Update failed', + fetchTextFailed: 'Failed to fetch content (the share may have expired)', + linkCopied: 'Pickup link copied', + codeCopied: 'Pickup code copied', + editModalTitle: 'Edit share', + expireAtHint: 'Expires at (leave empty for never)', + expireCountHint: 'Remaining pickups (-1 for unlimited)', + }, + audit: { + title: 'Audit Log', + subtitle: 'Upload / download events: time, IP, UA, device, result, bytes and duration', + action: 'Action', + result: 'Result', + actionUpload: 'Upload', + actionDownload: 'Download', + filterIp: 'IP', + filterStart: 'Start time', + filterEnd: 'End time', + empty: 'No audit records yet (only upload / download actions are recorded)', + loadFailed: 'Failed to load the audit log', + colTime: 'Time', + colAction: 'Action', + colResult: 'Result', + colFile: 'File', + colCode: 'Code', + colBytes: 'Bytes', + colIp: 'IP', + colDevice: 'Device', + colDuration: 'Duration', + colUaError: 'UA / Error', + }, + settings: { + title: 'System Settings', + subtitle: 'Site name and branding (custom values win; leave empty to restore built-in defaults)', + restoreDefaults: 'Restore defaults', + restoreDefaultsDone: 'Defaults filled in — click save to apply', + loading: 'Loading settings…', + loadFailed: 'Failed to load settings', + sectionBasic: 'Basic', + siteName: 'Site name · site_name', + siteNameHint: 'Shown in the nav bar, login page and browser title', + siteDomain: 'Public site domain', + + siteDomainHint: 'http(s)://host[:port], no path; leave empty to use the current address in share links', + sectionLogo: 'Nav logo', + logoUrl: 'Logo image URL · logo_url', + uploadImage: 'Upload image', + logoHint: 'Enter a URL or upload a local image (≤256KB, stored inline); leave empty for the built-in default', + imageTooLarge: 'Image exceeds 256KB — compress it or paste an image URL instead', + imageLoaded: 'Image loaded — click save to apply site-wide', + imageReadFailed: 'Failed to read the image', + navPreview: 'Nav bar preview:', + sectionFavicon: 'Browser favicon', + faviconUrl: 'Favicon URL · favicon_url', + faviconHint: 'Use a square PNG/ICO; leave empty for the built-in default', + faviconPreviewHint: 'Browser tab icon (applied after saving and refreshing)', + saveAll: 'Save settings (applies site-wide)', + saved: 'Settings saved site-wide', + saveFailed: 'Save failed', + sectionPassword: 'Change admin password', + passwordHint: 'After saving, all signed-in sessions are invalidated and you must sign in again', + oldPassword: 'Old password', + newPassword: 'New password (at least 6 characters)', + confirmPassword: 'Confirm new password', + pwdRequired: 'Please fill in the old and new passwords', + pwdTooShort: 'The new password must be at least 6 characters', + pwdMismatch: 'The two passwords do not match', + pwdChanged: 'Password changed — please sign in again with the new password', + pwdChangeFailed: 'Change failed', + pwdWrong: 'Old password is incorrect', + // —— v2 sections —— + sectionBackground: 'Background image', + backgroundUrl: 'Background URL · background_url', + backgroundHint: + 'http(s) image URL, data:image image or site-relative path (≤2048 chars); leave empty for the theme default', + sectionFooter: 'Footer', + footerText: 'Footer text · footer_text', + footerTextHint: 'Shown at the page bottom as plain text (≤2000 chars); leave empty for the default tagline', + footerBeian: 'ICP filing number · footer_beian', + footerBeianHint: 'e.g. 京ICP备2024xxxxxx号-1 (≤128 chars)', + sectionNotify: 'System notice', + notifyEnabled: 'Show floating notice · notify_enabled', + notifyTitle: 'Notice title · notify_title', + notifyTitleHint: 'Leave empty for the default title "System Notice" (≤128 chars)', + notifyContent: 'Notice content · notify_content', + notifyContentHint: 'Controlled HTML such as
    is allowed (≤2000 chars)', + sectionSavePolicy: 'Save policy', + maxSaveSeconds: 'Max save seconds · max_save_seconds', + maxSaveSecondsHint: '0 = unlimited (server default 7-day fallback), max {max} seconds (365 days)', + maxSaveCount: 'Max pickup count · max_save_count', + maxSaveCountHint: '0 = unlimited, max {max}', + sectionStorage: 'Storage policy', + maxFileSize: 'Max file size · max_file_size (bytes)', + maxFileSizeHint: '0 = fall back to uploadSize (currently {fallback}), max {max} bytes (10 GiB)', + allowedFileTypes: 'Allowed types · allowed_file_types', + allowedFileTypesHint: 'Comma separated: extensions (jpg) or MIME (image/*); * means no limit', + sectionUploadRate: 'Upload rate limit', + uploadCount: 'Uploads per window · uploadCount', + uploadCountHint: 'Min 1, max {max}', + uploadMinute: 'Window length (minutes) · uploadMinute', + uploadMinuteHint: 'Min 1, max {max}', + // —— v3: friendly units + storage engine —— + unitHour: 'Hour(s)', + unitDay: 'Day(s)', + unitMB: 'MB', + unitGB: 'GB', + maxSaveTime: 'Max save time · max_save_seconds', + maxSaveTimeHint: '0 = unlimited (server default 7-day fallback), max 365 days', + saveTimeUnlimited: 'Unlimited (0)', + maxFileSizeFriendly: 'Max file size · max_file_size', + maxFileSizeHintV3: '0 = fall back to uploadSize (current {fallback}), max 10 GB', + sizeUnlimited: 'Unlimited (0)', + sectionEngine: 'Storage engine', + engineCurrent: 'Current engine', + engineLocal: 'Local storage', + engineWebdav: 'WebDAV', + engineS3: 'S3 object storage', + engineSwitch: 'Switch to this engine', + engineSwitching: 'Switching…', + engineSwitchOk: 'Storage engine switched to {engine}', + engineSwitchFail: 'Switch failed (previous engine kept)', + engineParamsTitle: 'Engine parameters', + engineParamsSaved: 'Engine parameters saved', + localRoot: 'Storage root · local_storage_path', + localRootHint: 'Empty = system default data directory; applies to new writes', + webdavUrl: 'Server URL · webdav_url', + webdavUrlHint: 'e.g. https://dav.example.com/dav/', + webdavRoot: 'Remote root · webdav_root_path', + webdavRootHint: 'Remote base directory (created recursively if missing)', + webdavUser: 'Username · webdav_username', + webdavPass: 'Password · webdav_password', + secretKeepHint: 'Empty or ****** = keep unchanged', + s3Endpoint: 'Endpoint · s3_endpoint_url', + s3EndpointHint: 'e.g. https://s3.example.com:9000 (leave empty for AWS)', + s3Bucket: 'Bucket · s3_bucket_name', + s3Region: 'Region · s3_region_name', + s3Ak: 'AccessKeyID · s3_access_key_id', + s3Sk: 'SecretAccessKey · s3_secret_access_key', + s3Token: 'Session token · aws_session_token (optional)', + s3Style: 'Addressing style · s3_addressing_style', + styleAuto: 'auto', + stylePath: 'path (typical for MinIO)', + styleVirtual: 'virtual-hosted', + engineParamsSave: 'Save engine parameters', + approxSize: '≈ {size}', + }, + }, +} diff --git a/web/src/locales/zh-CN.ts b/web/src/locales/zh-CN.ts new file mode 100644 index 0000000..ac7e76c --- /dev/null +++ b/web/src/locales/zh-CN.ts @@ -0,0 +1,403 @@ +/** + * 中文文案(默认语言)。键结构与其他语言包保持一致; + * 新增文案请先加到这里,再同步 en-US.ts。 + */ +export default { + app: { + tagline: '文件快传', + description: '开箱即用的文件快传系统', + }, + nav: { + home: '分享', + docs: 'API 文档', + openapi: 'OpenAPI', + admin: '管理后台', + homeTitle: '{name} — 首页', + mainNav: '主导航', + }, + theme: { + label: '主题', + light: '浅色', + dark: '深色', + system: '跟随系统', + }, + lang: { + label: '语言', + }, + footer: { + linkNav: '页脚链接', + docs: 'API 文档', + openapi: 'OpenAPI', + admin: '管理后台', + copyright: '© {year} {name}', + }, + notify: { + title: '系统通知', + close: '知道了', + }, + common: { + loading: '加载中…', + cancel: '取消', + save: '保存', + search: '搜索', + refresh: '刷新', + copy: '复制', + copied: '已复制', + copyFailed: '复制失败', + close: '关闭', + actions: '操作', + all: '全部', + query: '查询', + reset: '重置', + previousPage: '上一页', + nextPage: '下一页', + pagerInfo: '共 {total} 条 · 第 {page}/{pages} 页', + text: '文本', + file: '文件', + success: '成功', + failed: '失败', + denied: '拒绝', + none: '-', + }, + time: { + forever: '永久有效', + permanent: '永久', + expired: '已过期', + lessThanMinute: '不足 1 分钟', + minutes: '{n} 分钟', + hoursMinutes: '{h} 小时 {m} 分', + daysHours: '{d} 天 {h} 小时', + }, + expireStyle: { + day: '天', + hour: '小时', + minute: '分钟', + count: '次数', + forever: '永久', + }, + home: { + heroTitle: '{name} · 文件快传', + heroDesc: '无需注册,文本文件一键分享,取件码即可领取', + pickupPlaceholder: '输入取件码直接领取', + pickupButton: '取 件', + pickupRequired: '请输入取件码', + tabText: '分享文本', + tabFile: '分享文件', + textContent: '文本内容', + textPlaceholder: '粘贴要分享的文本、代码片段…', + textBytes: '{bytes} / 222 KB(超出请改用文件分享)', + textTooLong: '内容过多(超过 222KB),建议采用文件形式分享', + textRequired: '请输入要分享的文本内容', + customCode: '自定义提取码(可选)', + customCodeHint: '留空随机生成;4-8 位字母或数字', + customCodeInvalid: '提取码须为 4-8 位字母或数字', + customCodeTaken: '该提取码已被占用,请换一个', + generateCode: '生成取件码', + fileRequired: '请选择要分享的文件', + fileTooLarge: '文件大小超过限制(最大 {size})', + chunkedUploading: '分片上传中', + uploading: '上传中', + uploadingDots: '上传中…', + uploadAndShare: '上传并生成取件码', + uploadDisabled: '管理员已关闭访客上传功能,如需分享请联系管理员', + shareAnother: '再分享一个', + textShared: '文本分享成功', + fileShared: '文件分享成功', + uploadCancelled: '上传已取消', + shareFailed: '分享失败,请稍后重试', + uploadFailed: '上传失败,请重试', + rateLimited: '操作过于频繁,请稍后再试', + notInitialized: '系统尚未初始化,请管理员先完成初始化配置', + }, + result: { + badge: '分享成功', + code: '取件码', + link: '取件链接', + copyLink: '复制链接', + copyLinkCode: '复制链接和提取码', + copyCode: '复制取件码', + codeCopied: '取件码已复制', + linkCopied: '取件链接已复制', + linkCodeCopied: '链接和提取码已复制', + clickCopyCode: '点击复制提取码', + expires: '有效期:{value}', + forever: '永久', + hint: '把取件码或链接发给对方,对方在首页输入取件码即可领取。', + copyFailed: '复制失败,请手动选择复制', + }, + pickup: { + emptyCode: '取件码为空', + querying: '正在查询取件码 {code} …', + failed: '取件失败', + failedDefault: '取件失败,请稍后重试', + notFound: '取件码不存在或分享已过期', + confirmHint: '请确认取件码是否正确,或联系分享人重新发送', + retryPlaceholder: '输入其他取件码', + retryButton: '重新取件', + remainingUnlimited: '不限次数', + remainingCount: '剩余 {n} 次', + expireAt: '过期时间:{time}', + loadingText: '正在获取内容…', + copyContent: '复制内容', + downloadTxt: '下载为 .txt', + downloaded: '下载完成', + downloadFailed: '下载失败,请重试', + copied: '内容已复制', + sizeUsed: '大小 {size} · 已被领取 {n} 次', + downloading: '下载中 {percent}%', + downloadFile: '下载文件({size})', + }, + expire: { + value: '数值', + label: '有效期', + foreverOption: '永久有效', + countOption: '按次数', + countHint: '分享在被领取指定次数后失效', + timeHint: '有效期 {value} {unit}', + foreverHint: '分享将一直有效,直到管理员删除', + maxSecondsHint: '最长 {value}', + maxCountHint: '最多 {n} 次', + }, + drop: { + aria: '选择或拖拽文件', + zone: '点击选择或拖拽文件到此处', + maxSize: '单文件最大 {size}', + noLimit: '上传后自动生成取件码', + remove: '移除', + tooLarge: '文件大小 {size} 超过限制 {limit}', + typeHint: '仅支持 {types}', + }, + docs: { + searchPlaceholder: '检索文档内容…', + notGenerated: '文档尚未生成', + buildHint: '构建时将从 docs/api/*.md 自动收录', + noMatch: '没有匹配的章节', + tocTitle: '本页目录', + loading: '加载文档…', + preparing: 'API 文档筹备中', + preparingHint: + '文档源位于项目 docs/api/ 目录(每个 .md 一级标题作为章节名)。重新构建前端后,文档将内嵌到页面中离线可用。', + emptyContent: '文档内容为空', + loadFailed: '文档「{title}」加载失败', + sidebar: '文档章节', + }, + openapi: { + title: 'OpenAPI 3.0 接口规范', + statusOk: '加载成功', + statusError: '规范加载失败', + statusLoading: '加载中…', + source: '来源:{source}', + sourceEmbedded: '构建内嵌 docs/openapi.yaml', + notAvailable: 'openapi.yaml 尚未生成或无法访问', + notAvailableHint: + '规范文件位于项目 docs/openapi.yaml。重新构建前端会将其内嵌;也可将文件部署到 {url} 供运行时加载。', + }, + notFound: { + title: '页面不存在', + desc: '你访问的地址可能已变更', + back: '回到首页', + }, + admin: { + login: { + title: '管理员登录', + subtitle: '{name} · 管理后台', + password: '管理员密码', + passwordPlaceholder: '请输入管理员密码', + submit: '登 录', + wrongPassword: '密码错误', + failed: '登录失败,请稍后重试', + required: '请输入管理员密码', + hint: '密码由部署方在环境变量或系统设置中配置;连续输错会触发 IP 限流保护。', + }, + nav: { + title: '管理后台', + files: '文件管理', + audit: '审计日志', + settings: '系统设置', + logout: '退出登录', + menu: '后台菜单', + loggedOut: '已退出登录', + }, + files: { + title: '文件管理', + totalRecords: '共 {total} 个分享记录', + searchPlaceholder: '搜索取件码 / 文件名', + batchDelete: '批量删除', + batchDeleteWithCount: '批量删除({count})', + deleteSelectedTitle: '删除选中的 {count} 项', + selectFirst: '先勾选要删除的行', + loading: '加载中…', + empty: '暂无分享记录', + loadFailed: '文件列表加载失败', + colCode: '取件码', + colName: '名称', + colType: '类型', + colSize: '大小', + colUsed: '已领取', + colRemaining: '剩余', + colExpireAt: '过期时间', + colStatus: '状态', + colCreatedAt: '创建时间', + remainingUnlimited: '不限', + remainingCount: '{n} 次', + statusValid: '有效', + statusExpired: '已过期', + copyCode: '复制码', + copyLink: '复制链接', + edit: '编辑', + fetchText: '取内容', + delete: '删除', + confirmDelete: '确认删除分享「{name}」?该操作不可恢复。', + confirmBatchDelete: '确认删除选中的 {count} 个分享?该操作不可恢复。', + deleteSuccess: '删除成功', + batchDeleteSuccess: '批量删除成功', + deleteFailed: '删除失败', + batchDeleteFailed: '批量删除失败', + nothingChanged: '没有修改任何字段', + updateSuccess: '更新成功', + updateFailed: '更新失败', + fetchTextFailed: '内容获取失败(分享可能已过期)', + linkCopied: '取件链接已复制', + codeCopied: '取件码已复制', + editModalTitle: '编辑分享', + expireAtHint: '过期时间(留空表示永久)', + expireCountHint: '剩余可领取次数(-1 表示不限)', + }, + audit: { + title: '审计日志', + subtitle: '记录上传 / 下载动作:时间、IP、UA、设备、结果、字节数与耗时', + action: '动作', + result: '结果', + actionUpload: '上传', + actionDownload: '下载', + filterIp: 'IP', + filterStart: '开始时间', + filterEnd: '结束时间', + empty: '暂无审计记录(审计仅记录上传 / 下载动作)', + loadFailed: '审计日志加载失败', + colTime: '时间', + colAction: '动作', + colResult: '结果', + colFile: '文件', + colCode: '取件码', + colBytes: '字节数', + colIp: 'IP', + colDevice: '设备', + colDuration: '耗时', + colUaError: 'UA / 错误', + }, + settings: { + title: '系统设置', + subtitle: '站点名称与 Logo(自定义优先,留空恢复内置默认)', + restoreDefaults: '恢复默认值', + restoreDefaultsDone: '已填回默认值,点击保存生效', + loading: '加载配置中…', + loadFailed: '配置读取失败', + sectionBasic: '基本', + siteName: '站点名称 site_name', + siteNameHint: '显示在导航栏、登录页与浏览器标题', + siteDomain: '网站对外域名', + siteDomainHint: 'http(s)://域名[:端口],不带路径;留空则分享链接用当前访问地址', + sectionLogo: '导航 Logo', + logoUrl: 'Logo 图片地址 logo_url', + uploadImage: '上传图片', + logoHint: '支持填写 URL 或上传本地图片(≤256KB,转存为内嵌数据);留空使用内置默认', + imageTooLarge: '图片超过 256KB,请压缩后重试或直接填写图片 URL', + imageLoaded: '图片已载入,点击保存后全站生效', + imageReadFailed: '图片读取失败', + navPreview: '导航栏实际效果:', + sectionFavicon: '浏览器图标 Favicon', + faviconUrl: 'Favicon 地址 favicon_url', + faviconHint: '建议使用 PNG/ICO 方形图标;留空使用内置默认', + faviconPreviewHint: '浏览器标签页图标(保存后刷新页面生效)', + saveAll: '保存设置(全站生效)', + saved: '设置已保存,全站生效', + saveFailed: '保存失败', + sectionPassword: '修改管理员密码', + passwordHint: '保存后所有已登录会话失效,需重新登录', + oldPassword: '旧密码', + newPassword: '新密码(至少 6 位)', + confirmPassword: '确认新密码', + pwdRequired: '请填写旧密码与新密码', + pwdTooShort: '新密码至少 6 位', + pwdMismatch: '两次输入的新密码不一致', + pwdChanged: '密码已修改,请使用新密码重新登录', + pwdChangeFailed: '修改失败', + pwdWrong: '旧密码错误', + // —— v2 新增分区 —— + sectionBackground: '背景图', + backgroundUrl: '背景图地址 background_url', + backgroundHint: '支持 http(s) 图片地址、data:image 图片或站内相对路径(≤2048 字符);留空使用主题默认', + sectionFooter: '页脚', + footerText: '页脚文案 footer_text', + footerTextHint: '展示在页面底部,支持纯文本(≤2000 字符);留空显示默认标语', + footerBeian: '备案号 footer_beian', + footerBeianHint: '如 京ICP备2024xxxxxx号-1(≤128 字符)', + sectionNotify: '系统通知', + notifyEnabled: '启用右上角通知 notify_enabled', + notifyTitle: '通知标题 notify_title', + notifyTitleHint: '留空显示默认标题「系统通知」(≤128 字符)', + notifyContent: '通知内容 notify_content', + notifyContentHint: '支持 等受控 HTML(≤2000 字符)', + sectionSavePolicy: '保存策略', + maxSaveSeconds: '最长保存秒数 max_save_seconds', + maxSaveSecondsHint: '0 = 不限制(服务端默认 7 天兜底),最大 {max} 秒(365 天)', + maxSaveCount: '最大可取次数 max_save_count', + maxSaveCountHint: '0 = 不限制,最大 {max} 次', + sectionStorage: '存储策略', + maxFileSize: '单文件上限 max_file_size(字节)', + maxFileSizeHint: '0 = 回落 uploadSize(当前 {fallback}),最大 {max} 字节(10 GiB)', + allowedFileTypes: '允许类型 allowed_file_types', + allowedFileTypesHint: '逗号分隔:扩展名(jpg)或 MIME(image/*),* 不限制', + sectionUploadRate: '上传频率限制', + uploadCount: '窗口内允许上传次数 uploadCount', + uploadCountHint: '最小 1,最大 {max}', + uploadMinute: '频率窗口(分钟)uploadMinute', + uploadMinuteHint: '最小 1,最大 {max}', + // —— v3:单位友好化 + 存储引擎 —— + unitHour: '小时', + unitDay: '天', + unitMB: 'MB', + unitGB: 'GB', + maxSaveTime: '最长保存时间 max_save_seconds', + maxSaveTimeHint: '0 = 不限制(服务端默认 7 天兜底),最大 365 天', + saveTimeUnlimited: '不限制(0)', + maxFileSizeFriendly: '单文件上限 max_file_size', + maxFileSizeHintV3: '0 = 回落 uploadSize(当前 {fallback}),最大 10 GB', + sizeUnlimited: '不限制(0)', + sectionEngine: '存储引擎', + engineCurrent: '当前引擎', + engineLocal: '本地存储', + engineWebdav: 'WebDAV', + engineS3: 'S3 对象存储', + engineSwitch: '切换到该引擎', + engineSwitching: '切换中…', + engineSwitchOk: '存储引擎已切换为 {engine}', + engineSwitchFail: '切换失败(已保持原引擎)', + engineParamsTitle: '引擎参数', + engineParamsSaved: '引擎参数已保存', + localRoot: '存储根目录 local_storage_path', + localRootHint: '留空 = 系统默认数据目录;修改后对新写入生效', + webdavUrl: '服务地址 webdav_url', + webdavUrlHint: '如 https://dav.example.com/dav/', + webdavRoot: '远端根目录 webdav_root_path', + webdavRootHint: '远端起始目录(不存在会自动逐级创建)', + webdavUser: '用户名 webdav_username', + webdavPass: '密码 webdav_password', + secretKeepHint: '留空或 ****** = 不修改', + s3Endpoint: '端点 s3_endpoint_url', + s3EndpointHint: '如 https://s3.example.com:9000(AWS 官方可留空)', + s3Bucket: '存储桶 s3_bucket_name', + s3Region: '区域 s3_region_name', + s3Ak: 'AccessKeyID s3_access_key_id', + s3Sk: 'SecretAccessKey s3_secret_access_key', + s3Token: '会话令牌 aws_session_token(可选)', + s3Style: '寻址样式 s3_addressing_style', + styleAuto: 'auto(自动)', + stylePath: 'path(路径式,MinIO 常用)', + styleVirtual: 'virtual(虚拟主机式)', + engineParamsSave: '保存引擎参数', + approxSize: '≈ {size}', + }, + }, +} diff --git a/web/src/main.ts b/web/src/main.ts new file mode 100644 index 0000000..e6e5a7d --- /dev/null +++ b/web/src/main.ts @@ -0,0 +1,16 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import App from './App.vue' +import router from './router' +import { i18n } from './i18n' +import { installMock } from './mock' +import './styles/main.css' + +// VITE_MOCK=1 时启用本地 mock(无后端推进前端开发) +installMock() + +const app = createApp(App) +app.use(createPinia()) +app.use(i18n) +app.use(router) +app.mount('#app') diff --git a/web/src/mock/index.ts b/web/src/mock/index.ts new file mode 100644 index 0000000..c933e86 --- /dev/null +++ b/web/src/mock/index.ts @@ -0,0 +1,256 @@ +/** + * 开发期 Mock(VITE_MOCK=1 时启用):在 service worker 之前仅用于本地调试, + * 通过拦截 fetch/XHR 实现无后端推进前端。 + */ +import type { MockRoute } from './types' + +const ok = (data: unknown, msg = '') => ({ code: 200, msg, data }) +const err = (code: number, msg: string) => ({ code, msg, data: null }) + +let seq = 1 + +function textToCode(text: string): string { + let h = 0 + for (let i = 0; i < text.length; i++) h = (h * 31 + text.charCodeAt(i)) >>> 0 + return (h % 100000).toString().padStart(5, '0') +} + +const shares: { code: string; kind: 'text' | 'file'; name: string; size: number; text?: string }[] = [] + +const routes: MockRoute[] = [ + { method: 'GET', pattern: /\/api\/v1\/health$/, reply: () => ok({ status: 'ok', version: 'mock', storage: 'local', cache: 'memory' }) }, + { + method: 'GET', + pattern: /\/api\/v1\/config$/, + reply: () => + ok({ + config: { + name: '文件快传 (Mock)', + site_domain: '', + description: '开发 Mock 模式 · 开箱即用的文件快传系统', + explain: '', + uploadSize: 10 * 1024 * 1024, + allowedFileTypes: [], + expireStyle: ['day', 'hour', 'minute', 'forever', 'count'], + enableChunk: true, + openUpload: true, + notify_enabled: 1, + notify_title: '系统通知', + notify_content: '欢迎使用文件快传(Mock)。', + logo_url: '', + favicon_url: '', + background_url: '', + footer_text: '', + footer_beian: '', + max_file_size: 0, + max_save_seconds: 0, + max_save_count: 0, + uploadCount: 10, + uploadMinute: 1, + }, + meta: { version: 'mock', features: { chunkUpload: true, guestUpload: true } }, + }), + }, + { + method: 'POST', + pattern: /\/share\/text$/, + reply: (_m, body) => { + const text = String((body as Record)?.text ?? '') + const code = textToCode(text + seq++) + shares.push({ code, kind: 'text', name: 'Text.txt', size: new Blob([text]).size, text }) + return ok({ code }) + }, + }, + { + method: 'POST', + pattern: /\/share\/file$/, + reply: (_m, body) => { + const fd = body as FormData + const file = fd?.get?.('file') + if (!(file instanceof File)) return err(400, '缺少文件') + const code = textToCode(file.name + seq++) + shares.push({ code, kind: 'file', name: file.name, size: file.size }) + return ok({ code, name: file.name }) + }, + }, + { + method: 'GET', + pattern: /\/share\/metadata$/, + reply: (_m, _body, q) => { + const s = shares.find((x) => x.code === q?.get('code')) + if (!s) return err(404, '文件不存在') + return ok({ + code: s.code, + name: s.name, + size: s.size, + type: s.kind, + is_text: s.kind === 'text', + created_at: new Date().toISOString(), + expired_at: null, + expired_count: -1, + used_count: 0, + remaining_downloads: null, + }) + }, + }, + { + method: 'GET', + pattern: /\/share\/select$/, + reply: (_m, _body, q) => { + const s = shares.find((x) => x.code === q?.get('code')) + if (!s) return { __rawError: 404 } + if (s.kind === 'text') return { __rawText: s.text ?? '' } + return { __rawBlob: new Blob([`mock-content-of-${s.name}`], { type: 'application/octet-stream' }), __rawName: s.name } + }, + }, + { + method: 'POST', + pattern: /\/chunk\/upload\/init$/, + reply: (_m, body) => { + const b = body as Record + const total = Math.ceil(Number(b?.file_size ?? 0) / Number(b?.chunk_size ?? 1024)) + return ok({ existed: false, upload_id: `mock-${seq++}`, chunk_size: b?.chunk_size, total_chunks: total, uploaded_chunks: [] }) + }, + }, + { method: 'POST', pattern: /\/chunk\/upload\/[^/]+\/\d+$/, reply: () => ok({ chunk_hash: 'mockhash' }) }, + { + method: 'GET', + pattern: /\/chunk\/upload\/status\/[^/]+$/, + reply: (m) => { + const id = String(m?.[0]?.split('/').pop() ?? 'mock') + return ok({ upload_id: id, chunk_size: 1024, total_chunks: 1, uploaded_chunks: [0] }) + }, + }, + { + method: 'POST', + pattern: /\/chunk\/upload\/complete\/[^/]+$/, + reply: () => { + const code = textToCode('chunk' + seq++) + shares.push({ code, kind: 'file', name: 'chunked.bin', size: 1024 }) + return ok({ code, name: 'chunked.bin' }) + }, + }, + { method: 'DELETE', pattern: /\/chunk\/upload\/[^/]+$/, reply: () => ok({ message: '已取消' }) }, + { method: 'POST', pattern: /\/admin\/login$/, reply: (_m, body) => ((body as { password?: string })?.password === 'admin123' ? ok({ id: 'admin', username: 'admin', token: 'mock-token', token_type: 'Bearer', expires_in: 86400 }) : err(401, '密码错误')) }, + { method: 'GET', pattern: /\/admin\/verify$/, reply: (_m, _b, _q) => (getToken() === 'mock-token' ? ok({ is_admin: true }) : err(401, '未登录')) }, + { method: 'POST', pattern: /\/admin\/logout$/, reply: () => ok({ ok: true }) }, + { + method: 'GET', + pattern: /\/admin\/file\/list$/, + reply: () => + ok({ + page: 1, + size: 10, + total: shares.length, + data: shares.map((s, i) => ({ + id: i + 1, + code: s.code, + name: s.name, + suffix: s.name.includes('.') ? s.name.split('.').pop() : '', + size: s.size, + isText: s.kind === 'text', + expiredAt: null, + expiredCount: -1, + usedCount: 0, + createdAt: new Date().toISOString(), + isExpired: false, + })), + }), + }, + { method: 'DELETE', pattern: /\/admin\/file\/delete$/, reply: () => ok(null) }, + { method: 'POST', pattern: /\/admin\/file\/batch-delete$/, reply: () => ok(null) }, + { method: 'PATCH', pattern: /\/admin\/file\/update$/, reply: () => ok('更新成功') }, + { + method: 'GET', + pattern: /\/admin\/config\/get$/, + reply: () => + ok({ + site_name: '文件快传 (Mock)', + logo_url: '', + favicon_url: '', + background_url: '', + footer_text: '', + footer_beian: '', + notify_enabled: 1, + notify_title: '系统通知', + notify_content: '欢迎使用文件快传(Mock)。', + max_save_seconds: 0, + max_save_count: 0, + max_file_size: 0, + allowed_file_types: ['*'], + uploadCount: 10, + uploadMinute: 1, + openUpload: 1, + }), + }, + { method: 'PATCH', pattern: /\/admin\/config\/update$/, reply: () => ok(null) }, + { + method: 'GET', + pattern: /\/admin\/audit\/list$/, + reply: () => + ok({ + page: 1, + size: 20, + total: 2, + data: [ + { id: 1, action: 'upload', result: 'success', file_code: shares[0]?.code ?? '10001', file_name: 'demo.txt', size_bytes: 1024, transferred_bytes: 1024, ip: '127.0.0.1', user_agent: 'Mozilla/5.0 (Mock)', device_os: 'macOS', device_browser: 'Chrome', device_type: 'desktop', actor: 'guest', error_msg: '', duration_ms: 42, created_at: new Date().toISOString() }, + { id: 2, action: 'download', result: 'success', file_code: shares[0]?.code ?? '10001', file_name: 'demo.txt', size_bytes: 1024, transferred_bytes: 1024, ip: '192.168.1.2', user_agent: 'Mozilla/5.0 (iPhone)', device_os: 'iOS', device_browser: 'Safari', device_type: 'mobile', actor: 'guest', error_msg: '', duration_ms: 88, created_at: new Date().toISOString() }, + ], + }), + }, +] + +function getToken(): string { + try { + return localStorage.getItem('fcb_admin_token') ?? '' + } catch { + return '' + } +} + +/** 安装 fetch 拦截(dev-only,不覆盖 XHR —— mock 下上传走 XHR 也会失败,可接受) */ +export function installMock(): void { + if (import.meta.env.VITE_MOCK !== '1') return + const origFetch = globalThis.fetch.bind(globalThis) + globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise => { + const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url + const method = (init?.method ?? 'GET').toUpperCase() + const qIndex = url.indexOf('?') + const path = qIndex >= 0 ? url.slice(0, qIndex) : url + const query = qIndex >= 0 ? new URL(url, location.origin).searchParams : undefined + + for (const r of routes) { + if (r.method !== method) continue + const m = r.pattern.exec(path) + if (!m) continue + let body: unknown = undefined + if (init?.body) { + if (typeof init.body === 'string') { + try { + body = JSON.parse(init.body) + } catch { + body = Object.fromEntries(new URLSearchParams(init.body)) + } + } else if (init.body instanceof FormData) { + body = init.body + } + } + const res = r.reply(m, body, query) + await new Promise((res) => setTimeout(res, 120)) // 模拟网络延迟 + if (res && typeof res === 'object' && '__rawText' in (res as Record)) { + return new Response((res as { __rawText: string }).__rawText, { status: 200, headers: { 'content-type': 'text/plain; charset=utf-8' } }) + } + if (res && typeof res === 'object' && '__rawBlob' in (res as Record)) { + const rb = res as { __rawBlob: Blob; __rawName: string } + return new Response(rb.__rawBlob, { status: 200, headers: { 'content-type': 'application/octet-stream', 'content-disposition': `attachment; filename*=UTF-8''${encodeURIComponent(rb.__rawName)}` } }) + } + if (res && typeof res === 'object' && '__rawError' in (res as Record)) { + const e = err((res as { __rawError: number }).__rawError, '文件不存在') + return new Response(JSON.stringify(e), { status: 200, headers: { 'content-type': 'application/json' } }) + } + return new Response(JSON.stringify(res), { status: 200, headers: { 'content-type': 'application/json' } }) + } + return origFetch(input, init) + } + console.info('[mock] 文件快传 API mock enabled (VITE_MOCK=1)') +} diff --git a/web/src/mock/types.ts b/web/src/mock/types.ts new file mode 100644 index 0000000..f6ed597 --- /dev/null +++ b/web/src/mock/types.ts @@ -0,0 +1,13 @@ +export interface MockRoute { + method: string + pattern: RegExp + reply: ( + m: RegExpMatchArray | null, + body: unknown, + query?: URLSearchParams, + ) => + | { code: number; msg: string; data: unknown } + | { __rawText: string } + | { __rawBlob: Blob; __rawName: string } + | { __rawError: number } +} diff --git a/web/src/router/index.ts b/web/src/router/index.ts new file mode 100644 index 0000000..b0ea580 --- /dev/null +++ b/web/src/router/index.ts @@ -0,0 +1,51 @@ +import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router' +import { setUnauthorizedHandler } from '@/api/http' + +// 默认 history 模式(go-api NoRoute 已实现 SPA 回退:静态资源优先、GET+text/html 回 index.html、 +// /api/* JSON 404);如后端未做回退可设 VITE_ROUTER_MODE=hash 切换。 +const mode = (import.meta.env.VITE_ROUTER_MODE ?? 'history') === 'hash' ? createWebHashHistory() : createWebHistory() + +const router = createRouter({ + history: mode, + routes: [ + // 需求⑩:标题键走 i18n(document.title 由 App.vue 统一组装:t(titleKey) · 站点名) + { path: '/', name: 'home', component: () => import('@/views/HomeView.vue'), meta: { titleKey: 'nav.home' } }, + { path: '/s/:code', name: 'pickup', component: () => import('@/views/PickupView.vue'), meta: { titleKey: 'nav.home' } }, + { path: '/admin/login', name: 'admin-login', component: () => import('@/views/admin/LoginView.vue'), meta: { titleKey: 'admin.nav.title' } }, + { + path: '/admin', + component: () => import('@/views/admin/AdminLayout.vue'), + meta: { requiresAuth: true, titleKey: 'admin.nav.title' }, + children: [ + { path: '', redirect: { name: 'admin-files' } }, + { path: 'files', name: 'admin-files', component: () => import('@/views/admin/FilesView.vue'), meta: { titleKey: 'admin.nav.files' } }, + { path: 'audit', name: 'admin-audit', component: () => import('@/views/admin/AuditView.vue'), meta: { titleKey: 'admin.nav.audit' } }, + { path: 'settings', name: 'admin-settings', component: () => import('@/views/admin/SettingsView.vue'), meta: { titleKey: 'admin.nav.settings' } }, + ], + }, + { path: '/docs', name: 'docs', component: () => import('@/views/DocsView.vue'), meta: { titleKey: 'nav.docs' } }, + { path: '/docs/:slug', name: 'docs-detail', component: () => import('@/views/DocsView.vue'), meta: { titleKey: 'nav.docs' } }, + { path: '/openapi', name: 'openapi', component: () => import('@/views/OpenApiView.vue'), meta: { titleKey: 'nav.openapi' } }, + { path: '/:pathMatch(.*)*', name: 'not-found', component: () => import('@/views/NotFoundView.vue'), meta: { titleKey: 'notFound.title' } }, + ], + scrollBehavior(to, _from, saved) { + if (saved) return saved + if (to.hash) return { el: to.hash, behavior: 'smooth' } + return { top: 0 } + }, +}) + +router.beforeEach((to) => { + if (to.meta.requiresAuth && !localStorage.getItem('fcb_admin_token')) { + return { name: 'admin-login', query: { redirect: to.fullPath } } + } +}) + +// API 401 → 跳转管理登录(携带回跳地址) +setUnauthorizedHandler(() => { + const current = router.currentRoute.value + if (current.name === 'admin-login') return + void router.push({ name: 'admin-login', query: { redirect: current.fullPath } }) +}) + +export default router diff --git a/web/src/stores/auth.ts b/web/src/stores/auth.ts new file mode 100644 index 0000000..3a99c04 --- /dev/null +++ b/web/src/stores/auth.ts @@ -0,0 +1,61 @@ +/** 管理员会话 store:JWT 持久化 + 登录/登出/校验 */ +import { defineStore } from 'pinia' +import { adminLogin, adminLogout, adminVerify } from '@/api/admin' +import { getToken, setToken } from '@/api/http' + +const ADMIN_FLAG_KEY = 'fcb_admin_flag' + +export const useAuthStore = defineStore('auth', { + state: () => ({ + token: getToken(), + checked: false, + }), + getters: { + isAuthed: (s) => Boolean(s.token), + }, + actions: { + async login(password: string): Promise { + const res = await adminLogin(password) + const token = res?.token + if (!token) throw new Error('登录响应缺少 token') + this.token = token + setToken(token) + try { + localStorage.setItem(ADMIN_FLAG_KEY, '1') + } catch { + /* ignore */ + } + this.checked = true + }, + /** 校验现有 token 是否仍有效(进入管理页时调用) */ + async verify(): Promise { + if (!this.token) return false + try { + await adminVerify() + this.checked = true + return true + } catch { + this.reset() + return false + } + }, + async logout(): Promise { + try { + if (this.token) await adminLogout() + } catch { + /* 后端登出失败不阻塞本地清理 */ + } + this.reset() + }, + reset(): void { + this.token = '' + this.checked = false + setToken('') + try { + localStorage.removeItem(ADMIN_FLAG_KEY) + } catch { + /* ignore */ + } + }, + }, +}) diff --git a/web/src/stores/config.ts b/web/src/stores/config.ts new file mode 100644 index 0000000..ef4d006 --- /dev/null +++ b/web/src/stores/config.ts @@ -0,0 +1,161 @@ +/** + * 站点配置 store:公共配置 + Logo/favicon(需求④)+ 背景/页脚/通知(⑤⑥⑦)+ 策略范围(⑧)。 + * 远程 URL 默认值已删除:Logo/favicon 使用本地打包资源兜底(config 自定义仍优先)。 + */ +import { defineStore } from 'pinia' +import { request } from '@/api/http' +import { paths } from '@/api/paths' +import { pick } from '@/utils/format' +import logoLocal from '@/assets/brand/logo.svg' +import faviconLocal from '@/assets/brand/favicon.png' + +/** 需求④:默认 Logo(导航栏)与 favicon(浏览器标签)——本地打包资源,无远程依赖 */ +export const DEFAULT_LOGO_URL = logoLocal +export const DEFAULT_FAVICON_URL = faviconLocal +export const DEFAULT_SITE_NAME = '文件快传' + +export interface ConfigState { + loaded: boolean + loading: boolean + siteName: string + siteDomain: string + description: string + explain: string + uploadSize: number + allowedFileTypes: string[] + expireStyle: string[] + enableChunk: boolean + openUpload: boolean + notifyEnabled: boolean + notifyTitle: string + notifyContent: string + logoUrl: string + faviconUrl: string + backgroundUrl: string + footerText: string + footerBeian: string + maxFileSize: number + maxSaveSeconds: number + maxSaveCount: number + uploadCount: number + uploadMinute: number +} + +function toBool(v: unknown, fallback = true): boolean { + if (v === undefined || v === null) return fallback + if (typeof v === 'boolean') return v + if (typeof v === 'number') return v !== 0 + return String(v) !== '0' && String(v) !== 'false' && String(v) !== '' +} + +function toList(v: unknown): string[] { + if (Array.isArray(v)) return v.map((x) => String(x).trim()).filter(Boolean) + if (typeof v === 'string') { + return v + .split(',') + .map((x) => x.trim()) + .filter(Boolean) + } + return [] +} + +function toNum(v: unknown, fallback: number): number { + const n = Number(v) + return Number.isFinite(n) ? n : fallback +} + +export const useConfigStore = defineStore('config', { + state: (): ConfigState => ({ + loaded: false, + loading: false, + siteName: DEFAULT_SITE_NAME, + siteDomain: '', + description: '', + explain: '', + uploadSize: 10 * 1024 * 1024, + allowedFileTypes: [], + expireStyle: ['day', 'hour', 'minute', 'forever', 'count'], + enableChunk: false, + openUpload: true, + notifyEnabled: false, + notifyTitle: '', + notifyContent: '', + logoUrl: '', + faviconUrl: '', + backgroundUrl: '', + footerText: '', + footerBeian: '', + maxFileSize: 0, + maxSaveSeconds: 0, + maxSaveCount: 0, + uploadCount: 0, + uploadMinute: 0, + }), + getters: { + /** 需求④:自定义优先,本地打包资源兜底 */ + displayLogoUrl: (s) => (s.logoUrl?.trim() ? s.logoUrl : DEFAULT_LOGO_URL), + displayFaviconUrl: (s) => (s.faviconUrl?.trim() ? s.faviconUrl : DEFAULT_FAVICON_URL), + displayName: (s) => (s.siteName?.trim() ? s.siteName : DEFAULT_SITE_NAME), + /** v3.1:分享链接基础地址——配置了对外域名用之,否则当前访问地址 */ + shareLinkBase: (s): string => (s.siteDomain?.trim() ? s.siteDomain.trim().replace(/\/$/, '') : location.origin), + /** ⑧ 存储策略:max_file_size>0 时优先,否则回落 uploadSize */ + effectiveMaxFileSize(): number { + return this.maxFileSize > 0 ? this.maxFileSize : this.uploadSize + }, + }, + actions: { + /** 拉取公共配置;失败静默使用默认值(未部署后端时前端仍可浏览) */ + async load(): Promise { + this.loading = true + try { + const raw = await request>(paths.publicConfig, { timeout: 8000 }) + const cfg = (pick>(raw, ['config']) ?? raw) as Record + this.siteName = String(pick(cfg, ['name', 'site_name', 'siteName']) ?? DEFAULT_SITE_NAME) + // v3.1:站点对外域名(空=当前访问地址) + this.siteDomain = String(pick(cfg, ['site_domain', 'siteDomain']) ?? '').trim() + this.description = String(pick(cfg, ['description']) ?? '') + this.explain = String(pick(cfg, ['explain', 'page_explain']) ?? '') + this.uploadSize = toNum(pick(cfg, ['uploadSize', 'upload_size']), 10 * 1024 * 1024) + this.allowedFileTypes = toList(pick(cfg, ['allowedFileTypes', 'allowed_file_types'])) + const styles = toList(pick(cfg, ['expireStyle', 'expire_style'])) + if (styles.length) this.expireStyle = styles + this.enableChunk = toBool(pick(cfg, ['enableChunk', 'enable_chunk']), false) + this.openUpload = toBool(pick(cfg, ['openUpload', 'open_upload']), true) + this.notifyTitle = String(pick(cfg, ['notify_title', 'notifyTitle']) ?? '') + this.notifyContent = String(pick(cfg, ['notify_content', 'notifyContent']) ?? '') + // 需求⑦:通知开关(1/0) + this.notifyEnabled = toBool(pick(cfg, ['notify_enabled', 'notifyEnabled']), false) + // 需求⑤:背景图 + this.backgroundUrl = String(pick(cfg, ['background_url', 'backgroundUrl']) ?? '').trim() + // 需求⑥:页脚 + this.footerText = String(pick(cfg, ['footer_text', 'footerText']) ?? '') + this.footerBeian = String(pick(cfg, ['footer_beian', 'footerBeian']) ?? '') + // 需求⑧:保存策略与上传频率 + this.maxFileSize = toNum(pick(cfg, ['max_file_size', 'maxFileSize', 'maxFileSize']), 0) + this.maxSaveSeconds = toNum(pick(cfg, ['max_save_seconds', 'maxSaveSeconds']), 0) + this.maxSaveCount = toNum(pick(cfg, ['max_save_count', 'maxSaveCount']), 0) + this.uploadCount = toNum(pick(cfg, ['uploadCount', 'upload_count']), 0) + this.uploadMinute = toNum(pick(cfg, ['uploadMinute', 'upload_minute']), 0) + // 需求④:运行时优先读 config 的 logo_url/favicon_url,空值回退本地默认 + this.logoUrl = String(pick(cfg, ['logo_url', 'logoUrl']) ?? '').trim() + this.faviconUrl = String(pick(cfg, ['favicon_url', 'faviconUrl']) ?? '').trim() + this.loaded = true + this.applyToDocument() + } catch { + // 保持默认值 + } finally { + this.loading = false + } + }, + /** 将站点名与 favicon 应用到 document(管理端保存配置后调用即可全站生效) */ + applyToDocument(): void { + let link = document.querySelector('link[rel="icon"]') + if (!link) { + link = document.createElement('link') + link.rel = 'icon' + document.head.appendChild(link) + } + link.href = this.displayFaviconUrl + }, + }, +}) diff --git a/web/src/stores/toast.ts b/web/src/stores/toast.ts new file mode 100644 index 0000000..5677644 --- /dev/null +++ b/web/src/stores/toast.ts @@ -0,0 +1,38 @@ +/** 轻量 Toast 通知 */ +import { defineStore } from 'pinia' + +export type ToastType = 'success' | 'error' | 'info' + +export interface ToastItem { + id: number + type: ToastType + text: string +} + +let seq = 0 + +export const useToastStore = defineStore('toast', { + state: () => ({ + items: [] as ToastItem[], + }), + actions: { + push(text: string, type: ToastType = 'info', duration = 3200): void { + const id = ++seq + this.items.push({ id, type, text }) + if (this.items.length > 4) this.items.shift() + setTimeout(() => this.dismiss(id), duration) + }, + success(text: string): void { + this.push(text, 'success') + }, + error(text: string): void { + this.push(text, 'error', 4200) + }, + info(text: string): void { + this.push(text, 'info') + }, + dismiss(id: number): void { + this.items = this.items.filter((t) => t.id !== id) + }, + }, +}) diff --git a/web/src/styles/main.css b/web/src/styles/main.css new file mode 100644 index 0000000..f9bfbff --- /dev/null +++ b/web/src/styles/main.css @@ -0,0 +1,1047 @@ +/* ========================================================= + 文件快传全局样式 — 液态玻璃风格(需求③) + - CSS 变量 token 主题系统(需求②):[data-theme='light' | 'dark'] + - 毛玻璃:backdrop-filter blur + 半透明层 + 光影渐变 + 悬浮圆角卡片 + - 降级:@supports 不支持 backdrop-filter 时回退实色; + prefers-reduced-transparency / 移动端收窄玻璃层保证可读性与性能 + ========================================================= */ + +:root, +html[data-theme='light'] { + color-scheme: light; + /* 品牌色 */ + --c-primary: #4f6ef7; + --c-primary-strong: #3d5bf0; + --c-primary-soft: rgb(79 110 247 / 10%); + --c-danger: #e5484d; + --c-danger-soft: rgb(229 72 77 / 10%); + --c-success: #2f9e6e; + --c-success-soft: rgb(47 158 110 / 12%); + --c-warn: #b36f0a; + --c-warn-soft: rgb(196 127 23 / 12%); + /* 文本与描边 */ + --c-text: #1c2230; + --c-text-2: #4d576a; + --c-text-3: #7f8ba0; + --c-border: rgb(20 32 64 / 10%); + /* 页面底色与卡片实色(降级/骨架用) */ + --c-bg: #eef1f6; + --c-card: #ffffff; + --c-input-bg: rgb(255 255 255 / 72%); + /* —— 玻璃 token(需求③)—— */ + --glass-bg: rgb(255 255 255 / 52%); + --glass-bg-strong: rgb(255 255 255 / 66%); + --glass-bg-soft: rgb(255 255 255 / 38%); + --glass-border: rgb(255 255 255 / 55%); + --glass-inner: rgb(255 255 255 / 65%); + --glass-blur: 18px; + --glass-saturate: 160%; + /* 环境光影渐变(页面最底层,配合背景图) */ + --ambient: radial-gradient(1200px 640px at 12% -8%, rgb(96 138 255 / 28%), transparent 60%), + radial-gradient(1100px 720px at 105% 8%, rgb(167 139 250 / 24%), transparent 55%), + radial-gradient(900px 560px at 50% 118%, rgb(64 199 255 / 18%), transparent 60%), + linear-gradient(180deg, #f3f6fc 0%, #e8edf7 100%); + --ambient-overlay: rgb(255 255 255 / 52%); + --bg-tint: rgb(245 247 252 / 72%); + --shadow-card: 0 1px 2px rgb(16 24 40 / 5%), 0 16px 40px -20px rgb(16 24 40 / 25%); + --shadow-float: 0 24px 60px -24px rgb(16 24 40 / 35%); + --radius: 16px; + --radius-lg: 20px; + --radius-sm: 10px; + --nav-h: 62px; + --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', + 'Microsoft YaHei', 'Noto Sans CJK SC', sans-serif; + --mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace; +} + +html[data-theme='dark'] { + color-scheme: dark; + --c-primary: #7d95ff; + --c-primary-strong: #98abff; + --c-primary-soft: rgb(125 149 255 / 14%); + --c-danger: #ff6b70; + --c-danger-soft: rgb(255 107 112 / 14%); + --c-success: #4cc38a; + --c-success-soft: rgb(76 195 138 / 14%); + --c-warn: #e5a34d; + --c-warn-soft: rgb(229 163 77 / 14%); + --c-text: #e8ecf6; + --c-text-2: #a9b3c6; + --c-text-3: #77839b; + --c-border: rgb(148 170 220 / 16%); + --c-bg: #0b0f1a; + --c-card: #131a2b; + --c-input-bg: rgb(15 21 36 / 60%); + --glass-bg: rgb(19 26 43 / 58%); + --glass-bg-strong: rgb(23 31 51 / 74%); + --glass-bg-soft: rgb(19 26 43 / 42%); + --glass-border: rgb(148 170 220 / 18%); + --glass-inner: rgb(255 255 255 / 8%); + --glass-blur: 20px; + --glass-saturate: 150%; + --ambient: radial-gradient(1200px 640px at 12% -8%, rgb(64 98 255 / 26%), transparent 60%), + radial-gradient(1100px 720px at 105% 8%, rgb(126 87 252 / 22%), transparent 55%), + radial-gradient(900px 560px at 50% 118%, rgb(20 150 200 / 16%), transparent 60%), + linear-gradient(180deg, #0c1120 0%, #0a0e19 100%); + --ambient-overlay: rgb(8 11 20 / 55%); + --bg-tint: rgb(10 14 25 / 68%); + --shadow-card: 0 1px 2px rgb(0 0 0 / 30%), 0 18px 44px -22px rgb(0 0 0 / 60%); + --shadow-float: 0 26px 64px -26px rgb(0 0 0 / 70%); +} + +* { + box-sizing: border-box; +} + +html, +body { + margin: 0; + padding: 0; +} + +body { + font-family: var(--font); + background: var(--c-bg); + color: var(--c-text); + font-size: 15px; + line-height: 1.6; + -webkit-font-smoothing: antialiased; + transition: background-color 0.25s, color 0.25s; +} + +#app { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +a { + color: var(--c-primary); + text-decoration: none; +} +a:hover { + color: var(--c-primary-strong); +} + +h1, h2, h3, h4 { + line-height: 1.35; + margin: 0 0 12px; +} + +::selection { background: rgb(79 110 247 / 30%); } + +/* ---------- 玻璃表面工具类(需求③:顶栏/卡片/按钮/弹层共用) ---------- */ +.glass { + background: var(--glass-bg); + -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate)); + backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate)); + border: 1px solid var(--glass-border); + /* 顶部高光 + 内侧描边,模拟玻璃折射 */ + box-shadow: var(--shadow-card), inset 0 1px 0 var(--glass-inner); +} + +/* ---------- 顶栏 ---------- */ +.site-nav { + position: sticky; + top: 0; + z-index: 50; + height: var(--nav-h); + display: flex; + align-items: center; + gap: 20px; + padding: 0 clamp(14px, 4vw, 32px); + background: var(--glass-bg-strong); + -webkit-backdrop-filter: blur(calc(var(--glass-blur) + 4px)) saturate(var(--glass-saturate)); + backdrop-filter: blur(calc(var(--glass-blur) + 4px)) saturate(var(--glass-saturate)); + border-bottom: 1px solid var(--glass-border); + box-shadow: 0 1px 0 var(--glass-inner); +} + +.brand { + display: flex; + align-items: center; + gap: 10px; + font-weight: 700; + font-size: 17px; + color: var(--c-text); + min-width: 0; +} +.brand:hover { color: var(--c-text); } +.brand img { + width: 30px; + height: 30px; + object-fit: contain; + flex: none; + filter: drop-shadow(0 2px 6px rgb(16 24 40 / 20%)); +} +.brand .brand-name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.nav-links { + display: flex; + align-items: center; + gap: 4px; + margin-left: auto; +} +.nav-links a { + padding: 7px 13px; + border-radius: 999px; + color: var(--c-text-2); + font-size: 14px; + white-space: nowrap; +} +.nav-links a:hover { background: var(--c-primary-soft); color: var(--c-primary-strong); } +.nav-links a.router-link-active { background: var(--c-primary-soft); color: var(--c-primary-strong); font-weight: 600; } + +/* 导航右侧控件(主题三态 / 语言切换) */ +.nav-controls { + display: flex; + align-items: center; + gap: 8px; + margin-left: 8px; +} +.nav-control { + display: inline-flex; + align-items: center; + gap: 5px; + border: 1px solid var(--c-border); + background: var(--glass-bg-soft); + color: var(--c-text-2); + font: inherit; + font-size: 13px; + font-weight: 600; + padding: 5px 10px; + border-radius: 999px; + cursor: pointer; + white-space: nowrap; + transition: background 0.15s, color 0.15s, border-color 0.15s; +} +.nav-control:hover { color: var(--c-primary-strong); border-color: var(--c-primary-soft); background: var(--c-primary-soft); } +.nav-control .nav-control-icon { font-size: 14px; line-height: 1; } +/* 主题三态:胶囊分段 */ +.theme-seg { + display: inline-flex; + padding: 3px; + gap: 2px; + border: 1px solid var(--c-border); + border-radius: 999px; + background: var(--glass-bg-soft); +} +.theme-seg button { + border: none; + background: transparent; + font-size: 13px; + line-height: 1; + padding: 5px 9px; + border-radius: 999px; + cursor: pointer; + color: var(--c-text-3); + transition: background 0.15s, color 0.15s; +} +.theme-seg button:hover { color: var(--c-primary-strong); } +.theme-seg button.active { + background: var(--c-primary-soft); + color: var(--c-primary-strong); + box-shadow: 0 1px 4px rgb(16 24 40 / 12%); +} + +/* ---------- 布局 ---------- */ +.page { + flex: 1; + width: 100%; + max-width: 1080px; + margin: 0 auto; + padding: 28px clamp(14px, 4vw, 32px) 48px; +} +.page-wide { max-width: 1280px; } + +/* ---------- 页脚(需求⑥:自定义文案 + 备案号 + 小字链接区) ---------- */ +.site-footer { + border-top: 1px solid var(--glass-border); + background: var(--glass-bg-soft); + -webkit-backdrop-filter: blur(var(--glass-blur)); + backdrop-filter: blur(var(--glass-blur)); + padding: 16px clamp(14px, 4vw, 32px); + color: var(--c-text-3); + font-size: 13px; + display: flex; + flex-wrap: wrap; + gap: 6px 20px; + align-items: center; + justify-content: space-between; +} +.site-footer .footer-text { white-space: pre-line; word-break: break-word; } +.site-footer .footer-beian { font-size: 12px; opacity: 0.9; } +.site-footer nav { + display: flex; + gap: 14px; + align-items: center; + font-size: 12.5px; +} +.site-footer nav a { color: var(--c-text-3); } +.site-footer nav a:hover { color: var(--c-primary-strong); } + +/* ---------- 悬浮圆角玻璃卡片 ---------- */ +.card { + background: var(--glass-bg); + -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate)); + backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate)); + border: 1px solid var(--glass-border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-card), inset 0 1px 0 var(--glass-inner); + padding: 22px; +} +.card-title { + font-size: 17px; + font-weight: 700; + margin: 0 0 4px; +} +.card-sub { + color: var(--c-text-3); + font-size: 13px; + margin: 0 0 16px; +} + +/* ---------- 表单 ---------- */ +.field { + margin-bottom: 16px; +} +.field > label { + display: block; + font-size: 13px; + font-weight: 600; + color: var(--c-text-2); + margin-bottom: 6px; +} +.input, +.textarea, +.select { + width: 100%; + border: 1px solid var(--c-border); + border-radius: var(--radius-sm); + background: var(--c-input-bg); + color: var(--c-text); + font: inherit; + padding: 9px 12px; + outline: none; + transition: border-color 0.15s, box-shadow 0.15s, background 0.15s; +} +.input::placeholder, +.textarea::placeholder { color: var(--c-text-3); } +.input:focus, +.textarea:focus, +.select:focus { + border-color: var(--c-primary); + box-shadow: 0 0 0 3px rgb(79 110 247 / 18%); +} +.textarea { + min-height: 140px; + resize: vertical; + font-family: inherit; +} +.select { appearance: auto; } +.select option { background: var(--c-card); color: var(--c-text); } +.input-mono { font-family: var(--mono); letter-spacing: 0.5px; } + +.field-row { + display: flex; + gap: 10px; + flex-wrap: wrap; +} +.field-row > * { flex: 1; min-width: 120px; } + +.hint { color: var(--c-text-3); font-size: 12.5px; margin-top: 5px; } + +/* ---------- 按钮 ---------- */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + border: 1px solid transparent; + border-radius: var(--radius-sm); + padding: 9px 18px; + font: inherit; + font-weight: 600; + font-size: 14px; + cursor: pointer; + background: var(--c-primary); + color: #fff; + box-shadow: 0 8px 20px -10px rgb(79 110 247 / 70%); + transition: background 0.15s, opacity 0.15s, transform 0.05s, box-shadow 0.15s; + user-select: none; + white-space: nowrap; +} +.btn:hover { background: var(--c-primary-strong); } +.btn:active { transform: translateY(1px); } +.btn:disabled { opacity: 0.55; cursor: not-allowed; } + +.btn-ghost { + background: var(--glass-bg-soft); + -webkit-backdrop-filter: blur(8px); + backdrop-filter: blur(8px); + color: var(--c-text-2); + border-color: var(--glass-border); + box-shadow: inset 0 1px 0 var(--glass-inner); +} +.btn-ghost:hover { background: var(--c-primary-soft); color: var(--c-primary-strong); border-color: var(--c-primary-soft); } + +.btn-danger { background: var(--c-danger); } +.btn-danger:hover { filter: brightness(0.94); } +.btn-danger-ghost { + background: var(--glass-bg-soft); + color: var(--c-danger); + border-color: rgb(229 72 77 / 35%); +} +.btn-danger-ghost:hover { background: var(--c-danger-soft); } + +.btn-sm { padding: 5px 11px; font-size: 13px; border-radius: 8px; } +.btn-block { width: 100%; } + +/* ---------- Tabs ---------- */ +.tabs { + display: flex; + gap: 6px; + background: var(--glass-bg-soft); + -webkit-backdrop-filter: blur(8px); + backdrop-filter: blur(8px); + border: 1px solid var(--glass-border); + border-radius: 12px; + padding: 4px; + width: fit-content; + max-width: 100%; +} +.tab { + border: none; + background: transparent; + font: inherit; + font-weight: 600; + font-size: 14px; + color: var(--c-text-2); + padding: 7px 18px; + border-radius: 9px; + cursor: pointer; + transition: background 0.15s, color 0.15s; +} +.tab.active { + background: var(--glass-bg-strong); + color: var(--c-primary-strong); + box-shadow: 0 1px 6px rgb(16 24 40 / 14%), inset 0 1px 0 var(--glass-inner); +} + +/* ---------- 徽标 ---------- */ +.badge { + display: inline-flex; + align-items: center; + gap: 4px; + border-radius: 999px; + font-size: 12px; + font-weight: 600; + padding: 2px 10px; + background: var(--c-primary-soft); + color: var(--c-primary-strong); + white-space: nowrap; +} +.badge-success { background: var(--c-success-soft); color: var(--c-success); } +.badge-danger { background: var(--c-danger-soft); color: var(--c-danger); } +.badge-warn { background: var(--c-warn-soft); color: var(--c-warn); } +.badge-muted { background: var(--glass-bg-soft); color: var(--c-text-2); border: 1px solid var(--c-border); } + +/* ---------- 表格(长列表区域:性能降级提示,见 perf 注释) ---------- */ +.table-wrap { + overflow-x: auto; + border: 1px solid var(--glass-border); + border-radius: var(--radius); + background: var(--glass-bg-strong); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + /* 长列表滚动性能:提示合成器此区域即将滚动(低端设备可接受——无动画仅滚动) */ + will-change: scroll-position; +} +table.table { + width: 100%; + border-collapse: collapse; + font-size: 13.5px; + min-width: 760px; +} +.table th, +.table td { + text-align: left; + padding: 10px 14px; + border-bottom: 1px solid var(--c-border); + white-space: nowrap; +} +.table th { + background: var(--glass-bg-strong); + color: var(--c-text-2); + font-weight: 600; + font-size: 12.5px; + position: sticky; + top: 0; + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); +} +.table tbody tr:hover { background: var(--c-primary-soft); } +.table td.wrap { white-space: normal; word-break: break-all; min-width: 160px; } +.table .code-cell { font-family: var(--mono); font-weight: 600; color: var(--c-primary-strong); } + +/* ---------- 分页 ---------- */ +.pager { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 10px; + margin-top: 14px; + font-size: 13px; + color: var(--c-text-2); + flex-wrap: wrap; +} +.pager .pager-info { color: var(--c-text-3); } + +/* ---------- 进度条 ---------- */ +.progress { + height: 8px; + background: var(--glass-bg-soft); + border: 1px solid var(--c-border); + border-radius: 999px; + overflow: hidden; +} +.progress > i { + display: block; + height: 100%; + background: linear-gradient(90deg, var(--c-primary), #7a8ffb, #a78bfa); + border-radius: 999px; + transition: width 0.2s; +} + +/* ---------- 结果卡 ---------- */ +.code-display { + font-family: var(--mono); + font-size: clamp(26px, 6vw, 38px); + font-weight: 700; + letter-spacing: 4px; + color: var(--c-primary-strong); + background: var(--c-primary-soft); + border: 1px dashed rgb(79 110 247 / 40%); + border-radius: var(--radius); + padding: 14px 18px; + text-align: center; + user-select: all; + word-break: break-all; +} + +/* ---------- 提示条 ---------- */ +.notice { + display: flex; + gap: 10px; + align-items: flex-start; + background: var(--c-warn-soft); + color: var(--c-warn); + border: 1px solid rgb(196 127 23 / 25%); + border-radius: var(--radius-sm); + padding: 10px 14px; + font-size: 13.5px; +} +.notice-info { background: var(--c-primary-soft); color: var(--c-primary-strong); border-color: rgb(79 110 247 / 25%); } + +/* ---------- 弹窗(玻璃弹层) ---------- */ +.modal-overlay { + position: fixed; + inset: 0; + background: rgb(8 12 24 / 45%); + -webkit-backdrop-filter: blur(6px); + backdrop-filter: blur(6px); + display: flex; + align-items: flex-start; + justify-content: center; + padding: 8vh 16px 16px; + z-index: 100; + overflow-y: auto; +} +.modal { + background: var(--glass-bg-strong); + -webkit-backdrop-filter: blur(calc(var(--glass-blur) + 6px)) saturate(var(--glass-saturate)); + backdrop-filter: blur(calc(var(--glass-blur) + 6px)) saturate(var(--glass-saturate)); + border: 1px solid var(--glass-border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-float), inset 0 1px 0 var(--glass-inner); + width: 100%; + max-width: 520px; + padding: 22px; +} +.modal-title { font-size: 16px; font-weight: 700; margin: 0 0 16px; } +.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; } + +/* ---------- Toast ---------- */ +.toast-host { + position: fixed; + top: 14px; + left: 50%; + transform: translateX(-50%); + z-index: 200; + display: flex; + flex-direction: column; + gap: 8px; + width: min(92vw, 420px); + pointer-events: none; +} +.toast { + background: var(--glass-bg-strong); + -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate)); + backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate)); + color: var(--c-text); + border: 1px solid var(--glass-border); + border-radius: 12px; + padding: 10px 16px; + font-size: 13.5px; + box-shadow: var(--shadow-float); + display: flex; + gap: 8px; + align-items: center; + animation: toast-in 0.18s ease-out; + pointer-events: auto; +} +.toast-success { border-color: rgb(76 195 138 / 45%); } +.toast-success .toast-icon { color: var(--c-success); } +.toast-error { border-color: rgb(255 107 112 / 45%); } +.toast-error .toast-icon { color: var(--c-danger); } +@keyframes toast-in { + from { opacity: 0; transform: translateY(-8px); } + to { opacity: 1; transform: translateY(0); } +} + +/* ---------- 系统通知悬浮卡(需求⑦) ---------- */ +.notify-pop { + position: fixed; + top: calc(var(--nav-h) + 14px); + right: 16px; + z-index: 90; + width: min(92vw, 340px); + background: var(--glass-bg-strong); + -webkit-backdrop-filter: blur(calc(var(--glass-blur) + 6px)) saturate(var(--glass-saturate)); + backdrop-filter: blur(calc(var(--glass-blur) + 6px)) saturate(var(--glass-saturate)); + border: 1px solid var(--glass-border); + border-radius: var(--radius); + box-shadow: var(--shadow-float), inset 0 1px 0 var(--glass-inner); + padding: 14px 16px; + animation: notify-in 0.22s ease-out; +} +@keyframes notify-in { + from { opacity: 0; transform: translateX(14px); } + to { opacity: 1; transform: translateX(0); } +} +.notify-pop .notify-head { + display: flex; + align-items: center; + gap: 8px; + font-weight: 700; + font-size: 14px; + margin-bottom: 6px; +} +.notify-pop .notify-content { + font-size: 13px; + color: var(--c-text-2); + word-break: break-word; + max-height: 40vh; + overflow-y: auto; + will-change: scroll-position; +} +.notify-pop .notify-content a { text-decoration: underline; } +.notify-pop .notify-close { + margin-left: auto; + border: none; + background: transparent; + color: var(--c-text-3); + font-size: 15px; + cursor: pointer; + padding: 2px 4px; + border-radius: 6px; +} +.notify-pop .notify-close:hover { color: var(--c-primary-strong); background: var(--c-primary-soft); } + +/* ---------- 管理后台布局 ---------- */ +.admin-shell { + display: flex; + min-height: calc(100vh - var(--nav-h)); + max-width: 1280px; + width: 100%; + margin: 0 auto; +} +.admin-aside { + width: 200px; + flex: none; + padding: 20px 12px; + border-right: 1px solid var(--c-border); +} +.admin-aside .aside-title { + font-size: 12.5px; + color: var(--c-text-3); + font-weight: 600; + padding: 0 10px; + margin-bottom: 8px; + letter-spacing: 1px; +} +.aside-menu { display: flex; flex-direction: column; gap: 2px; } +.aside-menu a { + display: flex; + align-items: center; + gap: 8px; + padding: 9px 12px; + border-radius: var(--radius-sm); + color: var(--c-text-2); + font-size: 14px; + font-weight: 500; +} +.aside-menu a:hover { background: var(--c-primary-soft); color: var(--c-primary-strong); } +.aside-menu a.router-link-active { background: var(--c-primary-soft); color: var(--c-primary-strong); font-weight: 600; } +.aside-menu .aside-sep { height: 1px; background: var(--c-border); margin: 10px 6px; } +.admin-main { flex: 1; min-width: 0; padding: 22px clamp(14px, 3vw, 28px); } + +/* ---------- 文档页 ---------- */ +.docs-shell { + display: flex; + gap: 24px; + align-items: flex-start; +} +.docs-sidebar { + width: 240px; + flex: none; + position: sticky; + top: calc(var(--nav-h) + 16px); + max-height: calc(100vh - var(--nav-h) - 32px); + overflow-y: auto; + will-change: scroll-position; +} +.docs-sidebar .docs-search { margin-bottom: 12px; } +.doc-list { display: flex; flex-direction: column; gap: 2px; } +.doc-list a { + display: block; + padding: 7px 11px; + border-radius: var(--radius-sm); + color: var(--c-text-2); + font-size: 13.5px; +} +.doc-list a:hover { background: var(--c-primary-soft); } +.doc-list a.active { background: var(--c-primary-soft); color: var(--c-primary-strong); font-weight: 600; } +.doc-toc { margin-top: 16px; border-top: 1px solid var(--c-border); padding-top: 12px; } +.doc-toc .toc-title { font-size: 12px; color: var(--c-text-3); font-weight: 600; margin-bottom: 6px; } +.doc-toc a { + display: block; + padding: 3px 11px; + font-size: 12.5px; + color: var(--c-text-3); + border-left: 2px solid transparent; +} +.doc-toc a:hover { color: var(--c-primary-strong); } +.doc-toc a.lvl-3 { padding-left: 24px; } + +.docs-content { + flex: 1; + min-width: 0; + padding: clamp(18px, 4vw, 36px); +} +.markdown-body { font-size: 14.5px; color: var(--c-text); word-break: break-word; } +.markdown-body h1 { font-size: 24px; border-bottom: 1px solid var(--c-border); padding-bottom: 10px; } +.markdown-body h2 { font-size: 19px; margin-top: 30px; } +.markdown-body h3 { font-size: 16px; margin-top: 22px; } +.markdown-body p { margin: 10px 0; } +.markdown-body ul, .markdown-body ol { padding-left: 22px; margin: 8px 0; } +.markdown-body code { + font-family: var(--mono); + font-size: 0.88em; + background: var(--c-primary-soft); + border-radius: 5px; + padding: 1.5px 6px; + color: var(--c-primary-strong); +} +.markdown-body pre { + background: rgb(10 15 30 / 88%); + color: #dbe4ff; + border: 1px solid var(--c-border); + border-radius: var(--radius-sm); + padding: 14px 16px; + overflow-x: auto; + font-size: 13px; + line-height: 1.55; +} +.markdown-body pre code { background: transparent; color: inherit; padding: 0; font-size: inherit; } +.markdown-body table { border-collapse: collapse; width: 100%; margin: 12px 0; font-size: 13px; } +.markdown-body th, .markdown-body td { border: 1px solid var(--c-border); padding: 7px 11px; text-align: left; } +.markdown-body th { background: var(--glass-bg-soft); } +.markdown-body blockquote { + margin: 12px 0; + padding: 8px 16px; + border-left: 3px solid var(--c-primary); + background: var(--c-primary-soft); + border-radius: 0 var(--radius-sm) var(--radius-sm) 0; + color: var(--c-text-2); +} +.markdown-body a { word-break: break-all; } + +/* ---------- Swagger UI 微调 ---------- */ +.openapi-wrap .swagger-ui { + font-family: var(--font); +} +.openapi-wrap .swagger-ui .topbar { display: none; } +.openapi-wrap .swagger-ui .info .title { font-size: 22px; } + +/* ---------- 空状态 / 骨架 ---------- */ +.empty { + text-align: center; + color: var(--c-text-3); + padding: 48px 16px; + font-size: 14px; +} +.empty .empty-icon { font-size: 34px; margin-bottom: 8px; } +.spin { + display: inline-block; + width: 18px; + height: 18px; + border: 2px solid rgb(79 110 247 / 25%); + border-top-color: var(--c-primary); + border-radius: 50%; + animation: spin 0.7s linear infinite; + vertical-align: -4px; +} +@keyframes spin { to { transform: rotate(360deg); } } +.loading-block { text-align: center; color: var(--c-text-3); padding: 40px 0; font-size: 14px; } + +/* ---------- 拖拽上传区(玻璃拖放区) ---------- */ +.dropzone { + border: 1.5px dashed var(--c-border); + border-radius: var(--radius); + background: var(--glass-bg-soft); + -webkit-backdrop-filter: blur(8px); + backdrop-filter: blur(8px); + padding: 34px 16px; + text-align: center; + color: var(--c-text-2); + cursor: pointer; + transition: border-color 0.15s, background 0.15s; +} +.dropzone:hover, .dropzone.dragover { + border-color: var(--c-primary); + background: var(--c-primary-soft); +} +.dropzone .dz-icon { font-size: 30px; margin-bottom: 6px; } +.dropzone .dz-main { font-weight: 600; color: var(--c-text); } +.dropzone .dz-sub { font-size: 12.5px; color: var(--c-text-3); margin-top: 3px; } + +.file-chip { + display: flex; + align-items: center; + gap: 10px; + background: var(--c-primary-soft); + border: 1px solid rgb(79 110 247 / 25%); + border-radius: var(--radius-sm); + padding: 10px 14px; + font-size: 14px; +} +.file-chip .fc-name { font-weight: 600; word-break: break-all; } +.file-chip .fc-size { color: var(--c-text-3); font-size: 12.5px; flex: none; } +.file-chip .fc-remove { margin-left: auto; cursor: pointer; color: var(--c-text-3); border: none; background: none; font-size: 16px; padding: 2px 6px; } +.file-chip .fc-remove:hover { color: var(--c-danger); } + +/* ---------- Logo 上传预览 ---------- */ +.logo-preview-row { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; } +.logo-input-row { display: flex; gap: 8px; } +.logo-input-row .input { flex: 1; } +.logo-box { + width: 64px; + height: 64px; + border: 1px solid var(--c-border); + border-radius: var(--radius-sm); + background: + linear-gradient(45deg, var(--glass-bg-soft) 25%, transparent 25%, transparent 75%, var(--glass-bg-soft) 75%), + linear-gradient(45deg, var(--glass-bg-soft) 25%, transparent 25%, transparent 75%, var(--glass-bg-soft) 75%); + background-size: 12px 12px; + background-position: 0 0, 6px 6px; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +} +.logo-box img { max-width: 52px; max-height: 52px; object-fit: contain; } + +/* ---------- 首页 Hero ---------- */ +.hero { text-align: center; padding: 26px 0 8px; } +.hero h1 { font-size: clamp(22px, 4.5vw, 30px); margin-bottom: 6px; } +.hero p { color: var(--c-text-2); margin: 0 auto; max-width: 560px; font-size: 14.5px; } + +.quick-pickup { + display: flex; + gap: 10px; + max-width: 460px; + margin: 18px auto 0; +} +.quick-pickup .input { font-family: var(--mono); letter-spacing: 2px; text-transform: uppercase; } + +.share-card { max-width: 640px; margin: 22px auto 0; } + +/* ---------- 取件页 ---------- */ +.text-view { + background: var(--glass-bg-soft); + border: 1px solid var(--c-border); + border-radius: var(--radius-sm); + padding: 14px 16px; + font-family: var(--mono); + font-size: 13px; + white-space: pre-wrap; + word-break: break-word; + max-height: 50vh; + overflow-y: auto; + margin: 0; + will-change: scroll-position; +} +.file-summary { + display: flex; + gap: 14px; + align-items: center; + background: var(--glass-bg-soft); + border: 1px solid var(--c-border); + border-radius: var(--radius-sm); + padding: 14px 16px; +} + +/* ---------- 管理工具条(视图自带样式公共部分) ---------- */ +.toolbar { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; + margin-bottom: 16px; +} +.page-title { font-size: 19px; margin: 0; } +.page-sub { color: var(--c-text-3); font-size: 13px; margin: 2px 0 0; } +.toolbar-actions { display: flex; gap: 8px; flex-wrap: wrap; } +.row-actions { display: flex; gap: 6px; flex-wrap: wrap; } + +/* ========================================================= + 背景图(需求⑤):App.vue 渲染 .app-ambient(渐变)与 .app-bg(图片) + 固定层 + 半透明遮罩保证前景可读性;z-index 低于内容。 + ========================================================= */ +.app-ambient, +.app-bg, +.app-bg-tint { + position: fixed; + inset: 0; + z-index: -1; + pointer-events: none; +} +.app-ambient { + background: var(--ambient); + /* 固定背景层为独立合成层,避免滚动重绘(性能) */ + will-change: transform; +} +.app-bg { + background-size: cover; + background-position: center; + will-change: transform; +} +.app-bg-tint { + background: var(--bg-tint); + backdrop-filter: none; +} + +/* ---------- 降级策略(需求③性能与低端设备) ---------- + 1) 不支持 backdrop-filter → 实色高不透明表面 + 2) prefers-reduced-transparency → 收窄模糊与透明度 + 3) 低性能提示(指针粗糙 + 小屏)→ 移除大面积 blur */ +@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { + .site-nav, + .card, + .modal, + .toast, + .notify-pop, + .table-wrap, + .tabs, + .dropzone, + .btn-ghost, + .site-footer { + background: var(--c-card); + -webkit-backdrop-filter: none; + backdrop-filter: none; + } + .btn-ghost, + .tabs, + .dropzone, + .site-footer { background: var(--c-bg); } +} + +@media (prefers-reduced-transparency: reduce) { + .site-nav, + .card, + .modal, + .toast, + .notify-pop, + .table-wrap, + .tabs, + .dropzone, + .btn-ghost, + .site-footer { + -webkit-backdrop-filter: none; + backdrop-filter: none; + background: var(--c-card); + border-color: var(--c-border); + } + .app-bg-tint { background: var(--c-card); } +} + +/* 无障碍:减少动态效果 */ +@media (prefers-reduced-motion: reduce) { + .toast, .notify-pop { animation: none; } + .btn:active { transform: none; } +} + +/* ---------- 响应式 ---------- */ +@media (max-width: 860px) { + .docs-shell { flex-direction: column; } + .docs-sidebar { + position: static; + width: 100%; + max-height: none; + } + .doc-list { flex-direction: row; flex-wrap: wrap; gap: 6px; } + .doc-list a { border: 1px solid var(--c-border); background: var(--glass-bg-soft); } + .doc-toc { display: none; } +} + +@media (max-width: 720px) { + .site-nav { gap: 10px; } + .nav-links a { padding: 6px 9px; font-size: 13px; } + /* 小屏移除大面积 blur(低端降级),保留半透明 */ + .site-nav, + .card, + .modal, + .notify-pop, + .table-wrap { + -webkit-backdrop-filter: none; + backdrop-filter: none; + background: var(--c-card); + } + .admin-shell { flex-direction: column; } + .admin-aside { + width: 100%; + border-right: none; + border-bottom: 1px solid var(--c-border); + padding: 10px 12px; + } + .aside-menu { flex-direction: row; overflow-x: auto; gap: 4px; } + .aside-menu a { white-space: nowrap; padding: 7px 11px; } + .aside-menu .aside-sep { width: 1px; height: auto; margin: 0 4px; } + .admin-main { padding: 16px 14px; } + .page { padding-top: 18px; } + .card { padding: 16px; } + .quick-pickup { flex-direction: column; } + .pager { justify-content: center; } +} + +@media (max-width: 420px) { + .brand .brand-name { font-size: 15px; } + .table { min-width: 640px; } + .theme-seg button { padding: 5px 7px; } +} diff --git a/web/src/theme/index.ts b/web/src/theme/index.ts new file mode 100644 index 0000000..ca2eb15 --- /dev/null +++ b/web/src/theme/index.ts @@ -0,0 +1,75 @@ +/** + * 主题系统(需求②): + * - 三态:light / dark / system(跟随 prefers-color-scheme) + * - 默认跟随系统;localStorage 记忆(键 fcb_theme_mode) + * - 实际生效主题写入 ,CSS 变量按此切换 + * - Naive UI darkTheme 由 App.vue 读取 useTheme().resolved 响应式传递 + */ +import { ref, watch } from 'vue' + +export type ThemeMode = 'light' | 'dark' | 'system' +export type ResolvedTheme = 'light' | 'dark' + +export const THEME_MODES: ThemeMode[] = ['light', 'dark', 'system'] + +const STORAGE_KEY = 'fcb_theme_mode' + +function readStoredMode(): ThemeMode | null { + try { + const v = localStorage.getItem(STORAGE_KEY) + return v === 'light' || v === 'dark' || v === 'system' ? v : null + } catch { + return null + } +} + +function persist(mode: ThemeMode): void { + try { + localStorage.setItem(STORAGE_KEY, mode) + } catch { + /* ignore */ + } +} + +function systemPrefersDark(): boolean { + return typeof matchMedia === 'function' && matchMedia('(prefers-color-scheme: dark)').matches +} + +/** 当前模式(未初始化时按 localStorage → 默认 system) */ +const mode = ref(readStoredMode() ?? 'system') +/** 实际生效主题(响应式,供 Naive UI 与 使用) */ +const resolved = ref(mode.value === 'system' ? (systemPrefersDark() ? 'dark' : 'light') : mode.value) + +let started = false + +/** 初始化系统主题监听(App.vue 挂载时调用一次) */ +function start(): void { + if (started || typeof matchMedia !== 'function') return + started = true + const mq = matchMedia('(prefers-color-scheme: dark)') + mq.addEventListener?.('change', () => { + if (mode.value === 'system') resolved.value = mq.matches ? 'dark' : 'light' + }) +} + +function apply(): void { + start() + resolved.value = mode.value === 'system' ? (systemPrefersDark() ? 'dark' : 'light') : mode.value + document.documentElement.dataset.theme = resolved.value +} + +watch(mode, apply, { immediate: true }) + +/** 切换模式并记忆(需求②:导航栏三态切换 + localStorage) */ +export function setThemeMode(next: ThemeMode): void { + mode.value = next + persist(next) +} + +export function useTheme() { + return { + mode, + resolved, + setMode: setThemeMode, + } +} diff --git a/web/src/utils/chunkUpload.ts b/web/src/utils/chunkUpload.ts new file mode 100644 index 0000000..4c1c336 --- /dev/null +++ b/web/src/utils/chunkUpload.ts @@ -0,0 +1,122 @@ +/** 分片大小与并发策略:按文件体积自适应,大文件用大分片 + 多并发。 */ +import { sha256Hex } from './format' + +export interface ChunkPlan { + chunkSize: number + concurrency: number +} + +export function planChunks(fileSize: number): ChunkPlan { + if (fileSize <= 10 * 1024 * 1024) return { chunkSize: 1 * 1024 * 1024, concurrency: 3 } // ≤10MB → 1MB + if (fileSize <= 100 * 1024 * 1024) return { chunkSize: 5 * 1024 * 1024, concurrency: 3 } // ≤100MB → 5MB + if (fileSize <= 512 * 1024 * 1024) return { chunkSize: 10 * 1024 * 1024, concurrency: 2 } + return { chunkSize: 20 * 1024 * 1024, concurrency: 2 } +} + +export interface ChunkDeps { + init: (fileName: string, fileSize: number, chunkSize: number, fileHash: string) => Promise<{ + upload_id: string + chunk_size: number + total_chunks: number + uploaded_chunks: number[] + }> + uploadOne: (uploadId: string, index: number, blob: Blob) => Promise + status: (uploadId: string) => Promise<{ uploaded_chunks: number[]; total_chunks: number }> + finish: (uploadId: string, expireValue: number, expireStyle: string, code?: string) => Promise<{ code: string; name: string }> + cancel: (uploadId: string) => Promise +} + +export interface ChunkUploadOptions { + file: File + expireValue: number + expireStyle: string + customCode?: string // v3.1:自定义提取码(4-8 位字母数字) + onProgress?: (percent: number, uploadedBytes: number, totalBytes: number) => void +} + +/** 返回取消句柄:调用返回值上的 cancel() 中止上传并清理服务端分片。 */ +export interface ChunkUploadSession { + promise: Promise<{ code: string; name: string }> + cancel: () => void +} + +/** + * 分片上传:断点续传(file_hash 匹配已传分片)、单分片失败自动重试一次、 + * 并发任务池、完成前向服务端核对分片清单。 + */ +export function uploadFileInChunks(opts: ChunkUploadOptions, deps: ChunkDeps): ChunkUploadSession { + const { file, expireValue, expireStyle, customCode, onProgress } = opts + + let cancelled = false + const promise = (async (): Promise<{ code: string; name: string }> => { + const plan = planChunks(file.size) + const totalChunks = Math.max(1, Math.ceil(file.size / plan.chunkSize)) + + // 整文件 SHA-256 作为断点续传 key(对齐参考实现语义;计算失败降级为指纹串) + let fileHash: string + try { + fileHash = await sha256Hex(await file.arrayBuffer()) + } catch { + fileHash = `nofp-${file.size}-${file.lastModified}` + } + + const initRes = await deps.init(file.name, file.size, plan.chunkSize, fileHash) + const uploadId = initRes.upload_id + const effChunkSize = initRes.chunk_size || plan.chunkSize + const effTotal = initRes.total_chunks || totalChunks + const done = new Set(initRes.uploaded_chunks ?? []) + + const report = () => { + if (!onProgress) return + let uploadedBytes = 0 + for (const i of done) { + const start = i * effChunkSize + uploadedBytes += Math.max(0, Math.min(effChunkSize, file.size - start)) + } + onProgress(Math.min(100, Math.round((uploadedBytes / file.size) * 100)), uploadedBytes, file.size) + } + report() + + const uploadOneWithRetry = async (index: number): Promise => { + for (let attempt = 0; attempt < 2; attempt++) { + try { + const start = index * effChunkSize + const blob = file.slice(start, Math.min(start + effChunkSize, file.size)) + await deps.uploadOne(uploadId, index, blob) + done.add(index) + report() + return + } catch (e) { + if (attempt === 1) throw e + } + } + } + + // 受并发上限的简易任务池 + let nextIndex = 0 + const worker = async (): Promise => { + while (!cancelled) { + const index = nextIndex++ + if (index >= effTotal) return + if (done.has(index)) continue + await uploadOneWithRetry(index) + } + } + await Promise.all(Array.from({ length: plan.concurrency }, () => worker())) + + if (cancelled) { + await deps.cancel(uploadId).catch(() => undefined) + throw new DOMException('上传已取消', 'AbortError') + } + + // 完成前向服务端核对一次分片清单(防个别分片响应丢失) + if (done.size < effTotal) { + const st = await deps.status(uploadId) + for (const i of st.uploaded_chunks ?? []) done.add(i) + } + + return deps.finish(uploadId, expireValue, expireStyle, customCode) + })() + + return { promise, cancel: () => (cancelled = true) } +} diff --git a/web/src/utils/format.ts b/web/src/utils/format.ts new file mode 100644 index 0000000..bd51904 --- /dev/null +++ b/web/src/utils/format.ts @@ -0,0 +1,141 @@ +/** 前端工具函数 */ +import { i18n } from '@/i18n' + +function tt(key: string, params?: Record): string { + return i18n.global.t(key, params ?? {}) +} + +export function formatBytes(bytes: number | null | undefined): string { + if (bytes === null || bytes === undefined || Number.isNaN(bytes)) return '-' + if (bytes < 1024) return `${bytes} B` + const units = ['KB', 'MB', 'GB', 'TB'] + let v = bytes + let i = -1 + do { + v /= 1024 + i++ + } while (v >= 1024 && i < units.length - 1) + return `${v.toFixed(v >= 100 ? 0 : 1)} ${units[i]}` +} + +export function formatDateTime(iso: string | null | undefined): string { + if (!iso) return '-' + const d = new Date(iso) + if (Number.isNaN(d.getTime())) return String(iso) + const p = (n: number) => `${n}`.padStart(2, '0') + return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}` +} + +/** 剩余有效期的人类描述 */ +export function formatRemaining(expiredAt: string | null | undefined): string { + if (!expiredAt) return tt('time.forever') + const t = new Date(expiredAt).getTime() + if (Number.isNaN(t)) return tt('time.forever') + const diff = t - Date.now() + if (diff <= 0) return tt('time.expired') + const m = Math.floor(diff / 60_000) + if (m < 1) return tt('time.lessThanMinute') + if (m < 60) return tt('time.minutes', { n: m }) + const h = Math.floor(m / 60) + if (h < 24) return tt('time.hoursMinutes', { h, m: m % 60 }) + const d = Math.floor(h / 24) + return tt('time.daysHours', { d, h: h % 24 }) +} + +export function formatDuration(ms: number | null | undefined): string { + if (ms === null || ms === undefined) return '-' + if (ms < 1000) return `${ms} ms` + return `${(ms / 1000).toFixed(2)} s` +} + +/** 过期样式选项(对齐参考实现的 expireStyle 配置;label 用 i18n 键名,展示时经 expireStyleLabel 翻译) */ +export const EXPIRE_STYLES: { value: string; label: string }[] = [ + { value: 'day', label: 'day' }, + { value: 'hour', label: 'hour' }, + { value: 'minute', label: 'minute' }, + { value: 'count', label: 'count' }, + { value: 'forever', label: 'forever' }, +] + +/** 过期样式翻译(未知样式原样返回) */ +export function expireStyleLabel(style: string): string { + const known = EXPIRE_STYLES.find((s) => s.value === style) + if (known) return tt(`expireStyle.${known.value}`) + return style +} + +/** 简单防抖 */ +export function debounce void>(fn: T, wait: number): (...args: Parameters) => void { + let timer: ReturnType | null = null + return (...args: Parameters) => { + if (timer) clearTimeout(timer) + timer = setTimeout(() => fn(...args), wait) + } +} + +/** 从 Content-Disposition 提取文件名(支持 RFC 5987 filename*=UTF-8'') */ +export function filenameFromDisposition(header: string | null): string | null { + if (!header) return null + const star = /filename\*=(?:UTF-8'')?([^;]+)/i.exec(header) + if (star) { + try { + return decodeURIComponent(star[1].replace(/["']/g, '').trim()) + } catch { + /* fallthrough */ + } + } + const plain = /filename="?([^";]+)"?/i.exec(header) + return plain ? plain[1] : null +} + +/** 触发浏览器下载 Blob */ +export function downloadBlob(blob: Blob, filename: string): void { + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = filename + document.body.appendChild(a) + a.click() + a.remove() + setTimeout(() => URL.revokeObjectURL(url), 5_000) +} + +export async function copyText(text: string): Promise { + try { + await navigator.clipboard.writeText(text) + return true + } catch { + // 兼容非安全上下文(http 部署) + try { + const ta = document.createElement('textarea') + ta.value = text + ta.style.position = 'fixed' + ta.style.opacity = '0' + document.body.appendChild(ta) + ta.select() + const ok = document.execCommand('copy') + ta.remove() + return ok + } catch { + return false + } + } +} + +/** 计算 SHA-256(hex),用于分片上传 file_hash */ +export async function sha256Hex(data: ArrayBuffer): Promise { + const digest = await crypto.subtle.digest('SHA-256', data) + return Array.from(new Uint8Array(digest)) + .map((b) => b.toString(16).padStart(2, '0')) + .join('') +} + +/** 宽松取值:对象上第一个存在的键(用于兼容 camelCase/snake_case 响应) */ +export function pick(obj: Record, keys: string[]): T | undefined { + for (const k of keys) { + if (obj && typeof obj === 'object' && k in obj && obj[k] !== undefined && obj[k] !== null) { + return obj[k] as T + } + } + return undefined +} diff --git a/web/src/utils/markdown.ts b/web/src/utils/markdown.ts new file mode 100644 index 0000000..28c6f4d --- /dev/null +++ b/web/src/utils/markdown.ts @@ -0,0 +1,98 @@ +/** Markdown 渲染:marked 解析 + DOM 级净化(防 XSS)+ 标题锚点 + 目录提取 */ +import { marked } from 'marked' + +export interface TocItem { + id: string + text: string + level: number +} + +export interface RenderResult { + html: string + toc: TocItem[] +} + +function slugify(text: string, used: Set): string { + const base = + text + .toLowerCase() + .replace(/[^\p{L}\p{N}\s-]/gu, '') + .trim() + .replace(/\s+/g, '-') || 'section' + let slug = base + let i = 2 + while (used.has(slug)) slug = `${base}-${i++}` + used.add(slug) + return slug +} + +// L7 加固:补齐 foreign-content/嵌入类标签与危险属性清单(原仅黑名单 9 类 + +// on* + javascript:,未覆盖 srcdoc/formaction/xlink:href/srcset/data: URL 等) +const BLOCKED_TAGS = new Set([ + 'script', 'style', 'iframe', 'object', 'embed', 'form', 'link', 'meta', 'base', + 'svg', 'math', 'frame', 'frameset', 'applet', 'template', 'noscript', 'title', +]) + +/** 危险属性:事件类、URL 注入类与沙箱逃逸类 */ +const BLOCKED_ATTRS = new Set([ + 'srcdoc', 'sandbox', 'formaction', 'action', 'xlink:href', 'srcset', 'poster', 'background', 'dynsrc', 'lowsrc', 'data', +]) + +/** 仅允许 http(s)、站内相对路径、锚点与 mailto(src 另允许 data:image/) */ +function safeUrl(value: string, isSrc: boolean): boolean { + const v = value.trim() + if (isSrc && /^data:image\//i.test(v)) return true + return /^(https?:|mailto:|\/|#|\.\/)/i.test(v) || !/^[a-z][a-z0-9+.-]*:/i.test(v) +} + +/** 净化 HTML:移除危险标签/危险属性/非白名单协议 URL */ +export function sanitizeHtml(html: string): string { + const doc = new DOMParser().parseFromString(html, 'text/html') + for (const el of [...doc.body.querySelectorAll('*')]) { + const tag = el.tagName.toLowerCase() + if (BLOCKED_TAGS.has(tag)) { + el.remove() + continue + } + for (const attr of [...el.attributes]) { + const name = attr.name.toLowerCase() + if (name.startsWith('on') || BLOCKED_ATTRS.has(name)) { + el.removeAttribute(attr.name) + continue + } + if ((name === 'href' || name === 'src' || name.endsWith(':src') || name.endsWith(':href')) && + !safeUrl(attr.value, name === 'src')) { + el.removeAttribute(attr.name) + } + } + } + return doc.body.innerHTML +} + +/** 渲染 markdown → 净化后的 HTML + 标题目录(h2/h3 作为二级/三级目录) */ +export function renderMarkdown(md: string): RenderResult { + const rawHtml = marked.parse(md, { gfm: true, breaks: false }) as string + const doc = new DOMParser().parseFromString(rawHtml, 'text/html') + + // 外链新窗口打开;站内锚点保持 + for (const a of [...doc.body.querySelectorAll('a[href]')]) { + const href = a.getAttribute('href') ?? '' + if (/^https?:\/\//i.test(href)) { + a.setAttribute('target', '_blank') + a.setAttribute('rel', 'noopener noreferrer') + } + } + + const used = new Set() + const toc: TocItem[] = [] + for (const h of [...doc.body.querySelectorAll('h1, h2, h3')]) { + const level = Number(h.tagName.substring(1)) + const text = (h.textContent ?? '').trim() + if (!text) continue + const id = slugify(text, used) + h.setAttribute('id', id) + if (level >= 2) toc.push({ id, text, level }) + } + + return { html: sanitizeHtml(doc.body.innerHTML), toc } +} diff --git a/web/src/views/DocsView.vue b/web/src/views/DocsView.vue new file mode 100644 index 0000000..4de2912 --- /dev/null +++ b/web/src/views/DocsView.vue @@ -0,0 +1,147 @@ + + + diff --git a/web/src/views/HomeView.vue b/web/src/views/HomeView.vue new file mode 100644 index 0000000..0dfe09f --- /dev/null +++ b/web/src/views/HomeView.vue @@ -0,0 +1,279 @@ + + + diff --git a/web/src/views/NotFoundView.vue b/web/src/views/NotFoundView.vue new file mode 100644 index 0000000..d670bb6 --- /dev/null +++ b/web/src/views/NotFoundView.vue @@ -0,0 +1,17 @@ + + + diff --git a/web/src/views/OpenApiView.vue b/web/src/views/OpenApiView.vue new file mode 100644 index 0000000..733f6db --- /dev/null +++ b/web/src/views/OpenApiView.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/web/src/views/PickupView.vue b/web/src/views/PickupView.vue new file mode 100644 index 0000000..05308c6 --- /dev/null +++ b/web/src/views/PickupView.vue @@ -0,0 +1,176 @@ + + + diff --git a/web/src/views/admin/AdminLayout.vue b/web/src/views/admin/AdminLayout.vue new file mode 100644 index 0000000..e29ce61 --- /dev/null +++ b/web/src/views/admin/AdminLayout.vue @@ -0,0 +1,44 @@ + + + diff --git a/web/src/views/admin/AuditView.vue b/web/src/views/admin/AuditView.vue new file mode 100644 index 0000000..d705909 --- /dev/null +++ b/web/src/views/admin/AuditView.vue @@ -0,0 +1,218 @@ + + + + + diff --git a/web/src/views/admin/FilesView.vue b/web/src/views/admin/FilesView.vue new file mode 100644 index 0000000..3a7b291 --- /dev/null +++ b/web/src/views/admin/FilesView.vue @@ -0,0 +1,277 @@ + + + diff --git a/web/src/views/admin/LoginView.vue b/web/src/views/admin/LoginView.vue new file mode 100644 index 0000000..4f1ea2b --- /dev/null +++ b/web/src/views/admin/LoginView.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/web/src/views/admin/SettingsView.vue b/web/src/views/admin/SettingsView.vue new file mode 100644 index 0000000..34771f9 --- /dev/null +++ b/web/src/views/admin/SettingsView.vue @@ -0,0 +1,784 @@ + + + + + diff --git a/web/src/vite-env.d.ts b/web/src/vite-env.d.ts new file mode 100644 index 0000000..0b29e78 --- /dev/null +++ b/web/src/vite-env.d.ts @@ -0,0 +1,19 @@ +/// + +interface ImportMetaEnv { + /** 后端 API 根地址,如 http://127.0.0.1:8466;默认为空 = 同源(embed 部署) */ + readonly VITE_API_BASE?: string + /** 置 1 时 dev server 启用内置 mock(无需启动后端) */ + readonly VITE_MOCK?: string + /** 路由模式,默认 history(后端 NoRoute 已做 SPA 回退);后端无回退时设 'hash' */ + readonly VITE_ROUTER_MODE?: 'history' | 'hash' +} + +interface ImportMeta { + readonly env: ImportMetaEnv +} + +declare module 'swagger-ui-dist/swagger-ui-es-bundle.js' { + const SwaggerUI: (options: Record) => unknown + export default SwaggerUI +} diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 0000000..a415f2c --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client", "node"], + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "jsx": "preserve", + "esModuleInterop": true, + "isolatedModules": true, + "skipLibCheck": true, + "noEmit": true, + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue", "vite.config.ts", "mock/**/*.ts"] +} diff --git a/web/vite.config.ts b/web/vite.config.ts new file mode 100644 index 0000000..4c8f9c5 --- /dev/null +++ b/web/vite.config.ts @@ -0,0 +1,56 @@ +import { fileURLToPath, URL } from 'node:url' +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vite.dev/config/ +export default defineConfig({ + // go:embed + NoRoute SPA 回退(go-api 已实现):history 路由用绝对资源路径 + base: '/', + plugins: [vue()], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + // 站内文档(docs/api/*.md、docs/openapi.yaml)位于 web/ 之外,dev server 允许访问 + server: { + fs: { + allow: ['.', '..'], + }, + // 业务 API 在根路径(/share /chunk /presign /admin,最终版契约),公共接口在 /api/v1。 + // /admin 与前端 SPA 路由前缀重叠:GET 且 Accept 含 text/html(浏览器导航/刷新)时 bypass + // 回 SPA,其余(fetch/XHR API 调用)代理到后端;embed 后由后端 NoRoute 回退兜底。 + proxy: import.meta.env?.VITE_API_BASE + ? undefined + : { + '/api': { target: 'http://127.0.0.1:8466', changeOrigin: true }, + '/share': { target: 'http://127.0.0.1:8466', changeOrigin: true }, + '/chunk': { target: 'http://127.0.0.1:8466', changeOrigin: true }, + '/presign': { target: 'http://127.0.0.1:8466', changeOrigin: true }, + '/admin': { + target: 'http://127.0.0.1:8466', + changeOrigin: true, + bypass(req) { + if (req.method === 'GET' && (req.headers.accept ?? '').includes('text/html')) { + return '/index.html' + } + return undefined + }, + }, + }, + }, + build: { + outDir: 'dist', + assetsDir: 'assets', + sourcemap: false, + chunkSizeWarningLimit: 1600, + rollupOptions: { + output: { + manualChunks: { + swagger: ['swagger-ui-dist'], + markdown: ['marked'], + }, + }, + }, + }, +})
    "+(r.highlight&&r.highlight.apply(r.highlight,[c.content].concat(o))||Mi(c.content))+"