Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9056168b5b | ||
|
|
72e8b2de91 |
+16
-8
@@ -1,9 +1,17 @@
|
|||||||
# GEMINI_API_KEY: Required for Gemini AI API calls.
|
HOST="127.0.0.1"
|
||||||
# AI Studio automatically injects this at runtime from user secrets.
|
PORT="3000"
|
||||||
# Users configure this via the Secrets panel in the AI Studio UI.
|
TRUST_PROXY="loopback"
|
||||||
GEMINI_API_KEY="MY_GEMINI_API_KEY"
|
|
||||||
|
|
||||||
# APP_URL: The URL where this applet is hosted.
|
# Gitea discovery is disabled unless all three values are configured.
|
||||||
# AI Studio automatically injects this at runtime with the Cloud Run service URL.
|
GITEA_BASE_URL="https://git.example.internal"
|
||||||
# Used for self-referential links, OAuth callbacks, and API endpoints.
|
GITEA_ALLOWED_ORIGINS="https://git.example.internal"
|
||||||
APP_URL="MY_APP_URL"
|
GITEA_TOKEN="replace-with-a-read-only-gitea-token"
|
||||||
|
GITEA_DISCOVERY_TIMEOUT_MS="5000"
|
||||||
|
GITEA_DISCOVERY_RATE_LIMIT="5"
|
||||||
|
GITEA_DISCOVERY_RATE_WINDOW_MS="60000"
|
||||||
|
|
||||||
|
# Hosts accepted by Git connectivity and synchronization endpoints.
|
||||||
|
GIT_ALLOWED_HOSTS="git.example.internal"
|
||||||
|
|
||||||
|
# Optional path-delimited roots for local development repositories.
|
||||||
|
GIT_ALLOWED_LOCAL_ROOTS="."
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
verify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
- run: npm run lint
|
||||||
|
- run: npm run build
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# ADR 0001: Keep source inventory subordinate to Git-owned build inputs
|
||||||
|
|
||||||
|
- Status: Accepted
|
||||||
|
- Date: 2026-07-25
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The control plane can inspect and discover repositories, while the deterministic
|
||||||
|
builder resolves its site definition and other declared inputs from exact Git
|
||||||
|
commits. A mutable source-management UI must not become an alternate build
|
||||||
|
configuration channel.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Repository discovery and connectivity state remain operational inventory only.
|
||||||
|
They may not alter a release input, theme selection, project declaration, or
|
||||||
|
source pin. Build inputs continue to come from closed-schema, Git-owned
|
||||||
|
configuration resolved into immutable snapshots.
|
||||||
|
|
||||||
|
Gitea discovery is fail-closed: its HTTPS origin must be configured and
|
||||||
|
allow-listed, its API token comes from the process environment, requests use a
|
||||||
|
bounded timeout, and callers are rate-limited. Discovered entries are merged
|
||||||
|
atomically into inventory and begin with projection disabled.
|
||||||
|
|
||||||
|
Source-management UX may add review and configuration workflows later, but any
|
||||||
|
accepted change must be committed to the Git-owned configuration before it can
|
||||||
|
affect a release.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Discovery cannot silently expand release inputs.
|
||||||
|
- Operators must configure Gitea credentials and origin policy explicitly.
|
||||||
|
- A future writable source UX requires a new ADR describing its Git commit and
|
||||||
|
review workflow.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# ADR 0002: Remote publication remains unimplemented and fail-closed
|
||||||
|
|
||||||
|
- Status: Accepted
|
||||||
|
- Date: 2026-07-25
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The control-plane UI models SSH/rsync targets, but there is no production-ready
|
||||||
|
remote deployment implementation. Returning simulated connectivity or success
|
||||||
|
would allow operators and reviewers to mistake a UI model for a verified
|
||||||
|
publication path.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Remote publication and remote connectivity tests return an explicit
|
||||||
|
`E_REMOTE_PUBLICATION_NOT_IMPLEMENTED`/HTTP 501 response. The store contains a
|
||||||
|
compile-time false capability constant and checks it before reading target or
|
||||||
|
build configuration. No configuration field, credential, target state, or
|
||||||
|
environment variable can enable shell execution or rsync.
|
||||||
|
|
||||||
|
Local staging and activation continue to operate only on checksum-verified,
|
||||||
|
immutable release directories.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- The control plane cannot deploy a release to a remote host.
|
||||||
|
- Target records remain descriptive UI state only.
|
||||||
|
- Implementing SSH/rsync requires a new ADR, an explicit capability boundary,
|
||||||
|
host-key verification, credential isolation, atomic remote promotion, and
|
||||||
|
end-to-end tests.
|
||||||
@@ -19,6 +19,8 @@ The build engine now:
|
|||||||
## Development
|
## Development
|
||||||
|
|
||||||
Requires Node.js 22 or later.
|
Requires Node.js 22 or later.
|
||||||
|
npm is the canonical package manager. CI uses `npm ci` and `package-lock.json`;
|
||||||
|
other lockfiles are intentionally unsupported.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
@@ -36,6 +38,15 @@ npm start
|
|||||||
|
|
||||||
The development server listens on port `3000` by default. In the current production deployment, the Node service listens privately on `127.0.0.1:3001`; Nginx exposes the control plane on port 80 and the active rendered release on port 3000.
|
The development server listens on port `3000` by default. In the current production deployment, the Node service listens privately on `127.0.0.1:3001`; Nginx exposes the control plane on port 80 and the active rendered release on port 3000.
|
||||||
|
|
||||||
|
Gitea discovery is disabled until `GITEA_BASE_URL`, `GITEA_ALLOWED_ORIGINS`,
|
||||||
|
and a read-only `GITEA_TOKEN` are configured. Discovery uses authenticated
|
||||||
|
HTTPS requests with an abort deadline and per-client rate limiting. Git
|
||||||
|
connectivity endpoints accept only configured hosts or local repositories under
|
||||||
|
`GIT_ALLOWED_LOCAL_ROOTS`; they invoke Git without a shell.
|
||||||
|
|
||||||
|
The deterministic renderer has no AI SDK dependency and does not accept model
|
||||||
|
output as a release input.
|
||||||
|
|
||||||
## Operations
|
## Operations
|
||||||
|
|
||||||
See [Website Engine Operations](docs/OPERATIONS.md) for the verified private-network topology, deployment procedure, service checks, key fingerprints, credential-handling rules, and recovery guidance. The operations document intentionally contains no passwords, tokens, or private-key material.
|
See [Website Engine Operations](docs/OPERATIONS.md) for the verified private-network topology, deployment procedure, service checks, key fingerprints, credential-handling rules, and recovery guidance. The operations document intentionally contains no passwords, tokens, or private-key material.
|
||||||
@@ -57,7 +68,7 @@ Validation failures produce a failed build record but never publish a partial re
|
|||||||
## Current boundaries
|
## Current boundaries
|
||||||
|
|
||||||
- Theme packages are presentation data only; Node, shell, WASM, package-manager scripts, custom Liquid tags, and custom filters are not executed.
|
- Theme packages are presentation data only; Node, shell, WASM, package-manager scripts, custom Liquid tags, and custom filters are not executed.
|
||||||
- The reference loader resolves the configured site-definition repository and supports separately pinned source snapshots through the typed boundary; source-management UX remains follow-up work.
|
- The reference loader resolves the configured site-definition repository and supports separately pinned source snapshots through the typed boundary; source-management UX remains follow-up work governed by [ADR 0001](.pandaos/adrs/0001-git-source-management-boundary.md).
|
||||||
- Remote rsync deployment is still represented by the existing control-plane simulation and must not be treated as a completed production deploy path.
|
- Remote rsync deployment is not implemented. Push and connection-test endpoints fail closed with HTTP 501, and no configuration field can enable execution; see [ADR 0002](.pandaos/adrs/0002-remote-publication-not-implemented.md).
|
||||||
|
|
||||||
See [docs/OPERATIONS.md](docs/OPERATIONS.md) for snapshot, release, promotion, failure, and rollback procedures.
|
See [docs/OPERATIONS.md](docs/OPERATIONS.md) for snapshot, release, promotion, failure, and rollback procedures.
|
||||||
|
|||||||
@@ -1,661 +0,0 @@
|
|||||||
{
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"configVersion": 1,
|
|
||||||
"workspaces": {
|
|
||||||
"": {
|
|
||||||
"name": "react-example",
|
|
||||||
"dependencies": {
|
|
||||||
"@google/genai": "^2.4.0",
|
|
||||||
"@tailwindcss/vite": "^4.1.14",
|
|
||||||
"@vitejs/plugin-react": "^5.0.4",
|
|
||||||
"dotenv": "^17.2.3",
|
|
||||||
"express": "^4.21.2",
|
|
||||||
"lucide-react": "^0.546.0",
|
|
||||||
"motion": "^12.23.24",
|
|
||||||
"react": "^19.0.1",
|
|
||||||
"react-dom": "^19.0.1",
|
|
||||||
"vite": "^6.2.3",
|
|
||||||
"yaml": "^2.9.0",
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/express": "^4.17.21",
|
|
||||||
"@types/node": "^22.14.0",
|
|
||||||
"autoprefixer": "^10.4.21",
|
|
||||||
"esbuild": "^0.25.0",
|
|
||||||
"tailwindcss": "^4.1.14",
|
|
||||||
"tsx": "^4.21.0",
|
|
||||||
"typescript": "~5.8.2",
|
|
||||||
"vite": "^6.2.3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@babel/code-frame": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="],
|
|
||||||
|
|
||||||
"@babel/compat-data": ["@babel/[email protected]", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="],
|
|
||||||
|
|
||||||
"@babel/core": ["@babel/[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="],
|
|
||||||
|
|
||||||
"@babel/generator": ["@babel/[email protected]", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="],
|
|
||||||
|
|
||||||
"@babel/helper-compilation-targets": ["@babel/[email protected]", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="],
|
|
||||||
|
|
||||||
"@babel/helper-globals": ["@babel/[email protected]", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="],
|
|
||||||
|
|
||||||
"@babel/helper-module-imports": ["@babel/[email protected]", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="],
|
|
||||||
|
|
||||||
"@babel/helper-module-transforms": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="],
|
|
||||||
|
|
||||||
"@babel/helper-plugin-utils": ["@babel/[email protected]", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="],
|
|
||||||
|
|
||||||
"@babel/helper-string-parser": ["@babel/[email protected]", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="],
|
|
||||||
|
|
||||||
"@babel/helper-validator-identifier": ["@babel/[email protected]", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="],
|
|
||||||
|
|
||||||
"@babel/helper-validator-option": ["@babel/[email protected]", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="],
|
|
||||||
|
|
||||||
"@babel/helpers": ["@babel/[email protected]", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="],
|
|
||||||
|
|
||||||
"@babel/parser": ["@babel/[email protected]", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="],
|
|
||||||
|
|
||||||
"@babel/plugin-transform-react-jsx-self": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw=="],
|
|
||||||
|
|
||||||
"@babel/plugin-transform-react-jsx-source": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q=="],
|
|
||||||
|
|
||||||
"@babel/template": ["@babel/[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="],
|
|
||||||
|
|
||||||
"@babel/traverse": ["@babel/[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="],
|
|
||||||
|
|
||||||
"@babel/types": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="],
|
|
||||||
|
|
||||||
"@esbuild/aix-ppc64": ["@esbuild/[email protected]", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
|
|
||||||
|
|
||||||
"@esbuild/android-arm": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
|
|
||||||
|
|
||||||
"@esbuild/android-arm64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="],
|
|
||||||
|
|
||||||
"@esbuild/android-x64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="],
|
|
||||||
|
|
||||||
"@esbuild/darwin-arm64": ["@esbuild/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="],
|
|
||||||
|
|
||||||
"@esbuild/darwin-x64": ["@esbuild/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="],
|
|
||||||
|
|
||||||
"@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="],
|
|
||||||
|
|
||||||
"@esbuild/freebsd-x64": ["@esbuild/[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-arm": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-arm64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-ia32": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-loong64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-mips64el": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-ppc64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-riscv64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-s390x": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-x64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="],
|
|
||||||
|
|
||||||
"@esbuild/netbsd-arm64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="],
|
|
||||||
|
|
||||||
"@esbuild/netbsd-x64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="],
|
|
||||||
|
|
||||||
"@esbuild/openbsd-arm64": ["@esbuild/[email protected]", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="],
|
|
||||||
|
|
||||||
"@esbuild/openbsd-x64": ["@esbuild/[email protected]", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="],
|
|
||||||
|
|
||||||
"@esbuild/openharmony-arm64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="],
|
|
||||||
|
|
||||||
"@esbuild/sunos-x64": ["@esbuild/[email protected]", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="],
|
|
||||||
|
|
||||||
"@esbuild/win32-arm64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="],
|
|
||||||
|
|
||||||
"@esbuild/win32-ia32": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="],
|
|
||||||
|
|
||||||
"@esbuild/win32-x64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
|
|
||||||
|
|
||||||
"@google/genai": ["@google/[email protected]", "", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-GM7C8Kaomvjz05x5JEO6+l3d/pciL9LxAG9dUjJLD7nTPZ9X0Cfsf2Z7eET6UjgWyUmxXCHtYnQoQ77F9+ZIOQ=="],
|
|
||||||
|
|
||||||
"@jridgewell/gen-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
|
||||||
|
|
||||||
"@jridgewell/remapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
|
||||||
|
|
||||||
"@jridgewell/resolve-uri": ["@jridgewell/[email protected]", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec": ["@jridgewell/[email protected]", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
|
||||||
|
|
||||||
"@jridgewell/trace-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
|
||||||
|
|
||||||
"@protobufjs/aspromise": ["@protobufjs/[email protected]", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
|
|
||||||
|
|
||||||
"@protobufjs/base64": ["@protobufjs/[email protected]", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
|
|
||||||
|
|
||||||
"@protobufjs/codegen": ["@protobufjs/[email protected]", "", {}, "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g=="],
|
|
||||||
|
|
||||||
"@protobufjs/eventemitter": ["@protobufjs/[email protected]", "", {}, "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg=="],
|
|
||||||
|
|
||||||
"@protobufjs/fetch": ["@protobufjs/[email protected]", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1" } }, "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw=="],
|
|
||||||
|
|
||||||
"@protobufjs/float": ["@protobufjs/[email protected]", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
|
|
||||||
|
|
||||||
"@protobufjs/path": ["@protobufjs/[email protected]", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
|
|
||||||
|
|
||||||
"@protobufjs/pool": ["@protobufjs/[email protected]", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
|
|
||||||
|
|
||||||
"@protobufjs/utf8": ["@protobufjs/[email protected]", "", {}, "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug=="],
|
|
||||||
|
|
||||||
"@rolldown/pluginutils": ["@rolldown/[email protected]", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi": ["@rollup/[email protected]", "", { "os": "android", "cpu": "arm" }, "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-android-arm64": ["@rollup/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-darwin-arm64": ["@rollup/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-darwin-x64": ["@rollup/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-arm64": ["@rollup/[email protected]", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-x64": ["@rollup/[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-loong64-musl": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-ppc64-musl": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "ppc64" }, "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "s390x" }, "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-musl": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-openbsd-x64": ["@rollup/[email protected]", "", { "os": "openbsd", "cpu": "x64" }, "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-openharmony-arm64": ["@rollup/[email protected]", "", { "os": "none", "cpu": "arm64" }, "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/[email protected]", "", { "os": "win32", "cpu": "ia32" }, "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA=="],
|
|
||||||
|
|
||||||
"@tailwindcss/node": ["@tailwindcss/[email protected]", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.3" } }, "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide": ["@tailwindcss/[email protected]", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.3", "@tailwindcss/oxide-darwin-arm64": "4.3.3", "@tailwindcss/oxide-darwin-x64": "4.3.3", "@tailwindcss/oxide-freebsd-x64": "4.3.3", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", "@tailwindcss/oxide-linux-x64-musl": "4.3.3", "@tailwindcss/oxide-wasm32-wasi": "4.3.3", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" } }, "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/[email protected]", "", { "dependencies": { "@emnapi/core": "^1.11.1", "@emnapi/runtime": "^1.11.1", "@emnapi/wasi-threads": "^1.2.2", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw=="],
|
|
||||||
|
|
||||||
"@tailwindcss/vite": ["@tailwindcss/[email protected]", "", { "dependencies": { "@tailwindcss/node": "4.3.3", "@tailwindcss/oxide": "4.3.3", "tailwindcss": "4.3.3" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw=="],
|
|
||||||
|
|
||||||
"@types/babel__core": ["@types/[email protected]", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
|
|
||||||
|
|
||||||
"@types/babel__generator": ["@types/[email protected]", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
|
|
||||||
|
|
||||||
"@types/babel__template": ["@types/[email protected]", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
|
|
||||||
|
|
||||||
"@types/babel__traverse": ["@types/[email protected]", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
|
|
||||||
|
|
||||||
"@types/body-parser": ["@types/[email protected]", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="],
|
|
||||||
|
|
||||||
"@types/connect": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="],
|
|
||||||
|
|
||||||
"@types/estree": ["@types/[email protected]", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
|
|
||||||
|
|
||||||
"@types/express": ["@types/[email protected]", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "^1" } }, "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw=="],
|
|
||||||
|
|
||||||
"@types/express-serve-static-core": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg=="],
|
|
||||||
|
|
||||||
"@types/http-errors": ["@types/[email protected]", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="],
|
|
||||||
|
|
||||||
"@types/mime": ["@types/[email protected]", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="],
|
|
||||||
|
|
||||||
"@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q=="],
|
|
||||||
|
|
||||||
"@types/qs": ["@types/[email protected]", "", {}, "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw=="],
|
|
||||||
|
|
||||||
"@types/range-parser": ["@types/[email protected]", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="],
|
|
||||||
|
|
||||||
"@types/retry": ["@types/[email protected]", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
|
|
||||||
|
|
||||||
"@types/send": ["@types/[email protected]", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="],
|
|
||||||
|
|
||||||
"@types/serve-static": ["@types/[email protected]", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="],
|
|
||||||
|
|
||||||
"@vitejs/plugin-react": ["@vitejs/[email protected]", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="],
|
|
||||||
|
|
||||||
"accepts": ["[email protected]", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
|
|
||||||
|
|
||||||
"agent-base": ["[email protected]", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
|
||||||
|
|
||||||
"array-flatten": ["[email protected]", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="],
|
|
||||||
|
|
||||||
"autoprefixer": ["[email protected]", "", { "dependencies": { "browserslist": "^4.28.6", "caniuse-lite": "^1.0.30001806", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA=="],
|
|
||||||
|
|
||||||
"base64-js": ["[email protected]", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
|
||||||
|
|
||||||
"baseline-browser-mapping": ["[email protected]", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A=="],
|
|
||||||
|
|
||||||
"bignumber.js": ["[email protected]", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
|
||||||
|
|
||||||
"body-parser": ["[email protected]", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g=="],
|
|
||||||
|
|
||||||
"browserslist": ["[email protected]", "", { "dependencies": { "baseline-browser-mapping": "^2.10.44", "caniuse-lite": "^1.0.30001806", "electron-to-chromium": "^1.5.393", "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw=="],
|
|
||||||
|
|
||||||
"buffer-equal-constant-time": ["[email protected]", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
|
|
||||||
|
|
||||||
"bytes": ["[email protected]", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
|
||||||
|
|
||||||
"call-bind-apply-helpers": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
|
||||||
|
|
||||||
"call-bound": ["[email protected]", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
|
||||||
|
|
||||||
"caniuse-lite": ["[email protected]", "", {}, "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw=="],
|
|
||||||
|
|
||||||
"content-disposition": ["[email protected]", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="],
|
|
||||||
|
|
||||||
"content-type": ["[email protected]", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
|
||||||
|
|
||||||
"convert-source-map": ["[email protected]", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
|
||||||
|
|
||||||
"cookie": ["[email protected]", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
|
||||||
|
|
||||||
"cookie-signature": ["[email protected]", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="],
|
|
||||||
|
|
||||||
"data-uri-to-buffer": ["[email protected]", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
|
||||||
|
|
||||||
"debug": ["[email protected]", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
|
||||||
|
|
||||||
"depd": ["[email protected]", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
|
||||||
|
|
||||||
"destroy": ["[email protected]", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="],
|
|
||||||
|
|
||||||
"detect-libc": ["[email protected]", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
|
||||||
|
|
||||||
"dotenv": ["[email protected]", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
|
|
||||||
|
|
||||||
"dunder-proto": ["[email protected]", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
|
||||||
|
|
||||||
"ecdsa-sig-formatter": ["[email protected]", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
|
||||||
|
|
||||||
"ee-first": ["[email protected]", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
|
||||||
|
|
||||||
"electron-to-chromium": ["[email protected]", "", {}, "sha512-7zt9Aw+SrmxLWLN0zhaTWZQiCdryLVrYTq5R7iZakLvi2UQPYMMsROYV/2qVCzMeCiSXHwKOU+sZ4zOVVlrtKA=="],
|
|
||||||
|
|
||||||
"encodeurl": ["[email protected]", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
|
||||||
|
|
||||||
"enhanced-resolve": ["[email protected]", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ=="],
|
|
||||||
|
|
||||||
"es-define-property": ["[email protected]", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
|
||||||
|
|
||||||
"es-errors": ["[email protected]", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
|
||||||
|
|
||||||
"es-object-atoms": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="],
|
|
||||||
|
|
||||||
"esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
|
|
||||||
|
|
||||||
"escalade": ["[email protected]", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
|
||||||
|
|
||||||
"escape-html": ["[email protected]", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
|
||||||
|
|
||||||
"etag": ["[email protected]", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
|
||||||
|
|
||||||
"express": ["[email protected]", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q=="],
|
|
||||||
|
|
||||||
"extend": ["[email protected]", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
|
||||||
|
|
||||||
"fdir": ["[email protected]", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
|
||||||
|
|
||||||
"fetch-blob": ["[email protected]", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
|
||||||
|
|
||||||
"finalhandler": ["[email protected]", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "statuses": "~2.0.2", "unpipe": "~1.0.0" } }, "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg=="],
|
|
||||||
|
|
||||||
"formdata-polyfill": ["[email protected]", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
|
||||||
|
|
||||||
"forwarded": ["[email protected]", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
|
||||||
|
|
||||||
"fraction.js": ["[email protected]", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
|
|
||||||
|
|
||||||
"framer-motion": ["[email protected]", "", { "dependencies": { "motion-dom": "^12.42.2", "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw=="],
|
|
||||||
|
|
||||||
"fresh": ["[email protected]", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="],
|
|
||||||
|
|
||||||
"fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
|
||||||
|
|
||||||
"function-bind": ["[email protected]", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
|
||||||
|
|
||||||
"gaxios": ["[email protected]", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-CUVb4wcYe+771XevyH6HtGmXFAGGKkIC3kswAP8Z1JCe0j80JMaTPZH930DWFrvo0atjh18Arc0pEyUCWa5bfg=="],
|
|
||||||
|
|
||||||
"gcp-metadata": ["[email protected]", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
|
|
||||||
|
|
||||||
"gensync": ["[email protected]", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
|
||||||
|
|
||||||
"get-intrinsic": ["[email protected]", "", { "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": ["[email protected]", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
|
||||||
|
|
||||||
"google-auth-library": ["[email protected]", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-xtvUqvINPhTaBm7nXqlYPcrMHJPm1lCNdSovxnKKhTm+4JsvQ+KGVYJViLoH9Yxu8w+T0Qv5HubzYT9BLrppJg=="],
|
|
||||||
|
|
||||||
"google-logging-utils": ["[email protected]", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
|
|
||||||
|
|
||||||
"gopd": ["[email protected]", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
|
||||||
|
|
||||||
"graceful-fs": ["[email protected]", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
|
||||||
|
|
||||||
"has-symbols": ["[email protected]", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
|
||||||
|
|
||||||
"hasown": ["[email protected]", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="],
|
|
||||||
|
|
||||||
"http-errors": ["[email protected]", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
|
||||||
|
|
||||||
"https-proxy-agent": ["[email protected]", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
|
||||||
|
|
||||||
"iconv-lite": ["[email protected]", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
|
||||||
|
|
||||||
"inherits": ["[email protected]", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
|
||||||
|
|
||||||
"ipaddr.js": ["[email protected]", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
|
||||||
|
|
||||||
"jiti": ["[email protected]", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
|
|
||||||
|
|
||||||
"js-tokens": ["[email protected]", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
|
||||||
|
|
||||||
"jsesc": ["[email protected]", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
|
||||||
|
|
||||||
"json-bigint": ["[email protected]", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
|
|
||||||
|
|
||||||
"json5": ["[email protected]", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
|
||||||
|
|
||||||
"jwa": ["[email protected]", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
|
|
||||||
|
|
||||||
"jws": ["[email protected]", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
|
|
||||||
|
|
||||||
"lightningcss": ["[email protected]", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
|
|
||||||
|
|
||||||
"lightningcss-android-arm64": ["[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
|
|
||||||
|
|
||||||
"lightningcss-darwin-arm64": ["[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
|
|
||||||
|
|
||||||
"lightningcss-darwin-x64": ["[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
|
|
||||||
|
|
||||||
"lightningcss-freebsd-x64": ["[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-arm-gnueabihf": ["[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-arm64-gnu": ["[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-arm64-musl": ["[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-x64-gnu": ["[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-x64-musl": ["[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="],
|
|
||||||
|
|
||||||
"lightningcss-win32-arm64-msvc": ["[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="],
|
|
||||||
|
|
||||||
"lightningcss-win32-x64-msvc": ["[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
|
|
||||||
|
|
||||||
"long": ["[email protected]", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
|
|
||||||
|
|
||||||
"lru-cache": ["[email protected]", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
|
||||||
|
|
||||||
"lucide-react": ["[email protected]", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Z94u6fKT43lKeYHiVyvyR8fT7pwCzDu7RyMPpTvh054+xahSgj4HFQ+NmflvzdXsoAjYGdCguGaFKYuvq0ThCQ=="],
|
|
||||||
|
|
||||||
"magic-string": ["[email protected]", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
|
||||||
|
|
||||||
"math-intrinsics": ["[email protected]", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
|
||||||
|
|
||||||
"media-typer": ["[email protected]", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
|
|
||||||
|
|
||||||
"merge-descriptors": ["[email protected]", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="],
|
|
||||||
|
|
||||||
"methods": ["[email protected]", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="],
|
|
||||||
|
|
||||||
"mime": ["[email protected]", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
|
|
||||||
|
|
||||||
"mime-db": ["[email protected]", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
|
||||||
|
|
||||||
"mime-types": ["[email protected]", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
|
||||||
|
|
||||||
"motion": ["[email protected]", "", { "dependencies": { "framer-motion": "^12.42.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-Atvv11yUKIid41cVrRBDVX5m8tF8kNpExRSlbpt6APClhDjtwQssgFHhQzejxw7/7YYbjHSPKBVbHo05BuJT5Q=="],
|
|
||||||
|
|
||||||
"motion-dom": ["[email protected]", "", { "dependencies": { "motion-utils": "^12.39.0" } }, "sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA=="],
|
|
||||||
|
|
||||||
"motion-utils": ["[email protected]", "", {}, "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ=="],
|
|
||||||
|
|
||||||
"ms": ["[email protected]", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
|
||||||
|
|
||||||
"nanoid": ["[email protected]", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="],
|
|
||||||
|
|
||||||
"negotiator": ["[email protected]", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
|
||||||
|
|
||||||
"node-domexception": ["[email protected]", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
|
||||||
|
|
||||||
"node-fetch": ["[email protected]", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
|
||||||
|
|
||||||
"node-releases": ["[email protected]", "", {}, "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ=="],
|
|
||||||
|
|
||||||
"object-inspect": ["[email protected]", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
|
||||||
|
|
||||||
"on-finished": ["[email protected]", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
|
||||||
|
|
||||||
"p-retry": ["[email protected]", "", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="],
|
|
||||||
|
|
||||||
"parseurl": ["[email protected]", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
|
||||||
|
|
||||||
"path-to-regexp": ["[email protected]", "", {}, "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA=="],
|
|
||||||
|
|
||||||
"picocolors": ["[email protected]", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
|
||||||
|
|
||||||
"picomatch": ["[email protected]", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
|
|
||||||
|
|
||||||
"postcss": ["[email protected]", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ=="],
|
|
||||||
|
|
||||||
"postcss-value-parser": ["[email protected]", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
|
||||||
|
|
||||||
"protobufjs": ["[email protected]", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.1", "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.3.2" } }, "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw=="],
|
|
||||||
|
|
||||||
"proxy-addr": ["[email protected]", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
|
||||||
|
|
||||||
"qs": ["[email protected]", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A=="],
|
|
||||||
|
|
||||||
"range-parser": ["[email protected]", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
|
||||||
|
|
||||||
"raw-body": ["[email protected]", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="],
|
|
||||||
|
|
||||||
"react": ["[email protected]", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="],
|
|
||||||
|
|
||||||
"react-dom": ["[email protected]", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.8" } }, "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ=="],
|
|
||||||
|
|
||||||
"react-refresh": ["[email protected]", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
|
|
||||||
|
|
||||||
"retry": ["[email protected]", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
|
|
||||||
|
|
||||||
"rollup": ["[email protected]", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.62.2", "@rollup/rollup-android-arm64": "4.62.2", "@rollup/rollup-darwin-arm64": "4.62.2", "@rollup/rollup-darwin-x64": "4.62.2", "@rollup/rollup-freebsd-arm64": "4.62.2", "@rollup/rollup-freebsd-x64": "4.62.2", "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", "@rollup/rollup-linux-arm-musleabihf": "4.62.2", "@rollup/rollup-linux-arm64-gnu": "4.62.2", "@rollup/rollup-linux-arm64-musl": "4.62.2", "@rollup/rollup-linux-loong64-gnu": "4.62.2", "@rollup/rollup-linux-loong64-musl": "4.62.2", "@rollup/rollup-linux-ppc64-gnu": "4.62.2", "@rollup/rollup-linux-ppc64-musl": "4.62.2", "@rollup/rollup-linux-riscv64-gnu": "4.62.2", "@rollup/rollup-linux-riscv64-musl": "4.62.2", "@rollup/rollup-linux-s390x-gnu": "4.62.2", "@rollup/rollup-linux-x64-gnu": "4.62.2", "@rollup/rollup-linux-x64-musl": "4.62.2", "@rollup/rollup-openbsd-x64": "4.62.2", "@rollup/rollup-openharmony-arm64": "4.62.2", "@rollup/rollup-win32-arm64-msvc": "4.62.2", "@rollup/rollup-win32-ia32-msvc": "4.62.2", "@rollup/rollup-win32-x64-gnu": "4.62.2", "@rollup/rollup-win32-x64-msvc": "4.62.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA=="],
|
|
||||||
|
|
||||||
"safe-buffer": ["[email protected]", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
|
||||||
|
|
||||||
"safer-buffer": ["[email protected]", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
|
||||||
|
|
||||||
"scheduler": ["[email protected]", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
|
||||||
|
|
||||||
"semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
|
||||||
|
|
||||||
"send": ["[email protected]", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="],
|
|
||||||
|
|
||||||
"serve-static": ["[email protected]", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="],
|
|
||||||
|
|
||||||
"setprototypeof": ["[email protected]", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
|
||||||
|
|
||||||
"side-channel": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="],
|
|
||||||
|
|
||||||
"side-channel-list": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="],
|
|
||||||
|
|
||||||
"side-channel-map": ["[email protected]", "", { "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": ["[email protected]", "", { "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=="],
|
|
||||||
|
|
||||||
"source-map-js": ["[email protected]", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
|
||||||
|
|
||||||
"statuses": ["[email protected]", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
|
||||||
|
|
||||||
"tailwindcss": ["[email protected]", "", {}, "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ=="],
|
|
||||||
|
|
||||||
"tapable": ["[email protected]", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="],
|
|
||||||
|
|
||||||
"tinyglobby": ["[email protected]", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
|
|
||||||
|
|
||||||
"toidentifier": ["[email protected]", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
|
||||||
|
|
||||||
"tslib": ["[email protected]", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
|
||||||
|
|
||||||
"tsx": ["[email protected]", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ=="],
|
|
||||||
|
|
||||||
"type-is": ["[email protected]", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
|
|
||||||
|
|
||||||
"typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
|
||||||
|
|
||||||
"undici-types": ["[email protected]", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
|
||||||
|
|
||||||
"unpipe": ["[email protected]", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
|
||||||
|
|
||||||
"update-browserslist-db": ["[email protected]", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
|
||||||
|
|
||||||
"utils-merge": ["[email protected]", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
|
|
||||||
|
|
||||||
"vary": ["[email protected]", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
|
||||||
|
|
||||||
"vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="],
|
|
||||||
|
|
||||||
"web-streams-polyfill": ["[email protected]", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
|
|
||||||
|
|
||||||
"ws": ["[email protected]", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="],
|
|
||||||
|
|
||||||
"yallist": ["[email protected]", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
|
||||||
|
|
||||||
"yaml": ["[email protected]", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="],
|
|
||||||
|
|
||||||
"@babel/core/debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
|
||||||
|
|
||||||
"@babel/traverse/debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/[email protected]", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/[email protected]", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/tslib": ["[email protected]", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
|
||||||
|
|
||||||
"https-proxy-agent/debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
|
||||||
|
|
||||||
"send/ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
|
||||||
|
|
||||||
"tsx/esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="],
|
|
||||||
|
|
||||||
"@babel/core/debug/ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
|
||||||
|
|
||||||
"@babel/traverse/debug/ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
|
||||||
|
|
||||||
"https-proxy-agent/debug/ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/android-x64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/linux-arm": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/linux-x64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/[email protected]", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/[email protected]", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/[email protected]", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="],
|
|
||||||
|
|
||||||
"tsx/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Generated
+2
-407
@@ -8,7 +8,6 @@
|
|||||||
"name": "react-example",
|
"name": "react-example",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@google/genai": "^2.4.0",
|
|
||||||
"@tailwindcss/vite": "^4.1.14",
|
"@tailwindcss/vite": "^4.1.14",
|
||||||
"@vitejs/plugin-react": "^5.0.4",
|
"@vitejs/plugin-react": "^5.0.4",
|
||||||
"ajv": "8.20.0",
|
"ajv": "8.20.0",
|
||||||
@@ -712,30 +711,6 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@google/genai": {
|
|
||||||
"version": "2.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-2.13.0.tgz",
|
|
||||||
"integrity": "sha512-GM7C8Kaomvjz05x5JEO6+l3d/pciL9LxAG9dUjJLD7nTPZ9X0Cfsf2Z7eET6UjgWyUmxXCHtYnQoQ77F9+ZIOQ==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"google-auth-library": "^10.3.0",
|
|
||||||
"p-retry": "^4.6.2",
|
|
||||||
"protobufjs": "^7.5.4",
|
|
||||||
"ws": "^8.18.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@modelcontextprotocol/sdk": "^1.25.2"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@modelcontextprotocol/sdk": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.13",
|
"version": "0.3.13",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||||
@@ -781,63 +756,6 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@protobufjs/aspromise": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
|
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/@protobufjs/base64": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
|
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/@protobufjs/codegen": {
|
|
||||||
"version": "2.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz",
|
|
||||||
"integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==",
|
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/@protobufjs/eventemitter": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==",
|
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/@protobufjs/fetch": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==",
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"@protobufjs/aspromise": "^1.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@protobufjs/float": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
|
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/@protobufjs/path": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
|
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/@protobufjs/pool": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
|
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/@protobufjs/utf8": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==",
|
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/@rolldown/pluginutils": {
|
"node_modules/@rolldown/pluginutils": {
|
||||||
"version": "1.0.0-rc.3",
|
"version": "1.0.0-rc.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz",
|
||||||
@@ -1649,6 +1567,7 @@
|
|||||||
"version": "22.20.1",
|
"version": "22.20.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz",
|
||||||
"integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==",
|
"integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
@@ -1668,12 +1587,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/retry": {
|
|
||||||
"version": "0.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
|
|
||||||
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@types/send": {
|
"node_modules/@types/send": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
|
||||||
@@ -1740,15 +1653,6 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/agent-base": {
|
|
||||||
"version": "7.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
|
||||||
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ajv": {
|
"node_modules/ajv": {
|
||||||
"version": "8.20.0",
|
"version": "8.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
|
||||||
@@ -1808,26 +1712,6 @@
|
|||||||
"postcss": "^8.1.0"
|
"postcss": "^8.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/base64-js": {
|
|
||||||
"version": "1.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
|
||||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/feross"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "patreon",
|
|
||||||
"url": "https://www.patreon.com/feross"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "consulting",
|
|
||||||
"url": "https://feross.org/support"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/baseline-browser-mapping": {
|
"node_modules/baseline-browser-mapping": {
|
||||||
"version": "2.11.1",
|
"version": "2.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.1.tgz",
|
||||||
@@ -1840,15 +1724,6 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bignumber.js": {
|
|
||||||
"version": "9.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
|
|
||||||
"integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "1.20.6",
|
"version": "1.20.6",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz",
|
||||||
@@ -1921,12 +1796,6 @@
|
|||||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/buffer-equal-constant-time": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
@@ -2036,15 +1905,6 @@
|
|||||||
"integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
|
"integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/data-uri-to-buffer": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
|
||||||
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
@@ -2116,15 +1976,6 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ecdsa-sig-formatter": {
|
|
||||||
"version": "1.0.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
|
||||||
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"safe-buffer": "^5.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
@@ -2315,12 +2166,6 @@
|
|||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/extend": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
@@ -2360,29 +2205,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fetch-blob": {
|
|
||||||
"version": "3.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
|
||||||
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/jimmywarting"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "paypal",
|
|
||||||
"url": "https://paypal.me/jimmywarting"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"node-domexception": "^1.0.0",
|
|
||||||
"web-streams-polyfill": "^3.0.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^12.20 || >= 14.13"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/finalhandler": {
|
"node_modules/finalhandler": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
|
||||||
@@ -2416,18 +2238,6 @@
|
|||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/formdata-polyfill": {
|
|
||||||
"version": "4.0.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
|
||||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"fetch-blob": "^3.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12.20.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
@@ -2510,34 +2320,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/gaxios": {
|
|
||||||
"version": "7.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.3.0.tgz",
|
|
||||||
"integrity": "sha512-RB5vLV+vvQeoFPCX4QMK6/hjVkbIamPp1QSUD0CiZcnj12qbpiL+pLbYtgD+oZkWl0tl9z+o2Utp+MpM3QRhBA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"extend": "^3.0.2",
|
|
||||||
"https-proxy-agent": "^7.0.1",
|
|
||||||
"node-fetch": "^3.3.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gcp-metadata": {
|
|
||||||
"version": "8.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz",
|
|
||||||
"integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"gaxios": "^7.0.0",
|
|
||||||
"google-logging-utils": "^1.0.0",
|
|
||||||
"json-bigint": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gensync": {
|
"node_modules/gensync": {
|
||||||
"version": "1.0.0-beta.2",
|
"version": "1.0.0-beta.2",
|
||||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||||
@@ -2584,32 +2366,6 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/google-auth-library": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-xtvUqvINPhTaBm7nXqlYPcrMHJPm1lCNdSovxnKKhTm+4JsvQ+KGVYJViLoH9Yxu8w+T0Qv5HubzYT9BLrppJg==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"base64-js": "^1.3.0",
|
|
||||||
"ecdsa-sig-formatter": "^1.0.11",
|
|
||||||
"gaxios": "^7.1.4",
|
|
||||||
"gcp-metadata": "8.1.2",
|
|
||||||
"google-logging-utils": "1.1.3",
|
|
||||||
"jws": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/google-logging-utils": {
|
|
||||||
"version": "1.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
|
|
||||||
"integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gopd": {
|
"node_modules/gopd": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||||
@@ -2672,19 +2428,6 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/https-proxy-agent": {
|
|
||||||
"version": "7.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
|
||||||
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"agent-base": "^7.1.2",
|
|
||||||
"debug": "4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
@@ -2739,15 +2482,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/json-bigint": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"bignumber.js": "^9.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/json-schema-traverse": {
|
"node_modules/json-schema-traverse": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
@@ -2766,27 +2500,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jwa": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"buffer-equal-constant-time": "^1.0.1",
|
|
||||||
"ecdsa-sig-formatter": "1.0.11",
|
|
||||||
"safe-buffer": "^5.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jws": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
|
|
||||||
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"jwa": "^2.0.1",
|
|
||||||
"safe-buffer": "^5.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/lightningcss": {
|
"node_modules/lightningcss": {
|
||||||
"version": "1.32.0",
|
"version": "1.32.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
|
||||||
@@ -3068,12 +2781,6 @@
|
|||||||
"url": "https://opencollective.com/liquidjs"
|
"url": "https://opencollective.com/liquidjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/long": {
|
|
||||||
"version": "5.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
|
||||||
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||||
@@ -3244,44 +2951,6 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-domexception": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
|
||||||
"deprecated": "Use your platform's native DOMException instead",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/jimmywarting"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://paypal.me/jimmywarting"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-fetch": {
|
|
||||||
"version": "3.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
|
|
||||||
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"data-uri-to-buffer": "^4.0.0",
|
|
||||||
"fetch-blob": "^3.1.4",
|
|
||||||
"formdata-polyfill": "^4.0.10"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/node-fetch"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.51",
|
"version": "2.0.51",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz",
|
||||||
@@ -3315,19 +2984,6 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/p-retry": {
|
|
||||||
"version": "4.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
|
|
||||||
"integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/retry": "0.12.0",
|
|
||||||
"retry": "^0.13.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/parseurl": {
|
"node_modules/parseurl": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
@@ -3396,29 +3052,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/protobufjs": {
|
|
||||||
"version": "7.6.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz",
|
|
||||||
"integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"@protobufjs/aspromise": "^1.1.2",
|
|
||||||
"@protobufjs/base64": "^1.1.2",
|
|
||||||
"@protobufjs/codegen": "^2.0.5",
|
|
||||||
"@protobufjs/eventemitter": "^1.1.1",
|
|
||||||
"@protobufjs/fetch": "^1.1.1",
|
|
||||||
"@protobufjs/float": "^1.0.2",
|
|
||||||
"@protobufjs/path": "^1.1.2",
|
|
||||||
"@protobufjs/pool": "^1.1.0",
|
|
||||||
"@protobufjs/utf8": "^1.1.1",
|
|
||||||
"@types/node": ">=13.7.0",
|
|
||||||
"long": "^5.3.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/proxy-addr": {
|
"node_modules/proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
@@ -3511,15 +3144,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/retry": {
|
|
||||||
"version": "0.13.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
|
||||||
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.62.2",
|
"version": "4.62.2",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
|
||||||
@@ -4313,6 +3937,7 @@
|
|||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
@@ -4446,36 +4071,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/web-streams-polyfill": {
|
|
||||||
"version": "3.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
|
||||||
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ws": {
|
|
||||||
"version": "8.21.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz",
|
|
||||||
"integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"bufferutil": "^4.0.1",
|
|
||||||
"utf-8-validate": ">=5.0.2"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"bufferutil": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"utf-8-validate": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||||
|
|||||||
+2
-2
@@ -3,6 +3,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"packageManager": "[email protected]",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tsx server.ts",
|
"dev": "tsx server.ts",
|
||||||
"build": "vite build && esbuild server.ts --bundle --platform=node --format=cjs --packages=external --sourcemap --outfile=dist/server.cjs",
|
"build": "vite build && esbuild server.ts --bundle --platform=node --format=cjs --packages=external --sourcemap --outfile=dist/server.cjs",
|
||||||
@@ -10,10 +11,9 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"clean": "rm -rf dist server.js",
|
"clean": "rm -rf dist server.js",
|
||||||
"lint": "tsc --noEmit",
|
"lint": "tsc --noEmit",
|
||||||
"test": "node --import tsx --test src/backend/buildEngine.test.ts"
|
"test": "node --import tsx --test src/backend/buildEngine.test.ts src/backend/giteaDiscovery.test.ts src/backend/releaseLifecycle.test.ts src/backend/repositorySnapshot.test.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@google/genai": "^2.4.0",
|
|
||||||
"@tailwindcss/vite": "^4.1.14",
|
"@tailwindcss/vite": "^4.1.14",
|
||||||
"@vitejs/plugin-react": "^5.0.4",
|
"@vitejs/plugin-react": "^5.0.4",
|
||||||
"ajv": "8.20.0",
|
"ajv": "8.20.0",
|
||||||
|
|||||||
@@ -1,575 +1,33 @@
|
|||||||
|
import "dotenv/config";
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import path from "path";
|
import path from "node:path";
|
||||||
import fs from "fs";
|
|
||||||
import { createServer as createViteServer } from "vite";
|
import { createServer as createViteServer } from "vite";
|
||||||
import { store } from "./src/backend/store";
|
import { contentRouter } from "./src/backend/http/contentRouter";
|
||||||
import { CredentialRef } from "./src/types";
|
import { controlRouter } from "./src/backend/http/controlRouter";
|
||||||
import YAML from "yaml";
|
import { gitSourceRouter } from "./src/backend/http/gitSourceRouter";
|
||||||
import { exec } from "child_process";
|
import { previewRouter } from "./src/backend/http/previewRouter";
|
||||||
import { promisify } from "util";
|
|
||||||
|
|
||||||
const execAsync = promisify(exec);
|
const parsePort = (value: string): number => {
|
||||||
|
const port = Number.parseInt(value, 10);
|
||||||
async function startServer() {
|
if (!Number.isInteger(port) || port < 1 || port > 65_535) {
|
||||||
const app = express();
|
throw new Error(`Invalid PORT value: ${value}`);
|
||||||
const port = Number.parseInt(process.env.PORT ?? "3000", 10);
|
|
||||||
const host = process.env.HOST ?? "0.0.0.0";
|
|
||||||
|
|
||||||
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
||||||
throw new Error(`Invalid PORT value: ${process.env.PORT}`);
|
|
||||||
}
|
}
|
||||||
|
return port;
|
||||||
|
};
|
||||||
|
|
||||||
app.use(express.json());
|
async function startServer(): Promise<void> {
|
||||||
|
const app = express();
|
||||||
|
const port = parsePort(process.env.PORT ?? "3000");
|
||||||
|
const host = process.env.HOST ?? "127.0.0.1";
|
||||||
|
|
||||||
// --- API ROUTES ---
|
app.disable("x-powered-by");
|
||||||
|
app.set("trust proxy", process.env.TRUST_PROXY ?? "loopback");
|
||||||
|
app.use(express.json({ limit: "1mb" }));
|
||||||
|
app.use("/api", controlRouter);
|
||||||
|
app.use("/api", gitSourceRouter);
|
||||||
|
app.use("/api", contentRouter);
|
||||||
|
app.use("/api", previewRouter);
|
||||||
|
|
||||||
// Health
|
|
||||||
app.get("/api/health", (req, res) => {
|
|
||||||
res.json({ status: "ok", service: "labyricorn-site-builder" });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Site Config
|
|
||||||
app.get("/api/site-config", (req, res) => {
|
|
||||||
res.json(store.siteConfig);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.put("/api/site-config", (_req, res) => {
|
|
||||||
res.status(405).json({
|
|
||||||
code: "E_CONFIG_READ_ONLY",
|
|
||||||
message: "Site configuration is Git-owned and read-only in v1.",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- GITEA DISCOVERY ---
|
|
||||||
app.post("/api/gitea/discover", async (req, res) => {
|
|
||||||
store.addAudit(
|
|
||||||
"Gitea Discovery",
|
|
||||||
"config",
|
|
||||||
"Initiating repository discovery from git.labyricorn.com",
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Real backend operation: Attempt to connect to the Gitea instance
|
|
||||||
const response = await fetch(
|
|
||||||
"https://git.labyricorn.com/api/v1/repos/search",
|
|
||||||
{
|
|
||||||
timeout: 5000,
|
|
||||||
} as any,
|
|
||||||
).catch((e) => {
|
|
||||||
throw new Error(
|
|
||||||
`Connection to git.labyricorn.com failed: ${e.message}`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Gitea returned HTTP ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
const repos = data.data || [];
|
|
||||||
|
|
||||||
let added = 0;
|
|
||||||
for (const repo of repos) {
|
|
||||||
if (
|
|
||||||
!store.gitSources.find(
|
|
||||||
(s) =>
|
|
||||||
s.repository === repo.clone_url || s.repository === repo.ssh_url,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
store.gitSources.push({
|
|
||||||
id: repo.name,
|
|
||||||
repository: repo.clone_url,
|
|
||||||
ref: repo.default_branch || "main",
|
|
||||||
branch: repo.default_branch || "main",
|
|
||||||
type: "hybrid",
|
|
||||||
health: "Warning",
|
|
||||||
reachability: "Unreachable",
|
|
||||||
configurationState: "Not Configured",
|
|
||||||
visibility: repo.private ? "Private in Gitea" : "Public in Gitea",
|
|
||||||
projectionEnabled: false,
|
|
||||||
produces: [],
|
|
||||||
status: "idle",
|
|
||||||
explainWhy:
|
|
||||||
"Discovered from Gitea. Awaiting initial synchronization.",
|
|
||||||
required: false,
|
|
||||||
credential: "none",
|
|
||||||
});
|
|
||||||
added++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
discoveredCount: repos.length,
|
|
||||||
addedCount: added,
|
|
||||||
message: `Successfully discovered ${repos.length} repositories from Gitea.`,
|
|
||||||
});
|
|
||||||
} catch (e: any) {
|
|
||||||
store.addAudit("Gitea Discovery Failed", "config", e.message, "failure");
|
|
||||||
res.status(502).json({ error: e.message, success: false });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Git Sources
|
|
||||||
app.get("/api/git-sources", (req, res) => {
|
|
||||||
res.json(store.gitSources);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/git-sources", async (req, res) => {
|
|
||||||
const newSource = req.body;
|
|
||||||
|
|
||||||
// Phase 2 Defaults
|
|
||||||
newSource.type = newSource.type || "hybrid";
|
|
||||||
newSource.health = "Warning";
|
|
||||||
newSource.reachability = "Unreachable";
|
|
||||||
newSource.configurationState = "Not Configured";
|
|
||||||
newSource.visibility = "Private in Gitea";
|
|
||||||
newSource.projectionEnabled = false;
|
|
||||||
newSource.produces = [];
|
|
||||||
newSource.explainWhy = "Awaiting initial synchronization.";
|
|
||||||
newSource.status = "syncing";
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
newSource.repository.startsWith("http") ||
|
|
||||||
newSource.repository.startsWith("git@") ||
|
|
||||||
newSource.repository.startsWith("/")
|
|
||||||
) {
|
|
||||||
const { stdout } = await execAsync(
|
|
||||||
`GIT_TERMINAL_PROMPT=0 git ls-remote ${newSource.repository} ${newSource.ref || "HEAD"}`,
|
|
||||||
{ timeout: 10000 },
|
|
||||||
);
|
|
||||||
const match = stdout.trim().split("\\t")[0];
|
|
||||||
if (match) {
|
|
||||||
newSource.status = "connected";
|
|
||||||
newSource.reachability = "Healthy";
|
|
||||||
newSource.lastResolvedCommit = match;
|
|
||||||
newSource.lastSyncedAt = new Date().toISOString();
|
|
||||||
newSource.explainWhy =
|
|
||||||
"Connected to repository, but missing .labyricorn/site.yml metadata.";
|
|
||||||
} else {
|
|
||||||
newSource.status = "error";
|
|
||||||
newSource.explainWhy = "Ref not found in repository.";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newSource.status = "offline";
|
|
||||||
newSource.explainWhy =
|
|
||||||
"Backend Not Yet Implemented for non-http/git repositories. (Development Mode)";
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
newSource.status = "error";
|
|
||||||
newSource.explainWhy = `Git ls-remote failed: ${e.message.split("\\n")[0]}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
store.gitSources.push(newSource);
|
|
||||||
store.addAudit(
|
|
||||||
"Add Git Source",
|
|
||||||
"source",
|
|
||||||
`Added Git source ${newSource.id} (${newSource.repository})`,
|
|
||||||
);
|
|
||||||
res.json({ success: true, source: newSource });
|
|
||||||
});
|
|
||||||
|
|
||||||
app.put("/api/git-sources/:id", (req, res) => {
|
|
||||||
const { id } = req.params;
|
|
||||||
const idx = store.gitSources.findIndex((s) => s.id === id);
|
|
||||||
if (idx !== -1) {
|
|
||||||
store.gitSources[idx] = { ...store.gitSources[idx], ...req.body };
|
|
||||||
store.addAudit("Update Git Source", "source", `Updated Git source ${id}`);
|
|
||||||
res.json({ success: true, source: store.gitSources[idx] });
|
|
||||||
} else {
|
|
||||||
res.status(404).json({ error: "Source not found" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.delete("/api/git-sources/:id", (req, res) => {
|
|
||||||
const { id } = req.params;
|
|
||||||
store.gitSources = store.gitSources.filter((s) => s.id !== id);
|
|
||||||
store.addAudit("Delete Git Source", "source", `Removed Git source ${id}`);
|
|
||||||
res.json({ success: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/git-sources/:id/test", async (req, res) => {
|
|
||||||
const { id } = req.params;
|
|
||||||
const src = store.gitSources.find((s) => s.id === id);
|
|
||||||
if (!src) return res.status(404).json({ error: "Source not found" });
|
|
||||||
|
|
||||||
src.status = "syncing";
|
|
||||||
src.explainWhy = "Synchronization running...";
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
src.repository.startsWith("http") ||
|
|
||||||
src.repository.startsWith("git@") ||
|
|
||||||
src.repository.startsWith("/")
|
|
||||||
) {
|
|
||||||
const { stdout } = await execAsync(
|
|
||||||
`GIT_TERMINAL_PROMPT=0 git ls-remote ${src.repository} ${src.ref || "HEAD"}`,
|
|
||||||
{ timeout: 10000 },
|
|
||||||
);
|
|
||||||
const match = stdout.trim().split("\\t")[0];
|
|
||||||
if (match) {
|
|
||||||
src.status = "connected";
|
|
||||||
src.reachability = "Healthy";
|
|
||||||
src.lastResolvedCommit = match;
|
|
||||||
src.lastSyncedAt = new Date().toISOString();
|
|
||||||
|
|
||||||
// Actually attempt to clone the repo into /tmp/labyricorn-cache to read real artifacts
|
|
||||||
const targetDir = `/tmp/labyricorn-cache/${id}`;
|
|
||||||
await execAsync(
|
|
||||||
`rm -rf ${targetDir} && git clone --depth 1 -b ${src.ref || "main"} ${src.repository} ${targetDir}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check for site.yml
|
|
||||||
if (fs.existsSync(`${targetDir}/.labyricorn/site.yml`)) {
|
|
||||||
src.configurationState = "Valid";
|
|
||||||
src.projectionEnabled = true;
|
|
||||||
src.explainWhy =
|
|
||||||
"Repository synchronized and valid site.yml detected.";
|
|
||||||
src.health = "Healthy";
|
|
||||||
} else {
|
|
||||||
src.configurationState = "Not Configured";
|
|
||||||
src.projectionEnabled = false;
|
|
||||||
src.explainWhy =
|
|
||||||
"Repository synchronized, but missing .labyricorn/site.yml metadata.";
|
|
||||||
src.health = "Warning";
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.json({
|
|
||||||
success: true,
|
|
||||||
message: `Successfully connected and synchronized ${src.repository}!`,
|
|
||||||
resolvedCommit: src.lastResolvedCommit,
|
|
||||||
syncedAt: src.lastSyncedAt,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
src.status = "error";
|
|
||||||
src.explainWhy = "Ref not found in repository.";
|
|
||||||
return res.status(400).json({ error: src.explainWhy });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
src.status = "offline";
|
|
||||||
src.explainWhy =
|
|
||||||
"Backend Not Yet Implemented for non-http/git repositories. (Development Mode)";
|
|
||||||
return res.status(501).json({ error: src.explainWhy });
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
src.status = "error";
|
|
||||||
src.health = "Invalid";
|
|
||||||
src.explainWhy = `Git sync failed: ${e.message.split("\\n")[0]}`;
|
|
||||||
return res.status(400).json({ error: src.explainWhy });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/api/git-sources/:id/tree", async (req, res) => {
|
|
||||||
const { id } = req.params;
|
|
||||||
const src = store.gitSources.find((s) => s.id === id);
|
|
||||||
if (!src) return res.status(404).json({ error: "Source not found" });
|
|
||||||
|
|
||||||
const targetDir = `/tmp/labyricorn-cache/${id}`;
|
|
||||||
if (!fs.existsSync(targetDir)) {
|
|
||||||
return res.json({ sourceId: id, files: [] });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { stdout } = await execAsync(
|
|
||||||
`find . -type f -not -path "*/.git/*" | head -n 50`,
|
|
||||||
{ cwd: targetDir },
|
|
||||||
);
|
|
||||||
const files = stdout
|
|
||||||
.split("\\n")
|
|
||||||
.filter(Boolean)
|
|
||||||
.map((f) => {
|
|
||||||
const path = f.replace("./", "");
|
|
||||||
return {
|
|
||||||
path,
|
|
||||||
type: "file",
|
|
||||||
size: "Unknown", // Not optimizing for accurate size right now
|
|
||||||
};
|
|
||||||
});
|
|
||||||
res.json({ sourceId: id, files });
|
|
||||||
} catch (e) {
|
|
||||||
res.json({ sourceId: id, files: [] });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Style Packages & Instances
|
|
||||||
app.get("/api/style-packages", (req, res) => {
|
|
||||||
res.json(store.stylePackages);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/api/style-instances", (req, res) => {
|
|
||||||
res.json(store.styleInstances);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/style-instances", (req, res) => {
|
|
||||||
const inst = req.body;
|
|
||||||
store.styleInstances.push(inst);
|
|
||||||
store.addAudit(
|
|
||||||
"Add Style Instance",
|
|
||||||
"config",
|
|
||||||
`Created style instance '${inst.id}' using '${inst.uses}'`,
|
|
||||||
);
|
|
||||||
res.json({ success: true, instance: inst });
|
|
||||||
});
|
|
||||||
|
|
||||||
app.put("/api/style-instances/:id", (req, res) => {
|
|
||||||
const { id } = req.params;
|
|
||||||
const idx = store.styleInstances.findIndex((i) => i.id === id);
|
|
||||||
if (idx !== -1) {
|
|
||||||
store.styleInstances[idx] = { ...store.styleInstances[idx], ...req.body };
|
|
||||||
store.addAudit(
|
|
||||||
"Update Style Instance",
|
|
||||||
"config",
|
|
||||||
`Updated style instance '${id}'`,
|
|
||||||
);
|
|
||||||
res.json({ success: true, instance: store.styleInstances[idx] });
|
|
||||||
} else {
|
|
||||||
res.status(404).json({ error: "Instance not found" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Content
|
|
||||||
app.get("/api/content", (req, res) => {
|
|
||||||
res.json(store.contentItems);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/content/discover", async (req, res) => {
|
|
||||||
store.addAudit(
|
|
||||||
"Discover Content",
|
|
||||||
"config",
|
|
||||||
"Executed content discovery scan across all Git sources",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Clear old items
|
|
||||||
store.contentItems = [];
|
|
||||||
|
|
||||||
for (const source of store.gitSources) {
|
|
||||||
if (source.health !== "Healthy" || !source.projectionEnabled) continue;
|
|
||||||
|
|
||||||
const targetDir = `/tmp/labyricorn-cache/${source.id}`;
|
|
||||||
if (!fs.existsSync(targetDir)) continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Find markdown files
|
|
||||||
const { stdout } = await execAsync(
|
|
||||||
`find . -type f -name "*.md" -not -path "*/.git/*"`,
|
|
||||||
{ cwd: targetDir },
|
|
||||||
);
|
|
||||||
const mdFiles = stdout.split("\\n").filter(Boolean);
|
|
||||||
|
|
||||||
mdFiles.forEach((file, idx) => {
|
|
||||||
const relativePath = file.replace("./", "").trim();
|
|
||||||
const title =
|
|
||||||
relativePath.split("/").pop()?.replace(".md", "").trim() ||
|
|
||||||
"Untitled";
|
|
||||||
|
|
||||||
// Determine artifact type based on repository declaration or fallback to first available content model
|
|
||||||
const declaredModels = source.produces || [];
|
|
||||||
const defaultModel =
|
|
||||||
store.siteConfig.contentModels.length > 0
|
|
||||||
? store.siteConfig.contentModels[0].id
|
|
||||||
: "page";
|
|
||||||
const artifactType =
|
|
||||||
declaredModels.length > 0 ? declaredModels[0] : defaultModel;
|
|
||||||
const slug = title.toLowerCase();
|
|
||||||
|
|
||||||
const contentModelDef = store.siteConfig.contentModels.find(
|
|
||||||
(m) => m.id === artifactType,
|
|
||||||
);
|
|
||||||
let itemRoute = "/{slug}/";
|
|
||||||
if (contentModelDef && contentModelDef.routing && contentModelDef.routing.detail) {
|
|
||||||
itemRoute = contentModelDef.routing.detail;
|
|
||||||
} else {
|
|
||||||
const navEntry = store.siteConfig.navigation.find(
|
|
||||||
(nav) => nav.contentModel === artifactType,
|
|
||||||
);
|
|
||||||
itemRoute = navEntry ? `${navEntry.route}/{slug}/` : `/${artifactType}s/{slug}/`;
|
|
||||||
}
|
|
||||||
|
|
||||||
store.contentItems.push({
|
|
||||||
id: `art-${source.id}-${idx}`,
|
|
||||||
title: title
|
|
||||||
.replace(/-/g, " ")
|
|
||||||
.replace(/\\b\\w/g, (l) => l.toUpperCase()),
|
|
||||||
slug,
|
|
||||||
published: new Date().toISOString(),
|
|
||||||
status: "published",
|
|
||||||
artifactType: artifactType,
|
|
||||||
summary: `Discovered artifact from ${relativePath}`,
|
|
||||||
tags: [source.id],
|
|
||||||
aliases: [],
|
|
||||||
sourceRepo: source.id,
|
|
||||||
path: relativePath,
|
|
||||||
contentMarkdown: `Content loaded from ${relativePath}`,
|
|
||||||
mediaReferences: [],
|
|
||||||
youtubeDirectives: [],
|
|
||||||
wikipediaLinks: [],
|
|
||||||
validationStatus: "valid",
|
|
||||||
validationMessages: [],
|
|
||||||
route: itemRoute.replace("{slug}", slug).replace("//", "/"),
|
|
||||||
styleInstanceId: "default",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
// No files found or error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
discoveredCount: store.contentItems.length,
|
|
||||||
mediaCount: store.mediaAssets.length,
|
|
||||||
items: store.contentItems,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Media
|
|
||||||
app.get("/api/media", (req, res) => {
|
|
||||||
res.json(store.mediaAssets);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Theme
|
|
||||||
app.get("/api/theme", (req, res) => {
|
|
||||||
res.json(store.themeConfig);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/theme/validate", (_req, res) => {
|
|
||||||
const theme = store.validateTheme();
|
|
||||||
res.status(theme.status === "valid" ? 200 : 422).json({
|
|
||||||
success: theme.status === "valid",
|
|
||||||
theme,
|
|
||||||
commits: theme.commit ? { [theme.sourceId ?? "theme"]: theme.commit } : {},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Builds
|
|
||||||
app.get("/api/builds", (req, res) => {
|
|
||||||
res.json(store.builds);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/builds/trigger", (req, res) => {
|
|
||||||
const newBuild = store.runBuild();
|
|
||||||
res.json(newBuild);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Nginx Local Hosting
|
|
||||||
app.get("/api/nginx", (req, res) => {
|
|
||||||
res.json(store.nginxStatus);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/nginx/test", (req, res) => {
|
|
||||||
store.nginxStatus.configValid = true;
|
|
||||||
store.addAudit(
|
|
||||||
"Test Nginx Config",
|
|
||||||
"nginx",
|
|
||||||
"Executed nginx -t: configuration syntax is ok, test is successful",
|
|
||||||
);
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
message:
|
|
||||||
"nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\nnginx: configuration file /etc/nginx/nginx.conf test is successful",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/nginx/activate", (req, res) => {
|
|
||||||
const { buildId } = req.body;
|
|
||||||
const result = store.activateReleaseLocally(buildId);
|
|
||||||
res.json(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Push Targets
|
|
||||||
app.get("/api/push-targets", (req, res) => {
|
|
||||||
res.json(store.pushTargets);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/push-targets/:id/push", (req, res) => {
|
|
||||||
const { id } = req.params;
|
|
||||||
const { buildId } = req.body;
|
|
||||||
const result = store.pushToTarget(id, buildId);
|
|
||||||
res.json(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/push-targets/:id/test", (req, res) => {
|
|
||||||
const { id } = req.params;
|
|
||||||
const target = store.pushTargets.find((t) => t.id === id);
|
|
||||||
if (!target) return res.status(404).json({ error: "Target not found" });
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
message: `SSH test to ${target.host}:22 succeeded! Verification key fingerprint: SHA256:p92kALm10x9. Rsync binary detected at /usr/bin/rsync.`,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Credentials
|
|
||||||
app.get("/api/credentials", (req, res) => {
|
|
||||||
res.json(store.credentials);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/credentials", (req, res) => {
|
|
||||||
const { name, type, secretValue } = req.body;
|
|
||||||
const id = `secret:${name.toLowerCase().replace(/[^a-z0-9]/g, "-")}`;
|
|
||||||
const masked =
|
|
||||||
type === "ssh_key"
|
|
||||||
? `${secretValue.substring(0, 20)}... (RSA 4096)`
|
|
||||||
: `${secretValue.substring(0, 4)}********************`;
|
|
||||||
|
|
||||||
const cred: CredentialRef = {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
type,
|
|
||||||
maskedValue: masked,
|
|
||||||
createdAt: new Date().toISOString(),
|
|
||||||
lastUsedAt: new Date().toISOString(),
|
|
||||||
usageCount: 0,
|
|
||||||
};
|
|
||||||
store.credentials.push(cred);
|
|
||||||
store.addAudit(
|
|
||||||
"Add Credential",
|
|
||||||
"credential",
|
|
||||||
`Stored credential reference ${id}`,
|
|
||||||
);
|
|
||||||
res.json({ success: true, credential: cred });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Audit Logs
|
|
||||||
app.get("/api/audit-logs", (req, res) => {
|
|
||||||
res.json(store.auditLogs);
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- RELEASE-ONLY SITE PREVIEW ENDPOINT ---
|
|
||||||
app.get("/api/live-site/html", (req, res) => {
|
|
||||||
const rootDir = store.getPreviewDirectory();
|
|
||||||
if (!rootDir) {
|
|
||||||
return res.status(503).json({
|
|
||||||
code: "E_RELEASE_UNAVAILABLE",
|
|
||||||
message: "No verified staging or live release is selected.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let requested: string;
|
|
||||||
try {
|
|
||||||
requested = decodeURIComponent(String(req.query.route ?? "/").split(/[?#]/, 1)[0]);
|
|
||||||
} catch {
|
|
||||||
return res.status(400).json({ code: "E_ROUTE_INVALID" });
|
|
||||||
}
|
|
||||||
if (!requested.startsWith("/") || requested.includes("\\") || requested.split("/").includes("..")) {
|
|
||||||
return res.status(400).json({ code: "E_ROUTE_INVALID" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const relative = requested.replace(/^\/+/, "");
|
|
||||||
const outputFile = relative && path.posix.extname(relative) ? relative : path.posix.join(relative, "index.html");
|
|
||||||
const rootPath = `${path.resolve(rootDir)}${path.sep}`;
|
|
||||||
const target = path.resolve(rootDir, ...outputFile.split("/"));
|
|
||||||
if (!target.startsWith(rootPath)) return res.status(400).json({ code: "E_ROUTE_INVALID" });
|
|
||||||
|
|
||||||
res.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'self'");
|
|
||||||
if (fs.existsSync(target) && fs.statSync(target).isFile()) return res.type(path.extname(target)).send(fs.readFileSync(target));
|
|
||||||
const notFound = path.join(rootDir, "404.html");
|
|
||||||
if (fs.existsSync(notFound)) return res.status(404).type("html").send(fs.readFileSync(notFound));
|
|
||||||
return res.status(404).json({ code: "E_ROUTE_NOT_FOUND" });
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- VITE / PRODUCTION SERVING ---
|
|
||||||
if (process.env.NODE_ENV !== "production") {
|
if (process.env.NODE_ENV !== "production") {
|
||||||
const vite = await createViteServer({
|
const vite = await createViteServer({
|
||||||
server: { middlewareMode: true },
|
server: { middlewareMode: true },
|
||||||
@@ -579,7 +37,7 @@ async function startServer() {
|
|||||||
} else {
|
} else {
|
||||||
const distPath = path.join(process.cwd(), "dist");
|
const distPath = path.join(process.cwd(), "dist");
|
||||||
app.use(express.static(distPath));
|
app.use(express.static(distPath));
|
||||||
app.get("*", (req, res) => {
|
app.get("*", (_req, res) => {
|
||||||
res.sendFile(path.join(distPath, "index.html"));
|
res.sendFile(path.join(distPath, "index.html"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -589,4 +47,7 @@ async function startServer() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
startServer();
|
startServer().catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
process.exitCode = 1;
|
||||||
|
});
|
||||||
|
|||||||
@@ -79,10 +79,14 @@ test("equivalent resolved inputs are byte-identical despite different run IDs",
|
|||||||
assert.equal(first.input.artifactBuildId, second.input.artifactBuildId);
|
assert.equal(first.input.artifactBuildId, second.input.artifactBuildId);
|
||||||
const engine = new BuildEngine(output);
|
const engine = new BuildEngine(output);
|
||||||
const one = engine.build(first.input);
|
const one = engine.build(first.input);
|
||||||
assert.equal(fs.statSync(one.outputDirectory).mode & 0o777, 0o750);
|
if (process.platform !== "win32") {
|
||||||
|
assert.equal(fs.statSync(one.outputDirectory).mode & 0o777, 0o750);
|
||||||
|
}
|
||||||
fs.chmodSync(one.outputDirectory, 0o700);
|
fs.chmodSync(one.outputDirectory, 0o700);
|
||||||
const two = engine.build(second.input);
|
const two = engine.build(second.input);
|
||||||
assert.equal(fs.statSync(two.outputDirectory).mode & 0o777, 0o750);
|
if (process.platform !== "win32") {
|
||||||
|
assert.equal(fs.statSync(two.outputDirectory).mode & 0o777, 0o750);
|
||||||
|
}
|
||||||
assert.equal(one.artifactChecksum, two.artifactChecksum);
|
assert.equal(one.artifactChecksum, two.artifactChecksum);
|
||||||
assert.equal(one.outputDirectory, two.outputDirectory);
|
assert.equal(one.outputDirectory, two.outputDirectory);
|
||||||
assert.deepEqual(one.generatedFiles, two.generatedFiles);
|
assert.deepEqual(one.generatedFiles, two.generatedFiles);
|
||||||
@@ -210,9 +214,17 @@ test("markdown output is escaped and heading IDs are deterministic", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("production sources contain no embedded renderer or synthesized preview fallback", () => {
|
test("production sources contain no embedded renderer or synthesized preview fallback", () => {
|
||||||
assert.equal(fs.readFileSync(path.resolve("src/backend/buildEngine.ts"), "utf8").trim(), 'export * from "./repositoryBuildEngine";');
|
const boundary = fs.readFileSync(path.resolve("src/backend/buildEngine.ts"), "utf8");
|
||||||
|
assert.match(boundary, /AI or other open-schema output is never a release input/);
|
||||||
|
assert.match(boundary, /export \* from ".\/repositoryBuildEngine"/);
|
||||||
const server = fs.readFileSync(path.resolve("server.ts"), "utf8");
|
const server = fs.readFileSync(path.resolve("server.ts"), "utf8");
|
||||||
assert.doesNotMatch(server, /cdn\.tailwindcss\.com|Simple HTML renderer|SITE HEADER/);
|
assert.doesNotMatch(server, /cdn\.tailwindcss\.com|Simple HTML renderer|SITE HEADER/);
|
||||||
assert.match(server, /E_RELEASE_UNAVAILABLE/);
|
assert.doesNotMatch(server, /child_process|execAsync/);
|
||||||
assert.match(server, /E_CONFIG_READ_ONLY/);
|
const preview = fs.readFileSync(path.resolve("src/backend/http/previewRouter.ts"), "utf8");
|
||||||
|
const control = fs.readFileSync(path.resolve("src/backend/http/controlRouter.ts"), "utf8");
|
||||||
|
const storeSource = fs.readFileSync(path.resolve("src/backend/store.ts"), "utf8");
|
||||||
|
assert.match(preview, /E_RELEASE_UNAVAILABLE/);
|
||||||
|
assert.match(control, /E_CONFIG_READ_ONLY/);
|
||||||
|
assert.match(storeSource, /REMOTE_PUBLICATION_IMPLEMENTED = false as const/);
|
||||||
|
assert.doesNotMatch(storeSource, /REMOTE_PUBLICATION_IMPLEMENTED\s*=\s*process\.env/);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
// Security boundary: AI or other open-schema output is never a release input.
|
||||||
export * from "./repositoryBuildEngine";
|
export * from "./repositoryBuildEngine";
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
import { execFile } from "node:child_process";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { promisify } from "node:util";
|
||||||
|
|
||||||
|
const execFileAsync = promisify(execFile);
|
||||||
|
const cacheRoot = path.join(os.tmpdir(), "labyricorn-cache");
|
||||||
|
const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
||||||
|
|
||||||
|
const allowedHosts = (): Set<string> => {
|
||||||
|
const configured = (process.env.GIT_ALLOWED_HOSTS ?? "")
|
||||||
|
.split(",")
|
||||||
|
.map((host) => host.trim().toLowerCase())
|
||||||
|
.filter(Boolean);
|
||||||
|
if (process.env.GITEA_BASE_URL) {
|
||||||
|
configured.push(new URL(process.env.GITEA_BASE_URL).hostname.toLowerCase());
|
||||||
|
}
|
||||||
|
return new Set(configured);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isWithin = (root: string, candidate: string): boolean => {
|
||||||
|
const relative = path.relative(root, candidate);
|
||||||
|
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateLocalRepository = (repository: string): string => {
|
||||||
|
const resolved = path.resolve(repository);
|
||||||
|
const configuredRoots = (process.env.GIT_ALLOWED_LOCAL_ROOTS ?? process.cwd())
|
||||||
|
.split(path.delimiter)
|
||||||
|
.map((entry) => path.resolve(entry));
|
||||||
|
if (!configuredRoots.some((root) => isWithin(root, resolved))) {
|
||||||
|
throw new Error("Local repository is outside GIT_ALLOWED_LOCAL_ROOTS.");
|
||||||
|
}
|
||||||
|
return resolved;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const validateRepository = (repository: string): string => {
|
||||||
|
const value = repository.trim();
|
||||||
|
if (path.isAbsolute(value)) return validateLocalRepository(value);
|
||||||
|
|
||||||
|
if (!value.includes("://")) {
|
||||||
|
const scpLike = /^(?:[^@\s]+@)?([^:/\s]+):(.+)$/.exec(value);
|
||||||
|
if (scpLike) {
|
||||||
|
if (!allowedHosts().has(scpLike[1].toLowerCase())) {
|
||||||
|
throw new Error(`Git host '${scpLike[1]}' is not allow-listed.`);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(value);
|
||||||
|
if (!["https:", "ssh:"].includes(url.protocol) || url.username === "root") {
|
||||||
|
throw new Error("Repository URL must use HTTPS or SSH.");
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(url.protocol === "https:" && (url.username || url.password)) ||
|
||||||
|
(url.protocol === "ssh:" && url.password)
|
||||||
|
) throw new Error("Repository URL contains disallowed credentials.");
|
||||||
|
if (!allowedHosts().has(url.hostname.toLowerCase())) {
|
||||||
|
throw new Error(`Git host '${url.hostname}' is not allow-listed.`);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateRef = (ref: string): string => {
|
||||||
|
const value = ref.trim();
|
||||||
|
if (
|
||||||
|
value.length === 0 ||
|
||||||
|
value.length > 255 ||
|
||||||
|
/[\s~^:?*[\]\\]/.test(value) ||
|
||||||
|
value.includes("..") ||
|
||||||
|
value.includes("@{") ||
|
||||||
|
value.startsWith("-")
|
||||||
|
) {
|
||||||
|
throw new Error("Git ref is invalid.");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateId = (id: string): string => {
|
||||||
|
if (!SAFE_ID.test(id)) throw new Error("Git source ID is invalid.");
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
const gitEnvironment = {
|
||||||
|
...process.env,
|
||||||
|
GIT_TERMINAL_PROMPT: "0",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const resolveRemote = async (
|
||||||
|
repository: string,
|
||||||
|
ref: string,
|
||||||
|
): Promise<string | null> => {
|
||||||
|
const safeRepository = validateRepository(repository);
|
||||||
|
const safeRef = validateRef(ref || "HEAD");
|
||||||
|
const { stdout } = await execFileAsync(
|
||||||
|
"git",
|
||||||
|
["-c", "protocol.ext.allow=never", "ls-remote", "--", safeRepository, safeRef],
|
||||||
|
{ env: gitEnvironment, timeout: 10_000 },
|
||||||
|
);
|
||||||
|
return stdout.trim().split("\t")[0] || null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const synchronizeRepository = async (
|
||||||
|
id: string,
|
||||||
|
repository: string,
|
||||||
|
ref: string,
|
||||||
|
): Promise<string> => {
|
||||||
|
validateId(id);
|
||||||
|
const safeRepository = validateRepository(repository);
|
||||||
|
const safeRef = validateRef(ref || "main");
|
||||||
|
fs.mkdirSync(cacheRoot, { recursive: true });
|
||||||
|
const target = path.resolve(cacheRoot, id);
|
||||||
|
if (!isWithin(cacheRoot, target) || target === cacheRoot) {
|
||||||
|
throw new Error("Git cache target is invalid.");
|
||||||
|
}
|
||||||
|
const temporary = fs.mkdtempSync(path.join(cacheRoot, `.${id}-`));
|
||||||
|
fs.rmSync(temporary, { recursive: true, force: true });
|
||||||
|
try {
|
||||||
|
await execFileAsync(
|
||||||
|
"git",
|
||||||
|
[
|
||||||
|
"-c",
|
||||||
|
"protocol.ext.allow=never",
|
||||||
|
"clone",
|
||||||
|
"--depth",
|
||||||
|
"1",
|
||||||
|
"--branch",
|
||||||
|
safeRef,
|
||||||
|
"--",
|
||||||
|
safeRepository,
|
||||||
|
temporary,
|
||||||
|
],
|
||||||
|
{ env: gitEnvironment, timeout: 60_000 },
|
||||||
|
);
|
||||||
|
fs.rmSync(target, { recursive: true, force: true });
|
||||||
|
fs.renameSync(temporary, target);
|
||||||
|
return target;
|
||||||
|
} catch (error) {
|
||||||
|
fs.rmSync(temporary, { recursive: true, force: true });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const cachedRepositoryPath = (id: string): string => {
|
||||||
|
validateId(id);
|
||||||
|
const target = path.resolve(cacheRoot, id);
|
||||||
|
if (!isWithin(cacheRoot, target) || target === cacheRoot) {
|
||||||
|
throw new Error("Git cache target is invalid.");
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const listRepositoryFiles = (
|
||||||
|
id: string,
|
||||||
|
predicate: (relative: string) => boolean = () => true,
|
||||||
|
maximum = 50,
|
||||||
|
): Array<{ path: string; sizeBytes: number }> => {
|
||||||
|
const root = cachedRepositoryPath(id);
|
||||||
|
if (!fs.existsSync(root)) return [];
|
||||||
|
const files: Array<{ path: string; sizeBytes: number }> = [];
|
||||||
|
const visit = (directory: string): void => {
|
||||||
|
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
||||||
|
if (files.length >= maximum) return;
|
||||||
|
if (entry.name === ".git") continue;
|
||||||
|
const absolute = path.join(directory, entry.name);
|
||||||
|
if (entry.isSymbolicLink()) continue;
|
||||||
|
if (entry.isDirectory()) visit(absolute);
|
||||||
|
if (entry.isFile()) {
|
||||||
|
const relative = path.relative(root, absolute).replace(/\\/g, "/");
|
||||||
|
if (predicate(relative)) {
|
||||||
|
files.push({ path: relative, sizeBytes: fs.statSync(absolute).size });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
visit(root);
|
||||||
|
return files.sort((a, b) => a.path.localeCompare(b.path)).slice(0, maximum);
|
||||||
|
};
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import test from "node:test";
|
||||||
|
import {
|
||||||
|
fetchGiteaRepositories,
|
||||||
|
FixedWindowRateLimiter,
|
||||||
|
loadGiteaDiscoveryConfig,
|
||||||
|
toGitSource,
|
||||||
|
} from "./http/giteaDiscovery";
|
||||||
|
|
||||||
|
test("Gitea discovery requires an HTTPS allow-listed configured origin", () => {
|
||||||
|
assert.throws(
|
||||||
|
() => loadGiteaDiscoveryConfig({}),
|
||||||
|
/GITEA_TOKEN and GITEA_BASE_URL/,
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() =>
|
||||||
|
loadGiteaDiscoveryConfig({
|
||||||
|
GITEA_TOKEN: "secret",
|
||||||
|
GITEA_BASE_URL: "http://git.example.test",
|
||||||
|
GITEA_ALLOWED_ORIGINS: "http://git.example.test",
|
||||||
|
}),
|
||||||
|
/HTTPS/,
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() =>
|
||||||
|
loadGiteaDiscoveryConfig({
|
||||||
|
GITEA_TOKEN: "secret",
|
||||||
|
GITEA_BASE_URL: "https://git.example.test",
|
||||||
|
GITEA_ALLOWED_ORIGINS: "https://other.example.test",
|
||||||
|
}),
|
||||||
|
/not in GITEA_ALLOWED_ORIGINS/,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Gitea requests authenticate, disable redirects, and use an abort signal", async () => {
|
||||||
|
const config = loadGiteaDiscoveryConfig({
|
||||||
|
GITEA_TOKEN: "secret",
|
||||||
|
GITEA_BASE_URL: "https://git.example.test",
|
||||||
|
GITEA_ALLOWED_ORIGINS: "https://git.example.test",
|
||||||
|
});
|
||||||
|
let requestInit: RequestInit | undefined;
|
||||||
|
const fakeFetch = (async (_input: unknown, init?: RequestInit) => {
|
||||||
|
requestInit = init;
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
name: "engine",
|
||||||
|
full_name: "platform/engine",
|
||||||
|
clone_url: "https://git.example.test/platform/engine.git",
|
||||||
|
default_branch: "main",
|
||||||
|
private: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
}) as typeof fetch;
|
||||||
|
|
||||||
|
const repositories = await fetchGiteaRepositories(config, fakeFetch);
|
||||||
|
assert.equal(new Headers(requestInit?.headers).get("Authorization"), "token secret");
|
||||||
|
assert.equal(requestInit?.redirect, "error");
|
||||||
|
assert.ok(requestInit?.signal);
|
||||||
|
assert.equal(repositories.length, 1);
|
||||||
|
assert.equal(toGitSource(repositories[0]).id, "platform--engine");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Gitea fetch timeout aborts the request", async () => {
|
||||||
|
const config = {
|
||||||
|
apiUrl: new URL("https://git.example.test/api/v1/repos/search"),
|
||||||
|
token: "secret",
|
||||||
|
timeoutMs: 5,
|
||||||
|
};
|
||||||
|
const hangingFetch = ((_input: unknown, init?: RequestInit) =>
|
||||||
|
new Promise<Response>((_resolve, reject) => {
|
||||||
|
init?.signal?.addEventListener("abort", () =>
|
||||||
|
reject(new DOMException("aborted", "AbortError")),
|
||||||
|
);
|
||||||
|
})) as typeof fetch;
|
||||||
|
await assert.rejects(
|
||||||
|
fetchGiteaRepositories(config, hangingFetch),
|
||||||
|
/timed out after 5ms/,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Gitea discovery limiter is fixed-window and returns retry timing", () => {
|
||||||
|
const limiter = new FixedWindowRateLimiter(2, 1_000);
|
||||||
|
assert.equal(limiter.take("client", 0).allowed, true);
|
||||||
|
assert.equal(limiter.take("client", 1).allowed, true);
|
||||||
|
assert.deepEqual(limiter.take("client", 2), {
|
||||||
|
allowed: false,
|
||||||
|
retryAfter: 1,
|
||||||
|
});
|
||||||
|
assert.equal(limiter.take("client", 1_000).allowed, true);
|
||||||
|
});
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
import express from "express";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import { cachedRepositoryPath, listRepositoryFiles } from "../gitOperations";
|
||||||
|
import { store } from "../store";
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/style-packages", (_req, res) => {
|
||||||
|
res.json(store.stylePackages);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/style-instances", (_req, res) => {
|
||||||
|
res.json(store.styleInstances);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/style-instances", (req, res) => {
|
||||||
|
const instance = req.body;
|
||||||
|
store.styleInstances.push(instance);
|
||||||
|
store.addAudit(
|
||||||
|
"Add Style Instance",
|
||||||
|
"config",
|
||||||
|
`Created style instance '${instance.id}' using '${instance.uses}'`,
|
||||||
|
);
|
||||||
|
res.json({ success: true, instance });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.put("/style-instances/:id", (req, res) => {
|
||||||
|
const index = store.styleInstances.findIndex(
|
||||||
|
(instance) => instance.id === req.params.id,
|
||||||
|
);
|
||||||
|
if (index === -1) return res.status(404).json({ error: "Instance not found" });
|
||||||
|
store.styleInstances[index] = { ...store.styleInstances[index], ...req.body };
|
||||||
|
store.addAudit(
|
||||||
|
"Update Style Instance",
|
||||||
|
"config",
|
||||||
|
`Updated style instance '${req.params.id}'`,
|
||||||
|
);
|
||||||
|
return res.json({ success: true, instance: store.styleInstances[index] });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/content", (_req, res) => {
|
||||||
|
res.json(store.contentItems);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/content/discover", (_req, res) => {
|
||||||
|
store.addAudit(
|
||||||
|
"Discover Content",
|
||||||
|
"config",
|
||||||
|
"Executed content discovery scan across all Git sources",
|
||||||
|
);
|
||||||
|
const discovered = [];
|
||||||
|
|
||||||
|
for (const source of store.gitSources) {
|
||||||
|
if (source.health !== "Healthy" || !source.projectionEnabled) continue;
|
||||||
|
let root: string;
|
||||||
|
try {
|
||||||
|
root = cachedRepositoryPath(source.id);
|
||||||
|
} catch {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(root)) continue;
|
||||||
|
|
||||||
|
const markdownFiles = listRepositoryFiles(
|
||||||
|
source.id,
|
||||||
|
(relative) => relative.toLowerCase().endsWith(".md"),
|
||||||
|
10_000,
|
||||||
|
);
|
||||||
|
for (const [index, file] of markdownFiles.entries()) {
|
||||||
|
const title = path.posix.basename(file.path, ".md").trim() || "Untitled";
|
||||||
|
const declaredModels = source.produces || [];
|
||||||
|
const defaultModel = store.siteConfig.contentModels[0]?.id ?? "page";
|
||||||
|
const artifactType = declaredModels[0] ?? defaultModel;
|
||||||
|
const slug = title.toLowerCase();
|
||||||
|
const model = store.siteConfig.contentModels.find(
|
||||||
|
(candidate) => candidate.id === artifactType,
|
||||||
|
);
|
||||||
|
const navigation = store.siteConfig.navigation.find(
|
||||||
|
(entry) => entry.contentModel === artifactType,
|
||||||
|
);
|
||||||
|
const routePattern =
|
||||||
|
model?.routing?.detail ??
|
||||||
|
(navigation
|
||||||
|
? `${navigation.route}/{slug}/`
|
||||||
|
: `/${artifactType}s/{slug}/`);
|
||||||
|
discovered.push({
|
||||||
|
id: `art-${source.id}-${index}`,
|
||||||
|
title: title
|
||||||
|
.replace(/-/g, " ")
|
||||||
|
.replace(/\b\w/g, (letter) => letter.toUpperCase()),
|
||||||
|
slug,
|
||||||
|
published: new Date().toISOString(),
|
||||||
|
status: "published" as const,
|
||||||
|
artifactType,
|
||||||
|
summary: `Discovered artifact from ${file.path}`,
|
||||||
|
tags: [source.id],
|
||||||
|
aliases: [],
|
||||||
|
sourceRepo: source.id,
|
||||||
|
path: file.path,
|
||||||
|
contentMarkdown: fs.readFileSync(
|
||||||
|
path.join(root, ...file.path.split("/")),
|
||||||
|
"utf8",
|
||||||
|
),
|
||||||
|
mediaReferences: [],
|
||||||
|
youtubeDirectives: [],
|
||||||
|
wikipediaLinks: [],
|
||||||
|
validationStatus: "valid" as const,
|
||||||
|
validationMessages: [],
|
||||||
|
route: routePattern.replace("{slug}", slug).replace("//", "/"),
|
||||||
|
styleInstanceId: "default",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
store.contentItems = discovered;
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
discoveredCount: discovered.length,
|
||||||
|
mediaCount: store.mediaAssets.length,
|
||||||
|
items: discovered,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/media", (_req, res) => {
|
||||||
|
res.json(store.mediaAssets);
|
||||||
|
});
|
||||||
|
|
||||||
|
export { router as contentRouter };
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import express from "express";
|
||||||
|
import { CredentialRef } from "../../types";
|
||||||
|
import { store } from "../store";
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/health", (_req, res) => {
|
||||||
|
res.json({ status: "ok", service: "labyricorn-site-builder" });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/site-config", (_req, res) => {
|
||||||
|
res.json(store.siteConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.put("/site-config", (_req, res) => {
|
||||||
|
res.status(405).json({
|
||||||
|
code: "E_CONFIG_READ_ONLY",
|
||||||
|
message: "Site configuration is Git-owned and read-only in v1.",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/theme", (_req, res) => {
|
||||||
|
res.json(store.themeConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/theme/validate", (_req, res) => {
|
||||||
|
const theme = store.validateTheme();
|
||||||
|
res.status(theme.status === "valid" ? 200 : 422).json({
|
||||||
|
success: theme.status === "valid",
|
||||||
|
theme,
|
||||||
|
commits: theme.commit ? { [theme.sourceId ?? "theme"]: theme.commit } : {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/builds", (_req, res) => {
|
||||||
|
res.json(store.builds);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/builds/trigger", (_req, res) => {
|
||||||
|
res.json(store.runBuild());
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/nginx", (_req, res) => {
|
||||||
|
res.json(store.nginxStatus);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/nginx/test", (_req, res) => {
|
||||||
|
store.nginxStatus.configValid = true;
|
||||||
|
store.addAudit(
|
||||||
|
"Test Nginx Config",
|
||||||
|
"nginx",
|
||||||
|
"Control-plane simulation reported a valid Nginx configuration.",
|
||||||
|
);
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
simulated: true,
|
||||||
|
message: "Nginx validation is simulated by the control plane.",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/nginx/activate", (req, res) => {
|
||||||
|
res.json(store.activateReleaseLocally(req.body.buildId));
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/push-targets", (_req, res) => {
|
||||||
|
res.json(store.pushTargets);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/push-targets/:id/push", (req, res) => {
|
||||||
|
const result = store.pushToTarget(req.params.id, req.body.buildId);
|
||||||
|
res.status(result.success ? 200 : 501).json(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/push-targets/:id/test", (_req, res) => {
|
||||||
|
res.status(501).json({
|
||||||
|
success: false,
|
||||||
|
code: "E_REMOTE_PUBLICATION_NOT_IMPLEMENTED",
|
||||||
|
message: "Remote SSH and rsync checks are not implemented.",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/credentials", (_req, res) => {
|
||||||
|
res.json(store.credentials);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/credentials", (req, res) => {
|
||||||
|
const { name, type, secretValue } = req.body;
|
||||||
|
if (
|
||||||
|
typeof name !== "string" ||
|
||||||
|
!["ssh_key", "token", "password"].includes(type) ||
|
||||||
|
typeof secretValue !== "string"
|
||||||
|
) {
|
||||||
|
return res.status(400).json({
|
||||||
|
error: "name, type, and secretValue are required.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const id = `secret:${name.toLowerCase().replace(/[^a-z0-9]/g, "-")}`;
|
||||||
|
const masked =
|
||||||
|
type === "ssh_key"
|
||||||
|
? `${secretValue.substring(0, 20)}...`
|
||||||
|
: `${secretValue.substring(0, 4)}********************`;
|
||||||
|
const credential: CredentialRef = {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
type: type as CredentialRef["type"],
|
||||||
|
maskedValue: masked,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
lastUsedAt: new Date().toISOString(),
|
||||||
|
usageCount: 0,
|
||||||
|
};
|
||||||
|
store.credentials.push(credential);
|
||||||
|
store.addAudit(
|
||||||
|
"Add Credential",
|
||||||
|
"credential",
|
||||||
|
`Stored credential reference ${id}`,
|
||||||
|
);
|
||||||
|
return res.json({ success: true, credential });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/audit-logs", (_req, res) => {
|
||||||
|
res.json(store.auditLogs);
|
||||||
|
});
|
||||||
|
|
||||||
|
export { router as controlRouter };
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
import express from "express";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import { GitSource } from "../../types";
|
||||||
|
import {
|
||||||
|
cachedRepositoryPath,
|
||||||
|
listRepositoryFiles,
|
||||||
|
resolveRemote,
|
||||||
|
synchronizeRepository,
|
||||||
|
validateRepository,
|
||||||
|
} from "../gitOperations";
|
||||||
|
import { store } from "../store";
|
||||||
|
import {
|
||||||
|
fetchGiteaRepositories,
|
||||||
|
FixedWindowRateLimiter,
|
||||||
|
loadGiteaDiscoveryConfig,
|
||||||
|
toGitSource,
|
||||||
|
} from "./giteaDiscovery";
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
const discoveryLimiter = new FixedWindowRateLimiter();
|
||||||
|
let discoveryInFlight: Promise<ReturnType<typeof toGitSource>[]> | null = null;
|
||||||
|
|
||||||
|
const errorMessage = (error: unknown): string =>
|
||||||
|
error instanceof Error ? error.message : String(error);
|
||||||
|
|
||||||
|
router.post("/gitea/discover", async (req, res) => {
|
||||||
|
const limit = discoveryLimiter.take(req.ip || req.socket.remoteAddress || "unknown");
|
||||||
|
if (!limit.allowed) {
|
||||||
|
res.setHeader("Retry-After", String(limit.retryAfter));
|
||||||
|
return res.status(429).json({
|
||||||
|
code: "E_GITEA_DISCOVERY_RATE_LIMIT",
|
||||||
|
error: "Gitea discovery rate limit exceeded.",
|
||||||
|
success: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let config: ReturnType<typeof loadGiteaDiscoveryConfig>;
|
||||||
|
try {
|
||||||
|
config = loadGiteaDiscoveryConfig();
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(503).json({
|
||||||
|
code: "E_GITEA_DISCOVERY_CONFIG",
|
||||||
|
error: errorMessage(error),
|
||||||
|
success: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
store.addAudit(
|
||||||
|
"Gitea Discovery",
|
||||||
|
"config",
|
||||||
|
`Initiating repository discovery from ${config.apiUrl.origin}`,
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
discoveryInFlight ??= fetchGiteaRepositories(config)
|
||||||
|
.then((repositories) => repositories.map(toGitSource))
|
||||||
|
.finally(() => {
|
||||||
|
discoveryInFlight = null;
|
||||||
|
});
|
||||||
|
const discovered = await discoveryInFlight;
|
||||||
|
const added = store.mergeDiscoveredGitSources(discovered);
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
discoveredCount: discovered.length,
|
||||||
|
addedCount: added,
|
||||||
|
message: `Successfully discovered ${discovered.length} repositories from Gitea.`,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const message = errorMessage(error);
|
||||||
|
store.addAudit("Gitea Discovery Failed", "config", message, "failure");
|
||||||
|
return res.status(502).json({ error: message, success: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/git-sources", (_req, res) => {
|
||||||
|
res.json(store.gitSources);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/git-sources", async (req, res) => {
|
||||||
|
const body = req.body as Partial<GitSource>;
|
||||||
|
if (
|
||||||
|
typeof body.id !== "string" ||
|
||||||
|
typeof body.repository !== "string" ||
|
||||||
|
typeof body.ref !== "string"
|
||||||
|
) {
|
||||||
|
return res.status(400).json({ error: "id, repository, and ref are required." });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
validateRepository(body.repository);
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(400).json({ error: errorMessage(error) });
|
||||||
|
}
|
||||||
|
|
||||||
|
const source: GitSource = {
|
||||||
|
id: body.id,
|
||||||
|
repository: body.repository,
|
||||||
|
ref: body.ref,
|
||||||
|
branch: body.branch || body.ref,
|
||||||
|
type: body.type || "hybrid",
|
||||||
|
required: body.required ?? false,
|
||||||
|
credential: body.credential,
|
||||||
|
health: "Warning",
|
||||||
|
reachability: "Unreachable",
|
||||||
|
configurationState: "Not Configured",
|
||||||
|
visibility: "Private in Gitea",
|
||||||
|
projectionEnabled: false,
|
||||||
|
produces: [],
|
||||||
|
explainWhy: "Awaiting initial synchronization.",
|
||||||
|
status: "syncing",
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const commit = await resolveRemote(source.repository, source.ref);
|
||||||
|
if (commit) {
|
||||||
|
source.status = "connected";
|
||||||
|
source.reachability = "Healthy";
|
||||||
|
source.lastResolvedCommit = commit;
|
||||||
|
source.lastSyncedAt = new Date().toISOString();
|
||||||
|
source.explainWhy =
|
||||||
|
"Connected to repository, but missing .labyricorn/site.yml metadata.";
|
||||||
|
} else {
|
||||||
|
source.status = "error";
|
||||||
|
source.explainWhy = "Ref not found in repository.";
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
source.status = "error";
|
||||||
|
source.explainWhy = `Git ls-remote failed: ${errorMessage(error)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
store.gitSources.push(source);
|
||||||
|
store.addAudit(
|
||||||
|
"Add Git Source",
|
||||||
|
"source",
|
||||||
|
`Added Git source ${source.id} (${source.repository})`,
|
||||||
|
);
|
||||||
|
return res.json({ success: true, source });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.put("/git-sources/:id", (req, res) => {
|
||||||
|
const index = store.gitSources.findIndex((source) => source.id === req.params.id);
|
||||||
|
if (index === -1) return res.status(404).json({ error: "Source not found" });
|
||||||
|
store.gitSources[index] = { ...store.gitSources[index], ...req.body };
|
||||||
|
store.addAudit(
|
||||||
|
"Update Git Source",
|
||||||
|
"source",
|
||||||
|
`Updated Git source ${req.params.id}`,
|
||||||
|
);
|
||||||
|
return res.json({ success: true, source: store.gitSources[index] });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.delete("/git-sources/:id", (req, res) => {
|
||||||
|
store.gitSources = store.gitSources.filter(
|
||||||
|
(source) => source.id !== req.params.id,
|
||||||
|
);
|
||||||
|
store.addAudit(
|
||||||
|
"Delete Git Source",
|
||||||
|
"source",
|
||||||
|
`Removed Git source ${req.params.id}`,
|
||||||
|
);
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/git-sources/:id/test", async (req, res) => {
|
||||||
|
const source = store.gitSources.find((item) => item.id === req.params.id);
|
||||||
|
if (!source) return res.status(404).json({ error: "Source not found" });
|
||||||
|
source.status = "syncing";
|
||||||
|
source.explainWhy = "Synchronization running...";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const commit = await resolveRemote(source.repository, source.ref || "HEAD");
|
||||||
|
if (!commit) {
|
||||||
|
source.status = "error";
|
||||||
|
source.explainWhy = "Ref not found in repository.";
|
||||||
|
return res.status(400).json({ error: source.explainWhy });
|
||||||
|
}
|
||||||
|
|
||||||
|
source.status = "connected";
|
||||||
|
source.reachability = "Healthy";
|
||||||
|
source.lastResolvedCommit = commit;
|
||||||
|
source.lastSyncedAt = new Date().toISOString();
|
||||||
|
const target = await synchronizeRepository(
|
||||||
|
source.id,
|
||||||
|
source.repository,
|
||||||
|
source.ref || "main",
|
||||||
|
);
|
||||||
|
if (fs.existsSync(`${target}/.labyricorn/site.yml`)) {
|
||||||
|
source.configurationState = "Valid";
|
||||||
|
source.projectionEnabled = true;
|
||||||
|
source.explainWhy =
|
||||||
|
"Repository synchronized and valid site.yml detected.";
|
||||||
|
source.health = "Healthy";
|
||||||
|
} else {
|
||||||
|
source.configurationState = "Not Configured";
|
||||||
|
source.projectionEnabled = false;
|
||||||
|
source.explainWhy =
|
||||||
|
"Repository synchronized, but missing .labyricorn/site.yml metadata.";
|
||||||
|
source.health = "Warning";
|
||||||
|
}
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
message: `Successfully connected and synchronized ${source.repository}!`,
|
||||||
|
resolvedCommit: source.lastResolvedCommit,
|
||||||
|
syncedAt: source.lastSyncedAt,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
source.status = "error";
|
||||||
|
source.health = "Invalid";
|
||||||
|
source.explainWhy = `Git sync failed: ${errorMessage(error)}`;
|
||||||
|
return res.status(400).json({ error: source.explainWhy });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/git-sources/:id/tree", (req, res) => {
|
||||||
|
if (!store.gitSources.some((source) => source.id === req.params.id)) {
|
||||||
|
return res.status(404).json({ error: "Source not found" });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
cachedRepositoryPath(req.params.id);
|
||||||
|
const files = listRepositoryFiles(req.params.id).map((file) => ({
|
||||||
|
path: file.path,
|
||||||
|
type: "file",
|
||||||
|
size: `${file.sizeBytes} B`,
|
||||||
|
}));
|
||||||
|
return res.json({ sourceId: req.params.id, files });
|
||||||
|
} catch {
|
||||||
|
return res.json({ sourceId: req.params.id, files: [] });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export { router as gitSourceRouter };
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
import { GitSource } from "../../types";
|
||||||
|
|
||||||
|
const DEFAULT_TIMEOUT_MS = 5_000;
|
||||||
|
const DEFAULT_RATE_LIMIT = 5;
|
||||||
|
const DEFAULT_RATE_WINDOW_MS = 60_000;
|
||||||
|
|
||||||
|
export interface GiteaDiscoveryConfig {
|
||||||
|
apiUrl: URL;
|
||||||
|
token: string;
|
||||||
|
timeoutMs: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GiteaRepository {
|
||||||
|
clone_url?: unknown;
|
||||||
|
default_branch?: unknown;
|
||||||
|
full_name?: unknown;
|
||||||
|
name?: unknown;
|
||||||
|
private?: unknown;
|
||||||
|
ssh_url?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DiscoveredRepository {
|
||||||
|
cloneUrl: string;
|
||||||
|
defaultBranch: string;
|
||||||
|
fullName: string;
|
||||||
|
name: string;
|
||||||
|
private: boolean;
|
||||||
|
sshUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const positiveInteger = (value: string | undefined, fallback: number): number => {
|
||||||
|
if (value === undefined) return fallback;
|
||||||
|
const parsed = Number.parseInt(value, 10);
|
||||||
|
if (!Number.isInteger(parsed) || parsed < 1) {
|
||||||
|
throw new Error(`Invalid positive integer configuration value '${value}'.`);
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
const allowedOrigins = (value: string | undefined): Set<string> =>
|
||||||
|
new Set(
|
||||||
|
(value ?? "")
|
||||||
|
.split(",")
|
||||||
|
.map((entry) => entry.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((entry) => new URL(entry).origin),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const loadGiteaDiscoveryConfig = (
|
||||||
|
env: NodeJS.ProcessEnv = process.env,
|
||||||
|
): GiteaDiscoveryConfig => {
|
||||||
|
const token = env.GITEA_TOKEN?.trim();
|
||||||
|
const baseUrl = env.GITEA_BASE_URL?.trim();
|
||||||
|
if (!token || !baseUrl) {
|
||||||
|
throw new Error("Gitea discovery requires GITEA_TOKEN and GITEA_BASE_URL.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const base = new URL(baseUrl);
|
||||||
|
if (base.protocol !== "https:" || base.username || base.password) {
|
||||||
|
throw new Error("GITEA_BASE_URL must be an HTTPS origin without credentials.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const allowlist = allowedOrigins(env.GITEA_ALLOWED_ORIGINS);
|
||||||
|
if (!allowlist.has(base.origin)) {
|
||||||
|
throw new Error(
|
||||||
|
`GITEA_BASE_URL origin '${base.origin}' is not in GITEA_ALLOWED_ORIGINS.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiUrl = new URL("/api/v1/repos/search", base);
|
||||||
|
apiUrl.searchParams.set("limit", "50");
|
||||||
|
return {
|
||||||
|
apiUrl,
|
||||||
|
token,
|
||||||
|
timeoutMs: positiveInteger(
|
||||||
|
env.GITEA_DISCOVERY_TIMEOUT_MS,
|
||||||
|
DEFAULT_TIMEOUT_MS,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const repository = (value: GiteaRepository): DiscoveredRepository | null => {
|
||||||
|
if (
|
||||||
|
typeof value.name !== "string" ||
|
||||||
|
typeof value.clone_url !== "string" ||
|
||||||
|
value.name.length === 0 ||
|
||||||
|
value.clone_url.length === 0
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const fullName =
|
||||||
|
typeof value.full_name === "string" && value.full_name.length > 0
|
||||||
|
? value.full_name
|
||||||
|
: value.name;
|
||||||
|
return {
|
||||||
|
cloneUrl: value.clone_url,
|
||||||
|
defaultBranch:
|
||||||
|
typeof value.default_branch === "string" && value.default_branch.length > 0
|
||||||
|
? value.default_branch
|
||||||
|
: "main",
|
||||||
|
fullName,
|
||||||
|
name: value.name,
|
||||||
|
private: value.private === true,
|
||||||
|
sshUrl: typeof value.ssh_url === "string" ? value.ssh_url : undefined,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchGiteaRepositories = async (
|
||||||
|
config: GiteaDiscoveryConfig,
|
||||||
|
fetchImplementation: typeof fetch = fetch,
|
||||||
|
): Promise<DiscoveredRepository[]> => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeout = setTimeout(() => controller.abort(), config.timeoutMs);
|
||||||
|
try {
|
||||||
|
const response = await fetchImplementation(config.apiUrl, {
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
Authorization: `token ${config.token}`,
|
||||||
|
},
|
||||||
|
redirect: "error",
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error(`Gitea returned HTTP ${response.status}.`);
|
||||||
|
const document = (await response.json()) as { data?: unknown };
|
||||||
|
if (!Array.isArray(document.data)) {
|
||||||
|
throw new Error("Gitea returned an invalid repository search response.");
|
||||||
|
}
|
||||||
|
return document.data
|
||||||
|
.map((entry) => repository(entry as GiteaRepository))
|
||||||
|
.filter((entry): entry is DiscoveredRepository => entry !== null);
|
||||||
|
} catch (error) {
|
||||||
|
if (controller.signal.aborted) {
|
||||||
|
throw new Error(`Gitea discovery timed out after ${config.timeoutMs}ms.`);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sourceId = (repo: DiscoveredRepository): string =>
|
||||||
|
repo.fullName
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9._-]+/g, "--")
|
||||||
|
.replace(/^-+|-+$/g, "")
|
||||||
|
.slice(0, 128) || repo.name.toLowerCase();
|
||||||
|
|
||||||
|
export const toGitSource = (repo: DiscoveredRepository): GitSource => ({
|
||||||
|
id: sourceId(repo),
|
||||||
|
repository: repo.cloneUrl,
|
||||||
|
ref: repo.defaultBranch,
|
||||||
|
branch: repo.defaultBranch,
|
||||||
|
type: "hybrid",
|
||||||
|
health: "Warning",
|
||||||
|
reachability: "Unreachable",
|
||||||
|
configurationState: "Not Configured",
|
||||||
|
visibility: repo.private ? "Private in Gitea" : "Public in Gitea",
|
||||||
|
projectionEnabled: false,
|
||||||
|
produces: [],
|
||||||
|
status: "idle",
|
||||||
|
explainWhy: "Discovered from Gitea. Awaiting initial synchronization.",
|
||||||
|
required: false,
|
||||||
|
credential: "none",
|
||||||
|
});
|
||||||
|
|
||||||
|
export class FixedWindowRateLimiter {
|
||||||
|
private readonly clients = new Map<
|
||||||
|
string,
|
||||||
|
{ count: number; startedAt: number }
|
||||||
|
>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly maximum = positiveInteger(
|
||||||
|
process.env.GITEA_DISCOVERY_RATE_LIMIT,
|
||||||
|
DEFAULT_RATE_LIMIT,
|
||||||
|
),
|
||||||
|
private readonly windowMs = positiveInteger(
|
||||||
|
process.env.GITEA_DISCOVERY_RATE_WINDOW_MS,
|
||||||
|
DEFAULT_RATE_WINDOW_MS,
|
||||||
|
),
|
||||||
|
) {}
|
||||||
|
|
||||||
|
take(client: string, now = Date.now()): { allowed: boolean; retryAfter: number } {
|
||||||
|
const current = this.clients.get(client);
|
||||||
|
if (!current || now - current.startedAt >= this.windowMs) {
|
||||||
|
this.clients.set(client, { count: 1, startedAt: now });
|
||||||
|
return { allowed: true, retryAfter: 0 };
|
||||||
|
}
|
||||||
|
if (current.count >= this.maximum) {
|
||||||
|
return {
|
||||||
|
allowed: false,
|
||||||
|
retryAfter: Math.ceil((this.windowMs - (now - current.startedAt)) / 1_000),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
current.count += 1;
|
||||||
|
return { allowed: true, retryAfter: 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import express from "express";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import { store } from "../store";
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/live-site/html", (req, res) => {
|
||||||
|
const rootDir = store.getPreviewDirectory();
|
||||||
|
if (!rootDir) {
|
||||||
|
return res.status(503).json({
|
||||||
|
code: "E_RELEASE_UNAVAILABLE",
|
||||||
|
message: "No verified staging or live release is selected.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let requested: string;
|
||||||
|
try {
|
||||||
|
requested = decodeURIComponent(
|
||||||
|
String(req.query.route ?? "/").split(/[?#]/, 1)[0],
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
return res.status(400).json({ code: "E_ROUTE_INVALID" });
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!requested.startsWith("/") ||
|
||||||
|
requested.includes("\\") ||
|
||||||
|
requested.split("/").includes("..")
|
||||||
|
) {
|
||||||
|
return res.status(400).json({ code: "E_ROUTE_INVALID" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const relative = requested.replace(/^\/+/, "");
|
||||||
|
const outputFile =
|
||||||
|
relative && path.posix.extname(relative)
|
||||||
|
? relative
|
||||||
|
: path.posix.join(relative, "index.html");
|
||||||
|
const rootPath = `${path.resolve(rootDir)}${path.sep}`;
|
||||||
|
const target = path.resolve(rootDir, ...outputFile.split("/"));
|
||||||
|
if (!target.startsWith(rootPath)) {
|
||||||
|
return res.status(400).json({ code: "E_ROUTE_INVALID" });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.setHeader(
|
||||||
|
"Content-Security-Policy",
|
||||||
|
"default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'self'",
|
||||||
|
);
|
||||||
|
if (fs.existsSync(target) && fs.statSync(target).isFile()) {
|
||||||
|
return res.type(path.extname(target)).send(fs.readFileSync(target));
|
||||||
|
}
|
||||||
|
const notFound = path.join(rootDir, "404.html");
|
||||||
|
if (fs.existsSync(notFound)) {
|
||||||
|
return res.status(404).type("html").send(fs.readFileSync(notFound));
|
||||||
|
}
|
||||||
|
return res.status(404).json({ code: "E_ROUTE_NOT_FOUND" });
|
||||||
|
});
|
||||||
|
|
||||||
|
export { router as previewRouter };
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { ChildProcess, spawn } from "node:child_process";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import test from "node:test";
|
||||||
|
import { BuildEngine } from "./buildEngine";
|
||||||
|
import {
|
||||||
|
commit,
|
||||||
|
createRepository,
|
||||||
|
loadFixture,
|
||||||
|
} from "./testing/repositoryFixture";
|
||||||
|
|
||||||
|
const waitForExit = (child: ChildProcess): Promise<string> =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
let stdout = "";
|
||||||
|
let stderr = "";
|
||||||
|
child.stdout?.on("data", (chunk) => {
|
||||||
|
stdout += chunk;
|
||||||
|
});
|
||||||
|
child.stderr?.on("data", (chunk) => {
|
||||||
|
stderr += chunk;
|
||||||
|
});
|
||||||
|
child.on("error", reject);
|
||||||
|
child.on("exit", (code) => {
|
||||||
|
if (code === 0) resolve(stdout.trim());
|
||||||
|
else reject(new Error(`worker exited ${code}: ${stderr}`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const waitFor = async (predicate: () => boolean): Promise<void> => {
|
||||||
|
const deadline = Date.now() + 10_000;
|
||||||
|
while (!predicate()) {
|
||||||
|
if (Date.now() > deadline) throw new Error("Timed out waiting for workers.");
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test("concurrent writers converge on one verified immutable release", async () => {
|
||||||
|
const root = createRepository();
|
||||||
|
const output = fs.mkdtempSync(path.join(os.tmpdir(), "concurrent-release-"));
|
||||||
|
const barrier = path.join(output, "barrier");
|
||||||
|
const worker = path.resolve("src/backend/testing/concurrentBuildWorker.ts");
|
||||||
|
try {
|
||||||
|
const children = [1, 2].map(() =>
|
||||||
|
spawn(process.execPath, ["--import", "tsx", worker, root, output, barrier], {
|
||||||
|
cwd: process.cwd(),
|
||||||
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const completions = children.map(waitForExit);
|
||||||
|
await waitFor(
|
||||||
|
() =>
|
||||||
|
fs
|
||||||
|
.readdirSync(output)
|
||||||
|
.filter((entry) => entry.startsWith("barrier.") && entry.endsWith(".ready"))
|
||||||
|
.length === 2,
|
||||||
|
);
|
||||||
|
fs.writeFileSync(`${barrier}.go`, "");
|
||||||
|
const checksums = await Promise.all(completions);
|
||||||
|
assert.equal(new Set(checksums).size, 1);
|
||||||
|
|
||||||
|
const releases = fs.readdirSync(path.join(output, "releases"));
|
||||||
|
assert.equal(releases.length, 1);
|
||||||
|
new BuildEngine(output).verifyRelease(releases[0]);
|
||||||
|
assert.equal(
|
||||||
|
fs.readdirSync(output).some((entry) => entry.startsWith(".release-")),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(root, { recursive: true, force: true });
|
||||||
|
fs.rmSync(output, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("partial rendering failure leaves no release or temporary directory", () => {
|
||||||
|
const root = createRepository();
|
||||||
|
const output = fs.mkdtempSync(path.join(os.tmpdir(), "partial-release-"));
|
||||||
|
try {
|
||||||
|
const template = path.join(
|
||||||
|
root,
|
||||||
|
"packages/site-definition/.theme/templates/home.liquid",
|
||||||
|
);
|
||||||
|
fs.appendFileSync(template, "\n{{ undefined.value }}\n");
|
||||||
|
commit(root, "break rendering");
|
||||||
|
const fixture = loadFixture(root, "partial-failure");
|
||||||
|
try {
|
||||||
|
const engine = new BuildEngine(output);
|
||||||
|
assert.throws(() => engine.build(fixture.input), /undefined|not defined/i);
|
||||||
|
assert.equal(fs.existsSync(engine.buildDirectory(fixture.input.artifactBuildId)), false);
|
||||||
|
assert.equal(
|
||||||
|
fs.readdirSync(output).some((entry) => entry.startsWith(".release-")),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
fixture.manager.dispose();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(root, { recursive: true, force: true });
|
||||||
|
fs.rmSync(output, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("staging and current pointers promote only checksum-verified releases", () => {
|
||||||
|
const root = createRepository();
|
||||||
|
const output = fs.mkdtempSync(path.join(os.tmpdir(), "promotion-release-"));
|
||||||
|
const first = loadFixture(root, "first");
|
||||||
|
try {
|
||||||
|
const engine = new BuildEngine(output);
|
||||||
|
const firstResult = engine.build(first.input);
|
||||||
|
engine.stage(first.input.artifactBuildId);
|
||||||
|
engine.activate(first.input.artifactBuildId);
|
||||||
|
|
||||||
|
const css = path.join(
|
||||||
|
root,
|
||||||
|
"packages/site-definition/.theme/assets/styles/theme.css",
|
||||||
|
);
|
||||||
|
fs.appendFileSync(css, "\n.second-release{display:block}\n");
|
||||||
|
commit(root, "second release");
|
||||||
|
const second = loadFixture(root, "second");
|
||||||
|
try {
|
||||||
|
const secondResult = engine.build(second.input);
|
||||||
|
engine.stage(second.input.artifactBuildId);
|
||||||
|
assert.equal(path.basename(fs.realpathSync(engine.currentDirectory())), first.input.artifactBuildId);
|
||||||
|
assert.equal(path.basename(fs.realpathSync(engine.stagingDirectory())), second.input.artifactBuildId);
|
||||||
|
|
||||||
|
engine.activate(second.input.artifactBuildId);
|
||||||
|
assert.equal(path.basename(fs.realpathSync(engine.currentDirectory())), second.input.artifactBuildId);
|
||||||
|
assert.equal(path.basename(fs.realpathSync(engine.stagingDirectory())), second.input.artifactBuildId);
|
||||||
|
|
||||||
|
fs.appendFileSync(path.join(firstResult.outputDirectory, "index.html"), "tamper");
|
||||||
|
assert.throws(() => engine.activate(first.input.artifactBuildId), /E_RELEASE_CHECKSUM/);
|
||||||
|
assert.equal(path.basename(fs.realpathSync(engine.currentDirectory())), second.input.artifactBuildId);
|
||||||
|
assert.ok(fs.existsSync(secondResult.outputDirectory));
|
||||||
|
} finally {
|
||||||
|
second.manager.dispose();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
first.manager.dispose();
|
||||||
|
fs.rmSync(root, { recursive: true, force: true });
|
||||||
|
fs.rmSync(output, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("repeated identical builds retain identical artifact checksums", () => {
|
||||||
|
const root = createRepository();
|
||||||
|
const output = fs.mkdtempSync(path.join(os.tmpdir(), "idempotent-release-"));
|
||||||
|
const fixture = loadFixture(root, "idempotent");
|
||||||
|
try {
|
||||||
|
const engine = new BuildEngine(output);
|
||||||
|
const checksums = Array.from(
|
||||||
|
{ length: 20 },
|
||||||
|
() => engine.build(fixture.input).artifactChecksum,
|
||||||
|
);
|
||||||
|
assert.equal(new Set(checksums).size, 1);
|
||||||
|
} finally {
|
||||||
|
fixture.manager.dispose();
|
||||||
|
fs.rmSync(root, { recursive: true, force: true });
|
||||||
|
fs.rmSync(output, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -375,7 +375,19 @@ export class BuildEngine {
|
|||||||
const artifactChecksum = `sha256-${sha256(checksumsDocument)}`;
|
const artifactChecksum = `sha256-${sha256(checksumsDocument)}`;
|
||||||
const generatedFiles = [...files].sort();
|
const generatedFiles = [...files].sort();
|
||||||
const artifactSizeBytes = generatedFiles.reduce((sum, relative) => sum + fs.statSync(path.join(temporaryDirectory, ...relative.split("/"))).size, 0);
|
const artifactSizeBytes = generatedFiles.reduce((sum, relative) => sum + fs.statSync(path.join(temporaryDirectory, ...relative.split("/"))).size, 0);
|
||||||
fs.renameSync(temporaryDirectory, outputDirectory);
|
try {
|
||||||
|
fs.renameSync(temporaryDirectory, outputDirectory);
|
||||||
|
} catch (error) {
|
||||||
|
if (!fs.existsSync(outputDirectory)) throw error;
|
||||||
|
const verified = this.verifyRelease(input.artifactBuildId);
|
||||||
|
fs.rmSync(temporaryDirectory, { recursive: true, force: true });
|
||||||
|
fs.chmodSync(outputDirectory, 0o750);
|
||||||
|
return {
|
||||||
|
success: true, outputDirectory, generatedRoutesCount: verified.routes.length,
|
||||||
|
artifactChecksum: verified.artifactChecksum, artifactSizeBytes: verified.sizeBytes,
|
||||||
|
validationReport, generatedFiles: verified.files,
|
||||||
|
};
|
||||||
|
}
|
||||||
fs.chmodSync(outputDirectory, 0o750);
|
fs.chmodSync(outputDirectory, 0o750);
|
||||||
return { success: true, outputDirectory, generatedRoutesCount: routes.size, artifactChecksum, artifactSizeBytes, validationReport, generatedFiles };
|
return { success: true, outputDirectory, generatedRoutesCount: routes.size, artifactChecksum, artifactSizeBytes, validationReport, generatedFiles };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -432,8 +444,28 @@ export class BuildEngine {
|
|||||||
const pointer = path.join(this.outputRoot, name);
|
const pointer = path.join(this.outputRoot, name);
|
||||||
const next = path.join(this.outputRoot, `.${name}-${process.pid}-${crypto.randomUUID()}`);
|
const next = path.join(this.outputRoot, `.${name}-${process.pid}-${crypto.randomUUID()}`);
|
||||||
fs.symlinkSync(target, next, process.platform === "win32" ? "junction" : "dir");
|
fs.symlinkSync(target, next, process.platform === "win32" ? "junction" : "dir");
|
||||||
try { if (fs.lstatSync(pointer, { throwIfNoEntry: false })) fs.rmSync(pointer, { recursive: true, force: true }); fs.renameSync(next, pointer); }
|
try {
|
||||||
catch (error) { fs.rmSync(next, { recursive: true, force: true }); throw error; }
|
if (process.platform !== "win32") {
|
||||||
|
fs.renameSync(next, pointer);
|
||||||
|
} else {
|
||||||
|
const backup = path.join(
|
||||||
|
this.outputRoot,
|
||||||
|
`.${name}-backup-${process.pid}-${crypto.randomUUID()}`,
|
||||||
|
);
|
||||||
|
const existing = fs.lstatSync(pointer, { throwIfNoEntry: false });
|
||||||
|
if (existing) fs.renameSync(pointer, backup);
|
||||||
|
try {
|
||||||
|
fs.renameSync(next, pointer);
|
||||||
|
if (existing) fs.rmSync(backup, { recursive: true, force: true });
|
||||||
|
} catch (error) {
|
||||||
|
if (existing && !fs.existsSync(pointer)) fs.renameSync(backup, pointer);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
fs.rmSync(next, { recursive: true, force: true });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
return pointer;
|
return pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import test from "node:test";
|
||||||
|
import { RepositorySnapshotManager } from "./repositories/repositorySnapshot";
|
||||||
|
import { commit, createRepository, git } from "./testing/repositoryFixture";
|
||||||
|
|
||||||
|
test("snapshot loader materializes and retains one exact commit", () => {
|
||||||
|
const root = createRepository();
|
||||||
|
const workRoot = fs.mkdtempSync(path.join(os.tmpdir(), "snapshot-test-"));
|
||||||
|
const manager = new RepositorySnapshotManager(workRoot);
|
||||||
|
try {
|
||||||
|
const expectedCommit = git(root, ["rev-parse", "HEAD"]);
|
||||||
|
const snapshot = manager.resolve("site-definition", root, "HEAD");
|
||||||
|
const css = path.join(
|
||||||
|
snapshot.checkoutRoot,
|
||||||
|
"packages/site-definition/.theme/assets/styles/theme.css",
|
||||||
|
);
|
||||||
|
const original = fs.readFileSync(css, "utf8");
|
||||||
|
|
||||||
|
fs.appendFileSync(
|
||||||
|
path.join(
|
||||||
|
root,
|
||||||
|
"packages/site-definition/.theme/assets/styles/theme.css",
|
||||||
|
),
|
||||||
|
"\n.branch-advanced{display:block}\n",
|
||||||
|
);
|
||||||
|
const advancedCommit = commit(root, "advance branch");
|
||||||
|
|
||||||
|
assert.equal(snapshot.commit, expectedCommit);
|
||||||
|
assert.notEqual(snapshot.commit, advancedCommit);
|
||||||
|
assert.equal(fs.readFileSync(css, "utf8"), original);
|
||||||
|
assert.equal(fs.existsSync(path.join(snapshot.checkoutRoot, ".git")), false);
|
||||||
|
const checkoutRoot = snapshot.checkoutRoot;
|
||||||
|
manager.dispose();
|
||||||
|
assert.equal(fs.existsSync(checkoutRoot), false);
|
||||||
|
} finally {
|
||||||
|
manager.dispose();
|
||||||
|
fs.rmSync(root, { recursive: true, force: true });
|
||||||
|
fs.rmSync(workRoot, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -132,6 +132,18 @@ export class LabyricornStore {
|
|||||||
this.auditLogs.unshift({ id: `log-${Date.now()}-${this.auditLogs.length}`, timestamp: new Date().toISOString(), user: "[email protected]", action, category, details, result });
|
this.auditLogs.unshift({ id: `log-${Date.now()}-${this.auditLogs.length}`, timestamp: new Date().toISOString(), user: "[email protected]", action, category, details, result });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mergeDiscoveredGitSources(discovered: readonly GitSource[]): number {
|
||||||
|
const repositories = new Set(this.gitSources.map((source) => source.repository));
|
||||||
|
const additions: GitSource[] = [];
|
||||||
|
for (const source of discovered) {
|
||||||
|
if (repositories.has(source.repository)) continue;
|
||||||
|
repositories.add(source.repository);
|
||||||
|
additions.push(source);
|
||||||
|
}
|
||||||
|
if (additions.length > 0) this.gitSources = [...this.gitSources, ...additions];
|
||||||
|
return additions.length;
|
||||||
|
}
|
||||||
|
|
||||||
validateTheme(): ThemeConfig {
|
validateTheme(): ThemeConfig {
|
||||||
const loader = this.loader();
|
const loader = this.loader();
|
||||||
try {
|
try {
|
||||||
@@ -235,6 +247,10 @@ export class LabyricornStore {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
pushToTarget(targetId: string, buildId: string): { success: boolean; logs: string[] } {
|
pushToTarget(targetId: string, buildId: string): { success: boolean; logs: string[] } {
|
||||||
|
// Deliberately fail closed: no configuration field can enable remote execution.
|
||||||
|
if (!REMOTE_PUBLICATION_IMPLEMENTED) {
|
||||||
|
return { success: false, logs: ["Remote publication is not implemented."] };
|
||||||
|
}
|
||||||
const target = this.pushTargets.find((item) => item.id === targetId);
|
const target = this.pushTargets.find((item) => item.id === targetId);
|
||||||
const build = this.builds.find((item) => item.id === buildId);
|
const build = this.builds.find((item) => item.id === buildId);
|
||||||
if (!target || !build?.artifactBuildId) return { success: false, logs: ["Target or verified build not found."] };
|
if (!target || !build?.artifactBuildId) return { success: false, logs: ["Target or verified build not found."] };
|
||||||
@@ -244,4 +260,5 @@ export class LabyricornStore {
|
|||||||
rollbackLocal(targetReleaseId: string): { success: boolean; message: string } { return this.activateReleaseLocally(targetReleaseId); }
|
rollbackLocal(targetReleaseId: string): { success: boolean; message: string } { return this.activateReleaseLocally(targetReleaseId); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const REMOTE_PUBLICATION_IMPLEMENTED = false as const;
|
||||||
export const store = new LabyricornStore();
|
export const store = new LabyricornStore();
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
|
import { BuildEngine } from "../buildEngine";
|
||||||
|
import { loadFixture } from "./repositoryFixture";
|
||||||
|
|
||||||
|
const [repository, output, barrier] = process.argv.slice(2);
|
||||||
|
if (!repository || !output || !barrier) {
|
||||||
|
throw new Error("repository, output, and barrier arguments are required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixture = loadFixture(repository, `worker-${process.pid}`);
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(`${barrier}.${process.pid}.ready`, "");
|
||||||
|
const waitBuffer = new Int32Array(new SharedArrayBuffer(4));
|
||||||
|
while (!fs.existsSync(`${barrier}.go`)) {
|
||||||
|
Atomics.wait(waitBuffer, 0, 0, 10);
|
||||||
|
}
|
||||||
|
const result = new BuildEngine(output).build(fixture.input);
|
||||||
|
process.stdout.write(`${result.artifactChecksum}\n`);
|
||||||
|
} finally {
|
||||||
|
fixture.manager.dispose();
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { execFileSync } from "node:child_process";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { BuildInputLoader } from "../buildInputLoader";
|
||||||
|
import { BUILDER_VERSION } from "../buildEngine";
|
||||||
|
|
||||||
|
export const git = (root: string, args: string[]): string =>
|
||||||
|
execFileSync("git", ["-C", root, ...args], {
|
||||||
|
encoding: "utf8",
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
GIT_AUTHOR_DATE: "2026-01-01T00:00:00Z",
|
||||||
|
GIT_COMMITTER_DATE: "2026-01-01T00:00:00Z",
|
||||||
|
},
|
||||||
|
}).trim();
|
||||||
|
|
||||||
|
export const createRepository = (): string => {
|
||||||
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "labyricorn-git-fixture-"));
|
||||||
|
fs.mkdirSync(path.join(root, "packages"), { recursive: true });
|
||||||
|
fs.cpSync(
|
||||||
|
path.resolve("packages/site-definition"),
|
||||||
|
path.join(root, "packages/site-definition"),
|
||||||
|
{ recursive: true },
|
||||||
|
);
|
||||||
|
fs.cpSync(
|
||||||
|
path.resolve("packages/content-models"),
|
||||||
|
path.join(root, "packages/content-models"),
|
||||||
|
{ recursive: true },
|
||||||
|
);
|
||||||
|
git(root, ["init", "-b", "main"]);
|
||||||
|
git(root, ["config", "user.name", "Fixture"]);
|
||||||
|
git(root, ["config", "user.email", "[email protected]"]);
|
||||||
|
git(root, ["add", "."]);
|
||||||
|
git(root, ["commit", "-m", "fixture"]);
|
||||||
|
return root;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const commit = (root: string, message: string): string => {
|
||||||
|
git(root, ["add", "-A"]);
|
||||||
|
git(root, ["commit", "-m", message]);
|
||||||
|
return git(root, ["rev-parse", "HEAD"]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadFixture = (root: string, runId: string) => {
|
||||||
|
const manager = new BuildInputLoader({
|
||||||
|
repositoryRoot: root,
|
||||||
|
ref: "HEAD",
|
||||||
|
siteDefinitionPath: "packages/site-definition",
|
||||||
|
builderVersion: BUILDER_VERSION,
|
||||||
|
});
|
||||||
|
return { manager, input: manager.load(runId) };
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user