diff --git a/README.md b/README.md index 6344e0ac8d..7d7139099b 100644 --- a/README.md +++ b/README.md @@ -69,31 +69,32 @@ builders. ## What you can do with Onlook: - [x] Create Next.js app in seconds - - [x] Start from text or image - - [ ] Use prebuilt templates - - [ ] Import from Figma - - [ ] Start from GitHub repo + - [x] Start from text or image + - [ ] Use prebuilt templates + - [ ] Import from Figma + - [ ] Start from GitHub repo - [x] Visually edit your app - - [x] Use Figma-like UI - - [x] Preview your app in real-time - - [x] Manage brand assets and tokens - - [x] Create and navigate to Pages - - [x] Browse layers - - [x] Manage project Images - - [ ] Detect and use Components – _Previously in - [Onlook Desktop](https://github.com/onlook-dev/desktop)_ + - [x] Use Figma-like UI + - [x] Preview your app in real-time + - [x] Manage brand assets and tokens + - [x] Create and navigate to Pages + - [x] Browse layers + - [x] Manage project Images + - [ ] Detect and use Components – _Previously in + [Onlook Desktop](https://github.com/onlook-dev/desktop)_ - [x] Development Tools - - [x] Real-time code editor - - [x] Save and restore from checkpoints - - [x] Run commands via CLI - - [x] Connect with app marketplace + + - [x] Real-time code editor + - [x] Save and restore from checkpoints + - [x] Run commands via CLI + - [x] Connect with app marketplace - [x] Deploy your app in seconds - - [x] Generate sharable links - - [x] Link your custom domain + - [x] Generate sharable links + - [x] Link your custom domain - [ ] Collaborate with your team - - [ ] Real-time editing - - [ ] Leave comments + - [ ] Real-time editing + - [ ] Leave comments ![Onlook-GitHub-Example](https://github.com/user-attachments/assets/642de37a-72cc-4056-8eb7-8eb42714cdc4) @@ -182,6 +183,7 @@ For a full walkthrough, check out our #### Sandbox and hosting - [CodeSandboxSDK](https://codesandbox.io/docs/sdk) - Dev sandbox +- [E2B](https://e2b.dev/docs) - Dev sandbox - [Freestyle](https://www.freestyle.sh/) - Hosting #### Runtime diff --git a/apps/backend/supabase/config.toml b/apps/backend/supabase/config.toml index b5438ff440..121f4a961b 100644 --- a/apps/backend/supabase/config.toml +++ b/apps/backend/supabase/config.toml @@ -10,6 +10,8 @@ max_rows = 100 [auth] site_url = "https://onlook.com" additional_redirect_urls = [ + "https://onlook.internal", + "https://onlook.internal/auth/callback", "http://localhost:3000", "http://localhost:3000/auth/callback", ] diff --git a/apps/coderouter/.env.example b/apps/coderouter/.env.example new file mode 100644 index 0000000000..88f96942a7 --- /dev/null +++ b/apps/coderouter/.env.example @@ -0,0 +1,12 @@ +# Prefix the API paths +URL_PATH_PREFIX="/coderouter" + +# Do not share this secret key - used to generate JWT token used in the client +JWT_SECRET_KEY="Replace this with your own secret" + +# Insert your custom API key here and store in your other backend as well. +# It'll be used for server-to-server authentication. +CODEROUTER_API_KEY="" + +E2B_API_KEY="" +CSB_API_KEY="" \ No newline at end of file diff --git a/apps/coderouter/.gitignore b/apps/coderouter/.gitignore new file mode 100644 index 0000000000..1dcef2d9f2 --- /dev/null +++ b/apps/coderouter/.gitignore @@ -0,0 +1,2 @@ +node_modules +.env \ No newline at end of file diff --git a/apps/coderouter/Dockerfile.dev b/apps/coderouter/Dockerfile.dev new file mode 100644 index 0000000000..5907adfafe --- /dev/null +++ b/apps/coderouter/Dockerfile.dev @@ -0,0 +1,14 @@ +FROM alpine:3.21.3 + +WORKDIR /app + +COPY . . + +# ENV CARGO_HOME=/project/etc/cargo +# ENV RUSTUP_HOME=/project/etc/rustup +ENV BUN_INSTALL=/usr/local/bin/bun + +RUN apk add curl bash build-base +RUN curl -fsSL https://bun.sh/install | bash -s "bun-v1.2.5" + +CMD ["/usr/local/bin/bun/bin/bun", "dev"] diff --git a/apps/coderouter/README.md b/apps/coderouter/README.md new file mode 100644 index 0000000000..0cd8b600d4 --- /dev/null +++ b/apps/coderouter/README.md @@ -0,0 +1,53 @@ +YOOOOOBLAHHHHHH + +# Bun + TypeScript API Starter (Hono + Drizzle) + +A batteries-included starter for building an API with **Bun**, **TypeScript**, **Hono**, and **Drizzle ORM**. +It includes: multi-database support (Postgres/MySQL/SQLite) with migrations, unit + functional tests, +OpenAPI generation, Swagger UI, TypeDoc, and a top-tier GitHub toolchain (Actions, Codecov, Renovate, Biome, Dependabot, GitHub Pages). + +## Quickstart + +```bash +bun install +cp .env.example .env +# choose a DB (sqlite by default) +bun db:generate # generate SQL from schema +bun db:migrate # run migrations +bun dev # start the API on http://localhost:3000 +``` + +OpenAPI: http://localhost:3000/openapi.json +Docs (Swagger UI): http://localhost:3000/docs + +### Scripts + +- `bun dev` — run in watch mode +- `bun start` — production start +- `bun test` — run all tests with coverage +- `bun lint` — Biome lint/format check +- `bun fmt` — format files with Biome +- `bun db:generate` — generate migrations via drizzle-kit +- `bun db:migrate` — apply migrations +- `bun openapi` — regenerate OpenAPI JSON +- `bun docs` — build TypeDoc to `site/typedoc` + +### Multi-DB + +Set `DRIZZLE_DB` to `postgres` | `mysql` | `sqlite` and provide `DATABASE_URL` accordingly. +SQLite works out-of-the-box (`DATABASE_URL=file:./dev.sqlite`). + +### GitHub Pages (Docs) + +The `pages.yml` workflow builds and deploys: + +- `/openapi.json` -> `/site/api/openapi.json` +- Swagger UI -> `/site/api/` +- TypeDoc -> `/site/typedoc/` + A small `site/index.html` links to both. + +Enable Pages in **Settings → Pages** (source: GitHub Actions). + +## License + +MIT diff --git a/apps/coderouter/biome.json b/apps/coderouter/biome.json new file mode 100644 index 0000000000..8bbe87c895 --- /dev/null +++ b/apps/coderouter/biome.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.1.4/schema.json", + "formatter": { "enabled": true, "formatWithErrors": true }, + "linter": { "enabled": true } +} diff --git a/apps/coderouter/bun.lock b/apps/coderouter/bun.lock new file mode 100644 index 0000000000..9c6855bd8b --- /dev/null +++ b/apps/coderouter/bun.lock @@ -0,0 +1,708 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "coderouter", + "dependencies": { + "@codesandbox/sdk": "^1.1.6", + "@e2b/code-interpreter": "2.0.0", + "@hono/zod-openapi": "^1.1.0", + "@hono/zod-validator": "^0.2.1", + "@types/jsonwebtoken": "^9.0.10", + "better-sqlite3": "^9.4.3", + "dotenv": "^16.4.5", + "drizzle-kit": "^0.27.0", + "drizzle-orm": "^0.33.0", + "hono": "^4.9.2", + "jsonwebtoken": "^9.0.2", + "mysql2": "^3.9.7", + "pg": "^8.11.5", + "uuid": "^12.0.0", + "zod": "^4.0.17", + }, + "devDependencies": { + "@biomejs/biome": "^2.1.4", + "@types/node": "^22.5.0", + "bun-types": "^1.2.20", + "supertest": "^7.0.0", + "typedoc": "^0.26.6", + "undici": "^6.19.8", + }, + }, + }, + "packages": { + "@asteasolutions/zod-to-openapi": ["@asteasolutions/zod-to-openapi@8.1.0", "", { "dependencies": { "openapi3-ts": "^4.1.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-tQFxVs05J/6QXXqIzj6rTRk3nj1HFs4pe+uThwE95jL5II2JfpVXkK+CqkO7aT0Do5AYqO6LDrKpleLUFXgY+g=="], + + "@biomejs/biome": ["@biomejs/biome@2.2.3", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.3", "@biomejs/cli-darwin-x64": "2.2.3", "@biomejs/cli-linux-arm64": "2.2.3", "@biomejs/cli-linux-arm64-musl": "2.2.3", "@biomejs/cli-linux-x64": "2.2.3", "@biomejs/cli-linux-x64-musl": "2.2.3", "@biomejs/cli-win32-arm64": "2.2.3", "@biomejs/cli-win32-x64": "2.2.3" }, "bin": { "biome": "bin/biome" } }, "sha512-9w0uMTvPrIdvUrxazZ42Ib7t8Y2yoGLKLdNne93RLICmaHw7mcLv4PPb5LvZLJF3141gQHiCColOh/v6VWlWmg=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.2.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-OrqQVBpadB5eqzinXN4+Q6honBz+tTlKVCsbEuEpljK8ASSItzIRZUA02mTikl3H/1nO2BMPFiJ0nkEZNy3B1w=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.2.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-OCdBpb1TmyfsTgBAM1kPMXyYKTohQ48WpiN9tkt9xvU6gKVKHY4oVwteBebiOqyfyzCNaSiuKIPjmHjUZ2ZNMg=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-g/Uta2DqYpECxG+vUmTAmUKlVhnGEcY7DXWgKP8ruLRa8Si1QHsWknPY3B/wCo0KgYiFIOAZ9hjsHfNb9L85+g=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-q3w9jJ6JFPZPeqyvwwPeaiS/6NEszZ+pXKF+IczNo8Xj6fsii45a4gEEicKyKIytalV+s829ACZujQlXAiVLBQ=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-LEtyYL1fJsvw35CxrbQ0gZoxOG3oZsAjzfRdvRBRHxOpQ91Q5doRVjvWW/wepgSdgk5hlaNzfeqpyGmfSD0Eyw=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-y76Dn4vkP1sMRGPFlNc+OTETBhGPJ90jY3il6jAfur8XWrYBQV3swZ1Jo0R2g+JpOeeoA0cOwM7mJG6svDz79w=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.2.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-Ms9zFYzjcJK7LV+AOMYnjN3pV3xL8Prxf9aWdDVL74onLn5kcvZ1ZMQswE5XHtnd/r/0bnUd928Rpbs14BzVmA=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.3", "", { "os": "win32", "cpu": "x64" }, "sha512-gvCpewE7mBwBIpqk1YrUqNR4mCiyJm6UI3YWQQXkedSSEwzRdodRpaKhbdbHw1/hmTWOVXQ+Eih5Qctf4TCVOQ=="], + + "@bufbuild/protobuf": ["@bufbuild/protobuf@2.7.0", "", {}, "sha512-qn6tAIZEw5i/wiESBF4nQxZkl86aY4KoO0IkUa2Lh+rya64oTOdJQFlZuMwI1Qz9VBJQrQC4QlSA2DNek5gCOA=="], + + "@codesandbox/sdk": ["@codesandbox/sdk@1.1.6", "", { "dependencies": { "ora": "^8.2.0", "readline": "^1.3.0" }, "bin": { "csb": "dist/bin/codesandbox.cjs" } }, "sha512-65k6TZSr1c0u8xYjBsz32xhMEq1TwHbqRONJ8h6e3re01qylZ5BDtAU6Nnxr9GQmmhOA0wl+3c4h4zY3/HdHHg=="], + + "@connectrpc/connect": ["@connectrpc/connect@2.0.0-rc.3", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.2.0" } }, "sha512-ARBt64yEyKbanyRETTjcjJuHr2YXorzQo0etyS5+P6oSeW8xEuzajA9g+zDnMcj1hlX2dQE93foIWQGfpru7gQ=="], + + "@connectrpc/connect-web": ["@connectrpc/connect-web@2.0.0-rc.3", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.2.0", "@connectrpc/connect": "2.0.0-rc.3" } }, "sha512-w88P8Lsn5CCsA7MFRl2e6oLY4J/5toiNtJns/YJrlyQaWOy3RO8pDgkz+iIkG98RPMhj2thuBvsd3Cn4DKKCkw=="], + + "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], + + "@e2b/code-interpreter": ["@e2b/code-interpreter@2.0.0", "", { "dependencies": { "e2b": "^2.0.1" } }, "sha512-rCIW4dV544sUx2YQB/hhwDrK6sQzIb5lr5h/1CIVoOIRgU9q3NUxsFj+2OsgWd4rMG8l6b/oA7FVZJwP4sGX/A=="], + + "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="], + + "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.19.12", "", { "os": "android", "cpu": "arm64" }, "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.19.12", "", { "os": "android", "cpu": "x64" }, "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.19.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.19.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.19.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.19.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.19.12", "", { "os": "linux", "cpu": "arm" }, "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.19.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.19.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.19.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.19.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.19.12", "", { "os": "linux", "cpu": "x64" }, "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.19.12", "", { "os": "none", "cpu": "x64" }, "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.19.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.19.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.19.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.19.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="], + + "@hono/zod-openapi": ["@hono/zod-openapi@1.1.0", "", { "dependencies": { "@asteasolutions/zod-to-openapi": "^8.1.0", "@hono/zod-validator": "^0.7.2", "openapi3-ts": "^4.5.0" }, "peerDependencies": { "hono": ">=4.3.6", "zod": "^4.0.0" } }, "sha512-S4jVR+A/jI4MA/RKJqmpjdHAN2l/EsqLnKHBv68x3WxV1NGVe3Sh7f6LV6rHEGYNHfiqpD75664A/erc+r9dQA=="], + + "@hono/zod-validator": ["@hono/zod-validator@0.2.2", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.19.1" } }, "sha512-dSDxaPV70Py8wuIU2QNpoVEIOSzSXZ/6/B/h4xA7eOMz7+AarKTSGV8E6QwrdcCbBLkpqfJ4Q2TmBO0eP1tCBQ=="], + + "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], + + "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], + + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "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" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + + "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], + + "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + + "@paralleldrive/cuid2": ["@paralleldrive/cuid2@2.2.2", "", { "dependencies": { "@noble/hashes": "^1.1.5" } }, "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA=="], + + "@shikijs/core": ["@shikijs/core@1.29.2", "", { "dependencies": { "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@1.29.2", "", { "dependencies": { "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "oniguruma-to-es": "^2.2.0" } }, "sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@1.29.2", "", { "dependencies": { "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1" } }, "sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA=="], + + "@shikijs/langs": ["@shikijs/langs@1.29.2", "", { "dependencies": { "@shikijs/types": "1.29.2" } }, "sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ=="], + + "@shikijs/themes": ["@shikijs/themes@1.29.2", "", { "dependencies": { "@shikijs/types": "1.29.2" } }, "sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g=="], + + "@shikijs/types": ["@shikijs/types@1.29.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + + "@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.10", "", { "dependencies": { "@types/ms": "*", "@types/node": "*" } }, "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + + "@types/node": ["@types/node@22.18.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-rzSDyhn4cYznVG+PCzGe1lwuMYJrcBS1fc3JqSa2PvtABwWo+dZ1ij5OVok3tqfpEBCBoaR4d7upFJk73HRJDw=="], + + "@types/react": ["@types/react@19.1.12", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "asap": ["asap@2.0.6", "", {}, "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "better-sqlite3": ["better-sqlite3@9.6.0", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-yR5HATnqeYNVnkaUTf4bOP2dJSnyhP4puJN/QPRyx4YkBEEUxib422n2XzPqDEHjQQqazoYoADdAm5vE15+dAQ=="], + + "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], + + "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + + "bun-types": ["bun-types@1.2.21", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "compare-versions": ["compare-versions@6.1.1", "", {}, "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg=="], + + "component-emitter": ["component-emitter@1.3.1", "", {}, "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ=="], + + "cookiejar": ["cookiejar@2.1.4", "", {}, "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], + + "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "dezalgo": ["dezalgo@1.0.4", "", { "dependencies": { "asap": "^2.0.0", "wrappy": "1" } }, "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig=="], + + "dockerfile-ast": ["dockerfile-ast@0.7.1", "", { "dependencies": { "vscode-languageserver-textdocument": "^1.0.8", "vscode-languageserver-types": "^3.17.3" } }, "sha512-oX/A4I0EhSkGqrFv0YuvPkBUSYp1XiY8O8zAKc8Djglx8ocz+JfOr8gP0ryRMC2myqvDLagmnZaU9ot1vG2ijw=="], + + "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + + "drizzle-kit": ["drizzle-kit@0.27.2", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.19.7", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-F6cFZ1wxa9XzFyeeQsp/0/lIzUbDuQjS8/njpYBDWa+wdWmXuY+Z/X2hHFK/9PGHZkv3c9mER+mVWfKlp/B6Vw=="], + + "drizzle-orm": ["drizzle-orm@0.33.0", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=3", "@electric-sql/pglite": ">=0.1.1", "@libsql/client": "*", "@neondatabase/serverless": ">=0.1", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/react": ">=18", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=13.2.0", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "react": ">=18", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/react", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "knex", "kysely", "mysql2", "pg", "postgres", "react", "sql.js", "sqlite3"] }, "sha512-SHy72R2Rdkz0LEq0PSG/IdvnT3nGiWuRk+2tXZQ90GVq/XQhpCzu/EFT3V2rox+w8MlkBQxifF8pCStNYnERfA=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "e2b": ["e2b@2.1.2", "", { "dependencies": { "@bufbuild/protobuf": "^2.6.2", "@connectrpc/connect": "2.0.0-rc.3", "@connectrpc/connect-web": "2.0.0-rc.3", "compare-versions": "^6.1.0", "dockerfile-ast": "^0.7.1", "glob": "^11.0.3", "openapi-fetch": "^0.9.7", "platform": "^1.3.6", "tar": "^7.4.3" } }, "sha512-3Z+cYCir7VllmZ72N7O5lhGqEi1A6s4rD8pQnBH5S9g12+9HkhJf7RsbcHJhWNsvEbFQ0p0GHBQ4RgugWKYybw=="], + + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + + "emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], + + "emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="], + + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], + + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="], + + "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], + + "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], + + "fast-safe-stringify": ["fast-safe-stringify@2.1.1", "", {}, "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="], + + "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + + "form-data": ["form-data@4.0.4", "", { "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" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], + + "formidable": ["formidable@3.5.4", "", { "dependencies": { "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", "once": "^1.4.0" } }, "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug=="], + + "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="], + + "get-east-asian-width": ["get-east-asian-width@1.3.1", "", {}, "sha512-R1QfovbPsKmosqTnPoRFiJ7CF9MLRgb53ChvMZm+r4p76/+8yKDy17qLL2PKInORy2RkZZekuK0efYgmzTkXyQ=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "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" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], + + "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], + + "glob": ["glob@11.0.3", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "hono": ["hono@4.9.6", "", {}, "sha512-doVjXhSFvYZ7y0dNokjwwSahcrAfdz+/BCLvAMa/vHLzjj8+CFyV5xteThGUsKdkaasgN+gF2mUxao+SGLpUeA=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="], + + "is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="], + + "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jackspeak": ["jackspeak@4.1.1", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ=="], + + "jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="], + + "jwa": ["jwa@1.4.2", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw=="], + + "jws": ["jws@3.2.2", "", { "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA=="], + + "linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="], + + "lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="], + + "lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="], + + "lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="], + + "lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="], + + "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="], + + "lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="], + + "lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="], + + "log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="], + + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + + "lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], + + "lru.min": ["lru.min@1.1.2", "", {}, "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg=="], + + "lunr": ["lunr@2.3.9", "", {}, "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="], + + "markdown-it": ["markdown-it@14.1.0", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA=="], + + "mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="], + + "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + + "mime": ["mime@2.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + + "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], + + "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="], + + "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="], + + "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "mysql2": ["mysql2@3.14.5", "", { "dependencies": { "aws-ssl-profiles": "^1.1.1", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.0", "long": "^5.2.1", "lru.min": "^1.0.0", "named-placeholders": "^1.1.3", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" } }, "sha512-40hDf8LPUsuuJ2hFq+UgOuPwt2IFLIRDvMv6ez9hKbXeYuZPxDDwiJW7KdknvOsQqKznaKczOT1kELgFkhDvFg=="], + + "named-placeholders": ["named-placeholders@1.1.3", "", { "dependencies": { "lru-cache": "^7.14.1" } }, "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w=="], + + "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], + + "node-abi": ["node-abi@3.77.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="], + + "openapi-fetch": ["openapi-fetch@0.9.8", "", { "dependencies": { "openapi-typescript-helpers": "^0.0.8" } }, "sha512-zM6elH0EZStD/gSiNlcPrzXcVQ/pZo3BDvC6CDwRDUt1dDzxlshpmQnpD6cZaJ39THaSmwVCxxRrPKNM1hHrDg=="], + + "openapi-typescript-helpers": ["openapi-typescript-helpers@0.0.8", "", {}, "sha512-1eNjQtbfNi5Z/kFhagDIaIRj6qqDzhjNJKz8cmMW0CVdGwT6e1GLbAfgI0d28VTJa1A8jz82jm/4dG8qNoNS8g=="], + + "openapi3-ts": ["openapi3-ts@4.5.0", "", { "dependencies": { "yaml": "^2.8.0" } }, "sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ=="], + + "ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="], + + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-scurry": ["path-scurry@2.0.0", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg=="], + + "pg": ["pg@8.16.3", "", { "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", "pg-protocol": "^1.10.3", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.2.7" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw=="], + + "pg-cloudflare": ["pg-cloudflare@1.2.7", "", {}, "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg=="], + + "pg-connection-string": ["pg-connection-string@2.9.1", "", {}, "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="], + + "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], + + "pg-pool": ["pg-pool@3.10.1", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg=="], + + "pg-protocol": ["pg-protocol@1.10.3", "", {}, "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ=="], + + "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], + + "pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="], + + "platform": ["platform@1.3.6", "", {}, "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="], + + "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], + + "postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="], + + "postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="], + + "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], + + "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], + + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + + "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="], + + "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="], + + "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], + + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + + "readline": ["readline@1.3.0", "", {}, "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg=="], + + "regex": ["regex@5.1.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="], + + "regex-recursion": ["regex-recursion@5.1.1", "", { "dependencies": { "regex": "^5.1.1", "regex-utilities": "^2.3.0" } }, "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "seq-queue": ["seq-queue@0.0.5", "", {}, "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "shiki": ["shiki@1.29.2", "", { "dependencies": { "@shikijs/core": "1.29.2", "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/langs": "1.29.2", "@shikijs/themes": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], + + "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], + + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], + + "sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="], + + "stdin-discarder": ["stdin-discarder@0.2.2", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="], + + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + + "superagent": ["superagent@10.2.3", "", { "dependencies": { "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", "debug": "^4.3.7", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.4", "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", "qs": "^6.11.2" } }, "sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig=="], + + "supertest": ["supertest@7.1.4", "", { "dependencies": { "methods": "^1.1.2", "superagent": "^10.2.3" } }, "sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg=="], + + "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="], + + "tar-fs": ["tar-fs@2.1.3", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg=="], + + "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], + + "typedoc": ["typedoc@0.26.11", "", { "dependencies": { "lunr": "^2.3.9", "markdown-it": "^14.1.0", "minimatch": "^9.0.5", "shiki": "^1.16.2", "yaml": "^2.5.1" }, "peerDependencies": { "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x" }, "bin": { "typedoc": "bin/typedoc" } }, "sha512-sFEgRRtrcDl2FxVP58Ze++ZK2UQAEvtvvH8rRlig1Ja3o7dDaMHmaBfvJmdGnNEFaLTpQsN8dpvZaTqJSu/Ugw=="], + + "typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="], + + "uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="], + + "undici": ["undici@6.21.3", "", {}, "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "uuid": ["uuid@12.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-USe1zesMYh4fjCA8ZH5+X5WIVD0J4V1Jksm1bFTVBX2F/cwSXt0RO5w/3UXbdLKmZX65MiWV+hwhSS8p6oBTGA=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + + "vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="], + + "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + + "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + + "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="], + + "zod": ["zod@4.1.5", "", {}, "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], + + "@hono/zod-openapi/@hono/zod-validator": ["@hono/zod-validator@0.7.2", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-ub5eL/NeZ4eLZawu78JpW/J+dugDAYhwqUIdp9KYScI6PZECij4Hx4UsrthlEUutqDDhPwRI0MscUfNkvn/mqQ=="], + + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "glob/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], + + "log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], + + "path-scurry/lru-cache": ["lru-cache@11.2.1", "", {}, "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ=="], + + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + + "wrap-ansi/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], + + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + } +} diff --git a/apps/coderouter/bunfig.toml b/apps/coderouter/bunfig.toml new file mode 100644 index 0000000000..844b046e9e --- /dev/null +++ b/apps/coderouter/bunfig.toml @@ -0,0 +1,2 @@ +[install] +exact = false diff --git a/apps/coderouter/codecov.yml b/apps/coderouter/codecov.yml new file mode 100644 index 0000000000..2e7aa286a9 --- /dev/null +++ b/apps/coderouter/codecov.yml @@ -0,0 +1,6 @@ +coverage: + status: + project: + default: + target: 60% + threshold: 5% diff --git a/apps/coderouter/drizzle.config.ts b/apps/coderouter/drizzle.config.ts new file mode 100644 index 0000000000..c63a44a439 --- /dev/null +++ b/apps/coderouter/drizzle.config.ts @@ -0,0 +1,15 @@ +import "dotenv/config"; + +export default { + out: "./drizzle", + schema: "./src/db/schema.ts", + dialect: + process.env.DRIZZLE_DB === "postgres" + ? "postgresql" + : process.env.DRIZZLE_DB === "mysql" + ? "mysql" + : "sqlite", + dbCredentials: { + connectionString: process.env.DATABASE_URL, + }, +}; diff --git a/apps/coderouter/package.json b/apps/coderouter/package.json new file mode 100644 index 0000000000..d3d088d8cc --- /dev/null +++ b/apps/coderouter/package.json @@ -0,0 +1,40 @@ +{ + "name": "coderouter", + "version": "0.0.1", + "private": false, + "type": "module", + "scripts": { + "dev": "bun run --hot src/server.ts", + "start": "bun run src/server.ts", + "test": "bun test --coverage", + "lint": "biome check .", + "fmt": "biome format --write .", + "db:generate": "drizzle-kit generate", + "db:migrate": "bun run scripts/migrate.ts" + }, + "dependencies": { + "@codesandbox/sdk": "^1.1.6", + "@e2b/code-interpreter": "2.0.0", + "@hono/zod-openapi": "^1.1.0", + "@hono/zod-validator": "^0.2.1", + "@types/jsonwebtoken": "^9.0.10", + "better-sqlite3": "^9.4.3", + "dotenv": "^16.4.5", + "drizzle-kit": "^0.27.0", + "drizzle-orm": "^0.33.0", + "hono": "^4.9.2", + "jsonwebtoken": "^9.0.2", + "mysql2": "^3.9.7", + "pg": "^8.11.5", + "uuid": "^12.0.0", + "zod": "^4.0.17" + }, + "devDependencies": { + "@biomejs/biome": "^2.1.4", + "@types/node": "^22.5.0", + "bun-types": "^1.2.20", + "supertest": "^7.0.0", + "typedoc": "^0.26.6", + "undici": "^6.19.8" + } +} diff --git a/apps/coderouter/renovate.json b/apps/coderouter/renovate.json new file mode 100644 index 0000000000..d173ea70b1 --- /dev/null +++ b/apps/coderouter/renovate.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended"], + "automerge": false +} diff --git a/apps/coderouter/scripts/migrate.ts b/apps/coderouter/scripts/migrate.ts new file mode 100644 index 0000000000..91ae6bc8e3 --- /dev/null +++ b/apps/coderouter/scripts/migrate.ts @@ -0,0 +1,9 @@ +import "dotenv/config"; +import { execSync } from "node:child_process"; + +try { + execSync("bun x drizzle-kit migrate", { stdio: "inherit" }); +} catch (e) { + console.error("Migration failed", e); + process.exit(1); +} diff --git a/apps/coderouter/site/index.html b/apps/coderouter/site/index.html new file mode 100644 index 0000000000..df79a67f0e --- /dev/null +++ b/apps/coderouter/site/index.html @@ -0,0 +1,14 @@ + + + + + Project Docs + + +

Project Documentation

+ + + diff --git a/apps/coderouter/src/api/auth/sign/index.test.ts b/apps/coderouter/src/api/auth/sign/index.test.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/coderouter/src/api/auth/sign/index.ts b/apps/coderouter/src/api/auth/sign/index.ts new file mode 100644 index 0000000000..98d2e7d5c3 --- /dev/null +++ b/apps/coderouter/src/api/auth/sign/index.ts @@ -0,0 +1,81 @@ +import { LocalHono } from '@/server'; +import { encodeJwtToken, verifyApiKeyFromHeader } from '@/util/auth'; +import { createRoute, z } from '@hono/zod-openapi'; +import { env } from 'bun'; + +export interface AuthSignInput { + sandboxId?: string; + userId?: string; +} + +const BodySchema: z.ZodType = z.object({ + sandboxId: z.string().optional().openapi({ + description: + 'The ID of the sandbox. This is your own ID. It can be a UUID or any unique string. Required when using a sandbox.', + example: '00000000-0000-0000-0000-000000000000', + }), + userId: z.string().optional().openapi({ + description: + 'The ID of the user. This is your own ID. It can be a UUID or any unique string.', + example: '00000000-0000-0000-0000-000000000000', + }), +}); + +const ResponseSchema = z.object({ + jwt: z.string().openapi({ + description: `The JWT token to send when interacting with the API as header "X-Auth-Jwt.".`, + }), +}); + +const route = createRoute({ + method: 'post', + path: env.URL_PATH_PREFIX + '/api/auth/sign', + security: [{ apikey: [] }], + request: { + body: { + content: { + 'application/json': { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + 'application/json': { + schema: ResponseSchema, + }, + }, + description: + 'Create a new sandbox. If the sandbox already exists, the system will resume the sandbox.', + }, + 401: { + content: { + 'application/json': { + schema: z.object({ error: z.string() }), + }, + }, + description: 'The API key is invalid.', + }, + }, +}); + +export function api_auth_sign(app: LocalHono) { + app.openapi(route, async (c) => { + if (!verifyApiKeyFromHeader(c.req.header('Authorization'))) { + return c.json({ error: 'Unauthorized' }, 401); + } + const body = await c.req.valid('json'); + const jwt = encodeJwtToken({ + sandboxId: body.sandboxId, + userId: body.userId, + }); + return c.json( + { + jwt, + }, + 200, + ); + }); +} diff --git a/apps/coderouter/src/api/sandbox/create/index.test.ts b/apps/coderouter/src/api/sandbox/create/index.test.ts new file mode 100644 index 0000000000..b3e95269a4 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/create/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_create } from './index'; + +describe('sandbox create endpoints', () => { + it('POST /coderouter/api/sandbox/create returns empty object', async () => { + const app = new Hono(); + api_sandbox_create(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/create', { + method: 'POST', + }); + + expect(response.status).toBe(201); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/create/index.ts b/apps/coderouter/src/api/sandbox/create/index.ts new file mode 100644 index 0000000000..06bf6f9e3a --- /dev/null +++ b/apps/coderouter/src/api/sandbox/create/index.ts @@ -0,0 +1,73 @@ +import { ClientError, ClientErrorCode } from '@/provider/definition'; +import { SandboxCreateInput } from '@/provider/definition/sandbox'; +import { LocalHono } from '@/server'; +import { JwtAuthResponses } from '@/util/auth'; +import { createRoute, z } from '@hono/zod-openapi'; +import { env } from 'bun'; + +const BodySchema: z.ZodType = z.object({ + templateId: z.string().openapi({ + description: 'The ID of the template to use for the sandbox.', + example: '00000000-0000-0000-0000-000000000000', + }), + metadata: z.record(z.string(), z.string()).openapi({ + description: 'The metadata of the sandbox.', + }), +}); + +const ResponseSchema = z.object({ + id: z.string().openapi({ + description: + 'The ID of the sandbox. This is your own ID. It can be a UUID or any unique string.', + example: '00000000-0000-0000-0000-000000000000', + }), +}); + +const route = createRoute({ + method: 'post', + path: env.URL_PATH_PREFIX + '/api/sandbox/create', + security: [{ jwt: [] }], + request: { + body: { + content: { + 'application/json': { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + 'application/json': { + schema: ResponseSchema, + }, + }, + description: + 'Create a new sandbox. If the sandbox already exists, the system will resume the sandbox.', + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_create(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid('json'); + try { + await c.get('client').sandbox.get({}); + await c.get('client').sandbox.resume({}); + } catch (e) { + if (e instanceof ClientError && e.code === ClientErrorCode.SandboxNotFound) { + await c.get('client').sandbox.create(body); + } else { + throw e; + } + } + return c.json( + { + id: c.get('auth').sandboxId!, + }, + 200, + ); + }); +} diff --git a/apps/coderouter/src/api/sandbox/file/copy/index.test.ts b/apps/coderouter/src/api/sandbox/file/copy/index.test.ts new file mode 100644 index 0000000000..99e984e2f0 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/copy/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_file_copy } from './index'; + +describe('sandbox files copy endpoints', () => { + it('POST /coderouter/api/sandbox/file/copy returns empty object', async () => { + const app = new Hono(); + api_sandbox_file_copy(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/file/copy', { + method: 'POST', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/file/copy/index.ts b/apps/coderouter/src/api/sandbox/file/copy/index.ts new file mode 100644 index 0000000000..5d7c621ae9 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/copy/index.ts @@ -0,0 +1,64 @@ +import { + SandboxFileCopyInput, + SandboxFileCopyOutput, +} from "@/provider/definition/sandbox/file"; +import { JwtAuthResponses } from "@/util/auth"; +import { LocalHono } from "@/server"; +import { createRoute, z } from "@hono/zod-openapi"; +import { env } from "bun"; + +const BodySchema: z.ZodType = z.object({ + source: z.string().openapi({ + description: "The path of the source file.", + example: "/path/to/source.txt", + }), + destination: z.string().openapi({ + description: "The path of the destination file.", + example: "/path/to/destination.txt", + }), + overwrite: z.boolean().openapi({ + description: + "Whether to overwrite the destination file if it already exists.", + example: true, + }), + recursive: z.boolean().openapi({ + description: "Whether to copy the file recursively.", + example: true, + }), +}); + +const ResponseSchema: z.ZodType = z.object({}); + +const route = createRoute({ + method: "post", + path: env.URL_PATH_PREFIX + "/api/sandbox/file/copy", + security: [{ jwt: [] }], + request: { + body: { + content: { + "application/json": { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: ResponseSchema, + }, + }, + description: "Copy a file to the sandbox.", + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_file_copy(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid("json"); + const result = await c.get("client").sandbox.file.copy(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/file/delete/index.test.ts b/apps/coderouter/src/api/sandbox/file/delete/index.test.ts new file mode 100644 index 0000000000..573f7a447b --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/delete/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_file_delete } from './index'; + +describe('sandbox files delete endpoints', () => { + it('DELETE /coderouter/api/sandbox/file/delete returns empty object', async () => { + const app = new Hono(); + api_sandbox_file_delete(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/file/delete', { + method: 'DELETE', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/file/delete/index.ts b/apps/coderouter/src/api/sandbox/file/delete/index.ts new file mode 100644 index 0000000000..17cc10c2ab --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/delete/index.ts @@ -0,0 +1,51 @@ +import { + SandboxFileDeleteInput, + SandboxFileDeleteOutput, +} from "@/provider/definition/sandbox/file"; +import { LocalHono } from "@/server"; +import { JwtAuthResponses } from "@/util/auth"; +import { createRoute, z } from "@hono/zod-openapi"; +import { env } from "bun"; + +const BodySchema: z.ZodType = z.object({ + path: z.string().openapi({ + description: "The path of the file to delete.", + example: "/path/to/file.txt", + }), +}); + +const ResponseSchema: z.ZodType = z.object({}); + +const route = createRoute({ + method: "post", + path: env.URL_PATH_PREFIX + "/api/sandbox/file/delete", + security: [{ jwt: [] }], + request: { + body: { + content: { + "application/json": { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: ResponseSchema, + }, + }, + description: "Delete a file from the sandbox.", + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_file_delete(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid("json"); + const result = await c.get("client").sandbox.file.delete(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/file/download/index.test.ts b/apps/coderouter/src/api/sandbox/file/download/index.test.ts new file mode 100644 index 0000000000..43aa07f20f --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/download/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_file_download } from './index'; + +describe('sandbox files download endpoints', () => { + it('GET /coderouter/api/sandbox/file/download returns empty object', async () => { + const app = new Hono(); + api_sandbox_file_download(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/file/download', { + method: 'GET', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/file/download/index.ts b/apps/coderouter/src/api/sandbox/file/download/index.ts new file mode 100644 index 0000000000..2366ba9e99 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/download/index.ts @@ -0,0 +1,51 @@ +import { + SandboxFileDownloadInput, + SandboxFileDownloadOutput, +} from "@/provider/definition/sandbox/file"; +import { LocalHono } from "@/server"; +import { JwtAuthResponses } from "@/util/auth"; +import { createRoute, z } from "@hono/zod-openapi"; +import { env } from "bun"; + +const BodySchema: z.ZodType = z.object({ + path: z.string().openapi({ + description: "The path of the file to download.", + example: "/path/to/file.txt", + }), +}); + +const ResponseSchema: z.ZodType = z.object({}); + +const route = createRoute({ + method: "post", + path: env.URL_PATH_PREFIX + "/api/sandbox/file/download", + security: [{ jwt: [] }], + request: { + body: { + content: { + "application/json": { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: ResponseSchema, + }, + }, + description: "Download a file from the sandbox.", + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_file_download(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid("json"); + const result = await c.get("client").sandbox.file.download(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/file/list/index.test.ts b/apps/coderouter/src/api/sandbox/file/list/index.test.ts new file mode 100644 index 0000000000..8c8d069db7 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/list/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_file_list } from './index'; + +describe('sandbox files list endpoints', () => { + it('GET /coderouter/api/sandbox/file/list returns empty object', async () => { + const app = new Hono(); + api_sandbox_file_list(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/file/list', { + method: 'GET', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/file/list/index.ts b/apps/coderouter/src/api/sandbox/file/list/index.ts new file mode 100644 index 0000000000..40dbf08592 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/list/index.ts @@ -0,0 +1,56 @@ +import { SandboxFileListInput, SandboxFileListOutput } from '@/provider/definition/sandbox/file'; +import { LocalHono } from '@/server'; +import { JwtAuthResponses } from '@/util/auth'; +import { createRoute, z } from '@hono/zod-openapi'; +import { env } from 'bun'; + +const BodySchema: z.ZodType = z.object({ + path: z.string().openapi({ + description: 'The path of the directory to list.', + example: '/path/to/directory', + }), +}); + +const ResponseSchema: z.ZodType = z.object({ + files: z.array( + z.object({ + name: z.string(), + path: z.string(), + type: z.enum(['file', 'directory']), + }), + ), +}); + +const route = createRoute({ + method: 'post', + path: env.URL_PATH_PREFIX + '/api/sandbox/file/list', + security: [{ jwt: [] }], + request: { + body: { + content: { + 'application/json': { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + 'application/json': { + schema: ResponseSchema, + }, + }, + description: 'List files in the sandbox.', + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_file_list(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid('json'); + const result = await c.get('client').sandbox.file.list(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/file/read/index.test.ts b/apps/coderouter/src/api/sandbox/file/read/index.test.ts new file mode 100644 index 0000000000..ace82e05ba --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/read/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_file_read } from './index'; + +describe('sandbox files read endpoints', () => { + it('GET /coderouter/api/sandbox/file/read returns empty object', async () => { + const app = new Hono(); + api_sandbox_file_read(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/file/read', { + method: 'GET', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/file/read/index.ts b/apps/coderouter/src/api/sandbox/file/read/index.ts new file mode 100644 index 0000000000..11e1af33e0 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/read/index.ts @@ -0,0 +1,53 @@ +import { + SandboxFileReadInput, + SandboxFileReadOutput, +} from "@/provider/definition/sandbox/file"; +import { JwtAuthResponses } from "@/util/auth"; +import { LocalHono } from "@/server"; +import { createRoute, z } from "@hono/zod-openapi"; +import { env } from "bun"; + +const BodySchema: z.ZodType = z.object({ + path: z.string().openapi({ + description: "The path of the file to read.", + example: "/path/to/file.txt", + }), +}); + +const ResponseSchema: z.ZodType = z.object({ + data: z.string(), +}); + +const route = createRoute({ + method: "post", + path: env.URL_PATH_PREFIX + "/api/sandbox/file/read", + security: [{ jwt: [] }], + request: { + body: { + content: { + "application/json": { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: ResponseSchema, + }, + }, + description: "Read a file from the sandbox.", + }, + }, + ...JwtAuthResponses, +}); + +export function api_sandbox_file_read(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid("json"); + const result = await c.get("client").sandbox.file.read(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/file/rename/index.test.ts b/apps/coderouter/src/api/sandbox/file/rename/index.test.ts new file mode 100644 index 0000000000..fcdcf30771 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/rename/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_file_rename } from './index'; + +describe('sandbox files rename endpoints', () => { + it('PUT /coderouter/api/sandbox/file/rename returns empty object', async () => { + const app = new Hono(); + api_sandbox_file_rename(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/file/rename', { + method: 'PUT', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/file/rename/index.ts b/apps/coderouter/src/api/sandbox/file/rename/index.ts new file mode 100644 index 0000000000..682eb3b71a --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/rename/index.ts @@ -0,0 +1,55 @@ +import { + SandboxFileRenameInput, + SandboxFileRenameOutput, +} from "@/provider/definition/sandbox/file"; +import { JwtAuthResponses } from "@/util/auth"; +import { LocalHono } from "@/server"; +import { createRoute, z } from "@hono/zod-openapi"; +import { env } from "bun"; + +const BodySchema: z.ZodType = z.object({ + oldPath: z.string().openapi({ + description: "The path of the file to rename.", + example: "/path/to/file.txt", + }), + newPath: z.string().openapi({ + description: "The new path of the file.", + example: "/path/to/new/file.txt", + }), +}); + +const ResponseSchema: z.ZodType = z.object({}); + +const route = createRoute({ + method: "post", + path: env.URL_PATH_PREFIX + "/api/sandbox/file/rename", + security: [{ jwt: [] }], + request: { + body: { + content: { + "application/json": { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: ResponseSchema, + }, + }, + description: "Rename a file in the sandbox.", + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_file_rename(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid("json"); + const result = await c.get("client").sandbox.file.rename(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/file/stat/index.test.ts b/apps/coderouter/src/api/sandbox/file/stat/index.test.ts new file mode 100644 index 0000000000..46d9afe98b --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/stat/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_file_stat } from './index'; + +describe('sandbox files stat endpoints', () => { + it('GET /coderouter/api/sandbox/file/stat returns empty object', async () => { + const app = new Hono(); + api_sandbox_file_stat(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/file/stat', { + method: 'GET', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/file/stat/index.ts b/apps/coderouter/src/api/sandbox/file/stat/index.ts new file mode 100644 index 0000000000..e67f26db94 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/stat/index.ts @@ -0,0 +1,53 @@ +import { + SandboxFileStatInput, + SandboxFileStatOutput, +} from "@/provider/definition/sandbox/file"; +import { JwtAuthResponses } from "@/util/auth"; +import { LocalHono } from "@/server"; +import { createRoute, z } from "@hono/zod-openapi"; +import { env } from "bun"; + +const BodySchema: z.ZodType = z.object({ + path: z.string().openapi({ + description: "The path of the file to stat.", + example: "/path/to/file.txt", + }), +}); + +const ResponseSchema: z.ZodType = z.object({ + type: z.enum(["file", "directory"]), +}); + +const route = createRoute({ + method: "post", + path: env.URL_PATH_PREFIX + "/api/sandbox/file/stat", + security: [{ jwt: [] }], + request: { + body: { + content: { + "application/json": { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: ResponseSchema, + }, + }, + description: "Get the status of a file in the sandbox.", + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_file_stat(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid("json"); + const result = await c.get("client").sandbox.file.stat(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/file/watch/index.test.ts b/apps/coderouter/src/api/sandbox/file/watch/index.test.ts new file mode 100644 index 0000000000..6336c72869 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/watch/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_file_write } from './index'; + +describe('sandbox files write endpoints', () => { + it('POST /coderouter/api/sandbox/file/write returns empty object', async () => { + const app = new Hono(); + api_sandbox_file_write(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/file/write', { + method: 'POST', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/file/watch/index.ts b/apps/coderouter/src/api/sandbox/file/watch/index.ts new file mode 100644 index 0000000000..8c39ef5af5 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/watch/index.ts @@ -0,0 +1,89 @@ +import { SandboxFileWatchInput, SandboxFileWatchOutput } from '@/provider/definition/sandbox/file'; +import { LocalHono } from '@/server'; +import { JwtAuthResponses } from '@/util/auth'; +import { createRoute, z } from '@hono/zod-openapi'; +import { env } from 'bun'; +import { streamSSE } from 'hono/streaming'; +import { v4 as uuid } from 'uuid'; + +const BodySchema: z.ZodType = z.object({ + path: z.string().openapi({ + description: 'The path of the file to write.', + example: '/path/to/file.txt', + }), + recursive: z.boolean().openapi({ + description: 'Whether to watch the file recursively.', + example: false, + }), + excludePaths: z.array(z.string()).openapi({ + description: 'The paths to exclude from the watch.', + example: ['/path/to/exclude'], + }), +}); + +const route = createRoute({ + method: 'post', + path: env.URL_PATH_PREFIX + '/api/sandbox/file/watch', + security: [{ jwt: [] }], + request: { + body: { + content: { + 'application/json': { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + 'text/event-stream': { + schema: z.string().openapi({ + description: 'A stream of server-sent events.', + }), + }, + }, + description: 'Return file watch events. Design for SSE', + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_file_watch(app: LocalHono) { + app.openapi(route, async (c) => { + const query = await c.req.valid('json'); + return streamSSE(c, async (stream) => { + // Send a "connected" message immediately + await stream.writeSSE({ + id: uuid(), + event: 'status', + data: 'Connected to endpoint.', + }); + + const onOutput = (res: SandboxFileWatchOutput) => { + stream.writeSSE({ + id: res.id, + event: 'message', + data: JSON.stringify({ events: res.events }), + }); + }; + + const { close } = await c.get('client').sandbox.file.watch(query, onOutput); + + let open = true; + stream.onAbort(() => { + close(); + open = false; + }); + + while (open) { + await stream.writeSSE({ + id: uuid(), + event: 'status', + data: 'Endpoint is still open.', + }); + await stream.sleep(5000); + } + }); + }); +} diff --git a/apps/coderouter/src/api/sandbox/file/write/index.test.ts b/apps/coderouter/src/api/sandbox/file/write/index.test.ts new file mode 100644 index 0000000000..6336c72869 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/write/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_file_write } from './index'; + +describe('sandbox files write endpoints', () => { + it('POST /coderouter/api/sandbox/file/write returns empty object', async () => { + const app = new Hono(); + api_sandbox_file_write(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/file/write', { + method: 'POST', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/file/write/index.ts b/apps/coderouter/src/api/sandbox/file/write/index.ts new file mode 100644 index 0000000000..56f6da87ce --- /dev/null +++ b/apps/coderouter/src/api/sandbox/file/write/index.ts @@ -0,0 +1,63 @@ +import { + SandboxFileWriteInput, + SandboxFileWriteOutput, +} from "@/provider/definition/sandbox/file"; +import { LocalHono } from "@/server"; +import { JwtAuthResponses } from "@/util/auth"; +import { createRoute, z } from "@hono/zod-openapi"; +import { env } from "bun"; + +const BodySchema: z.ZodType = z.object({ + files: z.array( + z.object({ + path: z.string().openapi({ + description: "The path of the file to write.", + example: "/path/to/file.txt", + }), + data: z.string().openapi({ + description: "The content of the file to write.", + example: "Hello, world!", + }), + overwrite: z.boolean().openapi({ + description: "Whether to overwrite the file if it already exists.", + example: true, + }), + }) + ), +}); + +const ResponseSchema: z.ZodType = z.object({}); + +const route = createRoute({ + method: "post", + path: env.URL_PATH_PREFIX + "/api/sandbox/file/write", + security: [{ jwt: [] }], + request: { + body: { + content: { + "application/json": { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: ResponseSchema, + }, + }, + description: "Write a file to the sandbox.", + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_file_write(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid("json"); + const result = await c.get("client").sandbox.file.write(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/pause/index.test.ts b/apps/coderouter/src/api/sandbox/pause/index.test.ts new file mode 100644 index 0000000000..1d2cfa5604 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/pause/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_pause } from './index'; + +describe('sandbox pause endpoints', () => { + it('POST /coderouter/api/sandbox/pause returns empty object', async () => { + const app = new Hono(); + api_sandbox_pause(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/pause', { + method: 'POST', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/pause/index.ts b/apps/coderouter/src/api/sandbox/pause/index.ts new file mode 100644 index 0000000000..a53d9f5326 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/pause/index.ts @@ -0,0 +1,54 @@ +import { SandboxPauseInput } from "@/provider/definition/sandbox"; +import { LocalHono } from "@/server"; +import { JwtAuthResponses } from "@/util/auth"; +import { createRoute, z } from "@hono/zod-openapi"; +import { env } from "bun"; + +const BodySchema: z.ZodType = z.object({}); + +const ResponseSchema = z.object({ + id: z.string().openapi({ + description: + "The ID of the sandbox. This is your own ID. It can be a UUID or any unique string.", + example: "00000000-0000-0000-0000-000000000000", + }), +}); + +const route = createRoute({ + method: "post", + path: env.URL_PATH_PREFIX + "/api/sandbox/pause", + security: [{ jwt: [] }], + request: { + body: { + content: { + "application/json": { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: ResponseSchema, + }, + }, + description: "Pause the sandbox.", + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_pause(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid("json"); + await c.get("client").sandbox.pause(body); + return c.json( + { + id: c.get("auth").sandboxId!, + }, + 200 + ); + }); +} diff --git a/apps/coderouter/src/api/sandbox/resume/index.test.ts b/apps/coderouter/src/api/sandbox/resume/index.test.ts new file mode 100644 index 0000000000..a05bde127c --- /dev/null +++ b/apps/coderouter/src/api/sandbox/resume/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_resume } from './index'; + +describe('sandbox resume endpoints', () => { + it('POST /coderouter/api/sandbox/resume returns empty object', async () => { + const app = new Hono(); + api_sandbox_resume(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/resume', { + method: 'POST', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/resume/index.ts b/apps/coderouter/src/api/sandbox/resume/index.ts new file mode 100644 index 0000000000..01bdf5d950 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/resume/index.ts @@ -0,0 +1,54 @@ +import { SandboxResumeInput } from "@/provider/definition/sandbox"; +import { LocalHono } from "@/server"; +import { JwtAuthResponses } from "@/util/auth"; +import { createRoute, z } from "@hono/zod-openapi"; +import { env } from "bun"; + +const BodySchema: z.ZodType = z.object({}); + +const ResponseSchema = z.object({ + id: z.string().openapi({ + description: + "The ID of the sandbox. This is your own ID. It can be a UUID or any unique string.", + example: "00000000-0000-0000-0000-000000000000", + }), +}); + +const route = createRoute({ + method: "post", + path: env.URL_PATH_PREFIX + "/api/sandbox/resume", + security: [{ jwt: [] }], + request: { + body: { + content: { + "application/json": { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: ResponseSchema, + }, + }, + description: "Resume the sandbox.", + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_resume(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid("json"); + await c.get("client").sandbox.resume(body); + return c.json( + { + id: c.get("auth").sandboxId!, + }, + 200 + ); + }); +} diff --git a/apps/coderouter/src/api/sandbox/stop/index.test.ts b/apps/coderouter/src/api/sandbox/stop/index.test.ts new file mode 100644 index 0000000000..65c0502012 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/stop/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_stop } from './index'; + +describe('sandbox stop endpoints', () => { + it('POST /coderouter/api/sandbox/stop returns empty object', async () => { + const app = new Hono(); + api_sandbox_stop(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/stop', { + method: 'POST', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/stop/index.ts b/apps/coderouter/src/api/sandbox/stop/index.ts new file mode 100644 index 0000000000..61e38307fd --- /dev/null +++ b/apps/coderouter/src/api/sandbox/stop/index.ts @@ -0,0 +1,54 @@ +import { SandboxStopInput } from "@/provider/definition/sandbox"; +import { LocalHono } from "@/server"; +import { JwtAuthResponses } from "@/util/auth"; +import { createRoute, z } from "@hono/zod-openapi"; +import { env } from "bun"; + +const BodySchema: z.ZodType = z.object({}); + +const ResponseSchema = z.object({ + id: z.string().openapi({ + description: + "The ID of the sandbox. This is your own ID. It can be a UUID or any unique string.", + example: "00000000-0000-0000-0000-000000000000", + }), +}); + +const route = createRoute({ + method: "post", + path: env.URL_PATH_PREFIX + "/api/sandbox/stop", + security: [{ jwt: [] }], + request: { + body: { + content: { + "application/json": { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + "application/json": { + schema: ResponseSchema, + }, + }, + description: "Stop the sandbox.", + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_stop(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid("json"); + await c.get("client").sandbox.stop(body); + return c.json( + { + id: c.get("auth").sandboxId!, + }, + 200 + ); + }); +} diff --git a/apps/coderouter/src/api/sandbox/terminal/command/index.test.ts b/apps/coderouter/src/api/sandbox/terminal/command/index.test.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/coderouter/src/api/sandbox/terminal/command/index.ts b/apps/coderouter/src/api/sandbox/terminal/command/index.ts new file mode 100644 index 0000000000..df43939c90 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/terminal/command/index.ts @@ -0,0 +1,72 @@ +import { + SandboxTerminalCommandInput, + SandboxTerminalCommandOutput, +} from '@/provider/definition/sandbox/terminal'; +import { LocalHono } from '@/server'; +import { JwtAuthResponses } from '@/util/auth'; +import { createRoute, z } from '@hono/zod-openapi'; +import { env } from 'bun'; + +const BodySchema: z.ZodType = z.object({ + command: z.string().openapi({ + description: 'The command to run.', + example: 'ls -la', + }), +}); + +const ResponseSchema: z.ZodType = z.object({ + is_error: z.boolean().openapi({ + description: 'True if the command failed.', + example: false, + }), + output: z.string().openapi({ + description: 'Combined output (stdout + stderr).', + example: 'total 0', + }), + stdout: z.string().optional().openapi({ + description: 'The stdout of the command.', + example: 'total 0', + }), + stderr: z.string().optional().openapi({ + description: 'The stderr of the command.', + example: '', + }), + exit_code: z.number().optional().openapi({ + description: 'The exit code of the command.', + example: 0, + }), +}); + +const route = createRoute({ + method: 'post', + path: env.URL_PATH_PREFIX + '/api/sandbox/terminal/command', + security: [{ jwt: [] }], + request: { + body: { + content: { + 'application/json': { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + 'application/json': { + schema: ResponseSchema, + }, + }, + description: 'Run a command in the sandbox.', + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_terminal_command(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid('json'); + const result = await c.get('client').sandbox.terminal.command(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/terminal/create/index.test.ts b/apps/coderouter/src/api/sandbox/terminal/create/index.test.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/coderouter/src/api/sandbox/terminal/create/index.ts b/apps/coderouter/src/api/sandbox/terminal/create/index.ts new file mode 100644 index 0000000000..ebaabccc20 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/terminal/create/index.ts @@ -0,0 +1,64 @@ +import { + SandboxTerminalCreateInput, + SandboxTerminalCreateOutput, +} from '@/provider/definition/sandbox/terminal'; +import { LocalHono } from '@/server'; +import { JwtAuthResponses } from '@/util/auth'; +import { createRoute, z } from '@hono/zod-openapi'; +import { env } from 'bun'; + +const BodySchema: z.ZodType = z.object({ + terminalId: z.string().openapi({ + description: 'The ID of the terminal.', + example: '00000000-0000-0000-0000-000000000000 or any unique string', + }), + name: z.string().openapi({ + description: 'The name of the terminal.', + example: 'My Terminal', + }), +}); + +const ResponseSchema: z.ZodType = z.object({ + terminalId: z.string().openapi({ + description: 'The ID of the terminal.', + example: '00000000-0000-0000-0000-000000000000 or any unique string', + }), + name: z.string().openapi({ + description: 'The name of the terminal.', + example: 'My Terminal', + }), +}); + +const route = createRoute({ + method: 'post', + path: env.URL_PATH_PREFIX + '/api/sandbox/terminal/create', + security: [{ jwt: [] }], + request: { + body: { + content: { + 'application/json': { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + 'application/json': { + schema: ResponseSchema, + }, + }, + description: 'Create a terminal in the sandbox.', + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_terminal_create(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid('json'); + const result = await c.get('client').sandbox.terminal.create(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/terminal/kill/index.ts b/apps/coderouter/src/api/sandbox/terminal/kill/index.ts new file mode 100644 index 0000000000..b978d87525 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/terminal/kill/index.ts @@ -0,0 +1,56 @@ +import { + SandboxTerminalKillInput, + SandboxTerminalKillOutput, +} from '@/provider/definition/sandbox/terminal'; +import { LocalHono } from '@/server'; +import { JwtAuthResponses } from '@/util/auth'; +import { createRoute, z } from '@hono/zod-openapi'; +import { env } from 'bun'; + +const BodySchema: z.ZodType = z.object({ + terminalId: z.string().openapi({ + description: 'The ID of the terminal.', + example: '00000000-0000-0000-0000-000000000000 or any unique string', + }), +}); + +const ResponseSchema: z.ZodType = z.object({ + output: z.string().openapi({ + description: 'The output of the kill command.', + example: 'Terminal killed', + }), +}); + +const route = createRoute({ + method: 'post', + path: env.URL_PATH_PREFIX + '/api/sandbox/terminal/kill', + security: [{ jwt: [] }], + request: { + body: { + content: { + 'application/json': { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + 'application/json': { + schema: ResponseSchema, + }, + }, + description: 'Kill a terminal in the sandbox.', + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_terminal_kill(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid('json'); + const result = await c.get('client').sandbox.terminal.kill(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/terminal/open/index.ts b/apps/coderouter/src/api/sandbox/terminal/open/index.ts new file mode 100644 index 0000000000..68a8efaff4 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/terminal/open/index.ts @@ -0,0 +1,79 @@ +// import { +// SandboxTerminalOpenInput, +// SandboxTerminalOpenOutput, +// } from '@/provider/definition/sandbox/terminal'; +import { SandboxTerminalOpenOutput } from '@/provider/definition/sandbox/terminal'; +import { LocalHono } from '@/server'; +import { JwtAuthResponses } from '@/util/auth'; +import { createRoute, z } from '@hono/zod-openapi'; +import { env } from 'bun'; +import { streamSSE } from 'hono/streaming'; +import { v4 as uuid } from 'uuid'; + +const ParamsSchema = z.object({ + terminalId: z.string().openapi({ + description: 'The ID of the terminal.', + example: '00000000-0000-0000-0000-000000000000 or any unique string', + }), +}); + +const route = createRoute({ + method: 'get', + path: env.URL_PATH_PREFIX + '/api/sandbox/terminal/open', + security: [{ jwt: [] }], + request: { + query: ParamsSchema, + }, + responses: { + 200: { + content: { + 'text/event-stream': { + schema: z.string().openapi({ + description: 'A stream of server-sent events.', + }), + }, + }, + description: 'Return the output of a terminal in the sandbox. Design for SSE', + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_terminal_open(app: LocalHono) { + app.openapi(route, async (c) => { + const query = await c.req.valid('query'); + return streamSSE(c, async (stream) => { + // Send a "connected" message immediately + await stream.writeSSE({ + id: uuid(), + event: 'status', + data: 'Connected to endpoint.', + }); + + const onOutput = (res: SandboxTerminalOpenOutput) => { + stream.writeSSE({ + id: res.id, + event: 'message', + data: JSON.stringify({ output: res.output }), + }); + }; + + const { close } = await c.get('client').sandbox.terminal.open(query, onOutput); + + let open = true; + stream.onAbort(() => { + close(); + open = false; + }); + + while (open) { + await stream.writeSSE({ + id: uuid(), + event: 'status', + data: 'Endpoint is still open.', + }); + await stream.sleep(5000); + } + }); + }); +} diff --git a/apps/coderouter/src/api/sandbox/terminal/run/index.ts b/apps/coderouter/src/api/sandbox/terminal/run/index.ts new file mode 100644 index 0000000000..9393894225 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/terminal/run/index.ts @@ -0,0 +1,61 @@ +import { + SandboxTerminalRunInput, + SandboxTerminalRunOutput, +} from '@/provider/definition/sandbox/terminal'; +import { LocalHono } from '@/server'; +import { JwtAuthResponses } from '@/util/auth'; +import { createRoute, z } from '@hono/zod-openapi'; +import { env } from 'bun'; + +const BodySchema: z.ZodType = z.object({ + terminalId: z.string().openapi({ + description: 'The ID of the terminal.', + example: '00000000-0000-0000-0000-000000000000 or any unique string', + }), + input: z.string().openapi({ + description: 'The name of the terminal.', + example: 'My Terminal', + }), +}); + +const ResponseSchema: z.ZodType = z.object({ + output: z.string().openapi({ + description: + 'Combined stdout/stderr captured from the terminal around the command execution.', + example: '$ ls -la\\n.\\n..\\nREADME.md\\n', + }), +}); + +const route = createRoute({ + method: 'post', + path: env.URL_PATH_PREFIX + '/api/sandbox/terminal/run', + security: [{ jwt: [] }], + request: { + body: { + content: { + 'application/json': { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + 'application/json': { + schema: ResponseSchema, + }, + }, + description: 'Run a terminal in the sandbox.', + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_terminal_run(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid('json'); + const result = await c.get('client').sandbox.terminal.run(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/terminal/write/index.ts b/apps/coderouter/src/api/sandbox/terminal/write/index.ts new file mode 100644 index 0000000000..6258df7e3f --- /dev/null +++ b/apps/coderouter/src/api/sandbox/terminal/write/index.ts @@ -0,0 +1,59 @@ +import { + SandboxTerminalWriteInput, + SandboxTerminalWriteOutput, +} from '@/provider/definition/sandbox/terminal'; +import { LocalHono } from '@/server'; +import { JwtAuthResponses } from '@/util/auth'; +import { createRoute, z } from '@hono/zod-openapi'; +import { env } from 'bun'; + +const BodySchema: z.ZodType = z.object({ + terminalId: z.string().openapi({ + description: 'The ID of the terminal.', + example: '00000000-0000-0000-0000-000000000000 or any unique string', + }), + input: z.string().openapi({ + description: 'The input to the write command.', + example: 'ls -la', + }), +}); + +const ResponseSchema: z.ZodType = z.object({ + output: z.string().openapi({ + description: 'The output of the write command.', + }), +}); + +const route = createRoute({ + method: 'post', + path: env.URL_PATH_PREFIX + '/api/sandbox/terminal/write', + security: [{ jwt: [] }], + request: { + body: { + content: { + 'application/json': { + schema: BodySchema, + }, + }, + }, + }, + responses: { + 200: { + content: { + 'application/json': { + schema: ResponseSchema, + }, + }, + description: 'Write a terminal in the sandbox.', + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_terminal_write(app: LocalHono) { + app.openapi(route, async (c) => { + const body = await c.req.valid('json'); + const result = await c.get('client').sandbox.terminal.write(body); + return c.json(result, 200); + }); +} diff --git a/apps/coderouter/src/api/sandbox/url/index.test.ts b/apps/coderouter/src/api/sandbox/url/index.test.ts new file mode 100644 index 0000000000..260d15e915 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/url/index.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test'; +import { Hono } from 'hono'; +import { api_sandbox_url } from './index'; + +describe('sandbox url endpoints', () => { + it('GET /coderouter/api/sandbox/url returns empty object', async () => { + const app = new Hono(); + api_sandbox_url(app); + + const response = await app.request(env.URL_PATH_PREFIX + '/api/sandbox/url', { + method: 'GET', + }); + + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual({}); + }); +}); diff --git a/apps/coderouter/src/api/sandbox/url/index.ts b/apps/coderouter/src/api/sandbox/url/index.ts new file mode 100644 index 0000000000..467159bd94 --- /dev/null +++ b/apps/coderouter/src/api/sandbox/url/index.ts @@ -0,0 +1,54 @@ +import { SandboxUrlInput } from "@/provider/definition/sandbox"; +import { LocalHono } from "@/server"; +import { JwtAuthResponses } from "@/util/auth"; +import { createRoute, z } from "@hono/zod-openapi"; +import { env } from "bun"; + +const BodySchema: z.ZodType = z.object({}); + +const ResponseSchema = z.object({ + url: z.string().openapi({ + description: "The URL of the sandbox.", + example: "", + }), +}); + +const route = createRoute({ + method: "post", + path: env.URL_PATH_PREFIX + "/api/sandbox/url", + security: [{ jwt: [] }], + // no parameters + // request: { + // body: { + // content: { + // "application/json": { + // schema: BodySchema, + // }, + // }, + // }, + // }, + responses: { + 200: { + content: { + "application/json": { + schema: ResponseSchema, + }, + }, + description: "Get a URL to access the sandbox.", + }, + ...JwtAuthResponses, + }, +}); + +export function api_sandbox_url(app: LocalHono) { + app.openapi(route, async (c) => { + // const body = await c.req.valid("json"); + const res = await c.get("client").sandbox.url({}); + return c.json( + { + url: res.url, + }, + 200 + ); + }); +} diff --git a/apps/coderouter/src/db/connection.ts b/apps/coderouter/src/db/connection.ts new file mode 100644 index 0000000000..cec432874a --- /dev/null +++ b/apps/coderouter/src/db/connection.ts @@ -0,0 +1,37 @@ +import "dotenv/config"; +// import { drizzle } from "drizzle-orm"; +// import { Database } from "drizzle-orm/sqlite-proxy"; +import { mysqlUsers, pgUsers, sqliteUsers } from "./schema"; +// import { createPool as createPgPool } from "pg"; +// import mysql from "mysql2/promise"; +// import DatabaseSqlite from "better-sqlite3"; + +export type DBKind = "postgres" | "mysql" | "sqlite"; + +export const dbKind: DBKind = (process.env.DRIZZLE_DB as DBKind) || "sqlite"; + +export async function getDb() { + // if (dbKind === "postgres") { + // const { Pool } = await import('pg'); // Lazy if needed + // const pool = new Pool({ connectionString: process.env.DATABASE_URL }); + // return drizzle(pool); + // } else if (dbKind === "mysql") { + // return mysql + // .createConnection(process.env.DATABASE_URL!) + // .then((conn) => drizzle(conn)); + // } else { + // const sqlite = new DatabaseSqlite( + // process.env.DATABASE_URL?.replace("file:", "") || "dev.sqlite", + // ); + // // @ts-ignore - drizzle typing + // return drizzle(sqlite); + // } +} + +// helpers to pick correct table (for demo) +export const usersTable = + dbKind === "postgres" + ? pgUsers + : dbKind === "mysql" + ? mysqlUsers + : sqliteUsers; diff --git a/apps/coderouter/src/db/schema.ts b/apps/coderouter/src/db/schema.ts new file mode 100644 index 0000000000..5e49302a12 --- /dev/null +++ b/apps/coderouter/src/db/schema.ts @@ -0,0 +1,32 @@ +import { + int, + mysqlTable, + varchar as mysqlVarchar, +} from "drizzle-orm/mysql-core"; +import { pgTable, serial, varchar } from "drizzle-orm/pg-core"; +import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; + +// Simple cross-dialect "users" table definition helpers: +export const sqliteUsers = sqliteTable("users", { + id: integer("id").primaryKey({ autoIncrement: true }), + email: text("email").notNull().unique(), + name: text("name"), +}); + +export const pgUsers = pgTable("users", { + id: serial("id").primaryKey(), + email: varchar("email", { length: 255 }).notNull().unique(), + name: varchar("name", { length: 255 }), +}); + +export const mysqlUsers = mysqlTable("users", { + id: int("id").primaryKey().autoincrement(), + email: mysqlVarchar("email", { length: 255 }).notNull().unique(), + name: mysqlVarchar("name", { length: 255 }), +}); + +export type User = { + id: number; + email: string; + name: string | null; +}; diff --git a/apps/coderouter/src/middleware/auth.test.ts b/apps/coderouter/src/middleware/auth.test.ts new file mode 100644 index 0000000000..ef2f74bd24 --- /dev/null +++ b/apps/coderouter/src/middleware/auth.test.ts @@ -0,0 +1,244 @@ +import { describe, it, expect, beforeEach, afterEach, mock } from 'bun:test'; +import { OpenAPIHono } from '@hono/zod-openapi'; +import { setupAuthJwtMiddleware } from './auth'; +import { decodeJwtToken, encodeJwtToken, AuthJwtPayload } from '@/util/auth'; +import type { LocalHono } from '@/server'; +import { env } from 'bun'; + +// Mock the environment variable +const originalEnv = process.env.JWT_SECRET_KEY; +const mockJwtSecret = 'test-secret-key'; + +describe('Auth JWT Middleware', () => { + let app: LocalHono; + + beforeEach(() => { + // Set up test environment + process.env.JWT_SECRET_KEY = mockJwtSecret; + + // Create a fresh app instance for each test + app = new OpenAPIHono<{ + Variables: { client: any; auth: AuthJwtPayload }; + }>(); + + // Set up the auth middleware + setupAuthJwtMiddleware(app, env.URL_PATH_PREFIX + '/*'); + + // Add a test route to verify middleware works + app.get(env.URL_PATH_PREFIX + '/api/test', (c) => { + const auth = c.get('auth'); + return c.json({ success: true, auth }); + }); + }); + + afterEach(() => { + // Restore original environment + if (originalEnv) { + process.env.JWT_SECRET_KEY = originalEnv; + } else { + delete process.env.JWT_SECRET_KEY; + } + }); + + describe('Missing JWT token', () => { + it('should return 401 when X-Auth-Jwt header is missing', async () => { + const res = await app.request(env.URL_PATH_PREFIX + '/api/test'); + + expect(res.status).toBe(401); + const body = (await res.json()) as { error: string }; + expect(body).toEqual({ error: 'Unauthorized' }); + }); + + it('should return 401 when X-Auth-Jwt header is empty', async () => { + const res = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': '', + }, + }); + + expect(res.status).toBe(401); + const body = (await res.json()) as { error: string }; + expect(body).toEqual({ error: 'Unauthorized' }); + }); + }); + + describe('Valid JWT token', () => { + it('should allow request with valid JWT token', async () => { + const payload: AuthJwtPayload = { + sandboxId: 'test-sandbox-123', + userId: 'test-user-456', + }; + + const token = encodeJwtToken(payload); + + const res = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': token, + }, + }); + + expect(res.status).toBe(200); + const body = (await res.json()) as { + success: boolean; + auth: AuthJwtPayload; + }; + expect(body.success).toBe(true); + expect(body.auth.sandboxId).toBe(payload.sandboxId!); + expect(body.auth.userId).toBe(payload.userId!); + }); + + it('should allow request with minimal JWT payload', async () => { + const payload: AuthJwtPayload = {}; + + const token = encodeJwtToken(payload); + + const res = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': token, + }, + }); + + expect(res.status).toBe(200); + const body = (await res.json()) as { + success: boolean; + auth: AuthJwtPayload; + }; + expect(body.success).toBe(true); + expect(body.auth.sandboxId).toBeUndefined(); + expect(body.auth.userId).toBeUndefined(); + }); + + it('should set auth context for downstream handlers', async () => { + const payload: AuthJwtPayload = { + sandboxId: 'sandbox-789', + userId: 'user-101', + }; + + const token = encodeJwtToken(payload); + + // Add a custom handler that checks the auth context + app.get(env.URL_PATH_PREFIX + '/api/auth-test', (c) => { + const auth = c.get('auth'); + return c.json({ + sandboxId: auth.sandboxId, + userId: auth.userId, + }); + }); + + const res = await app.request(env.URL_PATH_PREFIX + '/api/auth-test', { + headers: { + 'X-Auth-Jwt': token, + }, + }); + + expect(res.status).toBe(200); + const body = (await res.json()) as { + sandboxId?: string; + userId?: string; + }; + expect(body.sandboxId).toBe('sandbox-789'); + expect(body.userId).toBe('user-101'); + }); + }); + + describe('Invalid JWT token', () => { + it('should return 401 when JWT token is malformed', async () => { + const res = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': 'invalid-token', + }, + }); + + expect(res.status).toBe(401); + const body = (await res.json()) as { error: string }; + expect(body).toEqual({ + error: 'Failed to decode JWT. Fetch a new JWT token.', + }); + }); + + it('should return 401 when JWT token is signed with wrong secret', async () => { + // Create a token with a different secret + const payload: AuthJwtPayload = { sandboxId: 'test' }; + const wrongToken = encodeJwtToken(payload); + + // Change the secret to make the token invalid + process.env.JWT_SECRET_KEY = 'different-secret'; + + const res = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': wrongToken, + }, + }); + + expect(res.status).toBe(401); + const body = (await res.json()) as { error: string }; + expect(body).toEqual({ + error: 'Failed to decode JWT. Fetch a new JWT token.', + }); + }); + + it('should return 401 when JWT token is expired', async () => { + // Create an expired token by using a past expiration time + const payload: AuthJwtPayload = { sandboxId: 'test' }; + const expiredToken = encodeJwtToken(payload); + + // Temporarily change the JWT secret to make the token invalid + const originalSecret = process.env.JWT_SECRET_KEY; + process.env.JWT_SECRET_KEY = 'different-secret-for-expired-test'; + + const res = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': expiredToken, + }, + }); + + expect(res.status).toBe(401); + const body = (await res.json()) as { error: string }; + expect(body).toEqual({ + error: 'Failed to decode JWT. Fetch a new JWT token.', + }); + + // Restore original secret + process.env.JWT_SECRET_KEY = originalSecret; + }); + }); + + describe('Middleware scope', () => { + it('should only apply to /coderouter/api/* routes', async () => { + // Add a route outside the /api scope + app.get('/public/test', (c) => { + return c.json({ public: true }); + }); + + const res = await app.request('/public/test'); + + expect(res.status).toBe(200); + const body = (await res.json()) as { public: boolean }; + expect(body).toEqual({ public: true }); + }); + + it('should apply to nested /api routes', async () => { + app.get(env.URL_PATH_PREFIX + '/api/nested/deep/route', (c) => { + const auth = c.get('auth'); + return c.json({ nested: true, auth }); + }); + + const payload: AuthJwtPayload = { sandboxId: 'nested-test' }; + const token = encodeJwtToken(payload); + + const res = await app.request(env.URL_PATH_PREFIX + '/api/nested/deep/route', { + headers: { + 'X-Auth-Jwt': token, + }, + }); + + expect(res.status).toBe(200); + const body = (await res.json()) as { + nested: boolean; + auth: AuthJwtPayload; + }; + expect(body.nested).toBe(true); + expect(body.auth.sandboxId).toBe(payload.sandboxId!); + }); + }); +}); diff --git a/apps/coderouter/src/middleware/auth.ts b/apps/coderouter/src/middleware/auth.ts new file mode 100644 index 0000000000..bd64d6d474 --- /dev/null +++ b/apps/coderouter/src/middleware/auth.ts @@ -0,0 +1,23 @@ +import { LocalHono } from "@/server"; +import { AuthJwtPayload, decodeJwtToken } from "@/util/auth"; + +export function setupAuthJwtMiddleware(app: LocalHono, path: string) { + return app.use(path, async (c, next) => { + const jwt = c.req.header("X-Auth-Jwt"); + if (!jwt) { + return c.json({ error: "Unauthorized" }, 401); + } + try { + const payload = decodeJwtToken(jwt) as AuthJwtPayload; + c.set("auth", payload); + } catch (e) { + console.error("Failed to decode JWT.", e); + return c.json( + { error: "Failed to decode JWT. Fetch a new JWT token." }, + 401 + ); + } + + await next(); + }); +} diff --git a/apps/coderouter/src/middleware/beforeSandboxCall.ts b/apps/coderouter/src/middleware/beforeSandboxCall.ts new file mode 100644 index 0000000000..07fade6155 --- /dev/null +++ b/apps/coderouter/src/middleware/beforeSandboxCall.ts @@ -0,0 +1,13 @@ +import { LocalHono } from '@/server'; + +export function setupBeforeSandboxCallMiddleware(app: LocalHono, path: string) { + return app.use(path, async (c, next) => { + const client = c.get('client'); + if (!client) { + console.error('The provider client is not set. Please check the middleware setup.'); + return c.json({ error: 'Client not found' }, 500); + } + await client.sandbox.beforeSandboxCall(); + await next(); + }); +} diff --git a/apps/coderouter/src/middleware/client.test.ts b/apps/coderouter/src/middleware/client.test.ts new file mode 100644 index 0000000000..4b1b48e72c --- /dev/null +++ b/apps/coderouter/src/middleware/client.test.ts @@ -0,0 +1,277 @@ +import { describe, it, expect, beforeEach, afterEach } from 'bun:test'; +import { OpenAPIHono } from '@hono/zod-openapi'; +import { setupClientMiddleware } from './client'; +import { setupAuthJwtMiddleware } from './auth'; +import type { LocalHono } from '@/server'; +import { AuthJwtPayload } from '@/util/auth'; +import { encodeJwtToken } from '@/util/auth'; + +// Store original environment +const originalEnv = process.env.E2B_API_KEY; +const originalJwtEnv = process.env.JWT_SECRET_KEY; + +describe('setupClientMiddleware', () => { + let app: LocalHono; + + beforeEach(() => { + // Set up test environment + process.env.JWT_SECRET_KEY = 'test-jwt-secret'; + + // Create a fresh app instance for each test + app = new OpenAPIHono<{ + Variables: { client: any; auth: AuthJwtPayload }; + }>(); + + // Set up auth middleware first (required for client middleware) + setupAuthJwtMiddleware(app); + + // Set up client middleware + setupClientMiddleware(app); + + // Add a test route to verify middleware works + app.get(env.URL_PATH_PREFIX + '/api/test', (c) => { + const client = c.get('client'); + const auth = c.get('auth'); + return c.json({ + success: true, + hasClient: !!client, + clientType: client?.constructor?.name, + auth, + }); + }); + }); + + afterEach(() => { + // Restore original environment + if (originalEnv) { + process.env.E2B_API_KEY = originalEnv; + } else { + delete process.env.E2B_API_KEY; + } + + if (originalJwtEnv) { + process.env.JWT_SECRET_KEY = originalJwtEnv; + } else { + delete process.env.JWT_SECRET_KEY; + } + }); + + describe('when E2B_API_KEY is set', () => { + beforeEach(() => { + process.env.E2B_API_KEY = 'test-api-key'; + }); + + it('should create E2BClient and set it in context', async () => { + const payload: AuthJwtPayload = { + sandboxId: 'test-sandbox-id', + userId: 'test-user-id', + }; + + const token = encodeJwtToken(payload); + + const res = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': token, + }, + }); + + expect(res.status).toBe(200); + const body = (await res.json()) as { + success: boolean; + hasClient: boolean; + clientType: string; + auth: AuthJwtPayload; + }; + expect(body.success).toBe(true); + expect(body.hasClient).toBe(true); + expect(body.clientType).toBe('E2BClient'); + expect(body.auth.sandboxId).toBe('test-sandbox-id'); + expect(body.auth.userId).toBe('test-user-id'); + }); + + it('should handle auth data with undefined values', async () => { + const payload: AuthJwtPayload = { + sandboxId: undefined, + userId: undefined, + }; + + const token = encodeJwtToken(payload); + + const res = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': token, + }, + }); + + expect(res.status).toBe(200); + const body = (await res.json()) as { + success: boolean; + hasClient: boolean; + clientType: string; + auth: AuthJwtPayload; + }; + expect(body.success).toBe(true); + expect(body.hasClient).toBe(true); + expect(body.auth.sandboxId).toBeUndefined(); + expect(body.auth.userId).toBeUndefined(); + }); + + it('should handle partial auth data', async () => { + const payload: AuthJwtPayload = { + sandboxId: 'test-sandbox-id', + userId: undefined, + }; + + const token = encodeJwtToken(payload); + + const res = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': token, + }, + }); + + expect(res.status).toBe(200); + const body = (await res.json()) as { + success: boolean; + hasClient: boolean; + clientType: string; + auth: AuthJwtPayload; + }; + expect(body.success).toBe(true); + expect(body.hasClient).toBe(true); + expect(body.auth.sandboxId).toBe('test-sandbox-id'); + expect(body.auth.userId).toBeUndefined(); + }); + }); + + describe('when E2B_API_KEY is not set', () => { + beforeEach(() => { + process.env.E2B_API_KEY = undefined; + }); + + it('should return 500 error response and not continue to route handler', async () => { + const payload: AuthJwtPayload = { + sandboxId: 'test-sandbox-id', + userId: 'test-user-id', + }; + + const token = encodeJwtToken(payload); + + const res = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': token, + }, + }); + + expect(res.status).toBe(500); + const body = (await res.json()) as { error: string }; + expect(body).toEqual({ + error: '"E2B_API_KEY" is not set', + }); + }); + + it('should return 500 error response even with valid auth data', async () => { + const payload: AuthJwtPayload = { + sandboxId: 'test-sandbox-id', + userId: 'test-user-id', + }; + + const token = encodeJwtToken(payload); + + const res = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': token, + }, + }); + + expect(res.status).toBe(500); + const body = (await res.json()) as { error: string }; + expect(body).toEqual({ + error: '"E2B_API_KEY" is not set', + }); + }); + + it('should return 500 error response for any API route', async () => { + // Add another API route + app.get(env.URL_PATH_PREFIX + '/api/another-test', (c) => { + return c.json({ another: true }); + }); + + const payload: AuthJwtPayload = { + sandboxId: 'test-sandbox-id', + userId: 'test-user-id', + }; + + const token = encodeJwtToken(payload); + + const res = await app.request(env.URL_PATH_PREFIX + '/api/another-test', { + headers: { + 'X-Auth-Jwt': token, + }, + }); + + expect(res.status).toBe(500); + const body = (await res.json()) as { error: string }; + expect(body).toEqual({ + error: '"E2B_API_KEY" is not set', + }); + }); + }); + + describe('middleware integration', () => { + it('should apply middleware to /coderouter/api/* routes', () => { + process.env.E2B_API_KEY = 'test-api-key'; + const result = setupClientMiddleware(app); + + // The middleware should return the app + expect(result).toBe(app); + }); + + it('should not apply middleware to non-api routes', async () => { + process.env.E2B_API_KEY = 'test-api-key'; + + // Add a route outside the /api scope + app.get('/public/test', (c) => { + return c.json({ public: true }); + }); + + const res = await app.request('/public/test'); + + expect(res.status).toBe(200); + const body = (await res.json()) as { public: boolean }; + expect(body).toEqual({ public: true }); + }); + + it('should handle multiple requests correctly', async () => { + process.env.E2B_API_KEY = 'test-api-key'; + + const payload: AuthJwtPayload = { + sandboxId: 'test-sandbox-id', + userId: 'test-user-id', + }; + + const token = encodeJwtToken(payload); + + // Make multiple requests + const res1 = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': token, + }, + }); + + const res2 = await app.request(env.URL_PATH_PREFIX + '/api/test', { + headers: { + 'X-Auth-Jwt': token, + }, + }); + + expect(res1.status).toBe(200); + expect(res2.status).toBe(200); + + const body1 = (await res1.json()) as { hasClient: boolean }; + const body2 = (await res2.json()) as { hasClient: boolean }; + expect(body1.hasClient).toBe(true); + expect(body2.hasClient).toBe(true); + }); + }); +}); diff --git a/apps/coderouter/src/middleware/client.ts b/apps/coderouter/src/middleware/client.ts new file mode 100644 index 0000000000..d4f0a0ecf6 --- /dev/null +++ b/apps/coderouter/src/middleware/client.ts @@ -0,0 +1,43 @@ +import { E2BClient } from '@/provider/e2b'; +import { env } from 'bun'; +import { LocalHono } from '@/server'; +import { CodesandboxClient } from '@/provider/codesandbox'; + +export function setupClientMiddleware(app: LocalHono, path: string) { + return app.use(path, async (c, next) => { + if (env.E2B_API_KEY) { + const auth = c.get('auth'); + c.set( + 'client', + new E2BClient( + { + sandboxId: auth?.sandboxId, + userId: auth?.userId, + }, + env.E2B_API_KEY, + ), + ); + await next(); + } else if (env.CSB_API_KEY) { + const auth = c.get('auth'); + c.set( + 'client', + new CodesandboxClient( + { + sandboxId: auth?.sandboxId, + userId: auth?.userId, + }, + env.CSB_API_KEY, + ), + ); + await next(); + } else { + return c.json( + { + error: `"E2B_API_KEY" is not set`, + }, + 500, + ); + } + }); +} diff --git a/apps/coderouter/src/middleware/error.ts b/apps/coderouter/src/middleware/error.ts new file mode 100644 index 0000000000..9d8af56952 --- /dev/null +++ b/apps/coderouter/src/middleware/error.ts @@ -0,0 +1,27 @@ +import { LocalHono } from "@/server"; +import { ClientError } from "@/provider/definition"; +import { HTTPException } from "hono/http-exception"; +import { ContentfulStatusCode } from "hono/utils/http-status"; + +export function setupErrorMiddleware(app: LocalHono) { + return app.onError(async (err, c) => { + if (err instanceof ClientError) { + const res = c.json( + { + error: err.message, + }, + err.status as ContentfulStatusCode + ); + return res; + } + console.error( + `[${c.req.method}] ${c.req.url}`, + "Unhandled error. Please convert to ClientError.", + err.toString() + ); + throw new HTTPException(500, { + message: "Internal server error", + cause: err, + }); + }); +} diff --git a/apps/coderouter/src/middleware/requireSandboxId.test.ts b/apps/coderouter/src/middleware/requireSandboxId.test.ts new file mode 100644 index 0000000000..6a21c9e393 --- /dev/null +++ b/apps/coderouter/src/middleware/requireSandboxId.test.ts @@ -0,0 +1,318 @@ +import { describe, it, expect } from "bun:test"; +import { OpenAPIHono } from "@hono/zod-openapi"; +import { setupRequiredSandboxIdMiddleware } from "./requireSandboxId"; +import { AuthJwtPayload } from "@/util/auth"; +import type { LocalHono } from "@/server"; + +describe("requireSandboxId middleware", () => { + describe("when sandboxId is present in auth context", () => { + it("should allow the request to proceed when sandboxId is a string", async () => { + const app = new OpenAPIHono<{ + Variables: { client: any; auth: AuthJwtPayload }; + }>(); + + // Set up auth context first + app.use(env.URL_PATH_PREFIX + "/api/test", async (c, next) => { + c.set("auth", { + sandboxId: "test-sandbox-123", + userId: "test-user-456", + }); + await next(); + }); + + // Apply the requireSandboxId middleware + setupRequiredSandboxIdMiddleware(app, env.URL_PATH_PREFIX + "/api/test"); + + // Add the route handler + app.get(env.URL_PATH_PREFIX + "/api/test", (c) => { + const auth = c.get("auth"); + return c.json({ + success: true, + sandboxId: auth?.sandboxId, + userId: auth?.userId, + }); + }); + + const res = await app.request(env.URL_PATH_PREFIX + "/api/test"); + + expect(res.status).toBe(200); + const body = (await res.json()) as { + success: boolean; + sandboxId: string; + userId: string; + }; + expect(body.success).toBe(true); + expect(body.sandboxId).toBe("test-sandbox-123"); + expect(body.userId).toBe("test-user-456"); + }); + + it("should allow the request to proceed when sandboxId is present but userId is undefined", async () => { + const app = new OpenAPIHono<{ + Variables: { client: any; auth: AuthJwtPayload }; + }>(); + + // Set up auth context first + app.use(env.URL_PATH_PREFIX + "/api/test", async (c, next) => { + c.set("auth", { + sandboxId: "test-sandbox-123", + userId: undefined, + }); + await next(); + }); + + // Apply the requireSandboxId middleware + setupRequiredSandboxIdMiddleware(app, env.URL_PATH_PREFIX + "/api/test"); + + // Add the route handler + app.get(env.URL_PATH_PREFIX + "/api/test", (c) => { + const auth = c.get("auth"); + return c.json({ + success: true, + sandboxId: auth?.sandboxId, + userId: auth?.userId, + }); + }); + + const res = await app.request(env.URL_PATH_PREFIX + "/api/test"); + + expect(res.status).toBe(200); + const body = (await res.json()) as { + success: boolean; + sandboxId: string; + userId: string | undefined; + }; + expect(body.success).toBe(true); + expect(body.sandboxId).toBe("test-sandbox-123"); + expect(body.userId).toBeUndefined(); + }); + + it("should allow the request to proceed when only sandboxId is present", async () => { + const app = new OpenAPIHono<{ + Variables: { client: any; auth: AuthJwtPayload }; + }>(); + + // Set up auth context first + app.use(env.URL_PATH_PREFIX + "/api/test", async (c, next) => { + c.set("auth", { + sandboxId: "test-sandbox-123", + }); + await next(); + }); + + // Apply the requireSandboxId middleware + setupRequiredSandboxIdMiddleware(app, env.URL_PATH_PREFIX + "/api/test"); + + // Add the route handler + app.get(env.URL_PATH_PREFIX + "/api/test", (c) => { + const auth = c.get("auth"); + return c.json({ + success: true, + sandboxId: auth?.sandboxId, + userId: auth?.userId, + }); + }); + + const res = await app.request(env.URL_PATH_PREFIX + "/api/test"); + + expect(res.status).toBe(200); + const body = (await res.json()) as { + success: boolean; + sandboxId: string; + userId: string | undefined; + }; + expect(body.success).toBe(true); + expect(body.sandboxId).toBe("test-sandbox-123"); + expect(body.userId).toBeUndefined(); + }); + }); + + describe("when sandboxId is missing from auth context", () => { + it("should return 400 error when sandboxId is undefined", async () => { + const app = new OpenAPIHono<{ + Variables: { client: any; auth: AuthJwtPayload }; + }>(); + + // Set up auth context first + app.use(env.URL_PATH_PREFIX + "/api/test", async (c, next) => { + c.set("auth", { + sandboxId: undefined, + userId: "test-user-456", + }); + await next(); + }); + + // Apply the requireSandboxId middleware + setupRequiredSandboxIdMiddleware(app, env.URL_PATH_PREFIX + "/api/test"); + + // Add the route handler (should not be reached) + app.get(env.URL_PATH_PREFIX + "/api/test", (c) => { + const auth = c.get("auth"); + return c.json({ + success: true, + sandboxId: auth?.sandboxId, + userId: auth?.userId, + }); + }); + + const res = await app.request(env.URL_PATH_PREFIX + "/api/test"); + + expect(res.status).toBe(400); + const body = (await res.json()) as { error: string }; + expect(body.error).toBe('"sandboxId" is not set in JWT token.'); + }); + + it("should return 400 error when sandboxId is not present in auth object", async () => { + const app = new OpenAPIHono<{ + Variables: { client: any; auth: AuthJwtPayload }; + }>(); + + // Set up auth context first + app.use(env.URL_PATH_PREFIX + "/api/test", async (c, next) => { + c.set("auth", { + userId: "test-user-456", + }); + await next(); + }); + + // Apply the requireSandboxId middleware + setupRequiredSandboxIdMiddleware(app, env.URL_PATH_PREFIX + "/api/test"); + + // Add the route handler (should not be reached) + app.get(env.URL_PATH_PREFIX + "/api/test", (c) => { + const auth = c.get("auth"); + return c.json({ + success: true, + sandboxId: auth?.sandboxId, + userId: auth?.userId, + }); + }); + + const res = await app.request(env.URL_PATH_PREFIX + "/api/test"); + + expect(res.status).toBe(400); + const body = (await res.json()) as { error: string }; + expect(body.error).toBe('"sandboxId" is not set in JWT token.'); + }); + + it("should return 400 error when auth object is empty", async () => { + const app = new OpenAPIHono<{ + Variables: { client: any; auth: AuthJwtPayload }; + }>(); + + // Set up auth context first + app.use(env.URL_PATH_PREFIX + "/api/test", async (c, next) => { + c.set("auth", {}); + await next(); + }); + + // Apply the requireSandboxId middleware + setupRequiredSandboxIdMiddleware(app, env.URL_PATH_PREFIX + "/api/test"); + + // Add the route handler (should not be reached) + app.get(env.URL_PATH_PREFIX + "/api/test", (c) => { + const auth = c.get("auth"); + return c.json({ + success: true, + sandboxId: auth?.sandboxId, + userId: auth?.userId, + }); + }); + + const res = await app.request(env.URL_PATH_PREFIX + "/api/test"); + + expect(res.status).toBe(400); + const body = (await res.json()) as { error: string }; + expect(body.error).toBe('"sandboxId" is not set in JWT token.'); + }); + + it("should return 400 error when auth context is not set", async () => { + const app = new OpenAPIHono<{ + Variables: { client: any; auth: AuthJwtPayload }; + }>(); + + // Apply the requireSandboxId middleware without setting auth context + setupRequiredSandboxIdMiddleware(app, env.URL_PATH_PREFIX + "/api/test"); + + // Add the route handler (should not be reached) + app.get(env.URL_PATH_PREFIX + "/api/test", (c) => { + const auth = c.get("auth"); + return c.json({ + success: true, + sandboxId: auth?.sandboxId, + userId: auth?.userId, + }); + }); + + const res = await app.request(env.URL_PATH_PREFIX + "/api/test"); + + expect(res.status).toBe(400); + const body = (await res.json()) as { error: string }; + expect(body.error).toBe('"sandboxId" is not set in JWT token.'); + }); + }); + + describe("middleware path matching", () => { + it("should only apply to the specified path", async () => { + const app = new OpenAPIHono<{ + Variables: { client: any; auth: AuthJwtPayload }; + }>(); + + // Apply the requireSandboxId middleware to a specific path + setupRequiredSandboxIdMiddleware(app, env.URL_PATH_PREFIX + "/api/test"); + + // Add a route that doesn't use the middleware + app.get(env.URL_PATH_PREFIX + "/api/other", (c) => { + return c.json({ success: true, message: "other route" }); + }); + + const res = await app.request(env.URL_PATH_PREFIX + "/api/other"); + + expect(res.status).toBe(200); + const body = (await res.json()) as { success: boolean; message: string }; + expect(body.success).toBe(true); + expect(body.message).toBe("other route"); + }); + + it("should apply to sub-paths of the specified path", async () => { + const app = new OpenAPIHono<{ + Variables: { client: any; auth: AuthJwtPayload }; + }>(); + + // Set up auth context first + app.use(env.URL_PATH_PREFIX + "/api/test/sub", async (c, next) => { + c.set("auth", { + sandboxId: "test-sandbox-123", + }); + await next(); + }); + + // Apply the requireSandboxId middleware to the sub-path + setupRequiredSandboxIdMiddleware( + app, + env.URL_PATH_PREFIX + "/api/test/sub" + ); + + // Add the route handler + app.get(env.URL_PATH_PREFIX + "/api/test/sub", (c) => { + const auth = c.get("auth"); + return c.json({ + success: true, + sandboxId: auth?.sandboxId, + path: "sub", + }); + }); + + const res = await app.request(env.URL_PATH_PREFIX + "/api/test/sub"); + + expect(res.status).toBe(200); + const body = (await res.json()) as { + success: boolean; + sandboxId: string; + path: string; + }; + expect(body.success).toBe(true); + expect(body.sandboxId).toBe("test-sandbox-123"); + expect(body.path).toBe("sub"); + }); + }); +}); diff --git a/apps/coderouter/src/middleware/requireSandboxId.ts b/apps/coderouter/src/middleware/requireSandboxId.ts new file mode 100644 index 0000000000..42aa69a572 --- /dev/null +++ b/apps/coderouter/src/middleware/requireSandboxId.ts @@ -0,0 +1,16 @@ +import { LocalHono } from "@/server"; + +export function setupRequiredSandboxIdMiddleware(app: LocalHono, path: string) { + return app.use(path, async (c, next) => { + const auth = c.get("auth"); + if (!auth || !auth.sandboxId) { + return c.json( + { + error: `"sandboxId" is not set in JWT token.`, + }, + 400 + ); + } + await next(); + }); +} diff --git a/apps/coderouter/src/provider/codesandbox/index.ts b/apps/coderouter/src/provider/codesandbox/index.ts new file mode 100644 index 0000000000..27c44e5a71 --- /dev/null +++ b/apps/coderouter/src/provider/codesandbox/index.ts @@ -0,0 +1,21 @@ +import { CodeSandbox, RestSession, Sandbox } from '@codesandbox/sdk'; +import { Client, ClientOptions } from '../definition'; +import { CodesandboxSandbox } from './sandbox'; + +export class CodesandboxClient extends Client { + public readonly sandbox: CodesandboxSandbox; + + // property is initialized by the sandbox class + // this is the Codesandbox sandbox instance + _sdk?: CodeSandbox; + _sandbox?: Sandbox; + _client?: RestSession; + + constructor( + options: ClientOptions, + public readonly apiKey: string, + ) { + super(options); + this.sandbox = new CodesandboxSandbox(this); + } +} diff --git a/apps/coderouter/src/provider/codesandbox/sandbox/file/index.test.ts b/apps/coderouter/src/provider/codesandbox/sandbox/file/index.test.ts new file mode 100644 index 0000000000..a0442ee119 --- /dev/null +++ b/apps/coderouter/src/provider/codesandbox/sandbox/file/index.test.ts @@ -0,0 +1,462 @@ +import { describe, expect, it, beforeEach, jest } from 'bun:test'; +import { CodesandboxClient } from '@/provider/e2b/index'; +import { CodesandboxSandboxFile } from './index'; +import { ClientError, ClientErrorCode, ClientOptions } from '@/provider/definition'; + +// Create a mock sandbox object that matches the expected interface +const createMockSandbox = () => + ({ + files: { + exists: jest.fn(), + read: jest.fn(), + write: jest.fn(), + remove: jest.fn(), + rename: jest.fn(), + list: jest.fn(), + getInfo: jest.fn(), + }, + create: jest.fn(), + kill: jest.fn(), + // Add other required properties to satisfy the Sandbox interface + runCode: jest.fn(), + createCodeContext: jest.fn(), + jupyterUrl: 'http://localhost:8888', + commands: jest.fn(), + sandboxId: 'test-sandbox-id', + process: { pid: 123 }, + close: jest.fn(), + restart: jest.fn(), + getLogs: jest.fn(), + getProcessLogs: jest.fn(), + getStdout: jest.fn(), + getStderr: jest.fn(), + getExitCode: jest.fn(), + getStatus: jest.fn(), + getMetadata: jest.fn(), + updateMetadata: jest.fn(), + getEnvironmentVariables: jest.fn(), + updateEnvironmentVariables: jest.fn(), + getPorts: jest.fn(), + getOpenPorts: jest.fn(), + getOpenPort: jest.fn(), + getPort: jest.fn(), + openPort: jest.fn(), + closePort: jest.fn(), + getHostname: jest.fn(), + getUrl: jest.fn(), + getLocalUrl: jest.fn(), + getPublicUrl: jest.fn(), + getLocalPort: jest.fn(), + getPublicPort: jest.fn(), + getLocalHostname: jest.fn(), + getPublicHostname: jest.fn(), + getLocalProtocol: jest.fn(), + getPublicProtocol: jest.fn(), + getLocalScheme: jest.fn(), + getPublicScheme: jest.fn(), + getLocalAuthority: jest.fn(), + getPublicAuthority: jest.fn(), + getLocalPath: jest.fn(), + getPublicPath: jest.fn(), + getLocalQuery: jest.fn(), + getPublicQuery: jest.fn(), + getLocalFragment: jest.fn(), + getPublicFragment: jest.fn(), + getLocalUserInfo: jest.fn(), + getPublicUserInfo: jest.fn(), + getLocalUsername: jest.fn(), + getPublicUsername: jest.fn(), + getLocalPassword: jest.fn(), + getPublicPassword: jest.fn(), + getLocalHost: jest.fn(), + getPublicHost: jest.fn(), + getLocalPortNumber: jest.fn(), + getPublicPortNumber: jest.fn(), + getLocalHostnameString: jest.fn(), + getPublicHostnameString: jest.fn(), + getLocalProtocolString: jest.fn(), + getPublicProtocolString: jest.fn(), + getLocalSchemeString: jest.fn(), + getPublicSchemeString: jest.fn(), + getLocalAuthorityString: jest.fn(), + getPublicAuthorityString: jest.fn(), + getLocalPathString: jest.fn(), + getPublicPathString: jest.fn(), + getLocalQueryString: jest.fn(), + getPublicQueryString: jest.fn(), + getLocalFragmentString: jest.fn(), + getPublicFragmentString: jest.fn(), + getLocalUserInfoString: jest.fn(), + getPublicUserInfoString: jest.fn(), + getLocalUsernameString: jest.fn(), + getPublicUsernameString: jest.fn(), + getLocalPasswordString: jest.fn(), + getPublicPasswordString: jest.fn(), + getLocalHostString: jest.fn(), + getPublicHostString: jest.fn(), + getLocalPortNumberString: jest.fn(), + getPublicPortNumberString: jest.fn(), + getLocalHostnameNumber: jest.fn(), + getPublicHostnameNumber: jest.fn(), + getLocalProtocolNumber: jest.fn(), + getPublicProtocolNumber: jest.fn(), + getLocalSchemeNumber: jest.fn(), + getPublicSchemeNumber: jest.fn(), + getLocalAuthorityNumber: jest.fn(), + getPublicAuthorityNumber: jest.fn(), + getLocalPathNumber: jest.fn(), + getPublicPathNumber: jest.fn(), + getLocalQueryNumber: jest.fn(), + getPublicQueryNumber: jest.fn(), + getLocalFragmentNumber: jest.fn(), + getPublicFragmentNumber: jest.fn(), + getLocalUserInfoNumber: jest.fn(), + getPublicUserInfoNumber: jest.fn(), + getLocalUsernameNumber: jest.fn(), + getPublicUsernameNumber: jest.fn(), + getLocalPasswordNumber: jest.fn(), + getPublicPasswordNumber: jest.fn(), + getLocalHostNumber: jest.fn(), + getPublicHostNumber: jest.fn(), + getLocalPortNumberNumber: jest.fn(), + getPublicPortNumberNumber: jest.fn(), + }) as any; + +class CodesandboxMockClient extends CodesandboxClient { + private _mockSandbox = createMockSandbox(); + + constructor(options: ClientOptions) { + super(options, 'test-api-key'); + } + + _sandbox = this._mockSandbox; + + // Method to set sandbox to null for testing error cases + setSandboxNull() { + this._mockSandbox = null as any; + this._sandbox = null as any; + } + + // Method to restore sandbox for testing + restoreSandbox() { + this._mockSandbox = createMockSandbox(); + this._sandbox = this._mockSandbox; + } +} + +describe('CodesandboxSandboxFile', () => { + let mockClient: CodesandboxMockClient; + let sandboxFile: CodesandboxSandboxFile; + + beforeEach(() => { + mockClient = new CodesandboxMockClient({ sandboxId: 'test-sandbox-id' }); + sandboxFile = new CodesandboxSandboxFile(mockClient); + jest.clearAllMocks(); + }); + + describe('copy', () => { + it('should throw Unimplemented error', async () => { + await expect(sandboxFile.copy({ oldPath: '/old', newPath: '/new' })).rejects.toThrow( + new ClientError(ClientErrorCode.Unimplemented, 'Not implemented', false), + ); + }); + }); + + describe('delete', () => { + it('should delete file successfully', async () => { + const input = { path: '/test/file.txt' }; + + await sandboxFile.delete(input); + + expect(mockClient._sandbox.files.remove).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.remove).toHaveBeenCalledWith('/test/file.txt'); + }); + + it('should throw error when sandbox is not instantiated', async () => { + mockClient.setSandboxNull(); + + const input = { path: '/test/file.txt' }; + + await expect(sandboxFile.delete(input)).rejects.toThrow( + new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ), + ); + + // Note: We can't check mockClient._sandbox.files.remove since sandbox is null + }); + }); + + describe('download', () => { + it('should throw Unimplemented error', async () => { + await expect(sandboxFile.download({ path: '/test/file.txt' })).rejects.toThrow( + new ClientError(ClientErrorCode.Unimplemented, 'Not implemented', false), + ); + }); + }); + + describe('list', () => { + it('should list files successfully', async () => { + const mockFiles = [ + { name: 'file1.txt', path: '/dir/file1.txt', type: 'file' }, + { name: 'subdir', path: '/dir/subdir', type: 'directory' }, + ]; + + (mockClient._sandbox.files.list as any).mockResolvedValue(mockFiles); + + const input = { path: '/dir' }; + const result = await sandboxFile.list(input); + + expect(mockClient._sandbox.files.list).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.list).toHaveBeenCalledWith('/dir'); + expect(result).toEqual({ + files: [ + { name: 'file1.txt', path: '/dir/file1.txt', type: 'file' }, + { name: 'subdir', path: '/dir/subdir', type: 'directory' }, + ], + }); + }); + + it('should throw error when sandbox is not instantiated', async () => { + mockClient.setSandboxNull(); + + const input = { path: '/dir' }; + + await expect(sandboxFile.list(input)).rejects.toThrow( + new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ), + ); + }); + }); + + describe('read', () => { + it('should read file successfully', async () => { + const mockData = 'file content'; + (mockClient._sandbox.files.read as any).mockResolvedValue(mockData); + + const input = { path: '/test/file.txt' }; + const result = await sandboxFile.read(input); + + expect(mockClient._sandbox.files.read).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.read).toHaveBeenCalledWith('/test/file.txt'); + expect(result).toEqual({ data: mockData }); + }); + + it("should throw FileNotFound error when file doesn't exist", async () => { + (mockClient._sandbox.files.read as any).mockResolvedValue(null); + + const input = { path: '/test/file.txt' }; + + await expect(sandboxFile.read(input)).rejects.toThrow( + new ClientError(ClientErrorCode.FileNotFound, 'File not found', false), + ); + + expect(mockClient._sandbox.files.read).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.read).toHaveBeenCalledWith('/test/file.txt'); + }); + + it('should throw error when sandbox is not instantiated', async () => { + mockClient.setSandboxNull(); + + const input = { path: '/test/file.txt' }; + + await expect(sandboxFile.read(input)).rejects.toThrow( + new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ), + ); + }); + }); + + describe('rename', () => { + it('should rename file successfully', async () => { + const input = { oldPath: '/old/file.txt', newPath: '/new/file.txt' }; + + await sandboxFile.rename(input); + + expect(mockClient._sandbox.files.rename).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.rename).toHaveBeenCalledWith( + '/old/file.txt', + '/new/file.txt', + ); + }); + + it('should throw error when sandbox is not instantiated', async () => { + mockClient.setSandboxNull(); + + const input = { oldPath: '/old/file.txt', newPath: '/new/file.txt' }; + + await expect(sandboxFile.rename(input)).rejects.toThrow( + new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ), + ); + }); + }); + + describe('stat', () => { + it('should get file info successfully for file', async () => { + const mockFileInfo = { type: 'file' }; + (mockClient._sandbox.files.getInfo as any).mockResolvedValue(mockFileInfo); + + const input = { path: '/test/file.txt' }; + const result = await sandboxFile.stat(input); + + expect(mockClient._sandbox.files.getInfo).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.getInfo).toHaveBeenCalledWith('/test/file.txt'); + expect(result).toEqual({ type: 'file' }); + }); + + it('should get file info successfully for directory', async () => { + const mockFileInfo = { type: 'directory' }; + (mockClient._sandbox.files.getInfo as any).mockResolvedValue(mockFileInfo); + + const input = { path: '/test/dir' }; + const result = await sandboxFile.stat(input); + + expect(mockClient._sandbox.files.getInfo).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.getInfo).toHaveBeenCalledWith('/test/dir'); + expect(result).toEqual({ type: 'directory' }); + }); + + it("should throw FileNotFound error when file doesn't exist", async () => { + (mockClient._sandbox.files.getInfo as any).mockResolvedValue(null); + + const input = { path: '/test/file.txt' }; + + await expect(sandboxFile.stat(input)).rejects.toThrow( + new ClientError(ClientErrorCode.FileNotFound, 'File not found', false), + ); + + expect(mockClient._sandbox.files.getInfo).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.getInfo).toHaveBeenCalledWith('/test/file.txt'); + }); + + it('should throw error when sandbox is not instantiated', async () => { + mockClient.setSandboxNull(); + + const input = { path: '/test/file.txt' }; + + await expect(sandboxFile.stat(input)).rejects.toThrow( + new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ), + ); + }); + }); + + describe('write', () => { + it('should write single file successfully', async () => { + const input = { + files: [ + { + path: '/test/file.txt', + data: 'file content', + overwrite: true, + }, + ], + }; + + await sandboxFile.write(input); + + expect(mockClient._sandbox.files.write).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.write).toHaveBeenCalledWith([ + { path: '/test/file.txt', data: 'file content' }, + ]); + }); + + it('should write multiple files successfully', async () => { + const input = { + files: [ + { path: '/test/file1.txt', data: 'content1', overwrite: true }, + { path: '/test/file2.txt', data: 'content2', overwrite: false }, + ], + }; + + await sandboxFile.write(input); + + expect(mockClient._sandbox.files.write).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.write).toHaveBeenCalledWith([ + { path: '/test/file1.txt', data: 'content1' }, + { path: '/test/file2.txt', data: 'content2' }, + ]); + }); + + it('should skip existing files when overwrite is false', async () => { + (mockClient._sandbox.files.exists as any).mockResolvedValue(true); + + const input = { + files: [ + { + path: '/test/file.txt', + data: 'file content', + overwrite: false, + }, + ], + }; + + await sandboxFile.write(input); + + expect(mockClient._sandbox.files.exists).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.exists).toHaveBeenCalledWith('/test/file.txt'); + expect(mockClient._sandbox.files.write).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.write).toHaveBeenCalledWith([]); + }); + + it("should write file when overwrite is false and file doesn't exist", async () => { + (mockClient._sandbox.files.exists as any).mockResolvedValue(false); + + const input = { + files: [ + { + path: '/test/file.txt', + data: 'file content', + overwrite: false, + }, + ], + }; + + await sandboxFile.write(input); + + expect(mockClient._sandbox.files.exists).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.exists).toHaveBeenCalledWith('/test/file.txt'); + expect(mockClient._sandbox.files.write).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.write).toHaveBeenCalledWith([ + { path: '/test/file.txt', data: 'file content' }, + ]); + }); + + it('should throw error when sandbox is not instantiated', async () => { + mockClient.setSandboxNull(); + + const input = { + files: [ + { + path: '/test/file.txt', + data: 'file content', + overwrite: true, + }, + ], + }; + + await expect(sandboxFile.write(input)).rejects.toThrow( + new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ), + ); + }); + }); +}); diff --git a/apps/coderouter/src/provider/codesandbox/sandbox/file/index.ts b/apps/coderouter/src/provider/codesandbox/sandbox/file/index.ts new file mode 100644 index 0000000000..66393504e7 --- /dev/null +++ b/apps/coderouter/src/provider/codesandbox/sandbox/file/index.ts @@ -0,0 +1,313 @@ +import { ClientError, ClientErrorCode } from '@/provider/definition'; +import { CodesandboxClient } from '../..'; +import { + SandboxFile, + SandboxFileCopyInput, + SandboxFileCopyOutput, + SandboxFileDeleteInput, + SandboxFileDeleteOutput, + SandboxFileDownloadInput, + SandboxFileDownloadOutput, + SandboxFileListInput, + SandboxFileListOutput, + SandboxFileReadInput, + SandboxFileReadOutput, + SandboxFileRenameInput, + SandboxFileRenameOutput, + SandboxFileStatInput, + SandboxFileStatOutput, + SandboxFileWriteOutput, + SandboxFileWriteInput, + SandboxFileWatchInput, + SandboxFileWatchOutput, +} from '../../../definition/sandbox/file'; +import { Watcher as CodesandboxWatcher } from '@codesandbox/sdk'; +// import { NotFoundError, WatchHandle } from '@e2b/code-interpreter'; +import path from 'path'; +import { v4 as uuid } from 'uuid'; + +const watchers: Map = new Map(); + +export class CodesandboxSandboxFile extends SandboxFile { + // the folder to store the files in the sandbox + // when creating a new template, the code must be stored in this folder + protected folder: string = '/code'; + + constructor(client: CodesandboxClient) { + super(client); + } + + async copy(input: SandboxFileCopyInput): Promise { + throw new ClientError(ClientErrorCode.Unimplemented, 'Not implemented', false); + } + + async delete(input: SandboxFileDeleteInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + const client = await this.client._sandbox.connect(); + await client.fs.remove(this.fullpath(input.path)); + return {}; + } + + async download(input: SandboxFileDownloadInput): Promise { + throw new ClientError(ClientErrorCode.Unimplemented, 'Not implemented', false); + } + + async list(input: SandboxFileListInput): Promise { + if (!this.client._client) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Client is not instantiated. Call start() or resume() first.', + false, + ); + } + const client = this.client._client; + const res = await client.fs.readDir({ path: this.fullpath(input.path) }); + return { + files: + res.data?.result?.entries.map((file) => ({ + name: file.name, + path: file.name.replace(this.folder, ''), + type: file.type === 'file' ? 'file' : 'directory', + })) || [], + }; + } + + async read(input: SandboxFileReadInput): Promise { + if (!this.client._client) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Client is not instantiated. Call start() or resume() first.', + false, + ); + } + const client = this.client._client; + const data = await client.fs.readFile({ path: this.fullpath(input.path) }); + if (!data) { + throw new ClientError(ClientErrorCode.FileNotFound, 'File not found', false); + } + + const content = data.data?.result?.content; + if (!content) { + throw new ClientError(ClientErrorCode.FileNotFound, 'File content not found', false); + } + + // Convert Blob or File to string + const contentString = await content.text(); + + return { + data: contentString, + }; + } + + async rename(input: SandboxFileRenameInput): Promise { + if (!this.client._client) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Client is not instantiated. Call start() or resume() first.', + false, + ); + } + const client = this.client._client; + await client.fs.rename({ + from: this.fullpath(input.oldPath), + to: this.fullpath(input.newPath), + }); + return {}; + } + + async stat(input: SandboxFileStatInput): Promise { + if (!this.client._client) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Client is not instantiated. Call start() or resume() first.', + false, + ); + } + const client = this.client._client; + const response = await client.fs.stat({ path: this.fullpath(input.path) }); + if (!response.data?.result) { + throw new ClientError(ClientErrorCode.FileNotFound, 'File not found', false); + } + return { + type: response.data.result.type === 'file' ? 'file' : 'directory', + }; + } + + async write(input: SandboxFileWriteInput): Promise { + if (!this.client._client) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Client is not instantiated. Call start() or resume() first.', + false, + ); + } + const client = this.client._client; + for (const file of Array.isArray(input.files) ? input.files : [input.files]) { + if (!file.overwrite) { + const exists = await this.stat({ path: this.fullpath(file.path) }); + if (exists) { + continue; + } + } + await client.fs.writeFile({ + path: this.fullpath(file.path), + content: new Blob([file.data], { type: 'text/plain' }), + }); + } + + return {}; + } + + async watch(input: SandboxFileWatchInput, onOutput: (output: SandboxFileWatchOutput) => void) { + if (!this.client._client) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Client is not instantiated. Call start() or resume() first.', + false, + ); + } + + const sandboxId = (this.client._client.constructor as any).id; + + if (!sandboxId) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not running. Call start() or resume() first.', + false, + ); + } + + if (!watchers.has(sandboxId)) { + watchers.set(sandboxId, new Watcher(this.client)); + } + + const watcher = watchers.get(sandboxId); + + // obviously this should never happen + if (!watcher) { + throw new ClientError(ClientErrorCode.ImplementationError, 'Watcher not found', false); + } + + if (watcher.off) { + await watcher.start({ + path: this.fullpath(input.path), + recursive: input.recursive, + excludePaths: input.excludePaths, + }); + } + + watcher.onOutput((output) => onOutput(output)); + + return { + close: () => { + watcher.stop(); + }, + }; + } + + protected fullpath(path: string): string { + return this.folder + (path.startsWith('/') ? '' : '/') + path; + } +} + +interface WatcherEvent { + path: string; + type: 'create' | 'update' | 'delete'; +} + +class Watcher { + protected readonly maxEvents = 500; + // longer timeout might lead to gateway timeouts + protected readonly promiseTimeout = 30000; // 30 seconds + protected readonly watchTimeout = 300000; // 5 minutes + + protected events: Array = []; + + protected _off: boolean = true; + protected _watchHandle: any = null; + protected _onEventCallbacks: Array<(output: SandboxFileWatchOutput) => void> = []; + + constructor(protected readonly client: CodesandboxClient) {} + + get off(): boolean { + return this._off; + } + + async start(input: SandboxFileWatchInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + if (!this._off) { + return; + } + + this._off = false; + let timeout: NodeJS.Timeout | null = null; + + console.log('starting watch', input); + // Use WebSocket approach + const wsClient = await this.client._sandbox.connect(); + console.log('wsClient', wsClient); + + this._watchHandle = await wsClient.fs.watch(input.path, { + recursive: input.recursive, + excludes: input.excludePaths, + }); + console.log('this._watchHandle', this._watchHandle); + this._watchHandle.onEvent((event: any) => { + this.events.push( + ...event.paths.map((path: string) => ({ + path, + type: + event.type === 'add' + ? 'create' + : event.type === 'remove' + ? 'delete' + : ('update' as 'create' | 'update' | 'delete'), + })), + ); + + // avoid memory leak + this.events = this.events.slice(0, this.maxEvents - 1); + + if (timeout) { + clearTimeout(timeout); + } + + // debounce the resolution of the promise + timeout = setTimeout(() => { + this._onEventCallbacks.forEach((callback) => + callback({ + id: uuid(), + events: this.events, + }), + ); + }, 200); + }); + } + + onOutput(callback: (output: SandboxFileWatchOutput) => void): void { + this._onEventCallbacks.push(callback); + } + + async stop(): Promise { + if (this._watchHandle) { + await this._watchHandle.dispose(); + this._watchHandle = null; + } + + this._off = true; + this.events = []; + } +} diff --git a/apps/coderouter/src/provider/codesandbox/sandbox/index.ts b/apps/coderouter/src/provider/codesandbox/sandbox/index.ts new file mode 100644 index 0000000000..9a7d67cc87 --- /dev/null +++ b/apps/coderouter/src/provider/codesandbox/sandbox/index.ts @@ -0,0 +1,126 @@ +import { CodesandboxClient } from '../index'; +import { + Sandbox, + SandboxCreateInput, + SandboxCreateOutput, + SandboxGetInput, + SandboxGetOutput, + SandboxPauseInput, + SandboxPauseOutput, + SandboxResumeInput, + SandboxResumeOutput, + SandboxStopInput, + SandboxStopOutput, + SandboxUrlInput, + SandboxUrlOutput, +} from '@/provider/definition/sandbox'; +import { CodeSandbox, Sandbox as _CodesandboxSandbox } from '@codesandbox/sdk'; +import { ClientError, ClientErrorCode } from '@/provider/definition'; +import { CodesandboxSandboxFile } from './file'; +import { CodesandboxSandboxTerminal } from './terminal'; + +export class CodesandboxSandbox extends Sandbox { + public readonly file: CodesandboxSandboxFile; + public readonly terminal: CodesandboxSandboxTerminal; + + constructor(protected readonly client: CodesandboxClient) { + super(client); + this.file = new CodesandboxSandboxFile(this.client); + this.terminal = new CodesandboxSandboxTerminal(this.client); + } + + async beforeSandboxCall(): Promise { + this.client._sdk = new CodeSandbox(); + if (this.client.options.sandboxId) { + this.client._sandbox = await this.client._sdk.sandboxes.resume( + this.client.options.sandboxId, + ); + this.client._client = await this.client._sandbox.createRestSession(); + } + } + + async create(input: SandboxCreateInput): Promise { + const sandboxId = this.client.options.sandboxId; + if (!sandboxId) { + throw new ClientError( + ClientErrorCode.MissingSandboxId, + 'Sandbox ID is not set. Please provide a sandbox ID in the JWT token.', + false, + ); + } + if (!input.templateId) { + throw new ClientError( + ClientErrorCode.MissingTemplateId, + 'Template ID is not set. Please provide a template ID.', + false, + ); + } + const sdk = new CodeSandbox(); + + const newSandbox = await sdk.sandboxes.create({ + id: sandboxId, + source: 'template', + title: input.metadata['title'], + description: input.metadata['description'], + tags: input.metadata['tags']?.split(','), + }); + + return { + externalId: newSandbox.id, + }; + } + + async get(input: SandboxGetInput): Promise { + if (!this.client.options.sandboxId) { + throw new ClientError( + ClientErrorCode.MissingSandboxId, + 'Sandbox ID is not set. Please provide a sandbox ID in the JWT token.', + false, + ); + } + // not a big fan of this, however, the other way is to filter a list of sandboxes and the API + // is not great for that + const sdk = new CodeSandbox(); + const sandbox = await sdk.sandboxes.resume(this.client.options.sandboxId); + return { + id: this.client.options.sandboxId, + externalId: sandbox.id, + }; + } + + async pause(input: SandboxPauseInput): Promise { + if (!this.client.options.sandboxId) { + throw new ClientError( + ClientErrorCode.MissingSandboxId, + 'Sandbox ID is not set. Please provide a sandbox ID in the JWT token.', + false, + ); + } + const sdk = new CodeSandbox(); + await sdk.sandboxes.hibernate(this.client.options.sandboxId); + return {}; + } + + async resume(input: SandboxResumeInput): Promise { + return {}; + } + + async stop(input: SandboxStopInput): Promise { + if (!this.client.options.sandboxId) { + throw new ClientError( + ClientErrorCode.MissingSandboxId, + 'Sandbox ID is not set. Please provide a sandbox ID in the JWT token.', + false, + ); + } + const sdk = new CodeSandbox(); + await sdk.sandboxes.shutdown(this.client.options.sandboxId); + return {}; + } + + async url(input: SandboxUrlInput): Promise { + return { + url: `https://${this.client.options.sandboxId}-3000.csb.app`, + }; + } +} diff --git a/apps/coderouter/src/provider/codesandbox/sandbox/terminal/index.ts b/apps/coderouter/src/provider/codesandbox/sandbox/terminal/index.ts new file mode 100644 index 0000000000..39639af390 --- /dev/null +++ b/apps/coderouter/src/provider/codesandbox/sandbox/terminal/index.ts @@ -0,0 +1,276 @@ +import { ClientError, ClientErrorCode } from '@/provider/definition'; +import { CodesandboxClient } from '../..'; +import { + SandboxTerminal, + SandboxTerminalCommandInput, + SandboxTerminalCommandOutput, + SandboxTerminalCreateInput, + SandboxTerminalCreateOutput, + SandboxTerminalOpenInput, + SandboxTerminalOpenOutput, + SandboxTerminalWriteInput, + SandboxTerminalWriteOutput, + SandboxTerminalRunInput, + SandboxTerminalRunOutput, + SandboxTerminalKillInput, + SandboxTerminalKillOutput, +} from '../../../definition/sandbox/terminal'; +import { Terminal as CodesandboxTerminal } from '@codesandbox/sdk'; +import { v4 as uuid } from 'uuid'; + +// sandboxId -> terminalId -> TerminalWatcher +const terminalWatchers: Map> = new Map(); + +export class CodesandboxSandboxTerminal extends SandboxTerminal { + constructor(client: CodesandboxClient) { + super(client); + } + + async command(input: SandboxTerminalCommandInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + try { + const client = await this.client._sandbox.connect(); + const result = await client.commands.run(input.command); + return { + output: result, + is_error: false, + stdout: undefined, + stderr: undefined, + exit_code: 0, + }; + } catch (error) { + throw new ClientError( + ClientErrorCode.ImplementationError, + `Failed to execute command: ${error}`, + false, + ); + } + } + + async create(input: SandboxTerminalCreateInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + const sandboxId = this.client._sandbox.id; + if (!sandboxId) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not running. Call start() or resume() first.', + false, + ); + } + + // Create a new terminal watcher for this terminal ID + if (!terminalWatchers.has(sandboxId)) { + terminalWatchers.set(sandboxId, new Map()); + } + if (!terminalWatchers.get(sandboxId)!.has(input.terminalId)) { + terminalWatchers + .get(sandboxId)! + .set(input.terminalId, new TerminalWatcher(this.client, input.terminalId)); + } + + terminalWatchers.get(sandboxId)!.get(input.terminalId)!.start(); + + return { + terminalId: input.terminalId, + name: input.name, + }; + } + + async open( + input: SandboxTerminalOpenInput, + onOutput: (output: SandboxTerminalOpenOutput) => void, + ) { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + const watcher = terminalWatchers.get(this.client._sandbox.id)?.get(input.terminalId); + if (!watcher) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Terminal not found. Call create() first.', + false, + ); + } + + // Start the terminal watcher if it's not already running + if (watcher.off) { + await watcher.start(); + } + + const unsubscribe = watcher.onOutput((output) => onOutput(output)); + + return { + close: () => { + unsubscribe(); + }, + }; + } + + async write(input: SandboxTerminalWriteInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + const watcher = terminalWatchers.get(this.client._sandbox.id)?.get(input.terminalId); + if (!watcher) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Terminal not found. Call create() first.', + false, + ); + } + + // Write input to the terminal + await watcher.writeInput(input.input); + + return {}; + } + + async run(input: SandboxTerminalRunInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + const watcher = terminalWatchers.get(this.client._sandbox.id)?.get(input.terminalId); + if (!watcher) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Terminal not found. Call create() first.', + false, + ); + } + + // Execute the command and get output + await watcher.executeCommand(input.input); + + return {}; + } + + async kill(input: SandboxTerminalKillInput): Promise { + if (!this.client._sandbox) { + return { + output: 'Sandbox is not instantiated. Call start() or resume() first.', + }; + } + + const watcher = terminalWatchers.get(this.client._sandbox.id)?.get(input.terminalId); + if (watcher) { + await watcher.stop(); + terminalWatchers.get(this.client._sandbox.id)?.delete(input.terminalId); + } + + return { + output: 'Terminal killed', + }; + } +} + +class TerminalWatcher { + protected readonly watchTimeout = 0; + + protected _off: boolean = true; + protected _terminalHandle: CodesandboxTerminal | null = null; + protected _onOutputCallbacks: Array<(output: SandboxTerminalOpenOutput) => void> = []; + + constructor( + protected readonly client: CodesandboxClient, + protected readonly terminalId: string, + ) {} + + get off(): boolean { + return this._off; + } + + async start(): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + if (!this._off) { + return; + } + + this._off = false; + + const client = await this.client._sandbox.connect(); + + // Create PTY terminal with real-time data callback + this._terminalHandle = await client.terminals.create(); + } + + onOutput(callback: (output: SandboxTerminalOpenOutput) => void): () => void { + const disposable = this._terminalHandle?.onOutput((output) => + callback({ id: uuid(), output }), + ); + return () => { + disposable?.dispose(); + }; + } + + async writeInput(input: string): Promise { + if (!this._terminalHandle) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Terminal not initialized. Call start() first.', + false, + ); + } + + this._terminalHandle.write(input); + } + + async executeCommand(command: string): Promise { + if (!this._terminalHandle) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Terminal not initialized. Call start() first.', + false, + ); + } + + this._terminalHandle.run(command); + } + + async stop(): Promise { + this._off = true; + + // Close the PTY terminal + if (this._terminalHandle) { + await this._terminalHandle.kill(); + this._terminalHandle = null; + } + + this._onOutputCallbacks = []; + } +} diff --git a/apps/coderouter/src/provider/definition/index.test.ts b/apps/coderouter/src/provider/definition/index.test.ts new file mode 100644 index 0000000000..717f736976 --- /dev/null +++ b/apps/coderouter/src/provider/definition/index.test.ts @@ -0,0 +1,320 @@ +import { describe, it, expect } from "bun:test"; +import { + ClientError, + ClientErrorCode, + ClientErrorCodeToStatus, + Client, +} from "./index"; +import { + SandboxFile, + SandboxFileListOutput, + SandboxFileListInput, + SandboxFileReadOutput, + SandboxFileReadInput, + SandboxFileRenameOutput, + SandboxFileRenameInput, + SandboxFileStatOutput, + SandboxFileWriteOutput, + SandboxFileWriteInput, + SandboxFileStatInput, + SandboxFileCopyInput, + SandboxFileCopyOutput, + SandboxFileDeleteInput, + SandboxFileDeleteOutput, + SandboxFileDownloadInput, + SandboxFileDownloadOutput, +} from "./sandbox/file"; +import { + Sandbox, + SandboxPauseInput, + SandboxResumeOutput, + SandboxResumeInput, + SandboxStopOutput, + SandboxStopInput, + SandboxUrlInput, + SandboxUrlOutput, + SandboxPauseOutput, + SandboxCreateInput, + SandboxCreateOutput, + SandboxGetInput, + SandboxGetOutput, +} from "./sandbox"; + +class MockSandboxFile extends SandboxFile { + constructor(protected readonly client: MockClient) { + super(client); + } + + copy(input: SandboxFileCopyInput): Promise { + throw new Error("Method not implemented."); + } + + delete(input: SandboxFileDeleteInput): Promise { + throw new Error("Method not implemented."); + } + + download( + input: SandboxFileDownloadInput + ): Promise { + throw new Error("Method not implemented."); + } + + list(input: SandboxFileListInput): Promise { + throw new Error("Method not implemented."); + } + + read(input: SandboxFileReadInput): Promise { + throw new Error("Method not implemented."); + } + + rename(input: SandboxFileRenameInput): Promise { + throw new Error("Method not implemented."); + } + + stat(input: SandboxFileStatInput): Promise { + throw new Error("Method not implemented."); + } + + write(input: SandboxFileWriteInput): Promise { + throw new Error("Method not implemented."); + } +} + +class MockSandbox extends Sandbox { + public readonly file: SandboxFile; + + constructor(protected readonly client: MockClient) { + super(client); + this.file = new MockSandboxFile(this.client); + } + + create(input: SandboxCreateInput): Promise { + throw new Error("Method not implemented."); + } + + get(input: SandboxGetInput): Promise { + throw new Error("Method not implemented."); + } + + pause(input: SandboxPauseInput): Promise { + throw new Error("Method not implemented."); + } + + resume(input: SandboxResumeInput): Promise { + throw new Error("Method not implemented."); + } + + stop(input: SandboxStopInput): Promise { + throw new Error("Method not implemented."); + } + + url(input: SandboxUrlInput): Promise { + throw new Error("Method not implemented."); + } +} + +// Mock class extending Client to test abstract class coverage +class MockClient extends Client { + public readonly sandbox: MockSandbox; + + constructor() { + super({}); + this.sandbox = new MockSandbox(this); + } + + testMethod() { + return "test"; + } +} + +describe("Client", () => { + it("should allow extending the abstract class", () => { + const mockClient = new MockClient(); + expect(mockClient).toBeInstanceOf(Client); + expect(mockClient).toBeInstanceOf(MockClient); + expect(mockClient.testMethod()).toBe("test"); + }); +}); + +describe("ClientErrorCode", () => { + it("should have the correct error codes", () => { + expect(ClientErrorCode.Unimplemented).toBe(501001); + expect(ClientErrorCode.ImplementationError).toBe(503001); + expect(ClientErrorCode.SandboxNotFound).toBe(404001); + expect(ClientErrorCode.FileNotFound).toBe(404002); + }); + + it("should have bidirectional enum mapping", () => { + // Test that the enum works both ways (number to string and string to number) + expect(ClientErrorCode[501001]).toBe("Unimplemented"); + expect(ClientErrorCode[503001]).toBe("ImplementationError"); + expect(ClientErrorCode[404001]).toBe("SandboxNotFound"); + expect(ClientErrorCode[404002]).toBe("FileNotFound"); + + // Test that the enum values are properly set + expect(ClientErrorCode.Unimplemented).toBe(501001); + expect(ClientErrorCode.ImplementationError).toBe(503001); + expect(ClientErrorCode.SandboxNotFound).toBe(404001); + expect(ClientErrorCode.FileNotFound).toBe(404002); + }); +}); + +describe("ClientErrorCodeToStatus", () => { + it("should map error codes to correct HTTP status codes", () => { + expect(ClientErrorCodeToStatus[ClientErrorCode.Unimplemented]).toBe(501); + expect(ClientErrorCodeToStatus[ClientErrorCode.ImplementationError]).toBe( + 503 + ); + expect(ClientErrorCodeToStatus[ClientErrorCode.SandboxNotFound]).toBe(404); + expect(ClientErrorCodeToStatus[ClientErrorCode.FileNotFound]).toBe(404); + }); + + it("should have all error codes mapped", () => { + // Get only the numeric enum values (filter out string keys) + const errorCodes = Object.values(ClientErrorCode).filter( + (code): code is ClientErrorCode => typeof code === "number" + ); + const mappedCodes = Object.keys(ClientErrorCodeToStatus).map(Number); + + expect(mappedCodes).toHaveLength(errorCodes.length); + errorCodes.forEach((code) => { + expect(ClientErrorCodeToStatus[code]).toBeDefined(); + }); + }); +}); + +describe("ClientError", () => { + it("should create a ClientError with correct properties", () => { + const error = new ClientError( + ClientErrorCode.FileNotFound, + "File not found", + false + ); + + expect(error.code).toBe(ClientErrorCode.FileNotFound); + expect(error.message).toBe("File not found"); + expect(error.retriable).toBe(false); + expect(error.name).toBe("Error"); + expect(error.status).toBe(404); + }); + + it("should extend Error class and call super constructor", () => { + const error = new ClientError( + ClientErrorCode.SandboxNotFound, + "Sandbox not found", + true + ); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(ClientError); + + // Test that the Error constructor was called with the message + expect(error.message).toBe("Sandbox not found"); + expect(error.name).toBe("Error"); + + // Test that the error stack trace is properly set (indicates super() was called) + expect(error.stack).toBeDefined(); + }); + + it("should have correct status property", () => { + const fileNotFoundError = new ClientError( + ClientErrorCode.FileNotFound, + "File not found", + false + ); + expect(fileNotFoundError.status).toBe(404); + + const unimplementedError = new ClientError( + ClientErrorCode.Unimplemented, + "Not implemented", + false + ); + expect(unimplementedError.status).toBe(501); + + const implementationError = new ClientError( + ClientErrorCode.ImplementationError, + "Implementation error", + true + ); + expect(implementationError.status).toBe(503); + }); + + it("should have correct toString representation", () => { + const error = new ClientError( + ClientErrorCode.ImplementationError, + "Something went wrong", + true + ); + + const expectedString = + "Something went wrong (code: 503001, retriable: true)"; + expect(error.toString()).toBe(expectedString); + }); + + it("should handle different retriable values", () => { + const retriableError = new ClientError( + ClientErrorCode.SandboxNotFound, + "Retry this", + true + ); + expect(retriableError.retriable).toBe(true); + + const nonRetriableError = new ClientError( + ClientErrorCode.FileNotFound, + "Don't retry this", + false + ); + expect(nonRetriableError.retriable).toBe(false); + }); + + it("should handle empty message", () => { + const error = new ClientError(ClientErrorCode.Unimplemented, "", false); + + expect(error.message).toBe(""); + expect(error.toString()).toBe(" (code: 501001, retriable: false)"); + }); + + it("should handle special characters in message", () => { + const error = new ClientError( + ClientErrorCode.ImplementationError, + "Error with special chars: !@#$%^&*()", + true + ); + + expect(error.message).toBe("Error with special chars: !@#$%^&*()"); + expect(error.toString()).toBe( + "Error with special chars: !@#$%^&*() (code: 503001, retriable: true)" + ); + }); + + it("should handle all error code types in constructor", () => { + // Test constructor with each error code to ensure full coverage + const unimplementedError = new ClientError( + ClientErrorCode.Unimplemented, + "Not implemented", + false + ); + expect(unimplementedError.code).toBe(ClientErrorCode.Unimplemented); + + const implementationError = new ClientError( + ClientErrorCode.ImplementationError, + "Implementation error", + true + ); + expect(implementationError.code).toBe(ClientErrorCode.ImplementationError); + + const sandboxNotFoundError = new ClientError( + ClientErrorCode.SandboxNotFound, + "Sandbox not found", + false + ); + expect(sandboxNotFoundError.code).toBe(ClientErrorCode.SandboxNotFound); + + const fileNotFoundError = new ClientError( + ClientErrorCode.FileNotFound, + "File not found", + true + ); + expect(fileNotFoundError.code).toBe(ClientErrorCode.FileNotFound); + }); +}); diff --git a/apps/coderouter/src/provider/definition/index.ts b/apps/coderouter/src/provider/definition/index.ts new file mode 100644 index 0000000000..7ab5d0e912 --- /dev/null +++ b/apps/coderouter/src/provider/definition/index.ts @@ -0,0 +1,48 @@ +import { Sandbox } from "./sandbox"; + +export interface ClientOptions { + sandboxId?: string; + userId?: string; +} + +export abstract class Client { + constructor(public readonly options: ClientOptions) {} + + public abstract readonly sandbox: Sandbox; +} + +export enum ClientErrorCode { + Unimplemented = 501001, + ImplementationError = 503001, + SandboxNotFound = 404001, + FileNotFound = 404002, + MissingSandboxId = 400001, + MissingTemplateId = 400002, +} + +export const ClientErrorCodeToStatus = { + [ClientErrorCode.Unimplemented]: 501, + [ClientErrorCode.ImplementationError]: 503, + [ClientErrorCode.SandboxNotFound]: 404, + [ClientErrorCode.FileNotFound]: 404, + [ClientErrorCode.MissingSandboxId]: 400, + [ClientErrorCode.MissingTemplateId]: 400, +}; + +export class ClientError extends Error { + constructor( + public readonly code: ClientErrorCode, + public readonly message: string, + public readonly retriable: boolean + ) { + super(message); + } + + get status() { + return ClientErrorCodeToStatus[this.code]; + } + + toString() { + return `${this.message} (code: ${this.code}, retriable: ${this.retriable})`; + } +} diff --git a/apps/coderouter/src/provider/definition/sandbox/file/index.ts b/apps/coderouter/src/provider/definition/sandbox/file/index.ts new file mode 100644 index 0000000000..656fb0289a --- /dev/null +++ b/apps/coderouter/src/provider/definition/sandbox/file/index.ts @@ -0,0 +1,82 @@ +import { Client } from '../../index'; + +export interface SandboxFileCopyInput {} +export interface SandboxFileCopyOutput {} + +export interface SandboxFileDeleteInput { + path: string; +} +export interface SandboxFileDeleteOutput {} + +export interface SandboxFileDownloadInput {} +export interface SandboxFileDownloadOutput {} + +export interface SandboxFileListInput { + path: string; +} +export interface SandboxFileListOutput { + files: Array<{ + name: string; + path: string; + type: 'file' | 'directory'; + }>; +} + +export interface SandboxFileReadInput { + path: string; +} +export interface SandboxFileReadOutput { + data: string; +} + +export interface SandboxFileRenameInput { + oldPath: string; + newPath: string; +} +export interface SandboxFileRenameOutput {} + +export interface SandboxFileStatInput { + path: string; +} +export interface SandboxFileStatOutput { + type: 'file' | 'directory'; +} + +export interface SandboxFileWriteInput { + files: Array<{ + path: string; + data: string; + overwrite: boolean; + }>; +} +export interface SandboxFileWriteOutput {} + +export interface SandboxFileWatchInput { + path: string; + recursive: boolean; + excludePaths: string[]; +} +export interface SandboxFileWatchOutput { + id: string; + events: Array<{ + path: string; + type: 'create' | 'update' | 'delete'; + }>; +} + +export abstract class SandboxFile { + constructor(protected readonly client: T) {} + + abstract copy(input: SandboxFileCopyInput): Promise; + abstract delete(input: SandboxFileDeleteInput): Promise; + abstract download(input: SandboxFileDownloadInput): Promise; + abstract list(input: SandboxFileListInput): Promise; + abstract read(input: SandboxFileReadInput): Promise; + abstract rename(input: SandboxFileRenameInput): Promise; + abstract stat(input: SandboxFileStatInput): Promise; + abstract watch( + input: SandboxFileWatchInput, + onOutput: (output: SandboxFileWatchOutput) => void, + ): Promise<{ close: () => void }>; + abstract write(input: SandboxFileWriteInput): Promise; +} diff --git a/apps/coderouter/src/provider/definition/sandbox/index.ts b/apps/coderouter/src/provider/definition/sandbox/index.ts new file mode 100644 index 0000000000..d572db7c57 --- /dev/null +++ b/apps/coderouter/src/provider/definition/sandbox/index.ts @@ -0,0 +1,68 @@ +import { Client } from '../index'; +import { SandboxFile } from './file'; +import { SandboxTerminal } from './terminal'; + +export interface SandboxCreateInput { + // the sandbox ID is fed from the JWT token + // id: string; + // specify your own template ID to start up your sandbox faster + templateId: string; + // customize your sandbox metadata + metadata: Record; +} +export interface SandboxCreateOutput { + externalId: string; +} + +export interface SandboxGetInput { + // the sandbox ID is fed from the JWT token + // id: string; +} +export interface SandboxGetOutput { + id: string; + externalId: string; +} + +export interface SandboxPauseInput { + // the sandbox ID is fed from the JWT token + // id: string; +} +export interface SandboxPauseOutput {} + +export interface SandboxResumeInput { + // the sandbox ID is fed from the JWT token + // id: string; +} +export interface SandboxResumeOutput {} + +export interface SandboxStopInput { + // the sandbox ID is fed from the JWT token + // id: string; +} +export interface SandboxStopOutput {} + +export interface SandboxUrlInput { + // the sandbox ID is fed from the JWT token + // id: string; +} +export interface SandboxUrlOutput { + url: string; +} + +export abstract class Sandbox { + public abstract readonly file: SandboxFile; + public abstract readonly terminal: SandboxTerminal; + + constructor(protected readonly client: T) {} + + // called when the class is instantiated + // use this to resume the sandbox, bump the timeout and/or connect to the sandbox + abstract beforeSandboxCall(): Promise; + + abstract create(input: SandboxCreateInput): Promise; + abstract get(input: SandboxGetInput): Promise; + abstract pause(input: SandboxPauseInput): Promise; + abstract resume(input: SandboxResumeInput): Promise; + abstract stop(input: SandboxStopInput): Promise; + abstract url(input: SandboxUrlInput): Promise; +} diff --git a/apps/coderouter/src/provider/definition/sandbox/terminal/index.ts b/apps/coderouter/src/provider/definition/sandbox/terminal/index.ts new file mode 100644 index 0000000000..67820a54b4 --- /dev/null +++ b/apps/coderouter/src/provider/definition/sandbox/terminal/index.ts @@ -0,0 +1,64 @@ +import { Client } from '../../index'; + +export interface SandboxTerminalCommandInput { + command: string; +} +export interface SandboxTerminalCommandOutput { + is_error: boolean; + output: string; + stdout?: string; + stderr?: string; + exit_code?: number; +} + +export interface SandboxTerminalCreateInput { + terminalId: string; + name?: string; +} +export interface SandboxTerminalCreateOutput { + terminalId: string; + name?: string; +} + +export interface SandboxTerminalOpenInput { + terminalId: string; +} +export interface SandboxTerminalOpenOutput { + id: string; + output: string; +} + +export interface SandboxTerminalWriteInput { + terminalId: string; + input: string; +} +export interface SandboxTerminalWriteOutput {} + +export interface SandboxTerminalKillInput { + terminalId: string; +} +export interface SandboxTerminalKillOutput { + output: string; +} + +export interface SandboxTerminalRunInput { + terminalId: string; + input: string; +} +export interface SandboxTerminalRunOutput {} + +export abstract class SandboxTerminal { + constructor(protected readonly client: T) {} + + // act like a static function, does not open a new terminal + abstract command(input: SandboxTerminalCommandInput): Promise; + + abstract create(input: SandboxTerminalCreateInput): Promise; + abstract open( + input: SandboxTerminalOpenInput, + onOutput: (output: SandboxTerminalOpenOutput) => void, + ): Promise<{ close: () => void }>; + abstract write(input: SandboxTerminalWriteInput): Promise; + abstract run(input: SandboxTerminalRunInput): Promise; + abstract kill(input: SandboxTerminalKillInput): Promise; +} diff --git a/apps/coderouter/src/provider/e2b/index.ts b/apps/coderouter/src/provider/e2b/index.ts new file mode 100644 index 0000000000..94c14ef7ed --- /dev/null +++ b/apps/coderouter/src/provider/e2b/index.ts @@ -0,0 +1,19 @@ +import { Sandbox as _Sandbox } from '@e2b/code-interpreter'; +import { Client, ClientOptions } from '../definition'; +import { E2BSandbox } from './sandbox'; + +export class E2BClient extends Client { + public readonly sandbox: E2BSandbox; + + // property is initialized by the sandbox class + // this is the E2B sandbox instance + _sandbox?: _Sandbox; + + constructor( + options: ClientOptions, + public readonly apiKey: string, + ) { + super(options); + this.sandbox = new E2BSandbox(this); + } +} diff --git a/apps/coderouter/src/provider/e2b/sandbox/file/index.test.ts b/apps/coderouter/src/provider/e2b/sandbox/file/index.test.ts new file mode 100644 index 0000000000..74460b91c8 --- /dev/null +++ b/apps/coderouter/src/provider/e2b/sandbox/file/index.test.ts @@ -0,0 +1,490 @@ +import { describe, expect, it, beforeEach, jest } from "bun:test"; +import { E2BClient } from "@/provider/e2b/index"; +import { E2BSandboxFile } from "./index"; +import { + ClientError, + ClientErrorCode, + ClientOptions, +} from "@/provider/definition"; + +// Create a mock sandbox object that matches the expected interface +const createMockSandbox = () => + ({ + files: { + exists: jest.fn(), + read: jest.fn(), + write: jest.fn(), + remove: jest.fn(), + rename: jest.fn(), + list: jest.fn(), + getInfo: jest.fn(), + }, + create: jest.fn(), + kill: jest.fn(), + // Add other required properties to satisfy the Sandbox interface + runCode: jest.fn(), + createCodeContext: jest.fn(), + jupyterUrl: "http://localhost:8888", + commands: jest.fn(), + sandboxId: "test-sandbox-id", + process: { pid: 123 }, + close: jest.fn(), + restart: jest.fn(), + getLogs: jest.fn(), + getProcessLogs: jest.fn(), + getStdout: jest.fn(), + getStderr: jest.fn(), + getExitCode: jest.fn(), + getStatus: jest.fn(), + getMetadata: jest.fn(), + updateMetadata: jest.fn(), + getEnvironmentVariables: jest.fn(), + updateEnvironmentVariables: jest.fn(), + getPorts: jest.fn(), + getOpenPorts: jest.fn(), + getOpenPort: jest.fn(), + getPort: jest.fn(), + openPort: jest.fn(), + closePort: jest.fn(), + getHostname: jest.fn(), + getUrl: jest.fn(), + getLocalUrl: jest.fn(), + getPublicUrl: jest.fn(), + getLocalPort: jest.fn(), + getPublicPort: jest.fn(), + getLocalHostname: jest.fn(), + getPublicHostname: jest.fn(), + getLocalProtocol: jest.fn(), + getPublicProtocol: jest.fn(), + getLocalScheme: jest.fn(), + getPublicScheme: jest.fn(), + getLocalAuthority: jest.fn(), + getPublicAuthority: jest.fn(), + getLocalPath: jest.fn(), + getPublicPath: jest.fn(), + getLocalQuery: jest.fn(), + getPublicQuery: jest.fn(), + getLocalFragment: jest.fn(), + getPublicFragment: jest.fn(), + getLocalUserInfo: jest.fn(), + getPublicUserInfo: jest.fn(), + getLocalUsername: jest.fn(), + getPublicUsername: jest.fn(), + getLocalPassword: jest.fn(), + getPublicPassword: jest.fn(), + getLocalHost: jest.fn(), + getPublicHost: jest.fn(), + getLocalPortNumber: jest.fn(), + getPublicPortNumber: jest.fn(), + getLocalHostnameString: jest.fn(), + getPublicHostnameString: jest.fn(), + getLocalProtocolString: jest.fn(), + getPublicProtocolString: jest.fn(), + getLocalSchemeString: jest.fn(), + getPublicSchemeString: jest.fn(), + getLocalAuthorityString: jest.fn(), + getPublicAuthorityString: jest.fn(), + getLocalPathString: jest.fn(), + getPublicPathString: jest.fn(), + getLocalQueryString: jest.fn(), + getPublicQueryString: jest.fn(), + getLocalFragmentString: jest.fn(), + getPublicFragmentString: jest.fn(), + getLocalUserInfoString: jest.fn(), + getPublicUserInfoString: jest.fn(), + getLocalUsernameString: jest.fn(), + getPublicUsernameString: jest.fn(), + getLocalPasswordString: jest.fn(), + getPublicPasswordString: jest.fn(), + getLocalHostString: jest.fn(), + getPublicHostString: jest.fn(), + getLocalPortNumberString: jest.fn(), + getPublicPortNumberString: jest.fn(), + getLocalHostnameNumber: jest.fn(), + getPublicHostnameNumber: jest.fn(), + getLocalProtocolNumber: jest.fn(), + getPublicProtocolNumber: jest.fn(), + getLocalSchemeNumber: jest.fn(), + getPublicSchemeNumber: jest.fn(), + getLocalAuthorityNumber: jest.fn(), + getPublicAuthorityNumber: jest.fn(), + getLocalPathNumber: jest.fn(), + getPublicPathNumber: jest.fn(), + getLocalQueryNumber: jest.fn(), + getPublicQueryNumber: jest.fn(), + getLocalFragmentNumber: jest.fn(), + getPublicFragmentNumber: jest.fn(), + getLocalUserInfoNumber: jest.fn(), + getPublicUserInfoNumber: jest.fn(), + getLocalUsernameNumber: jest.fn(), + getPublicUsernameNumber: jest.fn(), + getLocalPasswordNumber: jest.fn(), + getPublicPasswordNumber: jest.fn(), + getLocalHostNumber: jest.fn(), + getPublicHostNumber: jest.fn(), + getLocalPortNumberNumber: jest.fn(), + getPublicPortNumberNumber: jest.fn(), + } as any); + +class E2BMockClient extends E2BClient { + private _mockSandbox = createMockSandbox(); + + constructor(options: ClientOptions) { + super(options, "test-api-key"); + } + + _sandbox = this._mockSandbox; + + // Method to set sandbox to null for testing error cases + setSandboxNull() { + this._mockSandbox = null as any; + this._sandbox = null as any; + } + + // Method to restore sandbox for testing + restoreSandbox() { + this._mockSandbox = createMockSandbox(); + this._sandbox = this._mockSandbox; + } +} + +describe("E2BSandboxFile", () => { + let mockClient: E2BMockClient; + let sandboxFile: E2BSandboxFile; + + beforeEach(() => { + mockClient = new E2BMockClient({ sandboxId: "test-sandbox-id" }); + sandboxFile = new E2BSandboxFile(mockClient); + jest.clearAllMocks(); + }); + + describe("copy", () => { + it("should throw Unimplemented error", async () => { + await expect( + sandboxFile.copy({ oldPath: "/old", newPath: "/new" }) + ).rejects.toThrow( + new ClientError(ClientErrorCode.Unimplemented, "Not implemented", false) + ); + }); + }); + + describe("delete", () => { + it("should delete file successfully", async () => { + const input = { path: "/test/file.txt" }; + + await sandboxFile.delete(input); + + expect(mockClient._sandbox.files.remove).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.remove).toHaveBeenCalledWith( + "/test/file.txt" + ); + }); + + it("should throw error when sandbox is not instantiated", async () => { + mockClient.setSandboxNull(); + + const input = { path: "/test/file.txt" }; + + await expect(sandboxFile.delete(input)).rejects.toThrow( + new ClientError( + ClientErrorCode.ImplementationError, + "Sandbox is not instantiated. Call start() or resume() first.", + false + ) + ); + + // Note: We can't check mockClient._sandbox.files.remove since sandbox is null + }); + }); + + describe("download", () => { + it("should throw Unimplemented error", async () => { + await expect( + sandboxFile.download({ path: "/test/file.txt" }) + ).rejects.toThrow( + new ClientError(ClientErrorCode.Unimplemented, "Not implemented", false) + ); + }); + }); + + describe("list", () => { + it("should list files successfully", async () => { + const mockFiles = [ + { name: "file1.txt", path: "/dir/file1.txt", type: "file" }, + { name: "subdir", path: "/dir/subdir", type: "directory" }, + ]; + + (mockClient._sandbox.files.list as any).mockResolvedValue(mockFiles); + + const input = { path: "/dir" }; + const result = await sandboxFile.list(input); + + expect(mockClient._sandbox.files.list).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.list).toHaveBeenCalledWith("/dir"); + expect(result).toEqual({ + files: [ + { name: "file1.txt", path: "/dir/file1.txt", type: "file" }, + { name: "subdir", path: "/dir/subdir", type: "directory" }, + ], + }); + }); + + it("should throw error when sandbox is not instantiated", async () => { + mockClient.setSandboxNull(); + + const input = { path: "/dir" }; + + await expect(sandboxFile.list(input)).rejects.toThrow( + new ClientError( + ClientErrorCode.ImplementationError, + "Sandbox is not instantiated. Call start() or resume() first.", + false + ) + ); + }); + }); + + describe("read", () => { + it("should read file successfully", async () => { + const mockData = "file content"; + (mockClient._sandbox.files.read as any).mockResolvedValue(mockData); + + const input = { path: "/test/file.txt" }; + const result = await sandboxFile.read(input); + + expect(mockClient._sandbox.files.read).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.read).toHaveBeenCalledWith( + "/test/file.txt" + ); + expect(result).toEqual({ data: mockData }); + }); + + it("should throw FileNotFound error when file doesn't exist", async () => { + (mockClient._sandbox.files.read as any).mockResolvedValue(null); + + const input = { path: "/test/file.txt" }; + + await expect(sandboxFile.read(input)).rejects.toThrow( + new ClientError(ClientErrorCode.FileNotFound, "File not found", false) + ); + + expect(mockClient._sandbox.files.read).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.read).toHaveBeenCalledWith( + "/test/file.txt" + ); + }); + + it("should throw error when sandbox is not instantiated", async () => { + mockClient.setSandboxNull(); + + const input = { path: "/test/file.txt" }; + + await expect(sandboxFile.read(input)).rejects.toThrow( + new ClientError( + ClientErrorCode.ImplementationError, + "Sandbox is not instantiated. Call start() or resume() first.", + false + ) + ); + }); + }); + + describe("rename", () => { + it("should rename file successfully", async () => { + const input = { oldPath: "/old/file.txt", newPath: "/new/file.txt" }; + + await sandboxFile.rename(input); + + expect(mockClient._sandbox.files.rename).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.rename).toHaveBeenCalledWith( + "/old/file.txt", + "/new/file.txt" + ); + }); + + it("should throw error when sandbox is not instantiated", async () => { + mockClient.setSandboxNull(); + + const input = { oldPath: "/old/file.txt", newPath: "/new/file.txt" }; + + await expect(sandboxFile.rename(input)).rejects.toThrow( + new ClientError( + ClientErrorCode.ImplementationError, + "Sandbox is not instantiated. Call start() or resume() first.", + false + ) + ); + }); + }); + + describe("stat", () => { + it("should get file info successfully for file", async () => { + const mockFileInfo = { type: "file" }; + (mockClient._sandbox.files.getInfo as any).mockResolvedValue( + mockFileInfo + ); + + const input = { path: "/test/file.txt" }; + const result = await sandboxFile.stat(input); + + expect(mockClient._sandbox.files.getInfo).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.getInfo).toHaveBeenCalledWith( + "/test/file.txt" + ); + expect(result).toEqual({ type: "file" }); + }); + + it("should get file info successfully for directory", async () => { + const mockFileInfo = { type: "directory" }; + (mockClient._sandbox.files.getInfo as any).mockResolvedValue( + mockFileInfo + ); + + const input = { path: "/test/dir" }; + const result = await sandboxFile.stat(input); + + expect(mockClient._sandbox.files.getInfo).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.getInfo).toHaveBeenCalledWith( + "/test/dir" + ); + expect(result).toEqual({ type: "directory" }); + }); + + it("should throw FileNotFound error when file doesn't exist", async () => { + (mockClient._sandbox.files.getInfo as any).mockResolvedValue(null); + + const input = { path: "/test/file.txt" }; + + await expect(sandboxFile.stat(input)).rejects.toThrow( + new ClientError(ClientErrorCode.FileNotFound, "File not found", false) + ); + + expect(mockClient._sandbox.files.getInfo).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.getInfo).toHaveBeenCalledWith( + "/test/file.txt" + ); + }); + + it("should throw error when sandbox is not instantiated", async () => { + mockClient.setSandboxNull(); + + const input = { path: "/test/file.txt" }; + + await expect(sandboxFile.stat(input)).rejects.toThrow( + new ClientError( + ClientErrorCode.ImplementationError, + "Sandbox is not instantiated. Call start() or resume() first.", + false + ) + ); + }); + }); + + describe("write", () => { + it("should write single file successfully", async () => { + const input = { + files: [ + { + path: "/test/file.txt", + data: "file content", + overwrite: true, + }, + ], + }; + + await sandboxFile.write(input); + + expect(mockClient._sandbox.files.write).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.write).toHaveBeenCalledWith([ + { path: "/test/file.txt", data: "file content" }, + ]); + }); + + it("should write multiple files successfully", async () => { + const input = { + files: [ + { path: "/test/file1.txt", data: "content1", overwrite: true }, + { path: "/test/file2.txt", data: "content2", overwrite: false }, + ], + }; + + await sandboxFile.write(input); + + expect(mockClient._sandbox.files.write).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.write).toHaveBeenCalledWith([ + { path: "/test/file1.txt", data: "content1" }, + { path: "/test/file2.txt", data: "content2" }, + ]); + }); + + it("should skip existing files when overwrite is false", async () => { + (mockClient._sandbox.files.exists as any).mockResolvedValue(true); + + const input = { + files: [ + { + path: "/test/file.txt", + data: "file content", + overwrite: false, + }, + ], + }; + + await sandboxFile.write(input); + + expect(mockClient._sandbox.files.exists).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.exists).toHaveBeenCalledWith( + "/test/file.txt" + ); + expect(mockClient._sandbox.files.write).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.write).toHaveBeenCalledWith([]); + }); + + it("should write file when overwrite is false and file doesn't exist", async () => { + (mockClient._sandbox.files.exists as any).mockResolvedValue(false); + + const input = { + files: [ + { + path: "/test/file.txt", + data: "file content", + overwrite: false, + }, + ], + }; + + await sandboxFile.write(input); + + expect(mockClient._sandbox.files.exists).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.exists).toHaveBeenCalledWith( + "/test/file.txt" + ); + expect(mockClient._sandbox.files.write).toHaveBeenCalledTimes(1); + expect(mockClient._sandbox.files.write).toHaveBeenCalledWith([ + { path: "/test/file.txt", data: "file content" }, + ]); + }); + + it("should throw error when sandbox is not instantiated", async () => { + mockClient.setSandboxNull(); + + const input = { + files: [ + { + path: "/test/file.txt", + data: "file content", + overwrite: true, + }, + ], + }; + + await expect(sandboxFile.write(input)).rejects.toThrow( + new ClientError( + ClientErrorCode.ImplementationError, + "Sandbox is not instantiated. Call start() or resume() first.", + false + ) + ); + }); + }); +}); diff --git a/apps/coderouter/src/provider/e2b/sandbox/file/index.ts b/apps/coderouter/src/provider/e2b/sandbox/file/index.ts new file mode 100644 index 0000000000..14178bfa45 --- /dev/null +++ b/apps/coderouter/src/provider/e2b/sandbox/file/index.ts @@ -0,0 +1,323 @@ +import { ClientError, ClientErrorCode } from '@/provider/definition'; +import { E2BClient } from '../..'; +import { + SandboxFile, + SandboxFileCopyInput, + SandboxFileCopyOutput, + SandboxFileDeleteInput, + SandboxFileDeleteOutput, + SandboxFileDownloadInput, + SandboxFileDownloadOutput, + SandboxFileListInput, + SandboxFileListOutput, + SandboxFileReadInput, + SandboxFileReadOutput, + SandboxFileRenameInput, + SandboxFileRenameOutput, + SandboxFileStatInput, + SandboxFileStatOutput, + SandboxFileWriteOutput, + SandboxFileWriteInput, + SandboxFileWatchInput, + SandboxFileWatchOutput, +} from '../../../definition/sandbox/file'; +import { NotFoundError, WatchHandle } from '@e2b/code-interpreter'; +import path from 'path'; +import { v4 as uuid } from 'uuid'; + +const watchers: Map = new Map(); + +export class E2BSandboxFile extends SandboxFile { + // the folder to store the files in the sandbox + // when creating a new template, the code must be stored in this folder + protected folder: string = '/code'; + + constructor(client: E2BClient) { + super(client); + } + + async copy(input: SandboxFileCopyInput): Promise { + throw new ClientError(ClientErrorCode.Unimplemented, 'Not implemented', false); + } + + async delete(input: SandboxFileDeleteInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + await this.client._sandbox.files.remove(this.fullpath(input.path)); + return {}; + } + + async download(input: SandboxFileDownloadInput): Promise { + throw new ClientError(ClientErrorCode.Unimplemented, 'Not implemented', false); + } + + async list(input: SandboxFileListInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + try { + const p = path.normalize(this.fullpath(input.path)); + const files = await this.client._sandbox.files.list(p); + return { + files: files.map((file) => ({ + name: file.name, + path: file.path.replace(this.folder, ''), + type: file.type === 'file' ? 'file' : 'directory', + })), + }; + } catch (e) { + if (e instanceof NotFoundError) { + throw new ClientError( + ClientErrorCode.FileNotFound, + 'Folder or file not found', + false, + ); + } + throw e; + } + } + + async read(input: SandboxFileReadInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + try { + const data = await this.client._sandbox.files.read(this.fullpath(input.path)); + if (!data) { + throw new ClientError(ClientErrorCode.FileNotFound, 'File not found', false); + } + + return { + data, + }; + } catch (e) { + if (e instanceof NotFoundError) { + throw new ClientError(ClientErrorCode.FileNotFound, 'File not found', false); + } + throw e; + } + } + + async rename(input: SandboxFileRenameInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + await this.client._sandbox.files.rename( + this.fullpath(input.oldPath), + this.fullpath(input.newPath), + ); + return {}; + } + + async stat(input: SandboxFileStatInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + const file = await this.client._sandbox.files.getInfo(this.fullpath(input.path)); + if (!file) { + throw new ClientError(ClientErrorCode.FileNotFound, 'File not found', false); + } + return { + type: file.type === 'file' ? 'file' : 'directory', + }; + } + + async write(input: SandboxFileWriteInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + const files: { path: string; data: string }[] = []; + for (const file of Array.isArray(input.files) ? input.files : [input.files]) { + if (!file.overwrite) { + const exists = await this.client._sandbox.files.exists(this.fullpath(file.path)); + if (exists) { + continue; + } + } + files.push({ + path: this.fullpath(file.path), + data: file.data, + }); + } + await this.client._sandbox.files.write(files); + return {}; + } + + async watch(input: SandboxFileWatchInput, onOutput: (output: SandboxFileWatchOutput) => void) { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + const sandboxId = this.client._sandbox.sandboxId; + + if (!sandboxId) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not running. Call start() or resume() first.', + false, + ); + } + + if (!watchers.has(sandboxId)) { + watchers.set(sandboxId, new Watcher(this.client)); + } + + const watcher = watchers.get(sandboxId); + + // obviously this should never happen + if (!watcher) { + throw new ClientError(ClientErrorCode.ImplementationError, 'Watcher not found', false); + } + + if (watcher.off) { + await watcher.start({ + path: this.fullpath(input.path), + recursive: input.recursive, + excludePaths: input.excludePaths, + }); + } + + watcher.onOutput((output) => onOutput(output)); + + return { + close: () => { + watcher.stop(); + }, + }; + } + + protected fullpath(path: string): string { + return this.folder + (path.startsWith('/') ? '' : '/') + path; + } +} + +interface WatcherEvent { + path: string; + type: 'create' | 'update' | 'delete'; +} + +class Watcher { + protected readonly maxEvents = 500; + // longer timeout might lead to gateway timeouts + protected readonly promiseTimeout = 30000; // 30 seconds + protected readonly watchTimeout = 300000; // 5 minutes + + protected events: Array = []; + + protected _off: boolean = true; + protected _watchHandle: WatchHandle | null = null; + protected _onEventCallbacks: Array<(output: SandboxFileWatchOutput) => void> = []; + + constructor(protected readonly client: E2BClient) {} + + get off(): boolean { + return this._off; + } + + async start(input: SandboxFileWatchInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + if (!this._off) { + return; + } + + this._off = false; + let timeout: NodeJS.Timeout | null = null; + this._watchHandle = await this.client._sandbox.files.watchDir( + input.path, + (event) => { + if ( + input.excludePaths.some((exclude) => + event.name.startsWith(exclude.replace('**', '')), + ) + ) { + return; + } + + this.events.push({ + path: event.name, + type: + event.type === 'create' + ? 'create' + : event.type === 'remove' + ? 'delete' + : 'update', + }); + + // avoid memory leak + this.events = this.events.slice(0, this.maxEvents - 1); + + if (timeout) { + clearTimeout(timeout); + } + + // debounce the resolution of the promise + timeout = setTimeout(() => { + this._onEventCallbacks.forEach((callback) => + callback({ + id: uuid(), + events: this.events, + }), + ); + }, 200); + }, + { + timeoutMs: 0, + recursive: input.recursive, + onExit: (err) => { + console.error('[coderouter] watcher exited – error: ', err || 'none'); + this.stop(); + }, + }, + ); + } + + onOutput(callback: (output: SandboxFileWatchOutput) => void): void { + this._onEventCallbacks.push(callback); + } + + async stop(): Promise { + if (this._watchHandle) { + await this._watchHandle.stop(); + this._watchHandle = null; + } + + this._off = true; + this.events = []; + } +} diff --git a/apps/coderouter/src/provider/e2b/sandbox/index.ts b/apps/coderouter/src/provider/e2b/sandbox/index.ts new file mode 100644 index 0000000000..1ad510150e --- /dev/null +++ b/apps/coderouter/src/provider/e2b/sandbox/index.ts @@ -0,0 +1,153 @@ +import { E2BClient } from '../index'; +import { + Sandbox, + SandboxCreateInput, + SandboxCreateOutput, + SandboxGetInput, + SandboxGetOutput, + SandboxPauseInput, + SandboxPauseOutput, + SandboxResumeInput, + SandboxResumeOutput, + SandboxStopInput, + SandboxStopOutput, + SandboxUrlInput, + SandboxUrlOutput, +} from '@/provider/definition/sandbox'; +import { Sandbox as _E2BSandbox, SandboxState } from '@e2b/code-interpreter'; +import { ClientError, ClientErrorCode } from '@/provider/definition'; +import { E2BSandboxFile } from './file'; +import { E2BSandboxTerminal } from './terminal'; + +export class E2BSandbox extends Sandbox { + public readonly file: E2BSandboxFile; + public readonly terminal: E2BSandboxTerminal; + + private _sandboxTimeoutMs: number = 1000 * 60 * 10; + + constructor(protected readonly client: E2BClient) { + super(client); + this.file = new E2BSandboxFile(this.client); + this.terminal = new E2BSandboxTerminal(this.client); + } + + async beforeSandboxCall(): Promise { + const e2bSandboxId = (await this.get({})).externalId; + this.client._sandbox = await _E2BSandbox.connect(e2bSandboxId); + // bump the timeout to 5 minutes + this.client._sandbox.setTimeout(this._sandboxTimeoutMs); + } + + async create(input: SandboxCreateInput): Promise { + const sandboxId = this.client.options.sandboxId; + if (!sandboxId) { + throw new ClientError( + ClientErrorCode.MissingSandboxId, + 'Sandbox ID is not set. Please provide a sandbox ID in the JWT token.', + false, + ); + } + if (!input.templateId) { + throw new ClientError( + ClientErrorCode.MissingTemplateId, + 'Template ID is not set. Please provide a template ID.', + false, + ); + } + const metadata: Record = { + sandboxId, + }; + if (this.client.options.userId) { + metadata['userId'] = this.client.options.userId; + } + this.client._sandbox = await _E2BSandbox.betaCreate(input.templateId, { + apiKey: this.client.apiKey, + timeoutMs: this._sandboxTimeoutMs, + metadata, + autoPause: true, + }); + return { + externalId: this.client._sandbox.sandboxId, + }; + } + + async get(input: SandboxGetInput): Promise { + if (!this.client.options.sandboxId) { + throw new ClientError( + ClientErrorCode.MissingSandboxId, + 'Sandbox ID is not set. Please provide a sandbox ID in the JWT token.', + false, + ); + } + const query: { state: SandboxState[]; metadata: Record } = { + state: ['running', 'paused'], + metadata: { sandboxId: this.client.options.sandboxId }, + }; + if (this.client.options.userId) { + query.metadata['userId'] = this.client.options.userId; + } + const res = await _E2BSandbox.list({ query }); + const list = await res.nextItems(); + const e2bSandbox = list?.[0]; + if (!e2bSandbox) { + throw new ClientError( + ClientErrorCode.SandboxNotFound, + 'Sandbox not found. Please create a sandbox first.', + false, + ); + } + if (list.length > 1) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Multiple sandboxes found. The system should not create multiple sandboxes with the same sandbox ID.', + false, + ); + } + return { + id: this.client.options.sandboxId, + externalId: e2bSandbox.sandboxId, + }; + } + + async pause(input: SandboxPauseInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + await this.client._sandbox.betaPause(); + return {}; + } + + async resume(input: SandboxResumeInput): Promise { + return {}; + } + + async stop(input: SandboxStopInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + await this.client._sandbox.kill(); + return {}; + } + + async url(input: SandboxUrlInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + const url = await this.client._sandbox.getHost(8084); + return { + url: url.startsWith('http') ? url : `https://${url}`, + }; + } +} diff --git a/apps/coderouter/src/provider/e2b/sandbox/terminal/index.ts b/apps/coderouter/src/provider/e2b/sandbox/terminal/index.ts new file mode 100644 index 0000000000..ee2057c983 --- /dev/null +++ b/apps/coderouter/src/provider/e2b/sandbox/terminal/index.ts @@ -0,0 +1,303 @@ +import { ClientError, ClientErrorCode } from '@/provider/definition'; +import { E2BClient } from '../..'; +import { + SandboxTerminal, + SandboxTerminalCommandInput, + SandboxTerminalCommandOutput, + SandboxTerminalCreateInput, + SandboxTerminalCreateOutput, + SandboxTerminalOpenInput, + SandboxTerminalOpenOutput, + SandboxTerminalWriteInput, + SandboxTerminalWriteOutput, + SandboxTerminalRunInput, + SandboxTerminalRunOutput, + SandboxTerminalKillInput, + SandboxTerminalKillOutput, +} from '../../../definition/sandbox/terminal'; +import { CommandExitError, CommandHandle } from '@e2b/code-interpreter'; +import { v4 as uuid } from 'uuid'; + +// sandboxId -> terminalId -> TerminalWatcher +const terminalWatchers: Map> = new Map(); + +export class E2BSandboxTerminal extends SandboxTerminal { + constructor(client: E2BClient) { + super(client); + } + + async command(input: SandboxTerminalCommandInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + try { + const result = await this.client._sandbox.commands.run(input.command); + return { + output: result.stdout || result.stderr, + is_error: !!result.error || result.exitCode !== 0, + stdout: result.stdout, + stderr: result.stderr, + exit_code: result.exitCode, + }; + } catch (error) { + throw new ClientError( + ClientErrorCode.ImplementationError, + `Failed to execute command: ${error}`, + false, + ); + } + } + + async create(input: SandboxTerminalCreateInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + const sandboxId = this.client._sandbox.sandboxId; + if (!sandboxId) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not running. Call start() or resume() first.', + false, + ); + } + + // Create a new terminal watcher for this terminal ID + if (!terminalWatchers.has(sandboxId)) { + terminalWatchers.set(sandboxId, new Map()); + } + if (!terminalWatchers.get(sandboxId)!.has(input.terminalId)) { + terminalWatchers + .get(sandboxId)! + .set(input.terminalId, new TerminalWatcher(this.client, input.terminalId)); + } + + terminalWatchers.get(sandboxId)!.get(input.terminalId)!.start(); + + return { + terminalId: input.terminalId, + name: input.name, + }; + } + + async open( + input: SandboxTerminalOpenInput, + onOutput: (output: SandboxTerminalOpenOutput) => void, + ) { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + const watcher = terminalWatchers.get(this.client._sandbox.sandboxId)?.get(input.terminalId); + if (!watcher) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Terminal not found. Call create() first.', + false, + ); + } + + // Start the terminal watcher if it's not already running + if (watcher.off) { + await watcher.start(); + } + + const unsubscribe = watcher.onOutput((output) => onOutput(output)); + + return { + close: () => { + unsubscribe(); + }, + }; + } + + async write(input: SandboxTerminalWriteInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + const watcher = terminalWatchers.get(this.client._sandbox.sandboxId)?.get(input.terminalId); + if (!watcher) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Terminal not found. Call create() first.', + false, + ); + } + + // Write input to the terminal + await watcher.writeInput(input.input); + + return {}; + } + + async run(input: SandboxTerminalRunInput): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + const watcher = terminalWatchers.get(this.client._sandbox.sandboxId)?.get(input.terminalId); + if (!watcher) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Terminal not found. Call create() first.', + false, + ); + } + + // Execute the command and get output + await watcher.executeCommand(input.input); + + return {}; + } + + async kill(input: SandboxTerminalKillInput): Promise { + if (!this.client._sandbox) { + return { + output: 'Sandbox is not instantiated. Call start() or resume() first.', + }; + } + + const watcher = terminalWatchers.get(this.client._sandbox.sandboxId)?.get(input.terminalId); + if (watcher) { + await watcher.stop(); + terminalWatchers.get(this.client._sandbox.sandboxId)?.delete(input.terminalId); + } + + return { + output: 'Terminal killed', + }; + } +} + +class TerminalWatcher { + protected readonly watchTimeout = 0; + + protected _off: boolean = true; + protected _terminalHandle: CommandHandle | null = null; + protected _onOutputCallbacks: Array<(output: SandboxTerminalOpenOutput) => void> = []; + + constructor( + protected readonly client: E2BClient, + protected readonly terminalId: string, + ) {} + + get off(): boolean { + return this._off; + } + + async start(): Promise { + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Sandbox is not instantiated. Call start() or resume() first.', + false, + ); + } + + if (!this._off) { + return; + } + + this._off = false; + + // Create PTY terminal with real-time data callback + this._terminalHandle = await this.client._sandbox.pty.create({ + timeoutMs: this.watchTimeout, + onData: (data: Uint8Array) => { + // Convert Uint8Array to string + const output = new TextDecoder().decode(data); + this._onOutputCallbacks.forEach((callback) => callback({ id: uuid(), output })); + }, + cols: 80, + rows: 24, + }); + + try { + await this._terminalHandle?.wait(); + } catch (err) { + // taken from: https://github.com/e2b-dev/E2B/blob/main/packages/cli/src/terminal.ts#L10 + if (err instanceof CommandExitError) { + if (err.exitCode === -1 && err.error === 'signal: killed') { + return; + } + if (err.exitCode === 130) { + console.error('Terminal session was killed by user'); + return; + } + } + console.error('error waiting for terminal handle', err); + throw err; + } finally { + this.stop(); + } + } + + onOutput(callback: (output: SandboxTerminalOpenOutput) => void): () => void { + this._onOutputCallbacks.push(callback); + return () => { + this._onOutputCallbacks = this._onOutputCallbacks.filter( + (callback) => callback !== callback, + ); + }; + } + + async writeInput(input: string): Promise { + if (!this._terminalHandle) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Terminal not initialized. Call start() first.', + false, + ); + } + + if (!this.client._sandbox) { + throw new ClientError( + ClientErrorCode.ImplementationError, + 'Terminal not initialized. Call start() first.', + false, + ); + } + + // Convert string to Uint8Array and send to the PTY terminal + const data = new TextEncoder().encode(input); + await this.client._sandbox.pty.sendInput(this._terminalHandle.pid, data); + } + + async executeCommand(command: string): Promise { + await this.writeInput(command); + } + + async stop(): Promise { + this._off = true; + + // Close the PTY terminal + if (this._terminalHandle) { + await this._terminalHandle.kill(); + this._terminalHandle = null; + } + + this._onOutputCallbacks = []; + } +} diff --git a/apps/coderouter/src/provider/index.ts b/apps/coderouter/src/provider/index.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/coderouter/src/server.ts b/apps/coderouter/src/server.ts new file mode 100644 index 0000000000..07e27f3799 --- /dev/null +++ b/apps/coderouter/src/server.ts @@ -0,0 +1,114 @@ +import 'dotenv/config'; +import { OpenAPIHono } from '@hono/zod-openapi'; +import { api_sandbox_create } from './api/sandbox/create'; +import { api_sandbox_file_copy } from './api/sandbox/file/copy'; +import { api_sandbox_file_delete } from './api/sandbox/file/delete'; +import { api_sandbox_file_download } from './api/sandbox/file/download'; +import { api_sandbox_file_list } from './api/sandbox/file/list'; +import { api_sandbox_file_read } from './api/sandbox/file/read'; +import { api_sandbox_file_rename } from './api/sandbox/file/rename'; +import { api_sandbox_file_stat } from './api/sandbox/file/stat'; +import { api_sandbox_file_watch } from './api/sandbox/file/watch'; +import { api_sandbox_file_write } from './api/sandbox/file/write'; +import { api_sandbox_pause } from './api/sandbox/pause'; +import { api_sandbox_resume } from './api/sandbox/resume'; +import { api_sandbox_stop } from './api/sandbox/stop'; +import { api_sandbox_url } from './api/sandbox/url'; +import { Client } from './provider/definition'; +import { setupClientMiddleware } from './middleware/client'; +import { api_auth_sign } from './api/auth/sign'; +import { AuthJwtPayload } from './util/auth'; +import { setupAuthJwtMiddleware } from './middleware/auth'; +import { setupRequiredSandboxIdMiddleware } from './middleware/requireSandboxId'; +import { env, serve } from 'bun'; +import { setupBeforeSandboxCallMiddleware } from './middleware/beforeSandboxCall'; +import { setupErrorMiddleware } from './middleware/error'; +import { api_sandbox_terminal_create } from './api/sandbox/terminal/create'; +import { api_sandbox_terminal_command } from './api/sandbox/terminal/command'; +import { api_sandbox_terminal_open } from './api/sandbox/terminal/open'; +import { api_sandbox_terminal_run } from './api/sandbox/terminal/run'; +import { api_sandbox_terminal_write } from './api/sandbox/terminal/write'; +import { api_sandbox_terminal_kill } from './api/sandbox/terminal/kill'; +import { streamSSE } from 'hono/streaming'; + +export interface Variables { + client: Client; + auth: AuthJwtPayload; +} + +export type LocalHono = OpenAPIHono<{ Variables: Variables }>; + +const app: LocalHono = new OpenAPIHono<{ Variables: Variables }>(); + +app.openAPIRegistry.registerComponent('securitySchemes', 'apikey', { + type: 'http', + scheme: 'bearer', + bearerFormat: 'apikey', +}); + +app.openAPIRegistry.registerComponent('securitySchemes', 'jwt', { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', +}); + +const basePath = env.URL_PATH_PREFIX + '/api'; + +// must be first +setupErrorMiddleware(app); + +// /auth/sign is not protected by this middleware +setupAuthJwtMiddleware(app, basePath + '/sandbox/*'); +// sandbox ID is required for all sandbox routes +setupRequiredSandboxIdMiddleware(app, basePath + '/sandbox/*'); +// must be last +setupClientMiddleware(app, basePath + '/sandbox/*'); + +setupBeforeSandboxCallMiddleware(app, basePath + '/sandbox/pause'); +setupBeforeSandboxCallMiddleware(app, basePath + '/sandbox/resume'); +setupBeforeSandboxCallMiddleware(app, basePath + '/sandbox/stop'); +setupBeforeSandboxCallMiddleware(app, basePath + '/sandbox/url'); +setupBeforeSandboxCallMiddleware(app, basePath + '/sandbox/file/*'); +setupBeforeSandboxCallMiddleware(app, basePath + '/sandbox/terminal/*'); + +// auth routes +api_auth_sign(app); + +// sandbox routes +api_sandbox_create(app); +api_sandbox_pause(app); +api_sandbox_resume(app); +api_sandbox_stop(app); +api_sandbox_url(app); + +// sandbox file routes +api_sandbox_file_copy(app); +api_sandbox_file_delete(app); +api_sandbox_file_download(app); +api_sandbox_file_list(app); +api_sandbox_file_read(app); +api_sandbox_file_rename(app); +api_sandbox_file_stat(app); +api_sandbox_file_watch(app); +api_sandbox_file_write(app); + +// sandbox terminal routes +api_sandbox_terminal_command(app); +api_sandbox_terminal_create(app); +api_sandbox_terminal_open(app); +api_sandbox_terminal_run(app); +api_sandbox_terminal_write(app); +api_sandbox_terminal_kill(app); + +app.doc('/openapi.json', { + openapi: '3.0.0', + info: { + version: '1.0.0', + title: 'Coderouter', + }, +}); + +export default { + fetch: app.fetch, + port: 4444, +}; diff --git a/apps/coderouter/src/util/auth.ts b/apps/coderouter/src/util/auth.ts new file mode 100644 index 0000000000..9e2ce7c336 --- /dev/null +++ b/apps/coderouter/src/util/auth.ts @@ -0,0 +1,41 @@ +import { env } from 'bun'; +import jwt from 'jsonwebtoken'; +import { z } from '@hono/zod-openapi'; + +export const JwtAuthResponses = { + 401: { + content: { + 'application/json': { + schema: z.object({ error: z.string() }), + }, + }, + description: 'The JWT token is missing or is invalid.', + }, +}; + +export interface AuthJwtPayload { + sandboxId?: string; + userId?: string; +} + +export function encodeJwtToken(jwtPayload: AuthJwtPayload) { + return jwt.sign(jwtPayload, env.JWT_SECRET_KEY!, { + expiresIn: '1d', + }); +} + +export function decodeJwtToken(token: string) { + return jwt.verify(token, env.JWT_SECRET_KEY!); +} + +export function verifyApiKeyFromHeader(header?: string) { + const apiKey = header?.toLowerCase().split('bearer ')[1]; + if (!apiKey) { + return false; + } + return verifyApiKey(apiKey); +} + +export function verifyApiKey(apiKey: string) { + return apiKey === env.CODEROUTER_API_KEY; +} diff --git a/apps/coderouter/tsconfig.json b/apps/coderouter/tsconfig.json new file mode 100644 index 0000000000..a8b59d97f2 --- /dev/null +++ b/apps/coderouter/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2023"], + "types": ["@types/node"], + "strict": true, + "noUncheckedIndexedAccess": true, + "skipLibCheck": true, + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src", "scripts", "drizzle.config.ts"] +} diff --git a/apps/coderouter/typedoc.json b/apps/coderouter/typedoc.json new file mode 100644 index 0000000000..8948f7d3a4 --- /dev/null +++ b/apps/coderouter/typedoc.json @@ -0,0 +1,5 @@ +{ + "entryPoints": ["src"], + "out": "site/typedoc", + "tsconfig": "tsconfig.json" +} diff --git a/apps/docker-compose.yaml b/apps/docker-compose.yaml new file mode 100644 index 0000000000..7109fcf7ee --- /dev/null +++ b/apps/docker-compose.yaml @@ -0,0 +1,30 @@ +# Meant for local development only. +# Secret keys used here are not used in production and are only set for convenience. +name: onlook-web +services: + coderouter: + container_name: coderouter + environment: + - DEBUG=* + build: + context: ./coderouter + dockerfile: Dockerfile.dev + ports: + - '4444:4444' + volumes: + - ./coderouter:/app + - coderouter_node_modules:/app/node_modules + command: [/usr/local/bin/bun/bin/bun, dev] + nginx: + container_name: nginx + restart: always + image: nginx:1.27.4-alpine + ports: + - '443:443' + volumes: + - ./nginx/conf.d/server.conf:/etc/nginx/conf.d/server.conf:ro + - ./nginx/ssl/onlook-internal.crt:/etc/nginx/ssl/onlook-internal.crt:ro + - ./nginx/ssl/onlook-internal.key:/etc/nginx/ssl/onlook-internal.key:ro + command: [nginx, '-g', 'daemon off;'] +volumes: + coderouter_node_modules: diff --git a/apps/nginx/bin/genkeys.sh b/apps/nginx/bin/genkeys.sh new file mode 100644 index 0000000000..ad1771e3c8 --- /dev/null +++ b/apps/nginx/bin/genkeys.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./ssl/onlook-internal.key -out ./ssl/onlook-internal.crt -config ./ssl/openssl.cnf + +# Add the certificate to the keychain – works on Mac +if command -v security &> /dev/null; then + security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain-db ./ssl/onlook-internal.crt +else + echo "Warning: security is not installed" +fi diff --git a/apps/nginx/conf.d/server.conf b/apps/nginx/conf.d/server.conf new file mode 100644 index 0000000000..4e7c04e114 --- /dev/null +++ b/apps/nginx/conf.d/server.conf @@ -0,0 +1,54 @@ +# Main server block for HTTPS +server { + listen 443 ssl; + http2 on; + server_name onlook.internal *.onlook.internal; + + # SSL configuration + ssl_certificate /etc/nginx/ssl/onlook-internal.crt; + ssl_certificate_key /etc/nginx/ssl/onlook-internal.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + + # Common proxy settings + # proxy_connect_timeout 0.25s; + # proxy_send_timeout 0.25s; + # proxy_read_timeout 0.25s; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + # WebSocket support + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # to support SSE + location /coderouter/api/sandbox/terminal/open { + proxy_pass http://host.docker.internal:4444; + proxy_http_version 1.1; + proxy_set_header Connection ''; + chunked_transfer_encoding off; + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 1h; # keep open longer than your expected SSE interval + } + + location /coderouter { + proxy_pass http://host.docker.internal:4444; + } + + location / { + proxy_pass http://host.docker.internal:3000; + } +} + +# HTTP to HTTPS redirect +server { + listen 80; + server_name onlook.internal *.onlook.internal; + return 301 https://$server_name$request_uri; +} diff --git a/apps/nginx/ssl/README.md b/apps/nginx/ssl/README.md new file mode 100644 index 0000000000..d3f4dbc5eb --- /dev/null +++ b/apps/nginx/ssl/README.md @@ -0,0 +1 @@ +Do not use this private and public key in prod. Those are designed for local usage only. diff --git a/apps/nginx/ssl/onlook-internal.crt b/apps/nginx/ssl/onlook-internal.crt new file mode 100644 index 0000000000..c9714bea58 --- /dev/null +++ b/apps/nginx/ssl/onlook-internal.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEBDCCAuygAwIBAgIURypApXyi6zML9QoB5/0BYkPr5TowDQYJKoZIhvcNAQEL +BQAwgZIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQKDAZPbmxvb2sxDDAKBgNVBAsMA0VuZzEY +MBYGA1UEAwwPb25sb29rLmludGVybmFsMR0wGwYJKoZIhvcNAQkBFg5lbmdAb25s +b29rLmNvbTAeFw0yNTA4MDcxNzQ0NDVaFw0yNjA4MDcxNzQ0NDVaMIGSMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j +aXNjbzEPMA0GA1UECgwGT25sb29rMQwwCgYDVQQLDANFbmcxGDAWBgNVBAMMD29u +bG9vay5pbnRlcm5hbDEdMBsGCSqGSIb3DQEJARYOZW5nQG9ubG9vay5jb20wggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCkpqbfeXnSorbz8ciQ0q5ydN2 +UDsK6eqnJDXE6FO0WpbQlWEApORFML59zELaPx2sALTfnjfJuwFREuerSwMLqAl7 +UfHWrGtur1Sn99Xr2/gjToOnlIew0IY8t9hmY7Gg0lTlU1Xf7lNGjyiDj03prBQh +5txa2SzeqewmMbPKQ6hdac4kjga55UQP29MpPgNjW7HIr3SherR8JHKv03ZvnRR+ +SXJ00znocEbQaCLBuSO7ZNj3oNT0pn3WuhLTj47breaCC8mWADbYC1Ltss5tIKPT +ogjRqxkZbm3sfd2cq1HL0x4FwlboLPt9wo5UvlOs/qts1iP8XVKTy/uj9bcbAgMB +AAGjUDBOMC0GA1UdEQQmMCSCD29ubG9vay5pbnRlcm5hbIIRKi5vbmxvb2suaW50 +ZXJuYWwwHQYDVR0OBBYEFB3RhNcRNIcIqjVyhsEL2DFQtNLUMA0GCSqGSIb3DQEB +CwUAA4IBAQCWz0xa40patIChU0I4Y1MqfJ1ic+iy7iP8aLmSRdoDp/8JSf7LYpnd +I9NRGKtpQp6uv5bmGMoK8A+J1BfZp2TZ0dMxsIEANEYYiq9aej095isQueFaK2s7 +6ji4pR3ahhljdcwpsMDYicdJBnkl9xXWlaGxXb0ZerYfPh4+e0CP/5hJNQDZttww +sFe1ksupoWzKWJIYB80S+HDQo7FvHvTXm+VOqfNTIxait3I+KEnt8ovRbSu4PsMw +kAdvwDhb7QBxEIriZgxjgLEX0XaGuC84oA0fttjFuwdhOhy4o5aBnhUsWWFdHPoi +4vdIjPk+l06LeK3YdploPhnVv6ATpavZ +-----END CERTIFICATE----- diff --git a/apps/nginx/ssl/onlook-internal.key b/apps/nginx/ssl/onlook-internal.key new file mode 100644 index 0000000000..1fe143d2b8 --- /dev/null +++ b/apps/nginx/ssl/onlook-internal.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDCkpqbfeXnSorb +z8ciQ0q5ydN2UDsK6eqnJDXE6FO0WpbQlWEApORFML59zELaPx2sALTfnjfJuwFR +EuerSwMLqAl7UfHWrGtur1Sn99Xr2/gjToOnlIew0IY8t9hmY7Gg0lTlU1Xf7lNG +jyiDj03prBQh5txa2SzeqewmMbPKQ6hdac4kjga55UQP29MpPgNjW7HIr3SherR8 +JHKv03ZvnRR+SXJ00znocEbQaCLBuSO7ZNj3oNT0pn3WuhLTj47breaCC8mWADbY +C1Ltss5tIKPTogjRqxkZbm3sfd2cq1HL0x4FwlboLPt9wo5UvlOs/qts1iP8XVKT +y/uj9bcbAgMBAAECggEAXqkMZtGdZCUz4TT44IZ0eGbkZg8qamjbLG3FawLMllXs +QZYrFzEhjTfltTYG4D8MpH3DgXdsFMzSGytjYkagOK+LzV9UlOhVbAgI258LiDAA +TM0J6CGu0irg4/FdapLd/CvX+anNgaBlC1Lilv7FHQYG1WeHlPtLhRiONxa/LYtb +xpUV9RqazNAFZGSG7EPWfgj9CQOkX8pV6CJE3o+b7jLZfDFF/CVQVei556b3EAkk +REVe334UA5awosIxu/DjXSMi5KTlsRksT3LvFRzRxg9bbzGBOEXAECxEQBpKcr75 +NlsDr+6rSCEv1R6dIbURYYf1i1mHio2Z1qXDxYyqwQKBgQDk6+jo7Um35kCr3e9M +wsBQa244812CrdGdEsA3zT0U1YWs6yDnewGffQA4CcEtxRxs60Xl2qxpn1mgeCdy +tvGOCkQBKiLfuQJAoIudXsgrUTeOXKWyXdpgkK687nwtXg7lOcp1+bH3p3YsOoKQ +lLAP7N2+QybZOVxgfipWc3vnyQKBgQDZlo8WJMudfAnvrUSHjNkUjiPqs7iLwwvu +otitFZ3YKUa28TSJdHodPHFgQJdb1OHsLdsHwcz7+OEtMwAKyQ+zbuy0sFP4jnn7 +7bOOkY0G1upTCynPrXj54CrDneTT0j0ikEP9vgCLhNRK9OX7oMG01/r45BBTHIDE +D0aLLjlhwwKBgH7kAUNznEQyfjCGIYoT/ZPWKM+qnm+8N49wgFmuCyiMPr+dyaxl +831bRY7KYWkkdGAvfZwuPRmC+aRIVd1xaK3KJO3cVF5cZ9I464q8qgnQyBHCaxpW +iaCzMhiJvQ1MsMcA5KfGU46qJYfYmtzXfkwliLhY8qS/9eOKq58l/k15AoGAMWBR +KB1Bd4NmXdVb78aunFOFIwWVo2Gnm4eo095L63myamFiIq8j5u6Ia+c8ccJlYksl +oSUBd0yLDM69+7SUs4tAe+BnrcfnNpxCWt/8uMicdCvcWRxsj4enLKzv+IGFDgre +4v3y8bY61qesaOWaD4fTlBds/O9C6TruLzdWHjECgYBPyL/KonHisxwmL7HTRXzn +OtxIDysEem/a8A2YOVWv//Ge4kpS3r+lDDxUewxAzyKxkR4uuPMyaNZ7wNkV/bzZ +fxvlcGS4agztOvqzimieUDMnePSQQiz3zar4iPHmypJ/xUWCeGdZ95HFXMhAUNGu +aPxqpOZa9Q2B5HKNCY2bvA== +-----END PRIVATE KEY----- diff --git a/apps/nginx/ssl/openssl.cnf b/apps/nginx/ssl/openssl.cnf new file mode 100644 index 0000000000..052318e9fb --- /dev/null +++ b/apps/nginx/ssl/openssl.cnf @@ -0,0 +1,22 @@ +[req] +default_bits = 2048 +distinguished_name = req_distinguished_name +x509_extensions = v3_req +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +C = US +ST = California +L = San Francisco +O = Onlook +OU = Eng +CN = onlook.internal +emailAddress = eng@onlook.com + +[v3_req] +subjectAltName = @alt_names + +[alt_names] +DNS.1 = onlook.internal +DNS.2 = *.onlook.internal diff --git a/apps/web/client/.env.example b/apps/web/client/.env.example index dcfc17aec1..58c21673ed 100644 --- a/apps/web/client/.env.example +++ b/apps/web/client/.env.example @@ -10,7 +10,7 @@ SUPABASE_DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres # OpenRouter - Enables AI chat. Other providers are optional below OPENROUTER_API_KEY="" -# Codesandbox - Used to host user apps. Other providers may be supported in the future. May be optional in the future. +# Codesandbox - Used to host user apps. CSB_API_KEY="" # ------------- Optional Keys ------------- @@ -84,3 +84,12 @@ GOOGLE_LOCATION="{i=r!=null?f4(z4(r)):{};let o=t||!r||!r.__esModule?pe(i,"default",{value:r,enumerable:!0}):i;for(let n of D4(r))if(!_4.call(o,n))pe(o,n,{get:()=>r[n],enumerable:!0});return o};var vr=(r,t)=>()=>(t||r((t={exports:{}}).exports,t),t.exports);var a=(r,t)=>{for(var i in t)pe(r,i,{get:t[i],enumerable:!0,configurable:!0,set:(o)=>t[i]=()=>o})};var Ie=vr((N3,Uh)=>{function j4(r){var t=typeof r;return r!=null&&(t=="object"||t=="function")}Uh.exports=j4});var Jh=vr((B3,kh)=>{var O4=typeof global=="object"&&global&&global.Object===Object&&global;kh.exports=O4});var ae=vr((y3,Xh)=>{var p4=Jh(),I4=typeof self=="object"&&self&&self.Object===Object&&self,a4=p4||I4||Function("return this")();Xh.exports=a4});var qh=vr((A3,Ph)=>{var U4=ae(),k4=function(){return U4.Date.now()};Ph.exports=k4});var Kh=vr((H3,Wh)=>{var J4=/\s/;function X4(r){var t=r.length;while(t--&&J4.test(r.charAt(t)));return t}Wh.exports=X4});var Lh=vr((R3,Vh)=>{var P4=Kh(),q4=/^\s+/;function W4(r){return r?r.slice(0,P4(r)+1).replace(q4,""):r}Vh.exports=W4});var Ue=vr((M3,Yh)=>{var K4=ae(),V4=K4.Symbol;Yh.exports=V4});var Gh=vr((Z3,Fh)=>{var Eh=Ue(),Qh=Object.prototype,L4=Qh.hasOwnProperty,Y4=Qh.toString,fn=Eh?Eh.toStringTag:void 0;function E4(r){var t=L4.call(r,fn),i=r[fn];try{r[fn]=void 0;var o=!0}catch(e){}var n=Y4.call(r);if(o)if(t)r[fn]=i;else delete r[fn];return n}Fh.exports=E4});var Nh=vr((C3,Sh)=>{var Q4=Object.prototype,F4=Q4.toString;function G4(r){return F4.call(r)}Sh.exports=G4});var Hh=vr((T3,Ah)=>{var Bh=Ue(),S4=Gh(),N4=Nh(),B4="[object Null]",y4="[object Undefined]",yh=Bh?Bh.toStringTag:void 0;function A4(r){if(r==null)return r===void 0?y4:B4;return yh&&yh in Object(r)?S4(r):N4(r)}Ah.exports=A4});var Mh=vr((d3,Rh)=>{function H4(r){return r!=null&&typeof r=="object"}Rh.exports=H4});var Ch=vr((s3,Zh)=>{var R4=Hh(),M4=Mh(),Z4="[object Symbol]";function C4(r){return typeof r=="symbol"||M4(r)&&R4(r)==Z4}Zh.exports=C4});var r$=vr((rU,sh)=>{var T4=Lh(),Th=Ie(),d4=Ch(),dh=NaN,s4=/^[-+]0x[0-9a-f]+$/i,r6=/^0b[01]+$/i,t6=/^0o[0-7]+$/i,n6=parseInt;function i6(r){if(typeof r=="number")return r;if(d4(r))return dh;if(Th(r)){var t=typeof r.valueOf=="function"?r.valueOf():r;r=Th(t)?t+"":t}if(typeof r!="string")return r===0?r:+r;r=T4(r);var i=r6.test(r);return i||t6.test(r)?n6(r.slice(2),i?2:8):s4.test(r)?dh:+r}sh.exports=i6});var Je=vr((tU,n$)=>{var o6=Ie(),ke=qh(),t$=r$(),e6="Expected a function",l6=Math.max,c6=Math.min;function u6(r,t,i){var o,n,e,l,u,g,c=0,b=!1,v=!1,h=!0;if(typeof r!="function")throw new TypeError(e6);if(t=t$(t)||0,o6(i))b=!!i.leading,v="maxWait"in i,e=v?l6(t$(i.maxWait)||0,t):e,h="trailing"in i?!!i.trailing:h;function m(Q){var B=o,tr=n;return o=n=void 0,c=Q,l=r.apply(tr,B),l}function w(Q){return c=Q,u=setTimeout(_,t),b?m(Q):l}function O(Q){var B=Q-g,tr=Q-c,Oe=t-B;return v?c6(Oe,e-tr):Oe}function I(Q){var B=Q-g,tr=Q-c;return g===void 0||B>=t||B<0||v&&tr>=e}function _(){var Q=ke();if(I(Q))return J(Q);u=setTimeout(_,O(Q))}function J(Q){if(u=void 0,h&&o)return m(Q);return o=n=void 0,l}function L(){if(u!==void 0)clearTimeout(u);c=0,o=g=n=u=void 0}function P(){return u===void 0?l:J(ke())}function W(){var Q=ke(),B=I(Q);if(o=arguments,n=this,g=Q,B){if(u===void 0)return w(g);if(v)return clearTimeout(u),u=setTimeout(_,t),m(g)}if(u===void 0)u=setTimeout(_,t);return l}return W.cancel=L,W.flush=P,W}n$.exports=u6});var i0=vr((ez)=>{var n0="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");ez.encode=function(r){if(0<=r&&r{var o0=i0(),Be=5,e0=1<>1;return t?-i:i}bz.encode=function r(t){var i="",o,n=uz(t);do{if(o=n&l0,n>>>=Be,n>0)o|=c0;i+=o0.encode(o)}while(n>0);return i};bz.decode=function r(t,i,o){var n=t.length,e=0,l=0,u,g;do{if(i>=n)throw new Error("Expected more digits in base 64 VLQ value.");if(g=o0.decode(t.charCodeAt(i++)),g===-1)throw new Error("Invalid base64 digit: "+t.charAt(i-1));u=!!(g&c0),g&=l0,e=e+(g<{function hz(r,t,i){if(t in r)return r[t];else if(arguments.length===3)return i;else throw new Error('"'+t+'" is a required argument.')}kz.getArg=hz;var g0=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/,$z=/^data:.+\,.+$/;function an(r){var t=r.match(g0);if(!t)return null;return{scheme:t[1],auth:t[2],host:t[3],port:t[4],path:t[5]}}kz.urlParse=an;function At(r){var t="";if(r.scheme)t+=r.scheme+":";if(t+="//",r.auth)t+=r.auth+"@";if(r.host)t+=r.host;if(r.port)t+=":"+r.port;if(r.path)t+=r.path;return t}kz.urlGenerate=At;var xz=32;function wz(r){var t=[];return function(i){for(var o=0;oxz)t.pop();return e}}var ye=wz(function r(t){var i=t,o=an(t);if(o){if(!o.path)return t;i=o.path}var n=kz.isAbsolute(i),e=[],l=0,u=0;while(!0)if(l=u,u=i.indexOf("/",l),u===-1){e.push(i.slice(l));break}else{e.push(i.slice(l,u));while(u=0;u--)if(g=e[u],g===".")e.splice(u,1);else if(g==="..")c++;else if(c>0)if(g==="")e.splice(u+1,c),c=0;else e.splice(u,2),c--;if(i=e.join("/"),i==="")i=n?"/":".";if(o)return o.path=i,At(o);return i});kz.normalize=ye;function b0(r,t){if(r==="")r=".";if(t==="")t=".";var i=an(t),o=an(r);if(o)r=o.path||"/";if(i&&!i.scheme){if(o)i.scheme=o.scheme;return At(i)}if(i||t.match($z))return t;if(o&&!o.host&&!o.path)return o.host=t,At(o);var n=t.charAt(0)==="/"?t:ye(r.replace(/\/+$/,"")+"/"+t);if(o)return o.path=n,At(o);return n}kz.join=b0;kz.isAbsolute=function(r){return r.charAt(0)==="/"||g0.test(r)};function fz(r,t){if(r==="")r=".";r=r.replace(/\/$/,"");var i=0;while(t.indexOf(r+"/")!==0){var o=r.lastIndexOf("/");if(o<0)return t;if(r=r.slice(0,o),r.match(/^([^\/]+:\/)?\/*$/))return t;++i}return Array(i+1).join("../")+t.substr(r.length+1)}kz.relative=fz;var m0=function(){var r=Object.create(null);return!("__proto__"in r)}();function v0(r){return r}function zz(r){if(h0(r))return"$"+r;return r}kz.toSetString=m0?v0:zz;function Dz(r){if(h0(r))return r.slice(1);return r}kz.fromSetString=m0?v0:Dz;function h0(r){if(!r)return!1;var t=r.length;if(t<9)return!1;if(r.charCodeAt(t-1)!==95||r.charCodeAt(t-2)!==95||r.charCodeAt(t-3)!==111||r.charCodeAt(t-4)!==116||r.charCodeAt(t-5)!==111||r.charCodeAt(t-6)!==114||r.charCodeAt(t-7)!==112||r.charCodeAt(t-8)!==95||r.charCodeAt(t-9)!==95)return!1;for(var i=t-10;i>=0;i--)if(r.charCodeAt(i)!==36)return!1;return!0}function _z(r,t,i){var o=tt(r.source,t.source);if(o!==0)return o;if(o=r.originalLine-t.originalLine,o!==0)return o;if(o=r.originalColumn-t.originalColumn,o!==0||i)return o;if(o=r.generatedColumn-t.generatedColumn,o!==0)return o;if(o=r.generatedLine-t.generatedLine,o!==0)return o;return tt(r.name,t.name)}kz.compareByOriginalPositions=_z;function jz(r,t,i){var o=r.originalLine-t.originalLine;if(o!==0)return o;if(o=r.originalColumn-t.originalColumn,o!==0||i)return o;if(o=r.generatedColumn-t.generatedColumn,o!==0)return o;if(o=r.generatedLine-t.generatedLine,o!==0)return o;return tt(r.name,t.name)}kz.compareByOriginalPositionsNoSource=jz;function Oz(r,t,i){var o=r.generatedLine-t.generatedLine;if(o!==0)return o;if(o=r.generatedColumn-t.generatedColumn,o!==0||i)return o;if(o=tt(r.source,t.source),o!==0)return o;if(o=r.originalLine-t.originalLine,o!==0)return o;if(o=r.originalColumn-t.originalColumn,o!==0)return o;return tt(r.name,t.name)}kz.compareByGeneratedPositionsDeflated=Oz;function pz(r,t,i){var o=r.generatedColumn-t.generatedColumn;if(o!==0||i)return o;if(o=tt(r.source,t.source),o!==0)return o;if(o=r.originalLine-t.originalLine,o!==0)return o;if(o=r.originalColumn-t.originalColumn,o!==0)return o;return tt(r.name,t.name)}kz.compareByGeneratedPositionsDeflatedNoLine=pz;function tt(r,t){if(r===t)return 0;if(r===null)return 1;if(t===null)return-1;if(r>t)return 1;return-1}function Iz(r,t){var i=r.generatedLine-t.generatedLine;if(i!==0)return i;if(i=r.generatedColumn-t.generatedColumn,i!==0)return i;if(i=tt(r.source,t.source),i!==0)return i;if(i=r.originalLine-t.originalLine,i!==0)return i;if(i=r.originalColumn-t.originalColumn,i!==0)return i;return tt(r.name,t.name)}kz.compareByGeneratedPositionsInflated=Iz;function az(r){return JSON.parse(r.replace(/^\)]}'[^\n]*\n/,""))}kz.parseSourceMapInput=az;function Uz(r,t,i){if(t=t||"",r){if(r[r.length-1]!=="/"&&t[0]!=="/")r+="/";t=r+t}if(i){var o=an(i);if(!o)throw new Error("sourceMapURL could not be parsed");if(o.path){var n=o.path.lastIndexOf("/");if(n>=0)o.path=o.path.substring(0,n+1)}t=b0(At(o),t)}return ye(t)}kz.computeSourceURL=Uz});var $0=vr((yz)=>{var Ae=yi(),He=Object.prototype.hasOwnProperty,Ut=typeof Map!=="undefined";function nt(){this._array=[],this._set=Ut?new Map:Object.create(null)}nt.fromArray=function r(t,i){var o=new nt;for(var n=0,e=t.length;n=0)return i}else{var o=Ae.toSetString(t);if(He.call(this._set,o))return this._set[o]}throw new Error('"'+t+'" is not in the set.')};nt.prototype.at=function r(t){if(t>=0&&t{var x0=yi();function Hz(r,t){var i=r.generatedLine,o=t.generatedLine,n=r.generatedColumn,e=t.generatedColumn;return o>i||o==i&&e>=n||x0.compareByGeneratedPositionsInflated(r,t)<=0}function Ai(){this._array=[],this._sorted=!0,this._last={generatedLine:-1,generatedColumn:0}}Ai.prototype.unsortedForEach=function r(t,i){this._array.forEach(t,i)};Ai.prototype.add=function r(t){if(Hz(this._last,t))this._last=t,this._array.push(t);else this._sorted=!1,this._array.push(t)};Ai.prototype.toArray=function r(){if(!this._sorted)this._array.sort(x0.compareByGeneratedPositionsInflated),this._sorted=!0;return this._array};Rz.MappingList=Ai});var Lt="PENPAL_CHILD";var x4=ah(Je(),1);var g6=class extends Error{code;constructor(r,t){super(t);this.name="PenpalError",this.code=r}},_r=g6,b6=(r)=>({name:r.name,message:r.message,stack:r.stack,penpalCode:r instanceof _r?r.code:void 0}),m6=({name:r,message:t,stack:i,penpalCode:o})=>{let n=o?new _r(o,t):new Error(t);return n.name=r,n.stack=i,n},v6=Symbol("Reply"),h6=class{value;transferables;#r=v6;constructor(r,t){this.value=r,this.transferables=t?.transferables}},$6=h6,Jr="penpal",ki=(r)=>{return typeof r==="object"&&r!==null},c$=(r)=>{return typeof r==="function"},x6=(r)=>{return ki(r)&&r.namespace===Jr},Yt=(r)=>{return r.type==="SYN"},Ji=(r)=>{return r.type==="ACK1"},zn=(r)=>{return r.type==="ACK2"},u$=(r)=>{return r.type==="CALL"},g$=(r)=>{return r.type==="REPLY"},w6=(r)=>{return r.type==="DESTROY"},b$=(r,t=[])=>{let i=[];for(let o of Object.keys(r)){let n=r[o];if(c$(n))i.push([...t,o]);else if(ki(n))i.push(...b$(n,[...t,o]))}return i},f6=(r,t)=>{let i=r.reduce((o,n)=>{return ki(o)?o[n]:void 0},t);return c$(i)?i:void 0},bt=(r)=>{return r.join(".")},i$=(r,t,i)=>({namespace:Jr,channel:r,type:"REPLY",callId:t,isError:!0,...i instanceof Error?{value:b6(i),isSerializedErrorInstance:!0}:{value:i}}),z6=(r,t,i,o)=>{let n=!1,e=async(l)=>{if(n)return;if(!u$(l))return;o?.(`Received ${bt(l.methodPath)}() call`,l);let{methodPath:u,args:g,id:c}=l,b,v;try{let h=f6(u,t);if(!h)throw new _r("METHOD_NOT_FOUND",`Method \`${bt(u)}\` is not found.`);let m=await h(...g);if(m instanceof $6)v=m.transferables,m=await m.value;b={namespace:Jr,channel:i,type:"REPLY",callId:c,value:m}}catch(h){b=i$(i,c,h)}if(n)return;try{o?.(`Sending ${bt(u)}() reply`,b),r.sendMessage(b,v)}catch(h){if(h.name==="DataCloneError")b=i$(i,c,h),o?.(`Sending ${bt(u)}() reply`,b),r.sendMessage(b);throw h}};return r.addMessageHandler(e),()=>{n=!0,r.removeMessageHandler(e)}},D6=z6,m$=crypto.randomUUID?.bind(crypto)??(()=>new Array(4).fill(0).map(()=>Math.floor(Math.random()*Number.MAX_SAFE_INTEGER).toString(16)).join("-")),_6=Symbol("CallOptions"),j6=class{transferables;timeout;#r=_6;constructor(r){this.transferables=r?.transferables,this.timeout=r?.timeout}},O6=j6,p6=new Set(["apply","call","bind"]),v$=(r,t,i=[])=>{return new Proxy(i.length?()=>{}:Object.create(null),{get(o,n){if(n==="then")return;if(i.length&&p6.has(n))return Reflect.get(o,n);return v$(r,t,[...i,n])},apply(o,n,e){return r(i,e)}})},o$=(r)=>{return new _r("CONNECTION_DESTROYED",`Method call ${bt(r)}() failed due to destroyed connection`)},I6=(r,t,i)=>{let o=!1,n=new Map,e=(g)=>{if(!g$(g))return;let{callId:c,value:b,isError:v,isSerializedErrorInstance:h}=g,m=n.get(c);if(!m)return;if(n.delete(c),i?.(`Received ${bt(m.methodPath)}() call`,g),v)m.reject(h?m6(b):b);else m.resolve(b)};return r.addMessageHandler(e),{remoteProxy:v$((g,c)=>{if(o)throw o$(g);let b=m$(),v=c[c.length-1],h=v instanceof O6,{timeout:m,transferables:w}=h?v:{},O=h?c.slice(0,-1):c;return new Promise((I,_)=>{let J=m!==void 0?window.setTimeout(()=>{n.delete(b),_(new _r("METHOD_CALL_TIMEOUT",`Method call ${bt(g)}() timed out after ${m}ms`))},m):void 0;n.set(b,{methodPath:g,resolve:I,reject:_,timeoutId:J});try{let L={namespace:Jr,channel:t,type:"CALL",id:b,methodPath:g,args:O};i?.(`Sending ${bt(g)}() call`,L),r.sendMessage(L,w)}catch(L){_(new _r("TRANSMISSION_FAILED",L.message))}})},i),destroy:()=>{o=!0,r.removeMessageHandler(e);for(let{methodPath:g,reject:c,timeoutId:b}of n.values())clearTimeout(b),c(o$(g));n.clear()}}},a6=I6,U6=()=>{let r,t;return{promise:new Promise((o,n)=>{r=o,t=n}),resolve:r,reject:t}},k6=U6,J6=class extends Error{constructor(r){super(`You've hit a bug in Penpal. Please file an issue with the following information: ${r}`)}},Et=J6,Xe="deprecated-penpal",X6=(r)=>{return ki(r)&&"penpal"in r},P6=(r)=>r.split("."),e$=(r)=>r.join("."),h$=(r)=>{return new Et(`Unexpected message to translate: ${JSON.stringify(r)}`)},q6=(r)=>{if(r.penpal==="syn")return{namespace:Jr,channel:void 0,type:"SYN",participantId:Xe};if(r.penpal==="ack")return{namespace:Jr,channel:void 0,type:"ACK2"};if(r.penpal==="call")return{namespace:Jr,channel:void 0,type:"CALL",id:r.id,methodPath:P6(r.methodName),args:r.args};if(r.penpal==="reply")if(r.resolution==="fulfilled")return{namespace:Jr,channel:void 0,type:"REPLY",callId:r.id,value:r.returnValue};else return{namespace:Jr,channel:void 0,type:"REPLY",callId:r.id,isError:!0,...r.returnValueIsError?{value:r.returnValue,isSerializedErrorInstance:!0}:{value:r.returnValue}};throw h$(r)},W6=(r)=>{if(Ji(r))return{penpal:"synAck",methodNames:r.methodPaths.map(e$)};if(u$(r))return{penpal:"call",id:r.id,methodName:e$(r.methodPath),args:r.args};if(g$(r))if(r.isError)return{penpal:"reply",id:r.callId,resolution:"rejected",...r.isSerializedErrorInstance?{returnValue:r.value,returnValueIsError:!0}:{returnValue:r.value}};else return{penpal:"reply",id:r.callId,resolution:"fulfilled",returnValue:r.value};throw h$(r)},K6=({messenger:r,methods:t,timeout:i,channel:o,log:n})=>{let e=m$(),l,u=[],g=!1,c=b$(t),{promise:b,resolve:v,reject:h}=k6(),m=i!==void 0?setTimeout(()=>{h(new _r("CONNECTION_TIMEOUT",`Connection timed out after ${i}ms`))},i):void 0,w=()=>{for(let W of u)W()},O=()=>{if(g)return;u.push(D6(r,t,o,n));let{remoteProxy:W,destroy:Q}=a6(r,o,n);u.push(Q),clearTimeout(m),g=!0,v({remoteProxy:W,destroy:w})},I=()=>{let W={namespace:Jr,type:"SYN",channel:o,participantId:e};n?.("Sending handshake SYN",W);try{r.sendMessage(W)}catch(Q){h(new _r("TRANSMISSION_FAILED",Q.message))}},_=(W)=>{if(n?.("Received handshake SYN",W),W.participantId===l&&l!==Xe)return;if(l=W.participantId,I(),!(e>l||l===Xe))return;let B={namespace:Jr,channel:o,type:"ACK1",methodPaths:c};n?.("Sending handshake ACK1",B);try{r.sendMessage(B)}catch(tr){h(new _r("TRANSMISSION_FAILED",tr.message));return}},J=(W)=>{n?.("Received handshake ACK1",W);let Q={namespace:Jr,channel:o,type:"ACK2"};n?.("Sending handshake ACK2",Q);try{r.sendMessage(Q)}catch(B){h(new _r("TRANSMISSION_FAILED",B.message));return}O()},L=(W)=>{n?.("Received handshake ACK2",W),O()},P=(W)=>{if(Yt(W))_(W);if(Ji(W))J(W);if(zn(W))L(W)};return r.addMessageHandler(P),u.push(()=>r.removeMessageHandler(P)),I(),b},V6=K6,L6=(r)=>{let t=!1,i;return(...o)=>{if(!t)t=!0,i=r(...o);return i}},Y6=L6,l$=new WeakSet,E6=({messenger:r,methods:t={},timeout:i,channel:o,log:n})=>{if(!r)throw new _r("INVALID_ARGUMENT","messenger must be defined");if(l$.has(r))throw new _r("INVALID_ARGUMENT","A messenger can only be used for a single connection");l$.add(r);let e=[r.destroy],l=Y6((c)=>{if(c){let b={namespace:Jr,channel:o,type:"DESTROY"};try{r.sendMessage(b)}catch(v){}}for(let b of e)b();n?.("Connection destroyed")}),u=(c)=>{return x6(c)&&c.channel===o};return{promise:(async()=>{try{r.initialize({log:n,validateReceivedMessage:u}),r.addMessageHandler((v)=>{if(w6(v))l(!1)});let{remoteProxy:c,destroy:b}=await V6({messenger:r,methods:t,timeout:i,channel:o,log:n});return e.push(b),c}catch(c){throw l(!0),c}})(),destroy:()=>{l(!0)}}},$$=E6,Q6=class{#r;#o;#n;#t;#l;#i=new Set;#e;#c=!1;constructor({remoteWindow:r,allowedOrigins:t}){if(!r)throw new _r("INVALID_ARGUMENT","remoteWindow must be defined");this.#r=r,this.#o=t?.length?t:[window.origin]}initialize=({log:r,validateReceivedMessage:t})=>{this.#n=r,this.#t=t,window.addEventListener("message",this.#m)};sendMessage=(r,t)=>{if(Yt(r)){let i=this.#u(r);this.#r.postMessage(r,{targetOrigin:i,transfer:t});return}if(Ji(r)||this.#c){let i=this.#c?W6(r):r,o=this.#u(r);this.#r.postMessage(i,{targetOrigin:o,transfer:t});return}if(zn(r)){let{port1:i,port2:o}=new MessageChannel;this.#e=i,i.addEventListener("message",this.#g),i.start();let n=[o,...t||[]],e=this.#u(r);this.#r.postMessage(r,{targetOrigin:e,transfer:n});return}if(this.#e){this.#e.postMessage(r,{transfer:t});return}throw new Et("Port is undefined")};addMessageHandler=(r)=>{this.#i.add(r)};removeMessageHandler=(r)=>{this.#i.delete(r)};destroy=()=>{window.removeEventListener("message",this.#m),this.#b(),this.#i.clear()};#v=(r)=>{return this.#o.some((t)=>t instanceof RegExp?t.test(r):t===r||t==="*")};#u=(r)=>{if(Yt(r))return"*";if(!this.#l)throw new Et("Concrete remote origin not set");return this.#l==="null"&&this.#o.includes("*")?"*":this.#l};#b=()=>{this.#e?.removeEventListener("message",this.#g),this.#e?.close(),this.#e=void 0};#m=({source:r,origin:t,ports:i,data:o})=>{if(r!==this.#r)return;if(X6(o))this.#n?.("Please upgrade the child window to the latest version of Penpal."),this.#c=!0,o=q6(o);if(!this.#t?.(o))return;if(!this.#v(t)){this.#n?.(`Received a message from origin \`${t}\` which did not match allowed origins \`[${this.#o.join(", ")}]\``);return}if(Yt(o))this.#b(),this.#l=t;if(zn(o)&&!this.#c){if(this.#e=i[0],!this.#e)throw new Et("No port received on ACK2");this.#e.addEventListener("message",this.#g),this.#e.start()}for(let n of this.#i)n(o)};#g=({data:r})=>{if(!this.#t?.(r))return;for(let t of this.#i)t(r)}},x$=Q6,nU=class{#r;#o;#n=new Set;#t;constructor({worker:r}){if(!r)throw new _r("INVALID_ARGUMENT","worker must be defined");this.#r=r}initialize=({validateReceivedMessage:r})=>{this.#o=r,this.#r.addEventListener("message",this.#i)};sendMessage=(r,t)=>{if(Yt(r)||Ji(r)){this.#r.postMessage(r,{transfer:t});return}if(zn(r)){let{port1:i,port2:o}=new MessageChannel;this.#t=i,i.addEventListener("message",this.#i),i.start(),this.#r.postMessage(r,{transfer:[o,...t||[]]});return}if(this.#t){this.#t.postMessage(r,{transfer:t});return}throw new Et("Port is undefined")};addMessageHandler=(r)=>{this.#n.add(r)};removeMessageHandler=(r)=>{this.#n.delete(r)};destroy=()=>{this.#r.removeEventListener("message",this.#i),this.#l(),this.#n.clear()};#l=()=>{this.#t?.removeEventListener("message",this.#i),this.#t?.close(),this.#t=void 0};#i=({ports:r,data:t})=>{if(!this.#o?.(t))return;if(Yt(t))this.#l();if(zn(t)){if(this.#t=r[0],!this.#t)throw new Et("No port received on ACK2");this.#t.addEventListener("message",this.#i),this.#t.start()}for(let i of this.#n)i(t)}};var iU=class{#r;#o;#n=new Set;constructor({port:r}){if(!r)throw new _r("INVALID_ARGUMENT","port must be defined");this.#r=r}initialize=({validateReceivedMessage:r})=>{this.#o=r,this.#r.addEventListener("message",this.#t),this.#r.start()};sendMessage=(r,t)=>{this.#r?.postMessage(r,{transfer:t})};addMessageHandler=(r)=>{this.#n.add(r)};removeMessageHandler=(r)=>{this.#n.delete(r)};destroy=()=>{this.#r.removeEventListener("message",this.#t),this.#r.close(),this.#n.clear()};#t=({data:r})=>{if(!this.#o?.(r))return;for(let t of this.#n)t(r)}};var w$=["SCRIPT","STYLE","LINK","META","NOSCRIPT"],f$=new Set(["a","abbr","area","audio","b","bdi","bdo","br","button","canvas","cite","code","data","datalist","del","dfn","em","embed","h1","h2","h3","h4","h5","h6","i","iframe","img","input","ins","kbd","label","li","map","mark","meter","noscript","object","output","p","picture","progress","q","ruby","s","samp","script","select","slot","small","span","strong","sub","sup","svg","template","textarea","time","u","var","video","wbr"]);var Pe=".next-prod";var _U={SCALE:0.7,PAN_POSITION:{x:175,y:100},URL:"http://localhost:3000/",ASPECT_RATIO_LOCKED:!1,DEVICE:"Custom:Custom",THEME:"system",ORIENTATION:"Portrait",MIN_DIMENSIONS:{width:"280px",height:"360px"},COMMANDS:{run:"bun run dev",build:"bun run build",install:"bun install"},IMAGE_FOLDER:"public",IMAGE_DIMENSION:{width:"100px",height:"100px"},FONT_FOLDER:"fonts",FONT_CONFIG:"app/fonts.ts",TAILWIND_CONFIG:"tailwind.config.ts",CHAT_SETTINGS:{showSuggestions:!0,autoApplyCode:!0,expandCodeBlocks:!1,showMiniChat:!0,maxImages:5},EDITOR_SETTINGS:{shouldWarnDelete:!1,enableBunReplace:!0,buildFlags:"--no-lint"}};var qe=["node_modules","dist","build",".git",".next"],pU=[...qe,"static","out",Pe],IU=[...qe,Pe],aU=[...qe,"coverage"],F6=[".jsx",".tsx"],G6=[".js",".ts",".mjs",".cjs"],UU=[...F6,...G6];var XU={["en"]:"English",["ja"]:"日本語",["zh"]:"中文",["ko"]:"한국어"};var j$=ah(Je(),1);function S(r){return document.querySelector(`[${"data-odid"}="${r}"]`)}function We(r,t=!1){let i=`[${"data-odid"}="${r}"]`;if(!t)return i;return S6(i)}function S6(r){return CSS.escape(r)}function Ot(r){return r&&r instanceof Node&&r.nodeType===Node.ELEMENT_NODE&&!w$.includes(r.tagName)&&!r.hasAttribute("data-onlook-ignore")&&r.style.display!=="none"}var N6="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";var z$=(r=21)=>{let t="",i=r|0;while(i--)t+=N6[Math.random()*64|0];return t};function Xr(r){let t=r.getAttribute("data-odid");if(!t)t=`odid-${z$()}`,r.setAttribute("data-odid",t);return t}function Sr(r){return r.getAttribute("data-oid")}function Nr(r){return r.getAttribute("data-oiid")}function D$(r,t){if(!jr)return;jr.onDomProcessed({layerMap:Object.fromEntries(r),rootNode:t}).catch((i)=>{console.error("Failed to send DOM processed event:",i)})}function Ke(r){window._onlookFrameId=r}function Qt(){let r=window._onlookFrameId;if(!r)return console.warn("Frame id not found"),jr?.getFrameId().then((t)=>{Ke(t)}),"";return r}function B6(r=document.body){if(!Qt())return console.warn("frameView id not found, skipping dom processing"),null;let i=zr(r);if(!i)return console.warn("Error building layer tree, root element is null"),null;let o=r.getAttribute("data-odid");if(!o)return console.warn("Root dom id not found"),null;let n=i.get(o);if(!n)return console.warn("Root node not found"),null;return D$(i,n),{rootDomId:o,layerMap:Array.from(i.entries())}}var Xi=j$.default(B6,500),y6=[(r)=>{let t=r.parentElement;return t&&t.tagName.toLowerCase()==="svg"},(r)=>{return r.tagName.toLowerCase()==="next-route-announcer"},(r)=>{return r.tagName.toLowerCase()==="nextjs-portal"}];function zr(r){if(!Ot(r))return null;let t=new Map,i=document.createTreeWalker(r,NodeFilter.SHOW_ELEMENT,{acceptNode:(e)=>{let l=e;if(y6.some((u)=>u(l)))return NodeFilter.FILTER_REJECT;return Ot(l)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}}),o=_$(r);o.children=[],t.set(o.domId,o);let n=i.nextNode();while(n){let e=_$(n);e.children=[];let l=n.parentElement;if(l){let u=l.getAttribute("data-odid");if(u){e.parent=u;let g=t.get(u);if(g&&g.children)g.children.push(e.domId)}}t.set(e.domId,e),n=i.nextNode()}return t}function _$(r){let t=Xr(r),i=Sr(r),o=Nr(r),n=Array.from(r.childNodes).map((g)=>g.nodeType===Node.TEXT_NODE?g.textContent:"").join(" ").trim().slice(0,500),e=window.getComputedStyle(r),l=r.getAttribute("data-ocname");return{domId:t,oid:i||null,instanceId:o||null,textContent:n||"",tagName:r.tagName.toLowerCase(),isVisible:e.visibility!=="hidden",component:l||null,frameId:Qt(),children:null,parent:null,dynamicType:null,coreElementType:null}}function Ve(r){throw new Error(`Expected \`never\`, found: ${JSON.stringify(r)}`)}var O$=(r)=>JSON.parse(JSON.stringify(r));function p$(r){let t=a$(r),i=A6(r),o=H6(r);return{defined:{width:"auto",height:"auto",...i,...o},computed:t}}function I$(r){let t=S(r);if(!t)return{};return a$(t)}function a$(r){return O$(window.getComputedStyle(r))}function A6(r){let t={},i=U$(r.style.cssText);return Object.entries(i).forEach(([o,n])=>{t[o]=n}),t}function H6(r){let t={},i=document.styleSheets;for(let o=0;ot[c]=b)}}catch(u){console.warn("Error",u)}}return t}function U$(r){let t={};return r.split(";").forEach((i)=>{if(i=i.trim(),!i)return;let[o,...n]=i.split(":");t[o?.trim()??""]=n.join(":").trim()}),t}var k$=(r,t)=>{let i=document.elementFromPoint(r,t);if(!i)return;let o=(e)=>{if(e?.shadowRoot){let l=e.shadowRoot.elementFromPoint(r,t);if(l==e)return e;else if(l?.shadowRoot)return o(l);else return l||e}else return e};return o(i)||i},lr=(r,t)=>{let i=r.parentElement,o=i?{domId:i.getAttribute("data-odid"),frameId:Qt(),oid:i.getAttribute("data-oid"),instanceId:i.getAttribute("data-oiid"),rect:i.getBoundingClientRect()}:null,n=r.getBoundingClientRect(),e=t?p$(r):null;return{domId:r.getAttribute("data-odid"),oid:r.getAttribute("data-oid"),frameId:Qt(),instanceId:r.getAttribute("data-oiid"),rect:n,tagName:r.tagName,parent:o,styles:e}};function Pi(r){try{let t=r.getAttribute("data-onlook-drag-saved-style");if(t){let i=JSON.parse(t);for(let o in i)r.style[o]=i[o]}}catch(t){console.warn("Error restoring style",t)}}function J$(r){let t=r.parentElement;if(!t)return;return{type:"index",targetDomId:t.getAttribute("data-odid"),targetOid:Nr(t)||Sr(t)||null,index:Array.from(r.parentElement?.children||[]).indexOf(r),originalIndex:Array.from(r.parentElement?.children||[]).indexOf(r)}}var X$=(r)=>{let t=Array.from(r.childNodes).filter((i)=>i.nodeType===Node.TEXT_NODE).map((i)=>i.textContent);if(t.length===0)return;return t.join("")};var qi=(r,t)=>{let i=S(r)||document.body;return lr(i,t)},P$=(r,t,i)=>{let o=R6(r,t)||document.body;return lr(o,i)},R6=(r,t)=>{let i=document.elementFromPoint(r,t);if(!i)return;let o=(e)=>{if(e?.shadowRoot){let l=e.shadowRoot.elementFromPoint(r,t);if(l==e)return e;else if(l?.shadowRoot)return o(l);else return l||e}else return e};return o(i)||i},q$=(r,t,i)=>{let o=S(r);if(!o){console.warn("Failed to updateElementInstanceId: Element not found");return}o.setAttribute("data-oiid",t),o.setAttribute("data-ocname",i)},W$=(r)=>{let t=S(r);if(!t?.parentElement)return null;return lr(t.parentElement,!1)},K$=(r)=>{let t=S(r);if(!t)return 0;return t.children.length},V$=(r)=>{let t=S(r);if(!t)return null;return lr(t.offsetParent,!1)};function L$(r,t,i){let o=S(r.domId);if(!o)return console.warn("Failed to find parent element",r.domId),null;let n=M6(t),e=new Set(i.map((c)=>c.domId)),l=Array.from(o.children).map((c,b)=>({element:c,index:b,domId:Xr(c)})).filter(({domId:c})=>e.has(c));if(l.length===0)return console.warn("No valid children found to group"),null;let u=Math.min(...l.map((c)=>c.index));return o.insertBefore(n,o.children[u]??null),l.forEach(({element:c})=>{let b=c.cloneNode(!0);b.setAttribute("data-onlook-inserted","true"),n.appendChild(b),c.style.display="none",E$(c)}),{domEl:lr(n,!0),newMap:zr(n)}}function Y$(r,t){let i=S(r.domId);if(!i)return console.warn(`Parent element not found: ${r.domId}`),null;let o;if(t.domId)o=S(t.domId);else return console.warn("Container domId is required for ungrouping"),null;if(!o)return console.warn("Container element not found for ungrouping"),null;return Array.from(o.children).forEach((l)=>{i.appendChild(l)}),o.remove(),{domEl:lr(i,!0),newMap:zr(i)}}function M6(r){let t=document.createElement(r.tagName);return Object.entries(r.attributes).forEach(([i,o])=>{t.setAttribute(i,o)}),t.setAttribute("data-onlook-inserted","true"),t.setAttribute("data-odid",r.domId),t.setAttribute("data-oid",r.oid),t}function E$(r){r.removeAttribute("data-odid"),r.removeAttribute("data-oid"),r.removeAttribute("data-onlook-inserted");let t=Array.from(r.children);if(t.length===0)return;t.forEach((i)=>{E$(i)})}function Wi(r){let t=S(r);if(!t)return console.warn("Element not found for domId:",r),null;return Q$(t)}function Q$(r){let t=Array.from(r.attributes).reduce((o,n)=>{return o[n.name]=n.value,o},{}),i=Nr(r)||Sr(r)||null;if(!i)return console.warn("Element has no oid"),null;return{oid:i,domId:Xr(r),tagName:r.tagName.toLowerCase(),children:Array.from(r.children).map((o)=>Q$(o)).filter(Boolean),attributes:t,textContent:X$(r)||null,styles:{}}}function F$(r){let t=S(r);if(!t)throw new Error("Element not found for domId: "+r);let i=t.parentElement;if(!i)throw new Error("Inserted element has no parent");let o=Nr(i)||Sr(i);if(!o)return console.warn("Parent element has no oid"),null;let n=Xr(i),e=Array.from(i.children).indexOf(t);if(e===-1)return{type:"append",targetDomId:n,targetOid:o};return{type:"index",targetDomId:n,targetOid:o,index:e,originalIndex:e}}function G$(r){let t=document.querySelector(`[${"data-odid"}="${r}"]`);if(!t)return console.warn("No element found",{domId:r}),{dynamicType:null,coreType:null};let i=t.getAttribute("data-onlook-dynamic-type")||null,o=t.getAttribute("data-onlook-core-element-type")||null;return{dynamicType:i,coreType:o}}function S$(r,t,i){let o=document.querySelector(`[${"data-odid"}="${r}"]`);if(o){if(t)o.setAttribute("data-onlook-dynamic-type",t);if(i)o.setAttribute("data-onlook-core-element-type",i)}}function N$(){let t=document.body.querySelector(`[${"data-oid"}]`);if(t)return lr(t,!0);return null}var Qr=0,f=1,U=2,R=3,G=4,gr=5,Ft=6,er=7,wr=8,X=9,k=10,y=11,q=12,Y=13,dr=14,hr=15,d=16,nr=17,ir=18,br=19,fr=20,K=21,p=22,Z=23,xr=24,A=25;function cr(r){return r>=48&&r<=57}function Ir(r){return cr(r)||r>=65&&r<=70||r>=97&&r<=102}function Li(r){return r>=65&&r<=90}function Z6(r){return r>=97&&r<=122}function C6(r){return Li(r)||Z6(r)}function T6(r){return r>=128}function Vi(r){return C6(r)||T6(r)||r===95}function Dn(r){return Vi(r)||cr(r)||r===45}function d6(r){return r>=0&&r<=8||r===11||r>=14&&r<=31||r===127}function _n(r){return r===10||r===13||r===12}function Br(r){return _n(r)||r===32||r===9}function Or(r,t){if(r!==92)return!1;if(_n(t)||t===0)return!1;return!0}function Gt(r,t,i){if(r===45)return Vi(t)||t===45||Or(t,i);if(Vi(r))return!0;if(r===92)return Or(r,t);return!1}function Yi(r,t,i){if(r===43||r===45){if(cr(t))return 2;return t===46&&cr(i)?3:0}if(r===46)return cr(t)?2:0;if(cr(r))return 1;return 0}function Ei(r){if(r===65279)return 1;if(r===65534)return 1;return 0}var Le=new Array(128),s6=128,jn=130,Ye=131,Qi=132,Ee=133;for(let r=0;rr.length)return!1;for(let n=t;n=0;t--)if(!Br(r.charCodeAt(t)))break;return t+1}function On(r,t){for(;t=55296&&t<=57343||t>1114111)t=65533;return String.fromCodePoint(t)}var Nt=["EOF-token","ident-token","function-token","at-keyword-token","hash-token","string-token","bad-string-token","url-token","bad-url-token","delim-token","number-token","percentage-token","dimension-token","whitespace-token","CDO-token","CDC-token","colon-token","semicolon-token","comma-token","[-token","]-token","(-token",")-token","{-token","}-token","comment-token"];function Bt(r=null,t){if(r===null||r.length0?Ei(t.charCodeAt(0)):0,n=Bt(r.lines,i),e=Bt(r.columns,i),l=r.startLine,u=r.startColumn;for(let g=o;g{}){r=String(r||"");let i=r.length,o=Bt(this.offsetAndType,r.length+1),n=Bt(this.balance,r.length+1),e=0,l=-1,u=0,g=r.length;this.offsetAndType=null,this.balance=null,n.fill(0),t(r,(c,b,v)=>{let h=e++;if(o[h]=c<>Hr]}else if(R$(c))g=h,u=It[c]}),o[e]=Qr<e)n[c]=e}this.source=r,this.firstCharOffset=l===-1?0:l,this.tokenCount=e,this.offsetAndType=o,this.balance=n,this.reset(),this.next()}lookupType(r){if(r+=this.tokenIndex,r>Hr;return Qr}lookupTypeNonSC(r){for(let t=this.tokenIndex;t>Hr;if(i!==Y&&i!==A){if(r--===0)return i}}return Qr}lookupOffset(r){if(r+=this.tokenIndex,r>Hr;if(i!==Y&&i!==A){if(r--===0)return t-this.tokenIndex}}return Qr}lookupValue(r,t){if(r+=this.tokenIndex,r0)return r>Hr,this.tokenEnd=t&Ar;else this.tokenIndex=this.tokenCount,this.next()}next(){let r=this.tokenIndex+1;if(r>Hr,this.tokenEnd=r&Ar;else this.eof=!0,this.tokenIndex=this.tokenCount,this.tokenType=Qr,this.tokenStart=this.tokenEnd=this.source.length}skipSC(){while(this.tokenType===Y||this.tokenType===A)this.next()}skipUntilBalanced(r,t){let i=r,o=0,n=0;r:for(;i0?this.offsetAndType[i-1]&Ar:this.firstCharOffset,t(this.source.charCodeAt(n))){case 1:break r;case 2:i++;break r;default:if(R$(this.offsetAndType[i]>>Hr))i=o}}this.skip(i-this.tokenIndex)}forEachToken(r){for(let t=0,i=this.firstCharOffset;t>Hr;i=e,r(l,o,e,t)}}dump(){let r=new Array(this.tokenCount);return this.forEachToken((t,i,o,n)=>{r[n]={idx:n,type:Nt[t],chunk:this.source.substring(i,o),balance:this.balance[n]}}),r}}function mt(r,t){function i(v){return v=r.length){if(cString(w+_+1).padStart(h)+" |"+I).join(` +var I4=Object.create;var{getPrototypeOf:k4,defineProperty:Ue,getOwnPropertyNames:U4}=Object;var J4=Object.prototype.hasOwnProperty;var qh=(r,t,i)=>{i=r!=null?I4(k4(r)):{};let o=t||!r||!r.__esModule?Ue(i,"default",{value:r,enumerable:!0}):i;for(let n of U4(r))if(!J4.call(o,n))Ue(o,n,{get:()=>r[n],enumerable:!0});return o};var vr=(r,t)=>()=>(t||r((t={exports:{}}).exports,t),t.exports);var I=(r,t)=>{for(var i in t)Ue(r,i,{get:t[i],enumerable:!0,configurable:!0,set:(o)=>t[i]=()=>o})};var Je=vr((sk,Wh)=>{function P4(r){var t=typeof r;return r!=null&&(t=="object"||t=="function")}Wh.exports=P4});var Vh=vr((rU,Kh)=>{var X4=typeof global=="object"&&global&&global.Object===Object&&global;Kh.exports=X4});var Pe=vr((tU,Lh)=>{var q4=Vh(),W4=typeof self=="object"&&self&&self.Object===Object&&self,K4=q4||W4||Function("return this")();Lh.exports=K4});var Eh=vr((nU,Yh)=>{var V4=Pe(),L4=function(){return V4.Date.now()};Yh.exports=L4});var Fh=vr((iU,Qh)=>{var Y4=/\s/;function E4(r){var t=r.length;while(t--&&Y4.test(r.charAt(t)));return t}Qh.exports=E4});var Nh=vr((oU,Sh)=>{var Q4=Fh(),F4=/^\s+/;function S4(r){return r?r.slice(0,Q4(r)+1).replace(F4,""):r}Sh.exports=S4});var Xe=vr((eU,Gh)=>{var N4=Pe(),G4=N4.Symbol;Gh.exports=G4});var Hh=vr((lU,Ah)=>{var Bh=Xe(),yh=Object.prototype,B4=yh.hasOwnProperty,y4=yh.toString,wn=Bh?Bh.toStringTag:void 0;function A4(r){var t=B4.call(r,wn),i=r[wn];try{r[wn]=void 0;var o=!0}catch(e){}var n=y4.call(r);if(o)if(t)r[wn]=i;else delete r[wn];return n}Ah.exports=A4});var Mh=vr((cU,Rh)=>{var H4=Object.prototype,R4=H4.toString;function M4(r){return R4.call(r)}Rh.exports=M4});var dh=vr((uU,Th)=>{var Zh=Xe(),Z4=Hh(),C4=Mh(),T4="[object Null]",d4="[object Undefined]",Ch=Zh?Zh.toStringTag:void 0;function s4(r){if(r==null)return r===void 0?d4:T4;return Ch&&Ch in Object(r)?Z4(r):C4(r)}Th.exports=s4});var r$=vr((gU,sh)=>{function r6(r){return r!=null&&typeof r=="object"}sh.exports=r6});var n$=vr((mU,t$)=>{var t6=dh(),n6=r$(),i6="[object Symbol]";function o6(r){return typeof r=="symbol"||n6(r)&&t6(r)==i6}t$.exports=o6});var l$=vr((bU,e$)=>{var e6=Nh(),i$=Je(),l6=n$(),o$=NaN,c6=/^[-+]0x[0-9a-f]+$/i,u6=/^0b[01]+$/i,g6=/^0o[0-7]+$/i,m6=parseInt;function b6(r){if(typeof r=="number")return r;if(l6(r))return o$;if(i$(r)){var t=typeof r.valueOf=="function"?r.valueOf():r;r=i$(t)?t+"":t}if(typeof r!="string")return r===0?r:+r;r=e6(r);var i=u6.test(r);return i||g6.test(r)?m6(r.slice(2),i?2:8):c6.test(r)?o$:+r}e$.exports=b6});var We=vr((vU,u$)=>{var v6=Je(),qe=Eh(),c$=l$(),h6="Expected a function",$6=Math.max,x6=Math.min;function f6(r,t,i){var o,n,e,l,u,g,c=0,m=!1,v=!1,h=!0;if(typeof r!="function")throw new TypeError(h6);if(t=c$(t)||0,v6(i))m=!!i.leading,v="maxWait"in i,e=v?$6(c$(i.maxWait)||0,t):e,h="trailing"in i?!!i.trailing:h;function b(Q){var B=o,tr=n;return o=n=void 0,c=Q,l=r.apply(tr,B),l}function f(Q){return c=Q,u=setTimeout(_,t),m?b(Q):l}function D(Q){var B=Q-g,tr=Q-c,ke=t-B;return v?x6(ke,e-tr):ke}function O(Q){var B=Q-g,tr=Q-c;return g===void 0||B>=t||B<0||v&&tr>=e}function _(){var Q=qe();if(O(Q))return J(Q);u=setTimeout(_,D(Q))}function J(Q){if(u=void 0,h&&o)return b(Q);return o=n=void 0,l}function L(){if(u!==void 0)clearTimeout(u);c=0,o=g=n=u=void 0}function X(){return u===void 0?l:J(qe())}function W(){var Q=qe(),B=O(Q);if(o=arguments,n=this,g=Q,B){if(u===void 0)return f(g);if(v)return clearTimeout(u),u=setTimeout(_,t),b(g)}if(u===void 0)u=setTimeout(_,t);return l}return W.cancel=L,W.flush=X,W}u$.exports=f6});var g0=vr((hz)=>{var u0="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");hz.encode=function(r){if(0<=r&&r{var m0=g0(),Re=5,b0=1<>1;return t?-i:i}zz.encode=function r(t){var i="",o,n=fz(t);do{if(o=n&v0,n>>>=Re,n>0)o|=h0;i+=m0.encode(o)}while(n>0);return i};zz.decode=function r(t,i,o){var n=t.length,e=0,l=0,u,g;do{if(i>=n)throw new Error("Expected more digits in base 64 VLQ value.");if(g=m0.decode(t.charCodeAt(i++)),g===-1)throw new Error("Invalid base64 digit: "+t.charAt(i-1));u=!!(g&h0),g&=v0,e=e+(g<{function _z(r,t,i){if(t in r)return r[t];else if(arguments.length===3)return i;else throw new Error('"'+t+'" is a required argument.')}Lz.getArg=_z;var x0=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/,Dz=/^data:.+\,.+$/;function In(r){var t=r.match(x0);if(!t)return null;return{scheme:t[1],auth:t[2],host:t[3],port:t[4],path:t[5]}}Lz.urlParse=In;function At(r){var t="";if(r.scheme)t+=r.scheme+":";if(t+="//",r.auth)t+=r.auth+"@";if(r.host)t+=r.host;if(r.port)t+=":"+r.port;if(r.path)t+=r.path;return t}Lz.urlGenerate=At;var jz=32;function Oz(r){var t=[];return function(i){for(var o=0;ojz)t.pop();return e}}var Me=Oz(function r(t){var i=t,o=In(t);if(o){if(!o.path)return t;i=o.path}var n=Lz.isAbsolute(i),e=[],l=0,u=0;while(!0)if(l=u,u=i.indexOf("/",l),u===-1){e.push(i.slice(l));break}else{e.push(i.slice(l,u));while(u=0;u--)if(g=e[u],g===".")e.splice(u,1);else if(g==="..")c++;else if(c>0)if(g==="")e.splice(u+1,c),c=0;else e.splice(u,2),c--;if(i=e.join("/"),i==="")i=n?"/":".";if(o)return o.path=i,At(o);return i});Lz.normalize=Me;function f0(r,t){if(r==="")r=".";if(t==="")t=".";var i=In(t),o=In(r);if(o)r=o.path||"/";if(i&&!i.scheme){if(o)i.scheme=o.scheme;return At(i)}if(i||t.match(Dz))return t;if(o&&!o.host&&!o.path)return o.host=t,At(o);var n=t.charAt(0)==="/"?t:Me(r.replace(/\/+$/,"")+"/"+t);if(o)return o.path=n,At(o);return n}Lz.join=f0;Lz.isAbsolute=function(r){return r.charAt(0)==="/"||x0.test(r)};function Iz(r,t){if(r==="")r=".";r=r.replace(/\/$/,"");var i=0;while(t.indexOf(r+"/")!==0){var o=r.lastIndexOf("/");if(o<0)return t;if(r=r.slice(0,o),r.match(/^([^\/]+:\/)?\/*$/))return t;++i}return Array(i+1).join("../")+t.substr(r.length+1)}Lz.relative=Iz;var w0=function(){var r=Object.create(null);return!("__proto__"in r)}();function z0(r){return r}function kz(r){if(a0(r))return"$"+r;return r}Lz.toSetString=w0?z0:kz;function Uz(r){if(a0(r))return r.slice(1);return r}Lz.fromSetString=w0?z0:Uz;function a0(r){if(!r)return!1;var t=r.length;if(t<9)return!1;if(r.charCodeAt(t-1)!==95||r.charCodeAt(t-2)!==95||r.charCodeAt(t-3)!==111||r.charCodeAt(t-4)!==116||r.charCodeAt(t-5)!==111||r.charCodeAt(t-6)!==114||r.charCodeAt(t-7)!==112||r.charCodeAt(t-8)!==95||r.charCodeAt(t-9)!==95)return!1;for(var i=t-10;i>=0;i--)if(r.charCodeAt(i)!==36)return!1;return!0}function Jz(r,t,i){var o=tt(r.source,t.source);if(o!==0)return o;if(o=r.originalLine-t.originalLine,o!==0)return o;if(o=r.originalColumn-t.originalColumn,o!==0||i)return o;if(o=r.generatedColumn-t.generatedColumn,o!==0)return o;if(o=r.generatedLine-t.generatedLine,o!==0)return o;return tt(r.name,t.name)}Lz.compareByOriginalPositions=Jz;function Pz(r,t,i){var o=r.originalLine-t.originalLine;if(o!==0)return o;if(o=r.originalColumn-t.originalColumn,o!==0||i)return o;if(o=r.generatedColumn-t.generatedColumn,o!==0)return o;if(o=r.generatedLine-t.generatedLine,o!==0)return o;return tt(r.name,t.name)}Lz.compareByOriginalPositionsNoSource=Pz;function Xz(r,t,i){var o=r.generatedLine-t.generatedLine;if(o!==0)return o;if(o=r.generatedColumn-t.generatedColumn,o!==0||i)return o;if(o=tt(r.source,t.source),o!==0)return o;if(o=r.originalLine-t.originalLine,o!==0)return o;if(o=r.originalColumn-t.originalColumn,o!==0)return o;return tt(r.name,t.name)}Lz.compareByGeneratedPositionsDeflated=Xz;function qz(r,t,i){var o=r.generatedColumn-t.generatedColumn;if(o!==0||i)return o;if(o=tt(r.source,t.source),o!==0)return o;if(o=r.originalLine-t.originalLine,o!==0)return o;if(o=r.originalColumn-t.originalColumn,o!==0)return o;return tt(r.name,t.name)}Lz.compareByGeneratedPositionsDeflatedNoLine=qz;function tt(r,t){if(r===t)return 0;if(r===null)return 1;if(t===null)return-1;if(r>t)return 1;return-1}function Wz(r,t){var i=r.generatedLine-t.generatedLine;if(i!==0)return i;if(i=r.generatedColumn-t.generatedColumn,i!==0)return i;if(i=tt(r.source,t.source),i!==0)return i;if(i=r.originalLine-t.originalLine,i!==0)return i;if(i=r.originalColumn-t.originalColumn,i!==0)return i;return tt(r.name,t.name)}Lz.compareByGeneratedPositionsInflated=Wz;function Kz(r){return JSON.parse(r.replace(/^\)]}'[^\n]*\n/,""))}Lz.parseSourceMapInput=Kz;function Vz(r,t,i){if(t=t||"",r){if(r[r.length-1]!=="/"&&t[0]!=="/")r+="/";t=r+t}if(i){var o=In(i);if(!o)throw new Error("sourceMapURL could not be parsed");if(o.path){var n=o.path.lastIndexOf("/");if(n>=0)o.path=o.path.substring(0,n+1)}t=f0(At(o),t)}return Me(t)}Lz.computeSourceURL=Vz});var p0=vr((dz)=>{var Ze=Mi(),Ce=Object.prototype.hasOwnProperty,kt=typeof Map!=="undefined";function nt(){this._array=[],this._set=kt?new Map:Object.create(null)}nt.fromArray=function r(t,i){var o=new nt;for(var n=0,e=t.length;n=0)return i}else{var o=Ze.toSetString(t);if(Ce.call(this._set,o))return this._set[o]}throw new Error('"'+t+'" is not in the set.')};nt.prototype.at=function r(t){if(t>=0&&t{var _0=Mi();function ra(r,t){var i=r.generatedLine,o=t.generatedLine,n=r.generatedColumn,e=t.generatedColumn;return o>i||o==i&&e>=n||_0.compareByGeneratedPositionsInflated(r,t)<=0}function Zi(){this._array=[],this._sorted=!0,this._last={generatedLine:-1,generatedColumn:0}}Zi.prototype.unsortedForEach=function r(t,i){this._array.forEach(t,i)};Zi.prototype.add=function r(t){if(ra(this._last,t))this._last=t,this._array.push(t);else this._sorted=!1,this._array.push(t)};Zi.prototype.toArray=function r(){if(!this._sorted)this._array.sort(_0.compareByGeneratedPositionsInflated),this._sorted=!0;return this._array};ta.MappingList=Zi});var Lt="PENPAL_CHILD";var j4=qh(We(),1);var w6=class extends Error{code;constructor(r,t){super(t);this.name="PenpalError",this.code=r}},pr=w6,z6=(r)=>({name:r.name,message:r.message,stack:r.stack,penpalCode:r instanceof pr?r.code:void 0}),a6=({name:r,message:t,stack:i,penpalCode:o})=>{let n=o?new pr(o,t):new Error(t);return n.name=r,n.stack=i,n},p6=Symbol("Reply"),_6=class{value;transferables;#r=p6;constructor(r,t){this.value=r,this.transferables=t?.transferables}},D6=_6,Jr="penpal",qi=(r)=>{return typeof r==="object"&&r!==null},h$=(r)=>{return typeof r==="function"},j6=(r)=>{return qi(r)&&r.namespace===Jr},Yt=(r)=>{return r.type==="SYN"},Wi=(r)=>{return r.type==="ACK1"},zn=(r)=>{return r.type==="ACK2"},$$=(r)=>{return r.type==="CALL"},x$=(r)=>{return r.type==="REPLY"},O6=(r)=>{return r.type==="DESTROY"},f$=(r,t=[])=>{let i=[];for(let o of Object.keys(r)){let n=r[o];if(h$(n))i.push([...t,o]);else if(qi(n))i.push(...f$(n,[...t,o]))}return i},I6=(r,t)=>{let i=r.reduce((o,n)=>{return qi(o)?o[n]:void 0},t);return h$(i)?i:void 0},mt=(r)=>{return r.join(".")},g$=(r,t,i)=>({namespace:Jr,channel:r,type:"REPLY",callId:t,isError:!0,...i instanceof Error?{value:z6(i),isSerializedErrorInstance:!0}:{value:i}}),k6=(r,t,i,o)=>{let n=!1,e=async(l)=>{if(n)return;if(!$$(l))return;o?.(`Received ${mt(l.methodPath)}() call`,l);let{methodPath:u,args:g,id:c}=l,m,v;try{let h=I6(u,t);if(!h)throw new pr("METHOD_NOT_FOUND",`Method \`${mt(u)}\` is not found.`);let b=await h(...g);if(b instanceof D6)v=b.transferables,b=await b.value;m={namespace:Jr,channel:i,type:"REPLY",callId:c,value:b}}catch(h){m=g$(i,c,h)}if(n)return;try{o?.(`Sending ${mt(u)}() reply`,m),r.sendMessage(m,v)}catch(h){if(h.name==="DataCloneError")m=g$(i,c,h),o?.(`Sending ${mt(u)}() reply`,m),r.sendMessage(m);throw h}};return r.addMessageHandler(e),()=>{n=!0,r.removeMessageHandler(e)}},U6=k6,w$=crypto.randomUUID?.bind(crypto)??(()=>new Array(4).fill(0).map(()=>Math.floor(Math.random()*Number.MAX_SAFE_INTEGER).toString(16)).join("-")),J6=Symbol("CallOptions"),P6=class{transferables;timeout;#r=J6;constructor(r){this.transferables=r?.transferables,this.timeout=r?.timeout}},X6=P6,q6=new Set(["apply","call","bind"]),z$=(r,t,i=[])=>{return new Proxy(i.length?()=>{}:Object.create(null),{get(o,n){if(n==="then")return;if(i.length&&q6.has(n))return Reflect.get(o,n);return z$(r,t,[...i,n])},apply(o,n,e){return r(i,e)}})},m$=(r)=>{return new pr("CONNECTION_DESTROYED",`Method call ${mt(r)}() failed due to destroyed connection`)},W6=(r,t,i)=>{let o=!1,n=new Map,e=(g)=>{if(!x$(g))return;let{callId:c,value:m,isError:v,isSerializedErrorInstance:h}=g,b=n.get(c);if(!b)return;if(n.delete(c),i?.(`Received ${mt(b.methodPath)}() call`,g),v)b.reject(h?a6(m):m);else b.resolve(m)};return r.addMessageHandler(e),{remoteProxy:z$((g,c)=>{if(o)throw m$(g);let m=w$(),v=c[c.length-1],h=v instanceof X6,{timeout:b,transferables:f}=h?v:{},D=h?c.slice(0,-1):c;return new Promise((O,_)=>{let J=b!==void 0?window.setTimeout(()=>{n.delete(m),_(new pr("METHOD_CALL_TIMEOUT",`Method call ${mt(g)}() timed out after ${b}ms`))},b):void 0;n.set(m,{methodPath:g,resolve:O,reject:_,timeoutId:J});try{let L={namespace:Jr,channel:t,type:"CALL",id:m,methodPath:g,args:D};i?.(`Sending ${mt(g)}() call`,L),r.sendMessage(L,f)}catch(L){_(new pr("TRANSMISSION_FAILED",L.message))}})},i),destroy:()=>{o=!0,r.removeMessageHandler(e);for(let{methodPath:g,reject:c,timeoutId:m}of n.values())clearTimeout(m),c(m$(g));n.clear()}}},K6=W6,V6=()=>{let r,t;return{promise:new Promise((o,n)=>{r=o,t=n}),resolve:r,reject:t}},L6=V6,Y6=class extends Error{constructor(r){super(`You've hit a bug in Penpal. Please file an issue with the following information: ${r}`)}},Et=Y6,Ke="deprecated-penpal",E6=(r)=>{return qi(r)&&"penpal"in r},Q6=(r)=>r.split("."),b$=(r)=>r.join("."),a$=(r)=>{return new Et(`Unexpected message to translate: ${JSON.stringify(r)}`)},F6=(r)=>{if(r.penpal==="syn")return{namespace:Jr,channel:void 0,type:"SYN",participantId:Ke};if(r.penpal==="ack")return{namespace:Jr,channel:void 0,type:"ACK2"};if(r.penpal==="call")return{namespace:Jr,channel:void 0,type:"CALL",id:r.id,methodPath:Q6(r.methodName),args:r.args};if(r.penpal==="reply")if(r.resolution==="fulfilled")return{namespace:Jr,channel:void 0,type:"REPLY",callId:r.id,value:r.returnValue};else return{namespace:Jr,channel:void 0,type:"REPLY",callId:r.id,isError:!0,...r.returnValueIsError?{value:r.returnValue,isSerializedErrorInstance:!0}:{value:r.returnValue}};throw a$(r)},S6=(r)=>{if(Wi(r))return{penpal:"synAck",methodNames:r.methodPaths.map(b$)};if($$(r))return{penpal:"call",id:r.id,methodName:b$(r.methodPath),args:r.args};if(x$(r))if(r.isError)return{penpal:"reply",id:r.callId,resolution:"rejected",...r.isSerializedErrorInstance?{returnValue:r.value,returnValueIsError:!0}:{returnValue:r.value}};else return{penpal:"reply",id:r.callId,resolution:"fulfilled",returnValue:r.value};throw a$(r)},N6=({messenger:r,methods:t,timeout:i,channel:o,log:n})=>{let e=w$(),l,u=[],g=!1,c=f$(t),{promise:m,resolve:v,reject:h}=L6(),b=i!==void 0?setTimeout(()=>{h(new pr("CONNECTION_TIMEOUT",`Connection timed out after ${i}ms`))},i):void 0,f=()=>{for(let W of u)W()},D=()=>{if(g)return;u.push(U6(r,t,o,n));let{remoteProxy:W,destroy:Q}=K6(r,o,n);u.push(Q),clearTimeout(b),g=!0,v({remoteProxy:W,destroy:f})},O=()=>{let W={namespace:Jr,type:"SYN",channel:o,participantId:e};n?.("Sending handshake SYN",W);try{r.sendMessage(W)}catch(Q){h(new pr("TRANSMISSION_FAILED",Q.message))}},_=(W)=>{if(n?.("Received handshake SYN",W),W.participantId===l&&l!==Ke)return;if(l=W.participantId,O(),!(e>l||l===Ke))return;let B={namespace:Jr,channel:o,type:"ACK1",methodPaths:c};n?.("Sending handshake ACK1",B);try{r.sendMessage(B)}catch(tr){h(new pr("TRANSMISSION_FAILED",tr.message));return}},J=(W)=>{n?.("Received handshake ACK1",W);let Q={namespace:Jr,channel:o,type:"ACK2"};n?.("Sending handshake ACK2",Q);try{r.sendMessage(Q)}catch(B){h(new pr("TRANSMISSION_FAILED",B.message));return}D()},L=(W)=>{n?.("Received handshake ACK2",W),D()},X=(W)=>{if(Yt(W))_(W);if(Wi(W))J(W);if(zn(W))L(W)};return r.addMessageHandler(X),u.push(()=>r.removeMessageHandler(X)),O(),m},G6=N6,B6=(r)=>{let t=!1,i;return(...o)=>{if(!t)t=!0,i=r(...o);return i}},y6=B6,v$=new WeakSet,A6=({messenger:r,methods:t={},timeout:i,channel:o,log:n})=>{if(!r)throw new pr("INVALID_ARGUMENT","messenger must be defined");if(v$.has(r))throw new pr("INVALID_ARGUMENT","A messenger can only be used for a single connection");v$.add(r);let e=[r.destroy],l=y6((c)=>{if(c){let m={namespace:Jr,channel:o,type:"DESTROY"};try{r.sendMessage(m)}catch(v){}}for(let m of e)m();n?.("Connection destroyed")}),u=(c)=>{return j6(c)&&c.channel===o};return{promise:(async()=>{try{r.initialize({log:n,validateReceivedMessage:u}),r.addMessageHandler((v)=>{if(O6(v))l(!1)});let{remoteProxy:c,destroy:m}=await G6({messenger:r,methods:t,timeout:i,channel:o,log:n});return e.push(m),c}catch(c){throw l(!0),c}})(),destroy:()=>{l(!0)}}},p$=A6,H6=class{#r;#o;#n;#t;#l;#i=new Set;#e;#c=!1;constructor({remoteWindow:r,allowedOrigins:t}){if(!r)throw new pr("INVALID_ARGUMENT","remoteWindow must be defined");this.#r=r,this.#o=t?.length?t:[window.origin]}initialize=({log:r,validateReceivedMessage:t})=>{this.#n=r,this.#t=t,window.addEventListener("message",this.#b)};sendMessage=(r,t)=>{if(Yt(r)){let i=this.#u(r);this.#r.postMessage(r,{targetOrigin:i,transfer:t});return}if(Wi(r)||this.#c){let i=this.#c?S6(r):r,o=this.#u(r);this.#r.postMessage(i,{targetOrigin:o,transfer:t});return}if(zn(r)){let{port1:i,port2:o}=new MessageChannel;this.#e=i,i.addEventListener("message",this.#g),i.start();let n=[o,...t||[]],e=this.#u(r);this.#r.postMessage(r,{targetOrigin:e,transfer:n});return}if(this.#e){this.#e.postMessage(r,{transfer:t});return}throw new Et("Port is undefined")};addMessageHandler=(r)=>{this.#i.add(r)};removeMessageHandler=(r)=>{this.#i.delete(r)};destroy=()=>{window.removeEventListener("message",this.#b),this.#m(),this.#i.clear()};#v=(r)=>{return this.#o.some((t)=>t instanceof RegExp?t.test(r):t===r||t==="*")};#u=(r)=>{if(Yt(r))return"*";if(!this.#l)throw new Et("Concrete remote origin not set");return this.#l==="null"&&this.#o.includes("*")?"*":this.#l};#m=()=>{this.#e?.removeEventListener("message",this.#g),this.#e?.close(),this.#e=void 0};#b=({source:r,origin:t,ports:i,data:o})=>{if(r!==this.#r)return;if(E6(o))this.#n?.("Please upgrade the child window to the latest version of Penpal."),this.#c=!0,o=F6(o);if(!this.#t?.(o))return;if(!this.#v(t)){this.#n?.(`Received a message from origin \`${t}\` which did not match allowed origins \`[${this.#o.join(", ")}]\``);return}if(Yt(o))this.#m(),this.#l=t;if(zn(o)&&!this.#c){if(this.#e=i[0],!this.#e)throw new Et("No port received on ACK2");this.#e.addEventListener("message",this.#g),this.#e.start()}for(let n of this.#i)n(o)};#g=({data:r})=>{if(!this.#t?.(r))return;for(let t of this.#i)t(r)}},_$=H6,hU=class{#r;#o;#n=new Set;#t;constructor({worker:r}){if(!r)throw new pr("INVALID_ARGUMENT","worker must be defined");this.#r=r}initialize=({validateReceivedMessage:r})=>{this.#o=r,this.#r.addEventListener("message",this.#i)};sendMessage=(r,t)=>{if(Yt(r)||Wi(r)){this.#r.postMessage(r,{transfer:t});return}if(zn(r)){let{port1:i,port2:o}=new MessageChannel;this.#t=i,i.addEventListener("message",this.#i),i.start(),this.#r.postMessage(r,{transfer:[o,...t||[]]});return}if(this.#t){this.#t.postMessage(r,{transfer:t});return}throw new Et("Port is undefined")};addMessageHandler=(r)=>{this.#n.add(r)};removeMessageHandler=(r)=>{this.#n.delete(r)};destroy=()=>{this.#r.removeEventListener("message",this.#i),this.#l(),this.#n.clear()};#l=()=>{this.#t?.removeEventListener("message",this.#i),this.#t?.close(),this.#t=void 0};#i=({ports:r,data:t})=>{if(!this.#o?.(t))return;if(Yt(t))this.#l();if(zn(t)){if(this.#t=r[0],!this.#t)throw new Et("No port received on ACK2");this.#t.addEventListener("message",this.#i),this.#t.start()}for(let i of this.#n)i(t)}};var $U=class{#r;#o;#n=new Set;constructor({port:r}){if(!r)throw new pr("INVALID_ARGUMENT","port must be defined");this.#r=r}initialize=({validateReceivedMessage:r})=>{this.#o=r,this.#r.addEventListener("message",this.#t),this.#r.start()};sendMessage=(r,t)=>{this.#r?.postMessage(r,{transfer:t})};addMessageHandler=(r)=>{this.#n.add(r)};removeMessageHandler=(r)=>{this.#n.delete(r)};destroy=()=>{this.#r.removeEventListener("message",this.#t),this.#r.close(),this.#n.clear()};#t=({data:r})=>{if(!this.#o?.(r))return;for(let t of this.#n)t(r)}};var D$=["SCRIPT","STYLE","LINK","META","NOSCRIPT"],j$=new Set(["a","abbr","area","audio","b","bdi","bdo","br","button","canvas","cite","code","data","datalist","del","dfn","em","embed","h1","h2","h3","h4","h5","h6","i","iframe","img","input","ins","kbd","label","li","map","mark","meter","noscript","object","output","p","picture","progress","q","ruby","s","samp","script","select","slot","small","span","strong","sub","sup","svg","template","textarea","time","u","var","video","wbr"]);var Ve=".next-prod";var qU={SCALE:0.7,PAN_POSITION:{x:175,y:100},URL:"http://localhost:3000/",ASPECT_RATIO_LOCKED:!1,DEVICE:"Custom:Custom",THEME:"system",ORIENTATION:"Portrait",MIN_DIMENSIONS:{width:"280px",height:"360px"},COMMANDS:{run:"bun run dev",build:"bun run build",install:"bun install"},IMAGE_FOLDER:"public",IMAGE_DIMENSION:{width:"100px",height:"100px"},FONT_FOLDER:"fonts",FONT_CONFIG:"app/fonts.ts",TAILWIND_CONFIG:"tailwind.config.ts",CHAT_SETTINGS:{showSuggestions:!0,autoApplyCode:!0,expandCodeBlocks:!1,showMiniChat:!0,maxImages:5},EDITOR_SETTINGS:{shouldWarnDelete:!1,enableBunReplace:!0,buildFlags:"--no-lint"}};var Le=["node_modules","dist","build",".git",".next"],VU=[...Le,"static","out",Ve],LU=[...Le,Ve],YU=[...Le,"coverage"],R6=[".jsx",".tsx"],M6=[".js",".ts",".mjs",".cjs"],EU=[...R6,...M6];var SU={["en"]:"English",["ja"]:"日本語",["zh"]:"中文",["ko"]:"한국어"};var U$=qh(We(),1);function N(r){return document.querySelector(`[${"data-odid"}="${r}"]`)}function Ye(r,t=!1){let i=`[${"data-odid"}="${r}"]`;if(!t)return i;return Z6(i)}function Z6(r){return CSS.escape(r)}function Dt(r){return r&&r instanceof Node&&r.nodeType===Node.ELEMENT_NODE&&!D$.includes(r.tagName)&&!r.hasAttribute("data-onlook-ignore")&&r.style.display!=="none"}var C6="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";var O$=(r=21)=>{let t="",i=r|0;while(i--)t+=C6[Math.random()*64|0];return t};function Pr(r){let t=r.getAttribute("data-odid");if(!t)t=`odid-${O$()}`,r.setAttribute("data-odid",t);return t}function Nr(r){return r.getAttribute("data-oid")}function Gr(r){return r.getAttribute("data-oiid")}function I$(r,t){if(!_r)return;_r.onDomProcessed({layerMap:Object.fromEntries(r),rootNode:t}).catch((i)=>{console.error("Failed to send DOM processed event:",i)})}function Ee(r){window._onlookFrameId=r}function Qt(){let r=window._onlookFrameId;if(!r)return console.warn("Frame id not found"),_r?.getFrameId().then((t)=>{Ee(t)}),"";return r}function T6(r=document.body){if(!Qt())return console.warn("frameView id not found, skipping dom processing"),null;let i=zr(r);if(!i)return console.warn("Error building layer tree, root element is null"),null;let o=r.getAttribute("data-odid");if(!o)return console.warn("Root dom id not found"),null;let n=i.get(o);if(!n)return console.warn("Root node not found"),null;return I$(i,n),{rootDomId:o,layerMap:Array.from(i.entries())}}var Ki=U$.default(T6,500),d6=[(r)=>{let t=r.parentElement;return t&&t.tagName.toLowerCase()==="svg"},(r)=>{return r.tagName.toLowerCase()==="next-route-announcer"},(r)=>{return r.tagName.toLowerCase()==="nextjs-portal"}];function zr(r){if(!Dt(r))return null;let t=new Map,i=document.createTreeWalker(r,NodeFilter.SHOW_ELEMENT,{acceptNode:(e)=>{let l=e;if(d6.some((u)=>u(l)))return NodeFilter.FILTER_REJECT;return Dt(l)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}}),o=k$(r);o.children=[],t.set(o.domId,o);let n=i.nextNode();while(n){let e=k$(n);e.children=[];let l=n.parentElement;if(l){let u=l.getAttribute("data-odid");if(u){e.parent=u;let g=t.get(u);if(g&&g.children)g.children.push(e.domId)}}t.set(e.domId,e),n=i.nextNode()}return t}function k$(r){let t=Pr(r),i=Nr(r),o=Gr(r),n=Array.from(r.childNodes).map((g)=>g.nodeType===Node.TEXT_NODE?g.textContent:"").join(" ").trim().slice(0,500),e=window.getComputedStyle(r),l=r.getAttribute("data-ocname");return{domId:t,oid:i||null,instanceId:o||null,textContent:n||"",tagName:r.tagName.toLowerCase(),isVisible:e.visibility!=="hidden",component:l||null,frameId:Qt(),children:null,parent:null,dynamicType:null,coreElementType:null}}function Qe(r){throw new Error(`Expected \`never\`, found: ${JSON.stringify(r)}`)}var J$=(r)=>JSON.parse(JSON.stringify(r));function P$(r){let t=q$(r),i=s6(r),o=rz(r);return{defined:{width:"auto",height:"auto",...i,...o},computed:t}}function X$(r){let t=N(r);if(!t)return{};return q$(t)}function q$(r){return J$(window.getComputedStyle(r))}function s6(r){let t={},i=W$(r.style.cssText);return Object.entries(i).forEach(([o,n])=>{t[o]=n}),t}function rz(r){let t={},i=document.styleSheets;for(let o=0;ot[c]=m)}}catch(u){console.warn("Error",u)}}return t}function W$(r){let t={};return r.split(";").forEach((i)=>{if(i=i.trim(),!i)return;let[o,...n]=i.split(":");t[o?.trim()??""]=n.join(":").trim()}),t}var K$=(r,t)=>{let i=document.elementFromPoint(r,t);if(!i)return;let o=(e)=>{if(e?.shadowRoot){let l=e.shadowRoot.elementFromPoint(r,t);if(l==e)return e;else if(l?.shadowRoot)return o(l);else return l||e}else return e};return o(i)||i},lr=(r,t)=>{let i=r.parentElement,o=i?{domId:i.getAttribute("data-odid"),frameId:Qt(),oid:i.getAttribute("data-oid"),instanceId:i.getAttribute("data-oiid"),rect:i.getBoundingClientRect()}:null,n=r.getBoundingClientRect(),e=t?P$(r):null;return{domId:r.getAttribute("data-odid"),oid:r.getAttribute("data-oid"),frameId:Qt(),instanceId:r.getAttribute("data-oiid"),rect:n,tagName:r.tagName,parent:o,styles:e}};function Vi(r){try{let t=r.getAttribute("data-onlook-drag-saved-style");if(t){let i=JSON.parse(t);for(let o in i)r.style[o]=i[o]}}catch(t){console.warn("Error restoring style",t)}}function V$(r){let t=r.parentElement;if(!t)return;return{type:"index",targetDomId:t.getAttribute("data-odid"),targetOid:Gr(t)||Nr(t)||null,index:Array.from(r.parentElement?.children||[]).indexOf(r),originalIndex:Array.from(r.parentElement?.children||[]).indexOf(r)}}var L$=(r)=>{let t=Array.from(r.childNodes).filter((i)=>i.nodeType===Node.TEXT_NODE).map((i)=>i.textContent);if(t.length===0)return;return t.join("")};var Li=(r,t)=>{let i=N(r)||document.body;return lr(i,t)},Y$=(r,t,i)=>{let o=tz(r,t)||document.body;return lr(o,i)},tz=(r,t)=>{let i=document.elementFromPoint(r,t);if(!i)return;let o=(e)=>{if(e?.shadowRoot){let l=e.shadowRoot.elementFromPoint(r,t);if(l==e)return e;else if(l?.shadowRoot)return o(l);else return l||e}else return e};return o(i)||i},E$=(r,t,i)=>{let o=N(r);if(!o){console.warn("Failed to updateElementInstanceId: Element not found");return}o.setAttribute("data-oiid",t),o.setAttribute("data-ocname",i)},Q$=(r)=>{let t=N(r);if(!t?.parentElement)return null;return lr(t.parentElement,!1)},F$=(r)=>{let t=N(r);if(!t)return 0;return t.children.length},S$=(r)=>{let t=N(r);if(!t)return null;return lr(t.offsetParent,!1)};function N$(r,t,i){let o=N(r.domId);if(!o)return console.warn("Failed to find parent element",r.domId),null;let n=nz(t),e=new Set(i.map((c)=>c.domId)),l=Array.from(o.children).map((c,m)=>({element:c,index:m,domId:Pr(c)})).filter(({domId:c})=>e.has(c));if(l.length===0)return console.warn("No valid children found to group"),null;let u=Math.min(...l.map((c)=>c.index));return o.insertBefore(n,o.children[u]??null),l.forEach(({element:c})=>{let m=c.cloneNode(!0);m.setAttribute("data-onlook-inserted","true"),n.appendChild(m),c.style.display="none",B$(c)}),{domEl:lr(n,!0),newMap:zr(n)}}function G$(r,t){let i=N(r.domId);if(!i)return console.warn(`Parent element not found: ${r.domId}`),null;let o;if(t.domId)o=N(t.domId);else return console.warn("Container domId is required for ungrouping"),null;if(!o)return console.warn("Container element not found for ungrouping"),null;return Array.from(o.children).forEach((l)=>{i.appendChild(l)}),o.remove(),{domEl:lr(i,!0),newMap:zr(i)}}function nz(r){let t=document.createElement(r.tagName);return Object.entries(r.attributes).forEach(([i,o])=>{t.setAttribute(i,o)}),t.setAttribute("data-onlook-inserted","true"),t.setAttribute("data-odid",r.domId),t.setAttribute("data-oid",r.oid),t}function B$(r){r.removeAttribute("data-odid"),r.removeAttribute("data-oid"),r.removeAttribute("data-onlook-inserted");let t=Array.from(r.children);if(t.length===0)return;t.forEach((i)=>{B$(i)})}function Yi(r){let t=N(r);if(!t)return console.warn("Element not found for domId:",r),null;return y$(t)}function y$(r){let t=Array.from(r.attributes).reduce((o,n)=>{return o[n.name]=n.value,o},{}),i=Gr(r)||Nr(r)||null;if(!i)return console.warn("Element has no oid"),null;return{oid:i,domId:Pr(r),tagName:r.tagName.toLowerCase(),children:Array.from(r.children).map((o)=>y$(o)).filter(Boolean),attributes:t,textContent:L$(r)||null,styles:{}}}function A$(r){let t=N(r);if(!t)throw new Error("Element not found for domId: "+r);let i=t.parentElement;if(!i)throw new Error("Inserted element has no parent");let o=Gr(i)||Nr(i);if(!o)return console.warn("Parent element has no oid"),null;let n=Pr(i),e=Array.from(i.children).indexOf(t);if(e===-1)return{type:"append",targetDomId:n,targetOid:o};return{type:"index",targetDomId:n,targetOid:o,index:e,originalIndex:e}}function H$(r){let t=document.querySelector(`[${"data-odid"}="${r}"]`);if(!t)return console.warn("No element found",{domId:r}),{dynamicType:null,coreType:null};let i=t.getAttribute("data-onlook-dynamic-type")||null,o=t.getAttribute("data-onlook-core-element-type")||null;return{dynamicType:i,coreType:o}}function R$(r,t,i){let o=document.querySelector(`[${"data-odid"}="${r}"]`);if(o){if(t)o.setAttribute("data-onlook-dynamic-type",t);if(i)o.setAttribute("data-onlook-core-element-type",i)}}function M$(){let t=document.body.querySelector(`[${"data-oid"}]`);if(t)return lr(t,!0);return null}var Qr=0,w=1,k=2,R=3,S=4,gr=5,Ft=6,er=7,fr=8,P=9,U=10,y=11,q=12,Y=13,dr=14,hr=15,d=16,nr=17,ir=18,mr=19,wr=20,K=21,j=22,Z=23,xr=24,A=25;function cr(r){return r>=48&&r<=57}function Or(r){return cr(r)||r>=65&&r<=70||r>=97&&r<=102}function Fi(r){return r>=65&&r<=90}function iz(r){return r>=97&&r<=122}function oz(r){return Fi(r)||iz(r)}function ez(r){return r>=128}function Qi(r){return oz(r)||ez(r)||r===95}function an(r){return Qi(r)||cr(r)||r===45}function lz(r){return r>=0&&r<=8||r===11||r>=14&&r<=31||r===127}function pn(r){return r===10||r===13||r===12}function Br(r){return pn(r)||r===32||r===9}function Dr(r,t){if(r!==92)return!1;if(pn(t)||t===0)return!1;return!0}function St(r,t,i){if(r===45)return Qi(t)||t===45||Dr(t,i);if(Qi(r))return!0;if(r===92)return Dr(r,t);return!1}function Si(r,t,i){if(r===43||r===45){if(cr(t))return 2;return t===46&&cr(i)?3:0}if(r===46)return cr(t)?2:0;if(cr(r))return 1;return 0}function Ni(r){if(r===65279)return 1;if(r===65534)return 1;return 0}var Fe=new Array(128),cz=128,_n=130,Se=131,Gi=132,Ne=133;for(let r=0;rr.length)return!1;for(let n=t;n=0;t--)if(!Br(r.charCodeAt(t)))break;return t+1}function Dn(r,t){for(;t=55296&&t<=57343||t>1114111)t=65533;return String.fromCodePoint(t)}var Gt=["EOF-token","ident-token","function-token","at-keyword-token","hash-token","string-token","bad-string-token","url-token","bad-url-token","delim-token","number-token","percentage-token","dimension-token","whitespace-token","CDO-token","CDC-token","colon-token","semicolon-token","comma-token","[-token","]-token","(-token",")-token","{-token","}-token","comment-token"];function Bt(r=null,t){if(r===null||r.length0?Ni(t.charCodeAt(0)):0,n=Bt(r.lines,i),e=Bt(r.columns,i),l=r.startLine,u=r.startColumn;for(let g=o;g{}){r=String(r||"");let i=r.length,o=Bt(this.offsetAndType,r.length+1),n=Bt(this.balance,r.length+1),e=0,l=-1,u=0,g=r.length;this.offsetAndType=null,this.balance=null,n.fill(0),t(r,(c,m,v)=>{let h=e++;if(o[h]=c<>Hr]}else if(s$(c))g=h,u=Ot[c]}),o[e]=Qr<e)n[c]=e}this.source=r,this.firstCharOffset=l===-1?0:l,this.tokenCount=e,this.offsetAndType=o,this.balance=n,this.reset(),this.next()}lookupType(r){if(r+=this.tokenIndex,r>Hr;return Qr}lookupTypeNonSC(r){for(let t=this.tokenIndex;t>Hr;if(i!==Y&&i!==A){if(r--===0)return i}}return Qr}lookupOffset(r){if(r+=this.tokenIndex,r>Hr;if(i!==Y&&i!==A){if(r--===0)return t-this.tokenIndex}}return Qr}lookupValue(r,t){if(r+=this.tokenIndex,r0)return r>Hr,this.tokenEnd=t&Ar;else this.tokenIndex=this.tokenCount,this.next()}next(){let r=this.tokenIndex+1;if(r>Hr,this.tokenEnd=r&Ar;else this.eof=!0,this.tokenIndex=this.tokenCount,this.tokenType=Qr,this.tokenStart=this.tokenEnd=this.source.length}skipSC(){while(this.tokenType===Y||this.tokenType===A)this.next()}skipUntilBalanced(r,t){let i=r,o=0,n=0;r:for(;i0?this.offsetAndType[i-1]&Ar:this.firstCharOffset,t(this.source.charCodeAt(n))){case 1:break r;case 2:i++;break r;default:if(s$(this.offsetAndType[i]>>Hr))i=o}}this.skip(i-this.tokenIndex)}forEachToken(r){for(let t=0,i=this.firstCharOffset;t>Hr;i=e,r(l,o,e,t)}}dump(){let r=new Array(this.tokenCount);return this.forEachToken((t,i,o,n)=>{r[n]={idx:n,type:Gt[t],chunk:this.source.substring(i,o),balance:this.balance[n]}}),r}}function bt(r,t){function i(v){return v=r.length){if(cString(f+_+1).padStart(h)+" |"+O).join(` `)}let u=` -`.repeat(Math.max(o-1,0)),g=" ".repeat(Math.max(n-1,0)),c=(u+g+r).split(/\r\n?|\n|\f/),b=Math.max(1,t-e)-1,v=Math.min(t+e,c.length+1),h=Math.max(4,String(v).length)+1,m=0;if(i+=(Z$.length-1)*(c[t-1].substr(0,i-1).match(/\t/g)||[]).length,i>Fe)m=i-M$+3,i=M$-2;for(let w=b;w<=v;w++)if(w>=0&&w0&&c[w].length>m?"…":"")+c[w].substr(m,Fe-2)+(c[w].length>m+Fe-1?"…":"");return[l(b,t),new Array(i+h+2).join("-")+"^",l(t,v)].filter(Boolean).join(` -`).replace(/^(\s+\d+\s+\|\n)+/,"").replace(/\n(\s+\d+\s+\|)+$/,"")}function Ge(r,t,i,o,n,e=1,l=1){return Object.assign(at("SyntaxError",r),{source:t,offset:i,line:o,column:n,sourceFragment(g){return C$({source:t,line:o,column:n,baseLine:e,baseColumn:l},isNaN(g)?0:g)},get formattedMessage(){return`Parse error: ${r} -`+C$({source:t,line:o,column:n,baseLine:e,baseColumn:l},2)}})}function T$(r){let t=this.createList(),i=!1,o={recognizer:r};while(!this.eof){switch(this.tokenType){case A:this.next();continue;case Y:i=!0,this.next();continue}let n=r.getNode.call(this,o);if(n===void 0)break;if(i){if(r.onWhiteSpace)r.onWhiteSpace.call(this,n,t,o);i=!1}t.push(n)}if(i&&r.onWhiteSpace)r.onWhiteSpace.call(this,null,t,o);return t}var d$=()=>{},tz=33,nz=35,Se=59,s$=123,r0=0;function iz(r){return function(){return this[r]()}}function Ne(r){let t=Object.create(null);for(let i of Object.keys(r)){let o=r[i],n=o.parse||o;if(n)t[i]=n}return t}function oz(r){let t={context:Object.create(null),features:Object.assign(Object.create(null),r.features),scope:Object.assign(Object.create(null),r.scope),atrule:Ne(r.atrule),pseudo:Ne(r.pseudo),node:Ne(r.node)};for(let[i,o]of Object.entries(r.parseContext))switch(typeof o){case"function":t.context[i]=o;break;case"string":t.context[i]=iz(o);break}return{config:t,...t,...t.node}}function t0(r){let t="",i="",o=!1,n=d$,e=!1,l=new Ni,u=Object.assign(new Bi,oz(r||{}),{parseAtrulePrelude:!0,parseRulePrelude:!0,parseValue:!0,parseCustomProperty:!1,readSequence:T$,consumeUntilBalanceEnd:()=>0,consumeUntilLeftCurlyBracket(c){return c===s$?1:0},consumeUntilLeftCurlyBracketOrSemicolon(c){return c===s$||c===Se?1:0},consumeUntilExclamationMarkOrSemicolon(c){return c===tz||c===Se?1:0},consumeUntilSemicolonIncluded(c){return c===Se?2:0},createList(){return new or},createSingleNodeList(c){return new or().appendData(c)},getFirstListNode(c){return c&&c.first},getLastListNode(c){return c&&c.last},parseWithFallback(c,b){let v=this.tokenIndex;try{return c.call(this)}catch(h){if(e)throw h;this.skip(v-this.tokenIndex);let m=b.call(this);return e=!0,n(h,m),e=!1,m}},lookupNonWSType(c){let b;do if(b=this.lookupType(c++),b!==Y&&b!==A)return b;while(b!==r0);return r0},charCodeAt(c){return c>=0&&cm.toUpperCase()),v=`${/[[\](){}]/.test(b)?`"${b}"`:b} is expected`,h=this.tokenStart;switch(c){case f:if(this.tokenType===U||this.tokenType===er)h=this.tokenEnd-1,v="Identifier is expected but function found";else v="Identifier is expected";break;case G:if(this.isDelim(nz))this.next(),h++,v="Name is expected";break;case y:if(this.tokenType===k)h=this.tokenEnd,v="Percent sign is expected";break}this.error(v,h)}this.next()},eatIdent(c){if(this.tokenType!==f||this.lookupValue(0,c)===!1)this.error(`Identifier "${c}" is expected`);this.next()},eatDelim(c){if(!this.isDelim(c))this.error(`Delim "${String.fromCharCode(c)}" is expected`);this.next()},getLocation(c,b){if(o)return l.getLocationRange(c,b,i);return null},getLocationFromList(c){if(o){let b=this.getFirstListNode(c),v=this.getLastListNode(c);return l.getLocationRange(b!==null?b.loc.start.offset-l.startOffset:this.tokenStart,v!==null?v.loc.end.offset-l.startOffset:this.tokenStart,i)}return null},error(c,b){let v=typeof b!=="undefined"&&b",o=Boolean(b.positions),n=typeof b.onParseError==="function"?b.onParseError:d$,e=!1,u.parseAtrulePrelude="parseAtrulePrelude"in b?Boolean(b.parseAtrulePrelude):!0,u.parseRulePrelude="parseRulePrelude"in b?Boolean(b.parseRulePrelude):!0,u.parseValue="parseValue"in b?Boolean(b.parseValue):!0,u.parseCustomProperty="parseCustomProperty"in b?Boolean(b.parseCustomProperty):!1;let{context:v="default",onComment:h}=b;if(v in u.context===!1)throw new Error("Unknown context `"+v+"`");if(typeof h==="function")u.forEachToken((w,O,I)=>{if(w===A){let _=u.getLocation(O,I),J=rt(t,I-2,I,"*/")?t.slice(O+2,I-2):t.slice(O+2,I);h(J,_)}});let m=u.context[v].call(u,b);if(!u.eof)u.error();return m},{SyntaxError:Ge,config:u.config})}var Un=u0(),ur=yi(),Hi=$0().ArraySet,Zz=w0().MappingList;function Vr(r){if(!r)r={};this._file=ur.getArg(r,"file",null),this._sourceRoot=ur.getArg(r,"sourceRoot",null),this._skipValidation=ur.getArg(r,"skipValidation",!1),this._ignoreInvalidMapping=ur.getArg(r,"ignoreInvalidMapping",!1),this._sources=new Hi,this._names=new Hi,this._mappings=new Zz,this._sourcesContents=null}Vr.prototype._version=3;Vr.fromSourceMap=function r(t,i){var o=t.sourceRoot,n=new Vr(Object.assign(i||{},{file:t.file,sourceRoot:o}));return t.eachMapping(function(e){var l={generated:{line:e.generatedLine,column:e.generatedColumn}};if(e.source!=null){if(l.source=e.source,o!=null)l.source=ur.relative(o,l.source);if(l.original={line:e.originalLine,column:e.originalColumn},e.name!=null)l.name=e.name}n.addMapping(l)}),t.sources.forEach(function(e){var l=e;if(o!==null)l=ur.relative(o,e);if(!n._sources.has(l))n._sources.add(l);var u=t.sourceContentFor(e);if(u!=null)n.setSourceContent(e,u)}),n};Vr.prototype.addMapping=function r(t){var i=ur.getArg(t,"generated"),o=ur.getArg(t,"original",null),n=ur.getArg(t,"source",null),e=ur.getArg(t,"name",null);if(!this._skipValidation){if(this._validateMapping(i,o,n,e)===!1)return}if(n!=null){if(n=String(n),!this._sources.has(n))this._sources.add(n)}if(e!=null){if(e=String(e),!this._names.has(e))this._names.add(e)}this._mappings.add({generatedLine:i.line,generatedColumn:i.column,originalLine:o!=null&&o.line,originalColumn:o!=null&&o.column,source:n,name:e})};Vr.prototype.setSourceContent=function r(t,i){var o=t;if(this._sourceRoot!=null)o=ur.relative(this._sourceRoot,o);if(i!=null){if(!this._sourcesContents)this._sourcesContents=Object.create(null);this._sourcesContents[ur.toSetString(o)]=i}else if(this._sourcesContents){if(delete this._sourcesContents[ur.toSetString(o)],Object.keys(this._sourcesContents).length===0)this._sourcesContents=null}};Vr.prototype.applySourceMap=function r(t,i,o){var n=i;if(i==null){if(t.file==null)throw new Error(`SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, or the source map's "file" property. Both were omitted.`);n=t.file}var e=this._sourceRoot;if(e!=null)n=ur.relative(e,n);var l=new Hi,u=new Hi;this._mappings.unsortedForEach(function(g){if(g.source===n&&g.originalLine!=null){var c=t.originalPositionFor({line:g.originalLine,column:g.originalColumn});if(c.source!=null){if(g.source=c.source,o!=null)g.source=ur.join(o,g.source);if(e!=null)g.source=ur.relative(e,g.source);if(g.originalLine=c.line,g.originalColumn=c.column,c.name!=null)g.name=c.name}}var b=g.source;if(b!=null&&!l.has(b))l.add(b);var v=g.name;if(v!=null&&!u.has(v))u.add(v)},this),this._sources=l,this._names=u,t.sources.forEach(function(g){var c=t.sourceContentFor(g);if(c!=null){if(o!=null)g=ur.join(o,g);if(e!=null)g=ur.relative(e,g);this.setSourceContent(g,c)}},this)};Vr.prototype._validateMapping=function r(t,i,o,n){if(i&&typeof i.line!=="number"&&typeof i.column!=="number"){var e="original.line and original.column are not numbers -- you probably meant to omit the original mapping entirely and only map the generated position. If so, pass null for the original mapping instead of an object with empty or null values.";if(this._ignoreInvalidMapping){if(typeof console!=="undefined"&&console.warn)console.warn(e);return!1}else throw new Error(e)}if(t&&"line"in t&&"column"in t&&t.line>0&&t.column>=0&&!i&&!o&&!n)return;else if(t&&"line"in t&&"column"in t&&i&&"line"in i&&"column"in i&&t.line>0&&t.column>=0&&i.line>0&&i.column>=0&&o)return;else{var e="Invalid mapping: "+JSON.stringify({generated:t,source:o,original:i,name:n});if(this._ignoreInvalidMapping){if(typeof console!=="undefined"&&console.warn)console.warn(e);return!1}else throw new Error(e)}};Vr.prototype._serializeMappings=function r(){var t=0,i=1,o=0,n=0,e=0,l=0,u="",g,c,b,v,h=this._mappings.toArray();for(var m=0,w=h.length;m0){if(!ur.compareByGeneratedPositionsInflated(c,h[m-1]))continue;g+=","}if(g+=Un.encode(c.generatedColumn-t),t=c.generatedColumn,c.source!=null){if(v=this._sources.indexOf(c.source),g+=Un.encode(v-l),l=v,g+=Un.encode(c.originalLine-1-n),n=c.originalLine-1,g+=Un.encode(c.originalColumn-o),o=c.originalColumn,c.name!=null)b=this._names.indexOf(c.name),g+=Un.encode(b-e),e=b}u+=g}return u};Vr.prototype._generateSourcesContent=function r(t,i){return t.map(function(o){if(!this._sourcesContents)return null;if(i!=null)o=ur.relative(i,o);var n=ur.toSetString(o);return Object.prototype.hasOwnProperty.call(this._sourcesContents,n)?this._sourcesContents[n]:null},this)};Vr.prototype.toJSON=function r(){var t={version:this._version,sources:this._sources.toArray(),names:this._names.toArray(),mappings:this._serializeMappings()};if(this._file!=null)t.file=this._file;if(this._sourceRoot!=null)t.sourceRoot=this._sourceRoot;if(this._sourcesContents)t.sourcesContent=this._generateSourcesContent(t.sources,t.sourceRoot);return t};Vr.prototype.toString=function r(){return JSON.stringify(this.toJSON())};var Re=Vr;var f0=new Set(["Atrule","Selector","Declaration"]);function z0(r){let t=new Re,i={line:1,column:0},o={line:0,column:0},n={line:1,column:0},e={generated:n},l=1,u=0,g=!1,c=r.node;r.node=function(h){if(h.loc&&h.loc.start&&f0.has(h.type)){let m=h.loc.start.line,w=h.loc.start.column-1;if(o.line!==m||o.column!==w){if(o.line=m,o.column=w,i.line=l,i.column=u,g){if(g=!1,i.line!==n.line||i.column!==n.column)t.addMapping(e)}g=!0,t.addMapping({source:h.loc.source,original:o,generated:i})}}if(c.call(this,h),g&&f0.has(h.type))n.line=l,n.column=u};let b=r.emit;r.emit=function(h,m,w){for(let O=0;Osz,safe:()=>Ze});var Cz=43,Tz=45,Me=(r,t)=>{if(r===X)r=t;if(typeof r==="string"){let i=r.charCodeAt(0);return i>127?32768:i<<8}return r},D0=[[f,f],[f,U],[f,er],[f,wr],[f,"-"],[f,k],[f,y],[f,q],[f,hr],[f,K],[R,f],[R,U],[R,er],[R,wr],[R,"-"],[R,k],[R,y],[R,q],[R,hr],[G,f],[G,U],[G,er],[G,wr],[G,"-"],[G,k],[G,y],[G,q],[G,hr],[q,f],[q,U],[q,er],[q,wr],[q,"-"],[q,k],[q,y],[q,q],[q,hr],["#",f],["#",U],["#",er],["#",wr],["#","-"],["#",k],["#",y],["#",q],["#",hr],["-",f],["-",U],["-",er],["-",wr],["-","-"],["-",k],["-",y],["-",q],["-",hr],[k,f],[k,U],[k,er],[k,wr],[k,k],[k,y],[k,q],[k,"%"],[k,hr],["@",f],["@",U],["@",er],["@",wr],["@","-"],["@",hr],[".",k],[".",y],[".",q],["+",k],["+",y],["+",q],["/","*"]],dz=D0.concat([[f,G],[q,G],[G,G],[R,K],[R,gr],[R,d],[y,y],[y,q],[y,U],[y,"-"],[p,f],[p,U],[p,y],[p,q],[p,G],[p,"-"]]);function _0(r){let t=new Set(r.map(([i,o])=>Me(i)<<16|Me(o)));return function(i,o,n){let e=Me(o,n),l=n.charCodeAt(0);if(l===Tz&&o!==f&&o!==U&&o!==hr||l===Cz?t.has(i<<16|l<<8):t.has(i<<16|e))this.emit(" ",Y,!0);return e}}var sz=_0(D0),Ze=_0(dz);var r1=92;function t1(r,t){if(typeof t==="function"){let i=null;r.children.forEach((o)=>{if(i!==null)t.call(this,i);this.node(o),i=o});return}r.children.forEach(this.node,this)}function n1(r){mt(r,(t,i,o)=>{this.token(t,r.slice(i,o))})}function j0(r){let t=new Map;for(let[i,o]of Object.entries(r.node))if(typeof(o.generate||o)==="function")t.set(i,o.generate||o);return function(i,o){let n="",e=0,l={node(g){if(t.has(g.type))t.get(g.type).call(u,g);else throw new Error("Unknown node type: "+g.type)},tokenBefore:Ze,token(g,c){if(e=this.tokenBefore(e,g,c),this.emit(c,g,!1),g===X&&c.charCodeAt(0)===r1)this.emit(` -`,Y,!0)},emit(g){n+=g},result(){return n}};if(o){if(typeof o.decorator==="function")l=o.decorator(l);if(o.sourceMap)l=z0(l);if(o.mode in Ri)l.tokenBefore=Ri[o.mode]}let u={node:(g)=>l.node(g),children:t1,token:(g,c)=>l.token(g,c),tokenize:n1};return l.node(i),l.result()}}function O0(r){return{fromPlainObject(t){return r(t,{enter(i){if(i.children&&i.children instanceof or===!1)i.children=new or().fromArray(i.children)}}),t},toPlainObject(t){return r(t,{leave(i){if(i.children&&i.children instanceof or)i.children=i.children.toArray()}}),t}}}var{hasOwnProperty:Ce}=Object.prototype,kn=function(){};function p0(r){return typeof r==="function"?r:kn}function I0(r,t){return function(i,o,n){if(i.type===t)r.call(this,i,o,n)}}function i1(r,t){let i=t.structure,o=[];for(let n in i){if(Ce.call(i,n)===!1)continue;let e=i[n],l={name:n,type:!1,nullable:!1};if(!Array.isArray(e))e=[e];for(let u of e)if(u===null)l.nullable=!0;else if(typeof u==="string")l.type="node";else if(Array.isArray(u))l.type="list";if(l.type)o.push(l)}if(o.length)return{context:t.walkContext,fields:o};return null}function o1(r){let t={};for(let i in r.node)if(Ce.call(r.node,i)){let o=r.node[i];if(!o.structure)throw new Error("Missed `structure` field in `"+i+"` node type definition");t[i]=i1(i,o)}return t}function a0(r,t){let i=r.fields.slice(),o=r.context,n=typeof o==="string";if(t)i.reverse();return function(e,l,u,g){let c;if(n)c=l[o],l[o]=e;for(let b of i){let v=e[b.name];if(!b.nullable||v){if(b.type==="list"){if(t?v.reduceRight(g,!1):v.reduce(g,!1))return!0}else if(u(v))return!0}}if(n)l[o]=c}}function U0({StyleSheet:r,Atrule:t,Rule:i,Block:o,DeclarationList:n}){return{Atrule:{StyleSheet:r,Atrule:t,Rule:i,Block:o},Rule:{StyleSheet:r,Atrule:t,Rule:i,Block:o},Declaration:{StyleSheet:r,Atrule:t,Rule:i,Block:o,DeclarationList:n}}}function k0(r){let t=o1(r),i={},o={},n=Symbol("break-walk"),e=Symbol("skip-node");for(let c in t)if(Ce.call(t,c)&&t[c]!==null)i[c]=a0(t[c],!1),o[c]=a0(t[c],!0);let l=U0(i),u=U0(o),g=function(c,b){function v(_,J,L){let P=h.call(I,_,J,L);if(P===n)return!0;if(P===e)return!1;if(w.hasOwnProperty(_.type)){if(w[_.type](_,I,v,O))return!0}if(m.call(I,_,J,L)===n)return!0;return!1}let h=kn,m=kn,w=i,O=(_,J,L,P)=>_||v(J,L,P),I={break:n,skip:e,root:c,stylesheet:null,atrule:null,atrulePrelude:null,rule:null,selector:null,block:null,declaration:null,function:null};if(typeof b==="function")h=b;else if(b){if(h=p0(b.enter),m=p0(b.leave),b.reverse)w=o;if(b.visit){if(l.hasOwnProperty(b.visit))w=b.reverse?u[b.visit]:l[b.visit];else if(!t.hasOwnProperty(b.visit))throw new Error("Bad value `"+b.visit+"` for `visit` option (should be: "+Object.keys(t).sort().join(", ")+")");h=I0(h,b.visit),m=I0(m,b.visit)}}if(h===kn&&m===kn)throw new Error("Neither `enter` nor `leave` walker handler is set or both aren't a function");v(c)};return g.break=n,g.skip=e,g.find=function(c,b){let v=null;return g(c,function(h,m,w){if(b.call(this,h,m,w))return v=h,n}),v},g.findLast=function(c,b){let v=null;return g(c,{reverse:!0,enter(h,m,w){if(b.call(this,h,m,w))return v=h,n}}),v},g.findAll=function(c,b){let v=[];return g(c,function(h,m,w){if(b.call(this,h,m,w))v.push(h)}),v},g}function e1(r){return r}function l1(r){let{min:t,max:i,comma:o}=r;if(t===0&&i===0)return o?"#?":"*";if(t===0&&i===1)return"?";if(t===1&&i===0)return o?"#":"+";if(t===1&&i===1)return"";return(o?"#":"")+(t===i?"{"+t+"}":"{"+t+","+(i!==0?i:"")+"}")}function c1(r){switch(r.type){case"Range":return" ["+(r.min===null?"-∞":r.min)+","+(r.max===null?"∞":r.max)+"]";default:throw new Error("Unknown node type `"+r.type+"`")}}function u1(r,t,i,o){let n=r.combinator===" "||o?r.combinator:" "+r.combinator+" ",e=r.terms.map((l)=>Mi(l,t,i,o)).join(n);if(r.explicit||i)return(o||e[0]===","?"[":"[ ")+e+(o?"]":" ]");return e}function Mi(r,t,i,o){let n;switch(r.type){case"Group":n=u1(r,t,i,o)+(r.disallowEmpty?"!":"");break;case"Multiplier":return Mi(r.term,t,i,o)+t(l1(r),r);case"Boolean":n="";break;case"Type":n="<"+r.name+(r.opts?t(c1(r.opts),r.opts):"")+">";break;case"Property":n="<'"+r.name+"'>";break;case"Keyword":n=r.name;break;case"AtKeyword":n="@"+r.name;break;case"Function":n=r.name+"(";break;case"String":case"Token":n=r.value;break;case"Comma":n=",";break;default:throw new Error("Unknown node type `"+r.type+"`")}return t(n,r)}function Ht(r,t){let i=e1,o=!1,n=!1;if(typeof t==="function")i=t;else if(t){if(o=Boolean(t.forceBraces),n=Boolean(t.compact),typeof t.decorate==="function")i=t.decorate}return Mi(r,i,o,n)}var J0={offset:0,line:1,column:1};function g1(r,t){let{tokens:i,longestMatch:o}=r,n=o1)b=Zi(e||t,"end")||Jn(J0,c),v=Jn(b);else b=Zi(e,"start")||Jn(Zi(t,"start")||J0,c.slice(0,l)),v=Zi(e,"end")||Jn(b,c.substr(l,u));return{css:c,mismatchOffset:l,mismatchLength:u,start:b,end:v}}function Zi(r,t){let i=r&&r.loc&&r.loc[t];if(i)return"line"in i?Jn(i):i;return null}function Jn({offset:r,line:t,column:i},o){let n={offset:r,line:t,column:i};if(o){let e=o.split(/\n|\r\n?|\f/);n.offset+=o.length,n.line+=e.length-1,n.column=e.length===1?n.column+o.length:e.pop().length+1}return n}var Rt=function(r,t){let i=at("SyntaxReferenceError",r+(t?" `"+t+"`":""));return i.reference=t,i},X0=function(r,t,i,o){let n=at("SyntaxMatchError",r),{css:e,mismatchOffset:l,mismatchLength:u,start:g,end:c}=g1(o,i);return n.rawMessage=r,n.syntax=t?Ht(t):"",n.css=e,n.mismatchOffset=l,n.mismatchLength=u,n.message=r+` +`.repeat(Math.max(o-1,0)),g=" ".repeat(Math.max(n-1,0)),c=(u+g+r).split(/\r\n?|\n|\f/),m=Math.max(1,t-e)-1,v=Math.min(t+e,c.length+1),h=Math.max(4,String(v).length)+1,b=0;if(i+=(t0.length-1)*(c[t-1].substr(0,i-1).match(/\t/g)||[]).length,i>Be)b=i-r0+3,i=r0-2;for(let f=m;f<=v;f++)if(f>=0&&f0&&c[f].length>b?"…":"")+c[f].substr(b,Be-2)+(c[f].length>b+Be-1?"…":"");return[l(m,t),new Array(i+h+2).join("-")+"^",l(t,v)].filter(Boolean).join(` +`).replace(/^(\s+\d+\s+\|\n)+/,"").replace(/\n(\s+\d+\s+\|)+$/,"")}function ye(r,t,i,o,n,e=1,l=1){return Object.assign(It("SyntaxError",r),{source:t,offset:i,line:o,column:n,sourceFragment(g){return n0({source:t,line:o,column:n,baseLine:e,baseColumn:l},isNaN(g)?0:g)},get formattedMessage(){return`Parse error: ${r} +`+n0({source:t,line:o,column:n,baseLine:e,baseColumn:l},2)}})}function i0(r){let t=this.createList(),i=!1,o={recognizer:r};while(!this.eof){switch(this.tokenType){case A:this.next();continue;case Y:i=!0,this.next();continue}let n=r.getNode.call(this,o);if(n===void 0)break;if(i){if(r.onWhiteSpace)r.onWhiteSpace.call(this,n,t,o);i=!1}t.push(n)}if(i&&r.onWhiteSpace)r.onWhiteSpace.call(this,null,t,o);return t}var o0=()=>{},gz=33,mz=35,Ae=59,e0=123,l0=0;function bz(r){return function(){return this[r]()}}function He(r){let t=Object.create(null);for(let i of Object.keys(r)){let o=r[i],n=o.parse||o;if(n)t[i]=n}return t}function vz(r){let t={context:Object.create(null),features:Object.assign(Object.create(null),r.features),scope:Object.assign(Object.create(null),r.scope),atrule:He(r.atrule),pseudo:He(r.pseudo),node:He(r.node)};for(let[i,o]of Object.entries(r.parseContext))switch(typeof o){case"function":t.context[i]=o;break;case"string":t.context[i]=bz(o);break}return{config:t,...t,...t.node}}function c0(r){let t="",i="",o=!1,n=o0,e=!1,l=new Hi,u=Object.assign(new Ri,vz(r||{}),{parseAtrulePrelude:!0,parseRulePrelude:!0,parseValue:!0,parseCustomProperty:!1,readSequence:i0,consumeUntilBalanceEnd:()=>0,consumeUntilLeftCurlyBracket(c){return c===e0?1:0},consumeUntilLeftCurlyBracketOrSemicolon(c){return c===e0||c===Ae?1:0},consumeUntilExclamationMarkOrSemicolon(c){return c===gz||c===Ae?1:0},consumeUntilSemicolonIncluded(c){return c===Ae?2:0},createList(){return new or},createSingleNodeList(c){return new or().appendData(c)},getFirstListNode(c){return c&&c.first},getLastListNode(c){return c&&c.last},parseWithFallback(c,m){let v=this.tokenIndex;try{return c.call(this)}catch(h){if(e)throw h;this.skip(v-this.tokenIndex);let b=m.call(this);return e=!0,n(h,b),e=!1,b}},lookupNonWSType(c){let m;do if(m=this.lookupType(c++),m!==Y&&m!==A)return m;while(m!==l0);return l0},charCodeAt(c){return c>=0&&cb.toUpperCase()),v=`${/[[\](){}]/.test(m)?`"${m}"`:m} is expected`,h=this.tokenStart;switch(c){case w:if(this.tokenType===k||this.tokenType===er)h=this.tokenEnd-1,v="Identifier is expected but function found";else v="Identifier is expected";break;case S:if(this.isDelim(mz))this.next(),h++,v="Name is expected";break;case y:if(this.tokenType===U)h=this.tokenEnd,v="Percent sign is expected";break}this.error(v,h)}this.next()},eatIdent(c){if(this.tokenType!==w||this.lookupValue(0,c)===!1)this.error(`Identifier "${c}" is expected`);this.next()},eatDelim(c){if(!this.isDelim(c))this.error(`Delim "${String.fromCharCode(c)}" is expected`);this.next()},getLocation(c,m){if(o)return l.getLocationRange(c,m,i);return null},getLocationFromList(c){if(o){let m=this.getFirstListNode(c),v=this.getLastListNode(c);return l.getLocationRange(m!==null?m.loc.start.offset-l.startOffset:this.tokenStart,v!==null?v.loc.end.offset-l.startOffset:this.tokenStart,i)}return null},error(c,m){let v=typeof m!=="undefined"&&m",o=Boolean(m.positions),n=typeof m.onParseError==="function"?m.onParseError:o0,e=!1,u.parseAtrulePrelude="parseAtrulePrelude"in m?Boolean(m.parseAtrulePrelude):!0,u.parseRulePrelude="parseRulePrelude"in m?Boolean(m.parseRulePrelude):!0,u.parseValue="parseValue"in m?Boolean(m.parseValue):!0,u.parseCustomProperty="parseCustomProperty"in m?Boolean(m.parseCustomProperty):!1;let{context:v="default",onComment:h}=m;if(v in u.context===!1)throw new Error("Unknown context `"+v+"`");if(typeof h==="function")u.forEachToken((f,D,O)=>{if(f===A){let _=u.getLocation(D,O),J=rt(t,O-2,O,"*/")?t.slice(D+2,O-2):t.slice(D+2,O);h(J,_)}});let b=u.context[v].call(u,m);if(!u.eof)u.error();return b},{SyntaxError:ye,config:u.config})}var kn=$0(),ur=Mi(),Ci=p0().ArraySet,ia=D0().MappingList;function Vr(r){if(!r)r={};this._file=ur.getArg(r,"file",null),this._sourceRoot=ur.getArg(r,"sourceRoot",null),this._skipValidation=ur.getArg(r,"skipValidation",!1),this._ignoreInvalidMapping=ur.getArg(r,"ignoreInvalidMapping",!1),this._sources=new Ci,this._names=new Ci,this._mappings=new ia,this._sourcesContents=null}Vr.prototype._version=3;Vr.fromSourceMap=function r(t,i){var o=t.sourceRoot,n=new Vr(Object.assign(i||{},{file:t.file,sourceRoot:o}));return t.eachMapping(function(e){var l={generated:{line:e.generatedLine,column:e.generatedColumn}};if(e.source!=null){if(l.source=e.source,o!=null)l.source=ur.relative(o,l.source);if(l.original={line:e.originalLine,column:e.originalColumn},e.name!=null)l.name=e.name}n.addMapping(l)}),t.sources.forEach(function(e){var l=e;if(o!==null)l=ur.relative(o,e);if(!n._sources.has(l))n._sources.add(l);var u=t.sourceContentFor(e);if(u!=null)n.setSourceContent(e,u)}),n};Vr.prototype.addMapping=function r(t){var i=ur.getArg(t,"generated"),o=ur.getArg(t,"original",null),n=ur.getArg(t,"source",null),e=ur.getArg(t,"name",null);if(!this._skipValidation){if(this._validateMapping(i,o,n,e)===!1)return}if(n!=null){if(n=String(n),!this._sources.has(n))this._sources.add(n)}if(e!=null){if(e=String(e),!this._names.has(e))this._names.add(e)}this._mappings.add({generatedLine:i.line,generatedColumn:i.column,originalLine:o!=null&&o.line,originalColumn:o!=null&&o.column,source:n,name:e})};Vr.prototype.setSourceContent=function r(t,i){var o=t;if(this._sourceRoot!=null)o=ur.relative(this._sourceRoot,o);if(i!=null){if(!this._sourcesContents)this._sourcesContents=Object.create(null);this._sourcesContents[ur.toSetString(o)]=i}else if(this._sourcesContents){if(delete this._sourcesContents[ur.toSetString(o)],Object.keys(this._sourcesContents).length===0)this._sourcesContents=null}};Vr.prototype.applySourceMap=function r(t,i,o){var n=i;if(i==null){if(t.file==null)throw new Error(`SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, or the source map's "file" property. Both were omitted.`);n=t.file}var e=this._sourceRoot;if(e!=null)n=ur.relative(e,n);var l=new Ci,u=new Ci;this._mappings.unsortedForEach(function(g){if(g.source===n&&g.originalLine!=null){var c=t.originalPositionFor({line:g.originalLine,column:g.originalColumn});if(c.source!=null){if(g.source=c.source,o!=null)g.source=ur.join(o,g.source);if(e!=null)g.source=ur.relative(e,g.source);if(g.originalLine=c.line,g.originalColumn=c.column,c.name!=null)g.name=c.name}}var m=g.source;if(m!=null&&!l.has(m))l.add(m);var v=g.name;if(v!=null&&!u.has(v))u.add(v)},this),this._sources=l,this._names=u,t.sources.forEach(function(g){var c=t.sourceContentFor(g);if(c!=null){if(o!=null)g=ur.join(o,g);if(e!=null)g=ur.relative(e,g);this.setSourceContent(g,c)}},this)};Vr.prototype._validateMapping=function r(t,i,o,n){if(i&&typeof i.line!=="number"&&typeof i.column!=="number"){var e="original.line and original.column are not numbers -- you probably meant to omit the original mapping entirely and only map the generated position. If so, pass null for the original mapping instead of an object with empty or null values.";if(this._ignoreInvalidMapping){if(typeof console!=="undefined"&&console.warn)console.warn(e);return!1}else throw new Error(e)}if(t&&"line"in t&&"column"in t&&t.line>0&&t.column>=0&&!i&&!o&&!n)return;else if(t&&"line"in t&&"column"in t&&i&&"line"in i&&"column"in i&&t.line>0&&t.column>=0&&i.line>0&&i.column>=0&&o)return;else{var e="Invalid mapping: "+JSON.stringify({generated:t,source:o,original:i,name:n});if(this._ignoreInvalidMapping){if(typeof console!=="undefined"&&console.warn)console.warn(e);return!1}else throw new Error(e)}};Vr.prototype._serializeMappings=function r(){var t=0,i=1,o=0,n=0,e=0,l=0,u="",g,c,m,v,h=this._mappings.toArray();for(var b=0,f=h.length;b0){if(!ur.compareByGeneratedPositionsInflated(c,h[b-1]))continue;g+=","}if(g+=kn.encode(c.generatedColumn-t),t=c.generatedColumn,c.source!=null){if(v=this._sources.indexOf(c.source),g+=kn.encode(v-l),l=v,g+=kn.encode(c.originalLine-1-n),n=c.originalLine-1,g+=kn.encode(c.originalColumn-o),o=c.originalColumn,c.name!=null)m=this._names.indexOf(c.name),g+=kn.encode(m-e),e=m}u+=g}return u};Vr.prototype._generateSourcesContent=function r(t,i){return t.map(function(o){if(!this._sourcesContents)return null;if(i!=null)o=ur.relative(i,o);var n=ur.toSetString(o);return Object.prototype.hasOwnProperty.call(this._sourcesContents,n)?this._sourcesContents[n]:null},this)};Vr.prototype.toJSON=function r(){var t={version:this._version,sources:this._sources.toArray(),names:this._names.toArray(),mappings:this._serializeMappings()};if(this._file!=null)t.file=this._file;if(this._sourceRoot!=null)t.sourceRoot=this._sourceRoot;if(this._sourcesContents)t.sourcesContent=this._generateSourcesContent(t.sources,t.sourceRoot);return t};Vr.prototype.toString=function r(){return JSON.stringify(this.toJSON())};var Te=Vr;var j0=new Set(["Atrule","Selector","Declaration"]);function O0(r){let t=new Te,i={line:1,column:0},o={line:0,column:0},n={line:1,column:0},e={generated:n},l=1,u=0,g=!1,c=r.node;r.node=function(h){if(h.loc&&h.loc.start&&j0.has(h.type)){let b=h.loc.start.line,f=h.loc.start.column-1;if(o.line!==b||o.column!==f){if(o.line=b,o.column=f,i.line=l,i.column=u,g){if(g=!1,i.line!==n.line||i.column!==n.column)t.addMapping(e)}g=!0,t.addMapping({source:h.loc.source,original:o,generated:i})}}if(c.call(this,h),g&&j0.has(h.type))n.line=l,n.column=u};let m=r.emit;r.emit=function(h,b,f){for(let D=0;Dca,safe:()=>se});var oa=43,ea=45,de=(r,t)=>{if(r===P)r=t;if(typeof r==="string"){let i=r.charCodeAt(0);return i>127?32768:i<<8}return r},I0=[[w,w],[w,k],[w,er],[w,fr],[w,"-"],[w,U],[w,y],[w,q],[w,hr],[w,K],[R,w],[R,k],[R,er],[R,fr],[R,"-"],[R,U],[R,y],[R,q],[R,hr],[S,w],[S,k],[S,er],[S,fr],[S,"-"],[S,U],[S,y],[S,q],[S,hr],[q,w],[q,k],[q,er],[q,fr],[q,"-"],[q,U],[q,y],[q,q],[q,hr],["#",w],["#",k],["#",er],["#",fr],["#","-"],["#",U],["#",y],["#",q],["#",hr],["-",w],["-",k],["-",er],["-",fr],["-","-"],["-",U],["-",y],["-",q],["-",hr],[U,w],[U,k],[U,er],[U,fr],[U,U],[U,y],[U,q],[U,"%"],[U,hr],["@",w],["@",k],["@",er],["@",fr],["@","-"],["@",hr],[".",U],[".",y],[".",q],["+",U],["+",y],["+",q],["/","*"]],la=I0.concat([[w,S],[q,S],[S,S],[R,K],[R,gr],[R,d],[y,y],[y,q],[y,k],[y,"-"],[j,w],[j,k],[j,y],[j,q],[j,S],[j,"-"]]);function k0(r){let t=new Set(r.map(([i,o])=>de(i)<<16|de(o)));return function(i,o,n){let e=de(o,n),l=n.charCodeAt(0);if(l===ea&&o!==w&&o!==k&&o!==hr||l===oa?t.has(i<<16|l<<8):t.has(i<<16|e))this.emit(" ",Y,!0);return e}}var ca=k0(I0),se=k0(la);var ua=92;function ga(r,t){if(typeof t==="function"){let i=null;r.children.forEach((o)=>{if(i!==null)t.call(this,i);this.node(o),i=o});return}r.children.forEach(this.node,this)}function ma(r){bt(r,(t,i,o)=>{this.token(t,r.slice(i,o))})}function U0(r){let t=new Map;for(let[i,o]of Object.entries(r.node))if(typeof(o.generate||o)==="function")t.set(i,o.generate||o);return function(i,o){let n="",e=0,l={node(g){if(t.has(g.type))t.get(g.type).call(u,g);else throw new Error("Unknown node type: "+g.type)},tokenBefore:se,token(g,c){if(e=this.tokenBefore(e,g,c),this.emit(c,g,!1),g===P&&c.charCodeAt(0)===ua)this.emit(` +`,Y,!0)},emit(g){n+=g},result(){return n}};if(o){if(typeof o.decorator==="function")l=o.decorator(l);if(o.sourceMap)l=O0(l);if(o.mode in Ti)l.tokenBefore=Ti[o.mode]}let u={node:(g)=>l.node(g),children:ga,token:(g,c)=>l.token(g,c),tokenize:ma};return l.node(i),l.result()}}function J0(r){return{fromPlainObject(t){return r(t,{enter(i){if(i.children&&i.children instanceof or===!1)i.children=new or().fromArray(i.children)}}),t},toPlainObject(t){return r(t,{leave(i){if(i.children&&i.children instanceof or)i.children=i.children.toArray()}}),t}}}var{hasOwnProperty:rl}=Object.prototype,Un=function(){};function P0(r){return typeof r==="function"?r:Un}function X0(r,t){return function(i,o,n){if(i.type===t)r.call(this,i,o,n)}}function ba(r,t){let i=t.structure,o=[];for(let n in i){if(rl.call(i,n)===!1)continue;let e=i[n],l={name:n,type:!1,nullable:!1};if(!Array.isArray(e))e=[e];for(let u of e)if(u===null)l.nullable=!0;else if(typeof u==="string")l.type="node";else if(Array.isArray(u))l.type="list";if(l.type)o.push(l)}if(o.length)return{context:t.walkContext,fields:o};return null}function va(r){let t={};for(let i in r.node)if(rl.call(r.node,i)){let o=r.node[i];if(!o.structure)throw new Error("Missed `structure` field in `"+i+"` node type definition");t[i]=ba(i,o)}return t}function q0(r,t){let i=r.fields.slice(),o=r.context,n=typeof o==="string";if(t)i.reverse();return function(e,l,u,g){let c;if(n)c=l[o],l[o]=e;for(let m of i){let v=e[m.name];if(!m.nullable||v){if(m.type==="list"){if(t?v.reduceRight(g,!1):v.reduce(g,!1))return!0}else if(u(v))return!0}}if(n)l[o]=c}}function W0({StyleSheet:r,Atrule:t,Rule:i,Block:o,DeclarationList:n}){return{Atrule:{StyleSheet:r,Atrule:t,Rule:i,Block:o},Rule:{StyleSheet:r,Atrule:t,Rule:i,Block:o},Declaration:{StyleSheet:r,Atrule:t,Rule:i,Block:o,DeclarationList:n}}}function K0(r){let t=va(r),i={},o={},n=Symbol("break-walk"),e=Symbol("skip-node");for(let c in t)if(rl.call(t,c)&&t[c]!==null)i[c]=q0(t[c],!1),o[c]=q0(t[c],!0);let l=W0(i),u=W0(o),g=function(c,m){function v(_,J,L){let X=h.call(O,_,J,L);if(X===n)return!0;if(X===e)return!1;if(f.hasOwnProperty(_.type)){if(f[_.type](_,O,v,D))return!0}if(b.call(O,_,J,L)===n)return!0;return!1}let h=Un,b=Un,f=i,D=(_,J,L,X)=>_||v(J,L,X),O={break:n,skip:e,root:c,stylesheet:null,atrule:null,atrulePrelude:null,rule:null,selector:null,block:null,declaration:null,function:null};if(typeof m==="function")h=m;else if(m){if(h=P0(m.enter),b=P0(m.leave),m.reverse)f=o;if(m.visit){if(l.hasOwnProperty(m.visit))f=m.reverse?u[m.visit]:l[m.visit];else if(!t.hasOwnProperty(m.visit))throw new Error("Bad value `"+m.visit+"` for `visit` option (should be: "+Object.keys(t).sort().join(", ")+")");h=X0(h,m.visit),b=X0(b,m.visit)}}if(h===Un&&b===Un)throw new Error("Neither `enter` nor `leave` walker handler is set or both aren't a function");v(c)};return g.break=n,g.skip=e,g.find=function(c,m){let v=null;return g(c,function(h,b,f){if(m.call(this,h,b,f))return v=h,n}),v},g.findLast=function(c,m){let v=null;return g(c,{reverse:!0,enter(h,b,f){if(m.call(this,h,b,f))return v=h,n}}),v},g.findAll=function(c,m){let v=[];return g(c,function(h,b,f){if(m.call(this,h,b,f))v.push(h)}),v},g}function ha(r){return r}function $a(r){let{min:t,max:i,comma:o}=r;if(t===0&&i===0)return o?"#?":"*";if(t===0&&i===1)return"?";if(t===1&&i===0)return o?"#":"+";if(t===1&&i===1)return"";return(o?"#":"")+(t===i?"{"+t+"}":"{"+t+","+(i!==0?i:"")+"}")}function xa(r){switch(r.type){case"Range":return" ["+(r.min===null?"-∞":r.min)+","+(r.max===null?"∞":r.max)+"]";default:throw new Error("Unknown node type `"+r.type+"`")}}function fa(r,t,i,o){let n=r.combinator===" "||o?r.combinator:" "+r.combinator+" ",e=r.terms.map((l)=>di(l,t,i,o)).join(n);if(r.explicit||i)return(o||e[0]===","?"[":"[ ")+e+(o?"]":" ]");return e}function di(r,t,i,o){let n;switch(r.type){case"Group":n=fa(r,t,i,o)+(r.disallowEmpty?"!":"");break;case"Multiplier":return di(r.term,t,i,o)+t($a(r),r);case"Boolean":n="";break;case"Type":n="<"+r.name+(r.opts?t(xa(r.opts),r.opts):"")+">";break;case"Property":n="<'"+r.name+"'>";break;case"Keyword":n=r.name;break;case"AtKeyword":n="@"+r.name;break;case"Function":n=r.name+"(";break;case"String":case"Token":n=r.value;break;case"Comma":n=",";break;default:throw new Error("Unknown node type `"+r.type+"`")}return t(n,r)}function Ht(r,t){let i=ha,o=!1,n=!1;if(typeof t==="function")i=t;else if(t){if(o=Boolean(t.forceBraces),n=Boolean(t.compact),typeof t.decorate==="function")i=t.decorate}return di(r,i,o,n)}var V0={offset:0,line:1,column:1};function wa(r,t){let{tokens:i,longestMatch:o}=r,n=o1)m=si(e||t,"end")||Jn(V0,c),v=Jn(m);else m=si(e,"start")||Jn(si(t,"start")||V0,c.slice(0,l)),v=si(e,"end")||Jn(m,c.substr(l,u));return{css:c,mismatchOffset:l,mismatchLength:u,start:m,end:v}}function si(r,t){let i=r&&r.loc&&r.loc[t];if(i)return"line"in i?Jn(i):i;return null}function Jn({offset:r,line:t,column:i},o){let n={offset:r,line:t,column:i};if(o){let e=o.split(/\n|\r\n?|\f/);n.offset+=o.length,n.line+=e.length-1,n.column=e.length===1?n.column+o.length:e.pop().length+1}return n}var Rt=function(r,t){let i=It("SyntaxReferenceError",r+(t?" `"+t+"`":""));return i.reference=t,i},L0=function(r,t,i,o){let n=It("SyntaxMatchError",r),{css:e,mismatchOffset:l,mismatchLength:u,start:g,end:c}=wa(o,i);return n.rawMessage=r,n.syntax=t?Ht(t):"",n.css=e,n.mismatchOffset=l,n.mismatchLength=u,n.message=r+` syntax: `+n.syntax+` value: `+(e||"")+` - --------`+new Array(n.mismatchOffset+1).join("-")+"^",Object.assign(n,g),n.loc={source:i&&i.loc&&i.loc.source||"",start:g,end:c},n};var Ci=new Map,Mt=new Map;var Ti=b1,Te=m1;function di(r,t){return t=t||0,r.length-t>=2&&r.charCodeAt(t)===45&&r.charCodeAt(t+1)===45}function P0(r,t){if(t=t||0,r.length-t>=3){if(r.charCodeAt(t)===45&&r.charCodeAt(t+1)!==45){let i=r.indexOf("-",t+2);if(i!==-1)return r.substring(t,i+1)}}return""}function b1(r){if(Ci.has(r))return Ci.get(r);let t=r.toLowerCase(),i=Ci.get(t);if(i===void 0){let o=di(t,0),n=!o?P0(t,0):"";i=Object.freeze({basename:t.substr(n.length),name:t,prefix:n,vendor:n,custom:o})}return Ci.set(r,i),i}function m1(r){if(Mt.has(r))return Mt.get(r);let t=r,i=r[0];if(i==="/")i=r[1]==="/"?"//":"/";else if(i!=="_"&&i!=="*"&&i!=="$"&&i!=="#"&&i!=="+"&&i!=="&")i="";let o=di(t,i.length);if(!o){if(t=t.toLowerCase(),Mt.has(t)){let u=Mt.get(t);return Mt.set(r,u),u}}let n=!o?P0(t,i.length):"",e=t.substr(0,i.length+n.length),l=Object.freeze({basename:t.substr(e.length),name:t.substr(i.length),hack:i,vendor:n,prefix:e,custom:o});return Mt.set(r,l),l}var Zt=["initial","inherit","unset","revert","revert-layer"];var Pn=43,Rr=45,de=110,Ct=!0,h1=!1;function rl(r,t){return r!==null&&r.type===X&&r.value.charCodeAt(0)===t}function Xn(r,t,i){while(r!==null&&(r.type===Y||r.type===A))r=i(++t);return t}function vt(r,t,i,o){if(!r)return 0;let n=r.value.charCodeAt(t);if(n===Pn||n===Rr){if(i)return 0;t++}for(;t6)return 0}return o}function si(r,t,i){if(!r)return 0;while(nl(i(t),W0)){if(++r>6)return 0;t++}return t}function il(r,t){let i=0;if(r===null||r.type!==f||!sr(r.value,0,x1))return 0;if(r=t(++i),r===null)return 0;if(nl(r,$1)){if(r=t(++i),r===null)return 0;if(r.type===f)return si(qn(r,0,!0),++i,t);if(nl(r,W0))return si(1,++i,t);return 0}if(r.type===k){let o=qn(r,1,!0);if(o===0)return 0;if(r=t(++i),r===null)return i;if(r.type===q||r.type===k){if(!w1(r,q0)||!qn(r,1,!1))return 0;return i+1}return si(o,i,t)}if(r.type===q)return si(qn(r,1,!0),++i,t);return 0}var f1=["calc(","-moz-calc(","-webkit-calc("],ol=new Map([[U,p],[K,p],[br,fr],[Z,xr]]);function Fr(r,t){return tr.max&&typeof r.max!=="string")return!0}return!1}function z1(r,t){let i=0,o=[],n=0;r:do{switch(r.type){case xr:case p:case fr:if(r.type!==i)break r;if(i=o.pop(),o.length===0){n++;break r}break;case U:case K:case br:case Z:o.push(i),i=ol.get(r.type);break}n++}while(r=t(n));return n}function Lr(r){return function(t,i,o){if(t===null)return 0;if(t.type===U&&V0(t.value,f1))return z1(t,i);return r(t,i,o)}}function C(r){return function(t){if(t===null||t.type!==r)return 0;return 1}}function D1(r){if(r===null||r.type!==f)return 0;let t=r.value.toLowerCase();if(V0(t,Zt))return 0;if(K0(t,"default"))return 0;return 1}function Y0(r){if(r===null||r.type!==f)return 0;if(Fr(r.value,0)!==45||Fr(r.value,1)!==45)return 0;return 1}function _1(r){if(!Y0(r))return 0;if(r.value==="--")return 0;return 1}function j1(r){if(r===null||r.type!==G)return 0;let t=r.value.length;if(t!==4&&t!==5&&t!==7&&t!==9)return 0;for(let i=1;iK1,semitones:()=>Q1,resolution:()=>L1,length:()=>q1,frequency:()=>V1,flex:()=>Y1,decibel:()=>E1,angle:()=>W1});var q1=["cm","mm","q","in","pt","pc","px","em","rem","ex","rex","cap","rcap","ch","rch","ic","ric","lh","rlh","vw","svw","lvw","dvw","vh","svh","lvh","dvh","vi","svi","lvi","dvi","vb","svb","lvb","dvb","vmin","svmin","lvmin","dvmin","vmax","svmax","lvmax","dvmax","cqw","cqh","cqi","cqb","cqmin","cqmax"],W1=["deg","grad","rad","turn"],K1=["s","ms"],V1=["hz","khz"],L1=["dpi","dpcm","dppx","x"],Y1=["fr"],E1=["db"],Q1=["st"];function el(r,t,i){return Object.assign(at("SyntaxError",r),{input:t,offset:i,rawMessage:r,message:r+` + --------`+new Array(n.mismatchOffset+1).join("-")+"^",Object.assign(n,g),n.loc={source:i&&i.loc&&i.loc.source||"",start:g,end:c},n};var ro=new Map,Mt=new Map;var to=za,tl=aa;function no(r,t){return t=t||0,r.length-t>=2&&r.charCodeAt(t)===45&&r.charCodeAt(t+1)===45}function Y0(r,t){if(t=t||0,r.length-t>=3){if(r.charCodeAt(t)===45&&r.charCodeAt(t+1)!==45){let i=r.indexOf("-",t+2);if(i!==-1)return r.substring(t,i+1)}}return""}function za(r){if(ro.has(r))return ro.get(r);let t=r.toLowerCase(),i=ro.get(t);if(i===void 0){let o=no(t,0),n=!o?Y0(t,0):"";i=Object.freeze({basename:t.substr(n.length),name:t,prefix:n,vendor:n,custom:o})}return ro.set(r,i),i}function aa(r){if(Mt.has(r))return Mt.get(r);let t=r,i=r[0];if(i==="/")i=r[1]==="/"?"//":"/";else if(i!=="_"&&i!=="*"&&i!=="$"&&i!=="#"&&i!=="+"&&i!=="&")i="";let o=no(t,i.length);if(!o){if(t=t.toLowerCase(),Mt.has(t)){let u=Mt.get(t);return Mt.set(r,u),u}}let n=!o?Y0(t,i.length):"",e=t.substr(0,i.length+n.length),l=Object.freeze({basename:t.substr(e.length),name:t.substr(i.length),hack:i,vendor:n,prefix:e,custom:o});return Mt.set(r,l),l}var Zt=["initial","inherit","unset","revert","revert-layer"];var Xn=43,Rr=45,nl=110,Ct=!0,_a=!1;function ol(r,t){return r!==null&&r.type===P&&r.value.charCodeAt(0)===t}function Pn(r,t,i){while(r!==null&&(r.type===Y||r.type===A))r=i(++t);return t}function vt(r,t,i,o){if(!r)return 0;let n=r.value.charCodeAt(t);if(n===Xn||n===Rr){if(i)return 0;t++}for(;t6)return 0}return o}function io(r,t,i){if(!r)return 0;while(ll(i(t),Q0)){if(++r>6)return 0;t++}return t}function cl(r,t){let i=0;if(r===null||r.type!==w||!sr(r.value,0,ja))return 0;if(r=t(++i),r===null)return 0;if(ll(r,Da)){if(r=t(++i),r===null)return 0;if(r.type===w)return io(qn(r,0,!0),++i,t);if(ll(r,Q0))return io(1,++i,t);return 0}if(r.type===U){let o=qn(r,1,!0);if(o===0)return 0;if(r=t(++i),r===null)return i;if(r.type===q||r.type===U){if(!Oa(r,E0)||!qn(r,1,!1))return 0;return i+1}return io(o,i,t)}if(r.type===q)return io(qn(r,1,!0),++i,t);return 0}var Ia=["calc(","-moz-calc(","-webkit-calc("],ul=new Map([[k,j],[K,j],[mr,wr],[Z,xr]]);function Fr(r,t){return tr.max&&typeof r.max!=="string")return!0}return!1}function ka(r,t){let i=0,o=[],n=0;r:do{switch(r.type){case xr:case j:case wr:if(r.type!==i)break r;if(i=o.pop(),o.length===0){n++;break r}break;case k:case K:case mr:case Z:o.push(i),i=ul.get(r.type);break}n++}while(r=t(n));return n}function Lr(r){return function(t,i,o){if(t===null)return 0;if(t.type===k&&S0(t.value,Ia))return ka(t,i);return r(t,i,o)}}function C(r){return function(t){if(t===null||t.type!==r)return 0;return 1}}function Ua(r){if(r===null||r.type!==w)return 0;let t=r.value.toLowerCase();if(S0(t,Zt))return 0;if(F0(t,"default"))return 0;return 1}function G0(r){if(r===null||r.type!==w)return 0;if(Fr(r.value,0)!==45||Fr(r.value,1)!==45)return 0;return 1}function Ja(r){if(!G0(r))return 0;if(r.value==="--")return 0;return 1}function Pa(r){if(r===null||r.type!==S)return 0;let t=r.value.length;if(t!==4&&t!==5&&t!==7&&t!==9)return 0;for(let i=1;iNa,semitones:()=>Ha,resolution:()=>Ba,length:()=>Fa,frequency:()=>Ga,flex:()=>ya,decibel:()=>Aa,angle:()=>Sa});var Fa=["cm","mm","q","in","pt","pc","px","em","rem","ex","rex","cap","rcap","ch","rch","ic","ric","lh","rlh","vw","svw","lvw","dvw","vh","svh","lvh","dvh","vi","svi","lvi","dvi","vb","svb","lvb","dvb","vmin","svmin","lvmin","dvmin","vmax","svmax","lvmax","dvmax","cqw","cqh","cqi","cqb","cqmin","cqmax"],Sa=["deg","grad","rad","turn"],Na=["s","ms"],Ga=["hz","khz"],Ba=["dpi","dpcm","dppx","x"],ya=["fr"],Aa=["db"],Ha=["st"];function gl(r,t,i){return Object.assign(It("SyntaxError",r),{input:t,offset:i,rawMessage:r,message:r+` `+t+` ---`+new Array((i||t.length)+1).join("-")+"^"})}var F1=9,G1=10,S1=12,N1=13,B1=32,F0=new Uint8Array(128).map((r,t)=>/[a-zA-Z0-9\-]/.test(String.fromCharCode(t))?1:0);class ll{constructor(r){this.str=r,this.pos=0}charCodeAt(r){return r=128||F0[t]===0)break}if(this.pos===r)this.error("Expect a keyword");return this.substringToPos(r)}scanNumber(){let r=this.pos;for(;r57)break}if(this.pos===r)this.error("Expect a number");return this.substringToPos(r)}scanString(){let r=this.str.indexOf("'",this.pos+1);if(r===-1)this.pos=this.str.length,this.error("Expect an apostrophe");return this.substringToPos(r+1)}}var y1=9,A1=10,H1=12,R1=13,M1=32,R0=33,bl=35,G0=38,no=39,M0=40,Z1=41,Z0=42,ml=43,vl=44,S0=45,hl=60,ul=62,gl=63,C1=64,Wn=91,Kn=93,io=123,N0=124,B0=125,y0=8734,A0={" ":1,"&&":2,"||":3,"|":4};function H0(r){let t=null,i=null;if(r.eat(io),r.skipWs(),t=r.scanNumber(r),r.skipWs(),r.charCode()===vl){if(r.pos++,r.skipWs(),r.charCode()!==B0)i=r.scanNumber(r),r.skipWs()}else i=t;return r.eat(B0),{min:Number(t),max:i?Number(i):0}}function T1(r){let t=null,i=!1;switch(r.charCode()){case Z0:r.pos++,t={min:0,max:0};break;case ml:r.pos++,t={min:1,max:0};break;case gl:r.pos++,t={min:0,max:1};break;case bl:if(r.pos++,i=!0,r.charCode()===io)t=H0(r);else if(r.charCode()===gl)r.pos++,t={min:0,max:0};else t={min:1,max:0};break;case io:t=H0(r);break;default:return null}return{type:"Multiplier",comma:i,min:t.min,max:t.max,term:null}}function ht(r,t){let i=T1(r);if(i!==null){if(i.term=t,r.charCode()===bl&&r.charCodeAt(r.pos-1)===ml)return ht(r,i);return i}return t}function cl(r){let t=r.peek();if(t==="")return null;return ht(r,{type:"Token",value:t})}function d1(r){let t;return r.eat(hl),r.eat(no),t=r.scanWord(),r.eat(no),r.eat(ul),ht(r,{type:"Property",name:t})}function s1(r){let t=null,i=null,o=1;if(r.eat(Wn),r.charCode()===S0)r.peek(),o=-1;if(o==-1&&r.charCode()===y0)r.peek();else if(t=o*Number(r.scanNumber(r)),r.isNameCharCode())t+=r.scanWord();if(r.skipWs(),r.eat(vl),r.skipWs(),r.charCode()===y0)r.peek();else{if(o=1,r.charCode()===S0)r.peek(),o=-1;if(i=o*Number(r.scanNumber(r)),r.isNameCharCode())i+=r.scanWord()}return r.eat(Kn),{type:"Range",min:t,max:i}}function r2(r){let t,i=null;if(r.eat(hl),t=r.scanWord(),t==="boolean-expr"){r.eat(Wn);let o=$l(r,Kn);return r.eat(Kn),r.eat(ul),ht(r,{type:"Boolean",term:o.terms.length===1?o.terms[0]:o})}if(r.charCode()===M0&&r.nextCharCode()===Z1)r.pos+=2,t+="()";if(r.charCodeAt(r.findWsEnd(r.pos))===Wn)r.skipWs(),i=s1(r);return r.eat(ul),ht(r,{type:"Type",name:t,opts:i})}function t2(r){let t=r.scanWord();if(r.charCode()===M0)return r.pos++,{type:"Function",name:t};return ht(r,{type:"Keyword",name:t})}function n2(r,t){function i(n,e){return{type:"Group",terms:n,combinator:e,disallowEmpty:!1,explicit:!1}}let o;t=Object.keys(t).sort((n,e)=>A0[n]-A0[e]);while(t.length>0){o=t.shift();let n=0,e=0;for(;n1)r.splice(e,n-e,i(r.slice(e,n),o)),n=e+1;e=-1}}if(e!==-1&&t.length)r.splice(e,n-e,i(r.slice(e,n),o))}return o}function $l(r,t){let i=Object.create(null),o=[],n,e=null,l=r.pos;while(r.charCode()!==t&&(n=o2(r,t)))if(n.type!=="Spaces"){if(n.type==="Combinator"){if(e===null||e.type==="Combinator")r.pos=l,r.error("Unexpected combinator");i[n.value]=!0}else if(e!==null&&e.type!=="Combinator")i[" "]=!0,o.push({type:"Combinator",value:" "});o.push(n),e=n,l=r.pos}if(e!==null&&e.type==="Combinator")r.pos-=l,r.error("Unexpected combinator");return{type:"Group",terms:o,combinator:n2(o,i)||" ",disallowEmpty:!1,explicit:!1}}function i2(r,t){let i;if(r.eat(Wn),i=$l(r,t),r.eat(Kn),i.explicit=!0,r.charCode()===R0)r.pos++,i.disallowEmpty=!0;return i}function o2(r,t){let i=r.charCode();switch(i){case Kn:break;case Wn:return ht(r,i2(r,t));case hl:return r.nextCharCode()===no?d1(r):r2(r);case N0:return{type:"Combinator",value:r.substringToPos(r.pos+(r.nextCharCode()===N0?2:1))};case G0:return r.pos++,r.eat(G0),{type:"Combinator",value:"&&"};case vl:return r.pos++,{type:"Comma"};case no:return ht(r,{type:"String",value:r.scanString()});case M1:case y1:case A1:case R1:case H1:return{type:"Spaces",value:r.scanSpaces()};case C1:if(i=r.nextCharCode(),r.isNameCharCode(i))return r.pos++,{type:"AtKeyword",name:r.scanWord()};return cl(r);case Z0:case ml:case gl:case bl:case R0:break;case io:if(i=r.nextCharCode(),i<48||i>57)return cl(r);break;default:if(r.isNameCharCode(i))return t2(r);return cl(r)}}function Vn(r){let t=new ll(r),i=$l(t);if(t.pos!==r.length)t.error("Unexpected input");if(i.terms.length===1&&i.terms[0].type==="Group")return i.terms[0];return i}var Ln=function(){};function C0(r){return typeof r==="function"?r:Ln}function xl(r,t,i){function o(l){switch(n.call(i,l),l.type){case"Group":l.terms.forEach(o);break;case"Multiplier":case"Boolean":o(l.term);break;case"Type":case"Property":case"Keyword":case"AtKeyword":case"Function":case"String":case"Token":case"Comma":break;default:throw new Error("Unknown type: "+l.type)}e.call(i,l)}let n=Ln,e=Ln;if(typeof t==="function")n=t;else if(t)n=C0(t.enter),e=C0(t.leave);if(n===Ln&&e===Ln)throw new Error("Neither `enter` nor `leave` walker handler is set or both aren't a function");o(r,i)}var l2={decorator(r){let t=[],i=null;return{...r,node(o){let n=i;i=o,r.node.call(this,o),i=n},emit(o,n,e){t.push({type:n,value:o,node:e?null:i})},result(){return t}}}};function c2(r){let t=[];return mt(r,(i,o,n)=>t.push({type:i,value:r.slice(o,n),node:null})),t}function wl(r,t){if(typeof r==="string")return c2(r);return t.generate(r,l2)}var N={type:"Match"},H={type:"Mismatch"},oo={type:"DisallowEmpty"},u2=40,g2=41;function Dr(r,t,i){if(t===N&&i===H)return r;if(r===N&&t===N&&i===N)return r;if(r.type==="If"&&r.else===H&&t===N)t=r.then,r=r.match;return{type:"If",match:r,then:t,else:i}}function d0(r){return r.length>2&&r.charCodeAt(r.length-2)===u2&&r.charCodeAt(r.length-1)===g2}function T0(r){return r.type==="Keyword"||r.type==="AtKeyword"||r.type==="Function"||r.type==="Type"&&d0(r.name)}function $t(r,t=" ",i=!1){return{type:"Group",terms:r,combinator:t,disallowEmpty:!1,explicit:i}}function Yn(r,t,i=new Set){if(!i.has(r))switch(i.add(r),r.type){case"If":r.match=Yn(r.match,t,i),r.then=Yn(r.then,t,i),r.else=Yn(r.else,t,i);break;case"Type":return t[r.name]||r}return r}function fl(r,t,i){switch(r){case" ":{let o=N;for(let n=t.length-1;n>=0;n--){let e=t[n];o=Dr(e,o,H)}return o}case"|":{let o=H,n=null;for(let e=t.length-1;e>=0;e--){let l=t[e];if(T0(l)){if(n===null&&e>0&&T0(t[e-1]))n=Object.create(null),o=Dr({type:"Enum",map:n},N,o);if(n!==null){let u=(d0(l.name)?l.name.slice(0,-1):l.name).toLowerCase();if(u in n===!1){n[u]=l;continue}}}n=null,o=Dr(l,N,o)}return o}case"&&":{if(t.length>5)return{type:"MatchOnce",terms:t,all:!0};let o=H;for(let n=t.length-1;n>=0;n--){let e=t[n],l;if(t.length>1)l=fl(r,t.filter(function(u){return u!==e}),!1);else l=N;o=Dr(e,l,o)}return o}case"||":{if(t.length>5)return{type:"MatchOnce",terms:t,all:!1};let o=i?N:H;for(let n=t.length-1;n>=0;n--){let e=t[n],l;if(t.length>1)l=fl(r,t.filter(function(u){return u!==e}),!0);else l=N;o=Dr(e,l,o)}return o}}}function b2(r){let t=N,i=Tt(r.term);if(r.max===0){if(i=Dr(i,oo,H),t=Dr(i,null,H),t.then=Dr(N,N,t),r.comma)t.then.else=Dr({type:"Comma",syntax:r},t,H)}else for(let o=r.min||1;o<=r.max;o++){if(r.comma&&t!==N)t=Dr({type:"Comma",syntax:r},t,H);t=Dr(i,Dr(N,N,t),H)}if(r.min===0)t=Dr(N,N,t);else for(let o=0;o=65&&n<=90)n=n|32;if(n!==o)return!1}return!0}function f2(r){if(r.type!==X)return!1;return r.value!=="?"}function nx(r){if(r===null)return!0;return r.type===ir||r.type===U||r.type===K||r.type===br||r.type===Z||f2(r)}function ix(r){if(r===null)return!0;return r.type===p||r.type===fr||r.type===xr||r.type===X&&r.value==="/"}function z2(r,t,i){function o(){do J++,_=JL)L=J}function c(){v={syntax:t.syntax,opts:t.syntax.opts||v!==null&&v.opts||null,prev:v},P={type:Dl,syntax:t.syntax,token:P.token,prev:P}}function b(){if(P.type===Dl)P=P.prev;else P={type:ox,syntax:v.syntax,token:P.token,prev:P};v=v.prev}let v=null,h=null,m=null,w=null,O=0,I=null,_=null,J=-1,L=0,P={type:m2,syntax:null,token:null,prev:null};o();while(I===null&&++Om.tokenIndex)m=w,w=!1}else if(m===null){I=h2;break}t=m.nextState,h=m.thenStack,v=m.syntaxStack,P=m.matchStack,J=m.tokenIndex,_=JJ){while(J":"<'"+t.name+"'>"));if(w!==!1&&_!==null&&t.type==="Type"){if(t.name==="custom-ident"&&_.type===f||t.name==="length"&&_.value==="0"){if(w===null)w=e(t,m);t=H;break}}c(),t=tr.matchRef||tr.match;break}case"Keyword":{let B=t.name;if(_!==null){let tr=_.value;if(tr.indexOf("\\")!==-1)tr=tr.replace(/\\[09].*$/,"");if(zl(tr,B)){g(),t=N;break}}t=H;break}case"AtKeyword":case"Function":if(_!==null&&zl(_.value,t.name)){g(),t=N;break}t=H;break;case"Token":if(_!==null&&_.value===t.value){g(),t=N;break}t=H;break;case"Comma":if(_!==null&&_.type===ir)if(nx(P.token))t=H;else g(),t=ix(_)?H:N;else t=nx(P.token)||ix(_)?N:H;break;case"String":let W="",Q=J;for(;QD2,isProperty:()=>_2,isKeyword:()=>j2,getTrace:()=>ex});function ex(r){function t(n){if(n===null)return!1;return n.type==="Type"||n.type==="Property"||n.type==="Keyword"}function i(n){if(Array.isArray(n.match)){for(let e=0;ei.type==="Type"&&i.name===t)}function _2(r,t){return jl(this,r,(i)=>i.type==="Property"&&i.name===t)}function j2(r){return jl(this,r,(t)=>t.type==="Keyword")}function jl(r,t,i){let o=ex.call(r,t);if(o===null)return!1;return o.some(i)}function lx(r){if("node"in r)return r.node;return lx(r.match[0])}function cx(r){if("node"in r)return r.node;return cx(r.match[r.match.length-1])}function pl(r,t,i,o,n){function e(u){if(u.syntax!==null&&u.syntax.type===o&&u.syntax.name===n){let g=lx(u),c=cx(u);r.syntax.walk(t,function(b,v,h){if(b===g){let m=new or;do{if(m.appendData(v.data),v.data===c)break;v=v.next}while(v!==null);l.push({parent:h,nodes:m})}})}if(Array.isArray(u.match))u.match.forEach(e)}let l=[];if(i.matched!==null)e(i.matched);return l}var{hasOwnProperty:Qn}=Object.prototype;function Il(r){return typeof r==="number"&&isFinite(r)&&Math.floor(r)===r&&r>=0}function ux(r){return Boolean(r)&&Il(r.offset)&&Il(r.line)&&Il(r.column)}function O2(r,t){return function i(o,n){if(!o||o.constructor!==Object)return n(o,"Type of node should be an Object");for(let e in o){let l=!0;if(Qn.call(o,e)===!1)continue;if(e==="type"){if(o.type!==r)n(o,"Wrong node type `"+o.type+"`, expected `"+r+"`")}else if(e==="loc"){if(o.loc===null)continue;else if(o.loc&&o.loc.constructor===Object)if(typeof o.loc.source!=="string")e+=".source";else if(!ux(o.loc.start))e+=".start";else if(!ux(o.loc.end))e+=".end";else continue;l=!1}else if(t.hasOwnProperty(e)){l=!1;for(let u=0;!l&&u");else throw new Error("Wrong value `"+n+"` in `"+t+"` structure definition")}return i.join(" | ")}function p2(r,t){let i=t.structure,o={type:String,loc:!0},n={type:'"'+r+'"'};for(let e in i){if(Qn.call(i,e)===!1)continue;let l=o[e]=Array.isArray(i[e])?i[e].slice():[i[e]];n[e]=gx(l,r+"."+e)}return{docs:n,check:O2(r,o)}}function bx(r){let t={};if(r.node){for(let i in r.node)if(Qn.call(r.node,i)){let o=r.node[i];if(o.structure)t[i]=p2(i,o);else throw new Error("Missed `structure` field in `"+i+"` node type definition")}}return t}function al(r,t,i){let o={};for(let n in r)if(r[n].syntax)o[n]=i?r[n].syntax:Ht(r[n].syntax,{compact:t});return o}function I2(r,t,i){let o={};for(let[n,e]of Object.entries(r))o[n]={prelude:e.prelude&&(i?e.prelude.syntax:Ht(e.prelude.syntax,{compact:t})),descriptors:e.descriptors&&al(e.descriptors,t,i)};return o}function a2(r){for(let t=0;t{return i[o]=this.createDescriptor(t.descriptors[o],"AtruleDescriptor",o,r),i},Object.create(null)):null}}addProperty_(r,t){if(!t)return;this.properties[r]=this.createDescriptor(t,"Property",r)}addType_(r,t){if(!t)return;this.types[r]=this.createDescriptor(t,"Type",r)}checkAtruleName(r){if(!this.getAtrule(r))return new Rt("Unknown at-rule","@"+r)}checkAtrulePrelude(r,t){let i=this.checkAtruleName(r);if(i)return i;let o=this.getAtrule(r);if(!o.prelude&&t)return new SyntaxError("At-rule `@"+r+"` should not contain a prelude");if(o.prelude&&!t){if(!dt(this,o.prelude,"",!1).matched)return new SyntaxError("At-rule `@"+r+"` should contain a prelude")}}checkAtruleDescriptorName(r,t){let i=this.checkAtruleName(r);if(i)return i;let o=this.getAtrule(r),n=Ti(t);if(!o.descriptors)return new SyntaxError("At-rule `@"+r+"` has no known descriptors");if(!o.descriptors[n.name]&&!o.descriptors[n.basename])return new Rt("Unknown at-rule descriptor",t)}checkPropertyName(r){if(!this.getProperty(r))return new Rt("Unknown property",r)}matchAtrulePrelude(r,t){let i=this.checkAtrulePrelude(r,t);if(i)return Gr(null,i);let o=this.getAtrule(r);if(!o.prelude)return Gr(null,null);return dt(this,o.prelude,t||"",!1)}matchAtruleDescriptor(r,t,i){let o=this.checkAtruleDescriptorName(r,t);if(o)return Gr(null,o);let n=this.getAtrule(r),e=Ti(t);return dt(this,n.descriptors[e.name]||n.descriptors[e.basename],i,!1)}matchDeclaration(r){if(r.type!=="Declaration")return Gr(null,new Error("Not a Declaration node"));return this.matchProperty(r.property,r.value)}matchProperty(r,t){if(Te(r).custom)return Gr(null,new Error("Lexer matching doesn't applicable for custom properties"));let i=this.checkPropertyName(r);if(i)return Gr(null,i);return dt(this,this.getProperty(r),t,!0)}matchType(r,t){let i=this.getType(r);if(!i)return Gr(null,new Rt("Unknown type",r));return dt(this,i,t,!1)}match(r,t){if(typeof r!=="string"&&(!r||!r.type))return Gr(null,new Rt("Bad syntax"));if(typeof r==="string"||!r.match)r=this.createDescriptor(r,"Type","anonymous");return dt(this,r,t,!1)}findValueFragments(r,t,i,o){return pl(this,t,this.matchProperty(r,t),i,o)}findDeclarationValueFragments(r,t,i){return pl(this,r.value,this.matchDeclaration(r),t,i)}findAllFragments(r,t,i){let o=[];return this.syntax.walk(r,{visit:"Declaration",enter:(n)=>{o.push.apply(o,this.findDeclarationValueFragments(n,t,i))}}),o}getAtrule(r,t=!0){let i=Ti(r);return(i.vendor&&t?this.atrules[i.name]||this.atrules[i.basename]:this.atrules[i.name])||null}getAtrulePrelude(r,t=!0){let i=this.getAtrule(r,t);return i&&i.prelude||null}getAtruleDescriptor(r,t){return this.atrules.hasOwnProperty(r)&&this.atrules.declarators?this.atrules[r].declarators[t]||null:null}getProperty(r,t=!0){let i=Te(r);return(i.vendor&&t?this.properties[i.name]||this.properties[i.basename]:this.properties[i.name])||null}getType(r){return hasOwnProperty.call(this.types,r)?this.types[r]:null}validate(){function r(u,g){return g?`<${u}>`:`<'${u}'>`}function t(u,g,c,b){if(c.has(g))return c.get(g);if(c.set(g,!1),b.syntax!==null)xl(b.syntax,function(v){if(v.type!=="Type"&&v.type!=="Property")return;let h=v.type==="Type"?u.types:u.properties,m=v.type==="Type"?o:n;if(!hasOwnProperty.call(h,v.name))i.push(`${r(g,c===o)} used missed syntax definition ${r(v.name,v.type==="Type")}`),c.set(g,!0);else if(t(u,v.name,m,h[v.name]))i.push(`${r(g,c===o)} used broken syntax definition ${r(v.name,v.type==="Type")}`),c.set(g,!0)},this)}let i=[],o=new Map,n=new Map;for(let u in this.types)t(this,u,o,this.types[u]);for(let u in this.properties)t(this,u,n,this.properties[u]);let e=[...o.keys()].filter((u)=>o.get(u)),l=[...n.keys()].filter((u)=>n.get(u));if(e.length||l.length)return{errors:i,types:e,properties:l};return null}dump(r,t){return{generic:this.generic,cssWideKeywords:this.cssWideKeywords,units:this.units,types:al(this.types,!t,r),properties:al(this.properties,!t,r),atrules:I2(this.atrules,!t,r)}}toString(){return JSON.stringify(this.dump())}}function Ul(r,t){if(typeof t==="string"&&/^\s*\|/.test(t))return typeof r==="string"?r+t:t.replace(/^\s*\|\s*/,"");return t||null}function mx(r,t){let i=Object.create(null);for(let[o,n]of Object.entries(r))if(n){i[o]={};for(let e of Object.keys(n))if(t.includes(e))i[o][e]=n[e]}return i}function Gn(r,t){let i={...r};for(let[o,n]of Object.entries(t))switch(o){case"generic":i[o]=Boolean(n);break;case"cssWideKeywords":i[o]=r[o]?[...r[o],...n]:n||[];break;case"units":i[o]={...r[o]};for(let[e,l]of Object.entries(n))i[o][e]=Array.isArray(l)?l:[];break;case"atrules":i[o]={...r[o]};for(let[e,l]of Object.entries(n)){let u=i[o][e]||{},g=i[o][e]={prelude:u.prelude||null,descriptors:{...u.descriptors}};if(!l)continue;g.prelude=l.prelude?Ul(g.prelude,l.prelude):g.prelude||null;for(let[c,b]of Object.entries(l.descriptors||{}))g.descriptors[c]=b?Ul(g.descriptors[c],b):null;if(!Object.keys(g.descriptors).length)g.descriptors=null}break;case"types":case"properties":i[o]={...r[o]};for(let[e,l]of Object.entries(n))i[o][e]=Ul(i[o][e],l);break;case"scope":case"features":i[o]={...r[o]};for(let[e,l]of Object.entries(n))i[o][e]={...i[o][e],...l};break;case"parseContext":i[o]={...r[o],...n};break;case"atrule":case"pseudo":i[o]={...r[o],...mx(n,["parse"])};break;case"node":i[o]={...r[o],...mx(n,["name","structure","parse","generate","walkContext"])};break}return i}function vx(r){let t=t0(r),i=k0(r),o=j0(r),{fromPlainObject:n,toPlainObject:e}=O0(i),l={lexer:null,createLexer:(u)=>new Fn(u,l,l.lexer.structure),tokenize:mt,parse:t,generate:o,walk:i,find:i.find,findLast:i.findLast,findAll:i.findAll,fromPlainObject:n,toPlainObject:e,fork(u){let g=Gn({},r);return vx(typeof u==="function"?u(g):Gn(g,u))}};return l.lexer=new Fn({generic:r.generic,cssWideKeywords:r.cssWideKeywords,units:r.units,types:r.types,atrules:r.atrules,properties:r.properties,node:r.node},l),l}var kl=(r)=>vx(Gn({},r));var hx={generic:!0,cssWideKeywords:["initial","inherit","unset","revert","revert-layer"],units:{angle:["deg","grad","rad","turn"],decibel:["db"],flex:["fr"],frequency:["hz","khz"],length:["cm","mm","q","in","pt","pc","px","em","rem","ex","rex","cap","rcap","ch","rch","ic","ric","lh","rlh","vw","svw","lvw","dvw","vh","svh","lvh","dvh","vi","svi","lvi","dvi","vb","svb","lvb","dvb","vmin","svmin","lvmin","dvmin","vmax","svmax","lvmax","dvmax","cqw","cqh","cqi","cqb","cqmin","cqmax"],resolution:["dpi","dpcm","dppx","x"],semitones:["st"],time:["s","ms"]},types:{"abs()":"abs( )","absolute-size":"xx-small|x-small|small|medium|large|x-large|xx-large|xxx-large","acos()":"acos( )","alpha-value":"|","angle-percentage":"|","angular-color-hint":"","angular-color-stop":"&&?","angular-color-stop-list":"[ [, ]?]# , ","animateable-feature":"scroll-position|contents|","asin()":"asin( )","atan()":"atan( )","atan2()":"atan2( , )",attachment:"scroll|fixed|local","attr()":"attr( ? [, ]? )","attr-matcher":"['~'|'|'|'^'|'$'|'*']? '='","attr-modifier":"i|s","attribute-selector":"'[' ']'|'[' [|] ? ']'","auto-repeat":"repeat( [auto-fill|auto-fit] , [? ]+ ? )","auto-track-list":"[? [|]]* ? [? [|]]* ?",axis:"block|inline|x|y","baseline-position":"[first|last]? baseline","basic-shape":"||||||","bg-image":"none|","bg-layer":"|| [/ ]?||||||||","bg-position":"[[left|center|right|top|bottom|]|[left|center|right|] [top|center|bottom|]|[center|[left|right] ?]&&[center|[top|bottom] ?]]","bg-size":"[|auto]{1,2}|cover|contain","blur()":"blur( )","blend-mode":"normal|multiply|screen|overlay|darken|lighten|color-dodge|color-burn|hard-light|soft-light|difference|exclusion|hue|saturation|color|luminosity",box:"border-box|padding-box|content-box","brightness()":"brightness( )","calc()":"calc( )","calc-sum":" [['+'|'-'] ]*","calc-product":" ['*' |'/' ]*","calc-value":"||||( )","calc-constant":"e|pi|infinity|-infinity|NaN","cf-final-image":"|","cf-mixing-image":"?&&","circle()":"circle( []? [at ]? )","clamp()":"clamp( #{3} )","class-selector":"'.' ","clip-source":"",color:"|currentColor||||<-non-standard-color>","color-stop":"|","color-stop-angle":"{1,2}","color-stop-length":"{1,2}","color-stop-list":"[ [, ]?]# , ","color-interpolation-method":"in [| ?|]",combinator:"'>'|'+'|'~'|['|' '|']","common-lig-values":"[common-ligatures|no-common-ligatures]","compat-auto":"searchfield|textarea|push-button|slider-horizontal|checkbox|radio|square-button|menulist|listbox|meter|progress-bar|button","composite-style":"clear|copy|source-over|source-in|source-out|source-atop|destination-over|destination-in|destination-out|destination-atop|xor","compositing-operator":"add|subtract|intersect|exclude","compound-selector":"[? *]!","compound-selector-list":"#","complex-selector":" [? ]*","complex-selector-list":"#","conic-gradient()":"conic-gradient( [from ]? [at ]? , )","contextual-alt-values":"[contextual|no-contextual]","content-distribution":"space-between|space-around|space-evenly|stretch","content-list":"[|contents||||||]+","content-position":"center|start|end|flex-start|flex-end","content-replacement":"","contrast()":"contrast( [] )","cos()":"cos( )",counter:"|","counter()":"counter( , ? )","counter-name":"","counter-style":"|symbols( )","counter-style-name":"","counters()":"counters( , , ? )","cross-fade()":"cross-fade( , ? )","cubic-bezier-timing-function":"ease|ease-in|ease-out|ease-in-out|cubic-bezier( , , , )","deprecated-system-color":"ActiveBorder|ActiveCaption|AppWorkspace|Background|ButtonFace|ButtonHighlight|ButtonShadow|ButtonText|CaptionText|GrayText|Highlight|HighlightText|InactiveBorder|InactiveCaption|InactiveCaptionText|InfoBackground|InfoText|Menu|MenuText|Scrollbar|ThreeDDarkShadow|ThreeDFace|ThreeDHighlight|ThreeDLightShadow|ThreeDShadow|Window|WindowFrame|WindowText","discretionary-lig-values":"[discretionary-ligatures|no-discretionary-ligatures]","display-box":"contents|none","display-inside":"flow|flow-root|table|flex|grid|ruby","display-internal":"table-row-group|table-header-group|table-footer-group|table-row|table-cell|table-column-group|table-column|table-caption|ruby-base|ruby-text|ruby-base-container|ruby-text-container","display-legacy":"inline-block|inline-list-item|inline-table|inline-flex|inline-grid","display-listitem":"?&&[flow|flow-root]?&&list-item","display-outside":"block|inline|run-in","drop-shadow()":"drop-shadow( {2,3} ? )","east-asian-variant-values":"[jis78|jis83|jis90|jis04|simplified|traditional]","east-asian-width-values":"[full-width|proportional-width]","element()":"element( , [first|start|last|first-except]? )|element( )","ellipse()":"ellipse( [{2}]? [at ]? )","ending-shape":"circle|ellipse","env()":"env( , ? )","exp()":"exp( )","explicit-track-list":"[? ]+ ?","family-name":"|+","feature-tag-value":" [|on|off]?","feature-type":"@stylistic|@historical-forms|@styleset|@character-variant|@swash|@ornaments|@annotation","feature-value-block":" '{' '}'","feature-value-block-list":"+","feature-value-declaration":" : + ;","feature-value-declaration-list":"","feature-value-name":"","fill-rule":"nonzero|evenodd","filter-function":"|||||||||","filter-function-list":"[|]+","final-bg-layer":"<'background-color'>|||| [/ ]?||||||||","fixed-breadth":"","fixed-repeat":"repeat( [] , [? ]+ ? )","fixed-size":"|minmax( , )|minmax( , )","font-stretch-absolute":"normal|ultra-condensed|extra-condensed|condensed|semi-condensed|semi-expanded|expanded|extra-expanded|ultra-expanded|","font-variant-css21":"[normal|small-caps]","font-weight-absolute":"normal|bold|","frequency-percentage":"|","general-enclosed":"[ ? )]|[( ? )]","generic-family":"|||<-non-standard-generic-family>","generic-name":"serif|sans-serif|cursive|fantasy|monospace","geometry-box":"|fill-box|stroke-box|view-box",gradient:"||||||<-legacy-gradient>","grayscale()":"grayscale( )","grid-line":"auto||[&&?]|[span&&[||]]","historical-lig-values":"[historical-ligatures|no-historical-ligatures]","hsl()":"hsl( [/ ]? )|hsl( , , , ? )","hsla()":"hsla( [/ ]? )|hsla( , , , ? )",hue:"|","hue-rotate()":"hue-rotate( )","hue-interpolation-method":"[shorter|longer|increasing|decreasing] hue","hwb()":"hwb( [|none] [|none] [|none] [/ [|none]]? )","hypot()":"hypot( # )",image:"||||||","image()":"image( ? [? , ?]! )","image-set()":"image-set( # )","image-set-option":"[|] [||type( )]","image-src":"|","image-tags":"ltr|rtl","inflexible-breadth":"|min-content|max-content|auto","inset()":"inset( {1,4} [round <'border-radius'>]? )","invert()":"invert( )","keyframes-name":"|","keyframe-block":"# { }","keyframe-block-list":"+","keyframe-selector":"from|to|| ","lab()":"lab( [||none] [||none] [||none] [/ [|none]]? )","layer()":"layer( )","layer-name":" ['.' ]*","lch()":"lch( [||none] [||none] [|none] [/ [|none]]? )","leader()":"leader( )","leader-type":"dotted|solid|space|","length-percentage":"|","light-dark()":"light-dark( , )","line-names":"'[' * ']'","line-name-list":"[|]+","line-style":"none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset","line-width":"|thin|medium|thick","linear-color-hint":"","linear-color-stop":" ?","linear-gradient()":"linear-gradient( [[|to ]||]? , )","log()":"log( , ? )","mask-layer":"|| [/ ]?||||||[|no-clip]||||","mask-position":"[|left|center|right] [|top|center|bottom]?","mask-reference":"none||","mask-source":"","masking-mode":"alpha|luminance|match-source","matrix()":"matrix( #{6} )","matrix3d()":"matrix3d( #{16} )","max()":"max( # )","media-and":" [and ]+","media-condition":"|||","media-condition-without-or":"||","media-feature":"( [||] )","media-in-parens":"( )||","media-not":"not ","media-or":" [or ]+","media-query":"|[not|only]? [and ]?","media-query-list":"#","media-type":"","mf-boolean":"","mf-name":"","mf-plain":" : ","mf-range":" ['<'|'>']? '='? | ['<'|'>']? '='? | '<' '='? '<' '='? | '>' '='? '>' '='? ","mf-value":"|||","min()":"min( # )","minmax()":"minmax( [|min-content|max-content|auto] , [||min-content|max-content|auto] )","mod()":"mod( , )","name-repeat":"repeat( [|auto-fill] , + )","named-color":"transparent|aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|rebeccapurple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen","namespace-prefix":"","ns-prefix":"[|'*']? '|'","number-percentage":"|","numeric-figure-values":"[lining-nums|oldstyle-nums]","numeric-fraction-values":"[diagonal-fractions|stacked-fractions]","numeric-spacing-values":"[proportional-nums|tabular-nums]",nth:"|even|odd","opacity()":"opacity( [] )","overflow-position":"unsafe|safe","outline-radius":"|","page-body":"? [; ]?| ","page-margin-box":" '{' '}'","page-margin-box-type":"@top-left-corner|@top-left|@top-center|@top-right|@top-right-corner|@bottom-left-corner|@bottom-left|@bottom-center|@bottom-right|@bottom-right-corner|@left-top|@left-middle|@left-bottom|@right-top|@right-middle|@right-bottom","page-selector-list":"[#]?","page-selector":"+| *","page-size":"A5|A4|A3|B5|B4|JIS-B5|JIS-B4|letter|legal|ledger","path()":"path( [ ,]? )","paint()":"paint( , ? )","perspective()":"perspective( [|none] )","polygon()":"polygon( ? , [ ]# )","polar-color-space":"hsl|hwb|lch|oklch",position:"[[left|center|right]||[top|center|bottom]|[left|center|right|] [top|center|bottom|]?|[[left|right] ]&&[[top|bottom] ]]","pow()":"pow( , )","pseudo-class-selector":"':' |':' ')'","pseudo-element-selector":"':' |","pseudo-page":": [left|right|first|blank]",quote:"open-quote|close-quote|no-open-quote|no-close-quote","radial-gradient()":"radial-gradient( [||]? [at ]? , )",ratio:" [/ ]?","ray()":"ray( &&?&&contain?&&[at ]? )","ray-size":"closest-side|closest-corner|farthest-side|farthest-corner|sides","rectangular-color-space":"srgb|srgb-linear|display-p3|a98-rgb|prophoto-rgb|rec2020|lab|oklab|xyz|xyz-d50|xyz-d65","relative-selector":"? ","relative-selector-list":"#","relative-size":"larger|smaller","rem()":"rem( , )","repeat-style":"repeat-x|repeat-y|[repeat|space|round|no-repeat]{1,2}","repeating-conic-gradient()":"repeating-conic-gradient( [from ]? [at ]? , )","repeating-linear-gradient()":"repeating-linear-gradient( [|to ]? , )","repeating-radial-gradient()":"repeating-radial-gradient( [||]? [at ]? , )","reversed-counter-name":"reversed( )","rgb()":"rgb( {3} [/ ]? )|rgb( {3} [/ ]? )|rgb( #{3} , ? )|rgb( #{3} , ? )","rgba()":"rgba( {3} [/ ]? )|rgba( {3} [/ ]? )|rgba( #{3} , ? )|rgba( #{3} , ? )","rotate()":"rotate( [|] )","rotate3d()":"rotate3d( , , , [|] )","rotateX()":"rotateX( [|] )","rotateY()":"rotateY( [|] )","rotateZ()":"rotateZ( [|] )","round()":"round( ? , , )","rounding-strategy":"nearest|up|down|to-zero","saturate()":"saturate( )","scale()":"scale( [|]#{1,2} )","scale3d()":"scale3d( [|]#{3} )","scaleX()":"scaleX( [|] )","scaleY()":"scaleY( [|] )","scaleZ()":"scaleZ( [|] )","scroll()":"scroll( [||]? )",scroller:"root|nearest|self","self-position":"center|start|end|self-start|self-end|flex-start|flex-end","shape-radius":"|closest-side|farthest-side","sign()":"sign( )","skew()":"skew( [|] , [|]? )","skewX()":"skewX( [|] )","skewY()":"skewY( [|] )","sepia()":"sepia( )",shadow:"inset?&&{2,4}&&?","shadow-t":"[{2,3}&&?]",shape:"rect( , , , )|rect( )","shape-box":"|margin-box","side-or-corner":"[left|right]||[top|bottom]","sin()":"sin( )","single-animation":"<'animation-duration'>||||<'animation-delay'>||||||||||[none|]||","single-animation-direction":"normal|reverse|alternate|alternate-reverse","single-animation-fill-mode":"none|forwards|backwards|both","single-animation-iteration-count":"infinite|","single-animation-play-state":"running|paused","single-animation-timeline":"auto|none|||","single-transition":"[none|]||