diff --git a/.github/DockStat-dark.png b/.github/DockStat-dark.png new file mode 100644 index 00000000..00ac779a Binary files /dev/null and b/.github/DockStat-dark.png differ diff --git a/.github/workflows/build-image.yaml b/.github/workflows/build-image.yaml index 41f18cb8..bbb4875d 100644 --- a/.github/workflows/build-image.yaml +++ b/.github/workflows/build-image.yaml @@ -1,4 +1,4 @@ -name: "Build dockstatapi:latest" +name: "Build and Push Docker Image" on: release: @@ -12,28 +12,43 @@ jobs: build-release: runs-on: ubuntu-24.04 steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Login to Github Container Registry + - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ github.token }} - - name: Generate Docker tags + - name: Extract version and create tag + id: get-tag + run: | + # Remove 'v' prefix from release tag if present + VERSION="${GITHUB_REF#refs/tags/v}" + # Check if pre-release and append '-pre' + if ${{ github.event.release.prerelease }}; then + TAG="$VERSION-pre" + else + TAG="$VERSION" + fi + echo "tag=$TAG" >> $GITHUB_OUTPUT + + - name: Generate Docker metadata uses: docker/metadata-action@v5 id: metadata with: images: ghcr.io/${{ github.repository }} tags: | - type=sha,format=long,prefix= - flavor: | - latest=true + type=raw,value=${{ steps.get-tag.outputs.tag }} + type=raw,value=latest,enable=${{ !github.event.release.prerelease }} - name: Build and push uses: docker/build-push-action@v6 diff --git a/.github/workflows/validation.yaml b/.github/workflows/validation.yaml index 4582071e..7e2b685c 100644 --- a/.github/workflows/validation.yaml +++ b/.github/workflows/validation.yaml @@ -1,6 +1,10 @@ name: "Run all tests" -on: [push] +on: + push: + release: + types: + - published jobs: validation: @@ -42,8 +46,40 @@ jobs: - name: Jests run: npm run test:silent - CodeQL: + ToDo: needs: validation + runs-on: ubuntu-20.04 + name: "ToDo comment to issue" + permissions: + contents: write + issues: write + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: "TODO to Issue" + uses: "alstr/todo-to-issue-action@v5" + with: + INSERT_ISSUE_URLS: "true" + + - name: Set Git user + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Commit and Push Changes + run: | + git add -A + if [[ `git status --porcelain` ]]; then + git commit -m "Automatically added GitHub issue links to TODOs" + git push + else + echo "No changes to commit" + fi + + CodeQL: + needs: [ToDo] runs-on: ubuntu-24.04 name: "Analyze TypeScript" permissions: @@ -78,7 +114,7 @@ jobs: category: "/language:javascript-typescript" Anchore: - needs: validation + needs: [ToDo] runs-on: ubuntu-24.04 name: "Anchore" permissions: @@ -113,7 +149,7 @@ jobs: sarif_file: ./results.sarif test-building: - needs: [validation] + needs: [ToDo] runs-on: ubuntu-24.04 name: "Test building" permissions: @@ -216,3 +252,52 @@ jobs: labels: ${{ steps.metadata.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + + build-pre-release: + name: "Pre-Release-build" + permissions: + security-events: read + packages: write + actions: read + contents: read + runs-on: ubuntu-24.04 + if: "github.event.release.prerelease" + needs: [test-building, Anchore, CodeQL] + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Github Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ github.token }} + + - name: Generate Docker tags + uses: docker/metadata-action@v5 + id: metadata + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=raw,enable=true,priority=200,prefix=,suffix=,value=pre + flavor: | + latest=false + + - name: Build and Push Docker Images + uses: docker/build-push-action@v6 + with: + context: . + file: docker/Dockerfile-dev + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.metadata.outputs.tags }} + labels: ${{ steps.metadata.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore index 6381947a..9e264ac0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,12 @@ # custom paths: src/data/* +src/data/frontendConfiguration.json + .tmp docker/master docker/slave .test* +stacks # Created by https://www.toptal.com/developers/gitignore/api/node ### Node ### *-audit.json diff --git a/CREDITS.md b/CREDITS.md index 864d1843..50b66abb 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -8,6 +8,12 @@ This file shows all npm packages used in DockStatAPI (also Dev packages) | ----------------- | -------------------------------------------- | -------------------- | | spdx-ranges@2.1.1 | https://github.com/kemitchell/spdx-ranges.js | The Linux Foundation | +### License: Apache 2.0 + +| Name | Repository | Publisher | +| ---------------------- | ------------------------------------------ | --------- | +| qrcode-terminal@0.12.0 | https://github.com/gtanner/qrcode-terminal | N/A | + ### License: Apache-2.0 | Name | Repository | Publisher | @@ -25,6 +31,12 @@ This file shows all npm packages used in DockStatAPI (also Dev packages) | @humanwhocodes/retry@0.4.1 | https://github.com/humanwhocodes/retry | Nicholas C. Zaks | | @puppeteer/browsers@2.7.0 | https://github.com/puppeteer/puppeteer/tree/main/packages/browsers | The Chromium Authors | | @scarf/scarf@1.4.0 | https://github.com/scarf-sh/scarf-js | Scarf Systems | +| @sigstore/bundle@3.0.0 | https://github.com/sigstore/sigstore-js | bdehamer@github.com | +| @sigstore/core@2.0.0 | https://github.com/sigstore/sigstore-js | bdehamer@github.com | +| @sigstore/protobuf-specs@0.3.2 | https://github.com/sigstore/protobuf-specs | bdehamer@github.com | +| @sigstore/sign@3.0.0 | https://github.com/sigstore/sigstore-js | bdehamer@github.com | +| @sigstore/tuf@3.0.0 | https://github.com/sigstore/sigstore-js | bdehamer@github.com | +| @sigstore/verify@2.0.0 | https://github.com/sigstore/sigstore-js | bdehamer@github.com | | b4a@1.6.7 | https://github.com/holepunchto/b4a | Holepunch | | bare-events@2.5.4 | https://github.com/holepunchto/bare-events | Holepunch | | bare-fs@2.3.5 | https://github.com/holepunchto/bare-fs | Holepunch | @@ -40,12 +52,14 @@ This file shows all npm packages used in DockStatAPI (also Dev packages) | ejs@3.1.10 | https://github.com/mde/ejs | Matthew Eernisse | | eslint-visitor-keys@3.4.3 | https://github.com/eslint/eslint-visitor-keys | Toru Nagashima | | eslint-visitor-keys@4.2.0 | https://github.com/eslint/js | Toru Nagashima | +| exponential-backoff@3.1.1 | https://github.com/coveo/exponential-backoff | Sami Sayegh | | fb-watchman@2.0.2 | https://github.com/facebook/watchman | Wez Furlong | | filelist@1.0.4 | https://github.com/mde/filelist | Matthew Eernisse | | human-signals@2.1.0 | https://github.com/ehmicky/human-signals | ehmicky | | jake@10.9.2 | https://github.com/jakejs/jake | Matthew Eernisse | | puppeteer-core@24.0.0 | https://github.com/puppeteer/puppeteer/tree/main/packages/puppeteer-core | The Chromium Authors | | puppeteer@24.0.0 | https://github.com/puppeteer/puppeteer/tree/main/packages/puppeteer | The Chromium Authors | +| sigstore@3.0.0 | https://github.com/sigstore/sigstore-js | bdehamer@github.com | | spdx-correct@3.2.0 | https://github.com/jslicense/spdx-correct.js | N/A | | swagger-ui-dist@5.18.2 | https://github.com/swagger-api/swagger-ui | N/A | | text-decoder@1.2.3 | https://github.com/holepunchto/text-decoder | Holepunch | @@ -54,6 +68,22 @@ This file shows all npm packages used in DockStatAPI (also Dev packages) | validate-npm-package-license@3.0.4 | https://github.com/kemitchell/validate-npm-package-license.js | Kyle E. Mitchell | | walker@1.0.8 | https://github.com/daaku/nodejs-walker | Naitik Shah | +### License: Artistic-2.0 + +| Name | Repository | Publisher | +| ---------- | -------------------------- | ----------- | +| npm@11.0.0 | https://github.com/npm/cli | GitHub Inc. | + +### License: BlueOak-1.0.0 + +| Name | Repository | Publisher | +| ---------------------------- | ------------------------------------------------ | ------------------ | +| chownr@3.0.0 | https://github.com/isaacs/chownr | Isaac Z. Schlueter | +| jackspeak@3.4.3 | https://github.com/isaacs/jackspeak | Isaac Z. Schlueter | +| package-json-from-dist@1.0.1 | https://github.com/isaacs/package-json-from-dist | Isaac Z. Schlueter | +| path-scurry@1.11.1 | https://github.com/isaacs/path-scurry | Isaac Z. Schlueter | +| yallist@5.0.0 | https://github.com/isaacs/yallist | Isaac Z. Schlueter | + ### License: CC-BY-3.0 | Name | Repository | Publisher | diff --git a/TODO.md b/TODO.md index abf109dd..44a128d3 100644 --- a/TODO.md +++ b/TODO.md @@ -7,12 +7,12 @@ - [x] Structure code differently - [x] Write new README and make the docs better - [x] Update more files to correct TS syntax => remove "any" -- [ ] Websockets +- [X] Websockets - [x] Better /api/status endpoint with connection status of each host - [x] Update notification service - [x] Adjust process.env variables since they don't really work as expected (See [commit](https://github.com/Its4Nik/dockstatapi/pull/21/commits/a03b58c7a17e269f46216df5492e18d008774961)) - [ ] Better project structure - [x] Update logging => Better errors - [x] Update json responses -- [ ] Swagger update +- [X] Swagger update - [ ] Edge case testing diff --git a/__tests__/auth.spec.ts b/__tests__/auth.spec.ts index 886f75e6..bcf0eb21 100644 --- a/__tests__/auth.spec.ts +++ b/__tests__/auth.spec.ts @@ -1,14 +1,19 @@ -export const testPass: string = "123321"; +export const testPass = "123456789"; +import { Server } from 'http'; import supertest from "supertest"; import { startServer } from "../src/utils/startServer"; import app from "../src/server"; -const server = supertest("http://localhost:13001"); -startServer(app, 13001); +const port = 13001; +const server = new Server(app); + +startServer(app, server, port); + +const request = supertest(`http://localhost:${port}`); describe("Authentication", () => { it("Enable Authentication", async () => { - const res = await server.post(`/auth/enable?password=${testPass}`); + const res = await request.post(`/auth/enable?password=${testPass}`); expect(res.status).toEqual(200); expect(res.type).toEqual(expect.stringContaining("json")); expect(res.body).toHaveProperty( @@ -18,16 +23,16 @@ describe("Authentication", () => { }); it("Test no password", async () => { - const res = await server.get("/api/status"); + const res = await request.get("/api/status"); expect(res.status).toEqual(403); expect(res.type).toEqual(expect.stringContaining("json")); }); it("Disable authentication", async () => { - const res = await server + const res = await request .post(`/auth/disable?password=${testPass}`) .set("x-password", testPass); expect(res.status).toEqual(200); expect(res.type).toEqual(expect.stringContaining("json")); }); -}); +}); \ No newline at end of file diff --git a/__tests__/config.spec.ts b/__tests__/config.spec.ts index ebb878da..d6356004 100644 --- a/__tests__/config.spec.ts +++ b/__tests__/config.spec.ts @@ -1,9 +1,14 @@ import supertest from "supertest"; import { startServer } from "../src/utils/startServer"; import app from "../src/server"; -const server = supertest("http://localhost:13002"); +import { Server } from 'http'; -startServer(app, 13002); +const port = 13002; +const server = new Server(app); + +startServer(app, server, port); + +const request = supertest(`http://localhost:${port}`); const mockServerName: string = "mockstatapi"; const mockServerIP: string = "127.0.0.1"; @@ -11,33 +16,33 @@ const mockServerPort: number = 2375; describe("Config endpoints", () => { it("Add an host", async () => { - let res = await server.put( + let res = await request.put( `/conf/addHost?name=${mockServerName}&url=${mockServerIP}&port=${mockServerPort}`, ); expect(res.status).toEqual(200); - res = await server.get("/api/hosts"); + res = await request.get("/api/hosts"); expect(res.status).toEqual(200); expect(res.body).toContain("mockstatapi"); }); it("Adjust scheduler", async () => { - let res = await server.put("/conf/scheduler?interval=10m"); + let res = await request.put("/conf/scheduler?interval=10m"); expect(res.status).toEqual(200); - res = await server.get("/api/current-schedule"); + res = await request.get("/api/current-schedule"); expect(res.status).toEqual(200); // Reset to standart 5m - res = await server.put("/conf/scheduler?interval=5m"); + res = await request.put("/conf/scheduler?interval=5m"); expect(res.status).toEqual(200); }); it("Remove Host from config", async () => { - let res = await server.delete(`/conf/removeHost?hostName=mockstatapi`); + let res = await request.delete(`/conf/removeHost?hostName=mockstatapi`); expect(res.status).toEqual(200); - res = await server.get("/api/hosts"); + res = await request.get("/api/hosts"); expect(res.status).toEqual(200); expect(res.body).not.toHaveProperty("mockstatapi"); }); diff --git a/__tests__/database.spec.ts b/__tests__/database.spec.ts index 506778c5..c0c46c1b 100644 --- a/__tests__/database.spec.ts +++ b/__tests__/database.spec.ts @@ -1,26 +1,31 @@ import supertest from "supertest"; import { startServer } from "../src/utils/startServer"; import app from "../src/server"; +import { Server } from 'http'; -startServer(app, 13003); -const server = supertest("http://localhost:13003"); +const port = 13003; +const server = new Server(app); + +startServer(app, server, port); + +const request = supertest(`http://localhost:${port}`); describe("Database", () => { it("Get latest database entry", async () => { - const res = await server.get("/data/latest"); + const res = await request.get("/data/latest"); expect(res.status).toEqual(200); }); it("Get all database entries", async () => { - const res = await server.get("/data/all"); + const res = await request.get("/data/all"); expect(res.status).toEqual(200); }); it("Clear database", async () => { - let res = await server.delete("/data/clear"); + let res = await request.delete("/data/clear"); expect(res.status).toEqual(200); - res = await server.get("/data/latest"); + res = await request.get("/data/latest"); expect(res.status).toEqual(404); expect(res.body).toHaveProperty( "message", diff --git a/__tests__/frontend.spec.ts b/__tests__/frontend.spec.ts index 92fe3e2e..753b98da 100644 --- a/__tests__/frontend.spec.ts +++ b/__tests__/frontend.spec.ts @@ -1,6 +1,14 @@ import supertest from "supertest"; import { startServer } from "../src/utils/startServer"; import app from "../src/server"; +import { Server } from 'http'; + +const port = 13004; +const server = new Server(app); + +startServer(app, server, port); + +const request = supertest(`http://localhost:${port}`); const sec: number = 1000; @@ -21,41 +29,39 @@ const verifiedResponse = [ }, ]; -startServer(app, 13004); -const server = supertest("http://localhost:13004"); describe("Test frontend specific configurations", () => { it( "Setup the configuration file", async () => { // Hide container - let res = await server.delete(`/frontend/hide/${mockContainer}`); + let res = await request.delete(`/frontend/hide/${mockContainer}`); expect(res.status).toEqual(200); // Add Tag(s) - res = await server.post(`/frontend/tag/${mockContainer}/${mockTag1}`); + res = await request.post(`/frontend/tag/${mockContainer}/${mockTag1}`); expect(res.status).toEqual(200); - res = await server.post(`/frontend/tag/${mockContainer}/${mockTag2}`); + res = await request.post(`/frontend/tag/${mockContainer}/${mockTag2}`); expect(res.status).toEqual(200); // Pin container - res = await server.post(`/frontend/pin/${mockContainer}`); + res = await request.post(`/frontend/pin/${mockContainer}`); expect(res.status).toEqual(200); // Add link - res = await server.post( + res = await request.post( `/frontend/add-link/${mockContainer}/${encodeURIComponent(mockLink)}`, ); expect(res.status).toEqual(200); // Add icon - res = await server.post( + res = await request.post( `/frontend/add-icon/${mockContainer}/${mockIcon}/false`, ); @@ -65,7 +71,7 @@ describe("Test frontend specific configurations", () => { ); it("Verify the configuration", async () => { - const res = await server.get("/api/frontend-config"); + const res = await request.get("/api/frontend-config"); expect(res.status).toEqual(200); expect(res.body).toEqual(verifiedResponse); @@ -75,35 +81,35 @@ describe("Test frontend specific configurations", () => { "Reset configuration", async () => { // Show container - let res = await server.post(`/frontend/show/${mockContainer}`); + let res = await request.post(`/frontend/show/${mockContainer}`); expect(res.status).toEqual(200); // Remove tag(s) - res = await server.delete( + res = await request.delete( `/frontend/remove-tag/${mockContainer}/${mockTag1}`, ); expect(res.status).toEqual(200); - res = await server.delete( + res = await request.delete( `/frontend/remove-tag/${mockContainer}/${mockTag2}`, ); expect(res.status).toEqual(200); // Unpin - res = await server.delete(`/frontend/unpin/${mockContainer}`); + res = await request.delete(`/frontend/unpin/${mockContainer}`); expect(res.status).toEqual(200); // Remove link - res = await server.delete(`/frontend/remove-link/${mockContainer}`); + res = await request.delete(`/frontend/remove-link/${mockContainer}`); expect(res.status).toEqual(200); // Remove icon - res = await server.delete(`/frontend/remove-icon/${mockContainer}`); + res = await request.delete(`/frontend/remove-icon/${mockContainer}`); expect(res.status).toEqual(200); }, @@ -111,7 +117,7 @@ describe("Test frontend specific configurations", () => { ); it("Verify the reset configuration", async () => { - const res = await server.get("/api/frontend-config"); + const res = await request.get("/api/frontend-config"); expect(res.status).toEqual(200); expect(res.body).toEqual([]); diff --git a/__tests__/getters.spec.ts b/__tests__/getters.spec.ts index 3b4b855e..3ba5950b 100644 --- a/__tests__/getters.spec.ts +++ b/__tests__/getters.spec.ts @@ -2,15 +2,19 @@ import { createPreviousResponse } from "./util/previousResponse"; import supertest from "supertest"; import { startServer } from "../src/utils/startServer"; import app from "../src/server"; +import { Server } from 'http'; -const PreviousResponse = createPreviousResponse(); -const server = supertest("http://localhost:13005"); +const port = 13005; +const server = new Server(app); + +startServer(app, server, port); -startServer(app, 13005); +const request = supertest(`http://localhost:${port}`); +const PreviousResponse = createPreviousResponse(); describe("Get endpoints", () => { it("GET /api/hosts", async () => { - const res = await server.get("/api/hosts"); + const res = await request.get("/api/hosts"); expect(res.status).toEqual(200); expect(res.type).toEqual(expect.stringContaining("json")); @@ -32,40 +36,40 @@ describe("Get endpoints", () => { return; } - const res = await server.get(`/api/host/${host}/stats`); + const res = await request.get(`/api/host/${host}/stats`); expect(res.status).toEqual(200); expect(res.type).toEqual(expect.stringContaining("json")); }); it("GET /api/system", async () => { - const res = await server.get("/api/system"); + const res = await request.get("/api/system"); expect(res.status).toEqual(200); expect(res.type).toEqual(expect.stringContaining("json")); }); it("GET /api/status", async () => { - const res = await server.get("/api/status"); + const res = await request.get("/api/status"); expect(res.status).toEqual(200); expect(res.type).toEqual(expect.stringContaining("json")); expect(res.body).toHaveProperty("ApiReachable", true); }); it("GET /api/containers", async () => { - const res = await server.get("/api/containers"); + const res = await request.get("/api/containers"); expect(res.status).toEqual(200); expect(res.type).toEqual(expect.stringContaining("json")); }); it("GET /api/config", async () => { - const res = await server.get("/api/config"); + const res = await request.get("/api/config"); expect(res.status).toEqual(200); expect(res.type).toEqual(expect.stringContaining("json")); expect(res.body).toHaveProperty("hosts"); }); it("GET /api/current-schedule", async () => { - const res = await server.get("/api/current-schedule"); + const res = await request.get("/api/current-schedule"); expect(res.status).toEqual(200); expect(res.type).toEqual(expect.stringContaining("json")); @@ -73,20 +77,20 @@ describe("Get endpoints", () => { }); it("GET /api/frontend-config", async () => { - const res = await server.get("/api/frontend-config"); + const res = await request.get("/api/frontend-config"); expect(res.status).toEqual(200); expect(res.type).toEqual(expect.stringContaining("json")); }); it("GET /ha/config", async () => { - const res = await server.get("/ha/config"); + const res = await request.get("/ha/config"); expect(res.status).toEqual(200); expect(res.type).toEqual(expect.stringContaining("json")); }); it("GET /notification-service/get-template", async () => { - const res = await server.get("/notification-service/get-template"); + const res = await request.get("/notification-service/get-template"); expect(res.status).toEqual(200); expect(res.type).toEqual(expect.stringContaining("json")); diff --git a/environment.d.ts b/environment.d.ts index 74801b7d..df2595f5 100644 --- a/environment.d.ts +++ b/environment.d.ts @@ -4,6 +4,7 @@ declare global { // Node specific: NODE_ENV: "development" | "production" | "testing"; PORT: string | undefined; + CI: "true" | null; } } } diff --git a/package-lock.json b/package-lock.json index 0ec69732..6efc7ed3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,12 +13,15 @@ "chokidar": "^4.0.1", "cors": "^2.8.5", "cytoscape": "^3.30.4", + "docker-compose": "^1.1.0", "dockerode": "^4.0.2", "express": "^4.21.1", "express-rate-limit": "^7.4.1", "https": "^1.0.0", + "i": "^0.3.7", "ipaddr.js": "^2.2.0", "nodemailer": "^6.9.16", + "npm": "^11.0.0", "puppeteer": "^24.0.0", "sqlite3": "^5.1.7", "swagger-ui-express": "^5.0.1", @@ -42,6 +45,7 @@ "@types/supports-color": "^8.1.3", "@types/swagger-jsdoc": "^6.0.4", "@types/swagger-ui-express": "^4.1.7", + "@types/ws": "^8.5.14", "@types/yamljs": "^0.2.34", "@typescript-eslint/eslint-plugin": "^8.18.2", "@typescript-eslint/parser": "^8.18.2", @@ -77,17 +81,6 @@ "node": ">=6.0.0" } }, - "node_modules/@ampproject/remapping/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -103,9 +96,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", - "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", "dev": true, "license": "MIT", "engines": { @@ -113,22 +106,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", + "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", + "@babel/helpers": "^7.26.7", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -154,14 +147,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -170,25 +163,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -198,16 +180,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -218,13 +190,6 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, "node_modules/@babel/helper-module-imports": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", @@ -258,9 +223,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "dev": true, "license": "MIT", "engines": { @@ -297,27 +262,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.26.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -581,17 +546,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", + "@babel/types": "^7.26.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -610,9 +575,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", "dev": true, "license": "MIT", "dependencies": { @@ -658,6 +623,17 @@ "node": ">=12" } }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@dabh/diagnostics": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", @@ -1107,13 +1083,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.5", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -1146,9 +1122,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", - "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz", + "integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1244,9 +1220,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", - "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "version": "9.20.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz", + "integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==", "dev": true, "license": "MIT", "engines": { @@ -1254,9 +1230,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", - "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1264,18 +1240,32 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", - "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.10.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -1283,6 +1273,37 @@ "license": "MIT", "optional": true }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.6", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.6.tgz", + "integrity": "sha512-JXUj6PI0oqqzTGvKtzOkxtpsyPRNsrmhh41TtIz/zEB6J+AUiZZ0dxWzcMwO9Ns5rmSPuMdghlTbUuqIM48d3Q==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1660,17 +1681,6 @@ } } }, - "node_modules/@jest/reporters/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", @@ -1699,17 +1709,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/source-map/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@jest/test-result": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", @@ -1769,17 +1768,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/transform/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@jest/types": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", @@ -1813,17 +1801,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -1852,14 +1829,24 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" } }, "node_modules/@mapbox/node-pre-gyp": { @@ -1959,19 +1946,82 @@ "node": ">=10" } }, + "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.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "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/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "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.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, "node_modules/@puppeteer/browsers": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.7.0.tgz", - "integrity": "sha512-bO61XnTuopsz9kvtfqhVbH6LTM1koxK0IlBR+yuVrM2LB7mk8+5o1w18l5zqd5cs8xlf+ntgambqRqGifMDjog==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.7.1.tgz", + "integrity": "sha512-MK7rtm8JjaxPN7Mf1JdZIZKPD2Z+W7osvrC1vjpvfOX1K0awDIHYbNi89f7eotp7eMUn2shWnt03HwVbriXtKQ==", "license": "Apache-2.0", "dependencies": { "debug": "^4.4.0", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", - "semver": "^7.6.3", - "tar-fs": "^3.0.6", - "unbzip2-stream": "^1.4.3", + "semver": "^7.7.0", + "tar-fs": "^3.0.8", "yargs": "^17.7.2" }, "bin": { @@ -1982,17 +2032,17 @@ } }, "node_modules/@puppeteer/browsers/node_modules/tar-fs": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", - "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz", + "integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==", "license": "MIT", "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" } }, "node_modules/@puppeteer/browsers/node_modules/tar-stream": { @@ -2178,9 +2228,9 @@ } }, "node_modules/@types/cytoscape": { - "version": "3.21.8", - "resolved": "https://registry.npmjs.org/@types/cytoscape/-/cytoscape-3.21.8.tgz", - "integrity": "sha512-6Bo9ZDrv0vfwe8Sg/ERc5VL0yU0gYvP4dgZi0fAXYkKHfyHaNqWRMcwYm3mu4sLsXbB8ZuXE75sR7qnaOL5JgQ==", + "version": "3.21.9", + "resolved": "https://registry.npmjs.org/@types/cytoscape/-/cytoscape-3.21.9.tgz", + "integrity": "sha512-JyrG4tllI6jvuISPjHK9j2Xv/LTbnLekLke5otGStjFluIyA9JjgnvgZrSBsp8cEDpiTjwgZUZwpPv8TSBcoLw==", "dev": true, "license": "MIT" }, @@ -2196,9 +2246,9 @@ } }, "node_modules/@types/dockerode": { - "version": "3.3.32", - "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.32.tgz", - "integrity": "sha512-xxcG0g5AWKtNyh7I7wswLdFvym4Mlqks5ZlKzxEUrGHS0r0PUOfxm2T0mspwu10mHQqu3Ck3MI3V2HqvLWE1fg==", + "version": "3.3.34", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.34.tgz", + "integrity": "sha512-mH9SuIb8NuTDsMus5epcbTzSbEo52fKLBMo0zapzYIAIyfDqoIFn7L3trekHLKC8qmxGV++pPUP4YqQ9n5v2Zg==", "dev": true, "license": "MIT", "dependencies": { @@ -2235,9 +2285,9 @@ "license": "MIT" }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.3.tgz", - "integrity": "sha512-JEhMNwUJt7bw728CydvYzntD0XJeTmDnvwLlbfbAhE7Tbslm/ax6bdIiUwTgeVlZTsJQPwZwKpAkyDtIjsvx3g==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", "dev": true, "license": "MIT", "dependencies": { @@ -2324,10 +2374,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.3.tgz", - "integrity": "sha512-DifAyw4BkrufCILvD3ucnuN8eydUfc/C1GlyrnI+LK6543w5/L3VeVgf05o3B4fqSXP1dKYLOZsKfutpxPzZrw==", - "devOptional": true, + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -2355,9 +2404,9 @@ } }, "node_modules/@types/qs": { - "version": "6.9.17", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", - "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", "dev": true, "license": "MIT" }, @@ -2392,9 +2441,9 @@ } }, "node_modules/@types/ssh2": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.1.tgz", - "integrity": "sha512-ZIbEqKAsi5gj35y4P4vkJYly642wIbY6PqoN0xiyQGshKUGXR9WQjF/iF9mXBQ8uBKy3ezfsCkcoHKhd0BzuDA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.4.tgz", + "integrity": "sha512-9JTQgVBWSgq6mAen6PVnrAmty1lqgCMvpfN+1Ck5WRUsyMYPa6qd50/vMJ0y1zkGpOEgLzm8m8Dx/Y5vRouLaA==", "dev": true, "license": "MIT", "dependencies": { @@ -2402,9 +2451,9 @@ } }, "node_modules/@types/ssh2/node_modules/@types/node": { - "version": "18.19.69", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.69.tgz", - "integrity": "sha512-ECPdY1nlaiO/Y6GUnwgtAAhLNaQ53AyIVz+eILxpEo5OvuqE6yWkqWBIb5dU0DqhKQtMeny+FBD3PK6lm7L5xQ==", + "version": "18.19.75", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.75.tgz", + "integrity": "sha512-UIksWtThob6ZVSyxcOqCLOUNg/dyO1Qvx4McgeuhrEtHTLFTf7BBhEazaE4K806FGTPtzd/2sE90qn4fVr7cyw==", "dev": true, "license": "MIT", "dependencies": { @@ -2480,6 +2529,16 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, + "node_modules/@types/ws": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", + "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yamljs": { "version": "0.2.34", "resolved": "https://registry.npmjs.org/@types/yamljs/-/yamljs-0.2.34.tgz", @@ -2515,21 +2574,21 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz", - "integrity": "sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.23.0.tgz", + "integrity": "sha512-vBz65tJgRrA1Q5gWlRfvoH+w943dq9K1p1yDBY2pc+a1nbBLZp7fB9+Hk8DaALUbzjqlMfgaqlVPT1REJdkt/w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/type-utils": "8.19.0", - "@typescript-eslint/utils": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.23.0", + "@typescript-eslint/type-utils": "8.23.0", + "@typescript-eslint/utils": "8.23.0", + "@typescript-eslint/visitor-keys": "8.23.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2545,16 +2604,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", - "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.23.0.tgz", + "integrity": "sha512-h2lUByouOXFAlMec2mILeELUbME5SZRN/7R9Cw2RD2lRQQY08MWMM+PmVVKKJNK1aIwqTo9t/0CvOxwPbRIE2Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.23.0", + "@typescript-eslint/types": "8.23.0", + "@typescript-eslint/typescript-estree": "8.23.0", + "@typescript-eslint/visitor-keys": "8.23.0", "debug": "^4.3.4" }, "engines": { @@ -2570,14 +2629,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", - "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.23.0.tgz", + "integrity": "sha512-OGqo7+dXHqI7Hfm+WqkZjKjsiRtFUQHPdGMXzk5mYXhJUedO7e/Y7i8AK3MyLMgZR93TX4bIzYrfyVjLC+0VSw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0" + "@typescript-eslint/types": "8.23.0", + "@typescript-eslint/visitor-keys": "8.23.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2588,16 +2647,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz", - "integrity": "sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.23.0.tgz", + "integrity": "sha512-iIuLdYpQWZKbiH+RkCGc6iu+VwscP5rCtQ1lyQ7TYuKLrcZoeJVpcLiG8DliXVkUxirW/PWlmS+d6yD51L9jvA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/utils": "8.19.0", + "@typescript-eslint/typescript-estree": "8.23.0", + "@typescript-eslint/utils": "8.23.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2612,9 +2671,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", - "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.23.0.tgz", + "integrity": "sha512-1sK4ILJbCmZOTt9k4vkoulT6/y5CHJ1qUYxqpF1K/DBAd8+ZUL4LlSCxOssuH5m4rUaaN0uS0HlVPvd45zjduQ==", "dev": true, "license": "MIT", "engines": { @@ -2626,20 +2685,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", - "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.23.0.tgz", + "integrity": "sha512-LcqzfipsB8RTvH8FX24W4UUFk1bl+0yTOf9ZA08XngFwMg4Kj8A+9hwz8Cr/ZS4KwHrmo9PJiLZkOt49vPnuvQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/types": "8.23.0", + "@typescript-eslint/visitor-keys": "8.23.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2653,16 +2712,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.0.tgz", - "integrity": "sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.23.0.tgz", + "integrity": "sha512-uB/+PSo6Exu02b5ZEiVtmY6RVYO7YU5xqgzTIVZwTHvvK3HsL8tZZHFaTLFtRG3CsV4A5mhOv+NZx5BlhXPyIA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0" + "@typescript-eslint/scope-manager": "8.23.0", + "@typescript-eslint/types": "8.23.0", + "@typescript-eslint/typescript-estree": "8.23.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2677,13 +2736,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", - "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.23.0.tgz", + "integrity": "sha512-oWWhcWDLwDfu++BGTZcmXWqpwtkwb5o7fxUIGksMQQDSdPW9prsSnfIOZMlsj4vBOSrcnjIUZMiIjODgGosFhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/types": "8.23.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3141,42 +3200,60 @@ "optional": true }, "node_modules/bare-fs": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", - "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.0.1.tgz", + "integrity": "sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg==", "license": "Apache-2.0", "optional": true, "dependencies": { "bare-events": "^2.0.0", - "bare-path": "^2.0.0", + "bare-path": "^3.0.0", "bare-stream": "^2.0.0" + }, + "engines": { + "bare": ">=1.7.0" } }, "node_modules/bare-os": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", - "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.4.0.tgz", + "integrity": "sha512-9Ous7UlnKbe3fMi7Y+qh0DwAup6A1JkYgPnjvMDNOlmnxNRQvQ/7Nst+OnUQKzk0iAT0m9BisbDVp9gCv8+ETA==", "license": "Apache-2.0", - "optional": true + "optional": true, + "engines": { + "bare": ">=1.6.0" + } }, "node_modules/bare-path": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", - "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", "license": "Apache-2.0", "optional": true, "dependencies": { - "bare-os": "^2.1.0" + "bare-os": "^3.0.1" } }, "node_modules/bare-stream": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.1.tgz", - "integrity": "sha512-eVZbtKM+4uehzrsj49KtCy3Pbg7kO1pJ3SKZ1SFrIH/0pnj9scuGGgUlNDf/7qS8WKtGdiJY5Kyhs/ivYPTB/g==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", + "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", "license": "Apache-2.0", "optional": true, "dependencies": { "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } } }, "node_modules/base64-js": { @@ -3327,9 +3404,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", - "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -3470,6 +3547,19 @@ "node": ">= 10" } }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cacache/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -3483,6 +3573,13 @@ "node": ">=10" } }, + "node_modules/cacache/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", @@ -3532,9 +3629,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001690", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", - "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", + "version": "1.0.30001698", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001698.tgz", + "integrity": "sha512-xJ3km2oiG/MbNU8G6zIq6XRZ6HtAOVXsbOrP/blGazi52kc5Yy7b6sDA5O+FbROzRrV7BSTllLHuNvmawYUJjw==", "dev": true, "funding": [ { @@ -3604,13 +3701,13 @@ } }, "node_modules/chromium-bidi": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.12.0.tgz", - "integrity": "sha512-xzXveJmX826GGq1MeE5okD8XxaDT8172CXByhFJ687eY65rbjOIebdbUuQh+jXKaNyGKI14Veb3KjLLmSueaxA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-1.2.0.tgz", + "integrity": "sha512-XtdJ1GSN6S3l7tO7F77GhNsw0K367p0IsLYf2yZawCVAKKC3lUvDhPdMVrB2FNhmhfW43QGYbEX3Wg6q0maGwQ==", "license": "Apache-2.0", "dependencies": { - "mitt": "3.0.1", - "zod": "3.24.1" + "mitt": "^3.0.1", + "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" @@ -3633,9 +3730,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true, "license": "MIT" }, @@ -3767,9 +3864,9 @@ } }, "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, "license": "MIT", "engines": { @@ -3946,9 +4043,9 @@ } }, "node_modules/cytoscape": { - "version": "3.30.4", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.4.tgz", - "integrity": "sha512-OxtlZwQl1WbwMmLiyPSEBuzeTIQnwZhJYYWFzZ2PhEHVFwpeaqNIkUzSiso00D98qk60l8Gwon2RP304d3BJ1A==", + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.31.0.tgz", + "integrity": "sha512-zDGn1K/tfZwEnoGOcHc0H4XazqAAXAuDpcYw9mUnUjATjqljyCNGJv8uEvbvxGaGHaVshxMecyl6oc6uKzRfbw==", "license": "MIT", "engines": { "node": ">=0.10" @@ -4087,9 +4184,9 @@ } }, "node_modules/dependency-cruiser": { - "version": "16.8.0", - "resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-16.8.0.tgz", - "integrity": "sha512-VyBzIrLHfG7rT36URln+CTy8VSjrLB7YDlMx5vtBSHRHCOXgLUCcP4n5ZoD+s166T0i5LN33q1CvBkEOGsDTSg==", + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-16.9.0.tgz", + "integrity": "sha512-Gc/xHNOBq1nk5i7FPCuexCD0m2OXB/WEfiSHfNYQaQaHZiZltnl5Ixp/ZG38Jvi8aEhKBQTHV4Aw6gmR7rWlOw==", "dev": true, "license": "MIT", "dependencies": { @@ -4099,9 +4196,9 @@ "acorn-loose": "^8.4.0", "acorn-walk": "^8.3.4", "ajv": "^8.17.1", - "commander": "^12.1.0", - "enhanced-resolve": "^5.17.1", - "ignore": "^6.0.2", + "commander": "^13.0.0", + "enhanced-resolve": "^5.18.0", + "ignore": "^7.0.0", "interpret": "^3.1.1", "is-installed-globally": "^1.0.0", "json5": "^2.2.3", @@ -4129,9 +4226,9 @@ } }, "node_modules/dependency-cruiser/node_modules/ignore": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz", - "integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.3.tgz", + "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==", "dev": true, "license": "MIT", "engines": { @@ -4168,9 +4265,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1367902", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz", - "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==", + "version": "0.0.1402036", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1402036.tgz", + "integrity": "sha512-JwAYQgEvm3yD45CHB+RmF5kMbWtXBaOGwuxa87sZogHcLCv8c/IqnThaoQ1y60d7pXWjSKWQphPEc+1rAScVdg==", "license": "BSD-3-Clause" }, "node_modules/dezalgo": { @@ -4204,10 +4301,22 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/docker-compose": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-1.1.0.tgz", + "integrity": "sha512-VrkQJNafPQ5d6bGULW0P6KqcxSkv3ZU5Wn2wQA19oB71o7+55vQ9ogFe2MMeNbK+jc9rrKVy280DnHO5JLMWOQ==", + "license": "MIT", + "dependencies": { + "yaml": "^2.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/docker-modem": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.3.tgz", - "integrity": "sha512-89zhop5YVhcPEt5FpUFGr3cDyceGhq/F9J+ZndQ4KfqNvfbJpPMfgeixFgUj5OjCYAboElqODxY5Z1EBsSa6sg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz", + "integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==", "license": "Apache-2.0", "dependencies": { "debug": "^4.1.1", @@ -4220,14 +4329,18 @@ } }, "node_modules/dockerode": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.2.tgz", - "integrity": "sha512-9wM1BVpVMFr2Pw3eJNXrYYt6DT9k0xMcsSCjtPvyQ+xa1iPg/Mo3T/gUcwI0B2cczqCeCYRPF8yFYDwtFXT0+w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.4.tgz", + "integrity": "sha512-6GYP/EdzEY50HaOxTVTJ2p+mB5xDHTMJhS+UoGrVyS6VC+iQRh7kZ4FRpUYq6nziby7hPqWhOrFFUFTMUZJJ5w==", "license": "Apache-2.0", "dependencies": { "@balena/dockerignore": "^1.0.2", - "docker-modem": "^5.0.3", - "tar-fs": "~2.0.1" + "@grpc/grpc-js": "^1.11.1", + "@grpc/proto-loader": "^0.7.13", + "docker-modem": "^5.0.6", + "protobufjs": "^7.3.2", + "tar-fs": "~2.0.1", + "uuid": "^10.0.0" }, "engines": { "node": ">= 8.0" @@ -4270,9 +4383,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.76", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.76.tgz", - "integrity": "sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ==", + "version": "1.5.96", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.96.tgz", + "integrity": "sha512-8AJUW6dh75Fm/ny8+kZKJzI1pgoE8bKLZlzDU2W1ENd+DXKJrx7I7l9hb8UWR4ojlnb5OlixMt00QWiYJoVw1w==", "dev": true, "license": "ISC" }, @@ -4343,9 +4456,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", - "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "dev": true, "license": "MIT", "dependencies": { @@ -4381,12 +4494,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/error-ex/node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT" - }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -4406,9 +4513,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -4507,19 +4614,19 @@ } }, "node_modules/eslint": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", - "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "version": "9.20.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.0.tgz", + "integrity": "sha512-aL4F8167Hg4IvsW89ejnpTwx+B/UQRzJPGgbIOl+4XqffWsahVVsLEWoZvnrVuwpWmnRd7XeXmQI1zlKcFDteA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", + "@eslint/core": "^0.11.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.17.0", - "@eslint/plugin-kit": "^0.2.3", + "@eslint/js": "9.20.0", + "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", @@ -4938,9 +5045,9 @@ "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -4948,7 +5055,7 @@ "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -4989,16 +5096,26 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", - "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "BSD-3-Clause" }, "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", "dev": true, "license": "ISC", "dependencies": { @@ -5244,9 +5361,9 @@ "license": "ISC" }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5342,9 +5459,9 @@ } }, "node_modules/get-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.0.tgz", - "integrity": "sha512-TtLgOcKaF1nMP2ijJnITkE4nRhbpshHhmzKiuhmSniiwWzovoqwqQ8rNuhf0mXJOqIY5iU+QkUe0CkJYrLsG9w==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -5368,9 +5485,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", - "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", "dev": true, "license": "MIT", "dependencies": { @@ -5599,18 +5716,25 @@ } }, "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "license": "MIT", - "optional": true, "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" } }, "node_modules/https": { @@ -5652,6 +5776,14 @@ "ms": "^2.0.0" } }, + "node_modules/i": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", + "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -5702,9 +5834,9 @@ "license": "ISC" }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -5824,9 +5956,9 @@ } }, "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "license": "MIT" }, "node_modules/is-binary-path": { @@ -6546,16 +6678,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runtime/node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/jest-snapshot": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", @@ -6985,6 +7107,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -7016,17 +7144,20 @@ "node": ">= 12.0.0" } }, + "node_modules/long": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==", + "license": "Apache-2.0" + }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "license": "ISC", - "optional": true, "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "yallist": "^3.0.2" } }, "node_modules/make-dir": { @@ -7088,6 +7219,56 @@ "node": ">= 10" } }, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -7369,6 +7550,12 @@ "node": ">=8" } }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", @@ -7382,6 +7569,12 @@ "node": ">= 8" } }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", @@ -7430,9 +7623,9 @@ "optional": true }, "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", "license": "MIT" }, "node_modules/natural-compare": { @@ -7461,9 +7654,9 @@ } }, "node_modules/node-abi": { - "version": "3.71.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", - "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", + "version": "3.74.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz", + "integrity": "sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==", "license": "MIT", "dependencies": { "semver": "^7.3.5" @@ -7591,9 +7784,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "6.9.16", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz", - "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz", + "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -7750,63 +7943,2519 @@ "bin": { "nopt": "bin/nopt.js" }, - "engines": { - "node": ">=6" + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.1.0.tgz", + "integrity": "sha512-rPMBrZud26lI/LcjQeLw/K5Hf1apXMKgkpNNEzp0YQYmM877+T1ZNKPcB2hnTi7e6fBNz8xLtMMn/w46fVUqGw==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which" + ], + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^9.0.0", + "@npmcli/config": "^10.0.1", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.1.1", + "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "@sigstore/tuf": "^3.0.0", + "abbrev": "^3.0.0", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.4.1", + "ci-info": "^4.1.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.0.2", + "ini": "^5.0.0", + "init-package-json": "^8.0.0", + "is-cidr": "^5.1.0", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^10.0.0", + "libnpmdiff": "^8.0.0", + "libnpmexec": "^10.0.0", + "libnpmfund": "^7.0.0", + "libnpmorg": "^8.0.0", + "libnpmpack": "^9.0.0", + "libnpmpublish": "^11.0.0", + "libnpmsearch": "^9.0.0", + "libnpmteam": "^8.0.0", + "libnpmversion": "^8.0.0", + "make-fetch-happen": "^14.0.3", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.0.0", + "nopt": "^8.0.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.1", + "npm-package-arg": "^12.0.1", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.2", + "npm-user-validate": "^3.0.0", + "p-map": "^7.0.3", + "pacote": "^21.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.0.0", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.0", + "which": "^5.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true, + "license": "ISC" + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^9.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^21.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^4.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "10.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^21.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.1.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/core": { + "version": "2.0.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.3", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^14.0.1", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "2.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.1.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.4.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "7.0.0", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.1.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^3.0.0", + "diff": "^7.0.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^21.0.0", + "tar": "^6.2.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.0", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^21.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.0", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^21.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "11.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^3.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/nopt/node_modules/abbrev": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "7.0.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "21.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^10.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "5.0.10", + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.6.3", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^3.0.0", + "@sigstore/tuf": "^3.0.0", + "@sigstore/verify": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.21", + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.3.6", + "make-fetch-happen": "^14.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "dev": true, - "license": "ISC" - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", "dependencies": { - "path-key": "^3.0.0" + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=8" + "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, "node_modules/npmlog": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", @@ -7839,9 +10488,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -8032,19 +10681,6 @@ "node": ">= 14" } }, - "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -8058,20 +10694,6 @@ "node": ">= 14" } }, - "node_modules/pac-proxy-agent/node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/pac-resolver": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", @@ -8271,9 +10893,9 @@ } }, "node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", - "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", "license": "MIT", "dependencies": { "detect-libc": "^2.0.0", @@ -8281,7 +10903,7 @@ "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", + "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", @@ -8394,6 +11016,30 @@ "node": ">= 6" } }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -8444,19 +11090,6 @@ "node": ">= 14" } }, - "node_modules/proxy-agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/proxy-agent/node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -8479,20 +11112,6 @@ "node": ">=12" } }, - "node_modules/proxy-agent/node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -8527,17 +11146,17 @@ } }, "node_modules/puppeteer": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.0.0.tgz", - "integrity": "sha512-KRF2iWdHGSZkQ8pqftR5XR1jqnTqKRVZghMGJfJ665zS8++0cErRG2tXWfp98YqvMzsVLHfzBtTQlk0MMhCxzg==", + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.2.0.tgz", + "integrity": "sha512-z8vv7zPEgrilIbOo3WNvM+2mXMnyM9f4z6zdrB88Fzeuo43Oupmjrzk3EpuvuCtyK0A7Lsllfx7Z+4BvEEGJcQ==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.7.0", - "chromium-bidi": "0.12.0", + "@puppeteer/browsers": "2.7.1", + "chromium-bidi": "1.2.0", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1367902", - "puppeteer-core": "24.0.0", + "devtools-protocol": "0.0.1402036", + "puppeteer-core": "24.2.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -8548,15 +11167,15 @@ } }, "node_modules/puppeteer-core": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.0.0.tgz", - "integrity": "sha512-bHVXmnkYnMVSbsD+pJGt8fmGZLaVYOAieVnJcDxtLIVTMq0s5RfYdzN4xVlFoBQ3T06/sPkXxca3VLVfaqLxzg==", + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.2.0.tgz", + "integrity": "sha512-e4A4/xqWdd4kcE6QVHYhJ+Qlx/+XpgjP4d8OwBx0DJoY/nkIRhSgYmKQnv7+XSs1ofBstalt+XPGrkaz4FoXOQ==", "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.7.0", - "chromium-bidi": "0.11.0", + "@puppeteer/browsers": "2.7.1", + "chromium-bidi": "1.2.0", "debug": "^4.4.0", - "devtools-protocol": "0.0.1367902", + "devtools-protocol": "0.0.1402036", "typed-query-selector": "^2.12.0", "ws": "^8.18.0" }, @@ -8564,28 +11183,6 @@ "node": ">=18" } }, - "node_modules/puppeteer-core/node_modules/chromium-bidi": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.11.0.tgz", - "integrity": "sha512-6CJWHkNRoyZyjV9Rwv2lYONZf1Xm0IuDyNq97nwSsxxP3wf5Bwy15K5rOvVKMtJ127jJBmxFUanSAOjgFRxgrA==", - "license": "Apache-2.0", - "dependencies": { - "mitt": "3.0.1", - "zod": "3.23.8" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/puppeteer-core/node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -8639,12 +11236,6 @@ ], "license": "MIT" }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "license": "MIT" - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -8778,12 +11369,12 @@ } }, "node_modules/readdirp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", + "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", "license": "MIT", "engines": { - "node": ">= 14.16.0" + "node": ">= 14.18.0" }, "funding": { "type": "individual", @@ -9012,9 +11603,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -9253,6 +11844,12 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -9318,18 +11915,26 @@ } }, "node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "license": "MIT", - "optional": true, "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" }, "engines": { - "node": ">= 10" + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" } }, "node_modules/source-map": { @@ -9395,9 +12000,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", - "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", "dev": true, "license": "CC0-1.0" }, @@ -9534,13 +12139,12 @@ } }, "node_modules/streamx": { - "version": "2.21.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", - "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", + "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", "license": "MIT", "dependencies": { "fast-fifo": "^1.3.2", - "queue-tick": "^1.0.1", "text-decoder": "^1.1.0" }, "optionalDependencies": { @@ -9597,13 +12201,13 @@ } }, "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/strip-final-newline": { @@ -9704,9 +12308,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.18.2.tgz", - "integrity": "sha512-J+y4mCw/zXh1FOj5wGJvnAajq6XgHOyywsa9yITmwxIlJbMqITq3gYRZHaeqLVH/eV/HOPphE6NjF+nbSNC5Zw==", + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.18.3.tgz", + "integrity": "sha512-G33HFW0iFNStfY2x6QXO2JYVMrFruc8AZRX0U/L71aA7WeWfX2E5Nm8E/tsipSZJeIZZbSjUDeynLK/wcuNWIw==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" @@ -9809,6 +12413,12 @@ "node": ">=10" } }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/teamcity-service-messages": { "version": "0.1.14", "resolved": "https://registry.npmjs.org/teamcity-service-messages/-/teamcity-service-messages-0.1.14.tgz", @@ -9870,12 +12480,6 @@ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "license": "MIT" }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "license": "MIT" - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -9941,16 +12545,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-jest": { @@ -10077,6 +12681,16 @@ "node": ">=10.13.0" } }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -10103,21 +12717,6 @@ "fsevents": "~2.3.3" } }, - "node_modules/tsx/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -10192,9 +12791,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "devOptional": true, "license": "Apache-2.0", "peer": true, @@ -10207,15 +12806,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.19.0.tgz", - "integrity": "sha512-Ni8sUkVWYK4KAcTtPjQ/UTiRk6jcsuDhPpxULapUDi8A/l8TSBk+t1GtJA1RsCzIJg0q6+J7bf35AwQigENWRQ==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.23.0.tgz", + "integrity": "sha512-/LBRo3HrXr5LxmrdYSOCvoAMm7p2jNizNfbIpCgvG4HMsnoprRUOce/+8VJ9BDYWW68rqIENE/haVLWPeFZBVQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.19.0", - "@typescript-eslint/parser": "8.19.0", - "@typescript-eslint/utils": "8.19.0" + "@typescript-eslint/eslint-plugin": "8.23.0", + "@typescript-eslint/parser": "8.23.0", + "@typescript-eslint/utils": "8.23.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10242,16 +12841,6 @@ "node": ">=0.8.0" } }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "license": "MIT", - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -10263,7 +12852,6 @@ "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "devOptional": true, "license": "MIT" }, "node_modules/unique-filename": { @@ -10296,9 +12884,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "funding": [ { @@ -10317,7 +12905,7 @@ "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -10358,6 +12946,19 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -10380,17 +12981,6 @@ "node": ">=10.12.0" } }, - "node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -10607,11 +13197,24 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yamljs": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", diff --git a/package.json b/package.json index 2535a002..c48ee738 100644 --- a/package.json +++ b/package.json @@ -37,12 +37,15 @@ "chokidar": "^4.0.1", "cors": "^2.8.5", "cytoscape": "^3.30.4", + "docker-compose": "^1.1.0", "dockerode": "^4.0.2", "express": "^4.21.1", "express-rate-limit": "^7.4.1", "https": "^1.0.0", + "i": "^0.3.7", "ipaddr.js": "^2.2.0", "nodemailer": "^6.9.16", + "npm": "^11.0.0", "puppeteer": "^24.0.0", "sqlite3": "^5.1.7", "swagger-ui-express": "^5.0.1", @@ -66,6 +69,7 @@ "@types/supports-color": "^8.1.3", "@types/swagger-jsdoc": "^6.0.4", "@types/swagger-ui-express": "^4.1.7", + "@types/ws": "^8.5.14", "@types/yamljs": "^0.2.34", "@typescript-eslint/eslint-plugin": "^8.18.2", "@typescript-eslint/parser": "^8.18.2", diff --git a/src/config/hostsystem.ts b/src/config/hostsystem.ts index 5e280d09..87928a8e 100644 --- a/src/config/hostsystem.ts +++ b/src/config/hostsystem.ts @@ -3,7 +3,8 @@ import { VERSION, HA_MASTER, HA_UNSAFE, - TRUSTED_PROXYS, + TRUSTED_PROXIES, + LOG_LEVEL, } from "./variables"; import fs from "fs"; import logger from "../utils/logger"; @@ -16,6 +17,14 @@ const version: string = VERSION || "unknown"; const masterNode: string = HA_MASTER === "true" ? "✓" : "✗"; const unsafeSync: string = HA_UNSAFE === "true" ? "✓" : "✗"; +let trustedProxies: string = ""; + +if (TRUSTED_PROXIES) { + trustedProxies = TRUSTED_PROXIES; +} else { + trustedProxies = "✗"; +} + function writeUserConf(port: number) { let previousConfig = null; let shouldRewriteConfig = false; @@ -72,7 +81,8 @@ function writeUserConf(port: number) { logger.info(`Arch : ${installationDetails.arch}`); logger.info(`Master node : ${masterNode}`); logger.info(`Unsafe sync : ${unsafeSync}`); - logger.info(`Proxies : ${TRUSTED_PROXYS}`); + logger.info(`Proxies : ${trustedProxies}`); + logger.info(`Log Level : ${LOG_LEVEL}`); logger.info(`Server : http://localhost:${port}`); if (process.env.NODE_ENV !== "production") { logger.info(`Swagger-UI : http://localhost:${port}/api-docs`); diff --git a/src/config/stacks.ts b/src/config/stacks.ts new file mode 100644 index 00000000..def75dcb --- /dev/null +++ b/src/config/stacks.ts @@ -0,0 +1,260 @@ +import logger from "../utils/logger"; +import fs from "fs"; +import path from "path"; +import YAML from "yamljs"; +import { DockerComposeFile } from "../typings/dockerCompose"; +import { dockerStackProperty, dockerStackEnv } from "../typings/dockerStackEnv"; +import { stackConfig } from "../typings/stackConfig"; +import { validate } from "../handlers/stack"; +import { atomicWrite } from "../utils/atomicWrite"; +import { AUTOMATIC_ENVIRONMENT_FILE_MANAGEMENT } from "./variables"; + +const nameRegex = /^[A-Za-z0-9_-]+$/; +const stackRootFolder = "./stacks"; +const configFilePath = `${stackRootFolder}/.config.json`; + +async function getStackCompose(name: string) { + try { + await validate(name); + const stackCompose = `${stackRootFolder}/${name}/docker-compose.yaml`; + + return YAML.parse(fs.readFileSync(stackCompose, "utf-8")); + } catch (error: unknown) { + const errorMsg = error instanceof Error ? error.message : String(error); + logger.error(errorMsg); + throw new Error(errorMsg); + } +} + +async function getStackConfig(): Promise { + try { + return fs.readFileSync(configFilePath, "utf-8"); + } catch (error: unknown) { + const errorMsg = error instanceof Error ? error.message : String(error); + logger.error(errorMsg); + throw new Error(errorMsg); + } +} + +async function createStack( + name: string, + content: DockerComposeFile, + override: boolean, +) { + try { + if (!name) { + const errorMsg = "Name required"; + logger.error(errorMsg); + throw new Error(errorMsg); + } + + if (!nameRegex.test(name)) { + const errorMsg = "Name does not match [A-Za-z0-9_-]"; + logger.error(errorMsg); + throw new Error(errorMsg); + } + + if (!content) { + const errorMsg = "Data for this stack is required"; + logger.error(errorMsg); + throw new Error(errorMsg); + } + + const stackFolderPath = `${stackRootFolder}/${name}`; + + if (!fs.existsSync(stackFolderPath)) { + fs.mkdirSync(stackFolderPath, { recursive: true }); + logger.debug(`Created stack folder at ${stackFolderPath}`); + } + + updateConfigFile(name); + + let yamlContent = ""; + let environmentFileData: dockerStackEnv = { environment: [] }; + if (AUTOMATIC_ENVIRONMENT_FILE_MANAGEMENT == "true" && override == false) { + logger.debug("AEFM is activated"); + const { cleanCompose, envSchema } = extractAndRemoveEnv(content); + yamlContent = YAML.stringify(cleanCompose, 10, 2); + environmentFileData = envSchema; + + await writeEnvFile(name, environmentFileData); + } else { + yamlContent = YAML.stringify(content, 10, 2); + } + + const filePath = `${stackFolderPath}/docker-compose.yaml`; + atomicWrite(filePath, yamlContent); + logger.debug(`Stack content written to ${filePath}`); + } catch (error: unknown) { + const errorMsg = error instanceof Error ? error.message : String(error); + logger.error(errorMsg); + throw new Error(errorMsg); + } +} + +function updateConfigFile(stackName: string) { + try { + let config: stackConfig = { stacks: [] }; + if (fs.existsSync(configFilePath)) { + const configData = fs.readFileSync(configFilePath, "utf-8"); + config = JSON.parse(configData); + } + + const stacks = config.stacks || []; + + if (!stacks.includes(stackName)) { + stacks.push(stackName); + } + + const updatedConfig = { stacks }; + atomicWrite(configFilePath, JSON.stringify(updatedConfig, null, 2)); + logger.debug(`Updated .config.json with stack name: ${stackName}`); + } catch (error: unknown) { + const errorMsg = error instanceof Error ? error.message : String(error); + logger.error(`Error updating .config.json: ${errorMsg}`); + throw new Error(errorMsg); + } +} + +async function writeEnvFile( + name: string, + data: dockerStackEnv, +): Promise { + try { + await validate(name); + + if (!nameRegex.test(name)) { + const sanitizedStackName = name.replace(/\n|\r/g, ""); + const errorMsg = `Invalid stack name: ${sanitizedStackName}`; + logger.error(errorMsg); + return false; + } + + const dockerEnvPath = path.resolve(stackRootFolder, name, "docker.env"); + const dockerEnvPathBak = path.resolve( + stackRootFolder, + name, + ".docker.env.bak", + ); + + if ( + !dockerEnvPath.startsWith(path.resolve(stackRootFolder)) || + !dockerEnvPathBak.startsWith(path.resolve(stackRootFolder)) + ) { + const sanitizedStackName = name.replace(/\n|\r/g, ""); + const errorMsg = `Path traversal attempt detected: ${sanitizedStackName}`; + logger.error(errorMsg); + return false; + } + + const variableNames = data.environment.map(({ name }) => name); + const duplicateVars = variableNames.filter( + (item, index) => variableNames.indexOf(item) !== index, + ); + + if (duplicateVars.length > 0) { + const duplicatesList = duplicateVars.join(", "); + const sanitizedDuplicatesList = duplicatesList.replace(/\n|\r/g, ""); + const errorMsg = `Duplicate environment variables detected: ${sanitizedDuplicatesList}`; + logger.error(errorMsg); + return false; + } + + const envFileContent = data.environment + .map(({ name, value }) => `${name}="${value}"`) + .join("\n"); + + if (fs.existsSync(dockerEnvPath)) { + logger.debug("Creating a local backup"); + const previousData = fs.readFileSync(dockerEnvPath); + atomicWrite(dockerEnvPathBak, previousData); + } + + atomicWrite(dockerEnvPath, envFileContent); + return true; + } catch (error: unknown) { + const errorMsg = ( + error instanceof Error ? error.message : String(error) + ).replace(/\n|\r/g, ""); + logger.error(errorMsg); + throw new Error(errorMsg); + } +} + +async function getEnvFile(name: string) { + await validate(name); + const dockerEnvPath = path.resolve(stackRootFolder, name, "docker.env"); + if (!dockerEnvPath.startsWith(path.resolve(stackRootFolder))) { + throw new Error("Invalid path"); + } + + if (fs.existsSync(dockerEnvPath)) { + const data = fs.readFileSync(dockerEnvPath, "utf-8"); + + const environment: dockerStackProperty[] = data + .split("\n") + .filter((line) => line.trim() !== "" && line.includes("=")) + .map((line) => { + const [name, ...valueParts] = line.split("="); + const value = valueParts.join("=").replace(/^"|"$/g, ""); + return { name: name.trim(), value: value.trim() }; + }); + + return { environment }; + } else { + return null; + } +} + +function extractAndRemoveEnv(data: DockerComposeFile): { + cleanCompose: DockerComposeFile; + envSchema: dockerStackEnv; +} { + const environment: dockerStackProperty[] = []; + const envCount: Record = {}; + + for (const [, service] of Object.entries(data.services)) { + if (service.environment) { + for (const key of Object.keys(service.environment)) { + envCount[key] = (envCount[key] || 0) + 1; + } + } + } + + for (const [, service] of Object.entries(data.services)) { + if (service.environment) { + const remainingEnvironment: Record = {}; + + for (const [key, value] of Object.entries(service.environment)) { + if (envCount[key] === 1) { + environment.push({ name: key, value }); + } else { + remainingEnvironment[key] = value; + } + } + + service.environment = remainingEnvironment; + + if (Object.keys(service.environment).length === 0) { + delete service.environment; + } + } + + if (!service.env_file) { + service.env_file = ["./docker.env"]; + } + } + + return { + cleanCompose: data, + envSchema: { environment }, + }; +} + +export { + createStack, + getStackConfig, + getStackCompose, + writeEnvFile, + getEnvFile, +}; diff --git a/src/config/swagger.yaml b/src/config/swagger.yaml index c2489f63..9a1d50fb 100644 --- a/src/config/swagger.yaml +++ b/src/config/swagger.yaml @@ -33,6 +33,17 @@ info: - Multi Arch Docker builds through docker buildx - High Availability using single master and unlimited worker nodes! +
+ Your container graph + [Interactive Graph](http://localhost:9876/graph) + + [Raw image](http://localhost:9876/graph/image) + + --- + + ![Your container graph](http://localhost:9876/graph/image) +
+ # 🔗 DockStatAPI v2 Documentation _⚠️ = Deprecation warning_ @@ -76,6 +87,9 @@ tags: - name: Notification Service description: Routes to configure the notification service + - name: Stacks + description: Management of the Stack module + servers: - url: http://localhost:9876 description: "Your DockStatAPI instance" @@ -99,7 +113,7 @@ paths: default: super-secret responses: "200": - description: Succes - Succesfully enabled authentication + description: Success - Successfully enabled authentication content: application/json: schema: @@ -159,12 +173,7 @@ paths: message: type: string example: "Authentication disabled successfully" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -199,12 +208,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ServerContainers" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -239,12 +243,7 @@ paths: application/json: schema: $ref: "#/components/schemas/IndexedServerContainers" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -287,12 +286,6 @@ paths: type: string example: "Successfully cleared the database" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" "403": description: Error - Password is required content: @@ -331,12 +324,7 @@ paths: "400": description: Error - No hosts defined, please add a host via /conf/addHost - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -376,12 +364,7 @@ paths: application/json: schema: $ref: "#/components/schemas/HostInfo" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -438,12 +421,7 @@ paths: example: "x64" "400": description: Error - Received empty configuration - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -491,12 +469,7 @@ paths: port: type: string example: "2375" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -529,12 +502,7 @@ paths: application/json: schema: $ref: "#/components/schemas/FrontendConfig" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -572,12 +540,6 @@ paths: type: integer example: 600 - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" "403": description: Error - Password is required content: @@ -610,12 +572,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiStatus" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -648,12 +605,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ServerContainers" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -688,12 +640,7 @@ paths: application/json: schema: $ref: "#/components/schemas/HaConfig" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -719,12 +666,7 @@ paths: description: Succes - Synchronized successfully "400": description: Error - `files` object is missing or invalid - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -748,12 +690,7 @@ paths: responses: "200": description: Succes - Prepared all files for syncing - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -784,12 +721,7 @@ paths: $ref: "#/components/schemas/Notification-Template" "400": description: Error - Error while reading file (see server logs) - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -849,12 +781,7 @@ paths: message: type: string example: "Invalid input format. Expected JSON with a 'text' field" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -908,12 +835,7 @@ paths: message: type: string example: "Sent test notification" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -993,12 +915,7 @@ paths: message: type: string example: "Host already exists" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -1056,12 +973,7 @@ paths: message: type: string example: "Host name is required" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -1104,56 +1016,52 @@ paths: in: query required: true description: "Adjust the schedule timing (in seconds)" - "200": - description: Success - Timing updated - content: - application/json: - schema: - type: object - properties: - status: - type: string - example: "success" - message: - type: string - example: "Updated interval" - "401": - description: Error - Interval must be between 5 minutes and 6 hours - content: - application/json: - schema: - type: object - properties: - status: - type: string - example: "error" - message: - type: string - example: "Interval must be between 5 minutes and 6 hours." - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" - "403": - description: Error - Password is required - content: - application/json: - schema: - $ref: "#/components/schemas/403" - "500": - description: Error - Critical Error, please see the server's logs - content: - application/json: - schema: - $ref: "#/components/schemas/500" - "503": - description: Error - The high-availability lock is currently active, please try again later - content: - application/json: - schema: - $ref: "#/components/schemas/503" + responses: + "200": + description: Success - Timing updated + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: "success" + message: + type: string + example: "Updated interval" + "401": + description: Error - Interval must be between 5 minutes and 6 hours + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: "error" + message: + type: string + example: "Interval must be between 5 minutes and 6 hours." + + "403": + description: Error - Password is required + content: + application/json: + schema: + $ref: "#/components/schemas/403" + "500": + description: Error - Critical Error, please see the server's logs + content: + application/json: + schema: + $ref: "#/components/schemas/500" + "503": + description: Error - The high-availability lock is currently active, please try again later + content: + application/json: + schema: + $ref: "#/components/schemas/503" # ------------------------------ # Frontend routes: @@ -1184,12 +1092,7 @@ paths: message: type: string example: "Container unhidden successfully." - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -1236,12 +1139,7 @@ paths: message: type: string example: "Hid container succesfully" - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -1294,12 +1192,7 @@ paths: message: type: string example: "Tag added successfully." - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -1352,12 +1245,7 @@ paths: message: type: string example: "Tag removed successfully." - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -1404,12 +1292,7 @@ paths: message: type: string example: "Container pinned successfully." - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -1456,12 +1339,7 @@ paths: message: type: string example: "Container unpinned successfully." - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -1499,7 +1377,7 @@ paths: schema: type: URI required: true - llowReserved: false + allowReserved: false description: The URI of the link (please use Uniform Resource Identifier format) responses: "200": @@ -1515,12 +1393,7 @@ paths: message: type: string example: "Link added successfully." - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -1567,12 +1440,7 @@ paths: message: type: string example: "Link removed successfully." - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -1631,12 +1499,7 @@ paths: message: type: string example: "Icon added successfully." - "402": - description: Error - Invalid password - content: - application/json: - schema: - $ref: "#/components/schemas/402" + "403": description: Error - Password is required content: @@ -1661,7 +1524,7 @@ paths: tags: - "Frontend Configuration" operationId: frRmIcon - summary: Removean icon from the specified container + summary: Remove an icon from the specified container parameters: - name: containerName in: path @@ -1683,12 +1546,110 @@ paths: message: type: string example: "Icon removed successfully." - "402": - description: Error - Invalid password + + "403": + description: Error - Password is required content: application/json: schema: - $ref: "#/components/schemas/402" + $ref: "#/components/schemas/403" + "500": + description: Error - Critical Error, please see the server's logs + content: + application/json: + schema: + $ref: "#/components/schemas/500" + "503": + description: Error - The high-availability lock is currently active, please try again later + content: + application/json: + schema: + $ref: "#/components/schemas/503" + + # ------------------------------ + # Stack management + /stacks/create/{name}: + post: + tags: + - "Stacks" + operationId: createStack + summary: Creates a docker-compose file inside the stack name directory + requestBody: + required: true + content: + application/json: + schema: + type: string + description: Your docker-compose.yaml contents + parameters: + - name: name + in: path + schema: + type: string + required: true + description: The name of the stack + responses: + "200": + description: Success - Stack created + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: "success" + message: + type: string + example: "Stack created" + + "403": + description: Error - Password is required + content: + application/json: + schema: + $ref: "#/components/schemas/403" + "500": + description: Error - Critical Error, please see the server's logs + content: + application/json: + schema: + $ref: "#/components/schemas/500" + "503": + description: Error - The high-availability lock is currently active, please try again later + content: + application/json: + schema: + $ref: "#/components/schemas/503" + + /stacks/start/{name}: + post: + tags: + - "Stacks" + operationId: startStack + summary: Starts the defined stack + parameters: + - name: name + in: path + schema: + type: string + required: true + description: The name of the stack + responses: + "200": + description: Success - Stack started + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: "success" + message: + type: string + example: "Stack created" + "403": description: Error - Password is required content: @@ -1707,6 +1668,170 @@ paths: application/json: schema: $ref: "#/components/schemas/503" + + /stacks/stop/{name}: + post: + tags: + - "Stacks" + operationId: stopStack + summary: Stops the defined stack + parameters: + - name: name + in: path + schema: + type: string + required: true + description: The name of the stack + responses: + "200": + description: Success - Stack stopped + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: "success" + message: + type: string + example: "Stack stopped" + + "403": + description: Error - Password is required + content: + application/json: + schema: + $ref: "#/components/schemas/403" + "500": + description: Error - Critical Error, please see the server's logs + content: + application/json: + schema: + $ref: "#/components/schemas/500" + "503": + description: Error - The high-availability lock is currently active, please try again later + content: + application/json: + schema: + $ref: "#/components/schemas/503" + + /stacks/get/{name}: + get: + tags: + - "Stacks" + operationId: getStack + summary: Get the docker-compose.yaml (as JSON) from the defined stack + parameters: + - name: name + in: path + schema: + type: string + required: true + description: The name of the stack + responses: + "200": + description: Success - Stack fetched + "403": + description: Error - Password is required + content: + application/json: + schema: + $ref: "#/components/schemas/403" + "500": + description: Error - Critical Error, please see the server's logs + content: + application/json: + schema: + $ref: "#/components/schemas/500" + "503": + description: Error - The high-availability lock is currently active, please try again later + content: + application/json: + schema: + $ref: "#/components/schemas/503" + + /stacks/set-env/{name}: + post: + tags: + - "Stacks" + operationId: setStackEnv + summary: Set the docker.env (as JSON) from the defined stack + requestBody: + required: true + content: + application/json: + schema: + type: string + description: Your docker.env contents + parameters: + - name: override + in: query + required: false + description: Whether to override (true) the automatic environment file management (boolean value) + - name: name + in: path + schema: + type: string + required: true + description: The name of the stack + responses: + "200": + description: Success - Stack environment set + "403": + description: Error - Password is required + content: + application/json: + schema: + $ref: "#/components/schemas/403" + "500": + description: Error - Critical Error, please see the server's logs + content: + application/json: + schema: + $ref: "#/components/schemas/500" + "503": + description: Error - The high-availability lock is currently active, please try again later + content: + application/json: + schema: + $ref: "#/components/schemas/503" + + /stacks/get-env/{name}: + get: + tags: + - "Stacks" + operationId: getStackEnv + summary: Get the docker.env (as JSON) from the defined stack + parameters: + - name: name + in: path + schema: + type: string + required: true + description: The name of the stack + responses: + "200": + description: Success - Stack config fetched + "403": + description: Error - Password is required + content: + application/json: + schema: + $ref: "#/components/schemas/403" + "500": + description: Error - Critical Error, please see the server's logs + content: + application/json: + schema: + $ref: "#/components/schemas/500" + "503": + description: Error - The high-availability lock is currently active, please try again later + content: + application/json: + schema: + $ref: "#/components/schemas/503" + # ------------------------------ components: securitySchemes: diff --git a/src/config/variables.ts b/src/config/variables.ts index 26a522be..37c67a23 100644 --- a/src/config/variables.ts +++ b/src/config/variables.ts @@ -3,7 +3,7 @@ import vars from "../data/variables.json"; export const { VERSION, RUNNING_IN_DOCKER, - TRUSTED_PROXYS, + TRUSTED_PROXIES, HA_MASTER, HA_MASTER_IP, HA_NODE, @@ -21,4 +21,6 @@ export const { TELEGRAM_CHAT_ID, WHATSAPP_API_URL, WHATSAPP_RECIPIENT, + AUTOMATIC_ENVIRONMENT_FILE_MANAGEMENT, + LOG_LEVEL, } = vars; diff --git a/src/controllers/containerController.ts b/src/controllers/containerController.ts index 61745e17..2883dad9 100644 --- a/src/controllers/containerController.ts +++ b/src/controllers/containerController.ts @@ -1,4 +1,4 @@ -import getDockerClient from "../utils/dockerClient"; +import { getDockerClient } from "../utils/dockerClient"; import logger from "../utils/logger"; import { Request, Response } from "express"; import { createResponseHandler } from "../handlers/response"; diff --git a/src/controllers/fetchData.ts b/src/controllers/fetchData.ts index 314e33ac..06e52a93 100644 --- a/src/controllers/fetchData.ts +++ b/src/controllers/fetchData.ts @@ -1,5 +1,5 @@ import db from "../config/db"; -import fetchAllContainers from "../utils/containerService"; +import { fetchAllContainers } from "../utils/containerService"; import logger from "../utils/logger"; import fs from "fs"; import { atomicWrite } from "../utils/atomicWrite"; diff --git a/src/controllers/proxy.ts b/src/controllers/proxy.ts index 601f1556..c091590a 100644 --- a/src/controllers/proxy.ts +++ b/src/controllers/proxy.ts @@ -1,9 +1,9 @@ import { Application } from "express"; import logger from "../utils/logger"; -import { TRUSTED_PROXYS } from "../config/variables"; +import { TRUSTED_PROXIES } from "../config/variables"; export default function trustedProxies(app: Application) { - const trusted: string = TRUSTED_PROXYS; + const trusted: string = TRUSTED_PROXIES; if (!trusted) { logger.warn( diff --git a/src/handlers/api.ts b/src/handlers/api.ts index a71e56a3..fa7f1f7e 100644 --- a/src/handlers/api.ts +++ b/src/handlers/api.ts @@ -1,7 +1,7 @@ import extractRelevantData from "../utils/extractHostData"; import { Request, Response } from "express"; -import getDockerClient from "../utils/dockerClient"; -import fetchAllContainers from "../utils/containerService"; +import { getDockerClient } from "../utils/dockerClient"; +import { fetchAllContainers } from "../utils/containerService"; import { getCurrentSchedule } from "../controllers/scheduler"; import fs from "fs"; import checkReachability from "../utils/connectionChecker"; diff --git a/src/handlers/graph.ts b/src/handlers/graph.ts index 85550448..53e245f7 100644 --- a/src/handlers/graph.ts +++ b/src/handlers/graph.ts @@ -3,7 +3,6 @@ import logger from "../utils/logger"; import { AllContainerData, ContainerData } from "./../typings/dockerConfig"; import { atomicWrite } from "../utils/atomicWrite"; import { rateLimitedReadFile } from "../utils/rateLimitFS"; -import puppeteer from "puppeteer"; const CACHE_DIR_JSON = "./src/data/graph.json"; const CACHE_DIR_HTML = "./src/data/graph.html"; @@ -14,8 +13,8 @@ const pngPath = "./src/data/graph.png"; async function getPathData(path: string) { try { - const data = await rateLimitedReadFile(path); - return data; + return await rateLimitedReadFile(path); + } catch (error: unknown) { const errorMsg = error instanceof Error ? error.message : String(error); logger.error(errorMsg); @@ -27,89 +26,124 @@ async function renderGraphToImage( htmlContent: string, outputImagePath: string, ): Promise { - const browser = await puppeteer.launch({ - headless: true, - args: [`--window-size=1920,1080`], - defaultViewport: { - width: 1920, - height: 1080, - }, - }); - const page = await browser.newPage(); - await page.setContent(htmlContent, { waitUntil: "networkidle0" }); - //await page.waitForNavigation({ waitUntil: "load" }); - await page.waitForSelector("#cy", { visible: true }); - await page.waitForFunction( - () => { - const cyContainer = document.querySelector("#cy"); - return cyContainer && cyContainer.children.length > 0; - }, - { timeout: 10000 }, - ); - - await page.screenshot({ - path: outputImagePath, - type: outputImagePath.endsWith(".jpg") ? "jpeg" : "png", - fullPage: true, - }); - - await browser.close(); + let puppeteer; + try { + puppeteer = await import("puppeteer"); + } catch (error) { + logger.error("Puppeteer is not installed. Please install it to generate images."); + throw new Error(`Puppeteer is not installed (${error})`); + } + + let browser; + try { + browser = await puppeteer.default.launch({ + headless: "shell", + args: ["--disable-setuid-sandbox", "--no-sandbox"], + executablePath: process.env.PUPPETEER_EXECUTABLE_PATH, + }); + + const page = await browser.newPage(); + await page.setContent(htmlContent, { waitUntil: "networkidle0" }); + await page.waitForSelector("#cy", { visible: true, timeout: 15000 }); + + await page.waitForFunction( + () => { + const cyElement = document.querySelector("#cy"); + return cyElement ? cyElement.children.length > 0 : false; + }, + { timeout: 10000 } + ); + + await page.screenshot({ + path: outputImagePath, + type: outputImagePath.endsWith(".jpg") ? "jpeg" : "png", + fullPage: true, + captureBeyondViewport: true, + }); + } catch (error: unknown) { + let errorMessage = "Unknown error occurred during browser operation"; + + if (error instanceof Error) { + errorMessage = error.message; + + // Detect common dependency errors + if (errorMessage.includes("libnss3") || errorMessage.includes("libxcb")) { + errorMessage = `❗ Missing system dependencies (libnss3)`; + } + + // Detect Chrome not found errors + if (errorMessage.includes("Failed to launch")) { + errorMessage = `❗ Chrome not found!`; + } + } + + throw new Error(`Graph rendering failed: ${errorMessage}`); + } finally { + if (browser) { + await browser.close().catch(() => { }); + } + } + logger.info(`Graph rendered and image saved to: ${outputImagePath}`); } async function generateGraphFiles( allContainerData: AllContainerData, ): Promise { - try { - logger.info("generateGraphFiles >>> Starting generation"); - const graphElements: cytoscape.ElementDefinition[] = []; - - for (const [hostName, containers] of Object.entries(allContainerData)) { - if ("error" in containers) { - // TODO: make error'ed hosts better - graphElements.push({ - data: { - id: hostName, - label: `Host: ${hostName} Error: ${containers.error}`, - type: "server", - }, - }); - } else { - const containerList = containers as ContainerData[]; - - // host node with container count - graphElements.push({ - data: { - id: hostName, - label: `${hostName} - ${containerList.length} Containers`, - type: "server", - }, - }); - - for (const container of containerList) { - // container node + if (process.env.CI === "true") { + logger.warn("Running inside a CI/CD Action, wont generated graphs"); + return false; + } else { + try { + logger.info("generateGraphFiles >>> Starting generation"); + const graphElements: cytoscape.ElementDefinition[] = []; + + for (const [hostName, containers] of Object.entries(allContainerData)) { + if ("error" in containers) { + // TODO: make error'ed hosts better graphElements.push({ data: { - id: container.id, - label: `${container.name} (${container.state})`, - type: "container", + id: hostName, + label: `Host: ${hostName} Error: ${containers.error}`, + type: "server", }, }); + } else { + const containerList = containers as ContainerData[]; - // edge between host and container + // host node with container count graphElements.push({ data: { - source: hostName, - target: container.id, + id: hostName, + label: `${hostName} - ${containerList.length} Containers`, + type: "server", }, }); + + for (const container of containerList) { + // container node + graphElements.push({ + data: { + id: container.id, + label: `${container.name} (${container.state})`, + type: "container", + }, + }); + + // edge between host and container + graphElements.push({ + data: { + source: hostName, + target: container.id, + }, + }); + } } } - } - atomicWrite(CACHE_DIR_JSON, JSON.stringify(graphElements, null, 2)); + atomicWrite(CACHE_DIR_JSON, JSON.stringify(graphElements, null, 2)); - const htmlContent = ` + const htmlContent = ` @@ -201,17 +235,18 @@ async function generateGraphFiles( `; - atomicWrite(CACHE_DIR_HTML, htmlContent); - await renderGraphToImage(htmlContent, pngPath) - .then(() => logger.debug("HTML converted to image successfully!")) - .catch((err) => logger.error("Error:", err)); + atomicWrite(CACHE_DIR_HTML, htmlContent); + await renderGraphToImage(htmlContent, pngPath) + .then(() => logger.debug("HTML converted to image successfully!")) + .catch((err) => logger.error("Error:", err)); - logger.info("generateGraphFiles <<< Files generated successfully"); - return true; - } catch (error: unknown) { - const errorMsg = error instanceof Error ? error.message : String(error); - logger.error(errorMsg); - return false; + logger.info("generateGraphFiles <<< Files generated successfully"); + return true; + } catch (error: unknown) { + const errorMsg = error instanceof Error ? error.message : String(error); + logger.error(errorMsg); + return false; + } } } diff --git a/src/handlers/response.ts b/src/handlers/response.ts index 8c6e95b8..ee062102 100644 --- a/src/handlers/response.ts +++ b/src/handlers/response.ts @@ -29,7 +29,7 @@ class ResponseHandler { } critical(log: string) { - logger.error(log); + logger.error(log.replace(/\n|\r/g, "")); this.res.status(500).json({ status: "critical", message: "Please see the server logs for more info", diff --git a/src/handlers/stack.ts b/src/handlers/stack.ts new file mode 100644 index 00000000..e87b533f --- /dev/null +++ b/src/handlers/stack.ts @@ -0,0 +1,162 @@ +import { Response, Request } from "express"; +import { + createStack, + getStackConfig, + getStackCompose, + writeEnvFile, + getEnvFile, +} from "../config/stacks"; +import { DockerComposeFile } from "../typings/dockerCompose"; +import logger from "../utils/logger"; +import * as compose from "docker-compose"; +import { createResponseHandler } from "./response"; +import { stackConfig } from "../typings/stackConfig"; +import { dockerStackEnv } from "../typings/dockerStackEnv"; +import path from "path"; + +const PROJECT_ROOT = path.resolve(__dirname, "../.."); + +export async function validate(name: string): Promise { + const config: stackConfig = JSON.parse(await getStackConfig()); + if (!config.stacks.find((element) => element === name)) { + throw new Error("Stack not found"); + } + + return true; +} + +async function composeAction(option: string, name: string): Promise { + const composeFile: string = path.join(PROJECT_ROOT, `stacks/${name}`); + switch (option) { + case "start": { + await compose.upAll({ cwd: composeFile, log: false }).then( + () => { + return true; + }, + (err: unknown) => { + throw new Error(err as string); + }, + ); + break; + } + case "stop": { + await compose.downAll({ cwd: composeFile, log: false }).then( + () => { + return true; + }, + (err: unknown) => { + throw new Error(err as string); + }, + ); + break; + } + } +} + +class StackHandler { + private req: Request; + private res: Response; + + constructor(req: Request, res: Response) { + this.req = req; + this.res = res; + } + + async createStack(req: Request, res: Response) { + const ResponseHandler = createResponseHandler(res); + try { + const name: string = req.params.name; + const content: DockerComposeFile = req.body; + let override = false; + override = req.query.override == "true"; + + await createStack(name, content, override); + return ResponseHandler.ok("Stack created"); + } catch (error: unknown) { + const errorMsg = error instanceof Error ? error.message : String(error); + return ResponseHandler.critical(errorMsg); + } + } + + async start(req: Request, res: Response) { + const ResponseHandler = createResponseHandler(res); + try { + const name: string = req.params.name; + await validate(name); + await composeAction("start", name); + return ResponseHandler.ok("Stack started"); + } catch (error: unknown) { + const errorMsg = error instanceof Error ? error.message : String(error); + return ResponseHandler.critical(errorMsg); + } + } + + async stop(req: Request, res: Response) { + const ResponseHandler = createResponseHandler(res); + try { + const name: string = req.params.name; + await validate(name); + await composeAction("stop", name); + return ResponseHandler.ok("Stack stopped"); + } catch (error: unknown) { + const errorMsg = error instanceof Error ? error.message : String(error); + return ResponseHandler.critical(errorMsg); + } + } + + async stackCompose(req: Request, res: Response) { + const ResponseHandler = createResponseHandler(res); + try { + const {name} = req.params; + return ResponseHandler.rawData( + await getStackCompose(name), + "Stack compose fetched", + ); + } catch (error: unknown) { + const errorMsg = error instanceof Error ? error.message : String(error); + logger.error(errorMsg.replace(/\n|\r/g, "")); + throw new Error(errorMsg); + } + } + + async setStackEnv(req: Request, res: Response) { + const ResponseHandler = createResponseHandler(res); + try { + const data: dockerStackEnv = req.body; + const name: string = req.params.name; + if (await writeEnvFile(name, data)) { + return ResponseHandler.ok("Wrote docker.env"); + } else { + return ResponseHandler.critical( + "Something went wrong while writing the env File!", + ); + } + } catch (error: unknown) { + const errorMsg = error instanceof Error ? error.message : String(error); + logger.error(errorMsg.replace(/\n|\r/g, "")); + throw new Error(errorMsg); + } + } + + async getStackEnv(req: Request, res: Response) { + const ResponseHandler = createResponseHandler(res); + try { + const name: string = req.params.name; + const data = await getEnvFile(name); + if (data == null) { + return ResponseHandler.error( + "No environment file found for this Stack!", + 404, + ); + } + return ResponseHandler.rawData(data, "Read docker.env"); + } catch (error: unknown) { + const errorMsg = error instanceof Error ? error.message : String(error); + logger.error(errorMsg.replace(/\n|\r/g, "")); + throw new Error(errorMsg); + } + } +} + +export const createStackHandler = (req: Request, res: Response) => + new StackHandler(req, res); diff --git a/src/init.ts b/src/init.ts index a0ace664..188542f6 100644 --- a/src/init.ts +++ b/src/init.ts @@ -13,15 +13,28 @@ import ha from "./routes/highavailability/routes"; import trustedProxies from "./controllers/proxy"; import { limiter } from "./middleware/rateLimiter"; import { scheduleFetch } from "./controllers/scheduler"; +import { Server } from 'http'; import cors from "cors"; +import { setupWebSocket } from "./utils/webSocket"; +import stacks from "./routes/stack/routes"; import { blockWhileLocked } from "./middleware/checkLock"; import logger from "./utils/logger"; import initFiles from "./config/initFiles"; const LAB = [limiter, authMiddleware, blockWhileLocked]; -const initializeApp = (app: express.Application): void => { +const initializeApp = (app: express.Application, server: Server): void => { initFiles(); + + try { + logger.debug("Starting Websocket server, with these endpoints:"); + logger.debug("ws://localhost:9876/wss/container-data") + logger.debug("ws://localhost:9876/wss/server-logs") + setupWebSocket(server); + } catch (error: unknown) { + logger.error("Error starting WebSocket: ", error) + } + app.use(cors()); app.use(express.json()); @@ -45,6 +58,7 @@ const initializeApp = (app: express.Application): void => { app.use("/frontend", LAB, frontend); app.use("/graph", LAB, graph); app.use("/notification-service", LAB, notificationService); + app.use("/stacks", LAB, stacks); app.use("/ha", limiter, authMiddleware, ha); process.on("exit", (code: number) => { diff --git a/src/misc/createEnvDev.sh b/src/misc/createEnvDev.sh index 13b5a201..1f231aa6 100755 --- a/src/misc/createEnvDev.sh +++ b/src/misc/createEnvDev.sh @@ -3,6 +3,9 @@ # Version VERSION="$(cat ./package.json | grep version | cut -d '"' -f 4)" +# Automatic Stack environment management +AUTOMATIC_ENVIRONMENT_FILE_MANAGEMENT="${AUTOMATIC_ENVIRONMENT_FILE_MANAGEMENT:-true}" + # Docker if grep -q '/docker' /proc/1/cgroup 2>/dev/null || [ -f /.dockerenv ]; then RUNNING_IN_DOCKER="true" @@ -10,11 +13,14 @@ else RUNNING_IN_DOCKER="false" fi +# Default dev log level +LOG_LEVEL="${LOG_LEVEL:-debug}" + echo -n "\ { \"VERSION\": \"${VERSION}\", \"RUNNING_IN_DOCKER\": \"${RUNNING_IN_DOCKER}\", - \"TRUSTED_PROXYS\": \"${TRUSTED_PROXYS}\", + \"TRUSTED_PROXIES\": \"${TRUSTED_PROXIES}\", \"HA_MASTER\": \"${HA_MASTER}\", \"HA_MASTER_IP\": \"${HA_MASTER_IP}\", \"HA_NODE\": \"${HA_NODE}\", @@ -31,6 +37,8 @@ echo -n "\ \"TELEGRAM_BOT_TOKEN\": \"${TELEGRAM_BOT_TOKEN}\", \"TELEGRAM_CHAT_ID\": \"${TELEGRAM_CHAT_ID}\", \"WHATSAPP_API_URL\": \"${WHATSAPP_API_URL}\", - \"WHATSAPP_RECIPIENT\": \"${WHATSAPP_RECIPIENT}\" + \"WHATSAPP_RECIPIENT\": \"${WHATSAPP_RECIPIENT}\", + \"AUTOMATIC_ENVIRONMENT_FILE_MANAGEMENT\": \"${AUTOMATIC_ENVIRONMENT_FILE_MANAGEMENT}\", + \"LOG_LEVEL\": \"${LOG_LEVEL}\" } \ " > ./src/data/variables.json || exit 1 diff --git a/src/misc/createEnvFile.sh b/src/misc/createEnvFile.sh index 1f7fc76f..0fbd15de 100755 --- a/src/misc/createEnvFile.sh +++ b/src/misc/createEnvFile.sh @@ -3,6 +3,9 @@ # Version VERSION="$(cat ./package.json | grep version | cut -d '"' -f 4)" +# Automatic Stack environment management +AUTOMATIC_ENVIRONMENT_FILE_MANAGEMENT="${AUTOMATIC_ENVIRONMENT_FILE_MANAGEMENT:-true}" + # Docker if grep -q '/docker' /proc/1/cgroup 2>/dev/null || [ -f /.dockerenv ]; then RUNNING_IN_DOCKER="true" @@ -10,11 +13,14 @@ else RUNNING_IN_DOCKER="false" fi +# Default log level +LOG_LEVEL="${LOG_LEVEL:-info}" + echo -n "\ { \"VERSION\": \"${VERSION}\", \"RUNNING_IN_DOCKER\": \"${RUNNING_IN_DOCKER}\", - \"TRUSTED_PROXYS\": \"${TRUSTED_PROXYS}\", + \"TRUSTED_PROXIES\": \"${TRUSTED_PROXIES}\", \"HA_MASTER\": \"${HA_MASTER}\", \"HA_MASTER_IP\": \"${HA_MASTER_IP}\", \"HA_NODE\": \"${HA_NODE}\", @@ -31,6 +37,8 @@ echo -n "\ \"TELEGRAM_BOT_TOKEN\": \"${TELEGRAM_BOT_TOKEN}\", \"TELEGRAM_CHAT_ID\": \"${TELEGRAM_CHAT_ID}\", \"WHATSAPP_API_URL\": \"${WHATSAPP_API_URL}\", - \"WHATSAPP_RECIPIENT\": \"${WHATSAPP_RECIPIENT}\" + \"WHATSAPP_RECIPIENT\": \"${WHATSAPP_RECIPIENT}\", + \"AUTOMATIC_ENVIRONMENT_FILE_MANAGEMENT\": \"${AUTOMATIC_ENVIRONMENT_FILE_MANAGEMENT}\", + \"LOG_LEVEL\": \"${LOG_LEVEL}\" } \ " > /api/src/data/variables.json || exit 1 diff --git a/src/routes/setter/routes.ts b/src/routes/setter/routes.ts index fc551a9c..16150293 100644 --- a/src/routes/setter/routes.ts +++ b/src/routes/setter/routes.ts @@ -1,6 +1,6 @@ import express, { Router, Request, Response } from "express"; -import { createConfHandler } from "../../handlers/conf"; const router: Router = express.Router(); +import { createConfHandler } from "../../handlers/conf"; router.put("/addHost", async (req: Request, res: Response): Promise => { const ConfHandler = createConfHandler(req, res); diff --git a/src/routes/stack/routes.ts b/src/routes/stack/routes.ts new file mode 100644 index 00000000..8f9b9ae8 --- /dev/null +++ b/src/routes/stack/routes.ts @@ -0,0 +1,35 @@ +import express, { Router, Request, Response } from "express"; +const router: Router = express.Router(); +import { createStackHandler } from "../../handlers/stack"; + +router.post("/create/:name", async (req: Request, res: Response) => { + const StackHandler = createStackHandler(req, res); + return StackHandler.createStack(req, res); +}); + +router.post("/start/:name", async (req: Request, res: Response) => { + const StackHandler = createStackHandler(req, res); + return StackHandler.start(req, res); +}); + +router.post("/stop/:name", async (req: Request, res: Response) => { + const StackHandler = createStackHandler(req, res); + return StackHandler.stop(req, res); +}); + +router.get("/get/:name", async (req: Request, res: Response) => { + const StackHandler = createStackHandler(req, res); + return await StackHandler.stackCompose(req, res); +}); + +router.post("/set-env/:name", async (req: Request, res: Response) => { + const StackHandler = createStackHandler(req, res); + return await StackHandler.setStackEnv(req, res); +}); + +router.get("/get-env/:name", async (req: Request, res: Response) => { + const StackHandler = createStackHandler(req, res); + return await StackHandler.getStackEnv(req, res); +}); + +export default router; diff --git a/src/sample-variable.json b/src/sample-variable.json index 06153af5..f507796b 100644 --- a/src/sample-variable.json +++ b/src/sample-variable.json @@ -1,7 +1,7 @@ { "VERSION": "", "RUNNING_IN_DOCKER": "", - "TRUSTED_PROXYS": "", + "TRUSTED_PROXIES": "", "HA_MASTER": "", "HA_MASTER_IP": "", "HA_NODE": "", @@ -18,5 +18,7 @@ "TELEGRAM_BOT_TOKEN": "", "TELEGRAM_CHAT_ID": "", "WHATSAPP_API_URL": "", - "WHATSAPP_RECIPIENT": "" + "WHATSAPP_RECIPIENT": "", + "AUTOMATIC_ENVIRONMENT_FILE_MANAGEMENT": "true", + "LOG_LEVEL": "info" } diff --git a/src/server.ts b/src/server.ts index bb0ea225..edcb2ec5 100644 --- a/src/server.ts +++ b/src/server.ts @@ -2,15 +2,17 @@ import express from "express"; import initializeApp from "./init"; import writeUserConf from "./config/hostsystem"; import { startServer } from "./utils/startServer"; -const port: number = parseInt(process.env.PORT || "9876"); +import http from "http"; +const port: number = parseInt(process.env.PORT || "9876"); const app = express(); +const server = http.createServer(app); + +initializeApp(app, server); if (process.env.NODE_ENV !== "testing") { writeUserConf(port); - startServer(app, port); + startServer(app, server, port); } -initializeApp(app); - -export default app; +export default app; \ No newline at end of file diff --git a/src/typings/dockerCompose.ts b/src/typings/dockerCompose.ts new file mode 100644 index 00000000..e30f7e0d --- /dev/null +++ b/src/typings/dockerCompose.ts @@ -0,0 +1,92 @@ +export interface DockerComposeFile { + services: Record; + networks?: Record; + volumes?: Record; +} + +export interface ServiceDefinition { + image?: string; + build?: BuildDefinition; + container_name?: string; + command?: string | string[]; + environment?: Record; + ports?: string[] | PortMapping[]; + volumes?: string[]; + networks?: string[]; + restart?: string; + depends_on?: string[]; + deploy?: DeployDefinition; + env_file?: string[]; +} + +export interface BuildDefinition { + context: string; + dockerfile?: string; + args?: Record; + cache_from?: string[]; + labels?: Record; + target?: string; +} + +export interface PortMapping { + target: number; + published: number; + protocol?: "tcp" | "udp"; + mode?: "host" | "ingress"; +} + +export interface DeployDefinition { + replicas?: number; + resources?: ResourcesDefinition; + restart_policy?: RestartPolicyDefinition; + labels?: Record; + update_config?: UpdateConfigDefinition; +} + +export interface ResourcesDefinition { + limits?: ResourceLimits; + reservations?: ResourceReservations; +} + +export interface ResourceLimits { + cpus?: string; + memory?: string; +} + +export interface ResourceReservations { + cpus?: string; + memory?: string; +} + +export interface RestartPolicyDefinition { + condition?: "none" | "on-failure" | "any"; + delay?: string; + max_attempts?: number; + window?: string; +} + +export interface UpdateConfigDefinition { + parallelism?: number; + delay?: string; + failure_action?: "continue" | "pause"; + monitor?: string; + max_failure_ratio?: number; + order?: "start-first" | "stop-first"; +} + +export interface NetworkDefinition { + driver?: string; + driver_opts?: Record; + attachable?: boolean; + external?: boolean; + internal?: boolean; + labels?: Record; +} + +export interface VolumeDefinition { + driver?: string; + driver_opts?: Record; + external?: boolean; + labels?: Record; + name?: string; +} diff --git a/src/typings/dockerStackEnv.ts b/src/typings/dockerStackEnv.ts new file mode 100644 index 00000000..c784b85d --- /dev/null +++ b/src/typings/dockerStackEnv.ts @@ -0,0 +1,10 @@ +interface dockerStackProperty { + name: string; + value: string; +} + +interface dockerStackEnv { + environment: dockerStackProperty[]; +} + +export { dockerStackEnv, dockerStackProperty }; diff --git a/src/typings/stackConfig.ts b/src/typings/stackConfig.ts new file mode 100644 index 00000000..45c72553 --- /dev/null +++ b/src/typings/stackConfig.ts @@ -0,0 +1,5 @@ +interface stackConfig { + stacks: string[]; +} + +export { stackConfig }; diff --git a/src/utils/containerService.ts b/src/utils/containerService.ts index ca683ed3..86dc2d38 100644 --- a/src/utils/containerService.ts +++ b/src/utils/containerService.ts @@ -1,17 +1,18 @@ import logger from "./logger"; -import { ContainerInfo, ContainerStats, ContainerInspectInfo } from "dockerode"; -import getDockerClient from "./dockerClient"; +import { ContainerInfo, } from "dockerode"; +import { getDockerClient } from "./dockerClient"; import fs from "fs"; import { atomicWrite } from "./atomicWrite"; const configPath = "./src/data/dockerConfig.json"; import { AllContainerData, HostConfig } from "../typings/dockerConfig"; import { generateGraphFiles } from "../handlers/graph"; +import { WebSocket } from "ws"; -function loadConfig() { +export function loadConfig() { try { if (!fs.existsSync(configPath)) { logger.warn( - `Config file not found. Creating an empty file at ${configPath}`, + `Config file not found. Creating an empty file at ${configPath}` ); atomicWrite(configPath, JSON.stringify({ hosts: [] }, null, 2)); } @@ -22,96 +23,145 @@ function loadConfig() { } catch (error: unknown) { const errorMsg = error instanceof Error ? error.message : String(error); logger.error(errorMsg); + return { hosts: [] }; } } -async function fetchAllContainers(): Promise { +export async function fetchContainersForHost(hostName: string) { const config = loadConfig(); - if (!config || !config.hosts) { - logger.error("Invalid or missing host configuration."); - return {}; + const hostConfig = config.hosts.find((h: HostConfig) => h.name === hostName); + + if (!hostConfig) { + throw new Error(`Host ${hostName} not found in configuration`); } - const allContainerData: AllContainerData = {}; + try { + const docker = getDockerClient(hostName); + const containers: ContainerInfo[] = await docker.listContainers({ all: true }); - for (const hostConfig of config.hosts as HostConfig[]) { - const hostName = hostConfig.name; - try { - const docker = getDockerClient(hostName); - logger.debug(`Now processing: ${hostName}`); - const containers: ContainerInfo[] = await docker.listContainers({ - all: true, - }); - - allContainerData[hostName] = await Promise.all( - containers.map(async (container) => { - try { - const containerInstance = docker.getContainer(container.Id); - const containerInfo: ContainerInspectInfo = - await containerInstance.inspect(); - const containerStats: ContainerStats = - await containerInstance.stats({ stream: false }); - - const cpuDelta = - containerStats.cpu_stats.cpu_usage.total_usage - - containerStats.precpu_stats.cpu_usage.total_usage; - const systemCpuDelta = - containerStats.cpu_stats.system_cpu_usage - - containerStats.precpu_stats.system_cpu_usage; - const cpuUsage = - systemCpuDelta > 0 - ? (cpuDelta / systemCpuDelta) * - containerStats.cpu_stats.online_cpus - : 0; - - return { - name: container.Names[0].replace("/", ""), - id: container.Id, - hostName, - state: container.State, - cpu_usage: cpuUsage * 1000000000, - mem_usage: containerStats.memory_stats.usage, - mem_limit: containerStats.memory_stats.limit, - net_rx: containerStats.networks?.eth0?.rx_bytes || 0, - net_tx: containerStats.networks?.eth0?.tx_bytes || 0, - current_net_rx: containerStats.networks?.eth0?.rx_bytes || 0, - current_net_tx: containerStats.networks?.eth0?.tx_bytes || 0, - networkMode: containerInfo.HostConfig.NetworkMode || "unknown", - }; - } catch (error: unknown) { - const errorMsg = - error instanceof Error ? error.message : String(error); - logger.error(errorMsg); - - return { - name: container.Names[0].replace("/", ""), - id: container.Id, - hostName, - state: container.State, - cpu_usage: 0, - mem_usage: 0, - mem_limit: 0, - net_rx: 0, - net_tx: 0, - current_net_rx: 0, - current_net_tx: 0, - networkMode: "unknown", - }; - } - }), - ); - } catch (error: unknown) { - const errorMsg = error instanceof Error ? error.message : String(error); - logger.error(errorMsg); + return await Promise.all( + containers.map(async (container) => { + try { + const containerInstance = docker.getContainer(container.Id); + const [containerInfo, containerStats] = await Promise.all([ + containerInstance.inspect(), + containerInstance.stats({ stream: false }), + ]); - allContainerData[hostName] = { - error: `Error fetching containers: ${(error as Error).message}`, - }; - } + const cpuDelta = + containerStats.cpu_stats.cpu_usage.total_usage - + containerStats.precpu_stats.cpu_usage.total_usage; + const systemCpuDelta = + containerStats.cpu_stats.system_cpu_usage - + containerStats.precpu_stats.system_cpu_usage; + const cpuUsage = + systemCpuDelta > 0 + ? (cpuDelta / systemCpuDelta) * containerStats.cpu_stats.online_cpus + : 0; + + return { + name: container.Names[0].replace("/", ""), + id: container.Id, + hostName, + state: container.State, + cpu_usage: cpuUsage, + mem_usage: containerStats.memory_stats.usage, + mem_limit: containerStats.memory_stats.limit, + net_rx: containerStats.networks?.eth0?.rx_bytes || 0, + net_tx: containerStats.networks?.eth0?.tx_bytes || 0, + current_net_rx: containerStats.networks?.eth0?.rx_bytes || 0, + current_net_tx: containerStats.networks?.eth0?.tx_bytes || 0, + networkMode: containerInfo.HostConfig.NetworkMode || "unknown", + }; + } catch (error) { + logger.error(`Error processing container ${container.Id}: ${error}`); + return { + name: container.Names[0].replace("/", ""), + id: container.Id, + hostName, + state: container.State, + cpu_usage: 0, + mem_usage: 0, + mem_limit: 0, + net_rx: 0, + net_tx: 0, + current_net_rx: 0, + current_net_tx: 0, + networkMode: "unknown", + }; + } + }) + ); + } catch (error) { + logger.error(`Error fetching containers for ${hostName}: ${error}`); + throw error; } +} + +export async function fetchAllContainers(): Promise { + const config = loadConfig(); + const allContainerData: AllContainerData = {}; + + await Promise.all( + config.hosts.map(async (hostConfig: HostConfig) => { + try { + allContainerData[hostConfig.name] = await fetchContainersForHost(hostConfig.name); + } catch (error) { + allContainerData[hostConfig.name] = { + error: `Error fetching containers: ${error instanceof Error ? error.message : String(error)}` + }; + } + }) + ); generateGraphFiles(allContainerData); return allContainerData; } -export default fetchAllContainers; +export async function streamContainerData(ws: WebSocket, hostName: string) { + try { + const containers = await fetchContainersForHost(hostName); + ws.send(JSON.stringify({ type: "containers", data: containers })); + + const docker = getDockerClient(hostName); + const eventStream = await docker.getEvents(); + + // eslint-disable-next-line + if (!(eventStream instanceof require('stream').Readable)) { + throw new Error('Failed to get valid event stream'); + } + + const handleData = (chunk: Buffer) => { + ws.send(JSON.stringify({ type: "container-event", data: chunk.toString() })); + }; + + const handleError = (err: Error) => { + logger.error(`Event stream error for ${hostName}: ${err.message}`); + ws.close(); + }; + + eventStream + .on('data', handleData) + .on('error', handleError); + + const closeHandler = () => { + eventStream + .removeListener('data', handleData) + .removeListener('error', handleError) + .removeListener('closed', handleError); + logger.info(`Closed event stream for ${hostName}`); + }; + + ws.on('close', closeHandler); + ws.on('error', closeHandler); + + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + logger.error("Container data error:", message); + ws.send(JSON.stringify({ + error: "Failed to fetch container data", + details: message + })); + ws.close(); + } +} \ No newline at end of file diff --git a/src/utils/dockerClient.ts b/src/utils/dockerClient.ts index 4cd2579e..469c4096 100644 --- a/src/utils/dockerClient.ts +++ b/src/utils/dockerClient.ts @@ -1,7 +1,6 @@ import Docker from "dockerode"; import fs from "fs"; import logger from "./logger"; - import { dockerConfig, target } from "../typings/dockerConfig"; function loadDockerConfig(): dockerConfig { @@ -19,7 +18,7 @@ function loadDockerConfig(): dockerConfig { function createDockerClient(hostConfig: target): Docker { logger.info( - `Creating Docker client for host: ${hostConfig.url} on port: ${hostConfig.port || 2375}`, + `Creating Docker client for host: ${hostConfig.url} on port: ${hostConfig.port || 2375}` ); return new Docker({ host: hostConfig.url, @@ -28,7 +27,7 @@ function createDockerClient(hostConfig: target): Docker { }); } -const getDockerClient = (hostName: string): Docker => { +export const getDockerClient = (hostName: string): Docker => { logger.debug(`Getting Docker Client for ${hostName}`); const config = loadDockerConfig(); const hostConfig = config.hosts.find((host) => host.name === hostName); @@ -40,5 +39,3 @@ const getDockerClient = (hostName: string): Docker => { } return createDockerClient(hostConfig); }; - -export default getDockerClient; diff --git a/src/utils/extractHostData.ts b/src/utils/extractHostData.ts index 6c60953b..a383dc00 100644 --- a/src/utils/extractHostData.ts +++ b/src/utils/extractHostData.ts @@ -3,8 +3,52 @@ import logger from "./logger"; type ComponentMap = Record; -// Export the function with type annotations -function extractRelevantData(jsonData: JsonData) { +interface RelevantData { + hostName: string; + info: { + ID: string; + Containers: number; + ContainersRunning: number; + ContainersPaused: number; + ContainersStopped: number; + Images: number; + OperatingSystem: string; + KernelVersion: string; + Architecture: string; + MemTotal: number; + NCPU: number; + }; + version: { + Components: ComponentMap; + }; +} + +function processComponents(components: unknown): ComponentMap { + try { + if (!Array.isArray(components)) return {}; + + return components.reduce((acc, component) => { + if ( + typeof component === 'object' && + component !== null && + 'Name' in component && + 'Version' in component + ) { + const { Name, Version } = component; + if (typeof Name === 'string' && typeof Version === 'string') { + acc[Name] = Version; + } + } + return acc; + }, {}); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + logger.error(`Error processing components: ${errorMessage}`); + return {}; + } +} + +export function extractRelevantData(jsonData: JsonData): RelevantData { return { hostName: jsonData.hostName, info: { @@ -21,31 +65,7 @@ function extractRelevantData(jsonData: JsonData) { NCPU: jsonData.info.NCPU, }, version: { - Components: (() => { - try { - if (!Array.isArray(jsonData?.version?.Components)) { - return {}; - } - - return jsonData.version.Components.reduce( - (acc, component) => { - if ( - typeof component?.Name === "string" && - typeof component?.Version === "string" - ) { - acc[component.Name] = component.Version; - } - return acc; - }, - {}, - ); - } catch (error: unknown) { - const errorMsg = - error instanceof Error ? error.message : String(error); - logger.error(errorMsg); - return false; - } - })(), + Components: processComponents(jsonData?.version?.Components), }, }; } diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 00adbdfc..2fd67bd5 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -1,7 +1,7 @@ import { createLogger, format, transports } from "winston"; import DailyRotateFile from "winston-daily-rotate-file"; +import { LOG_LEVEL } from "../config/variables"; -// ANSI color codes for log level customization const colors = { gray: "\x1b[90m", reset: "\x1b[0m", @@ -12,7 +12,6 @@ const colors = { blue: "\x1b[34m", }; -// Custom formatter to colorize log levels function colorizeLogLevel(level: string, levelName: string) { switch (level) { case "info": @@ -28,7 +27,7 @@ function colorizeLogLevel(level: string, levelName: string) { } } -// Filter out unwanted logs (example: Exit listeners logs) +// Filter out Exit listeners logs const filterLogs = format((info) => { if ( typeof info.message === "string" && @@ -39,9 +38,8 @@ const filterLogs = format((info) => { return info; }); -// Logger instance const logger = createLogger({ - level: "debug", + level: LOG_LEVEL, format: format.combine( filterLogs(), format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), @@ -56,7 +54,7 @@ const logger = createLogger({ info.level.toLowerCase(), level, ); - const message = `${colors.white}${info.message}${colors.reset}`; + const message = `${colors.white}${(info.message as string).replace(/\n|\r/g, "")}${colors.reset}`; return `${timestamp} ${levelColorized} : ${message}`; }), diff --git a/src/utils/startServer.ts b/src/utils/startServer.ts index 5aaa79d7..7ca612f8 100644 --- a/src/utils/startServer.ts +++ b/src/utils/startServer.ts @@ -1,15 +1,18 @@ import { Express } from "express"; +import { Server } from 'http'; import { startMasterNode } from "../controllers/highAvailability"; import writeUserConf from "../config/hostsystem"; import initFiles from "../config/initFiles"; -export function startServer(app: Express, port: number) { + +export function startServer(app: Express, server: Server, port: number) { if (process.env.NODE_ENV === "testing") { writeUserConf(port); initFiles(); } - app.listen(port, () => { + + server.listen(port, () => { startMasterNode(); }); -} +} \ No newline at end of file diff --git a/src/utils/webSocket.ts b/src/utils/webSocket.ts new file mode 100644 index 00000000..893647e3 --- /dev/null +++ b/src/utils/webSocket.ts @@ -0,0 +1,113 @@ +import { Server } from 'http'; +import { WebSocketServer, WebSocket } from 'ws'; +import { URL } from 'url'; +import fs from 'fs'; +import logger from "./logger"; +import { streamContainerData } from './containerService'; + +export function setupWebSocket(server: Server) { + const wss = new WebSocketServer({ noServer: true }); + + server.on('upgrade', (req, socket, head) => { + logger.debug(`Received upgrade request for URL: ${req.url}`); + const baseURL = `http://${req.headers.host}/`; + const requestURL = new URL(req.url || '', baseURL); + const {pathname} = requestURL; + logger.debug(`Parsed pathname: ${pathname}`); + + // Debug log to verify path handling + logger.debug(`Handling upgrade for path: ${pathname}`); + + if (pathname === '/wss/container-data' || pathname === '/wss/server-logs') { + wss.handleUpgrade(req, socket, head, (ws) => { + wss.emit('connection', ws, req); + }); + } else { + logger.warn(`Rejected WebSocket connection to invalid path: ${pathname}`); + socket.write('HTTP/1.1 404 Not Found\r\n\r\n'); + socket.destroy(); + } + }); + + server.on("error", (error) => { + logger.error("HTTP server error:", error); + }); + + logger.debug("WebSocket server attached to HTTP server"); + + wss.on('connection', (ws: WebSocket, req) => { + const baseURL = `http://${req.headers.host}/`; + const requestURL = new URL(req.url || '', baseURL); + const {pathname} = requestURL; + + logger.info(`WebSocket connection established to ${pathname}`); + + const handleError = (error: string) => { + ws.send(JSON.stringify({ error })); + ws.close(); + }; + + if (pathname === '/wss/container-data') { + const hostName = requestURL.searchParams.get('host'); + if (!hostName) { + handleError('Missing required host parameter'); + return; + } + streamContainerData(ws, hostName); + } else if (pathname === '/wss/server-logs') { + const logFiles = fs.readdirSync("logs/").filter(file => file.startsWith('app-')); + + if (logFiles.length === 0) { + console.error('No log files found'); + return; + } + + const sortedLogFiles = logFiles.sort((a, b) => { + const dateA = a.match(/\d{4}-\d{2}-\d{2}/)?.[0] ?? ""; + const dateB = b.match(/\d{4}-\d{2}-\d{2}/)?.[0] ?? ""; + + return dateB.localeCompare(dateA); + }); + + const logPath = "logs/" + sortedLogFiles[0]; + + if (!fs.existsSync(logPath)) { + handleError('Log file not found'); + logger.error(`Log file ${logPath} not found`) + return; + } + + // Read the initial content of the log file + let lastSize = fs.statSync(logPath).size; + const history = fs.readFileSync(logPath, 'utf-8'); + ws.send(JSON.stringify({ type: 'log-history', data: history })); + + // Watch the log file for changes + const watcher = fs.watch(logPath, (eventType) => { + if (eventType === 'change') { + const newSize = fs.statSync(logPath).size; + if (newSize > lastSize) { + const stream = fs.createReadStream(logPath, { + start: lastSize, + end: newSize - 1, + encoding: 'utf-8' + }); + + stream.on('data', (chunk) => { + ws.send(JSON.stringify({ type: 'log-update', data: chunk })); + }); + + lastSize = newSize; + } + } + }); + + ws.on('close', () => { + watcher.close(); + logger.info('Closed WebSocket connection for logs'); + }); + } else { + handleError('Invalid WebSocket endpoint'); + } + }); +} \ No newline at end of file