From 9a8f91ec05c00878f3ebb4c9855b731cab1bd3fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E5=BF=97=E5=A8=81?= Date: Sun, 7 Jun 2026 17:52:13 +0800 Subject: [PATCH] chore: scaffold http client app plan implementation --- .gitignore | 23 + backend/cmd/api-client/main.go | 23 + backend/go.mod | 39 + backend/go.sum | 88 + backend/internal/api/router.go | 31 + backend/internal/api/router_test.go | 53 + backend/internal/model/envelope.go | 28 + contracts/app-api.openapi.yaml | 94 + contracts/events.schema.json | 41 + .../environments.schema.json | 63 + .../mock-file.schema.json | 156 + .../openapi-sources.schema.json | 113 + .../settings.schema.json | 72 + frontend/index.html | 12 + frontend/package-lock.json | 4324 +++++++++++++++++ frontend/package.json | 23 + frontend/src/App.test.ts | 12 + frontend/src/App.vue | 28 + frontend/src/main.ts | 6 + frontend/src/styles.css | 144 + frontend/src/vite-env.d.ts | 1 + frontend/tsconfig.json | 17 + frontend/vite.config.ts | 13 + frontend/vitest.config.ts | 17 + http-client-app-plan.md | 2709 +++++++++++ scripts/verify-docs.ps1 | 171 + 26 files changed, 8301 insertions(+) create mode 100644 .gitignore create mode 100644 backend/cmd/api-client/main.go create mode 100644 backend/go.mod create mode 100644 backend/go.sum create mode 100644 backend/internal/api/router.go create mode 100644 backend/internal/api/router_test.go create mode 100644 backend/internal/model/envelope.go create mode 100644 contracts/app-api.openapi.yaml create mode 100644 contracts/events.schema.json create mode 100644 contracts/workspace-json-schemas/environments.schema.json create mode 100644 contracts/workspace-json-schemas/mock-file.schema.json create mode 100644 contracts/workspace-json-schemas/openapi-sources.schema.json create mode 100644 contracts/workspace-json-schemas/settings.schema.json create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/src/App.test.ts create mode 100644 frontend/src/App.vue create mode 100644 frontend/src/main.ts create mode 100644 frontend/src/styles.css create mode 100644 frontend/src/vite-env.d.ts create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vite.config.ts create mode 100644 frontend/vitest.config.ts create mode 100644 http-client-app-plan.md create mode 100644 scripts/verify-docs.ps1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c841867 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Dependencies +frontend/node_modules/ + +# Build outputs +frontend/dist/ +backend/bin/ + +# Runtime/cache data +http-client-cache/ +http-client-responses/ +http-client-events/ +http-client-logs/ +http-client-diagnostics/ +http-client-backups/ + +# Local databases and logs +*.db +*.db-* +*.log + +# OS/editor noise +.DS_Store +Thumbs.db diff --git a/backend/cmd/api-client/main.go b/backend/cmd/api-client/main.go new file mode 100644 index 0000000..c0bc1e5 --- /dev/null +++ b/backend/cmd/api-client/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "errors" + "log" + "net/http" + "time" + + "github.com/local/http-client-app/backend/internal/api" +) + +func main() { + server := &http.Server{ + Addr: "127.0.0.1:32180", + Handler: api.NewRouter(), + ReadHeaderTimeout: 5 * time.Second, + } + + log.Printf("HTTP Client local service listening on http://%s", server.Addr) + if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + log.Fatal(err) + } +} diff --git a/backend/go.mod b/backend/go.mod new file mode 100644 index 0000000..204e20e --- /dev/null +++ b/backend/go.mod @@ -0,0 +1,39 @@ +module github.com/local/http-client-app/backend + +go 1.26.0 + +require github.com/gin-gonic/gin v1.11.0 + +require ( + github.com/bytedance/sonic v1.14.0 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gin-contrib/sse v1.1.0 // 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/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/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.54.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.0 // indirect + go.uber.org/mock v0.5.0 // indirect + golang.org/x/arch v0.20.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.27.0 // indirect + golang.org/x/tools v0.34.0 // indirect + google.golang.org/protobuf v1.36.9 // indirect +) diff --git a/backend/go.sum b/backend/go.sum new file mode 100644 index 0000000..a25fff5 --- /dev/null +++ b/backend/go.sum @@ -0,0 +1,88 @@ +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/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/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/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/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/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/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/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.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= +github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= +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.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.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +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.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +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= diff --git a/backend/internal/api/router.go b/backend/internal/api/router.go new file mode 100644 index 0000000..549c38e --- /dev/null +++ b/backend/internal/api/router.go @@ -0,0 +1,31 @@ +package api + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/local/http-client-app/backend/internal/model" +) + +func NewRouter() http.Handler { + gin.SetMode(gin.ReleaseMode) + + router := gin.New() + router.Use(gin.Recovery()) + + router.GET("/api/health", func(c *gin.Context) { + c.JSON(http.StatusOK, model.SuccessEnvelope(requestID(c), gin.H{ + "status": "ok", + })) + }) + + return router +} + +func requestID(c *gin.Context) string { + if requestID := c.GetHeader("X-Request-Id"); requestID != "" { + return requestID + } + + return "req_local" +} diff --git a/backend/internal/api/router_test.go b/backend/internal/api/router_test.go new file mode 100644 index 0000000..06e2f28 --- /dev/null +++ b/backend/internal/api/router_test.go @@ -0,0 +1,53 @@ +package api_test + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/local/http-client-app/backend/internal/api" +) + +func TestHealthEndpointReturnsEnvelope(t *testing.T) { + router := api.NewRouter() + + req := httptest.NewRequest(http.MethodGet, "/api/health", nil) + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("expected status 200, got %d", rec.Code) + } + + var body map[string]any + if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { + t.Fatalf("response is not JSON: %v", err) + } + + if body["success"] != true { + t.Fatalf("expected success=true, got %#v", body["success"]) + } + + if body["error"] != nil { + t.Fatalf("expected error=null, got %#v", body["error"]) + } + + data, ok := body["data"].(map[string]any) + if !ok { + t.Fatalf("expected data object, got %#v", body["data"]) + } + + if data["status"] != "ok" { + t.Fatalf("expected data.status=ok, got %#v", data["status"]) + } + + if body["requestId"] == "" { + t.Fatalf("expected requestId to be set") + } + + if body["timestamp"] == "" { + t.Fatalf("expected timestamp to be set") + } +} diff --git a/backend/internal/model/envelope.go b/backend/internal/model/envelope.go new file mode 100644 index 0000000..cef1215 --- /dev/null +++ b/backend/internal/model/envelope.go @@ -0,0 +1,28 @@ +package model + +import "time" + +type ErrorResponse struct { + Code string `json:"code"` + Message string `json:"message"` + Details any `json:"details"` + Location any `json:"location"` +} + +type Envelope struct { + Success bool `json:"success"` + Data any `json:"data"` + Error *ErrorResponse `json:"error"` + RequestID string `json:"requestId"` + Timestamp string `json:"timestamp"` +} + +func SuccessEnvelope(requestID string, data any) Envelope { + return Envelope{ + Success: true, + Data: data, + Error: nil, + RequestID: requestID, + Timestamp: time.Now().UTC().Format(time.RFC3339), + } +} diff --git a/contracts/app-api.openapi.yaml b/contracts/app-api.openapi.yaml new file mode 100644 index 0000000..7de52cc --- /dev/null +++ b/contracts/app-api.openapi.yaml @@ -0,0 +1,94 @@ +openapi: 3.1.0 +info: + title: HTTP Client App Internal API + version: 0.1.0 + description: Internal REST API contract for the local HTTP Client application. +servers: + - url: http://127.0.0.1:32180 + description: Local development server +paths: + /api/health: + get: + operationId: getHealth + summary: Check local service health + tags: + - system + responses: + "200": + description: Service is healthy. + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/ApiEnvelope" + - type: object + properties: + data: + $ref: "#/components/schemas/HealthData" + examples: + healthy: + value: + success: true + data: + status: ok + version: 0.1.0 + error: null + requestId: req_01HZY7R4S7S0Y73ZJ81EF1Z8Y4 + timestamp: "2026-06-06T10:20:30Z" +components: + schemas: + ApiEnvelope: + type: object + required: + - success + - data + - error + - requestId + - timestamp + properties: + success: + type: boolean + description: Whether the request completed successfully. + data: + description: Response payload. Null when the request failed. + error: + oneOf: + - $ref: "#/components/schemas/ApiError" + - type: "null" + requestId: + type: string + minLength: 1 + timestamp: + type: string + format: date-time + additionalProperties: false + ApiError: + type: object + required: + - code + - message + properties: + code: + type: string + minLength: 1 + message: + type: string + minLength: 1 + details: + description: Optional structured error details. + additionalProperties: false + HealthData: + type: object + required: + - status + properties: + status: + type: string + enum: + - ok + version: + type: string + uptimeSeconds: + type: integer + minimum: 0 + additionalProperties: false diff --git a/contracts/events.schema.json b/contracts/events.schema.json new file mode 100644 index 0000000..b130651 --- /dev/null +++ b/contracts/events.schema.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.local/http-client-app/contracts/events.schema.json", + "title": "WebSocket Event Envelope", + "description": "Base envelope for WebSocket events emitted by the local HTTP Client application.", + "type": "object", + "required": [ + "type", + "executionId", + "seq", + "timestamp", + "payload" + ], + "properties": { + "type": { + "type": "string", + "minLength": 1, + "examples": [ + "execution.response.chunk" + ] + }, + "executionId": { + "type": "string", + "minLength": 1 + }, + "seq": { + "type": "integer", + "minimum": 0, + "description": "Monotonically increasing sequence number within an execution." + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "payload": { + "type": "object", + "description": "Event-specific payload. Specialized schemas can narrow this object by event type." + } + }, + "additionalProperties": false +} diff --git a/contracts/workspace-json-schemas/environments.schema.json b/contracts/workspace-json-schemas/environments.schema.json new file mode 100644 index 0000000..11e6dd6 --- /dev/null +++ b/contracts/workspace-json-schemas/environments.schema.json @@ -0,0 +1,63 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.local/http-client-app/contracts/workspace-json-schemas/environments.schema.json", + "title": "HTTP Client Environments", + "type": "object", + "required": [ + "schemaVersion", + "environments" + ], + "properties": { + "schemaVersion": { + "type": "integer", + "const": 1 + }, + "defaultEnvironment": { + "type": [ + "string", + "null" + ] + }, + "environments": { + "type": "array", + "items": { + "type": "object", + "required": [ + "name", + "variables" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "variables": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "number", + "boolean", + "null" + ] + } + } + }, + "additionalProperties": false + } + }, + "globals": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "number", + "boolean", + "null" + ] + }, + "default": {} + } + }, + "additionalProperties": false +} diff --git a/contracts/workspace-json-schemas/mock-file.schema.json b/contracts/workspace-json-schemas/mock-file.schema.json new file mode 100644 index 0000000..ef194c1 --- /dev/null +++ b/contracts/workspace-json-schemas/mock-file.schema.json @@ -0,0 +1,156 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.local/http-client-app/contracts/workspace-json-schemas/mock-file.schema.json", + "title": "HTTP Client Mock Rule File", + "type": "object", + "required": [ + "schemaVersion", + "updatedAt", + "rules" + ], + "properties": { + "schemaVersion": { + "type": "integer", + "const": 1 + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "rules": { + "type": "array", + "items": { + "$ref": "#/$defs/mockRule" + } + } + }, + "additionalProperties": false, + "$defs": { + "mockRule": { + "type": "object", + "required": [ + "id", + "name", + "enabled", + "priority", + "match", + "response" + ], + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "name": { + "type": "string", + "minLength": 1 + }, + "enabled": { + "type": "boolean" + }, + "priority": { + "type": "integer" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": { + "type": "string" + }, + "match": { + "type": "object", + "required": [ + "method", + "path" + ], + "properties": { + "method": { + "type": "string", + "minLength": 1 + }, + "path": { + "type": "string", + "minLength": 1 + }, + "pathMode": { + "type": "string", + "enum": [ + "exact", + "prefix", + "regex" + ], + "default": "exact" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "query": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "response": { + "type": "object", + "required": [ + "statusCode" + ], + "properties": { + "statusCode": { + "type": "integer", + "minimum": 100, + "maximum": 599 + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "bodyType": { + "type": "string", + "enum": [ + "json", + "text", + "binary", + "file" + ] + }, + "body": { + "type": [ + "string", + "object", + "array", + "number", + "boolean", + "null" + ] + }, + "bodyFilePath": { + "type": "string" + }, + "delayMs": { + "type": "integer", + "minimum": 0 + }, + "templateEnabled": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/workspace-json-schemas/openapi-sources.schema.json b/contracts/workspace-json-schemas/openapi-sources.schema.json new file mode 100644 index 0000000..2a24161 --- /dev/null +++ b/contracts/workspace-json-schemas/openapi-sources.schema.json @@ -0,0 +1,113 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.local/http-client-app/contracts/workspace-json-schemas/openapi-sources.schema.json", + "title": "HTTP Client OpenAPI Sources", + "type": "object", + "required": [ + "schemaVersion", + "sources" + ], + "properties": { + "schemaVersion": { + "type": "integer", + "const": 1 + }, + "sources": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id", + "name", + "sourcePath", + "format" + ], + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "name": { + "type": "string", + "minLength": 1 + }, + "sourcePath": { + "type": "string", + "minLength": 1 + }, + "format": { + "type": "string", + "enum": [ + "json", + "yaml" + ] + }, + "openapiVersion": { + "type": "string" + }, + "title": { + "type": "string" + }, + "version": { + "type": "string" + }, + "contentHash": { + "type": "string" + }, + "lastSyncedAt": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "targets": { + "type": "object", + "properties": { + "requestFilePath": { + "type": "string" + }, + "mockFilePath": { + "type": "string" + } + }, + "additionalProperties": false + }, + "sync": { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "enum": [ + "skip-local-modified", + "overwrite-generated", + "preview-only" + ], + "default": "skip-local-modified" + }, + "generateRequests": { + "type": "boolean", + "default": true + }, + "generateMocks": { + "type": "boolean", + "default": false + }, + "enableSchemaValidation": { + "type": "boolean", + "default": false + }, + "baseUrlVariable": { + "type": "string", + "default": "baseUrl" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} diff --git a/contracts/workspace-json-schemas/settings.schema.json b/contracts/workspace-json-schemas/settings.schema.json new file mode 100644 index 0000000..f3c91ad --- /dev/null +++ b/contracts/workspace-json-schemas/settings.schema.json @@ -0,0 +1,72 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.local/http-client-app/contracts/workspace-json-schemas/settings.schema.json", + "title": "HTTP Client Workspace Settings", + "type": "object", + "required": [ + "schemaVersion" + ], + "properties": { + "schemaVersion": { + "type": "integer", + "const": 1 + }, + "defaultEnvironment": { + "type": [ + "string", + "null" + ] + }, + "history": { + "type": "object", + "properties": { + "retentionMaxCount": { + "type": "integer", + "minimum": 0, + "default": 1000 + } + }, + "additionalProperties": false + }, + "mock": { + "type": "object", + "properties": { + "proxyFallback": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "upstreamBaseUrl": { + "type": [ + "string", + "null" + ], + "format": "uri" + } + }, + "additionalProperties": false + }, + "hitLog": { + "type": "object", + "properties": { + "retentionMaxCount": { + "type": "integer", + "minimum": 0, + "default": 10000 + }, + "retentionDays": { + "type": "integer", + "minimum": 0, + "default": 7 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..8d773d3 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + HTTP Client App + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..bee1670 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,4324 @@ +{ + "name": "http-client-app-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "http-client-app-frontend", + "version": "0.1.0", + "dependencies": { + "vue": "^3.5.16" + }, + "devDependencies": { + "@types/node": "^22.15.29", + "@vitejs/plugin-vue": "^5.2.4", + "@vue/test-utils": "^2.4.6", + "jsdom": "^25.0.1", + "vite": "^6.3.5", + "vitest": "^2.1.9", + "vue-tsc": "^2.2.10" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@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.npmjs.org/@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.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", + "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz", + "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz", + "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz", + "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz", + "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz", + "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz", + "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz", + "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz", + "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz", + "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz", + "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz", + "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz", + "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz", + "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz", + "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz", + "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz", + "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", + "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz", + "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz", + "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz", + "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz", + "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz", + "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz", + "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz", + "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.20.tgz", + "integrity": "sha512-6tELRwSDYWW9EdZhbeZmYGZ1/7Djkt+Ah3/ScEYT9cDord7UJzasR/4D3VONg9tQI5CDp+/CZC1AXj2pCFOvpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz", + "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.15" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz", + "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.15.tgz", + "integrity": "sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.15", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.35.tgz", + "integrity": "sha512-BUmHaR1J+O+CKZ9uJucdVTEr1LHsdyvv7vG3eNRhK3CczEHeMd/LtsHAuD7PbrxvI2envCY2v7HI1vC1aBRzKw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/shared": "3.5.35", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.35.tgz", + "integrity": "sha512-k+bprkXxuqhVajgTx5mUHuir7TwQzUKOWR40ng1ncAqQRPnrLngGGgqVEEhOnTMlc8btHYVKmrP8s5Qyg0hvYA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.35", + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.35.tgz", + "integrity": "sha512-G5VPMcXTSywXBgtFOZOnHKBxKSrwXUcvY1iaF5/hRcy7t0J6CH/d8ha9F4nzi00Fax1eLV0QHM7v4mQu68jydw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/compiler-core": "3.5.35", + "@vue/compiler-dom": "3.5.35", + "@vue/compiler-ssr": "3.5.35", + "@vue/shared": "3.5.35", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.15", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.35.tgz", + "integrity": "sha512-rGhAeXgdM7/ffTJGXT69rCCdTmjDewnFuUZfBQQHTdcEBeWdT5HCGY60y2ytLJr9/Dsu7IntUi5z/w0h6Rjnzw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.35", + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/language-core": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.12.tgz", + "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.15", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^1.0.3", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.35.tgz", + "integrity": "sha512-tVc+SsHConvh/Lz64qq1pP3rYArBmK42xonovEcxY74SQtvctZodG/zhq54P5dr38cVuw25d27cPNRdlMidpGQ==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.35.tgz", + "integrity": "sha512-A/xFNX9loIcWDygeQuNCfKuh0CoYBzxhqEMNah5TSFg9Z53DrFYEN2qi5CU9necjM1OWYegYREUTHmXTmhfXtg==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.35", + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.35.tgz", + "integrity": "sha512-odrJ1C391dbGnyDRh8U+rnP7J2amIEzfmRk5vXy7xi3aZhEXofTvpi0T4HJb6jlNqQZTNPR5MPHSB3RHNkIORA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.35", + "@vue/runtime-core": "3.5.35", + "@vue/shared": "3.5.35", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.35.tgz", + "integrity": "sha512-NkebSOYdB97wi8OQcO3HqzZSlymJi/aWsN/7h74OSVhRTm6qGs3Jp3e0rCXynmWwSlKeRrnlIug+ilYoHBmQDA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.35", + "@vue/shared": "3.5.35" + }, + "peerDependencies": { + "vue": "3.5.35" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.35.tgz", + "integrity": "sha512-zSbjL7gRXwks2ZQLRGCajBtBXEOXW9Ddhn/HvSdrGkE2dqGnumzW8XtusRrxrE9LvqtiqDXQ+A60Hp6mvdYxfA==", + "license": "MIT" + }, + "node_modules/@vue/test-utils": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.11.tgz", + "integrity": "sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^3.0.0" + }, + "peerDependencies": { + "@vue/compiler-dom": "3.x", + "@vue/server-renderer": "3.x", + "vue": "3.x" + }, + "peerDependenciesMeta": { + "@vue/server-renderer": { + "optional": true + } + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/alien-signals": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz", + "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/editorconfig": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.7.tgz", + "integrity": "sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "^9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/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/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/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/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.8.tgz", + "integrity": "sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/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/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "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/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.24", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.24.tgz", + "integrity": "sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==", + "dev": true, + "license": "MIT" + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "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.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/rollup": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", + "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", + "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": { + "@rollup/rollup-android-arm-eabi": "4.61.1", + "@rollup/rollup-android-arm64": "4.61.1", + "@rollup/rollup-darwin-arm64": "4.61.1", + "@rollup/rollup-darwin-x64": "4.61.1", + "@rollup/rollup-freebsd-arm64": "4.61.1", + "@rollup/rollup-freebsd-x64": "4.61.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", + "@rollup/rollup-linux-arm-musleabihf": "4.61.1", + "@rollup/rollup-linux-arm64-gnu": "4.61.1", + "@rollup/rollup-linux-arm64-musl": "4.61.1", + "@rollup/rollup-linux-loong64-gnu": "4.61.1", + "@rollup/rollup-linux-loong64-musl": "4.61.1", + "@rollup/rollup-linux-ppc64-gnu": "4.61.1", + "@rollup/rollup-linux-ppc64-musl": "4.61.1", + "@rollup/rollup-linux-riscv64-gnu": "4.61.1", + "@rollup/rollup-linux-riscv64-musl": "4.61.1", + "@rollup/rollup-linux-s390x-gnu": "4.61.1", + "@rollup/rollup-linux-x64-gnu": "4.61.1", + "@rollup/rollup-linux-x64-musl": "4.61.1", + "@rollup/rollup-openbsd-x64": "4.61.1", + "@rollup/rollup-openharmony-arm64": "4.61.1", + "@rollup/rollup-win32-arm64-msvc": "4.61.1", + "@rollup/rollup-win32-ia32-msvc": "4.61.1", + "@rollup/rollup-win32-x64-gnu": "4.61.1", + "@rollup/rollup-win32-x64-msvc": "4.61.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", + "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/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/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/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/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "devOptional": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vite-node/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitest/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/vitest/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.35.tgz", + "integrity": "sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.35", + "@vue/compiler-sfc": "3.5.35", + "@vue/runtime-dom": "3.5.35", + "@vue/server-renderer": "3.5.35", + "@vue/shared": "3.5.35" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-component-type-helpers": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-3.3.3.tgz", + "integrity": "sha512-x4nsFpy5Pe8fqPzp/5vkTPeTTDBpAx4WVtV47Ejt0+2FQrq4pRRsJs7JmYRqMFzTu/LW+pCWEjQ3YVCkPV7f9g==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-tsc": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.12.tgz", + "integrity": "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.15", + "@vue/language-core": "2.2.12" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..f49fdf1 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,23 @@ +{ + "name": "http-client-app-frontend", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc --noEmit && vite build", + "test": "vitest run" + }, + "dependencies": { + "vue": "^3.5.16" + }, + "devDependencies": { + "@types/node": "^22.15.29", + "@vitejs/plugin-vue": "^5.2.4", + "@vue/test-utils": "^2.4.6", + "jsdom": "^25.0.1", + "vite": "^6.3.5", + "vitest": "^2.1.9", + "vue-tsc": "^2.2.10" + } +} diff --git a/frontend/src/App.test.ts b/frontend/src/App.test.ts new file mode 100644 index 0000000..31cbd82 --- /dev/null +++ b/frontend/src/App.test.ts @@ -0,0 +1,12 @@ +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' + +import App from './App.vue' + +describe('App', () => { + it('renders the application title', () => { + const wrapper = mount(App) + + expect(wrapper.get('h1').text()).toBe('HTTP Client App') + }) +}) diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 0000000..0a58122 --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,28 @@ + diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..b9ca7f1 --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,6 @@ +import { createApp } from 'vue' + +import App from './App.vue' +import './styles.css' + +createApp(App).mount('#app') diff --git a/frontend/src/styles.css b/frontend/src/styles.css new file mode 100644 index 0000000..47d4294 --- /dev/null +++ b/frontend/src/styles.css @@ -0,0 +1,144 @@ +:root { + color: #1f2a37; + background: #f3efe6; + font-family: "Avenir Next", "Segoe UI", sans-serif; + font-synthesis: none; + text-rendering: optimizeLegibility; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-width: 320px; + min-height: 100vh; + background: + radial-gradient(circle at top left, rgba(235, 176, 104, 0.32), transparent 34rem), + linear-gradient(135deg, #f8f4eb 0%, #e8dcc9 100%); +} + +button { + font: inherit; +} + +.app-shell { + width: min(960px, calc(100% - 32px)); + margin: 0 auto; + padding: 56px 0; +} + +.hero { + padding: 48px; + border: 1px solid rgba(71, 51, 34, 0.14); + border-radius: 28px; + background: rgba(255, 252, 246, 0.76); + box-shadow: 0 24px 80px rgba(65, 47, 29, 0.14); +} + +.eyebrow, +.section-label { + margin: 0 0 12px; + color: #9b4b24; + font-size: 0.78rem; + font-weight: 700; + letter-spacing: 0.14em; + text-transform: uppercase; +} + +h1, +h2, +p { + margin-top: 0; +} + +h1 { + margin-bottom: 16px; + color: #263326; + font-size: clamp(2.5rem, 8vw, 5.8rem); + line-height: 0.94; +} + +.subtitle { + max-width: 660px; + margin-bottom: 0; + color: #5f6a59; + font-size: 1.1rem; + line-height: 1.7; +} + +.status-card, +.action-card { + border: 1px solid rgba(71, 51, 34, 0.12); + border-radius: 22px; + background: rgba(255, 252, 246, 0.7); +} + +.status-card { + display: flex; + align-items: center; + justify-content: space-between; + gap: 24px; + margin: 24px 0; + padding: 24px; +} + +.status-card h2 { + margin-bottom: 0; + font-size: 1.25rem; +} + +.status-pill { + padding: 8px 14px; + border-radius: 999px; + color: #6d3b19; + background: #f3d9b8; + font-weight: 700; +} + +.action-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; +} + +.action-card { + min-height: 132px; + padding: 24px; + color: #263326; + text-align: left; + cursor: not-allowed; +} + +.action-card span { + display: block; + margin-bottom: 12px; + font-size: 1.25rem; + font-weight: 800; +} + +.action-card small { + color: #66705f; + font-size: 0.95rem; +} + +@media (max-width: 640px) { + .app-shell { + padding: 24px 0; + } + + .hero { + padding: 28px; + } + + .status-card, + .action-grid { + grid-template-columns: 1fr; + } + + .status-card { + align-items: flex-start; + flex-direction: column; + } +} diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..9bdc5b4 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "jsx": "preserve", + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"] + }, + "include": ["src/**/*.ts", "src/**/*.vue", "vite.config.ts"], + "exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"], + "references": [] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..d146337 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,13 @@ +import { fileURLToPath, URL } from 'node:url' + +import vue from '@vitejs/plugin-vue' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [vue()], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, +}) diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts new file mode 100644 index 0000000..f32a476 --- /dev/null +++ b/frontend/vitest.config.ts @@ -0,0 +1,17 @@ +import { fileURLToPath, URL } from 'node:url' + +import vue from '@vitejs/plugin-vue' +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + plugins: [vue()], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + test: { + environment: 'jsdom', + globals: true, + }, +}) diff --git a/http-client-app-plan.md b/http-client-app-plan.md new file mode 100644 index 0000000..2b61fdb --- /dev/null +++ b/http-client-app-plan.md @@ -0,0 +1,2709 @@ +# HTTP Client 应用程序实施计划 + +## 项目概述 + +创建一个类似 IntelliJ IDEA HTTP Client 的 BS 架构应用程序,支持 .http 文件解析和执行。 + +## 核心兼容范围 + +项目目标为:**完整兼容 IntelliJ IDEA HTTP Client 的核心使用方式,产品目标版本以“全部支持”为目标,不做语法子集裁剪。** + +兼容范围包括但不限于: + +* [ ] 支持标准 `.http` / `.rest` 文件解析 + +* [ ] 支持单文件内多个请求块及 `###` 分隔语法 + +* [ ] 支持请求命名、请求注释、请求描述信息展示 + +* [ ] 支持全部常见 HTTP 方法及自定义方法 + +* [ ] 支持 URL、Header、Query、Body 中的变量引用与替换 + +* [ ] 支持环境变量、全局变量、文件内变量及变量覆盖规则 + +* [ ] 支持 JSON、XML、Text、Form URL Encoded、Multipart Form Data、二进制文件等请求体类型 + +* [ ] 支持引用外部文件作为请求体或表单字段内容 + +* [ ] 支持 Cookie、重定向、代理、超时、SSL/TLS、客户端证书等常见请求配置 + +* [ ] 支持响应结果查看、Header/Cookie 展示、格式化预览、响应时间与大小统计 + +* [ ] 支持请求前置脚本、请求后置脚本、断言、链式请求、响应数据提取与变量回填 + +* [ ] 支持从当前光标位置识别并执行当前请求,行为与 IntelliJ IDEA HTTP Client 保持一致 + +* [ ] 支持常见语法错误提示,并尽可能与 IntelliJ IDEA HTTP Client 的解析行为保持一致 + +* [ ] 支持导出 curl、导入常见 HTTP 请求定义,尽量保持与原始请求语义一致 + +* [ ] 支持导入 OpenAPI 3.0 / 3.1 契约文件,并生成 `.http` 请求模板、Mock 规则骨架和响应 Schema 校验能力 + +兼容性要求: + +* [ ] 功能设计优先遵循 IntelliJ IDEA HTTP Client 的行为模型,新增自定义语法前必须保证不破坏原有兼容性 + +* [ ] 若遇到 IntelliJ IDEA HTTP Client 的边界语法或特殊行为,应以兼容为第一原则,不以“简化实现”为理由降级支持 + +* [ ] 所有解析器、执行器、前端交互设计均需以兼容性测试用例驱动,建立覆盖常见语法与边界场景的回归测试集 + +* [ ] 发布前需使用真实 `.http` 示例文件进行兼容性验收,确保常用工作流可直接迁移 + +### 兼容矩阵要求 + +* [ ] 文档需维护一份可持续更新的 IntelliJ IDEA HTTP Client 兼容矩阵,按“语法项 / 是否支持 / 差异说明 / 测试用例”逐项列出 + +* [ ] 兼容矩阵至少覆盖:请求分隔、请求命名、注释、变量声明与引用、环境文件、脚本块、断言、文件引用、multipart、重定向、Cookie、SSE、WebSocket、curl 导出、Postman 导入、OpenAPI 导入 + +* [ ] 对于暂未完全一致的行为,必须在兼容矩阵中记录具体差异、影响范围和回归用例,不能只写“部分支持” + +### 解析器 AST 约束 + +* [ ] `.http` / `.rest` 解析器需输出稳定 AST,不直接将文本解析结果绑定到执行器内部结构 + +* [ ] AST 至少包含:文件、请求块、注释、请求名称、方法、URL、Header、Body、变量引用、脚本块、断言块、源代码位置 + +* [ ] AST 节点必须保留 `range` 信息,至少包含起始行列、结束行列、起始 offset、结束 offset + +* [ ] AST 需支持错误恢复;单个请求块解析失败时,不应阻断同文件其他请求块的识别 + +* [ ] 前端当前请求定位、语法高亮、错误标记、变量预览应尽量复用后端 AST 或等价结构,避免前后端各自实现不一致的解析逻辑 + +* [ ] 解析器输出需可序列化为 JSON,用于 API 调试、测试快照与前端辅助展示 + +### 需求追踪与决策记录 + +* [ ] 需求、任务、测试和验收需建立可追踪关系,避免实现阶段出现“做了功能但找不到验收依据”的情况 + +* [ ] 核心需求建议使用稳定编号,例如 `REQ-CORE-*`、`REQ-FILE-*`、`REQ-EXEC-*`、`REQ-MOCK-*`、`REQ-OPENAPI-*`、`REQ-LOAD-*`、`REQ-UI-*` + +* [ ] MVP 任务编号需能映射到至少一个需求编号和至少一个测试或验收项 + +* [ ] 兼容矩阵中的每一项都应具备对应 fixture 或测试用例路径,不能只停留在文档描述 + +* [ ] 产品目标版本增强项如果暂未进入 MVP,需在矩阵或实施阶段中明确归属,避免被误判为 MVP 缺失 + +* [ ] 关键架构决策需记录为 ADR,默认路径为 `docs/decisions/ADR-{number}-{topic}.md` + +* [ ] ADR 至少记录:状态、背景、决策、备选方案、影响范围、后续迁移成本 + +* [ ] 已确定的关键决策至少包括:纯 Go 本地服务 + 浏览器前端、REST 控制面 + WebSocket 事件流、SQLite 仅做索引/缓存/命中日志、工作区文件作为事实源 + +* [ ] 若后续需求与已记录 ADR 冲突,应先更新 ADR 和影响说明,再修改实现计划 + +## 技术栈 + +* **后端**: Go (Gin 框架) + +* **前端**: Vue 3 + TypeScript + Vite + +* **本地集成**: 原生 Go + 系统托盘库 (systray) + +* **HTTP 解析**: Go 自定义 .http 文件解析器 + +* **架构**: 本地 Go 服务 + 浏览器前端,后端提供 Web 服务与静态资源托管 + +## 项目结构 + +``` +api-client/ +├── backend/ +│ ├── cmd/api-client/ # 本地服务入口 +│ ├── internal/api/ # REST API 与 WebSocket handler +│ ├── internal/app/ # 应用启动、依赖装配、生命周期管理 +│ ├── internal/parser/ # .http / .rest 解析器与 AST +│ ├── internal/workspace/ # 工作区模型、文件扫描、路径解析 +│ ├── internal/filestore/ # 原子写入、contentHash、外部变更检测 +│ ├── internal/execution/ # Execution 状态机、调度、结果固化 +│ ├── internal/eventbus/ # 实时事件、重放、背压与订阅管理 +│ ├── internal/runtime/ # JavaScript 脚本运行时与内置 API +│ ├── internal/httpclient/ # HTTP 请求发送、TLS、代理、Cookie +│ ├── internal/mock/ # Mock Server、规则匹配、命中日志 +│ ├── internal/openapi/ # OpenAPI 导入、同步、diff、Schema 校验 +│ ├── internal/cacheindex/ # SQLite 索引、缓存索引、迁移 +│ ├── internal/tray/ # 系统托盘与单实例协作 +│ └── internal/model/ # 跨模块共享模型与错误码 +├── frontend/ +│ ├── src/app/ # 应用入口、路由、全局状态装配 +│ ├── src/shared/ # 通用组件、工具、API client、类型 +│ ├── src/features/editor/ # 请求编辑器、当前请求定位、变量预览 +│ ├── src/features/response/ # 响应查看、流式渲染、Schema 校验结果 +│ ├── src/features/history/ # 历史列表与详情 +│ ├── src/features/environment/# 环境管理 +│ ├── src/features/mock/ # Mock 管理页 +│ ├── src/features/openapi/ # OpenAPI 管理页 +│ ├── src/features/loadtest/ # 压测配置与报告 +│ └── public/ +├── contracts/ # 本应用 REST API OpenAPI 契约与生成配置 +├── fixtures/ # 测试夹具与快照 +├── scripts/ # 构建、生成、校验脚本 +└── build/ # 打包输出与安装资源 +``` + +### 后端模块边界 + +* [ ] `api` 仅负责 HTTP handler、参数绑定、响应 envelope 与错误映射,不直接读写工作区文件或 SQLite + +* [ ] `parser` 仅负责 `.http` / `.rest` 文本到 AST 的解析、错误恢复和源位置映射,不负责变量替换和请求发送 + +* [ ] `workspace` 负责工作区根目录、相对路径、符号链接解析、文件扫描和最近文件来源抽象 + +* [ ] `filestore` 负责文件读取、原子写入、`contentHash`、外部变更检测、文件版本,不理解 Mock、OpenAPI 或环境变量业务语义 + +* [ ] `execution` 负责 Execution 生命周期、队列调度、取消、超时、父子任务、结果固化,不直接实现具体 HTTP transport + +* [ ] `eventbus` 负责事件发布、订阅、序号、重放、背压和 WebSocket fan-out,不包含业务执行逻辑 + +* [ ] `runtime` 负责 JavaScript 运行时、脚本上下文、内置 API 和脚本超时,不直接访问浏览器环境 + +* [ ] `httpclient` 负责最终 HTTP 请求发送、连接复用、TLS、代理、Cookie、重定向和响应流读取 + +* [ ] `mock` 负责 Mock Server、规则加载、匹配、模板渲染、代理回退和命中日志;规则事实源仍通过 `filestore` 写入 `.mock.json` + +* [ ] `openapi` 负责目标接口 OpenAPI 文件解析、导入预览、同步 diff、请求模板生成、Mock 骨架生成和 Schema 校验 + +* [ ] `cacheindex` 负责 SQLite schema、索引重建、缓存记录、迁移和保留策略,不保存工作区事实源数据 + +* [ ] `tray` 仅负责系统托盘、单实例唤起、打开浏览器和退出服务,不持有业务状态 + +### 前端模块边界 + +* [ ] `shared/api` 负责统一 REST client、WebSocket client、请求 envelope、错误码类型和重试策略 + +* [ ] `features/editor` 负责文件编辑、语法高亮、当前请求定位、请求块操作和变量预览 + +* [ ] `features/response` 负责普通响应、流式响应、SSE / WebSocket 会话视图、Schema 校验结果和大响应分段渲染 + +* [ ] `features/history` 负责历史列表、历史详情、重新执行、打开源文件和定位请求块 + +* [ ] `features/environment` 负责环境变量文件的展示、编辑、导入导出和默认环境选择 + +* [ ] `features/mock` 负责 Mock Server 状态、规则编辑、优先级排序、命中日志和规则文件保存重载 + +* [ ] `features/openapi` 负责 OpenAPI 源列表、导入向导、同步预览、diff 视图和生成目标选择 + +* [ ] `features/loadtest` 负责压测配置、实时指标、报告展示和采样明细,不直接调普通执行接口模拟压测 + +* [ ] 前端不得重复实现 `.http` 完整解析器;当前请求定位、错误标记和变量预览应优先消费后端 AST 或后端等价解析结果 + +## 文件管理约束 + +* [ ] `.http` / `.rest` 文件以本地工作区文件形式管理,不存入数据库 + +* [ ] 应用负责本地文件的打开、保存、另存为、最近文件记录与目录浏览 + +* [ ] 应用负责检测文件外部变更,并提示用户重新加载或保留当前未保存内容 + +* [ ] 应用不提供应用内三方合并或冲突解决界面 + +* [ ] 文件冲突统一通过 `Git` 同步、合并与解决,应用仅提供必要的变更提示 + +* [ ] 请求历史索引可存入 SQLite;环境变量与项目配置以工作区 JSON 文件为准;原始 `.http` 文件内容仍以本地文件为准 + +* [ ] 环境变量、项目配置、Mock 规则等可版本管理数据默认保存为工作区文件,文件冲突统一通过 Git 处理 + +* [ ] OpenAPI 契约文件默认以本地工作区文件形式管理,应用可读取、导入、同步和生成派生产物,但不将原始 OpenAPI 内容写入 SQLite + +### 文件写入一致性 + +* [ ] 所有工作区事实源文件保存均需采用原子写入:先写入同目录临时文件,校验成功后再执行原子替换 + +* [ ] 文件保存失败时不得破坏原文件;失败后需保留可理解错误,并尽量清理临时文件 + +* [ ] 文件保存请求需携带 `baseVersion` 或 `contentHash`,用于检测保存期间的外部变更 + +* [ ] 若保存时发现目标文件已被外部修改,应用不得静默覆盖,需提示用户重新加载、另存为或交给 Git 合并 + +* [ ] `.http-client/environments.json`、`.http-client/settings.json`、`.http-client/mocks/**/*.mock.json` 均需遵循同一套原子写入和冲突检测规则 + +* [ ] `.http-client/openapi/sources.json` 需遵循同一套原子写入和冲突检测规则,用于保存 OpenAPI 源文件映射与同步配置 + +### 文本编码与格式约束 + +* [ ] 工作区事实源文本文件默认使用 `UTF-8` 编码保存 + +* [ ] 读取 `.http` / `.rest` 文件时应兼容带 BOM 的 UTF-8 文件,保存时默认不主动添加 BOM + +* [ ] 文件保存时应尽量保持原文件换行风格;新建文件默认使用 `LF` + +* [ ] `.http-client/*.json` 文件保存时需使用稳定缩进,默认 `2` 个空格,并保持字段顺序稳定,减少 Git diff 噪音 + +* [ ] JSON 文件不得写入注释;若需要备注说明,应使用显式字段,例如 `description` + +* [ ] 生成的 `.http` 文件需包含来源注释和更新时间,但不得写入机器本地绝对路径,避免多人协作时产生无意义 diff + +* [ ] 二进制响应体、Mock 静态资源和缓存文件不得被误判为文本文件进行格式化 + +### 工作区配置 Schema + +* [ ] `.http-client/environments.json`、`.http-client/settings.json`、`.http-client/mocks/**/*.mock.json`、`.http-client/openapi/sources.json` 均需具备明确 `schemaVersion` + +* [ ] 工作区 JSON 文件需具备对应 JSON Schema 草案,默认保存到 `contracts/workspace-json-schemas/` + +* [ ] 应用保存工作区 JSON 前必须执行基础 schema 校验,失败时不得覆盖原文件 + +* [ ] 应用读取高版本 `schemaVersion` 文件时,默认只读或拒绝写入,并提示当前应用版本不支持该 schema + +* [ ] 应用读取低版本 `schemaVersion` 文件时,可执行自动迁移;迁移前必须备份原文件 + +* [ ] JSON Schema 需覆盖必填字段、枚举值、默认值、路径字段格式和数组元素结构 + +* [ ] 测试夹具中的工作区 JSON 文件需通过 schema 校验,防止示例与真实格式漂移 + +### 工作区模型 + +* [ ] 应用以“工作区目录”为主要操作单位,同时支持直接打开单个 `.http` / `.rest` 文件 + +* [ ] 当用户直接打开单个文件时,应自动以该文件所在目录作为临时工作区根目录 + +* [ ] 相对路径解析默认以当前请求文件所在目录为第一基准,以当前工作区根目录为第二基准 + +* [ ] 文件引用、脚本引用、外部请求体引用均需明确遵循统一的相对路径解析规则 + +* [ ] MVP 阶段支持单工作区模型;多工作区并行打开不纳入当前范围 + +* [ ] 是否允许访问工作区外文件需提供明确配置项,默认允许用户显式选择工作区外文件,但界面需给出路径提示 + +* [ ] 若遇到符号链接、快捷方式或软链接路径,应统一解析到真实路径后再执行文件访问与变更检测 + +### 文件监听与增量索引 + +* [ ] 后端需提供工作区文件监听能力,监听范围至少覆盖已打开的 `.http` / `.rest` 文件、`.http-client/environments.json`、`.http-client/settings.json`、`.http-client/mocks/**/*.mock.json`、`.http-client/openapi/sources.json` 和已登记 OpenAPI 源文件 + +* [ ] 文件监听事件需做去抖处理,默认去抖窗口为 `300ms`,避免保存过程中的多次底层事件触发重复解析和重复提示 + +* [ ] 文件监听不应直接覆盖编辑器内容;若当前文件存在未保存修改,仅提示外部变更并提供重新加载入口 + +* [ ] 外部文件变更后应优先执行增量索引:单个 `.http` 文件变更仅重建该文件 AST 与请求索引,单个 `.mock.json` 变更仅重建对应 Mock 规则索引,单个 OpenAPI 源变更仅重建对应 source 与 operation 索引 + +* [ ] 若增量索引失败,需记录错误摘要并允许用户触发全量 `POST /api/indexes/rebuild` + +* [ ] Git 分支切换、批量拉取、批量替换等可能产生大量文件变更时,应用应合并通知为一次“工作区发生大量变更”提示,并建议重建索引 + +* [ ] 文件监听可按平台差异采用不同底层实现,但对上层统一输出:`created`、`updated`、`deleted`、`renamed`、`unknown` 五类事件 + +## 本地服务与执行模型 + +### 本地服务约束 + +* [ ] 应用以后端本地服务形式运行,前端通过浏览器访问本地页面 + +* [ ] 本地服务负责提供 API、事件通道、静态资源托管、文件访问与执行调度能力 + +* [ ] 本地服务默认单实例运行,重复启动时应复用已有实例并直接打开应用页面 + +* [ ] 本地服务启动后默认自动打开浏览器页面,用户关闭浏览器后服务可继续驻留后台 + +* [ ] 首次启动时若默认端口被占用,应自动选择可用端口并更新应用页面打开地址 + +* [ ] 若检测到已有本应用实例正在运行,则始终复用该实例,不再新开服务进程或重新选择端口 + +### 本地服务启动协议 + +* [ ] 默认监听地址为 `127.0.0.1` + +* [ ] 默认端口为 `32180` + +* [ ] 若默认端口不可用,端口探测范围为 `32181-32200` + +* [ ] 浏览器打开地址格式为 `http://127.0.0.1:{port}/` + +* [ ] 单实例发现优先使用本地锁文件与端口探测结合的方式 + +* [ ] 服务启动成功后需写入运行时元信息文件,至少包含端口、进程 ID、启动时间、数据目录 + +### 执行模型总览 + +* [ ] 所有请求执行统一抽象为 `Execution` + +* [ ] 前端发起执行时,后端先创建执行任务并返回 `executionId`,不直接以同步接口返回完整响应 + +* [ ] REST API 负责执行创建、执行查询、执行取消、历史查询、文件管理、环境管理等控制面能力 + +* [ ] WebSocket 负责执行过程中的实时事件推送,包括状态变化、流式响应、脚本日志、断言结果、最终完成事件 + +* [ ] 单请求、批量请求、链式请求、SSE、WebSocket 会话、压测执行均复用同一套执行模型 + +### Execution 类型 + +* [ ] `http`: 单次 HTTP 请求执行 + +* [ ] `batch`: 批量请求执行,包含多个子执行任务 + +* [ ] `chain`: 链式请求执行,后续步骤可依赖前序步骤变量提取结果 + +* [ ] `sse`: Server-Sent Events 长连接执行 + +* [ ] `websocket`: WebSocket 会话执行 + +* [ ] `load-test`: 对目标接口发起轻量压测执行 + +* [ ] `mock-server`: 本地 Mock Server 运行任务 + +### Execution 状态机 + +* [ ] `pending`: 任务已创建,等待调度 + +* [ ] `running`: 任务已进入解析、变量替换、脚本执行、请求发送等阶段 + +* [ ] `streaming`: 已开始持续接收响应内容或实时消息 + +* [ ] `completed`: 执行成功结束,最终结果已固化 + +* [ ] `failed`: 执行失败,包括解析失败、脚本失败、请求失败、断言失败等 + +* [ ] `cancelled`: 执行被用户主动取消或被父任务取消 + +* [ ] 状态流转至少支持:`pending -> running -> completed/failed/cancelled`,以及 `running -> streaming -> completed/failed/cancelled` + +### REST API 设计 + +* [ ] `POST /api/executions`: 创建执行任务,返回 `executionId` + +* [ ] `GET /api/executions/:id`: 查询执行状态、当前阶段、开始时间、结束时间、错误摘要 + +* [ ] `POST /api/executions/:id/cancel`: 取消执行任务 + +* [ ] `GET /api/executions/:id/result`: 获取最终结果快照,用于页面刷新恢复与历史回看 + +* [ ] `GET /api/history`: 分页查询执行历史 + +* [ ] `GET /api/history/:id`: 查询历史执行详情 + +* [ ] `GET /api/events/ws`: 建立全局 WebSocket 事件通道 + +* [ ] `GET /api/files/*`: 本地文件与工作区读取接口 + +* [ ] `POST /api/files/save`: 文件保存接口 + +* [ ] `GET /api/environments`: 环境列表与变量读取接口 + +* [ ] `POST /api/environments`: 环境创建或更新接口 + +* [ ] `GET /api/mocks`: 查询当前工作区 Mock 规则列表 + +* [ ] `POST /api/mocks`: 创建或更新 Mock 规则,并写回对应 Mock 规则文件 + +* [ ] `DELETE /api/mocks/:id`: 删除 Mock 规则,并写回对应 Mock 规则文件 + +* [ ] `GET /api/mock-files`: 查询当前工作区 Mock 规则文件列表 + +* [ ] `POST /api/mock-files/save`: 保存当前工作区 Mock 规则文件 + +* [ ] `POST /api/mock-files/reload`: 从工作区 Mock 规则文件重新加载并重建索引 + +* [ ] `POST /api/mock-files/preview`: 预览 Mock 规则文件变更,返回新增、更新、冲突、跳过数量 + +* [ ] `POST /api/mock-server/start`: 启动当前工作区 Mock Server + +* [ ] `POST /api/mock-server/stop`: 停止当前工作区 Mock Server + +* [ ] `GET /api/mock-server/status`: 查询 Mock Server 状态 + +* [ ] `GET /api/mock-server/hit-logs`: 分页查询 Mock Server 命中日志 + +* [ ] `DELETE /api/mock-server/hit-logs`: 清空当前工作区 Mock Server 命中日志 + +* [ ] `GET /api/openapi/sources`: 查询当前工作区已登记的 OpenAPI 契约源列表 + +* [ ] `POST /api/openapi/import`: 导入 OpenAPI 文件并生成 `.http` 请求模板、Mock 规则骨架、Schema 校验映射的预览结果 + +* [ ] `POST /api/openapi/sync/preview`: 基于已登记 OpenAPI 契约源预览同步变更,返回新增、更新、删除、冲突和不可映射项 + +* [ ] `POST /api/openapi/sync/apply`: 应用用户确认后的 OpenAPI 同步变更,并写入对应工作区文件 + +* [ ] `POST /api/openapi/diff`: 对比两个 OpenAPI 契约文件,返回接口、参数、请求体、响应 Schema 的变更摘要 + +* [ ] `POST /api/openapi/validate-response`: 使用 OpenAPI 响应 Schema 校验某次执行结果或用户粘贴的响应样本 + +* [ ] `POST /api/indexes/rebuild`: 重建当前工作区 SQLite 索引与缓存索引 + +### API JSON 合同 + +* [ ] 所有 REST API 响应统一包含:`success`、`data`、`error`、`requestId`、`timestamp` + +* [ ] 成功响应中 `success=true`,`error=null`;失败响应中 `success=false`,`data=null` + +* [ ] 错误对象至少包含:`code`、`message`、`details`、`location` + +* [ ] 分页查询接口统一使用:`page`、`pageSize`、`total`、`items` + +* [ ] `POST /api/executions` 响应至少包含:`executionId`、`status`、`type` + +* [ ] `GET /api/executions/:id` 响应至少包含:`executionId`、`status`、`type`、`stage`、`startedAt`、`finishedAt`、`error` + +* [ ] `GET /api/executions/:id/result` 响应至少包含完整 `ExecutionResult` + +* [ ] `GET /api/history` 响应项至少包含:`executionId`、`type`、`status`、`requestName`、`filePath`、`environment`、`durationMs`、`startedAt` + +* [ ] WebSocket 事件统一采用 envelope 结构:`type`、`executionId`、`seq`、`timestamp`、`payload` + +* [ ] REST API 示例、OpenAPI 契约与测试快照必须统一使用响应 envelope,不允许同一接口同时存在裸对象响应与 envelope 响应 + +* [ ] 写入类接口需支持幂等请求标识,推荐使用 `Idempotency-Key` Header 或请求体 `operationId` + +* [ ] `POST /api/executions/:id/cancel`、`POST /api/mock-files/reload`、`POST /api/indexes/rebuild` 必须具备幂等语义,重复调用不得产生不一致状态 + +* [ ] 文件保存类接口需在请求体中携带 `baseVersion` 或 `contentHash`,并在冲突时返回 `FILE_CONFLICT` + +* [ ] 规则级写入接口(如 `POST /api/mocks`、`DELETE /api/mocks/:id`)需通过显式 `filePath` 或 `mock_rule_index` 定位目标规则文件,并复用同一套 `contentHash` 冲突检测 + +### API 类型与契约生成 + +* [ ] 后端需维护 API 契约源,推荐使用 OpenAPI 3.1 或等价 JSON Schema 描述 REST 接口 + +* [ ] 前端 TypeScript API 类型应由 API 契约生成,避免手写重复类型 + +* [ ] WebSocket 事件 envelope 与 payload 类型需纳入同一契约体系,至少提供 TypeScript 类型定义 + +* [ ] CI 或测试流程需校验 API 契约与示例 JSON 的基本一致性 + +* [ ] 破坏性 API 变更需在文档中记录迁移说明 + +### 内部 API 契约落地 + +* [ ] 本应用自身 REST API 契约默认保存为 `contracts/app-api.openapi.yaml` + +* [ ] WebSocket 事件 payload 类型默认保存为 `contracts/events.schema.json` 或在 `contracts/app-api.openapi.yaml` 的 `components.schemas` 中统一声明 + +* [ ] 前端 API 类型、请求参数类型、响应 envelope 类型必须从契约生成,生成目录默认为 `frontend/src/shared/api/generated/` + +* [ ] 后端 handler 层可使用契约校验工具或快照测试校验响应结构,避免文档、示例和实现漂移 + +* [ ] 生成代码不得手工编辑;如需改类型,应修改契约源并重新生成 + +* [ ] CI 至少执行契约格式校验、示例 JSON 快照校验和前端类型生成校验 + +* [ ] 契约变更需在 PR 或变更说明中标记为兼容变更或破坏性变更,并说明前端影响范围 + +### 目标接口 OpenAPI 文件支持 + +* [ ] 需明确区分两类 OpenAPI:本应用自身 REST API 的 OpenAPI 契约用于前后端类型生成;用户导入的目标接口 OpenAPI 文件用于生成请求、Mock、Schema 校验和变更对比 + +* [ ] 用户导入的 OpenAPI 文件定位为“接口契约来源”,不替代 `.http` / `.rest` 文件;`.http` / `.rest` 仍是实际调试、执行、脚本、断言和链式场景的主要载体 + +* [ ] 支持导入本地 `openapi.yaml`、`openapi.yml`、`openapi.json` 文件,产品目标版本支持 OpenAPI 3.0 与 3.1 + +* [ ] OpenAPI 导入需解析 `paths`、`operationId`、`tags`、`parameters`、`requestBody`、`responses`、`security` 的基础结构;`security` 字段暂不展开执行能力,仅保留为导入报告中的不可执行配置提示 + +* [ ] 导入后默认按 `tags` 或路径前缀生成请求分组,并生成可读的 `.http` 请求模板 + +* [ ] `.http` 请求模板需保留 OpenAPI 来源信息,至少包含 `sourceId`、`operationId`、`method`、`path`、`schemaRef`,用于后续同步定位 + +* [ ] OpenAPI 变量映射默认将 `servers[0].url` 映射为 `{{baseUrl}}`,并允许用户在导入预览中修改目标环境变量名 + +* [ ] OpenAPI `parameters` 需映射为 Query、Path、Header、Cookie 的请求模板占位符;必填参数需在模板中明显标记 + +* [ ] OpenAPI `requestBody` 需根据 `content-type` 生成 JSON、Form、Multipart 或 Raw Body 示例;多个示例存在时优先使用 `examples`,其次使用 `example`,最后根据 Schema 生成基础样例 + +* [ ] OpenAPI `responses` 需用于生成响应 Schema 校验映射,并可用于生成基础 Mock 响应骨架 + +* [ ] OpenAPI 导入不得静默覆盖已有 `.http`、Mock 规则或配置文件;所有写入前必须展示预览与冲突列表 + +* [ ] OpenAPI 同步需支持只新增、更新已有生成块、跳过本地已修改块三种策略;默认策略为跳过本地已修改块并提示用户确认 + +* [ ] OpenAPI 同步冲突统一通过文件 `contentHash` 和生成块来源标记识别;应用不提供复杂合并器,最终文件冲突仍交给 Git 处理 + +* [ ] OpenAPI diff 需识别新增接口、删除接口、方法变化、路径变化、参数变化、请求体变化、响应状态码变化、响应 Schema 变化,并标记潜在破坏性变更 + +* [ ] OpenAPI 生成的 Mock 规则默认保存到 `.http-client/mocks/openapi/{sourceName}.mock.json` + +* [ ] OpenAPI 响应 Schema 校验默认作为用户显式启用的断言能力,不应在普通执行中强制开启 + +* [ ] Schema 校验失败应生成结构化断言结果,错误码使用 `SCHEMA_VALIDATION_ERROR`,并展示字段路径、期望类型、实际值摘要 + +* [ ] OpenAPI 导入、同步、diff、Schema 校验均需生成导入或校验报告,报告可被前端展示,也可作为测试快照保存 + +### 创建执行请求模型 + +* [ ] 创建执行请求至少包含:执行类型、文件路径、请求定位信息、环境名、是否流式、是否保存历史 + +* [ ] 请求定位信息 `requestSelector` 至少支持三种模式: + +* [ ] `cursor`: 根据光标行列自动识别当前请求 + +* [ ] `requestId`: 根据解析后的请求唯一标识执行 + +* [ ] `range`: 根据选区或请求块范围执行,供批量执行或选中执行扩展 + +* [ ] `requestId` 必须在同一文件内容未发生结构性变化时保持稳定,推荐基于文件路径、请求块起始位置、请求名称生成 + +* [ ] 当文件内容变化导致原 `requestId` 失效时,系统应优先尝试通过请求块位置重新定位,并在失败时返回可理解错误 + +* [ ] 当 `cursor`、`requestId`、`range` 同时出现时,优先级固定为:`requestId` > `range` > `cursor` + +### 变量解析规则 + +* [ ] 变量解析需区分:全局变量、环境变量、文件内变量、运行时提取变量、链式上下文变量、脚本动态写入变量 + +* [ ] 变量优先级必须固定并在实现中保持一致,默认推荐顺序为:脚本动态写入变量 > 链式上下文变量 > 文件内变量 > 当前环境变量 > 全局变量 + +* [ ] 若同名变量在多个层级同时存在,必须以固定优先级解析,不允许根据加载顺序产生不确定结果 + +* [ ] 未定义变量的处理策略必须显式配置;默认行为为阻断执行并返回 `VARIABLE_ERROR` + +* [ ] 前置脚本写入的变量应在当前请求执行阶段立即生效;后置脚本写入的变量应在后续请求或链式后续步骤中生效 + +* [ ] 环境切换后应立即重新计算变量解析结果,并同步刷新前端预览 + +* [ ] 变量语法需与 IntelliJ IDEA HTTP Client 保持一致,默认使用 `{{variableName}}` 形式 + +* [ ] 变量解析需支持嵌套引用检测;若出现循环引用,应立即报错并指出引用链 + +* [ ] 变量值默认按字符串处理;若用于 JSON、Header、Query 等上下文,不隐式推断复杂类型,除非脚本显式构造 + +### 实时事件模型 + +* [ ] WebSocket 事件至少支持:`execution.created`、`execution.started`、`execution.progress`、`execution.log`、`execution.response.meta`、`execution.response.chunk`、`execution.assertion`、`execution.completed`、`execution.failed`、`execution.cancelled` + +* [ ] 所有事件均需包含 `executionId`、事件类型、事件时间戳、最小必要载荷 + +* [ ] 前端基于事件流增量渲染执行状态、响应内容、脚本输出与断言结果 + +* [ ] 当事件通道异常断开时,前端需自动尝试重连,并支持按 `executionId` 补拉最终状态或结果快照 + +* [ ] WebSocket 重连请求需携带客户端已收到的最新 `executionId` 与 `lastSeq`,后端优先从 `execution_event_index` 补推缺失事件 + +* [ ] 若缺失事件已被清理或无法补齐,后端需返回 `event.replay.unavailable` 事件,前端改为拉取 `GET /api/executions/:id/result` 最终快照 + +* [ ] WebSocket 事件 `seq` 在同一 `executionId` 内必须单调递增,前端需按 `seq` 去重和乱序保护 + +### 事件背压与高频推送 + +* [ ] 事件通道需区分关键事件与可合并事件;`execution.completed`、`execution.failed`、`execution.cancelled`、`execution.assertion` 为关键事件,不得被丢弃 + +* [ ] `execution.progress`、压测指标、响应 chunk、脚本日志等高频事件允许合并、采样或限频,但最终结果快照必须保留完整状态摘要 + +* [ ] 单个前端连接的 WebSocket 待发送队列需设置上限,默认最多缓存 `1000` 条事件或 `10MB` 载荷,超过后优先丢弃可合并事件并发送 `event.backpressure` 告警 + +* [ ] 响应 chunk 推送默认按 `64KB` 或 `100ms` 任一条件触发批量发送,避免小包过多导致 UI 卡顿 + +* [ ] 压测实时指标默认按 `500ms` 聚合推送一次;用户界面刷新频率不得高于事件聚合频率 + +* [ ] 脚本日志默认按行聚合推送,单次执行最多保留最近 `1000` 条日志,超出后在诊断结果中标记截断 + +* [ ] 当前端重连后,后端优先按 `lastSeq` 补发关键事件和最终快照;对已被采样丢弃的进度事件,只保证最终聚合结果可恢复 + +* [ ] 若 WebSocket 客户端持续消费过慢,后端可关闭该连接并返回可理解关闭原因,前端需自动切换到轮询最终结果快照 + +### 执行结果模型 + +* [ ] 最终结果以 `ExecutionResult` 形式固化,用于历史记录、页面刷新恢复、问题排查 + +* [ ] `ExecutionResult` 至少包含以下部分:`summary`、`request`、`response`、`timeline`、`assertions`、`diagnostics` + +* [ ] `summary` 包含任务标识、类型、状态、开始时间、结束时间、耗时、环境、文件路径、请求名称 + +* [ ] `request` 包含原始请求片段、解析后结构、变量替换后的最终请求、执行时环境快照 + +* [ ] `response` 包含状态码、状态文本、响应头、Cookie、响应体、编码、是否截断、大小 + +* [ ] `timeline` 包含各阶段耗时信息,如连接建立、TLS、首包、总耗时 + +* [ ] `assertions` 包含每条断言的名称、结果、失败原因与相关输出 + +* [ ] `diagnostics` 包含脚本日志、执行告警、错误码、错误消息、行列定位信息 + +* [ ] `ExecutionResult` 中的小体积快照属于可清理缓存,不作为唯一事实源;缓存被清理后应允许从请求文件、环境文件和响应缓存索引尽量恢复摘要 + +### 批量执行模型 + +* [ ] 批量执行使用 `type=batch` 父任务表示,内部包含多个子执行任务 + +* [ ] 父任务负责顺序执行、并行执行、最大并发数、`stopOnFailure` 等调度策略 + +* [ ] 子任务继续复用普通 `Execution` 结构,不单独发明第二套结果模型 + +* [ ] 父任务取消时必须级联取消所有尚未完成的子任务 + +### 链式执行模型 + +* [ ] 链式执行使用 `type=chain` 父任务表示,内部包含按顺序依赖的多个子执行任务 + +* [ ] 父任务维护 `chainContext`,用于保存变量提取结果并传递给后续步骤 + +* [ ] 每个链式步骤仍然独立产出执行结果,链级结果额外记录变量传递轨迹与中断位置 + +* [ ] 链式执行默认按顺序串行运行,前一步失败时默认中断后续步骤,除非显式配置允许继续 + +### SSE / WebSocket 会话交互 + +* [ ] SSE 执行需支持手动断开、超时断开、收到完成条件后自动断开三种结束方式 + +* [ ] WebSocket 会话需支持建立连接、主动发送消息、接收消息、手动断开、异常断开提示 + +* [ ] WebSocket 会话消息需区分文本、JSON、二进制三类展示方式 + +* [ ] SSE 与 WebSocket 会话在前端需提供独立会话视图,不与普通 HTTP 响应区完全混用 + +* [ ] 是否将 SSE / WebSocket 会话内容写入历史记录需提供明确策略;默认保存会话摘要与关键消息,不完整持久化无限流数据 + +### 执行调度策略 + +* [ ] 系统需定义统一的执行队列与并发上限,避免无限制并发占用本地资源 + +* [ ] 普通 HTTP 执行、批量子任务、链式步骤、SSE、WebSocket 会话均需纳入统一调度体系 + +* [ ] 长连接任务默认计入活动执行数,并支持单独配置最大长连接数 + +* [ ] 当达到并发上限时,新任务应进入 `pending` 队列等待,而不是直接失败 + +* [ ] 是否支持失败重试需提供显式配置;默认单次执行不自动重试 + +* [ ] 默认普通请求最大并发数为 `6` + +* [ ] 默认长连接最大并发数为 `2` + +* [ ] 默认执行队列最大等待任务数为 `100` + +* [ ] 默认批量执行最大并发数为 `4` + +### 扩展点模型 + +* [ ] 后端内部需预留轻量扩展点注册机制,用于导入器、导出器、执行类型、响应查看器、Schema 校验器和 Mock 生成器扩展 + +* [ ] 扩展点只作为内部模块边界,不设计第三方插件市场,也不要求运行时动态加载外部代码 + +* [ ] 导入器扩展点统一输入源文件路径和工作区上下文,统一输出导入预览、写入计划和不可映射项报告 + +* [ ] 导出器扩展点统一输入解析后请求、环境快照和执行配置,统一输出目标格式文本和差异提示 + +* [ ] 执行类型扩展点统一接入 `Execution` 状态机、事件通道、取消信号、历史固化和调度队列 + +* [ ] 响应查看器扩展点统一基于 `contentType`、响应大小、编码和流式状态选择展示方式,前端不得在各页面重复判断 + +* [ ] Schema 校验器扩展点统一输入响应体、content-type、schemaRef 和契约来源,统一输出结构化断言结果 + +* [ ] Mock 生成器扩展点统一支持从 `.http` 请求、真实响应、OpenAPI operation 三类来源生成规则草案 + +### 性能基准 + +* [ ] 解析 `1MB` `.http` 文件应在 `300ms` 内完成后端 AST 生成,解析 `10MB` 文件应在 `3000ms` 内完成并避免阻塞其他执行任务 + +* [ ] 打开包含 `500` 个请求块的工作区时,请求列表首次可交互时间目标不超过 `2s` + +* [ ] 导入包含 `1000` 个 operation 的 OpenAPI 文件时,预览生成目标时间目标不超过 `5s` + +* [ ] Mock Server 在 `1000` 条启用规则内的单次匹配目标耗时不超过 `5ms`,命中日志写入不得阻塞响应返回 + +* [ ] 普通响应体 `2MB` 以内可直接内存预览,超过阈值必须分段加载或落盘索引,避免前端一次性渲染大文本 + +* [ ] WebSocket 事件在普通请求执行中的端到端展示延迟目标不超过 `300ms` + +* [ ] 压测实时统计在默认阈值内的聚合计算不得成为主要瓶颈,报告中需记录脚本、签名、网络、聚合各阶段耗时占比 + +* [ ] SQLite 索引重建需按文件数量和耗时输出摘要,用于后续定位大工作区性能问题 + +### 压测执行模型 + +* [ ] 压测执行使用 `type=load-test` 表示,目标请求仍通过 `.http` / `.rest` 文件中的请求块选择 + +* [ ] 压测必须由 Go 后端调度执行,前端不得通过循环调用普通执行接口模拟压测 + +* [ ] 压测配置至少包含:并发数、总请求数、持续时间、QPS 限制、预热时间、失败率停止阈值 + +* [ ] 压测执行需支持按总请求数结束、按持续时间结束、用户主动取消、失败率阈值触发停止 + +* [ ] 压测执行默认不保存每次请求的完整响应,仅保存统计摘要、错误摘要和少量采样明细 + +* [ ] 压测执行应复用变量解析、前置脚本、请求构造逻辑;后置脚本与断言默认按采样或聚合策略执行,避免压测过程过重 + +* [ ] 压测执行中的签名计算默认在后端执行;静态签名可在压测开始前预计算,依赖时间戳、nonce、Body 等动态数据的签名需按请求逐次计算 + +* [ ] 压测报告需记录签名计算耗时占比,便于判断压测瓶颈是否来自脚本或签名逻辑 + +* [ ] 压测过程需通过 WebSocket 事件实时推送吞吐、延迟分位数、错误率、状态码分布、已完成请求数 + +### 压测保护阈值 + +* [ ] 默认最大压测并发数为 `100` + +* [ ] 默认最大压测持续时间为 `10min` + +* [ ] 默认最大压测请求数为 `100000` + +* [ ] 默认最大 QPS 限制为 `1000` + +* [ ] 当用户配置超过默认阈值时,界面需提示并要求显式确认 + +* [ ] 压测任务取消后应尽快停止新增请求,并等待已发出的请求完成或超时 + +### 压测结果模型 + +* [ ] 压测结果需包含总请求数、成功数、失败数、错误率、总耗时、实际吞吐 + +* [ ] 压测结果需包含延迟统计:`min`、`avg`、`p50`、`p90`、`p95`、`p99`、`max` + +* [ ] 压测结果需包含状态码分布、错误类型分布、超时数量 + +* [ ] 压测结果需包含配置快照,便于历史回看时确认当时的并发数、QPS、持续时间和环境 + +* [ ] 压测历史详情页需展示趋势图或分桶数据,产品目标版本至少提供按时间窗口聚合的表格数据 + +* [ ] 压测结果默认只保存聚合统计、分桶指标、错误摘要和少量采样明细,不保存每次请求完整明细 + +* [ ] 压测采样明细需受数量上限控制,默认最多保存 `100` 条错误样本与 `100` 条成功样本 + +### Mock Server 支持 + +* [ ] Mock 支持以本地 Mock Server 形式实现,由 Go 后端提供独立监听端口 + +* [ ] Mock Server 默认监听 `127.0.0.1`,端口与主应用服务端口分离 + +* [ ] 默认 Mock Server 端口为 `32190`,若端口被占用则在 `32191-32220` 范围内探测可用端口 + +* [ ] Mock 规则按工作区隔离管理,默认以工作区内 JSON 文件保存,文件可纳入 Git 版本管理 + +* [ ] SQLite 不作为 Mock 规则主存储,仅保存 Mock 规则索引、最近加载状态、命中日志和加速查询缓存 + +* [ ] Mock 规则支持拆分为多个 `.mock.json` 文件,默认加载 `.http-client/mocks/**/*.mock.json` + +* [ ] 多个 Mock 规则文件加载顺序按文件路径字典序稳定排序;实际命中仍以规则 `priority`、更新时间、规则 ID 作为最终决策 + +* [ ] 不同文件中出现相同 Mock 规则 ID 时视为冲突,应用需在加载预览和 Mock 管理页中提示,默认跳过后加载的重复规则 + +* [ ] 规则级新增、编辑、删除操作最终都必须落到对应 `.mock.json` 文件;SQLite 索引仅在保存成功后更新 + +* [ ] Mock 规则至少支持按方法、路径、Query、Header、Body 片段匹配请求 + +* [ ] Mock 路径匹配至少支持精确路径、路径参数、通配符三种模式,例如 `/users/1`、`/users/:id`、`/files/*` + +* [ ] 多条 Mock 规则同时匹配时,按 `priority` 从高到低选择;优先级相同时选择最近更新时间更晚的规则 + +* [ ] Mock 规则冲突需在保存时给出提示,但允许用户显式保存,用于临时覆盖旧规则 + +* [ ] Mock 响应至少支持状态码、Header、Body、固定延迟、响应模板 + +* [ ] Mock 响应 Body 支持 JSON、Text、二进制文件引用 + +* [ ] Mock 响应模板可读取请求路径参数、Query、Header 和 Body 摘要,用于生成动态响应 + +* [ ] Mock 响应模板语法优先采用轻量占位符,例如 `{{path.id}}`、`{{query.page}}`、`{{header.Authorization}}`、`{{body.userId}}` + +* [ ] Mock 响应模板不得执行任意 JavaScript,避免 Mock 响应生成与脚本运行时耦合;产品目标版本不支持在 Mock 模板中编写复杂控制逻辑 + +* [ ] Mock 规则支持启用/停用、复制、按标签分组和备注说明,便于同一接口维护多组模拟场景 + +* [ ] 未命中 Mock 规则时默认返回 `404`;可在工作区设置中配置代理回退到真实上游地址 + +* [ ] 代理回退为工作区级配置,不放在单条 Mock 规则内;启用后所有未命中请求按工作区上游地址转发 + +* [ ] 代理回退模式需在 UI 中明确标识,并在命中日志中区分 `mock-hit`、`mock-miss`、`proxy-hit`、`proxy-error` + +* [ ] Mock Server 需记录命中日志,包括时间、方法、路径、匹配规则、响应状态码、耗时 + +* [ ] Mock 命中日志默认仅记录请求摘要,不保存完整 Header、Body;用户可在设置中调整采样与保留策略 + +* [ ] Mock 命中日志需支持按数量与时间保留,默认最多保留最近 `10000` 条或 `7d` + +* [ ] 支持从当前 `.http` 请求生成基础 Mock 规则,默认以请求方法和 URL path 作为匹配条件 + +* [ ] 支持从已有响应结果生成 Mock 响应 Body、状态码与 Header,降低手写 Mock 成本 + +* [ ] 支持将工作区 Mock 规则保存为 JSON 文件,并支持从 JSON 文件重新加载;文件适合纳入 Git 版本管理 + +* [ ] Mock 规则文件保存或重载前需支持预览变更,至少展示新增、更新、冲突、跳过数量 + +* [ ] Mock 规则文件外部变更时,应用需提示重新加载;文件冲突不做应用内合并,统一交给 Git 处理 + +* [ ] Mock Server 启停状态需进入本地运行状态恢复;应用重启后默认不自动启动 Mock Server,除非用户显式配置 + +* [ ] 系统托盘菜单需展示 Mock Server 运行状态,并提供打开 Mock 管理页、启动、停止快捷操作 + +### 错误模型 + +* [ ] 错误码至少包含:`PARSE_ERROR`、`VARIABLE_ERROR`、`SCRIPT_ERROR`、`REQUEST_ERROR`、`ASSERTION_ERROR`、`TIMEOUT_ERROR`、`CANCELLED`、`FILE_CONFLICT`、`SCHEMA_VERSION_UNSUPPORTED`、`SCHEMA_VALIDATION_ERROR`、`OPENAPI_PARSE_ERROR`、`OPENAPI_SYNC_CONFLICT`、`INDEX_REBUILD_ERROR`、`MIGRATION_ERROR`、`INTERNAL_ERROR` + +* [ ] 语法错误、变量错误、脚本错误应尽量返回可映射到编辑器的行列定位信息 + +## 脚本与断言模型 + +### 脚本运行时边界 + +* [ ] 请求前置脚本、请求后置脚本、断言统一采用 JavaScript 作为脚本语言 + +* [ ] 后端采用嵌入式 JavaScript 运行时执行脚本,与 Go 执行器运行在同一进程内 + +* [ ] 脚本运行时需提供稳定、可控、可测试的内置 API,不依赖浏览器环境 + +* [ ] 脚本运行时默认不提供任意文件系统访问、任意网络访问、动态模块安装等能力;本条仅定义运行时能力边界 + +* [ ] 脚本运行时默认不提供浏览器 DOM、`window`、`document` 等浏览器对象 + +### 脚本生命周期 + +* [ ] 请求执行顺序为:解析请求 -> 变量替换 -> 前置脚本 -> 发送请求 -> 后置脚本 -> 断言 -> 结果固化 + +* [ ] 批量执行与链式执行中的每个子任务均独立执行上述生命周期 + +* [ ] 前置脚本可修改最终请求上下文,后置脚本可读取响应并提取变量 + +### 签名计算支持 + +* [ ] 签名计算默认通过后端前置脚本实现,执行时机为变量替换之后、请求发送之前 + +* [ ] 前置脚本可读取当前请求、环境变量、文件内变量和脚本动态变量,用于构造签名原文 + +* [ ] 前置脚本可将签名结果写回 Header、Query、Body 或运行时变量 + +* [ ] 签名计算不得在前端 UI 线程中执行;普通执行、批量执行、链式执行、压测执行均由后端统一完成签名 + +* [ ] 内置 `crypto` API 的底层实现应优先使用 Go 原生实现,JavaScript 脚本只负责编排签名原文和写回请求 + +* [ ] 签名计算失败应中断当前请求,并返回 `SCRIPT_ERROR` + +* [ ] 签名计算相关日志需进入脚本日志,但默认仅记录计算阶段与错误摘要,不主动输出 secret、token 等原始变量值 + +* [ ] 压测、批量执行、链式执行中的每个子请求均需独立执行签名前置脚本,确保时间戳、nonce、签名值按请求生成 + +### 脚本上下文 + +* [ ] 脚本上下文至少提供:当前环境变量、文件内变量、全局变量、当前请求对象、当前响应对象、链式上下文对象 + +* [ ] 前置脚本可访问请求对象但不可访问响应对象 + +* [ ] 后置脚本与断言可同时访问请求对象和响应对象 + +* [ ] 脚本对变量的写入应支持回填到执行上下文,以便后续链式步骤使用 + +### 脚本内置 API + +* [ ] 至少提供读取变量、设置变量、读取请求、修改请求、读取响应、输出日志、添加断言结果等内置 API + +* [ ] 至少提供便于 JSON、Header、状态码校验的断言辅助 API + +* [ ] 脚本输出日志需进入实时事件流与最终诊断结果 + +* [ ] 内置 API 需提供稳定命名,至少包括:`vars.get(name)`、`vars.set(name, value)`、`request.get()`、`request.setHeader(name, value)`、`request.setQuery(name, value)`、`request.setBody(value)`、`response.get()`、`log.info(message)`、`assert.equal(actual, expected, message)` + +* [ ] 后置脚本与断言需支持异步 API,但必须在脚本超时范围内完成 + +* [ ] 若支持 `console.log`,其输出需等价映射到 `log.info` + +* [ ] 脚本 `assert` 失败必须生成结构化断言结果,而不是仅输出文本日志 + +* [ ] 内置 `crypto` API 至少提供:`crypto.hmacSha256Hex(secret, data)`、`crypto.sha256Hex(data)`、`crypto.md5Hex(data)`、`crypto.base64Encode(data)`、`crypto.uuid()` + +### 脚本执行约束 + +* [ ] 每段脚本需设置独立超时时间,避免单个脚本阻塞整个执行任务 + +* [ ] 脚本执行失败应中断当前任务,并以 `SCRIPT_ERROR` 返回 + +* [ ] 断言失败应固化为 `ASSERTION_ERROR`,并在结果中展示断言明细 + +* [ ] 默认前置脚本超时时间为 `3000ms` + +* [ ] 默认后置脚本超时时间为 `3000ms` + +* [ ] 默认单次 HTTP 请求超时时间为 `30000ms` + +## 数据存储模型 + +### 存储边界 + +* [ ] 原始 `.http` / `.rest` 文件不写入 SQLite,仅以本地文件形式保存 + +* [ ] SQLite 仅用于索引、缓存、命中日志,不保存环境变量、项目配置、Mock 规则等事实源数据 + +* [ ] Mock 规则源数据默认保存为工作区 JSON 文件,不以 SQLite 作为唯一事实来源 + +* [ ] 环境变量与项目配置默认保存为工作区 JSON 文件,不以 SQLite 作为唯一事实来源 + +* [ ] OpenAPI 原始契约文件与 `.http-client/openapi/sources.json` 默认保存为工作区文件,不以 SQLite 作为唯一事实来源 + +* [ ] OpenAPI 派生的 `.http` 请求模板、Mock 规则文件、Schema 校验映射均按工作区文件管理,SQLite 仅保存可重建索引与摘要 + +### SQLite 设计 + +* [ ] 至少包含以下数据表:`execution_history_index`、`execution_event_index`、`response_cache_index`、`recent_file_cache`、`mock_rule_index`、`mock_hit_logs`、`openapi_source_index`、`openapi_operation_index` + +* [ ] `execution_history_index` 保存执行摘要、文件路径、请求标识、环境、状态、耗时、时间范围、结果索引信息 + +* [ ] `execution_event_index` 保存可选事件索引信息,用于快速回放关键执行过程 + +* [ ] `response_cache_index` 保存响应体、事件载荷、历史快照等落盘缓存文件的索引与摘要 + +* [ ] `recent_file_cache` 保存最近打开文件与最近访问目录,可清理、可重建 + +* [ ] `mock_rule_index` 保存 Mock 规则文件索引与摘要,便于快速查询和启动 Mock Server + +* [ ] `openapi_source_index` 保存 OpenAPI 源文件索引与摘要,便于快速展示契约来源和同步状态 + +* [ ] `openapi_operation_index` 保存 OpenAPI operation 索引与生成产物摘要,便于接口搜索、diff 和同步预览 + +### 索引重建策略 + +* [ ] SQLite 索引与缓存允许删除后重建,不得作为工作区事实源数据的唯一存储 + +* [ ] 索引重建需扫描 `.http-client/environments.json`、`.http-client/settings.json`、`.http-client/mocks/**/*.mock.json`、`.http-client/openapi/sources.json`、OpenAPI 源文件、响应缓存目录、事件缓存目录和最近文件缓存来源 + +* [ ] 索引重建完成后需返回重建摘要,至少包含扫描文件数、重建索引数、跳过数、错误数和错误摘要 + +* [ ] 若 SQLite 损坏,应用应提供“重建索引”恢复路径;命中日志可备份后清空,不阻断工作区继续使用 + +* [ ] 索引重建过程中不得修改工作区事实源 JSON 文件 + +### 数据保留策略 + +* [ ] 请求历史需支持数量上限与清理策略,避免数据库无限增长 + +* [ ] 响应体过大时允许落盘为单独文件,仅在 SQLite 中保存索引与摘要信息 + +* [ ] 批量执行与链式执行需支持父子任务关系索引,便于查询与展示 + +* [ ] SSE 与 WebSocket 长连接历史需支持消息数量截断与摘要化存储,避免无限增长 + +* [ ] 默认历史记录保留上限为 `1000` 条 + +* [ ] 超出保留上限时,系统按最旧优先策略自动清理 + +### 大响应体与二进制处理 + +* [ ] 系统需定义响应体内存预览阈值、自动落盘阈值、历史存储阈值,并在前后端保持一致 + +* [ ] 二进制响应默认不直接全文渲染,优先展示摘要信息、大小、MIME 类型与保存入口 + +* [ ] 大文本响应需支持分段加载、截断提示、另存为文件 + +* [ ] 历史回看时若响应体已落盘,系统需支持按索引重新加载该响应内容 + +* [ ] 默认响应体内存预览阈值为 `2MB` + +* [ ] 默认自动落盘阈值为 `5MB` + +* [ ] 默认历史存储阈值为 `10MB` + +### 数据目录约束 + +* [ ] 应用需提供独立的数据目录用于 SQLite、日志、缓存响应体、运行时文件存储 + +* [ ] 工作区 `.http-client/` 目录仅用于保存可版本管理的事实源文件,例如环境变量、项目配置、Mock 规则与 Mock 静态资源 + +* [ ] 工作区 `.http-client/openapi/` 目录仅用于保存 OpenAPI 源映射、同步配置和可版本管理的派生关系,不用于保存运行时响应缓存 + +* [ ] SQLite、响应缓存、事件缓存、日志、诊断包、备份文件默认保存到应用数据目录,不写入工作区 `.http-client/` + +* [ ] Mock 静态资源属于 Mock 规则事实源,可保存在工作区 `.http-client/mocks/assets/` 并随规则文件进入 Git;运行时响应缓存不得放入该目录 + +* [ ] 系统托盘菜单中的“打开数据目录”需直接定位到该目录 + +### 环境配置边界 + +* [ ] 环境配置默认按工作区维度隔离存储,不同工作区之间互不干扰 + +* [ ] 全局环境变量仅作为跨工作区共享的可选能力,不应覆盖工作区级环境的默认行为 + +* [ ] 需提供默认环境选择规则:若请求或工作区未显式指定环境,则使用最近一次选中的工作区默认环境 + +* [ ] 需明确请求执行时所使用的环境快照,确保历史回看时可以看到当时的实际变量值 + +* [ ] 环境变量需支持导入导出,导出格式需可读、可版本管理 + +### 默认值与配置优先级 + +* [ ] 应用默认值需集中定义,后端默认值源建议位于 `backend/internal/model/defaults.go`,前端展示默认值需由 API 或生成类型获得,避免前后端各自硬编码 + +* [ ] 配置优先级需固定为:本次执行请求显式参数 > 当前工作区设置 > 应用内置默认值 + +* [ ] 环境变量解析优先级继续遵循变量解析规则,不与应用配置优先级混用 + +* [ ] 执行类默认值至少包含:普通请求超时、前置脚本超时、后置脚本超时、普通请求最大并发数、长连接最大并发数、批量执行最大并发数、执行队列最大等待任务数 + +* [ ] 压测类默认值至少包含:最大并发数、最大持续时间、最大请求数、最大 QPS、采样明细数量、实时指标推送间隔 + +* [ ] Mock 类默认值至少包含:默认端口、端口探测范围、未命中响应策略、命中日志保留数量、命中日志保留天数、代理回退默认状态 + +* [ ] OpenAPI 类默认值至少包含:生成请求目录、生成 Mock 目录、同步策略、是否默认启用 Schema 校验、默认 `baseUrl` 变量名 + +* [ ] UI 类默认值至少包含:最近文件数量、历史分页大小、日志展示行数、响应体内存预览阈值、响应体自动落盘阈值 + +* [ ] 设置页展示的默认值需说明来源:内置默认值、工作区覆盖值或本次执行覆盖值 + +* [ ] 用户恢复默认值时,只应删除工作区覆盖项,不应改写内置默认值或历史执行快照 + +### 状态恢复策略 + +* [ ] 页面刷新后应恢复最近打开文件、当前选中文件、最近一次执行结果和当前环境选择 + +* [ ] 浏览器重新打开后应恢复最近工作区与最近会话摘要,但不强制恢复未保存临时编辑内容 + +* [ ] 对于正在运行中的普通执行任务,前端重新连接后应根据 `executionId` 自动恢复状态展示 + +* [ ] 对于 SSE / WebSocket 长连接,会话断开后默认不自动恢复原始连接,但需保留断开前的摘要与日志 + +* [ ] 页面刷新后应恢复编辑器光标位置、滚动位置与当前激活的响应标签页 + +* [ ] 未保存草稿默认仅保存在浏览器当前会话内,浏览器完全关闭后不保证恢复 + +## 前端信息架构 + +### 主界面布局 + +* [ ] 主界面采用三栏布局:左侧资源区、中间编辑区、右侧或下方响应区 + +* [ ] 左侧资源区至少包含文件树、最近文件、请求历史入口 + +* [ ] 中间编辑区使用 Monaco Editor,负责 `.http` / `.rest` 编辑、语法高亮、当前请求高亮和错误定位 + +* [ ] 响应区至少包含 `Body`、`Headers`、`Cookies`、`Timeline`、`Logs` 标签页 + +* [ ] 响应区需支持普通 HTTP 响应视图与 SSE / WebSocket 会话视图切换 + +### 历史详情页 + +* [ ] 历史详情页需展示执行摘要、请求快照、响应快照、断言结果、脚本日志、时间线 + +* [ ] 历史详情页需提供重新执行、复制 curl、打开源文件、定位请求块入口 + +* [ ] 压测历史详情页需展示吞吐、延迟分位数、错误率、状态码分布和配置快照 + +### 环境管理页 + +* [ ] 环境管理页需按工作区展示环境列表 + +* [ ] 环境变量表至少包含变量名、变量值、作用域、更新时间 + +* [ ] 环境管理页需提供新增、编辑、删除、导入、导出、设为默认环境操作 + +### 设置页 + +* [ ] 设置页需包含本地服务配置、执行默认值、历史保留策略、日志与诊断入口、数据目录入口 + +* [ ] 设置页修改默认值后需立即影响新创建的执行任务,不影响已运行任务 + +### Mock 管理页 + +* [ ] Mock 管理页需展示当前工作区 Mock Server 状态、访问地址、启动/停止操作 + +* [ ] Mock 管理页需展示 Mock 规则列表,至少包含方法、路径、状态码、延迟、启用状态、最近命中时间 + +* [ ] Mock 规则列表需支持按启用状态、方法、路径、标签、命中状态筛选,并支持按优先级拖拽排序 + +* [ ] Mock 规则编辑器需支持匹配条件、优先级、标签、备注、响应状态码、响应 Header、响应 Body、延迟、模板变量 + +* [ ] Mock 规则编辑器需在保存前展示冲突提示,说明可能被覆盖或覆盖其他规则的匹配范围 + +* [ ] Mock 响应 Body 编辑器需支持 JSON Pretty、Raw Text、二进制文件引用三种模式 + +* [ ] Mock 管理页需提供从最近一次真实响应生成 Mock 规则的入口 + +* [ ] Mock 管理页需展示命中日志,并支持按方法、路径、状态码、命中/未命中过滤 + +* [ ] Mock 命中日志详情需展示匹配结果、规则名称、请求摘要、响应来源、代理回退状态与耗时 + +* [ ] 请求编辑器需提供“从当前请求生成 Mock 规则”的入口 + +* [ ] Mock 管理页需提供 Mock 规则文件保存、重载、预览变更、清空命中日志操作 + +### OpenAPI 管理页 + +* [ ] OpenAPI 管理页需展示当前工作区已登记的 OpenAPI 契约源,至少包含名称、源文件路径、版本、接口数量、最近同步时间、同步状态 + +* [ ] OpenAPI 管理页需提供导入本地 OpenAPI 文件、重新解析、同步预览、应用同步、移除源映射操作 + +* [ ] OpenAPI 导入向导需展示生成目标,包括 `.http` 请求文件、Mock 规则文件、Schema 校验映射,并允许用户逐项勾选 + +* [ ] OpenAPI 导入预览需展示将要新增、更新、跳过、冲突的请求块和 Mock 规则,默认不直接写入文件 + +* [ ] OpenAPI 同步预览需突出本地已修改请求块,默认跳过并提示用户手动确认是否覆盖 + +* [ ] OpenAPI diff 视图需按接口维度展示新增、删除、变更和潜在破坏性变更,支持按 tag、路径、方法过滤 + +* [ ] 请求编辑器需展示 OpenAPI 来源标记,并提供打开来源 operation、重新生成当前请求模板、启用响应 Schema 校验入口 + +* [ ] 响应区需提供“按 OpenAPI Schema 校验当前响应”入口,校验结果进入断言结果视图 + +* [ ] Mock 管理页需提供“从 OpenAPI 生成 Mock 骨架”的入口,并展示生成来源、operationId 和响应状态码 + +### UI 空状态与异常状态 + +* [ ] 首次启动且未打开工作区时,主界面需展示打开工作区、打开 `.http` 文件、新建示例文件三个入口 + +* [ ] 当前工作区无 `.http` / `.rest` 文件时,文件树需展示空状态,并提供新建请求文件和导入 OpenAPI 的入口 + +* [ ] 请求文件解析失败时,编辑器需保留原文本展示,并在问题面板中列出可定位的解析错误 + +* [ ] 未选择请求块时,响应区需展示引导状态,提示用户将光标放到请求块内或从请求列表选择请求 + +* [ ] 执行中状态需清晰展示当前阶段,例如解析、变量替换、前置脚本、发送请求、接收响应、断言、固化历史 + +* [ ] 请求取消、超时、网络失败、脚本失败、断言失败、Schema 校验失败需使用不同错误状态展示,不得统一显示为“请求失败” + +* [ ] WebSocket 断开时,页面需展示连接状态和恢复动作;重连成功后需提示是否已补齐事件或已切换到结果快照 + +* [ ] 外部文件变更提示需区分“可安全重载”和“当前存在未保存修改”,避免用户误覆盖编辑内容 + +* [ ] 大响应体被截断、二进制响应不可直接预览、日志被截断时,界面需给出明确原因和可用操作 + +* [ ] OpenAPI 导入和同步预览为空时,需说明没有可写入变更,而不是展示空白列表 + +### 快捷键与交互一致性 + +* [ ] 核心快捷键需集中定义,避免不同页面重复绑定造成冲突 + +* [ ] MVP 至少支持执行当前请求、保存当前文件、打开文件、搜索请求、取消当前执行五类快捷操作 + +* [ ] 快捷键触发的执行行为必须与按钮点击行为复用同一前端 action,不得分叉实现 + +* [ ] 弹窗、抽屉、预览面板需统一支持 `Escape` 关闭和焦点回到触发元素 + +* [ ] 危险或不可逆操作需提供确认,例如清空命中日志、删除 Mock 规则、覆盖本地已修改的 OpenAPI 生成块 + +* [ ] 前端所有异步操作需具备加载中、成功、失败、重试或关闭入口,不允许只在控制台输出错误 + +### 可访问性与国际化 + +* [ ] 前端交互控件需具备键盘可达性,核心操作至少支持 Tab 导航与 Enter / Escape 确认取消 + +* [ ] 编辑器外的按钮、菜单、标签页、弹窗需具备可理解的 aria label 或等效可访问名称 + +* [ ] 默认界面语言为中文,文案需集中管理,避免散落在组件内部 + +* [ ] 产品目标版本需预留国际化结构,允许后续增加英文界面 + +* [ ] 时间、文件大小、耗时等显示格式需统一封装,避免各组件自行格式化 + +## MVP 范围 + +### MVP 必须完成 + +* [ ] 本地服务启动、浏览器访问、系统托盘打开页面与退出服务 + +* [ ] 本地工作区文件打开、保存、另存为、最近文件、外部变更提示 + +* [ ] `.http` 文件多请求解析、当前请求定位、变量替换、基础错误定位 + +* [ ] 单请求执行、执行取消、响应展示、历史记录回看 + +* [ ] 基础环境管理与环境切换 + +* [ ] 统一 `Execution` 模型、REST 控制面、WebSocket 事件通道 + +* [ ] JavaScript 前置脚本、后置脚本、基础断言能力 + +### 产品目标版本必须完成 + +* [ ] 批量执行 + +* [ ] 链式执行 + +* [ ] SSE 支持 + +* [ ] WebSocket 会话支持 + +* [ ] 对目标接口发起轻量压测 + +* [ ] 本地 Mock Server 与 Mock 规则管理 + +* [ ] curl 导出、Postman collection 导入与 OpenAPI 导入同步 + +* [ ] 完整兼容性回归测试集 + +### 可后续迭代 + +* [ ] 测试报告导出增强 + +* [ ] 更丰富的请求可视化预览 + +* [ ] 更完整的执行时间线分析 + +## 验收标准 + +### MVP 功能验收 + +* [ ] 用户可直接打开真实 `.http` 文件并执行当前请求,无需改写为应用私有格式 + +* [ ] 用户可在同一文件中执行多个请求块,并正确区分当前请求与其他请求 + +* [ ] 用户可切换环境后立即看到变量替换结果变化,并执行对应请求 + +* [ ] 用户可在长时间请求执行过程中看到实时状态变化,并主动取消任务 + +* [ ] 用户可在历史记录中重新查看执行结果,并恢复请求上下文 + +### 产品目标版本功能验收 + +* [ ] 用户可对当前请求发起轻量压测,并实时查看吞吐、延迟分位数和错误率 + +* [ ] 用户可启动本地 Mock Server,并通过 Mock 地址访问已配置的模拟响应 + +* [ ] 用户可为同一路径维护多条 Mock 规则,并通过优先级控制实际命中的响应 + +* [ ] 用户可从真实响应生成 Mock 规则,并保存到工作区 Mock 规则文件交给 Git 管理 + +* [ ] 用户可导入 OpenAPI 文件生成 `.http` 请求模板,并在不覆盖本地修改的前提下预览和应用后续同步 + +* [ ] 用户可基于 OpenAPI 响应 Schema 校验真实响应,并在断言结果中看到字段级错误 + +### 兼容性验收 + +* [ ] 建立一套分层的真实 `.http` 示例文件集,至少区分 `MVP` 用例集与产品目标版本完整用例集 + +* [ ] 每次阶段发布前均需执行对应阶段范围内的兼容性回归测试;产品目标版本发布前需执行完整兼容性回归测试 + +* [ ] 若发现与 IntelliJ IDEA HTTP Client 行为不一致,需记录差异并在发布前修复或明确列入已知限制 + +### 质量验收 + +* [ ] 后端需具备解析器、执行器、脚本运行时、API 层单元测试 + +* [ ] 前端需具备核心组件测试与关键交互测试 + +* [ ] 集成测试需覆盖文件打开、请求执行、事件推送、历史回看、环境切换等主流程 + +* [ ] 压测测试需覆盖并发限制、QPS 限制、取消、失败率停止阈值和结果统计准确性 + +* [ ] Mock 测试需覆盖规则匹配、优先级冲突、未命中、代理回退、动态响应、延迟、命中日志和启停状态 + +### 非功能验收 + +* [ ] 冷启动到本地服务可访问的目标时间不超过 `3s` + +* [ ] 打开 `1MB` `.http` 文件并完成请求块识别的目标时间不超过 `1s` + +* [ ] 普通 JSON 响应 `2MB` 以内 Pretty 展示目标时间不超过 `1s` + +* [ ] 空闲状态后端常驻内存目标不超过 `150MB` + +* [ ] 前端主要交互不应因流式响应持续追加而出现明显卡顿 + +* [ ] 大响应体、长连接和批量执行场景不得导致 UI 主线程长时间阻塞 + +* [ ] 压测执行期间前端统计展示刷新频率需受控,避免高频事件导致 UI 卡顿 + +### 质量门禁与完成定义 + +* [ ] 每个 MVP 任务完成前必须能对应到需求编号、实现提交、测试用例和验收项 + +* [ ] 后端代码合入前至少通过:`go test ./backend/...`、API envelope 测试、parser golden tests、核心执行集成测试 + +* [ ] 前端代码合入前至少通过:`npm run test --prefix frontend`、关键组件测试、主流程 E2E 或对应替代验证 + +* [ ] 契约相关变更合入前必须通过内部 API OpenAPI 契约校验、前端类型生成校验和 API 示例 JSON 快照校验 + +* [ ] 工作区 JSON schema 变更合入前必须通过 schema 校验、低版本迁移测试和高版本拒绝写入测试 + +* [ ] Mock、OpenAPI、环境变量、设置文件的写入逻辑变更必须覆盖 `contentHash` 冲突检测和原子写入失败路径 + +* [ ] 事件流、压测、SSE、WebSocket 相关变更必须覆盖慢客户端、断线重连、取消和最终结果快照恢复 + +* [ ] 大响应体、二进制响应、日志截断相关变更必须覆盖阈值、落盘、截断提示和历史回看 + +* [ ] 任一阶段发布前需更新兼容矩阵、测试 fixture、API 示例和实施状态,避免文档与实现漂移 + +* [ ] 若某项验收无法自动化,需在测试记录中说明手工验证步骤、输入数据和观察结果 + +* [ ] 完成定义固定为:功能可用、错误可理解、状态可恢复、数据不丢失、测试有覆盖、文档已同步 + +## 诊断与运维约束 + +### 日志与诊断 + +* [ ] 系统需区分应用日志、执行日志、脚本日志、会话日志,避免所有日志混入同一输出 + +* [ ] 日志需至少支持 `error`、`warn`、`info`、`debug` 四级别 + +* [ ] 每次执行任务需具备可追踪的日志标识,便于通过 `executionId` 检索相关日志 + +* [ ] 设置页或托盘菜单需提供“打开日志目录”或等效入口,便于排查问题 + +* [ ] 用户需能够导出基础诊断信息,至少包含版本号、系统信息、最近错误摘要、最近执行日志索引 + +* [ ] 日志文件命名格式为 `{category}-{yyyyMMdd}.log` + +* [ ] 执行日志需包含 `executionId`,便于与历史记录互相定位 + +* [ ] 诊断包导出格式为 zip,目录结构至少包含 `summary.json`、`logs/`、`recent-errors.json`、`runtime.json` + +### 故障恢复 + +* [ ] 本地服务异常退出后,再次启动时应提示最近一次非正常退出,并提供查看日志入口 + +* [ ] 若 SQLite 或缓存文件损坏,系统需给出可理解的错误提示,并提供“重建索引”恢复入口 + +* [ ] 重建索引失败不应阻断工作区事实源文件访问;用户仍可打开、编辑、保存 `.http` 与 `.http-client/*.json` 文件 + +## 构建与发布命令 + +### 本地开发命令 + +* [ ] 后端开发命令:`go run ./backend` + +* [ ] 前端开发命令:`npm run dev --prefix frontend` + +* [ ] 前端构建命令:`npm run build --prefix frontend` + +* [ ] 后端测试命令:`go test ./backend/...` + +* [ ] 前端测试命令:`npm run test --prefix frontend` + +### 打包命令 + +* [ ] 统一构建命令:`npm run build --prefix frontend` 后执行 `go build ./backend` + +* [ ] 前端构建产物目录固定为 `frontend/dist` + +* [ ] 后端需将 `frontend/dist` 作为静态资源托管目录 + +* [ ] 产品目标版本默认将前端静态资源嵌入 Go 二进制,MVP 阶段允许以目录形式随包分发 + +## 测试计划 + +### 后端测试 + +* [ ] Parser 使用 golden tests 覆盖 `.http` / `.rest` 解析、错误定位、变量引用、请求块定位 + +* [ ] Executor 使用 integration tests 覆盖普通 HTTP、取消、超时、重定向、Cookie、TLS 配置 + +* [ ] Execution 使用状态机测试覆盖状态流转、父子任务、取消级联、队列调度 + +* [ ] Script runtime 使用单元测试覆盖变量读写、请求修改、响应读取、断言失败、超时 + +* [ ] API 层使用 handler tests 覆盖统一响应 envelope、错误模型、分页与状态查询 + +* [ ] OpenAPI 模块测试覆盖 3.0 / 3.1 解析、请求模板生成、Mock 骨架生成、同步预览、diff、响应 Schema 校验 + +### 前端测试 + +* [ ] Editor 组件测试覆盖当前请求定位、错误标记、变量预览 + +* [ ] Response 组件测试覆盖标签页切换、Raw / Pretty / Preview、流式 chunk 增量渲染 + +* [ ] History 测试覆盖历史列表分页、详情回看、重新执行 + +* [ ] Environment 测试覆盖环境切换、变量编辑、导入导出 + +* [ ] Mock 管理测试覆盖规则新增、编辑、启停、优先级排序、文件保存、文件重载、命中日志过滤 + +* [ ] OpenAPI 管理测试覆盖导入向导、同步预览、diff 过滤、生成目标勾选、Schema 校验结果展示 + +### 端到端测试 + +* [ ] E2E 测试覆盖打开工作区、打开 `.http` 文件、执行当前请求、查看响应、取消请求、历史回看 + +* [ ] 兼容性回归测试需按附录 A 的用例路径组织 + +* [ ] 压测 E2E 测试需覆盖创建压测、实时统计更新、主动取消、历史报告回看 + +* [ ] Mock E2E 测试需覆盖从请求生成 Mock 规则、从真实响应生成 Mock 规则、启动 Mock Server、访问 Mock 响应、查看命中日志、保存与重载规则文件 + +* [ ] OpenAPI E2E 测试需覆盖导入契约、生成 `.http` 请求、生成 Mock 骨架、执行请求、校验响应 Schema、预览同步冲突 + +## 支持矩阵与发布约束 + +### 平台支持矩阵 + +* [ ] 当前最低支持版本定义为:Windows 10、macOS 12、主流 Linux x86_64 发行版 + +* [ ] 推荐浏览器定义为:Chrome / Edge 最新两个稳定版本 + +* [ ] 对于不同平台上的托盘、文件路径、默认浏览器唤起行为差异,需建立平台差异清单 + +### 打包与升级约束 + +* [ ] 打包产物需明确区分安装目录、应用数据目录、缓存目录、日志目录 + +* [ ] 版本升级时需提供 SQLite 缓存数据库迁移、工作区 JSON schema 迁移与失败回滚策略 + +* [ ] 升级后不得破坏现有工作区配置、环境配置、历史记录索引 + +* [ ] 安装包与二进制发布说明需明确首次启动行为、数据目录位置、卸载后数据保留策略 + +### 导入导出映射 + +* [ ] curl 导出需覆盖方法、URL、Query、Header、Cookie、Body、代理和 TLS 相关配置 + +* [ ] curl 导出遇到无法表达的应用内部配置时,需在导出结果旁展示差异提示 + +* [ ] Postman collection 导入需覆盖 collection、folder、request、environment variables 的基础映射 + +* [ ] Postman 导入遇到不可映射字段时,不应静默丢弃,需生成导入报告 + +* [ ] 导入报告至少包含:成功数量、失败数量、跳过数量、不可映射字段列表、源文件路径 + +* [ ] 导入后的请求默认生成 `.http` 文件,原始 Postman collection 不写入数据库 + +* [ ] OpenAPI 导入需覆盖 `paths`、`tags`、`operationId`、`parameters`、`requestBody`、`responses`、`servers` 的基础映射 + +* [ ] OpenAPI 导入遇到不可映射字段时,不应静默丢弃,需生成导入报告,并在报告中标记字段路径与影响范围 + +* [ ] OpenAPI 导入后的请求默认生成 `.http` 文件,原始 OpenAPI 文件仍保留为工作区契约源,不写入数据库 + +* [ ] OpenAPI 同步需保留本地手写脚本、断言和备注,除非用户显式选择重新生成对应请求块 + +* [ ] OpenAPI diff 报告需区分兼容变更与潜在破坏性变更,供同步预览和发布检查复用 + +## 附录 A:兼容矩阵草案 + +| 语法项 | 产品目标版本 | MVP | 差异说明 | 回归用例 | +| --- | --- | --- | --- | --- | +| `.http` 文件 | 支持 | 支持 | 无 | `compat/basic/get.http` | +| `.rest` 文件 | 支持 | 支持 | 与 `.http` 同解析流程 | `compat/basic/rest-file.rest` | +| `###` 请求分隔 | 支持 | 支持 | 无 | `compat/parser/multiple-requests.http` | +| 请求命名 | 支持 | 支持 | 需展示在执行摘要与历史记录中 | `compat/parser/named-request.http` | +| 行注释与块注释 | 支持 | 支持 | 与 IntelliJ IDEA HTTP Client 保持一致 | `compat/parser/comments.http` | +| 自定义 HTTP 方法 | 支持 | 支持 | 不限制为内置方法枚举 | `compat/request/custom-method.http` | +| Header 解析 | 支持 | 支持 | 支持同名 Header 多值 | `compat/request/headers.http` | +| Query 参数 | 支持 | 支持 | URL 原样解析,变量替换后执行 | `compat/request/query-vars.http` | +| JSON Body | 支持 | 支持 | 支持格式化预览 | `compat/body/json.http` | +| XML Body | 支持 | 支持 | 支持 Raw 与 Pretty 预览 | `compat/body/xml.http` | +| Text Body | 支持 | 支持 | 按文本展示 | `compat/body/text.http` | +| Form URL Encoded | 支持 | 产品目标版本 | MVP 可先作为 Raw Body 执行 | `compat/body/form-url-encoded.http` | +| Multipart Form Data | 支持 | 产品目标版本 | 需覆盖文本字段与文件字段 | `compat/body/multipart.http` | +| 二进制 Body | 支持 | 产品目标版本 | 需结合文件引用处理 | `compat/body/binary-file.http` | +| 外部文件引用 | 支持 | 产品目标版本 | 路径解析遵循工作区模型 | `compat/body/external-file.http` | +| 文件内变量 | 支持 | 支持 | 参与固定优先级解析 | `compat/vars/file-vars.http` | +| 环境变量 | 支持 | 支持 | 工作区级隔离 | `compat/vars/environment-vars.http` | +| 全局变量 | 支持 | 产品目标版本 | 作为跨工作区可选能力 | `compat/vars/global-vars.http` | +| 变量嵌套引用 | 支持 | 支持 | 循环引用需报错 | `compat/vars/nested-vars.http` | +| Cookie 管理 | 支持 | 产品目标版本 | 需展示 Cookie 视图 | `compat/request/cookies.http` | +| 重定向 | 支持 | 产品目标版本 | 需展示最终 URL 与跳转链 | `compat/request/redirect.http` | +| 代理配置 | 支持 | 产品目标版本 | 需在请求配置中体现 | `compat/request/proxy.http` | +| SSL/TLS 配置 | 支持 | 产品目标版本 | 包含跳过校验与证书配置 | `compat/request/tls.http` | +| 客户端证书 | 支持 | 产品目标版本 | 证书文件按工作区路径规则解析 | `compat/request/client-cert.http` | +| 前置脚本 | 支持 | 支持 | JavaScript 运行时执行 | `compat/scripts/pre-request.http` | +| 后置脚本 | 支持 | 支持 | 支持变量回填 | `compat/scripts/post-request.http` | +| 断言 | 支持 | 支持 | 失败生成结构化断言结果 | `compat/scripts/assertions.http` | +| 链式请求 | 支持 | 产品目标版本 | 通过 `chainContext` 传递变量 | `compat/flow/chain.http` | +| 批量执行 | 支持 | 产品目标版本 | 父子 `Execution` 模型 | `compat/flow/batch.http` | +| SSE | 支持 | 产品目标版本 | 独立会话视图 | `compat/stream/sse.http` | +| WebSocket | 支持 | 产品目标版本 | 支持收发消息与会话摘要 | `compat/stream/websocket.http` | +| 轻量压测 | 支持 | 产品目标版本 | 基于 `load-test` Execution 类型 | `compat/load-test/basic-load-test.http` | +| 本地 Mock Server | 支持 | 产品目标版本 | 工作区级 Mock 规则与命中日志 | `compat/mock/basic-mock.json` | +| 导出 curl | 支持 | 产品目标版本 | 保持请求语义一致 | `compat/import-export/export-curl.http` | +| 导入 Postman | 支持 | 产品目标版本 | 不可映射项需提示 | `compat/import-export/postman-collection.json` | +| 导入 OpenAPI | 支持 | 产品目标版本 | 作为接口契约来源生成 `.http`、Mock 骨架和 Schema 校验映射 | `compat/import-export/openapi-import.yaml` | +| OpenAPI 同步 | 支持 | 产品目标版本 | 预览 diff 并保护本地已修改请求块 | `compat/import-export/openapi-sync.yaml` | +| OpenAPI Schema 校验 | 支持 | 产品目标版本 | 用户显式启用后生成结构化断言结果 | `compat/import-export/openapi-schema-validation.http` | + +## 附录 B:API 示例 JSON + +### 统一成功响应 + +```json +{ + "success": true, + "data": { + "executionId": "exec_01HZY7R4S7M4H4QF3W2K2N4H8A", + "status": "pending", + "type": "http" + }, + "error": null, + "requestId": "req_01HZY7R4S7S0Y73ZJ81EF1Z8Y3", + "timestamp": "2026-06-06T10:20:30Z" +} +``` + +### 统一失败响应 + +```json +{ + "success": false, + "data": null, + "error": { + "code": "VARIABLE_ERROR", + "message": "Undefined variable: token", + "details": { + "variableName": "token" + }, + "location": { + "filePath": "C:/workspace/api/demo.http", + "line": 8, + "column": 23 + } + }, + "requestId": "req_01HZY7R4S7S0Y73ZJ81EF1Z8Y3", + "timestamp": "2026-06-06T10:20:31Z" +} +``` + +### 创建执行请求 + +```json +{ + "operationId": "op_exec_01HZY7R4S6", + "type": "http", + "filePath": "C:/workspace/api/demo.http", + "requestSelector": { + "mode": "cursor", + "line": 18, + "column": 5 + }, + "environment": "dev", + "stream": true, + "persistHistory": true +} +``` + +### 创建压测请求 + +```json +{ + "operationId": "op_loadtest_01HZY7R4S6", + "type": "load-test", + "filePath": "C:/workspace/api/demo.http", + "requestSelector": { + "mode": "requestId", + "requestId": "req_list_users" + }, + "environment": "dev", + "stream": true, + "persistHistory": true, + "loadTest": { + "concurrency": 20, + "totalRequests": 1000, + "durationSeconds": 60, + "qpsLimit": 100, + "warmupSeconds": 5, + "stopOnErrorRate": 0.2 + } +} +``` + +### 查询执行状态响应 + +```json +{ + "success": true, + "data": { + "executionId": "exec_01HZY7R4S7M4H4QF3W2K2N4H8A", + "type": "http", + "status": "running", + "stage": "sending", + "startedAt": "2026-06-06T10:20:30Z", + "finishedAt": null, + "error": null + }, + "error": null, + "requestId": "req_01HZY7R4S7S0Y73ZJ81EF1Z8Y4", + "timestamp": "2026-06-06T10:20:30Z" +} +``` + +### WebSocket 事件 envelope + +```json +{ + "type": "execution.response.chunk", + "executionId": "exec_01HZY7R4S7M4H4QF3W2K2N4H8A", + "seq": 12, + "timestamp": "2026-06-06T10:20:31Z", + "payload": { + "contentType": "application/json", + "encoding": "utf-8", + "chunk": "{\"id\":1,\"name\":\"demo\"}", + "offset": 0, + "done": false + } +} +``` + +### 压测进度事件 + +```json +{ + "type": "execution.loadtest.progress", + "executionId": "exec_01HZY7R4S7M4H4QF3W2K2N4H8B", + "seq": 42, + "timestamp": "2026-06-06T10:20:40Z", + "payload": { + "completedRequests": 500, + "successRequests": 490, + "failedRequests": 10, + "errorRate": 0.02, + "throughput": 83.3, + "latency": { + "avgMs": 45, + "p95Ms": 120, + "p99Ms": 240 + }, + "statusCodes": { + "200": 490, + "500": 10 + } + } +} +``` + +### ExecutionResult 示例 + +```json +{ + "summary": { + "executionId": "exec_01HZY7R4S7M4H4QF3W2K2N4H8A", + "type": "http", + "status": "completed", + "startedAt": "2026-06-06T10:20:30Z", + "finishedAt": "2026-06-06T10:20:31Z", + "durationMs": 864, + "environment": "dev", + "filePath": "C:/workspace/api/demo.http", + "requestName": "List users" + }, + "request": { + "raw": "GET https://api.example.com/users\nAccept: application/json", + "resolved": { + "method": "GET", + "url": "https://api.example.com/users", + "headers": { + "Accept": "application/json" + }, + "body": null + }, + "environmentSnapshot": { + "name": "dev", + "variables": { + "baseUrl": "https://api.example.com" + } + } + }, + "response": { + "statusCode": 200, + "statusText": "OK", + "headers": { + "Content-Type": "application/json" + }, + "cookies": [], + "body": "[{\"id\":1,\"name\":\"demo\"}]", + "bodyEncoding": "utf-8", + "bodyTruncated": false, + "size": 24 + }, + "timeline": { + "dnsMs": null, + "connectMs": 18, + "tlsMs": 42, + "firstByteMs": 210, + "totalMs": 864 + }, + "assertions": [], + "diagnostics": { + "logs": [], + "warnings": [], + "error": null + } +} +``` + +### 压测结果示例 + +```json +{ + "summary": { + "executionId": "exec_01HZY7R4S7M4H4QF3W2K2N4H8B", + "type": "load-test", + "status": "completed", + "requestName": "List users", + "durationMs": 60000, + "environment": "dev" + }, + "loadTest": { + "config": { + "concurrency": 20, + "totalRequests": 1000, + "durationSeconds": 60, + "qpsLimit": 100, + "warmupSeconds": 5, + "stopOnErrorRate": 0.2 + }, + "summary": { + "totalRequests": 1000, + "successRequests": 980, + "failedRequests": 20, + "errorRate": 0.02, + "throughput": 16.3 + }, + "latency": { + "minMs": 12, + "avgMs": 45, + "p50Ms": 38, + "p90Ms": 80, + "p95Ms": 120, + "p99Ms": 240, + "maxMs": 500 + }, + "statusCodes": { + "200": 980, + "500": 20 + }, + "errors": { + "REQUEST_ERROR": 20 + } + } +} +``` + +### 文件读取响应 + +```json +{ + "success": true, + "data": { + "filePath": "C:/workspace/api/demo.http", + "workspacePath": "C:/workspace/api", + "content": "GET https://api.example.com/users\nAccept: application/json", + "lastModifiedAt": "2026-06-06T10:00:00Z", + "version": "filever_01HZY7R4S7" + }, + "error": null, + "requestId": "req_01HZY7R4S7S0Y73ZJ81EF1Z8Y4", + "timestamp": "2026-06-06T10:20:30Z" +} +``` + +### 文件保存请求 + +```json +{ + "operationId": "op_save_file_01HZY7R4S7", + "filePath": "C:/workspace/api/demo.http", + "content": "GET https://api.example.com/users\nAccept: application/json", + "baseVersion": "filever_01HZY7R4S7", + "createIfMissing": false +} +``` + +### 环境列表响应 + +```json +{ + "success": true, + "data": { + "workspacePath": "C:/workspace/api", + "defaultEnvironment": "dev", + "items": [ + { + "id": "env_dev", + "name": "dev", + "scope": "workspace", + "isDefault": true, + "updatedAt": "2026-06-06T10:00:00Z" + } + ] + }, + "error": null, + "requestId": "req_01HZY7R4S7S0Y73ZJ81EF1Z8Y5", + "timestamp": "2026-06-06T10:20:30Z" +} +``` + +### 环境保存请求 + +```json +{ + "operationId": "op_save_env_01HZY7R4S8", + "workspacePath": "C:/workspace/api", + "filePath": "C:/workspace/api/.http-client/environments.json", + "contentHash": "sha256:envhash_01HZY7R4S8", + "name": "dev", + "isDefault": true, + "variables": [ + { + "name": "baseUrl", + "value": "https://api.example.com", + "scope": "workspace" + } + ] +} +``` + +### 历史列表响应 + +```json +{ + "success": true, + "data": { + "page": 1, + "pageSize": 20, + "total": 1, + "items": [ + { + "executionId": "exec_01HZY7R4S7M4H4QF3W2K2N4H8A", + "type": "http", + "status": "completed", + "requestName": "List users", + "filePath": "C:/workspace/api/demo.http", + "environment": "dev", + "durationMs": 864, + "startedAt": "2026-06-06T10:20:30Z" + } + ] + }, + "error": null, + "requestId": "req_01HZY7R4S7S0Y73ZJ81EF1Z8Y6", + "timestamp": "2026-06-06T10:20:30Z" +} +``` + +### Mock 规则保存请求 + +```json +{ + "operationId": "op_save_mock_01HZY7R4S9", + "workspacePath": "C:/workspace/api", + "filePath": "C:/workspace/api/.http-client/mocks/users.mock.json", + "contentHash": "sha256:mockhash_01HZY7R4S9", + "name": "Mock list users", + "enabled": true, + "priority": 100, + "tags": ["users", "happy-path"], + "description": "List users success response", + "match": { + "method": "GET", + "path": "/users", + "pathMode": "exact", + "query": {}, + "headers": {}, + "bodyContains": null + }, + "response": { + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "body": "[{\"id\":1,\"name\":\"demo\"}]", + "bodyType": "json", + "delayMs": 100, + "templateEnabled": true + } +} +``` + +### Mock 规则文件保存请求 + +```json +{ + "operationId": "op_save_mock_file_01HZY7R4T0", + "workspacePath": "C:/workspace/api", + "filePath": "C:/workspace/api/.http-client/mocks/users.mock.json", + "contentHash": "sha256:mockfilehash_01HZY7R4T0", + "schemaVersion": 1, + "updatedAt": "2026-06-06T10:20:30Z", + "rules": [ + { + "id": "mockrule_users", + "name": "Mock list users", + "enabled": true, + "priority": 100, + "match": { + "method": "GET", + "path": "/users", + "pathMode": "exact" + }, + "response": { + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "body": "[{\"id\":1,\"name\":\"demo\"}]", + "bodyType": "json", + "delayMs": 100, + "templateEnabled": true + } + } + ] +} +``` + +### Mock 规则文件预览请求 + +```json +{ + "workspacePath": "C:/workspace/api", + "filePath": "C:/workspace/api/.http-client/mocks/users.mock.json", + "mode": "preview", + "rules": [ + { + "name": "Mock get user", + "enabled": true, + "priority": 200, + "match": { + "method": "GET", + "path": "/users/:id", + "pathMode": "template" + }, + "response": { + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "body": "{\"id\":\"{{path.id}}\",\"name\":\"demo\"}", + "bodyType": "json", + "delayMs": 0, + "templateEnabled": true + } + } + ] +} +``` + +### Mock 规则文件预览响应 + +```json +{ + "success": true, + "data": { + "created": 1, + "updated": 0, + "conflicted": 0, + "skipped": 0, + "conflicts": [] + }, + "error": null, + "requestId": "req_01HZY7R4S7S0Y73ZJ81EF1Z8Y9", + "timestamp": "2026-06-06T10:20:30Z" +} +``` + +### Mock Server 状态响应 + +```json +{ + "success": true, + "data": { + "running": true, + "host": "127.0.0.1", + "port": 32190, + "baseUrl": "http://127.0.0.1:32190", + "workspacePath": "C:/workspace/api", + "ruleCount": 3, + "proxyFallbackEnabled": false, + "startedAt": "2026-06-06T10:20:30Z" + }, + "error": null, + "requestId": "req_01HZY7R4S7S0Y73ZJ81EF1Z8Y7", + "timestamp": "2026-06-06T10:20:30Z" +} +``` + +### Mock 命中日志响应 + +```json +{ + "success": true, + "data": { + "page": 1, + "pageSize": 20, + "total": 1, + "items": [ + { + "id": "mockhit_01HZY7R4S7", + "ruleId": "mockrule_users", + "matched": true, + "source": "mock-hit", + "method": "GET", + "path": "/users", + "statusCode": 200, + "durationMs": 101, + "requestSummary": { + "query": {}, + "headersCount": 5, + "bodySize": 0 + }, + "requestedAt": "2026-06-06T10:20:31Z" + } + ] + }, + "error": null, + "requestId": "req_01HZY7R4S7S0Y73ZJ81EF1Z8Y8", + "timestamp": "2026-06-06T10:20:31Z" +} +``` + +### OpenAPI 导入预览响应 + +```json +{ + "success": true, + "data": { + "source": { + "id": "openapi_users", + "name": "Users API", + "sourcePath": "C:/workspace/api/openapi/users.yaml", + "openapiVersion": "3.1.0", + "title": "Users API", + "version": "1.0.0", + "operationCount": 2 + }, + "targets": { + "requestFilePath": "C:/workspace/api/generated/openapi/users.http", + "mockFilePath": "C:/workspace/api/.http-client/mocks/openapi/users.mock.json" + }, + "summary": { + "createdRequests": 2, + "updatedRequests": 0, + "skippedRequests": 0, + "createdMockRules": 2, + "schemaMappings": 2, + "unmappedFields": 1 + }, + "conflicts": [], + "unmapped": [ + { + "fieldPath": "$.components.securitySchemes.ApiKeyAuth", + "reason": "security scheme is recorded but not converted to executable auth config" + } + ] + }, + "error": null, + "requestId": "req_01HZY7R4S7S0Y73ZJ81EF1Z8Y9", + "timestamp": "2026-06-06T10:20:31Z" +} +``` + +### OpenAPI 同步预览响应 + +```json +{ + "success": true, + "data": { + "sourceId": "openapi_users", + "sourcePath": "C:/workspace/api/openapi/users.yaml", + "strategy": "skip-local-modified", + "summary": { + "created": 1, + "updated": 1, + "deleted": 0, + "skipped": 1, + "conflicted": 1, + "breakingChanges": 1 + }, + "changes": [ + { + "operationId": "listUsers", + "method": "GET", + "path": "/users", + "changeType": "updated", + "breaking": false, + "targetFilePath": "C:/workspace/api/generated/openapi/users.http" + }, + { + "operationId": "createUser", + "method": "POST", + "path": "/users", + "changeType": "updated", + "breaking": true, + "targetFilePath": "C:/workspace/api/generated/openapi/users.http" + } + ], + "conflicts": [ + { + "operationId": "createUser", + "reason": "local request block was modified after last OpenAPI sync", + "targetFilePath": "C:/workspace/api/generated/openapi/users.http", + "requestBlockId": "req_create_user" + } + ] + }, + "error": null, + "requestId": "req_01HZY7R4S7S0Y73ZJ81EF1Z8Z0", + "timestamp": "2026-06-06T10:20:31Z" +} +``` + +### OpenAPI Schema 校验响应 + +```json +{ + "success": true, + "data": { + "valid": false, + "sourceId": "openapi_users", + "operationId": "listUsers", + "statusCode": 200, + "schemaRef": "#/paths/~1users/get/responses/200/content/application~1json/schema", + "errors": [ + { + "path": "$[0].id", + "expected": "integer", + "actual": "string", + "actualSummary": "\"1\"" + } + ], + "assertion": { + "name": "OpenAPI response schema", + "passed": false, + "errorCode": "SCHEMA_VALIDATION_ERROR" + } + }, + "error": null, + "requestId": "req_01HZY7R4S7S0Y73ZJ81EF1Z8Z1", + "timestamp": "2026-06-06T10:20:31Z" +} +``` + +## 附录 C:SQLite 表结构草案 + +### `execution_history_index` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | TEXT PRIMARY KEY | `executionId` | +| `parent_id` | TEXT NULL | 父执行任务 ID | +| `type` | TEXT NOT NULL | `http` / `batch` / `chain` / `sse` / `websocket` / `load-test` / `mock-server` | +| `status` | TEXT NOT NULL | 执行状态 | +| `request_name` | TEXT NULL | 请求名称 | +| `request_id` | TEXT NULL | 请求块稳定标识 | +| `file_path` | TEXT NOT NULL | 请求文件路径 | +| `workspace_path` | TEXT NULL | 工作区根路径 | +| `environment` | TEXT NULL | 环境名称 | +| `duration_ms` | INTEGER NULL | 执行耗时 | +| `started_at` | TEXT NOT NULL | 开始时间 | +| `finished_at` | TEXT NULL | 结束时间 | +| `result_path` | TEXT NULL | 大结果落盘路径 | +| `result_json` | TEXT NULL | 小结果 JSON 快照 | +| `created_at` | TEXT NOT NULL | 创建时间 | + +索引要求:`idx_execution_history_started_at`、`idx_execution_history_file_path`、`idx_execution_history_parent_id` + +### `execution_event_index` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | INTEGER PRIMARY KEY AUTOINCREMENT | 事件索引 ID | +| `execution_id` | TEXT NOT NULL | 执行任务 ID | +| `seq` | INTEGER NOT NULL | 事件序号 | +| `type` | TEXT NOT NULL | 事件类型 | +| `timestamp` | TEXT NOT NULL | 事件时间 | +| `payload_path` | TEXT NULL | 大事件载荷落盘路径 | +| `payload_json` | TEXT NULL | 小事件载荷 JSON | + +索引要求:`idx_execution_event_execution_seq`,并保证同一 `execution_id` 下 `seq` 递增唯一 + +### `response_cache_index` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | TEXT PRIMARY KEY | 缓存记录 ID | +| `workspace_path` | TEXT NULL | 工作区路径 | +| `owner_id` | TEXT NOT NULL | 关联执行、事件或历史记录 ID | +| `kind` | TEXT NOT NULL | `response-body` / `event-payload` / `history-snapshot` | +| `file_path` | TEXT NOT NULL | 缓存文件路径 | +| `content_type` | TEXT NULL | 内容类型 | +| `size_bytes` | INTEGER NULL | 文件大小 | +| `summary_json` | TEXT NULL | 摘要 JSON | +| `content_hash` | TEXT NULL | 内容哈希 | +| `created_at` | TEXT NOT NULL | 创建时间 | +| `expires_at` | TEXT NULL | 过期时间 | + +索引要求:`idx_response_cache_owner`、`idx_response_cache_workspace_kind`、`idx_response_cache_expires_at` + +### `recent_file_cache` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | TEXT PRIMARY KEY | 记录 ID | +| `workspace_path` | TEXT NULL | 工作区路径 | +| `file_path` | TEXT NOT NULL | 文件路径 | +| `cursor_line` | INTEGER NULL | 最近光标行 | +| `cursor_column` | INTEGER NULL | 最近光标列 | +| `scroll_top` | INTEGER NULL | 最近滚动位置 | +| `last_opened_at` | TEXT NOT NULL | 最近打开时间 | + +索引要求:`idx_recent_file_cache_last_opened_at` + +### `mock_rule_index` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | TEXT PRIMARY KEY | Mock 规则 ID | +| `workspace_path` | TEXT NOT NULL | 工作区路径 | +| `file_path` | TEXT NOT NULL | Mock 规则文件路径 | +| `name` | TEXT NOT NULL | 规则名称 | +| `enabled` | INTEGER NOT NULL | 是否启用 | +| `priority` | INTEGER NOT NULL | 匹配优先级 | +| `tags_json` | TEXT NULL | 标签 JSON | +| `description` | TEXT NULL | 备注说明 | +| `match_summary_json` | TEXT NOT NULL | 匹配条件摘要 JSON | +| `response_summary_json` | TEXT NOT NULL | 响应配置摘要 JSON | +| `content_hash` | TEXT NOT NULL | 规则文件内容哈希 | +| `indexed_at` | TEXT NOT NULL | 最近索引时间 | +| `created_at` | TEXT NOT NULL | 创建时间 | +| `updated_at` | TEXT NOT NULL | 更新时间 | + +索引要求:`idx_mock_rule_index_workspace_enabled`、`idx_mock_rule_index_workspace_priority`、`idx_mock_rule_index_file_path` + +### `mock_hit_logs` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | TEXT PRIMARY KEY | 命中日志 ID | +| `workspace_path` | TEXT NOT NULL | 工作区路径 | +| `rule_id` | TEXT NULL | 命中的规则 ID,未命中时为空 | +| `matched` | INTEGER NOT NULL | 是否命中 | +| `source` | TEXT NOT NULL | 响应来源:`mock-hit`、`mock-miss`、`proxy-hit`、`proxy-error` | +| `method` | TEXT NOT NULL | 请求方法 | +| `path` | TEXT NOT NULL | 请求路径 | +| `status_code` | INTEGER NOT NULL | 响应状态码 | +| `duration_ms` | INTEGER NULL | 处理耗时 | +| `request_summary_json` | TEXT NULL | 请求摘要 JSON | +| `error_summary` | TEXT NULL | 代理或模板错误摘要 | +| `requested_at` | TEXT NOT NULL | 请求时间 | + +索引要求:`idx_mock_hit_logs_workspace_time`、`idx_mock_hit_logs_rule_id` + +### `openapi_source_index` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | TEXT PRIMARY KEY | OpenAPI 契约源 ID | +| `workspace_path` | TEXT NOT NULL | 工作区路径 | +| `name` | TEXT NOT NULL | 契约源名称 | +| `source_path` | TEXT NOT NULL | OpenAPI 源文件路径 | +| `format` | TEXT NOT NULL | `json` / `yaml` | +| `openapi_version` | TEXT NULL | OpenAPI 版本 | +| `title` | TEXT NULL | API 标题 | +| `version` | TEXT NULL | API 业务版本 | +| `operation_count` | INTEGER NOT NULL | operation 数量 | +| `content_hash` | TEXT NOT NULL | 源文件内容哈希 | +| `last_synced_at` | TEXT NULL | 最近同步时间 | +| `indexed_at` | TEXT NOT NULL | 最近索引时间 | + +索引要求:`idx_openapi_source_workspace`、`idx_openapi_source_path` + +### `openapi_operation_index` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | TEXT PRIMARY KEY | operation 索引 ID | +| `source_id` | TEXT NOT NULL | OpenAPI 契约源 ID | +| `workspace_path` | TEXT NOT NULL | 工作区路径 | +| `operation_id` | TEXT NULL | OpenAPI `operationId` | +| `method` | TEXT NOT NULL | HTTP 方法 | +| `path` | TEXT NOT NULL | OpenAPI 路径 | +| `tags_json` | TEXT NULL | 标签 JSON | +| `summary` | TEXT NULL | operation 摘要 | +| `request_file_path` | TEXT NULL | 生成的 `.http` 文件路径 | +| `request_block_id` | TEXT NULL | 生成的请求块 ID | +| `mock_rule_id` | TEXT NULL | 生成的 Mock 规则 ID | +| `schema_ref` | TEXT NULL | 响应 Schema 引用 | +| `content_hash` | TEXT NOT NULL | operation 结构摘要哈希 | +| `indexed_at` | TEXT NOT NULL | 最近索引时间 | + +索引要求:`idx_openapi_operation_source`、`idx_openapi_operation_workspace_method_path`、`idx_openapi_operation_operation_id` + +### `schema_migrations` + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `version` | TEXT PRIMARY KEY | 迁移版本 | +| `name` | TEXT NOT NULL | 迁移名称 | +| `applied_at` | TEXT NOT NULL | 执行时间 | +| `checksum` | TEXT NULL | 迁移文件校验值 | + +### 落盘文件命名规则 + +* [ ] 工作区环境变量文件默认路径格式:`.http-client/environments.json` + +* [ ] 工作区项目配置文件默认路径格式:`.http-client/settings.json` + +* [ ] `.http-client/environments.json`、`.http-client/settings.json`、`.http-client/mocks/**/*.mock.json`、`.http-client/openapi/sources.json` 建议纳入 Git 管理 + +* [ ] SQLite 缓存数据库、响应缓存、事件缓存、命中日志、诊断包默认位于应用数据目录,不建议纳入 Git 管理 + +* [ ] 应用可提供推荐 `.gitignore` 片段,覆盖用户显式选择将缓存目录放在工作区内的情况 + +* [ ] 响应体落盘路径格式:`{appData}/responses/{workspaceHash}/{yyyyMMdd}/{executionId}-{part}.body` + +* [ ] WebSocket / SSE 大事件载荷落盘路径格式:`{appData}/events/{workspaceHash}/{yyyyMMdd}/{executionId}-{seq}.json` + +* [ ] Mock 规则文件默认路径格式:`.http-client/mocks/{mockFileName}.mock.json` + +* [ ] OpenAPI 源映射文件默认路径格式:`.http-client/openapi/sources.json` + +* [ ] OpenAPI 源文件建议保存在工作区可见目录,例如 `openapi/{sourceName}.yaml` 或 `docs/openapi/{sourceName}.json` + +* [ ] OpenAPI 生成的 `.http` 请求模板默认路径格式:`generated/openapi/{sourceName}.http` + +* [ ] OpenAPI 生成的 Mock 规则文件默认路径格式:`.http-client/mocks/openapi/{sourceName}.mock.json` + +* [ ] OpenAPI Schema 校验映射默认随 `.http` 请求块来源标记保存,不额外生成 SQLite 事实源数据 + +* [ ] Mock 二进制响应体落盘路径格式:`.http-client/mocks/assets/{mockRuleId}.body` + +* [ ] `.http-client/mocks/assets/` 仅用于 Mock 规则引用的静态响应体,不用于普通执行响应缓存或压测采样缓存 + +* [ ] Mock 规则文件需包含 `schemaVersion`、`rules`、`updatedAt` 字段 + +* [ ] Mock 规则文件加载时需按 `schemaVersion` 进行兼容处理 + +* [ ] Mock 规则 ID 默认由规则文件相对路径、HTTP 方法、路径匹配表达式和创建时间随机后缀生成,用户修改规则名称不应改变 ID + +* [ ] 从请求生成 Mock 规则时,若目标文件中已存在同方法同路径规则,默认创建新 ID 并提示可能冲突;用户可选择覆盖已有规则 + +* [ ] 复制 Mock 规则必须生成新 ID,避免跨文件或同文件重复 ID + +* [ ] 工作区项目配置文件需保存 Mock 代理回退、Mock 命中日志保留策略、默认环境、历史保留策略等配置 + +* [ ] Mock 命中日志保留策略需写入工作区项目配置文件,默认值为最近 `10000` 条 / `7d` + +* [ ] 诊断包导出路径格式:`{appData}/diagnostics/diagnostic-{yyyyMMdd-HHmmss}.zip` + +* [ ] SQLite 缓存备份文件格式:`{appData}/backups/cache-{schemaVersion}-{yyyyMMdd-HHmmss}.db` + +推荐 `.gitignore` 片段: + +```gitignore +# HTTP Client local cache +http-client-cache/ +http-client-responses/ +http-client-events/ +http-client-logs/ +http-client-diagnostics/ +http-client-backups/ +*.db +*.db-* +``` + +### 工作区 JSON 文件示例 + +`.http-client/environments.json` 示例: + +```json +{ + "schemaVersion": 1, + "defaultEnvironment": "dev", + "environments": [ + { + "name": "dev", + "variables": { + "baseUrl": "https://api.example.com" + } + } + ], + "globals": {} +} +``` + +`.http-client/settings.json` 示例: + +```json +{ + "schemaVersion": 1, + "defaultEnvironment": "dev", + "history": { + "retentionMaxCount": 1000 + }, + "mock": { + "proxyFallback": { + "enabled": false, + "upstreamBaseUrl": null + }, + "hitLog": { + "retentionMaxCount": 10000, + "retentionDays": 7 + } + } +} +``` + +`.http-client/mocks/users.mock.json` 示例: + +```json +{ + "schemaVersion": 1, + "updatedAt": "2026-06-06T10:20:30Z", + "rules": [ + { + "id": "mockrule_users", + "name": "Mock list users", + "enabled": true, + "priority": 100, + "match": { + "method": "GET", + "path": "/users", + "pathMode": "exact" + }, + "response": { + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyType": "json", + "body": "[{\"id\":1,\"name\":\"demo\"}]", + "delayMs": 100, + "templateEnabled": false + } + } + ] +} +``` + +`.http-client/openapi/sources.json` 示例: + +```json +{ + "schemaVersion": 1, + "sources": [ + { + "id": "openapi_users", + "name": "Users API", + "sourcePath": "openapi/users.yaml", + "format": "yaml", + "openapiVersion": "3.1.0", + "title": "Users API", + "version": "1.0.0", + "contentHash": "sha256:openapihash_01HZY7R4T1", + "lastSyncedAt": "2026-06-06T10:20:30Z", + "targets": { + "requestFilePath": "generated/openapi/users.http", + "mockFilePath": ".http-client/mocks/openapi/users.mock.json" + }, + "sync": { + "strategy": "skip-local-modified", + "generateRequests": true, + "generateMocks": true, + "enableSchemaValidation": false, + "baseUrlVariable": "baseUrl" + } + } + ] +} +``` + +### 迁移要求 + +* [ ] SQLite 缓存数据库需维护 `schema_migrations` 表,记录已执行迁移版本 + +* [ ] 每次版本升级前需备份 SQLite 缓存数据库文件;工作区 JSON 配置文件按普通文件迁移与 Git 管理处理 + +* [ ] SQLite 缓存数据库迁移失败时需保留原数据库文件,并提示用户查看日志 + +* [ ] 工作区 JSON 文件迁移需基于 `schemaVersion` 执行,低版本文件可自动迁移,高版本文件默认只读或拒绝写入 + +* [ ] 工作区 JSON 文件迁移前需在同目录生成备份文件,命名格式为 `{fileName}.bak-{yyyyMMdd-HHmmss}` + +* [ ] 工作区 JSON 文件迁移失败时不得覆盖原文件,并需提示用户查看迁移错误详情 + +* [ ] SQLite 缓存迁移失败时允许删除并重建索引;删除前应尽量备份原缓存数据库 + +### 测试 Fixture 目录结构 + +* [ ] 兼容性用例目录:`compat/{category}/{case}.http` + +* [ ] 工作区夹具目录:`fixtures/workspaces/{caseName}/` + +* [ ] API 示例快照目录:`fixtures/api-snapshots/{apiName}.json` + +* [ ] 解析器 golden 快照目录:`fixtures/parser-golden/{caseName}.json` + +* [ ] Mock 规则夹具目录:`fixtures/workspaces/{caseName}/.http-client/mocks/*.mock.json` + +* [ ] OpenAPI 契约夹具目录:`fixtures/workspaces/{caseName}/openapi/*.yaml` + +* [ ] OpenAPI 源映射夹具目录:`fixtures/workspaces/{caseName}/.http-client/openapi/sources.json` + +* [ ] OpenAPI 生成请求夹具目录:`fixtures/workspaces/{caseName}/generated/openapi/*.http` + +* [ ] 测试快照需纳入版本管理,运行时生成的响应缓存、日志、SQLite 缓存不得写入 fixture 目录 + +## MVP 任务拆分 + +### 后端 MVP 任务 + +* [ ] `MVP-BE-01`:完成本地服务启动、端口探测、单实例复用、静态资源托管和运行时元信息文件 + +* [ ] `MVP-BE-02`:完成 `workspace` 与 `filestore`,支持工作区打开、路径解析、文件读取、原子保存、`contentHash` 和外部变更检测 + +* [ ] `MVP-BE-03`:完成 `.http` / `.rest` MVP 解析器,输出稳定 AST、请求块列表、请求命名、注释、错误位置和 parser golden 快照 + +* [ ] `MVP-BE-04`:完成环境变量文件 `.http-client/environments.json` 的读取、保存、默认环境、变量优先级和未定义变量错误 + +* [ ] `MVP-BE-05`:完成 REST API envelope、错误码映射、分页模型、内部 OpenAPI 契约源和前端类型生成输入 + +* [ ] `MVP-BE-06`:完成 `eventbus` MVP,支持事件发布、WebSocket 订阅、`seq`、最终事件和页面刷新后的最终结果快照补拉 + +* [ ] `MVP-BE-07`:完成 `Execution` MVP 状态机,支持普通 HTTP 请求执行、取消、超时、响应 meta/body、结果固化 + +* [ ] `MVP-BE-08`:完成 JavaScript 前置脚本、后置脚本、基础断言、脚本日志和脚本超时 + +* [ ] `MVP-BE-09`:完成 SQLite `execution_history_index`、`execution_event_index`、`response_cache_index`、`recent_file_cache` 的初始化、写入和重建入口 + +* [ ] `MVP-BE-10`:完成后端测试基线,包括 parser golden tests、execution integration tests、API handler tests、script runtime tests + +### 前端 MVP 任务 + +* [ ] `MVP-FE-01`:完成应用 shell、三栏布局、路由、全局状态和基础错误提示 + +* [ ] `MVP-FE-02`:完成统一 API client、WebSocket event client、错误 envelope 处理、事件去重和断线重连 + +* [ ] `MVP-FE-03`:完成文件树、最近文件、文件打开、文件保存、外部变更提示和 Monaco Editor 集成 + +* [ ] `MVP-FE-04`:完成当前请求定位、请求块高亮、执行当前请求、取消执行和执行状态展示 + +* [ ] `MVP-FE-05`:完成环境切换、变量预览、环境列表、环境变量编辑和默认环境设置 + +* [ ] `MVP-FE-06`:完成响应查看器 MVP,支持 Body、Headers、Cookies、Timeline、Logs、Raw / Pretty 展示和大响应截断提示 + +* [ ] `MVP-FE-07`:完成历史列表、历史详情、重新执行、打开源文件和定位请求块 + +* [ ] `MVP-FE-08`:完成托盘相关前端入口,包括打开页面、退出服务、打开数据目录的状态展示 + +* [ ] `MVP-FE-09`:完成前端测试基线,包括编辑器、响应区、环境管理、历史详情和主流程 E2E + +### MVP 纵向验收切片 + +* [ ] `MVP-VS-01`:打开工作区 -> 扫描 `.http` 文件 -> 展示请求列表 -> 定位当前请求 + +* [ ] `MVP-VS-02`:编辑请求 -> 保存文件 -> 执行当前请求 -> WebSocket 增量展示响应 -> 固化历史 + +* [ ] `MVP-VS-03`:创建环境 -> 切换环境 -> 变量预览刷新 -> 使用环境变量执行请求 + +* [ ] `MVP-VS-04`:执行带前置脚本、后置脚本和断言的请求 -> 展示脚本日志和断言结果 + +* [ ] `MVP-VS-05`:取消长耗时请求 -> 状态进入 `cancelled` -> 历史中保留取消摘要 + +* [ ] `MVP-VS-06`:页面刷新或 WebSocket 断开重连 -> 根据 `executionId` 恢复最终状态或结果快照 + +* [ ] `MVP-VS-07`:外部修改 `.http` 或环境文件 -> 应用提示变更 -> 用户重新加载后索引更新 + +## 实施步骤 + +### 阶段 0:项目骨架 + +* [ ] 初始化 Go module 与后端目录结构 + +* [ ] 初始化 Vue 3 + TypeScript + Vite 前端项目 + +* [ ] 建立前后端开发命令、构建命令、测试命令 + +* [ ] 实现本地服务启动、默认端口、端口探测、静态资源托管 + +* [ ] 建立 SQLite 索引/缓存初始化、`schema_migrations`、数据目录结构 + +### 阶段 1:MVP 后端 + +* [ ] 实现 `.http` / `.rest` 基础解析、多请求块、请求命名、注释、错误定位 + +* [ ] 实现变量解析、MVP 变量优先级、未定义变量错误 + +* [ ] 实现本地工作区文件读取、保存、最近文件、外部变更检测 + +* [ ] 实现 `Execution` 状态机、REST 控制面、WebSocket 事件通道 + +* [ ] 实现单请求执行、取消、超时、响应结果固化 + +* [ ] 实现请求历史索引、响应缓存索引、环境变量文件存储、项目配置文件存储 + +* [ ] 实现 JavaScript 前置脚本、后置脚本、基础断言 + +### 阶段 2:MVP 前端 + +* [ ] 实现主界面三栏布局、文件树、Monaco Editor、响应区标签页 + +* [ ] 实现当前请求定位、当前请求高亮、执行当前请求 + +* [ ] 实现变量预览、环境切换、错误行列定位 + +* [ ] 实现响应 Body / Headers / Cookies / Timeline / Logs 展示 + +* [ ] 实现请求取消、执行状态展示、WebSocket 事件增量渲染 + +* [ ] 实现历史列表、历史详情、重新执行 + +* [ ] 实现托盘打开页面、退出服务、打开数据目录 + +### 阶段 3:MVP 验收 + +* [ ] 建立 MVP 兼容用例集并接入回归测试 + +* [ ] 完成 parser golden tests、executor integration tests、API handler tests + +* [ ] 完成前端核心组件测试与主流程 E2E 测试 + +* [ ] 验收文件打开、请求执行、取消、历史回看、环境切换、状态恢复主流程 + +### 阶段 4:产品目标版本增强 + +* [ ] 实现 Form URL Encoded、Multipart、二进制 Body、外部文件引用 + +* [ ] 实现 Cookie 管理、重定向链、代理、SSL/TLS、客户端证书 + +* [ ] 实现批量执行、链式执行、父子任务历史展示 + +* [ ] 实现 SSE 与 WebSocket 会话视图、消息发送、摘要存储 + +* [ ] 实现轻量压测执行、实时统计、保护阈值、历史报告 + +* [ ] 实现本地 Mock Server、Mock 规则管理、规则优先级、代理回退、命中日志、规则文件保存/重载与从请求/响应生成 Mock + +* [ ] 实现 curl 导出、Postman collection 导入、OpenAPI 导入与同步 + +* [ ] 实现 OpenAPI diff、生成 Mock 骨架和响应 Schema 校验 + +* [ ] 完善兼容矩阵完整用例集与产品目标版本回归测试 + +### 阶段 5:打包发布 + +* [ ] 完成 Windows / macOS / Linux 打包产物 + +* [ ] 完成 SQLite 缓存数据库迁移、工作区 JSON schema 迁移、升级备份、失败回滚验证 + +* [ ] 完成日志目录、诊断包导出、异常退出提示 + +* [ ] 完成平台差异清单与发布说明 + +## .http 文件语法示例 + +``` +### GET 请求示例 +GET https://api.example.com/users +Accept: application/json + +### + +### POST 请求示例 +POST https://api.example.com/users +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "John Doe", + "email": "john@example.com" +} + +### + +### PUT 请求示例 +PUT https://api.example.com/users/1 +Content-Type: application/json + +{ + "name": "Jane Doe" +} + +### + +### 签名请求示例 +GET https://api.example.com/orders +Accept: application/json + +< {% + const secret = vars.get("secret"); + const timestamp = Date.now().toString(); + const req = request.get(); + const payload = `${req.method}\n${req.url}\n${timestamp}`; + const signature = crypto.hmacSha256Hex(secret, payload); + + request.setHeader("X-Timestamp", timestamp); + request.setHeader("X-Signature", signature); +%} +``` + +## 系统托盘功能要求 + +* 显示应用图标 + +* 单击打开应用页面 + +* 右键菜单包含: + + * 打开应用页面 + + * 打开数据目录 + + * 分隔线 + + * 退出 diff --git a/scripts/verify-docs.ps1 b/scripts/verify-docs.ps1 new file mode 100644 index 0000000..21691cf --- /dev/null +++ b/scripts/verify-docs.ps1 @@ -0,0 +1,171 @@ +param( + [string]$DocumentPath = (Join-Path $PSScriptRoot "..\http-client-app-plan.md"), + [string[]]$DisabledKeywords = @( + "<<<<<<<", + ">>>>>>>", + "TODO_BLOCKER", + "FIXME_BLOCKER" + ) +) + +$ErrorActionPreference = "Stop" + +$resolvedDocument = Resolve-Path -LiteralPath $DocumentPath +$python = Get-Command python -ErrorAction SilentlyContinue +$usePyLauncher = $false + +if (-not $python) { + $python = Get-Command py -ErrorAction SilentlyContinue + $usePyLauncher = $true +} + +if (-not $python) { + Write-Error "Python was not found. Install Python 3 or make the 'python'/'py' command available on PATH." +} + +$checker = @' +import argparse +import json +import re +import sys +from pathlib import Path + + +def line_col(text, offset): + line = text.count("\n", 0, offset) + 1 + last_newline = text.rfind("\n", 0, offset) + column = offset + 1 if last_newline == -1 else offset - last_newline + return line, column + + +def check_code_fences(text): + errors = [] + fence_pattern = re.compile(r"^[ \t]*```", re.MULTILINE) + matches = list(fence_pattern.finditer(text)) + if len(matches) % 2 != 0: + line, _ = line_col(text, matches[-1].start()) + errors.append(f"Unbalanced fenced code block near line {line}.") + return errors + + +def check_json_blocks(text): + errors = [] + block_pattern = re.compile(r"^[ \t]*```json[^\n]*\n(.*?)(?:\n^[ \t]*```[ \t]*$)", re.MULTILINE | re.DOTALL) + for index, match in enumerate(block_pattern.finditer(text), start=1): + raw_json = match.group(1) + try: + json.loads(raw_json) + except json.JSONDecodeError as exc: + start_line, _ = line_col(text, match.start(1)) + errors.append( + f"Invalid JSON code block #{index} at line {start_line + exc.lineno - 1}, " + f"column {exc.colno}: {exc.msg}." + ) + return errors + + +def is_table_separator(line): + stripped = line.strip() + if "|" not in stripped: + return False + cells = [cell.strip() for cell in stripped.strip("|").split("|")] + return bool(cells) and all(re.fullmatch(r":?-{3,}:?", cell or "") for cell in cells) + + +def pipe_count(line): + stripped = line.strip() + return stripped.count("|") + + +def check_tables(text): + errors = [] + lines = text.splitlines() + in_fence = False + for index, line in enumerate(lines): + if re.match(r"^[ \t]*```", line): + in_fence = not in_fence + continue + if in_fence or not is_table_separator(line): + continue + + table_lines = [] + cursor = index - 1 + while cursor >= 0 and "|" in lines[cursor]: + table_lines.insert(0, (cursor + 1, lines[cursor])) + cursor -= 1 + table_lines.append((index + 1, line)) + cursor = index + 1 + while cursor < len(lines) and "|" in lines[cursor]: + table_lines.append((cursor + 1, lines[cursor])) + cursor += 1 + + counts = {pipe_count(table_line) for _, table_line in table_lines} + if len(counts) > 1: + first_line = table_lines[0][0] + errors.append(f"Markdown table pipe count mismatch near line {first_line}.") + return errors + + +def check_disabled_keywords(text, keywords): + errors = [] + for keyword in keywords: + if not keyword: + continue + offset = text.find(keyword) + if offset != -1: + line, column = line_col(text, offset) + errors.append(f"Disabled keyword '{keyword}' found at line {line}, column {column}.") + return errors + + +def main(): + parser = argparse.ArgumentParser(description="Verify Markdown planning document structure.") + parser.add_argument("document") + parser.add_argument("--keyword", action="append", default=[]) + args = parser.parse_args() + + document = Path(args.document) + text = document.read_text(encoding="utf-8-sig") + + errors = [] + errors.extend(check_code_fences(text)) + errors.extend(check_json_blocks(text)) + errors.extend(check_tables(text)) + errors.extend(check_disabled_keywords(text, args.keyword)) + + if errors: + print(f"Documentation verification failed for {document}:") + for error in errors: + print(f" - {error}") + return 1 + + print(f"Documentation verification passed for {document}.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) +'@ + +$tempChecker = Join-Path ([System.IO.Path]::GetTempPath()) ("verify-docs-{0}.py" -f ([System.Guid]::NewGuid().ToString("N"))) + +try { + Set-Content -LiteralPath $tempChecker -Value $checker -Encoding UTF8 + + $arguments = @() + if ($usePyLauncher) { + $arguments += "-3" + } + $arguments += @($tempChecker, $resolvedDocument.Path) + foreach ($keyword in $DisabledKeywords) { + $arguments += @("--keyword", $keyword) + } + + & $python.Source @arguments + exit $LASTEXITCODE +} +finally { + if (Test-Path -LiteralPath $tempChecker) { + Remove-Item -LiteralPath $tempChecker -Force + } +}