diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 270d28d4223..86f40af7a09 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,3 @@ -* @netlify/ecosystem-pod-composable-tooling -docs/ @netlify/ecosystem-pod-composable-tooling @netlify/department-docs -site/ @netlify/ecosystem-pod-composable-tooling @netlify/department-docs +* @netlify/composable-platform +docs/ @netlify/composable-platform @netlify/department-docs +site/ @netlify/composable-platform @netlify/department-docs diff --git a/.github/workflows/benchmark-post.yml b/.github/workflows/benchmark-post.yml index 938c17ba478..27791c8f41a 100644 --- a/.github/workflows/benchmark-post.yml +++ b/.github/workflows/benchmark-post.yml @@ -12,7 +12,7 @@ jobs: # This posts the status to the PR/commit - uses: haya14busa/action-workflow_run-status@v1 - name: Download deltas - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: # This is the workflow that triggered this run workflow: ${{ github.event.workflow.id }} diff --git a/.github/workflows/vale.yml b/.github/workflows/vale.yml index 066ea422308..f54dcf59b7a 100644 --- a/.github/workflows/vale.yml +++ b/.github/workflows/vale.yml @@ -11,7 +11,7 @@ permissions: jobs: lint-docs: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Git checkout uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index d080ce2a521..fbdb7fedf26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,34 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +## [17.37.2](https://github.com/netlify/cli/compare/v17.37.1...v17.37.2) (2024-10-29) + + +### Bug Fixes + +* add zsh autocomplete setup and file permissions instructions to completion:install ([#6882](https://github.com/netlify/cli/issues/6882)) ([75c0e7b](https://github.com/netlify/cli/commit/75c0e7b5e7e7e0db61ca4c0a8b7df8a5f5b9fd6e)) +* **deps:** update dependency @netlify/build to v29.55.4 ([#6892](https://github.com/netlify/cli/issues/6892)) ([da3563b](https://github.com/netlify/cli/commit/da3563b7e3059305bfb742d5ef5f6418d11a1208)) +* **deps:** update dependency @netlify/build to v29.56.0 ([#6906](https://github.com/netlify/cli/issues/6906)) ([e63a9c2](https://github.com/netlify/cli/commit/e63a9c275eaf3845102276d12ac7acd20075c5fa)) +* **deps:** update dependency @types/node to v20.17.1 ([#6903](https://github.com/netlify/cli/issues/6903)) ([93a728b](https://github.com/netlify/cli/commit/93a728b93964be16ff1d64e9a2f3ae5e7f2bafde)) +* **deps:** update dependency ws to v8.18.0 ([#6904](https://github.com/netlify/cli/issues/6904)) ([89e814d](https://github.com/netlify/cli/commit/89e814dd3ad950f50a5fedcc353972bb03db051e)) +* **deps:** update netlify packages ([#6891](https://github.com/netlify/cli/issues/6891)) ([8db8a3a](https://github.com/netlify/cli/commit/8db8a3a31bd65cf040f5d752dac58659d4eff673)) +* **deps:** update netlify packages ([#6899](https://github.com/netlify/cli/issues/6899)) ([62d3123](https://github.com/netlify/cli/commit/62d312335fa1fa1a4f857472b7e3c6bacc506a23)) +* improve console message when unlinking from directory without a netlify.toml ([#6897](https://github.com/netlify/cli/issues/6897)) ([622098e](https://github.com/netlify/cli/commit/622098e3a5d6463147cae420df235e14353b4c74)) +* link --name prefers exact match if exists, first match otherwise ([#6865](https://github.com/netlify/cli/issues/6865)) ([6a15c79](https://github.com/netlify/cli/commit/6a15c79843cc2f9af4571fcb630aabc9a2f4c8e0)) +* logs: deploy command instructs user to link to a site if one is … ([#6867](https://github.com/netlify/cli/issues/6867)) ([98763f7](https://github.com/netlify/cli/commit/98763f72f7aa6176e1625b81ba143a62f9984c3b)) +* updated error message for attempting to deploy a site with a bad… ([#6884](https://github.com/netlify/cli/issues/6884)) ([9cb44c5](https://github.com/netlify/cli/commit/9cb44c551262c1436776023d8048fa3ff4ad6f5e)) + +## [17.37.1](https://github.com/netlify/cli/compare/v17.37.0...v17.37.1) (2024-10-17) + + +### Bug Fixes + +* **deploy:** fix edge function logs url scope key ([#6881](https://github.com/netlify/cli/issues/6881)) ([f833f2d](https://github.com/netlify/cli/commit/f833f2dc4d238769217e30411cb98ee326dcb582)) +* **deps:** update dependency @netlify/blobs to v8.1.0 ([#6870](https://github.com/netlify/cli/issues/6870)) ([b82c536](https://github.com/netlify/cli/commit/b82c5369a5abbd763f53c2d107cab38826974979)) +* **deps:** update dependency @types/node to v20.16.11 ([#6874](https://github.com/netlify/cli/issues/6874)) ([e41ac33](https://github.com/netlify/cli/commit/e41ac33ca489aaeba6dc034222e89e015120cf28)) +* **deps:** update dependency cookie to v0.7.2 ([#6875](https://github.com/netlify/cli/issues/6875)) ([69a7a3b](https://github.com/netlify/cli/commit/69a7a3b09c96512471f451c90a6d4253dee5648d)) +* **deps:** update dependency express to v4.21.1 ([#6858](https://github.com/netlify/cli/issues/6858)) ([9d37ec0](https://github.com/netlify/cli/commit/9d37ec02a2b8a2112c56a24000148979429a0f9e)) + ## [17.37.0](https://github.com/netlify/cli/compare/v17.36.4...v17.37.0) (2024-10-11) diff --git a/bin/run.js b/bin/run.js index 0ab97550858..2e34d199ca8 100755 --- a/bin/run.js +++ b/bin/run.js @@ -3,9 +3,10 @@ import { argv } from 'process' import updateNotifier from 'update-notifier' -import { createMainCommand } from '../dist/commands/index.js' +import { runProgram } from '../dist/utils/run-program.js' import { error } from '../dist/utils/command-helpers.js' import getPackageJson from '../dist/utils/get-package-json.js' +import { createMainCommand } from '../dist/commands/main.js' // 12 hours const UPDATE_CHECK_INTERVAL = 432e5 @@ -24,7 +25,8 @@ try { const program = createMainCommand() try { - await program.parseAsync(argv) + await runProgram(program, argv) + program.onEnd() } catch (error_) { program.onEnd(error_) diff --git a/docs/commands/blobs.md b/docs/commands/blobs.md index 77388036925..d4553f13d20 100644 --- a/docs/commands/blobs.md +++ b/docs/commands/blobs.md @@ -57,6 +57,7 @@ netlify blobs:delete **Flags** - `filter` (*string*) - For monorepos, specify the name of the application to run the command in +- `force` (*boolean*) - Bypasses prompts & Force the command to run. - `debug` (*boolean*) - Print debugging information --- @@ -124,6 +125,7 @@ netlify blobs:set **Flags** - `filter` (*string*) - For monorepos, specify the name of the application to run the command in +- `force` (*boolean*) - Bypasses prompts & Force the command to run. - `input` (*string*) - Defines the filesystem path where the blob data should be read from - `debug` (*boolean*) - Print debugging information diff --git a/docs/commands/env.md b/docs/commands/env.md index 2581f6d3179..517102bfa81 100644 --- a/docs/commands/env.md +++ b/docs/commands/env.md @@ -54,6 +54,7 @@ netlify env:clone **Flags** - `filter` (*string*) - For monorepos, specify the name of the application to run the command in +- `force` (*boolean*) - Bypasses prompts & Force the command to run. - `from` (*string*) - Site ID (From) - `to` (*string*) - Site ID (To) - `debug` (*boolean*) - Print debugging information @@ -167,6 +168,7 @@ netlify env:set - `context` (*string*) - Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev") (default: all contexts) - `filter` (*string*) - For monorepos, specify the name of the application to run the command in +- `force` (*boolean*) - Bypasses prompts & Force the command to run. - `scope` (*builds | functions | post-processing | runtime*) - Specify a scope (default: all scopes) - `secret` (*boolean*) - Indicate whether the environment variable value can be read again. - `debug` (*boolean*) - Print debugging information @@ -202,6 +204,7 @@ netlify env:unset - `context` (*string*) - Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev") (default: all contexts) - `filter` (*string*) - For monorepos, specify the name of the application to run the command in +- `force` (*boolean*) - Bypasses prompts & Force the command to run. - `debug` (*boolean*) - Print debugging information **Examples** diff --git a/docs/commands/sites.md b/docs/commands/sites.md index a19222ef834..c6dfb003b7e 100644 --- a/docs/commands/sites.md +++ b/docs/commands/sites.md @@ -109,7 +109,7 @@ netlify sites:delete **Flags** - `filter` (*string*) - For monorepos, specify the name of the application to run the command in -- `force` (*boolean*) - delete without prompting (useful for CI) +- `force` (*boolean*) - Delete without prompting (useful for CI). - `debug` (*boolean*) - Print debugging information **Examples** diff --git a/functions-templates/javascript/sanity-create/package.json b/functions-templates/javascript/sanity-create/package.json index 21b951bae00..e961223558f 100644 --- a/functions-templates/javascript/sanity-create/package.json +++ b/functions-templates/javascript/sanity-create/package.json @@ -15,6 +15,6 @@ "author": "Sanity.io", "license": "MIT", "dependencies": { - "@sanity/client": "^0.147.3" + "@sanity/client": "^6.0.0" } } diff --git a/functions-templates/javascript/sanity-groq/package.json b/functions-templates/javascript/sanity-groq/package.json index c62c2e1bc27..d59656ca24f 100644 --- a/functions-templates/javascript/sanity-groq/package.json +++ b/functions-templates/javascript/sanity-groq/package.json @@ -16,6 +16,6 @@ "author": "Sanity.io", "license": "MIT", "dependencies": { - "@sanity/client": "^0.147.3" + "@sanity/client": "^6.0.0" } } diff --git a/functions-templates/rust/hello-world/Cargo.toml b/functions-templates/rust/hello-world/Cargo.toml index 92b487d5300..1b7ccf8f405 100644 --- a/functions-templates/rust/hello-world/Cargo.toml +++ b/functions-templates/rust/hello-world/Cargo.toml @@ -8,7 +8,7 @@ version = "0.1.0" [dependencies] aws_lambda_events = "0.15.0" http = "0.2.9" -lambda_runtime = "0.11.0" +lambda_runtime = "0.13.0" log = "0.4.17" simple_logger = "1.16.0" tokio = "1.27.0" diff --git a/functions-templates/typescript/hello-world/package-lock.json b/functions-templates/typescript/hello-world/package-lock.json index 309f957ea2a..ab285fe9d05 100644 --- a/functions-templates/typescript/hello-world/package-lock.json +++ b/functions-templates/typescript/hello-world/package-lock.json @@ -46,9 +46,9 @@ } }, "node_modules/@types/node": { - "version": "20.16.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", - "integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==", + "version": "20.17.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz", + "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==", "dependencies": { "undici-types": "~6.19.2" } @@ -100,9 +100,9 @@ } }, "@types/node": { - "version": "20.16.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", - "integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==", + "version": "20.17.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz", + "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==", "requires": { "undici-types": "~6.19.2" } diff --git a/package-lock.json b/package-lock.json index 27cabd48a91..db767c5dbdb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,25 +1,25 @@ { "name": "netlify-cli", - "version": "17.37.0", + "version": "17.37.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "netlify-cli", - "version": "17.37.0", + "version": "17.37.2", "hasInstallScript": true, "license": "MIT", "dependencies": { "@bugsnag/js": "7.25.0", "@fastify/static": "7.0.4", "@netlify/blobs": "8.1.0", - "@netlify/build": "29.55.2", - "@netlify/build-info": "7.15.1", - "@netlify/config": "20.19.0", + "@netlify/build": "29.56.1", + "@netlify/build-info": "7.15.2", + "@netlify/config": "20.19.1", "@netlify/edge-bundler": "12.2.3", "@netlify/edge-functions": "2.9.0", "@netlify/local-functions-proxy": "1.1.1", - "@netlify/zip-it-and-ship-it": "9.40.2", + "@netlify/zip-it-and-ship-it": "9.41.1", "@octokit/rest": "20.1.1", "@opentelemetry/api": "1.8.0", "ansi-escapes": "7.0.0", @@ -31,7 +31,7 @@ "boxen": "7.1.1", "chalk": "5.3.0", "chokidar": "3.6.0", - "ci-info": "4.0.0", + "ci-info": "4.1.0", "clean-deep": "3.4.0", "commander": "10.0.1", "comment-json": "4.2.5", @@ -92,7 +92,7 @@ "netlify-redirector": "0.5.0", "node-fetch": "3.3.2", "node-version-alias": "3.4.1", - "ora": "8.1.0", + "ora": "8.1.1", "p-filter": "4.1.0", "p-map": "7.0.2", "p-wait-for": "5.0.2", @@ -121,7 +121,7 @@ "uuid": "9.0.1", "wait-port": "1.1.0", "write-file-atomic": "5.0.1", - "ws": "8.17.1", + "ws": "8.18.0", "zod": "3.23.8" }, "bin": { @@ -129,21 +129,21 @@ "ntl": "bin/run.js" }, "devDependencies": { - "@babel/preset-react": "7.24.7", + "@babel/preset-react": "7.25.9", "@netlify/eslint-config-node": "7.0.1", "@netlify/functions": "2.8.2", "@sindresorhus/slugify": "2.2.1", "@types/fs-extra": "11.0.4", "@types/inquirer": "9.0.7", "@types/jsonwebtoken": "9.0.7", - "@types/lodash": "4.17.10", + "@types/lodash": "4.17.13", "@types/node": "20.14.8", - "@types/node-fetch": "2.6.11", + "@types/node-fetch": "2.6.12", "@types/parse-gitignore": "1.0.2", "@types/prettyjson": "0.0.33", "@types/semver": "7.5.8", "@types/uuid": "9.0.8", - "@types/ws": "8.5.10", + "@types/ws": "8.5.13", "@vitest/coverage-v8": "1.6.0", "c8": "9.1.0", "cheerio": "1.0.0", @@ -156,16 +156,16 @@ "husky": "8.0.3", "ini": "4.1.3", "is-ci": "3.0.1", - "mock-fs": "5.2.0", - "nock": "13.5.4", - "p-timeout": "6.1.2", + "mock-fs": "5.4.1", + "nock": "13.5.6", + "p-timeout": "6.1.3", "serialize-javascript": "6.0.2", "sinon": "18.0.1", "strip-ansi": "7.1.0", "temp-dir": "3.0.0", "tree-kill": "1.2.2", "typescript": "5.1.6", - "verdaccio": "6.0.1", + "verdaccio": "6.0.2", "vitest": "1.6.0" }, "engines": { @@ -195,11 +195,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz", + "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -282,15 +283,29 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz", + "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7", + "@babel/parser": "^7.26.0", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -307,12 +322,25 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure/node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -370,39 +398,27 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", + "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", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "node_modules/@babel/helper-module-imports/node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -428,9 +444,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "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==", "dev": true, "engines": { "node": ">=6.9.0" @@ -461,25 +477,25 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, "engines": { "node": ">=6.9.0" @@ -503,6 +519,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", @@ -517,6 +534,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -528,6 +546,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -541,6 +560,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -549,6 +569,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -557,6 +578,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -565,9 +587,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.26.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", + "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", + "dependencies": { + "@babel/types": "^7.26.0" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -575,13 +600,25 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/parser/node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -591,12 +628,12 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz", - "integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", + "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -606,16 +643,16 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz", - "integrity": "sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -625,12 +662,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz", - "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", + "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", "dev": true, "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.24.7" + "@babel/plugin-transform-react-jsx": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -639,14 +676,27 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-react-jsx/node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz", - "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", + "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -656,17 +706,17 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz", - "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", + "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.24.7", - "@babel/plugin-transform-react-jsx-development": "^7.24.7", - "@babel/plugin-transform-react-pure-annotations": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-transform-react-display-name": "^7.25.9", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-react-jsx-development": "^7.25.9", + "@babel/plugin-transform-react-pure-annotations": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -676,33 +726,43 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -710,6 +770,19 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/types": { "version": "7.25.6", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", @@ -2260,22 +2333,22 @@ } }, "node_modules/@netlify/build": { - "version": "29.55.2", - "resolved": "https://registry.npmjs.org/@netlify/build/-/build-29.55.2.tgz", - "integrity": "sha512-sXWDIeKtRWc6S9+dG3lCdTSNSB9XfhNFk80kG600sI3ytkhF5rPk6ijJw4Y7drhruCrYFpsDwlPxGY9kBjshBw==", + "version": "29.56.1", + "resolved": "https://registry.npmjs.org/@netlify/build/-/build-29.56.1.tgz", + "integrity": "sha512-0/4GiVTL69AXeIly6ZXIi5g4qU2Oi9djCUcJO6xCZCDgft6TD90JXlsCQ5P/+oh0CFcNPpsy9DBvY8mm0fSFVw==", "dependencies": { "@bugsnag/js": "^7.0.0", "@netlify/blobs": "^7.4.0", "@netlify/cache-utils": "^5.1.6", - "@netlify/config": "^20.19.0", + "@netlify/config": "^20.19.1", "@netlify/edge-bundler": "12.2.3", "@netlify/framework-info": "^9.8.13", - "@netlify/functions-utils": "^5.2.91", + "@netlify/functions-utils": "^5.2.93", "@netlify/git-utils": "^5.1.1", "@netlify/opentelemetry-utils": "^1.2.1", "@netlify/plugins-list": "^6.80.0", "@netlify/run-utils": "^5.1.1", - "@netlify/zip-it-and-ship-it": "9.40.2", + "@netlify/zip-it-and-ship-it": "9.41.1", "@sindresorhus/slugify": "^2.0.0", "ansi-escapes": "^6.0.0", "chalk": "^5.0.0", @@ -2285,7 +2358,7 @@ "figures": "^5.0.0", "filter-obj": "^5.0.0", "got": "^12.0.0", - "hot-shots": "10.1.1", + "hot-shots": "10.2.1", "indent-string": "^5.0.0", "is-plain-obj": "^4.0.0", "js-yaml": "^4.0.0", @@ -2340,9 +2413,9 @@ } }, "node_modules/@netlify/build-info": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@netlify/build-info/-/build-info-7.15.1.tgz", - "integrity": "sha512-4Z7JmoRA/biWnUhua2qsnSje9rXSEQJuX5v2+P8HIcsneNnKEnBqZry/Bct+EzL2+zyJ3DFRjHJB3n9tallg9g==", + "version": "7.15.2", + "resolved": "https://registry.npmjs.org/@netlify/build-info/-/build-info-7.15.2.tgz", + "integrity": "sha512-z249vRTIyeO1Coaa2UaaZJpTN2D9mE0HPvuQfVknJ+WqHdLjPHlmaKu2HyekfnA5zE8mVSkPAJsP9dip3kySSg==", "dependencies": { "@bugsnag/js": "^7.20.0", "@iarna/toml": "^2.2.5", @@ -2438,9 +2511,9 @@ } }, "node_modules/@netlify/build-info/node_modules/yaml": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", - "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", "bin": { "yaml": "bin.mjs" }, @@ -2820,9 +2893,9 @@ } }, "node_modules/@netlify/config": { - "version": "20.19.0", - "resolved": "https://registry.npmjs.org/@netlify/config/-/config-20.19.0.tgz", - "integrity": "sha512-vkqTQ7jaudPSRME6ZzYml6qRWxIJXnUQ8csqOSx5Yv0ysj1zb2l+Ke3c5bc6Cttkg4ay2YLx4M0/7n6nT3KojQ==", + "version": "20.19.1", + "resolved": "https://registry.npmjs.org/@netlify/config/-/config-20.19.1.tgz", + "integrity": "sha512-GkN8IwHilIlusFuAW+DFjhtpghnaelNcHUoZwBDcJou8eyhIZYAj6B4STMyGUggIfMobYGM28kEY3gN4uUVq0g==", "dependencies": { "@iarna/toml": "^2.2.5", "chalk": "^5.0.0", @@ -3939,11 +4012,11 @@ } }, "node_modules/@netlify/functions-utils": { - "version": "5.2.91", - "resolved": "https://registry.npmjs.org/@netlify/functions-utils/-/functions-utils-5.2.91.tgz", - "integrity": "sha512-PezRTuKzKzIbWu71tykioHS7W2Tk4wiGZqkkYzm92FeLRlubRERC9Dwv3Y5LFFNQsWXs701l7CVTojHrpSiA0w==", + "version": "5.2.93", + "resolved": "https://registry.npmjs.org/@netlify/functions-utils/-/functions-utils-5.2.93.tgz", + "integrity": "sha512-/b2JtJuB3KNN5AIfiH/tan/uM4i6nLj2QFGUL9oID58cMsd73iouRacKu4ct+gxUU78y+/6fiOeYRXbcthdltA==", "dependencies": { - "@netlify/zip-it-and-ship-it": "9.40.2", + "@netlify/zip-it-and-ship-it": "9.41.1", "cpy": "^9.0.0", "path-exists": "^5.0.0" }, @@ -4412,14 +4485,14 @@ } }, "node_modules/@netlify/zip-it-and-ship-it": { - "version": "9.40.2", - "resolved": "https://registry.npmjs.org/@netlify/zip-it-and-ship-it/-/zip-it-and-ship-it-9.40.2.tgz", - "integrity": "sha512-CkAwLnqFqhV9hNJO8HxMjd+g2HYtGevjo4gP1P84Sf50HBFyBE2cavfXNaBa1TfNq5/92CixnXz4YptU1DIOOw==", + "version": "9.41.1", + "resolved": "https://registry.npmjs.org/@netlify/zip-it-and-ship-it/-/zip-it-and-ship-it-9.41.1.tgz", + "integrity": "sha512-EzXw1+4OJ4mCZUOqVPDQZNM8jf/563utFo1Ph6dYtSR21E1oYlgt6Oib1pyG/bFGufvdtrxw845/1MTCPvXzJA==", "dependencies": { "@babel/parser": "^7.22.5", "@babel/types": "7.25.6", "@netlify/binary-info": "^1.0.0", - "@netlify/serverless-functions-api": "^1.30.1", + "@netlify/serverless-functions-api": "^1.31.0", "@vercel/nft": "^0.27.1", "archiver": "^7.0.0", "common-path-prefix": "^3.0.0", @@ -4459,9 +4532,9 @@ } }, "node_modules/@netlify/zip-it-and-ship-it/node_modules/@netlify/serverless-functions-api": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-1.30.1.tgz", - "integrity": "sha512-JkbaWFeydQdeDHz1mAy4rw+E3bl9YtbCgkntfTxq+IlNX/aIMv2/b1kZnQZcil4/sPoZGL831Dq6E374qRpU1A==", + "version": "1.31.0", + "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-1.31.0.tgz", + "integrity": "sha512-/ux3fefmw0yGmzRMOqhGrzbAuALtW8HY08bjE4yCk+y8CzB9r9mPd1ApSJe3KlYD+sqDvbQGrEXdifQ/LzUIDQ==", "dependencies": { "@netlify/node-cookies": "^0.1.0", "urlpattern-polyfill": "8.0.2" @@ -5658,9 +5731,9 @@ } }, "node_modules/@types/inquirer/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true }, "node_modules/@types/istanbul-lib-coverage": { @@ -5715,9 +5788,9 @@ } }, "node_modules/@types/lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==", + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", + "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==", "dev": true }, "node_modules/@types/mdast": { @@ -5751,9 +5824,9 @@ } }, "node_modules/@types/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", "dev": true, "dependencies": { "@types/node": "*", @@ -5844,9 +5917,9 @@ "dev": true }, "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", "dev": true, "dependencies": { "@types/node": "*" @@ -6088,20 +6161,19 @@ } }, "node_modules/@verdaccio/auth": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/auth/-/auth-8.0.0-next-8.3.tgz", - "integrity": "sha512-x7/gt4R41i5hat5dVT2WfwTeWolSKTo3k8t12ZBhnf+R+L/a79dQ7/sR8JIT6R9A+nkkFn+RiPspH75H7lprZg==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/auth/-/auth-8.0.0-next-8.4.tgz", + "integrity": "sha512-Bv+du+eIMK2/KU2wIjha2FHQrhBT5RuDOVi1nyRyjEyjmJrUt2RWU0Cb7ASxzQy61nkcJ3bs7kuu9dPHK/Z+jw==", "dev": true, "dependencies": { - "@verdaccio/config": "8.0.0-next-8.3", - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/config": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.4", "@verdaccio/loaders": "8.0.0-next-8.3", - "@verdaccio/logger": "8.0.0-next-8.3", "@verdaccio/signature": "8.0.0-next-8.1", - "@verdaccio/utils": "8.1.0-next-8.3", + "@verdaccio/utils": "8.1.0-next-8.4", "debug": "4.3.7", "lodash": "4.17.21", - "verdaccio-htpasswd": "13.0.0-next-8.3" + "verdaccio-htpasswd": "13.0.0-next-8.4" }, "engines": { "node": ">=18" @@ -6112,12 +6184,12 @@ } }, "node_modules/@verdaccio/auth/node_modules/@verdaccio/utils": { - "version": "8.1.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", - "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "version": "8.1.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", + "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", "dev": true, "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -6197,13 +6269,13 @@ } }, "node_modules/@verdaccio/config": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-8.0.0-next-8.3.tgz", - "integrity": "sha512-bxlesiVi7A1GAHurq7RLFAFd67NTySSwtVMw7D1Ku2Q3v6kAF4TLqxKUrOaA14k0Zk4qyRu4OgXzbjDg0oARcQ==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-8.0.0-next-8.4.tgz", + "integrity": "sha512-9CTYYhaO4xrGbiYjLqRy8EMFPm0YL4a7P6ae8Zm4Dx85Dd6i8XqZ7tlU/+a6qf1g/qggYloolU8pcjaLWNDKAQ==", "dev": true, "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", - "@verdaccio/utils": "8.1.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/utils": "8.1.0-next-8.4", "debug": "4.3.7", "js-yaml": "4.1.0", "lodash": "4.17.21", @@ -6218,12 +6290,12 @@ } }, "node_modules/@verdaccio/config/node_modules/@verdaccio/utils": { - "version": "8.1.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", - "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "version": "8.1.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", + "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", "dev": true, "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -6261,9 +6333,9 @@ } }, "node_modules/@verdaccio/core": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-8.0.0-next-8.3.tgz", - "integrity": "sha512-DPJmWANbpbtJN+cfz5CN4kfAl15F5JCv5qRAIQB9sOTNWjTw26cRpMYHFB9/buBQPuH3mWCjOwE6c+EVlvObLg==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-8.0.0-next-8.4.tgz", + "integrity": "sha512-TCaHwIpr97f4YQkU25E6pk1dbfWTQwYPos1tMb9Q7k6IapoxN0c1s+SyF5FBuMOIfJpTHoWJDo/z7QCouQC3lw==", "dev": true, "dependencies": { "ajv": "8.17.1", @@ -6432,12 +6504,12 @@ "dev": true }, "node_modules/@verdaccio/logger": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/logger/-/logger-8.0.0-next-8.3.tgz", - "integrity": "sha512-rLjv/W9QfFHT80L1L/xXQwY3DaZ03NIq64/Bb4GHOudZgzl8C5Efe6u1q8Ti+jqXVAKCzEswMNISdq2XIQ+azQ==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/logger/-/logger-8.0.0-next-8.4.tgz", + "integrity": "sha512-zJIFpKYNR/api/mxj5HqJSlEMFh9J4sVKk+3QYlPmppW68beZLLzqwchb5+c/V559lnSrGy5HvDUEGLXvp6reA==", "dev": true, "dependencies": { - "@verdaccio/logger-commons": "8.0.0-next-8.3", + "@verdaccio/logger-commons": "8.0.0-next-8.4", "pino": "9.4.0" }, "engines": { @@ -6449,12 +6521,12 @@ } }, "node_modules/@verdaccio/logger-commons": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/logger-commons/-/logger-commons-8.0.0-next-8.3.tgz", - "integrity": "sha512-eMG0UDh66JcPX8ez57HCpsZ0FE9G0pCZ51Xei1MeCFVgNLkzrEcnwOcEGZhd3Tew79A4wGgjFFFywkrRIIomwg==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/logger-commons/-/logger-commons-8.0.0-next-8.4.tgz", + "integrity": "sha512-neDbq5IIRoidFT4Rv3zH9YydICDCJEybb06BzCGVOzlhZ7F+fBzJH1qlBhAEISfbONugDgfuUQ2jbRCKEkHezQ==", "dev": true, "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "@verdaccio/logger-prettify": "8.0.0-next-8.1", "colorette": "2.0.20", "debug": "4.3.7" @@ -6488,17 +6560,17 @@ } }, "node_modules/@verdaccio/middleware": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/middleware/-/middleware-8.0.0-next-8.3.tgz", - "integrity": "sha512-yUe4BGA2/o4GnFuKdquU53kI07xsLMKiMvZfj+M0ZFnFCshCqKmXOoSR8hbzQReUm/OZQSLOppliJn68xmXEVQ==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/middleware/-/middleware-8.0.0-next-8.4.tgz", + "integrity": "sha512-tzpfSpeLKUeyTsQ+fvUsokgdh1NrjDJX/oz2ya8wTYSInKAt1Ld9MRzRVSHJwIQc7wfg46zSjpcKZVLA/YkJ6w==", "dev": true, "dependencies": { - "@verdaccio/config": "8.0.0-next-8.3", - "@verdaccio/core": "8.0.0-next-8.3", - "@verdaccio/url": "13.0.0-next-8.3", - "@verdaccio/utils": "8.1.0-next-8.3", + "@verdaccio/config": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/url": "13.0.0-next-8.4", + "@verdaccio/utils": "8.1.0-next-8.4", "debug": "4.3.7", - "express": "4.21.0", + "express": "4.21.1", "express-rate-limit": "5.5.1", "lodash": "4.17.21", "lru-cache": "7.18.3", @@ -6513,12 +6585,12 @@ } }, "node_modules/@verdaccio/middleware/node_modules/@verdaccio/utils": { - "version": "8.1.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", - "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "version": "8.1.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", + "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", "dev": true, "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -6540,91 +6612,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/@verdaccio/middleware/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@verdaccio/middleware/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@verdaccio/middleware/node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "dev": true, - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/@verdaccio/middleware/node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@verdaccio/middleware/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/@verdaccio/middleware/node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", @@ -6661,36 +6648,10 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@verdaccio/middleware/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/@verdaccio/middleware/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/@verdaccio/search-indexer": { - "version": "8.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/search-indexer/-/search-indexer-8.0.0-next-8.1.tgz", - "integrity": "sha512-Mwwg2o9GicZd6uiCbjBk6xZiWAH/O/2NbEkicPZINFIoJKy1NUihS4RexdDjcsxKEBEggGZXCkzHjzhfaZv1Gg==", + "version": "8.0.0-next-8.2", + "resolved": "https://registry.npmjs.org/@verdaccio/search-indexer/-/search-indexer-8.0.0-next-8.2.tgz", + "integrity": "sha512-sWliVN5BkAGbZ3e/GD0CsZMfPJdRMRuN0tEKQFsvEJifxToq5UkfCw6vKaVvhezsTWqb+Rp5y+2d4n5BDOA49w==", "dev": true, "engines": { "node": ">=18" @@ -6732,14 +6693,14 @@ } }, "node_modules/@verdaccio/tarball": { - "version": "13.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-13.0.0-next-8.3.tgz", - "integrity": "sha512-jJatpGgiKLmTqyW4WlRpIkldd26rHDj5WTugWqa2Wxa1hVS0b6sZKT/9fEffOvjjAJv69Ued4nH1YCcB2hlhfA==", + "version": "13.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-13.0.0-next-8.4.tgz", + "integrity": "sha512-zizQwACK+P9sHtArbuW5MJluRpc3lC6bilGTFNc0TLkHbwL73F8wxkKr5VLzWV7H54+sVKMDs1lCnaoHa0ygmw==", "dev": true, "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", - "@verdaccio/url": "13.0.0-next-8.3", - "@verdaccio/utils": "8.1.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/url": "13.0.0-next-8.4", + "@verdaccio/utils": "8.1.0-next-8.4", "debug": "4.3.7", "gunzip-maybe": "^1.4.2", "lodash": "4.17.21", @@ -6754,12 +6715,12 @@ } }, "node_modules/@verdaccio/tarball/node_modules/@verdaccio/utils": { - "version": "8.1.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", - "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "version": "8.1.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", + "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", "dev": true, "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -6797,18 +6758,18 @@ } }, "node_modules/@verdaccio/ui-theme": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-8.0.0-next-8.3.tgz", - "integrity": "sha512-3A5v8bkvK5Bm+ERsZLYPv4vwo49dwfSy5a3WcAQgWde/oc6jytl/5XOZOaKX33mEiLj313k02j9ArhKuY1JjgA==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-8.0.0-next-8.4.tgz", + "integrity": "sha512-j3STxUuIgvn058LqfXWv+SwRi1fQ7HapMSfVKXAhi09+f4zlD2mtvKLth0WbuzU1NqJPTGLAP9ueBf1210C1Hw==", "dev": true }, "node_modules/@verdaccio/url": { - "version": "13.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-13.0.0-next-8.3.tgz", - "integrity": "sha512-77BkY3j1d1ZPpmBodK2QlRwNP9tn/IneVry4RHX9j+1xNj4clvpn5rObFnVRGeYf2GPZrYZvIdzVP3BjXl0nkA==", + "version": "13.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-13.0.0-next-8.4.tgz", + "integrity": "sha512-Xo+9DUcwYTBV6d0n4vjLAN2k92J33XM/9JNltWM6140oI8lz+VJKiajtejG/hRBi82RioRdWJ0RZDDY6FsbS3Q==", "dev": true, "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "debug": "4.3.7", "lodash": "4.17.21", "validator": "13.12.0" @@ -8923,9 +8884,9 @@ } }, "node_modules/ci-info": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", - "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", "funding": [ { "type": "github", @@ -9018,6 +8979,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "license": "MIT", "dependencies": { "restore-cursor": "^2.0.0" }, @@ -9483,32 +9445,23 @@ } }, "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", "dev": true, "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", + "negotiator": "~0.6.4", "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/compression/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -9524,6 +9477,35 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -10061,9 +10043,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -13975,9 +13957,9 @@ } }, "node_modules/hot-shots": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/hot-shots/-/hot-shots-10.1.1.tgz", - "integrity": "sha512-KTsH9hb+YZHH0IIRf22y0X8mPw8j521W5xRAUeaUlGNBDsf44ixE7ZeyXbUHd/nQ1n04UEhi2ja05/QVOS/CgQ==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/hot-shots/-/hot-shots-10.2.1.tgz", + "integrity": "sha512-tmjcyZkG/qADhcdC7UjAp8D7v7W2DOYFgaZ48fYMuayMQmVVUg8fntKmrjes/b40ef6yZ+qt1lB8kuEDfLC4zw==", "engines": { "node": ">=10.0.0" }, @@ -14307,6 +14289,7 @@ "version": "6.5.2", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "license": "MIT", "dependencies": { "ansi-escapes": "^3.2.0", "chalk": "^2.4.2", @@ -14429,6 +14412,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -14477,6 +14461,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.5" }, @@ -14496,6 +14481,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "license": "MIT", "engines": { "node": ">=4" } @@ -14504,6 +14490,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "license": "MIT", "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -14516,6 +14503,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -14524,6 +14512,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "license": "MIT", "dependencies": { "ansi-regex": "^3.0.0" }, @@ -15484,15 +15473,15 @@ "dev": true }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-buffer": { @@ -17071,9 +17060,9 @@ } }, "node_modules/mock-fs": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.2.0.tgz", - "integrity": "sha512-2dF2R6YMSZbpip1V1WHKGLNjr/k48uQClqMVb5H3MOvwc9qhYis3/IWbj02qIg/Y8MDXKFF4c5v0rxx2o6xTZw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.4.1.tgz", + "integrity": "sha512-sz/Q8K1gXXXHR+qr0GZg2ysxCRr323kuN10O7CtQjraJsFDJ4SJ+0I5MzALz7aRp9lHk8Cc/YdsT95h9Ka1aFw==", "dev": true, "engines": { "node": ">=12.0.0" @@ -17173,7 +17162,8 @@ "node_modules/mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==" + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", + "license": "ISC" }, "node_modules/nan": { "version": "2.17.0", @@ -17376,9 +17366,9 @@ "dev": true }, "node_modules/nock": { - "version": "13.5.4", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.4.tgz", - "integrity": "sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", "dev": true, "dependencies": { "debug": "^4.1.0", @@ -17631,9 +17621,9 @@ } }, "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "dependencies": { "nice-try": "^1.0.4", @@ -18025,9 +18015,9 @@ } }, "node_modules/ora": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.1.0.tgz", - "integrity": "sha512-GQEkNkH/GHOhPFXcqZs3IDahXEQcQxsSjEkK4KvEEST4t7eNzoMjxTzef+EZ+JluDEV+Raoi3WQ2CflnRdSVnQ==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.1.1.tgz", + "integrity": "sha512-YWielGi1XzG1UTvOaCFaNgEnuhZVMSHYkW/FQ7UX8O26PtlpdM84c0f7wLPlkvx2RfiQmnzd61d/MGxmpQeJPw==", "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", @@ -18297,9 +18287,9 @@ } }, "node_modules/p-timeout": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz", - "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.3.tgz", + "integrity": "sha512-UJUyfKbwvr/uZSV6btANfb+0t/mOhKV/KXcCUTp8FcQI+v/0d+wXqH4htrW0E4rR6WiEO/EPvUFiV9D5OI4vlw==", "engines": { "node": ">=14.16" }, @@ -19927,6 +19917,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "license": "MIT", "dependencies": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" @@ -19939,6 +19930,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -19947,6 +19939,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "license": "MIT", "dependencies": { "mimic-fn": "^1.0.0" }, @@ -22195,28 +22188,28 @@ } }, "node_modules/verdaccio": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-6.0.1.tgz", - "integrity": "sha512-fGP5V18Pz3yIDZNZjQrBqRnMr4sDn8fPO7eoZTIX2D7MvqoGELRG88Townq8PhYU5KAPZ2c2OnNRr8SKReO3Ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-6.0.2.tgz", + "integrity": "sha512-XthgJlF1hGW+GR/apRLZ7DQw26XpLI+xjMGb7dhJKxI4Pz2gSiEY1RXP9T9I/rlIBr9Zx6rYOgRk7A9Aeq/kpg==", "dev": true, "dependencies": { "@cypress/request": "3.0.5", - "@verdaccio/auth": "8.0.0-next-8.3", - "@verdaccio/config": "8.0.0-next-8.3", - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/auth": "8.0.0-next-8.4", + "@verdaccio/config": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.4", "@verdaccio/local-storage-legacy": "11.0.2", - "@verdaccio/logger": "8.0.0-next-8.3", - "@verdaccio/middleware": "8.0.0-next-8.3", - "@verdaccio/search-indexer": "8.0.0-next-8.1", + "@verdaccio/logger": "8.0.0-next-8.4", + "@verdaccio/middleware": "8.0.0-next-8.4", + "@verdaccio/search-indexer": "8.0.0-next-8.2", "@verdaccio/signature": "8.0.0-next-8.1", "@verdaccio/streams": "10.2.1", - "@verdaccio/tarball": "13.0.0-next-8.3", - "@verdaccio/ui-theme": "8.0.0-next-8.3", - "@verdaccio/url": "13.0.0-next-8.3", + "@verdaccio/tarball": "13.0.0-next-8.4", + "@verdaccio/ui-theme": "8.0.0-next-8.4", + "@verdaccio/url": "13.0.0-next-8.4", "@verdaccio/utils": "7.0.1-next-8.1", "async": "3.2.6", "clipanion": "4.0.0-rc.4", - "compression": "1.7.4", + "compression": "1.7.5", "cors": "2.8.5", "debug": "4.3.7", "envinfo": "7.14.0", @@ -22235,8 +22228,8 @@ "pkginfo": "0.4.1", "semver": "7.6.3", "validator": "13.12.0", - "verdaccio-audit": "13.0.0-next-8.3", - "verdaccio-htpasswd": "13.0.0-next-8.3" + "verdaccio-audit": "13.0.0-next-8.4", + "verdaccio-htpasswd": "13.0.0-next-8.4" }, "bin": { "verdaccio": "bin/verdaccio" @@ -22250,14 +22243,14 @@ } }, "node_modules/verdaccio-audit": { - "version": "13.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-13.0.0-next-8.3.tgz", - "integrity": "sha512-/mgRfsg+RENtUggcf0xnfPKNJJqidyKING3nyHgS3vABE6CBe4/fWQKs67X4mfCFiIVLf0PiOTFGT8tmwSZubA==", + "version": "13.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-13.0.0-next-8.4.tgz", + "integrity": "sha512-T4yi/46fLngllx5mvFtXsGcW3MxGZZ9IkHYPK1OQw9+Xj9aOuMec2eFdztTRo9SgqZfgblGSY1ESZYy19sQLvw==", "dev": true, "dependencies": { - "@verdaccio/config": "8.0.0-next-8.3", - "@verdaccio/core": "8.0.0-next-8.3", - "express": "4.21.0", + "@verdaccio/config": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.4", + "express": "4.21.1", "https-proxy-agent": "5.0.1", "node-fetch": "cjs" }, @@ -22269,91 +22262,6 @@ "url": "https://opencollective.com/verdaccio" } }, - "node_modules/verdaccio-audit/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/verdaccio-audit/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verdaccio-audit/node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "dev": true, - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/verdaccio-audit/node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/verdaccio-audit/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/verdaccio-audit/node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -22367,40 +22275,14 @@ "node": ">= 6" } }, - "node_modules/verdaccio-audit/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/verdaccio-audit/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/verdaccio-htpasswd": { - "version": "13.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-13.0.0-next-8.3.tgz", - "integrity": "sha512-Nl2rEEyGHJQ/1/93BE9TxBMRN4tKF/51VYTb+hWDQFhDEDAR20rcvJ4ND0jOIIZljI7Lg/WrCPBh90u5IyPJ5Q==", + "version": "13.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-13.0.0-next-8.4.tgz", + "integrity": "sha512-w0knjKz8SdBGSv0Kt61LoUOCYBZ2/iig3bVbGFWTj4MwCUG6eNewMoQ6nbrk+kyHNFVq75IzT1eIhXDEysx15Q==", "dev": true, "dependencies": { - "@verdaccio/core": "8.0.0-next-8.3", - "@verdaccio/file-locking": "13.0.0-next-8.1", + "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/file-locking": "13.0.0-next-8.2", "apache-md5": "1.1.8", "bcryptjs": "2.4.3", "core-js": "3.37.1", @@ -22417,9 +22299,9 @@ } }, "node_modules/verdaccio-htpasswd/node_modules/@verdaccio/file-locking": { - "version": "13.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/file-locking/-/file-locking-13.0.0-next-8.1.tgz", - "integrity": "sha512-9PhfRKXsWaWJkON/2/jdG5/N+9Kk4UINvbMGuJz0A/PbzIYfVrBhry7fcnjn6hFKxVPTbSOSSztRzLF30nmBFg==", + "version": "13.0.0-next-8.2", + "resolved": "https://registry.npmjs.org/@verdaccio/file-locking/-/file-locking-13.0.0-next-8.2.tgz", + "integrity": "sha512-TcHgN3I/N28WBSvtukpGrJhBljl4jyIXq0vEv94vXAG6nUE3saK+vtgo8PfYA3Ueo88v/1zyAbiZM4uxwojCmQ==", "dev": true, "dependencies": { "lockfile": "1.0.4" @@ -23631,9 +23513,9 @@ } }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, @@ -23907,11 +23789,12 @@ } }, "@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz", + "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==", "requires": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, @@ -23972,17 +23855,28 @@ } }, "@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz", + "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==", "dev": true, "requires": { - "@babel/types": "^7.24.7", + "@babel/parser": "^7.26.0", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "dependencies": { + "@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + } + }, "@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", @@ -23996,12 +23890,24 @@ } }, "@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dev": true, "requires": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" + }, + "dependencies": { + "@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + } + } } }, "@babel/helper-compilation-targets": { @@ -24049,33 +23955,26 @@ "@babel/types": "^7.24.7" } }, - "@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dev": true, - "requires": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dev": true, - "requires": { - "@babel/types": "^7.24.7" - } - }, "@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "dependencies": { + "@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + } + } } }, "@babel/helper-module-transforms": { @@ -24092,9 +23991,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "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==", "dev": true }, "@babel/helper-simple-access": { @@ -24116,19 +24015,19 @@ } }, "@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==" + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==" }, "@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==" + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==" }, "@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true }, "@babel/helpers": { @@ -24146,6 +24045,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", @@ -24157,6 +24057,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -24165,6 +24066,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -24174,17 +24076,20 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -24192,101 +24097,148 @@ } }, "@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==" + "version": "7.26.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", + "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", + "requires": { + "@babel/types": "^7.26.0" + }, + "dependencies": { + "@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "requires": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + } + } + } }, "@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-react-display-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz", - "integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", + "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-react-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz", - "integrity": "sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "dependencies": { + "@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + } + } } }, "@babel/plugin-transform-react-jsx-development": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz", - "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", + "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", "dev": true, "requires": { - "@babel/plugin-transform-react-jsx": "^7.24.7" + "@babel/plugin-transform-react-jsx": "^7.25.9" } }, "@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz", - "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", + "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/preset-react": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz", - "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", + "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.24.7", - "@babel/plugin-transform-react-jsx-development": "^7.24.7", - "@babel/plugin-transform-react-pure-annotations": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-transform-react-display-name": "^7.25.9", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-react-jsx-development": "^7.25.9", + "@babel/plugin-transform-react-pure-annotations": "^7.25.9" } }, "@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, "requires": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "dependencies": { + "@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + } + } } }, "@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" + }, + "dependencies": { + "@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + } + } } }, "@babel/types": { @@ -25374,22 +25326,22 @@ "integrity": "sha512-9hIbusvAZjSGBJ42OyFC2AxsEph1LuKQahMWFcPGEIsOqIYHhMRkYA7wSUMhH7naydjNmllpcp3pJLOK4RhFaQ==" }, "@netlify/build": { - "version": "29.55.2", - "resolved": "https://registry.npmjs.org/@netlify/build/-/build-29.55.2.tgz", - "integrity": "sha512-sXWDIeKtRWc6S9+dG3lCdTSNSB9XfhNFk80kG600sI3ytkhF5rPk6ijJw4Y7drhruCrYFpsDwlPxGY9kBjshBw==", + "version": "29.56.1", + "resolved": "https://registry.npmjs.org/@netlify/build/-/build-29.56.1.tgz", + "integrity": "sha512-0/4GiVTL69AXeIly6ZXIi5g4qU2Oi9djCUcJO6xCZCDgft6TD90JXlsCQ5P/+oh0CFcNPpsy9DBvY8mm0fSFVw==", "requires": { "@bugsnag/js": "^7.0.0", "@netlify/blobs": "^7.4.0", "@netlify/cache-utils": "^5.1.6", - "@netlify/config": "^20.19.0", + "@netlify/config": "^20.19.1", "@netlify/edge-bundler": "12.2.3", "@netlify/framework-info": "^9.8.13", - "@netlify/functions-utils": "^5.2.91", + "@netlify/functions-utils": "^5.2.93", "@netlify/git-utils": "^5.1.1", "@netlify/opentelemetry-utils": "^1.2.1", "@netlify/plugins-list": "^6.80.0", "@netlify/run-utils": "^5.1.1", - "@netlify/zip-it-and-ship-it": "9.40.2", + "@netlify/zip-it-and-ship-it": "9.41.1", "@sindresorhus/slugify": "^2.0.0", "ansi-escapes": "^6.0.0", "chalk": "^5.0.0", @@ -25399,7 +25351,7 @@ "figures": "^5.0.0", "filter-obj": "^5.0.0", "got": "^12.0.0", - "hot-shots": "10.1.1", + "hot-shots": "10.2.1", "indent-string": "^5.0.0", "is-plain-obj": "^4.0.0", "js-yaml": "^4.0.0", @@ -25624,9 +25576,9 @@ } }, "@netlify/build-info": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@netlify/build-info/-/build-info-7.15.1.tgz", - "integrity": "sha512-4Z7JmoRA/biWnUhua2qsnSje9rXSEQJuX5v2+P8HIcsneNnKEnBqZry/Bct+EzL2+zyJ3DFRjHJB3n9tallg9g==", + "version": "7.15.2", + "resolved": "https://registry.npmjs.org/@netlify/build-info/-/build-info-7.15.2.tgz", + "integrity": "sha512-z249vRTIyeO1Coaa2UaaZJpTN2D9mE0HPvuQfVknJ+WqHdLjPHlmaKu2HyekfnA5zE8mVSkPAJsP9dip3kySSg==", "requires": { "@bugsnag/js": "^7.20.0", "@iarna/toml": "^2.2.5", @@ -25689,9 +25641,9 @@ } }, "yaml": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", - "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==" } } }, @@ -25735,9 +25687,9 @@ } }, "@netlify/config": { - "version": "20.19.0", - "resolved": "https://registry.npmjs.org/@netlify/config/-/config-20.19.0.tgz", - "integrity": "sha512-vkqTQ7jaudPSRME6ZzYml6qRWxIJXnUQ8csqOSx5Yv0ysj1zb2l+Ke3c5bc6Cttkg4ay2YLx4M0/7n6nT3KojQ==", + "version": "20.19.1", + "resolved": "https://registry.npmjs.org/@netlify/config/-/config-20.19.1.tgz", + "integrity": "sha512-GkN8IwHilIlusFuAW+DFjhtpghnaelNcHUoZwBDcJou8eyhIZYAj6B4STMyGUggIfMobYGM28kEY3gN4uUVq0g==", "requires": { "@iarna/toml": "^2.2.5", "chalk": "^5.0.0", @@ -26387,11 +26339,11 @@ } }, "@netlify/functions-utils": { - "version": "5.2.91", - "resolved": "https://registry.npmjs.org/@netlify/functions-utils/-/functions-utils-5.2.91.tgz", - "integrity": "sha512-PezRTuKzKzIbWu71tykioHS7W2Tk4wiGZqkkYzm92FeLRlubRERC9Dwv3Y5LFFNQsWXs701l7CVTojHrpSiA0w==", + "version": "5.2.93", + "resolved": "https://registry.npmjs.org/@netlify/functions-utils/-/functions-utils-5.2.93.tgz", + "integrity": "sha512-/b2JtJuB3KNN5AIfiH/tan/uM4i6nLj2QFGUL9oID58cMsd73iouRacKu4ct+gxUU78y+/6fiOeYRXbcthdltA==", "requires": { - "@netlify/zip-it-and-ship-it": "9.40.2", + "@netlify/zip-it-and-ship-it": "9.41.1", "cpy": "^9.0.0", "path-exists": "^5.0.0" }, @@ -26654,14 +26606,14 @@ } }, "@netlify/zip-it-and-ship-it": { - "version": "9.40.2", - "resolved": "https://registry.npmjs.org/@netlify/zip-it-and-ship-it/-/zip-it-and-ship-it-9.40.2.tgz", - "integrity": "sha512-CkAwLnqFqhV9hNJO8HxMjd+g2HYtGevjo4gP1P84Sf50HBFyBE2cavfXNaBa1TfNq5/92CixnXz4YptU1DIOOw==", + "version": "9.41.1", + "resolved": "https://registry.npmjs.org/@netlify/zip-it-and-ship-it/-/zip-it-and-ship-it-9.41.1.tgz", + "integrity": "sha512-EzXw1+4OJ4mCZUOqVPDQZNM8jf/563utFo1Ph6dYtSR21E1oYlgt6Oib1pyG/bFGufvdtrxw845/1MTCPvXzJA==", "requires": { "@babel/parser": "^7.22.5", "@babel/types": "7.25.6", "@netlify/binary-info": "^1.0.0", - "@netlify/serverless-functions-api": "^1.30.1", + "@netlify/serverless-functions-api": "^1.31.0", "@vercel/nft": "^0.27.1", "archiver": "^7.0.0", "common-path-prefix": "^3.0.0", @@ -26695,9 +26647,9 @@ }, "dependencies": { "@netlify/serverless-functions-api": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-1.30.1.tgz", - "integrity": "sha512-JkbaWFeydQdeDHz1mAy4rw+E3bl9YtbCgkntfTxq+IlNX/aIMv2/b1kZnQZcil4/sPoZGL831Dq6E374qRpU1A==", + "version": "1.31.0", + "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-1.31.0.tgz", + "integrity": "sha512-/ux3fefmw0yGmzRMOqhGrzbAuALtW8HY08bjE4yCk+y8CzB9r9mPd1ApSJe3KlYD+sqDvbQGrEXdifQ/LzUIDQ==", "requires": { "@netlify/node-cookies": "^0.1.0", "urlpattern-polyfill": "8.0.2" @@ -27457,9 +27409,9 @@ } }, "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true } } @@ -27516,9 +27468,9 @@ } }, "@types/lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==", + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", + "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==", "dev": true }, "@types/mdast": { @@ -27552,9 +27504,9 @@ } }, "@types/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", "dev": true, "requires": { "@types/node": "*", @@ -27645,9 +27597,9 @@ "dev": true }, "@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", "dev": true, "requires": { "@types/node": "*" @@ -27790,29 +27742,28 @@ } }, "@verdaccio/auth": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/auth/-/auth-8.0.0-next-8.3.tgz", - "integrity": "sha512-x7/gt4R41i5hat5dVT2WfwTeWolSKTo3k8t12ZBhnf+R+L/a79dQ7/sR8JIT6R9A+nkkFn+RiPspH75H7lprZg==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/auth/-/auth-8.0.0-next-8.4.tgz", + "integrity": "sha512-Bv+du+eIMK2/KU2wIjha2FHQrhBT5RuDOVi1nyRyjEyjmJrUt2RWU0Cb7ASxzQy61nkcJ3bs7kuu9dPHK/Z+jw==", "dev": true, "requires": { - "@verdaccio/config": "8.0.0-next-8.3", - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/config": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.4", "@verdaccio/loaders": "8.0.0-next-8.3", - "@verdaccio/logger": "8.0.0-next-8.3", "@verdaccio/signature": "8.0.0-next-8.1", - "@verdaccio/utils": "8.1.0-next-8.3", + "@verdaccio/utils": "8.1.0-next-8.4", "debug": "4.3.7", "lodash": "4.17.21", - "verdaccio-htpasswd": "13.0.0-next-8.3" + "verdaccio-htpasswd": "13.0.0-next-8.4" }, "dependencies": { "@verdaccio/utils": { - "version": "8.1.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", - "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "version": "8.1.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", + "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", "dev": true, "requires": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -27870,13 +27821,13 @@ } }, "@verdaccio/config": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-8.0.0-next-8.3.tgz", - "integrity": "sha512-bxlesiVi7A1GAHurq7RLFAFd67NTySSwtVMw7D1Ku2Q3v6kAF4TLqxKUrOaA14k0Zk4qyRu4OgXzbjDg0oARcQ==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-8.0.0-next-8.4.tgz", + "integrity": "sha512-9CTYYhaO4xrGbiYjLqRy8EMFPm0YL4a7P6ae8Zm4Dx85Dd6i8XqZ7tlU/+a6qf1g/qggYloolU8pcjaLWNDKAQ==", "dev": true, "requires": { - "@verdaccio/core": "8.0.0-next-8.3", - "@verdaccio/utils": "8.1.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/utils": "8.1.0-next-8.4", "debug": "4.3.7", "js-yaml": "4.1.0", "lodash": "4.17.21", @@ -27884,12 +27835,12 @@ }, "dependencies": { "@verdaccio/utils": { - "version": "8.1.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", - "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "version": "8.1.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", + "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", "dev": true, "requires": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -27916,9 +27867,9 @@ } }, "@verdaccio/core": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-8.0.0-next-8.3.tgz", - "integrity": "sha512-DPJmWANbpbtJN+cfz5CN4kfAl15F5JCv5qRAIQB9sOTNWjTw26cRpMYHFB9/buBQPuH3mWCjOwE6c+EVlvObLg==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-8.0.0-next-8.4.tgz", + "integrity": "sha512-TCaHwIpr97f4YQkU25E6pk1dbfWTQwYPos1tMb9Q7k6IapoxN0c1s+SyF5FBuMOIfJpTHoWJDo/z7QCouQC3lw==", "dev": true, "requires": { "ajv": "8.17.1", @@ -28039,22 +27990,22 @@ } }, "@verdaccio/logger": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/logger/-/logger-8.0.0-next-8.3.tgz", - "integrity": "sha512-rLjv/W9QfFHT80L1L/xXQwY3DaZ03NIq64/Bb4GHOudZgzl8C5Efe6u1q8Ti+jqXVAKCzEswMNISdq2XIQ+azQ==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/logger/-/logger-8.0.0-next-8.4.tgz", + "integrity": "sha512-zJIFpKYNR/api/mxj5HqJSlEMFh9J4sVKk+3QYlPmppW68beZLLzqwchb5+c/V559lnSrGy5HvDUEGLXvp6reA==", "dev": true, "requires": { - "@verdaccio/logger-commons": "8.0.0-next-8.3", + "@verdaccio/logger-commons": "8.0.0-next-8.4", "pino": "9.4.0" } }, "@verdaccio/logger-commons": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/logger-commons/-/logger-commons-8.0.0-next-8.3.tgz", - "integrity": "sha512-eMG0UDh66JcPX8ez57HCpsZ0FE9G0pCZ51Xei1MeCFVgNLkzrEcnwOcEGZhd3Tew79A4wGgjFFFywkrRIIomwg==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/logger-commons/-/logger-commons-8.0.0-next-8.4.tgz", + "integrity": "sha512-neDbq5IIRoidFT4Rv3zH9YydICDCJEybb06BzCGVOzlhZ7F+fBzJH1qlBhAEISfbONugDgfuUQ2jbRCKEkHezQ==", "dev": true, "requires": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "@verdaccio/logger-prettify": "8.0.0-next-8.1", "colorette": "2.0.20", "debug": "4.3.7" @@ -28074,17 +28025,17 @@ } }, "@verdaccio/middleware": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/middleware/-/middleware-8.0.0-next-8.3.tgz", - "integrity": "sha512-yUe4BGA2/o4GnFuKdquU53kI07xsLMKiMvZfj+M0ZFnFCshCqKmXOoSR8hbzQReUm/OZQSLOppliJn68xmXEVQ==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/middleware/-/middleware-8.0.0-next-8.4.tgz", + "integrity": "sha512-tzpfSpeLKUeyTsQ+fvUsokgdh1NrjDJX/oz2ya8wTYSInKAt1Ld9MRzRVSHJwIQc7wfg46zSjpcKZVLA/YkJ6w==", "dev": true, "requires": { - "@verdaccio/config": "8.0.0-next-8.3", - "@verdaccio/core": "8.0.0-next-8.3", - "@verdaccio/url": "13.0.0-next-8.3", - "@verdaccio/utils": "8.1.0-next-8.3", + "@verdaccio/config": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/url": "13.0.0-next-8.4", + "@verdaccio/utils": "8.1.0-next-8.4", "debug": "4.3.7", - "express": "4.21.0", + "express": "4.21.1", "express-rate-limit": "5.5.1", "lodash": "4.17.21", "lru-cache": "7.18.3", @@ -28092,12 +28043,12 @@ }, "dependencies": { "@verdaccio/utils": { - "version": "8.1.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", - "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "version": "8.1.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", + "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", "dev": true, "requires": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -28112,81 +28063,6 @@ "balanced-match": "^1.0.0" } }, - "cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "dev": true, - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, "lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", @@ -28207,25 +28083,13 @@ "requires": { "brace-expansion": "^2.0.1" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true } } }, "@verdaccio/search-indexer": { - "version": "8.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/search-indexer/-/search-indexer-8.0.0-next-8.1.tgz", - "integrity": "sha512-Mwwg2o9GicZd6uiCbjBk6xZiWAH/O/2NbEkicPZINFIoJKy1NUihS4RexdDjcsxKEBEggGZXCkzHjzhfaZv1Gg==", + "version": "8.0.0-next-8.2", + "resolved": "https://registry.npmjs.org/@verdaccio/search-indexer/-/search-indexer-8.0.0-next-8.2.tgz", + "integrity": "sha512-sWliVN5BkAGbZ3e/GD0CsZMfPJdRMRuN0tEKQFsvEJifxToq5UkfCw6vKaVvhezsTWqb+Rp5y+2d4n5BDOA49w==", "dev": true }, "@verdaccio/signature": { @@ -28245,14 +28109,14 @@ "dev": true }, "@verdaccio/tarball": { - "version": "13.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-13.0.0-next-8.3.tgz", - "integrity": "sha512-jJatpGgiKLmTqyW4WlRpIkldd26rHDj5WTugWqa2Wxa1hVS0b6sZKT/9fEffOvjjAJv69Ued4nH1YCcB2hlhfA==", + "version": "13.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-13.0.0-next-8.4.tgz", + "integrity": "sha512-zizQwACK+P9sHtArbuW5MJluRpc3lC6bilGTFNc0TLkHbwL73F8wxkKr5VLzWV7H54+sVKMDs1lCnaoHa0ygmw==", "dev": true, "requires": { - "@verdaccio/core": "8.0.0-next-8.3", - "@verdaccio/url": "13.0.0-next-8.3", - "@verdaccio/utils": "8.1.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/url": "13.0.0-next-8.4", + "@verdaccio/utils": "8.1.0-next-8.4", "debug": "4.3.7", "gunzip-maybe": "^1.4.2", "lodash": "4.17.21", @@ -28260,12 +28124,12 @@ }, "dependencies": { "@verdaccio/utils": { - "version": "8.1.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.3.tgz", - "integrity": "sha512-TR9S+RYpmOERqiqoXwtBFmWqyyByTUFSkZgfDqKbsBaDbYYUls738NKFQHpg/s6i2E3r46mHcWI+jue/EnOoSg==", + "version": "8.1.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-8.1.0-next-8.4.tgz", + "integrity": "sha512-mAEBWV5zsjtC4e/hfj1Q/eYtMlML5wxedk7mqqmvAydjw+ycSH/D/ksU+B10h4STX2NcBlcLtgLl7OI/wFzrgA==", "dev": true, "requires": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "lodash": "4.17.21", "minimatch": "7.4.6", "semver": "7.6.3" @@ -28292,18 +28156,18 @@ } }, "@verdaccio/ui-theme": { - "version": "8.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-8.0.0-next-8.3.tgz", - "integrity": "sha512-3A5v8bkvK5Bm+ERsZLYPv4vwo49dwfSy5a3WcAQgWde/oc6jytl/5XOZOaKX33mEiLj313k02j9ArhKuY1JjgA==", + "version": "8.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-8.0.0-next-8.4.tgz", + "integrity": "sha512-j3STxUuIgvn058LqfXWv+SwRi1fQ7HapMSfVKXAhi09+f4zlD2mtvKLth0WbuzU1NqJPTGLAP9ueBf1210C1Hw==", "dev": true }, "@verdaccio/url": { - "version": "13.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-13.0.0-next-8.3.tgz", - "integrity": "sha512-77BkY3j1d1ZPpmBodK2QlRwNP9tn/IneVry4RHX9j+1xNj4clvpn5rObFnVRGeYf2GPZrYZvIdzVP3BjXl0nkA==", + "version": "13.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-13.0.0-next-8.4.tgz", + "integrity": "sha512-Xo+9DUcwYTBV6d0n4vjLAN2k92J33XM/9JNltWM6140oI8lz+VJKiajtejG/hRBi82RioRdWJ0RZDDY6FsbS3Q==", "dev": true, "requires": { - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/core": "8.0.0-next-8.4", "debug": "4.3.7", "lodash": "4.17.21", "validator": "13.12.0" @@ -29805,9 +29669,9 @@ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, "ci-info": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", - "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==" }, "citty": { "version": "0.1.6", @@ -30194,26 +30058,20 @@ } }, "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", "dev": true, "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", + "negotiator": "~0.6.4", "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "dev": true - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -30228,6 +30086,18 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true + }, + "negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true } } }, @@ -30599,9 +30469,9 @@ } }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -33453,9 +33323,9 @@ } }, "hot-shots": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/hot-shots/-/hot-shots-10.1.1.tgz", - "integrity": "sha512-KTsH9hb+YZHH0IIRf22y0X8mPw8j521W5xRAUeaUlGNBDsf44ixE7ZeyXbUHd/nQ1n04UEhi2ja05/QVOS/CgQ==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/hot-shots/-/hot-shots-10.2.1.tgz", + "integrity": "sha512-tmjcyZkG/qADhcdC7UjAp8D7v7W2DOYFgaZ48fYMuayMQmVVUg8fntKmrjes/b40ef6yZ+qt1lB8kuEDfLC4zw==", "requires": { "unix-dgram": "2.x" } @@ -34447,9 +34317,9 @@ "dev": true }, "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true }, "json-buffer": { @@ -35646,9 +35516,9 @@ } }, "mock-fs": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.2.0.tgz", - "integrity": "sha512-2dF2R6YMSZbpip1V1WHKGLNjr/k48uQClqMVb5H3MOvwc9qhYis3/IWbj02qIg/Y8MDXKFF4c5v0rxx2o6xTZw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.4.1.tgz", + "integrity": "sha512-sz/Q8K1gXXXHR+qr0GZg2ysxCRr323kuN10O7CtQjraJsFDJ4SJ+0I5MzALz7aRp9lHk8Cc/YdsT95h9Ka1aFw==", "dev": true }, "module-definition": { @@ -35871,9 +35741,9 @@ } }, "nock": { - "version": "13.5.4", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.4.tgz", - "integrity": "sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", "dev": true, "requires": { "debug": "^4.1.0", @@ -36040,9 +35910,9 @@ } }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -36331,9 +36201,9 @@ } }, "ora": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.1.0.tgz", - "integrity": "sha512-GQEkNkH/GHOhPFXcqZs3IDahXEQcQxsSjEkK4KvEEST4t7eNzoMjxTzef+EZ+JluDEV+Raoi3WQ2CflnRdSVnQ==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.1.1.tgz", + "integrity": "sha512-YWielGi1XzG1UTvOaCFaNgEnuhZVMSHYkW/FQ7UX8O26PtlpdM84c0f7wLPlkvx2RfiQmnzd61d/MGxmpQeJPw==", "requires": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", @@ -36498,9 +36368,9 @@ } }, "p-timeout": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz", - "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==" + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.3.tgz", + "integrity": "sha512-UJUyfKbwvr/uZSV6btANfb+0t/mOhKV/KXcCUTp8FcQI+v/0d+wXqH4htrW0E4rR6WiEO/EPvUFiV9D5OI4vlw==" }, "p-try": { "version": "1.0.0", @@ -39381,28 +39251,28 @@ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, "verdaccio": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-6.0.1.tgz", - "integrity": "sha512-fGP5V18Pz3yIDZNZjQrBqRnMr4sDn8fPO7eoZTIX2D7MvqoGELRG88Townq8PhYU5KAPZ2c2OnNRr8SKReO3Ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-6.0.2.tgz", + "integrity": "sha512-XthgJlF1hGW+GR/apRLZ7DQw26XpLI+xjMGb7dhJKxI4Pz2gSiEY1RXP9T9I/rlIBr9Zx6rYOgRk7A9Aeq/kpg==", "dev": true, "requires": { "@cypress/request": "3.0.5", - "@verdaccio/auth": "8.0.0-next-8.3", - "@verdaccio/config": "8.0.0-next-8.3", - "@verdaccio/core": "8.0.0-next-8.3", + "@verdaccio/auth": "8.0.0-next-8.4", + "@verdaccio/config": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.4", "@verdaccio/local-storage-legacy": "11.0.2", - "@verdaccio/logger": "8.0.0-next-8.3", - "@verdaccio/middleware": "8.0.0-next-8.3", - "@verdaccio/search-indexer": "8.0.0-next-8.1", + "@verdaccio/logger": "8.0.0-next-8.4", + "@verdaccio/middleware": "8.0.0-next-8.4", + "@verdaccio/search-indexer": "8.0.0-next-8.2", "@verdaccio/signature": "8.0.0-next-8.1", "@verdaccio/streams": "10.2.1", - "@verdaccio/tarball": "13.0.0-next-8.3", - "@verdaccio/ui-theme": "8.0.0-next-8.3", - "@verdaccio/url": "13.0.0-next-8.3", + "@verdaccio/tarball": "13.0.0-next-8.4", + "@verdaccio/ui-theme": "8.0.0-next-8.4", + "@verdaccio/url": "13.0.0-next-8.4", "@verdaccio/utils": "7.0.1-next-8.1", "async": "3.2.6", "clipanion": "4.0.0-rc.4", - "compression": "1.7.4", + "compression": "1.7.5", "cors": "2.8.5", "debug": "4.3.7", "envinfo": "7.14.0", @@ -39421,8 +39291,8 @@ "pkginfo": "0.4.1", "semver": "7.6.3", "validator": "13.12.0", - "verdaccio-audit": "13.0.0-next-8.3", - "verdaccio-htpasswd": "13.0.0-next-8.3" + "verdaccio-audit": "13.0.0-next-8.4", + "verdaccio-htpasswd": "13.0.0-next-8.4" }, "dependencies": { "async": { @@ -39452,93 +39322,18 @@ } }, "verdaccio-audit": { - "version": "13.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-13.0.0-next-8.3.tgz", - "integrity": "sha512-/mgRfsg+RENtUggcf0xnfPKNJJqidyKING3nyHgS3vABE6CBe4/fWQKs67X4mfCFiIVLf0PiOTFGT8tmwSZubA==", + "version": "13.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-13.0.0-next-8.4.tgz", + "integrity": "sha512-T4yi/46fLngllx5mvFtXsGcW3MxGZZ9IkHYPK1OQw9+Xj9aOuMec2eFdztTRo9SgqZfgblGSY1ESZYy19sQLvw==", "dev": true, "requires": { - "@verdaccio/config": "8.0.0-next-8.3", - "@verdaccio/core": "8.0.0-next-8.3", - "express": "4.21.0", + "@verdaccio/config": "8.0.0-next-8.4", + "@verdaccio/core": "8.0.0-next-8.4", + "express": "4.21.1", "https-proxy-agent": "5.0.1", "node-fetch": "cjs" }, "dependencies": { - "cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "dev": true, - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, "https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -39548,29 +39343,17 @@ "agent-base": "6", "debug": "4" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true } } }, "verdaccio-htpasswd": { - "version": "13.0.0-next-8.3", - "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-13.0.0-next-8.3.tgz", - "integrity": "sha512-Nl2rEEyGHJQ/1/93BE9TxBMRN4tKF/51VYTb+hWDQFhDEDAR20rcvJ4ND0jOIIZljI7Lg/WrCPBh90u5IyPJ5Q==", + "version": "13.0.0-next-8.4", + "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-13.0.0-next-8.4.tgz", + "integrity": "sha512-w0knjKz8SdBGSv0Kt61LoUOCYBZ2/iig3bVbGFWTj4MwCUG6eNewMoQ6nbrk+kyHNFVq75IzT1eIhXDEysx15Q==", "dev": true, "requires": { - "@verdaccio/core": "8.0.0-next-8.3", - "@verdaccio/file-locking": "13.0.0-next-8.1", + "@verdaccio/core": "8.0.0-next-8.4", + "@verdaccio/file-locking": "13.0.0-next-8.2", "apache-md5": "1.1.8", "bcryptjs": "2.4.3", "core-js": "3.37.1", @@ -39580,9 +39363,9 @@ }, "dependencies": { "@verdaccio/file-locking": { - "version": "13.0.0-next-8.1", - "resolved": "https://registry.npmjs.org/@verdaccio/file-locking/-/file-locking-13.0.0-next-8.1.tgz", - "integrity": "sha512-9PhfRKXsWaWJkON/2/jdG5/N+9Kk4UINvbMGuJz0A/PbzIYfVrBhry7fcnjn6hFKxVPTbSOSSztRzLF30nmBFg==", + "version": "13.0.0-next-8.2", + "resolved": "https://registry.npmjs.org/@verdaccio/file-locking/-/file-locking-13.0.0-next-8.2.tgz", + "integrity": "sha512-TcHgN3I/N28WBSvtukpGrJhBljl4jyIXq0vEv94vXAG6nUE3saK+vtgo8PfYA3Ueo88v/1zyAbiZM4uxwojCmQ==", "dev": true, "requires": { "lockfile": "1.0.4" @@ -40275,9 +40058,9 @@ } }, "ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "requires": {} }, "xdg-basedir": { diff --git a/package.json b/package.json index 01fe12195f6..214156d0761 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "netlify-cli", "description": "Netlify command line tool", - "version": "17.37.0", + "version": "17.37.2", "author": "Netlify Inc.", "type": "module", "engines": { @@ -74,13 +74,13 @@ "@bugsnag/js": "7.25.0", "@fastify/static": "7.0.4", "@netlify/blobs": "8.1.0", - "@netlify/build": "29.55.2", - "@netlify/build-info": "7.15.1", - "@netlify/config": "20.19.0", + "@netlify/build": "29.56.1", + "@netlify/build-info": "7.15.2", + "@netlify/config": "20.19.1", "@netlify/edge-bundler": "12.2.3", "@netlify/edge-functions": "2.9.0", "@netlify/local-functions-proxy": "1.1.1", - "@netlify/zip-it-and-ship-it": "9.40.2", + "@netlify/zip-it-and-ship-it": "9.41.1", "@octokit/rest": "20.1.1", "@opentelemetry/api": "1.8.0", "ansi-escapes": "7.0.0", @@ -92,7 +92,7 @@ "boxen": "7.1.1", "chalk": "5.3.0", "chokidar": "3.6.0", - "ci-info": "4.0.0", + "ci-info": "4.1.0", "clean-deep": "3.4.0", "commander": "10.0.1", "comment-json": "4.2.5", @@ -153,7 +153,7 @@ "netlify-redirector": "0.5.0", "node-fetch": "3.3.2", "node-version-alias": "3.4.1", - "ora": "8.1.0", + "ora": "8.1.1", "p-filter": "4.1.0", "p-map": "7.0.2", "p-wait-for": "5.0.2", @@ -182,25 +182,25 @@ "uuid": "9.0.1", "wait-port": "1.1.0", "write-file-atomic": "5.0.1", - "ws": "8.17.1", + "ws": "8.18.0", "zod": "3.23.8" }, "devDependencies": { - "@babel/preset-react": "7.24.7", + "@babel/preset-react": "7.25.9", "@netlify/eslint-config-node": "7.0.1", "@netlify/functions": "2.8.2", "@sindresorhus/slugify": "2.2.1", "@types/fs-extra": "11.0.4", "@types/inquirer": "9.0.7", "@types/jsonwebtoken": "9.0.7", - "@types/lodash": "4.17.10", + "@types/lodash": "4.17.13", "@types/node": "20.14.8", - "@types/node-fetch": "2.6.11", + "@types/node-fetch": "2.6.12", "@types/parse-gitignore": "1.0.2", "@types/prettyjson": "0.0.33", "@types/semver": "7.5.8", "@types/uuid": "9.0.8", - "@types/ws": "8.5.10", + "@types/ws": "8.5.13", "@vitest/coverage-v8": "1.6.0", "c8": "9.1.0", "cheerio": "1.0.0", @@ -213,16 +213,16 @@ "husky": "8.0.3", "ini": "4.1.3", "is-ci": "3.0.1", - "mock-fs": "5.2.0", - "nock": "13.5.4", - "p-timeout": "6.1.2", + "mock-fs": "5.4.1", + "nock": "13.5.6", + "p-timeout": "6.1.3", "serialize-javascript": "6.0.2", "sinon": "18.0.1", "strip-ansi": "7.1.0", "temp-dir": "3.0.0", "tree-kill": "1.2.2", "typescript": "5.1.6", - "verdaccio": "6.0.1", + "verdaccio": "6.0.2", "vitest": "1.6.0" }, "ava": { diff --git a/site/package-lock.json b/site/package-lock.json index a1f205aa4dd..b67224b9012 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -4524,9 +4524,9 @@ "integrity": "sha512-I97q0MmRAAqj53+a8vZsDkEXBZki+ehYAOPzwtQzALip52aEp2+BJqHFtTlsfjoqVZYwPpHC8wM6MbsSZQ/Eqw==" }, "node_modules/elliptic": { - "version": "6.5.7", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", - "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -17917,9 +17917,9 @@ "integrity": "sha512-I97q0MmRAAqj53+a8vZsDkEXBZki+ehYAOPzwtQzALip52aEp2+BJqHFtTlsfjoqVZYwPpHC8wM6MbsSZQ/Eqw==" }, "elliptic": { - "version": "6.5.7", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", - "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", "requires": { "bn.js": "^4.11.9", "brorand": "^1.1.0", diff --git a/src/commands/addons/addons-config.ts b/src/commands/addons/addons-config.ts index 9d23fe36380..2d2698b0224 100644 --- a/src/commands/addons/addons-config.ts +++ b/src/commands/addons/addons-config.ts @@ -8,7 +8,7 @@ import { ADDON_VALIDATION, prepareAddonCommand } from '../../utils/addons/prepar import generatePrompts from '../../utils/addons/prompts.js' import { renderConfigValues } from '../../utils/addons/render.js' import { missingConfigValues, requiredConfigValues, updateConfigValues } from '../../utils/addons/validation.js' -import { chalk, error, log } from '../../utils/command-helpers.js' +import { chalk, error, log, APIError } from '../../utils/command-helpers.js' import { parseRawFlags } from '../../utils/parse-raw-flags.js' import BaseCommand from '../base-command.js' @@ -35,8 +35,7 @@ const update = async function ({ addonName, api, currentConfig, instanceId, newC }) log(`Add-on "${addonName}" successfully updated`) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(error_.message) + error((error_ as APIError).message) } } diff --git a/src/commands/addons/addons-create.ts b/src/commands/addons/addons-create.ts index 1fa244725d3..4453f87edba 100644 --- a/src/commands/addons/addons-create.ts +++ b/src/commands/addons/addons-create.ts @@ -6,7 +6,7 @@ import { ADDON_VALIDATION, prepareAddonCommand } from '../../utils/addons/prepar import generatePrompts from '../../utils/addons/prompts.js' import { renderConfigValues, renderMissingValues } from '../../utils/addons/render.js' import { missingConfigValues, requiredConfigValues, updateConfigValues } from '../../utils/addons/validation.js' -import { chalk, error, log } from '../../utils/command-helpers.js' +import { chalk, error, log, APIError } from '../../utils/command-helpers.js' import { parseRawFlags } from '../../utils/parse-raw-flags.js' import BaseCommand from '../base-command.js' // @ts-expect-error TS(7031) FIXME: Binding element 'addonName' implicitly has an 'any... Remove this comment to see the full error message @@ -23,8 +23,7 @@ const createAddon = async ({ addonName, api, config, siteData, siteId }) => { log(`${response.config.message}`) } } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(error_.message) + error((error_ as APIError).message) } } diff --git a/src/commands/addons/addons-delete.ts b/src/commands/addons/addons-delete.ts index 32f9e46eb61..dee08cf0d7a 100644 --- a/src/commands/addons/addons-delete.ts +++ b/src/commands/addons/addons-delete.ts @@ -2,7 +2,7 @@ import { OptionValues } from 'commander' import inquirer from 'inquirer' import { ADDON_VALIDATION, prepareAddonCommand } from '../../utils/addons/prepare.js' -import { error, exit, log } from '../../utils/command-helpers.js' +import { error, exit, log, APIError } from '../../utils/command-helpers.js' import BaseCommand from '../base-command.js' export const addonsDelete = async (addonName: string, options: OptionValues, command: BaseCommand) => { @@ -31,7 +31,6 @@ export const addonsDelete = async (addonName: string, options: OptionValues, com }) log(`Addon "${addonName}" deleted`) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(error_.message) + error((error_ as APIError).message) } } diff --git a/src/commands/addons/addons.ts b/src/commands/addons/addons.ts index 8795f4484f3..f73571409c3 100644 --- a/src/commands/addons/addons.ts +++ b/src/commands/addons/addons.ts @@ -51,7 +51,6 @@ Add-ons are a way to extend the functionality of your Netlify site`, .description( `Remove an add-on extension to your site\nAdd-ons are a way to extend the functionality of your Netlify site`, ) - .option('-f, --force', 'delete without prompting (useful for CI)') .action(async (addonName: string, options: OptionValues, command: BaseCommand) => { const { addonsDelete } = await import('./addons-delete.js') await addonsDelete(addonName, options, command) diff --git a/src/commands/api/api.ts b/src/commands/api/api.ts index 10caa2d127c..b594b1646dd 100644 --- a/src/commands/api/api.ts +++ b/src/commands/api/api.ts @@ -40,7 +40,6 @@ export const apiCommand = async (apiMethod: string, options: OptionValues, comma const apiResponse = await api[apiMethod](payload) logJson(apiResponse) } catch (error_) { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } diff --git a/src/commands/base-command.ts b/src/commands/base-command.ts index 592f464b755..129197271fe 100644 --- a/src/commands/base-command.ts +++ b/src/commands/base-command.ts @@ -1,5 +1,3 @@ -import { isCI } from 'ci-info' - import { existsSync } from 'fs' import { join, relative, resolve } from 'path' import process from 'process' @@ -8,6 +6,7 @@ import { format } from 'util' import { DefaultLogger, Project } from '@netlify/build-info' import { NodeFS, NoopLogger } from '@netlify/build-info/node' import { resolveConfig } from '@netlify/config' +import { isCI } from 'ci-info' import { Command, Help, Option } from 'commander' // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'debu... Remove this comment to see the full error message import debug from 'debug' @@ -187,6 +186,7 @@ export default class BaseCommand extends Command { createCommand(name: string): BaseCommand { const base = new BaseCommand(name) // If --silent or --json flag passed disable logger + // .addOption(new Option('--force', 'Force command to run. Bypasses prompts for certain destructive commands.')) .addOption(new Option('--json', 'Output return values as JSON').hideHelp(true)) .addOption(new Option('--silent', 'Silence CLI output').hideHelp(true)) .addOption(new Option('--cwd ').hideHelp(true)) @@ -725,7 +725,6 @@ export default class BaseCommand extends Command { // the option to say that we don't need API data.) if (isUserError && !config.offline && config.token) { if (flags.debug) { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_, { exit: false }) warn('Failed to resolve config, falling back to offline resolution') } diff --git a/src/commands/blobs/blobs-delete.ts b/src/commands/blobs/blobs-delete.ts index e26ffa813c3..4a8bd52cadd 100644 --- a/src/commands/blobs/blobs-delete.ts +++ b/src/commands/blobs/blobs-delete.ts @@ -1,12 +1,15 @@ import { getStore } from '@netlify/blobs' -import { chalk, error as printError } from '../../utils/command-helpers.js' +import { chalk, error as printError, log } from '../../utils/command-helpers.js' +import { promptBlobDelete } from '../../utils/prompts/blob-delete-prompts.js' /** * The blobs:delete command */ export const blobsDelete = async (storeName: string, key: string, _options: Record, command: any) => { const { api, siteInfo } = command.netlify + const { force } = _options + const store = getStore({ apiURL: `${api.scheme}://${api.host}`, name: storeName, @@ -14,8 +17,14 @@ export const blobsDelete = async (storeName: string, key: string, _options: Reco token: api.accessToken ?? '', }) + if (force === undefined) { + await promptBlobDelete(key, storeName) + } + try { await store.delete(key) + + log(`${chalk.greenBright('Success')}: Blob ${chalk.yellow(key)} deleted from store ${chalk.yellow(storeName)}`) } catch { return printError(`Could not delete blob ${chalk.yellow(key)} from store ${chalk.yellow(storeName)}`) } diff --git a/src/commands/blobs/blobs-set.ts b/src/commands/blobs/blobs-set.ts index e4e76bdf53b..ca481f7fb7d 100644 --- a/src/commands/blobs/blobs-set.ts +++ b/src/commands/blobs/blobs-set.ts @@ -4,11 +4,13 @@ import { resolve } from 'path' import { getStore } from '@netlify/blobs' import { OptionValues } from 'commander' -import { chalk, error as printError, isNodeError } from '../../utils/command-helpers.js' +import { chalk, error as printError, isNodeError, log } from '../../utils/command-helpers.js' +import { promptBlobSetOverwrite } from '../../utils/prompts/blob-set-prompt.js' import BaseCommand from '../base-command.js' interface Options extends OptionValues { input?: string + force?: string | boolean } export const blobsSet = async ( @@ -19,19 +21,17 @@ export const blobsSet = async ( command: BaseCommand, ) => { const { api, siteInfo } = command.netlify - const { input } = options + const { force, input } = options const store = getStore({ apiURL: `${api.scheme}://${api.host}`, name: storeName, siteID: siteInfo.id ?? '', token: api.accessToken ?? '', }) - let value = valueParts.join(' ') if (input) { const inputPath = resolve(input) - try { value = await fs.readFile(inputPath, 'utf8') } catch (error) { @@ -57,8 +57,17 @@ export const blobsSet = async ( ) } + if (force === undefined) { + const existingValue = await store.get(key) + + if (existingValue) { + await promptBlobSetOverwrite(key, storeName) + } + } + try { await store.set(key, value) + log(`${chalk.greenBright('Success')}: Blob ${chalk.yellow(key)} set in store ${chalk.yellow(storeName)}`) } catch { return printError(`Could not set blob ${chalk.yellow(key)} in store ${chalk.yellow(storeName)}`) } diff --git a/src/commands/build/build.ts b/src/commands/build/build.ts index 7151a67e226..0f2a30fbff1 100644 --- a/src/commands/build/build.ts +++ b/src/commands/build/build.ts @@ -27,7 +27,6 @@ export const build = async (options: OptionValues, command: BaseCommand) => { const { cachedConfig, siteInfo } = command.netlify command.setAnalyticsPayload({ dry: options.dry }) // Retrieve Netlify Build options - // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. const [token] = await getToken() const settings = await detectFrameworkSettings(command, 'build') diff --git a/src/commands/completion/completion.ts b/src/commands/completion/completion.ts index 049660234e8..9c69fcb32c8 100644 --- a/src/commands/completion/completion.ts +++ b/src/commands/completion/completion.ts @@ -1,12 +1,22 @@ +import fs from 'fs' +import { homedir } from 'os' import { dirname, join } from 'path' import { fileURLToPath } from 'url' +import inquirer from 'inquirer' import { OptionValues } from 'commander' // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'tabt... Remove this comment to see the full error message import { install, uninstall } from 'tabtab' import { generateAutocompletion } from '../../lib/completion/index.js' -import { error } from '../../utils/command-helpers.js' +import { + error, + log, + chalk, + checkFileForLine, + TABTAB_CONFIG_LINE, + AUTOLOAD_COMPINIT, +} from '../../utils/command-helpers.js' import BaseCommand from '../base-command.js' const completer = join(dirname(fileURLToPath(import.meta.url)), '../../lib/completion/script.js') @@ -20,13 +30,47 @@ export const completionGenerate = async (options: OptionValues, command: BaseCom } generateAutocompletion(parent) - await install({ name: parent.name(), completer, }) + const zshConfigFilepath = join(process.env.HOME || homedir(), '.zshrc') + + if ( + fs.existsSync(zshConfigFilepath) && + checkFileForLine(zshConfigFilepath, TABTAB_CONFIG_LINE) && + !checkFileForLine(zshConfigFilepath, AUTOLOAD_COMPINIT) + ) { + log(`To enable Tabtab autocompletion with zsh, the following line may need to be added to your ~/.zshrc:`) + log(chalk.bold.cyan(`\n${AUTOLOAD_COMPINIT}\n`)) + const { compinitAdded } = await inquirer.prompt([ + { + type: 'confirm', + name: 'compinitAdded', + message: `Would you like to add it?`, + default: true, + }, + ]) + if (compinitAdded) { + await fs.readFile(zshConfigFilepath, 'utf8', (err, data) => { + const updatedZshFile = AUTOLOAD_COMPINIT + '\n' + data - console.log(`Completion for ${parent.name()} successful installed!`) + fs.writeFileSync(zshConfigFilepath, updatedZshFile, 'utf8') + }) + + log('Successfully added compinit line to .zshrc') + } + } + + log(`Completion for ${parent.name()} successfully installed!`) + + if (process.platform !== 'win32') { + log("\nTo ensure proper functionality, you'll need to set appropriate file permissions.") + log(chalk.bold('Add executable permissions by running the following command:')) + log(chalk.bold.cyan(`\nchmod +x ${completer}\n`)) + } else { + log(`\nTo ensure proper functionality, you may need to set appropriate file permissions to ${completer}.`) + } } export const completionUninstall = async (options: OptionValues, command: BaseCommand) => { diff --git a/src/commands/deploy/deploy.ts b/src/commands/deploy/deploy.ts index 2624362593f..a2ad454b014 100644 --- a/src/commands/deploy/deploy.ts +++ b/src/commands/deploy/deploy.ts @@ -30,6 +30,7 @@ import { log, logJson, warn, + APIError, } from '../../utils/command-helpers.js' import { DEFAULT_DEPLOY_TIMEOUT } from '../../utils/deploy/constants.js' import { deploySite } from '../../utils/deploy/deploy-site.js' @@ -58,12 +59,10 @@ const triggerDeploy = async ({ api, options, siteData, siteId }) => { ) } } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if ((error_ as APIError).status === 404) { error('Site not found. Please rerun "netlify link" and make sure that your site has CI configured.') } else { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(error_.message) + error((error_ as APIError).message) } } } @@ -372,8 +371,9 @@ const uploadDeployBlobs = async ({ phase: 'start', }) - const [token] = await getToken(false) + const [token] = await getToken() + const blobsToken = token || undefined const { success } = await runCoreSteps(['blobs_upload'], { ...options, quiet: silent, @@ -381,7 +381,7 @@ const uploadDeployBlobs = async ({ packagePath, deployId, siteId, - token, + token: blobsToken, }) if (!success) { @@ -566,7 +566,6 @@ const handleBuild = async ({ cachedConfig, currentDir, defaultConfig, deployHand if (!options.build) { return {} } - // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. const [token] = await getToken() const resolvedOptions = await getBuildOptions({ cachedConfig, diff --git a/src/commands/env/env-clone.ts b/src/commands/env/env-clone.ts index 1d06a515eed..5d08117d980 100644 --- a/src/commands/env/env-clone.ts +++ b/src/commands/env/env-clone.ts @@ -1,6 +1,7 @@ import { OptionValues } from 'commander' import { chalk, log, error as logError } from '../../utils/command-helpers.js' +import { promptEnvCloneOverwrite } from '../../utils/prompts/env-clone-prompt.js' import BaseCommand from '../base-command.js' // @ts-expect-error TS(7006) FIXME: Parameter 'api' implicitly has an 'any' type. @@ -18,7 +19,7 @@ const safeGetSite = async (api, siteId) => { * @returns {Promise} */ // @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message -const cloneEnvVars = async ({ api, siteFrom, siteTo }): Promise => { +const cloneEnvVars = async ({ api, force, siteFrom, siteTo }): Promise => { const [envelopeFrom, envelopeTo] = await Promise.all([ api.getEnvVars({ accountId: siteFrom.account_slug, siteId: siteFrom.id }), api.getEnvVars({ accountId: siteTo.account_slug, siteId: siteTo.id }), @@ -36,6 +37,10 @@ const cloneEnvVars = async ({ api, siteFrom, siteTo }): Promise => { const siteId = siteTo.id // @ts-expect-error TS(7031) FIXME: Binding element 'key' implicitly has an 'any' type... Remove this comment to see the full error message const envVarsToDelete = envelopeTo.filter(({ key }) => keysFrom.includes(key)) + + if (envVarsToDelete.length !== 0 && Boolean(force) === false) { + await promptEnvCloneOverwrite(siteTo.id, envVarsToDelete) + } // delete marked env vars in parallel // @ts-expect-error TS(7031) FIXME: Binding element 'key' implicitly has an 'any' type... Remove this comment to see the full error message await Promise.all(envVarsToDelete.map(({ key }) => api.deleteEnvVar({ accountId, siteId, key }))) @@ -47,12 +52,12 @@ const cloneEnvVars = async ({ api, siteFrom, siteTo }): Promise => { // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. throw error.json ? error.json.msg : error } - return true } export const envClone = async (options: OptionValues, command: BaseCommand) => { const { api, site } = command.netlify + const { force } = options if (!site.id && !options.from) { log( @@ -61,8 +66,16 @@ export const envClone = async (options: OptionValues, command: BaseCommand) => { return false } + const sourceId = options.from || site.id + + if (!sourceId) { + log( + 'Please include the source site Id as the `--from` option, or run `netlify link` to link this folder to a Netlify site', + ) + } + const siteId = { - from: options.from || site.id, + from: sourceId, to: options.to, } @@ -81,7 +94,7 @@ export const envClone = async (options: OptionValues, command: BaseCommand) => { return false } - const success = await cloneEnvVars({ api, siteFrom, siteTo }) + const success = await cloneEnvVars({ api, siteFrom, siteTo, force }) if (!success) { return false diff --git a/src/commands/env/env-set.ts b/src/commands/env/env-set.ts index daa45a2ca84..8818d71861f 100644 --- a/src/commands/env/env-set.ts +++ b/src/commands/env/env-set.ts @@ -2,6 +2,7 @@ import { OptionValues } from 'commander' import { chalk, error, log, logJson } from '../../utils/command-helpers.js' import { AVAILABLE_CONTEXTS, AVAILABLE_SCOPES, translateFromEnvelopeToMongo } from '../../utils/env/index.js' +import { promptOverwriteEnvVariable } from '../../utils/prompts/env-set-prompts.js' import BaseCommand from '../base-command.js' /** @@ -9,7 +10,7 @@ import BaseCommand from '../base-command.js' * @returns {Promise} */ // @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message -const setInEnvelope = async ({ api, context, key, scope, secret, siteInfo, value }) => { +const setInEnvelope = async ({ api, context, force, key, scope, secret, siteInfo, value }) => { const accountId = siteInfo.account_slug const siteId = siteInfo.id @@ -47,6 +48,10 @@ const setInEnvelope = async ({ api, context, key, scope, secret, siteInfo, value // @ts-expect-error TS(7006) FIXME: Parameter 'envVar' implicitly has an 'any' type. const existing = envelopeVariables.find((envVar) => envVar.key === key) + // Checks if --force is passed and if it is an existing variaible, then we need to prompt the user + if (Boolean(force) === false && existing) { + await promptOverwriteEnvVariable(key) + } const params = { accountId, siteId, key } try { @@ -108,20 +113,17 @@ const setInEnvelope = async ({ api, context, key, scope, secret, siteInfo, value } export const envSet = async (key: string, value: string, options: OptionValues, command: BaseCommand) => { - const { context, scope, secret } = options - + const { context, force, scope, secret } = options const { api, cachedConfig, site } = command.netlify const siteId = site.id - if (!siteId) { log('No site id found, please run inside a site folder or `netlify link`') return false } - const { siteInfo } = cachedConfig // Get current environment variables set in the UI - const finalEnv = await setInEnvelope({ api, siteInfo, key, value, context, scope, secret }) + const finalEnv = await setInEnvelope({ api, siteInfo, force, key, value, context, scope, secret }) if (!finalEnv) { return false diff --git a/src/commands/env/env-unset.ts b/src/commands/env/env-unset.ts index 044a32f4942..14668251e2e 100644 --- a/src/commands/env/env-unset.ts +++ b/src/commands/env/env-unset.ts @@ -1,15 +1,15 @@ import { OptionValues } from 'commander' -import { chalk, log, logJson } from '../../utils/command-helpers.js' +import { chalk, log, logJson, exit } from '../../utils/command-helpers.js' import { AVAILABLE_CONTEXTS, translateFromEnvelopeToMongo } from '../../utils/env/index.js' +import { promptOverwriteEnvVariable } from '../../utils/prompts/env-unset-prompts.js' import BaseCommand from '../base-command.js' - /** * Deletes a given key from the env of a site configured with Envelope * @returns {Promise} */ // @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message -const unsetInEnvelope = async ({ api, context, key, siteInfo }) => { +const unsetInEnvelope = async ({ api, context, force, key, siteInfo }) => { const accountId = siteInfo.account_slug const siteId = siteInfo.id // fetch envelope env vars @@ -26,6 +26,10 @@ const unsetInEnvelope = async ({ api, context, key, siteInfo }) => { return env } + if (Boolean(force) === false) { + await promptOverwriteEnvVariable(key) + } + const params = { accountId, siteId, key } try { if (context) { @@ -64,7 +68,7 @@ const unsetInEnvelope = async ({ api, context, key, siteInfo }) => { } export const envUnset = async (key: string, options: OptionValues, command: BaseCommand) => { - const { context } = options + const { context, force } = options const { api, cachedConfig, site } = command.netlify const siteId = site.id @@ -75,7 +79,7 @@ export const envUnset = async (key: string, options: OptionValues, command: Base const { siteInfo } = cachedConfig - const finalEnv = await unsetInEnvelope({ api, context, siteInfo, key }) + const finalEnv = await unsetInEnvelope({ api, context, force, siteInfo, key }) // Return new environment variables of site if using json flag if (options.json) { diff --git a/src/commands/functions/functions-create.ts b/src/commands/functions/functions-create.ts index 1fcf7fe4781..40bacd10617 100644 --- a/src/commands/functions/functions-create.ts +++ b/src/commands/functions/functions-create.ts @@ -15,7 +15,15 @@ import ora from 'ora' import { fileExistsAsync } from '../../lib/fs.js' import { getAddons, getCurrentAddon, getSiteData } from '../../utils/addons/prepare.js' -import { NETLIFYDEVERR, NETLIFYDEVLOG, NETLIFYDEVWARN, chalk, error, log } from '../../utils/command-helpers.js' +import { + APIError, + NETLIFYDEVERR, + NETLIFYDEVLOG, + NETLIFYDEVWARN, + chalk, + error, + log, +} from '../../utils/command-helpers.js' import { copyTemplateDir } from '../../utils/copy-template-dir/copy-template-dir.js' import { getDotEnvVariables, injectEnvVariables } from '../../utils/dev.js' import execa from '../../utils/execa.js' @@ -492,7 +500,6 @@ const scaffoldFromTemplate = async function (command, options, argumentName, fun await downloadFromURL(command, options, argumentName, functionsDir) } catch (error_) { error(`$${NETLIFYDEVERR} Error downloading from URL: ${options.url}`) - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) process.exit(1) } @@ -583,8 +590,7 @@ const createFunctionAddon = async function ({ addonName, addons, api, siteData, log(`Add-on "${addonName}" created for ${siteData.name}`) return true } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(error_.message) + error((error_ as APIError).message) } } @@ -682,8 +688,7 @@ const installAddons = async function (command, functionAddons, fnPath) { await handleAddonDidInstall({ addonCreated, addonDidInstall, command, fnPath }) } catch (error_) { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message - error(`${NETLIFYDEVERR} Error installing addon: `, error_) + error(`${NETLIFYDEVERR} Error installing addon: ${error_}`) } }) return Promise.all(arr) diff --git a/src/commands/functions/functions-invoke.ts b/src/commands/functions/functions-invoke.ts index 0ca0da575f0..f8af526ba4d 100644 --- a/src/commands/functions/functions-invoke.ts +++ b/src/commands/functions/functions-invoke.ts @@ -6,7 +6,7 @@ import { OptionValues } from 'commander' import inquirer from 'inquirer' import fetch from 'node-fetch' -import { NETLIFYDEVWARN, chalk, error, exit } from '../../utils/command-helpers.js' +import { APIError, NETLIFYDEVWARN, chalk, error, exit } from '../../utils/command-helpers.js' import { BACKGROUND, CLOCKWORK_USERAGENT, getFunctions } from '../../utils/functions/index.js' import BaseCommand from '../base-command.js' @@ -234,7 +234,6 @@ export const functionsInvoke = async (nameArgument: string, options: OptionValue const data = await response.text() console.log(data) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`Ran into an error invoking your function: ${error_.message}`) + error(`Ran into an error invoking your function: ${(error_ as APIError).message}`) } } diff --git a/src/commands/init/index.ts b/src/commands/init/index.ts index 3c22c422548..947093d4c53 100644 --- a/src/commands/init/index.ts +++ b/src/commands/init/index.ts @@ -9,7 +9,6 @@ export const createInitCommand = (program: BaseCommand) => 'Configure continuous deployment for a new or existing site. To create a new site without continuous deployment, use `netlify sites:create`', ) .option('-m, --manual', 'Manually configure a git remote for CI') - .option('--force', 'Reinitialize CI hooks if the linked site is already configured to use CI') .addOption( new Option( '--gitRemoteName ', diff --git a/src/commands/integration/deploy.ts b/src/commands/integration/deploy.ts index 56c0def7bae..8e83653817e 100644 --- a/src/commands/integration/deploy.ts +++ b/src/commands/integration/deploy.ts @@ -1,6 +1,7 @@ import fs from 'fs' +import process from 'process' import { resolve } from 'path' -import { env, exit } from 'process' +import { exit } from 'process' import { OptionValues } from 'commander' import inquirer from 'inquirer' @@ -17,7 +18,7 @@ import { checkOptions } from '../build/build.js' import { deploy as siteDeploy } from '../deploy/deploy.js' function getIntegrationAPIUrl() { - return env.INTEGRATION_URL || 'https://api.netlifysdk.com' + return process.env.INTEGRATION_URL || 'https://api.netlifysdk.com' } // @ts-expect-error TS(7006) FIXME: Parameter 'localScopes' implicitly has an 'any' ty... Remove this comment to see the full error message @@ -383,7 +384,6 @@ export const getConfiguration = (workingDir) => { export const deploy = async (options: OptionValues, command: BaseCommand) => { const { api, cachedConfig, site, siteInfo } = command.netlify const { id: siteId } = site - // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. const [token] = await getToken() const workingDir = resolve(command.workingDir) const buildOptions = await getBuildOptions({ @@ -401,6 +401,7 @@ export const deploy = async (options: OptionValues, command: BaseCommand) => { const { description, name, scopes, slug } = await getConfiguration(command.workingDir) const localIntegrationConfig = { name, description, scopes, slug } + const headers = token ? { 'netlify-token': token } : undefined // @ts-expect-error TS(2345) FIXME: Argument of type '{ api: any; site: any; siteInfo:... Remove this comment to see the full error message const { accountId } = await getSiteInformation({ api, @@ -411,9 +412,7 @@ export const deploy = async (options: OptionValues, command: BaseCommand) => { const { body: registeredIntegration, statusCode } = await fetch( `${getIntegrationAPIUrl()}/${accountId}/integrations?site_id=${siteId}`, { - headers: { - 'netlify-token': token, - }, + headers, }, ).then(async (res) => { const body = await res.json() diff --git a/src/commands/link/link.ts b/src/commands/link/link.ts index 30889cb2525..0b6c469ff2f 100644 --- a/src/commands/link/link.ts +++ b/src/commands/link/link.ts @@ -3,10 +3,11 @@ import inquirer from 'inquirer' import isEmpty from 'lodash/isEmpty.js' import { listSites } from '../../lib/api.js' -import { chalk, error, exit, log } from '../../utils/command-helpers.js' +import { chalk, error, exit, log, APIError } from '../../utils/command-helpers.js' import getRepoData from '../../utils/get-repo-data.js' import { ensureNetlifyIgnore } from '../../utils/gitignore.js' import { track } from '../../utils/telemetry/index.js' +import type { SiteInfo } from '../../utils/types.js' import BaseCommand from '../base-command.js' /** @@ -61,7 +62,6 @@ const linkPrompt = async (command, options) => { } const matchingSites = sites.filter( - // @ts-expect-error TS(2339) FIXME: Property 'repo_url' does not exist on type '{}'. ({ build_settings: buildSettings = {} }) => repoData.httpsUrl === buildSettings.repo_url, ) @@ -92,7 +92,6 @@ Run ${chalk.cyanBright('git remote -v')} to see a list of your git remotes.`) type: 'list', name: 'selectedSite', message: 'Which site do you want to link?', - // @ts-expect-error TS(7006) FIXME: Parameter 'matchingSite' implicitly has an 'any' t... Remove this comment to see the full error message choices: matchingSites.map((matchingSite) => ({ name: `${matchingSite.name} - ${matchingSite.ssl_url}`, value: matchingSite, @@ -118,18 +117,16 @@ Run ${chalk.cyanBright('git remote -v')} to see a list of your git remotes.`) log(`Looking for sites with names containing '${searchTerm}'...`) log() - let matchingSites + let matchingSites: SiteInfo[] = [] try { matchingSites = await listSites({ api, options: { name: searchTerm, filter: 'all' }, }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if ((error_ as APIError).status === 404) { error(`'${searchTerm}' not found`) } else { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } @@ -149,7 +146,6 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`) name: 'selectedSite', message: 'Which site do you want to link?', paginated: true, - // @ts-expect-error TS(7006) FIXME: Parameter 'matchingSite' implicitly has an 'any' t... Remove this comment to see the full error message choices: matchingSites.map((matchingSite) => ({ name: matchingSite.name, value: matchingSite })), }, ]) @@ -172,7 +168,6 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`) try { sites = await listSites({ api, options: { maxPages: 1, filter: 'all' } }) } catch (error_) { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } @@ -209,11 +204,9 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`) try { site = await api.getSite({ siteId }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { - error(new Error(`Site ID '${siteId}' not found`)) + if ((error_ as APIError).status === 404) { + error(`Site ID '${siteId}' not found`) } else { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } @@ -281,11 +274,9 @@ export const link = async (options: OptionValues, command: BaseCommand) => { try { siteData = await api.getSite({ site_id: options.id }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if ((error_ as APIError).status === 404) { error(new Error(`Site id ${options.id} not found`)) } else { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } @@ -300,7 +291,7 @@ export const link = async (options: OptionValues, command: BaseCommand) => { kind: 'byId', }) } else if (options.name) { - let results + let results: SiteInfo[] = [] try { results = await listSites({ api, @@ -310,11 +301,9 @@ export const link = async (options: OptionValues, command: BaseCommand) => { }, }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if ((error_ as APIError).status === 404) { error(new Error(`${options.name} not found`)) } else { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } @@ -322,13 +311,14 @@ export const link = async (options: OptionValues, command: BaseCommand) => { if (results.length === 0) { error(new Error(`No sites found named ${options.name}`)) } - const [firstSiteData] = results - state.set('siteId', firstSiteData.id) - log(`Linked to ${firstSiteData.name}`) + const matchingSiteData = results.find((site: SiteInfo) => site.name === options.name) || results[0] + state.set('siteId', matchingSiteData.id) + + log(`Linked to ${matchingSiteData.name}`) await track('sites_linked', { - siteId: (firstSiteData && firstSiteData.id) || siteId, + siteId: (matchingSiteData && matchingSiteData.id) || siteId, linkType: 'manual', kind: 'byName', }) diff --git a/src/commands/lm/lm-setup.ts b/src/commands/lm/lm-setup.ts index 409b0fad854..f32940ab4c5 100644 --- a/src/commands/lm/lm-setup.ts +++ b/src/commands/lm/lm-setup.ts @@ -65,7 +65,6 @@ export const lmSetup = async (options: OptionValues, command: BaseCommand) => { try { helperInstalled = await installHelperIfMissing({ force: options.forceInstall }) } catch (error_) { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } diff --git a/src/commands/login/login.ts b/src/commands/login/login.ts index 4917460434e..c4ca33f562b 100644 --- a/src/commands/login/login.ts +++ b/src/commands/login/login.ts @@ -1,10 +1,10 @@ import { OptionValues } from 'commander' import { chalk, exit, getToken, log } from '../../utils/command-helpers.js' +import { TokenLocation } from '../../utils/types.js' import BaseCommand from '../base-command.js' -// @ts-expect-error TS(7006) FIXME: Parameter 'location' implicitly has an 'any' type. -const msg = function (location) { +const msg = function (location: TokenLocation) { switch (location) { case 'env': return 'via process.env.NETLIFY_AUTH_TOKEN set in your terminal session' @@ -18,7 +18,6 @@ const msg = function (location) { } export const login = async (options: OptionValues, command: BaseCommand) => { - // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. const [accessToken, location] = await getToken() command.setAnalyticsPayload({ new: options.new }) diff --git a/src/commands/logout/logout.ts b/src/commands/logout/logout.ts index f6abe875b6d..03881179654 100644 --- a/src/commands/logout/logout.ts +++ b/src/commands/logout/logout.ts @@ -5,7 +5,6 @@ import { track } from '../../utils/telemetry/index.js' import BaseCommand from '../base-command.js' export const logout = async (options: OptionValues, command: BaseCommand) => { - // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. const [accessToken, location] = await getToken() if (!accessToken) { diff --git a/src/commands/logs/build.ts b/src/commands/logs/build.ts index 502d1b97d92..d16fdf43bd4 100644 --- a/src/commands/logs/build.ts +++ b/src/commands/logs/build.ts @@ -39,6 +39,11 @@ export const logsBuild = async (options: OptionValues, command: BaseCommand) => const { id: siteId } = site const userId = command.netlify.globalConfig.get('userId') + if (!siteId) { + log('You must link a site before attempting to view deploy logs') + return + } + const deploys = await client.listSiteDeploys({ siteId, state: 'building' }) if (deploys.length === 0) { diff --git a/src/commands/main.ts b/src/commands/main.ts index 451dd0307b3..99a356baf70 100644 --- a/src/commands/main.ts +++ b/src/commands/main.ts @@ -37,30 +37,55 @@ import { createStatusCommand } from './status/index.js' import { createSwitchCommand } from './switch/index.js' import { createUnlinkCommand } from './unlink/index.js' import { createWatchCommand } from './watch/index.js' - +import { AddressInUseError } from './types.js' const SUGGESTION_TIMEOUT = 1e4 -process.on('uncaughtException', async (err) => { - console.log('') - error( - `${chalk.red( - 'Netlify CLI has terminated unexpectedly', - )}\nThis is a problem with the Netlify CLI, not with your application.\nIf you recently updated the CLI, consider reverting to an older version by running:\n\n${chalk.bold( - 'npm install -g netlify-cli@VERSION', - )}\n\nYou can use any version from ${chalk.underline( - 'https://ntl.fyi/cli-versions', - )}.\n\nPlease report this problem at ${chalk.underline( - 'https://ntl.fyi/cli-error', - )} including the error details below.\n`, - { exit: false }, - ) +// These commands run with the --force flag in non-interactive and CI environments +export const CI_FORCED_COMMANDS = { + 'env:set': { options: '--force', description: 'Bypasses prompts & Force the command to run.' }, + 'env:unset': { options: '--force', description: 'Bypasses prompts & Force the command to run.' }, + 'env:clone': { options: '--force', description: 'Bypasses prompts & Force the command to run.' }, + 'blobs:set': { options: '--force', description: 'Bypasses prompts & Force the command to run.' }, + 'blobs:delete': { options: '--force', description: 'Bypasses prompts & Force the command to run.' }, + 'addons:delete': { options: '-f, --force', description: 'Delete without prompting (useful for CI)' }, + init: { options: '--force', description: 'Reinitialize CI hooks if the linked site is already configured to use CI' }, + 'sites:delete': { options: '-f, --force', description: 'Delete without prompting (useful for CI).' }, +} - const systemInfo = await getSystemInfo() +process.on('uncaughtException', async (err: AddressInUseError | Error) => { + if ('code' in err && err.code === 'EADDRINUSE') { + error( + `${chalk.red(`Port ${err.port} is already in use`)}\n\n` + + `Your serverless functions might be initializing a server\n` + + `to listen on specific port without properly closing it.\n\n` + + `This behavior is generally not advised\n` + + `To resolve this issue, try the following:\n` + + `1. If you NEED your serverless function to listen on a specific port,\n` + + `use a randomly assigned port as we do not officially support this.\n` + + `2. Review your serverless functions for lingering server connections, close them\n` + + `3. Check if any other applications are using port ${err.port}\n`, + { exit: false }, + ) + } else { + error( + `${chalk.red( + 'Netlify CLI has terminated unexpectedly', + )}\nThis is a problem with the Netlify CLI, not with your application.\nIf you recently updated the CLI, consider reverting to an older version by running:\n\n${chalk.bold( + 'npm install -g netlify-cli@VERSION', + )}\n\nYou can use any version from ${chalk.underline( + 'https://ntl.fyi/cli-versions', + )}.\n\nPlease report this problem at ${chalk.underline( + 'https://ntl.fyi/cli-error', + )} including the error details below.\n`, + { exit: false }, + ) - console.log(chalk.dim(err.stack || err)) - console.log(chalk.dim(systemInfo)) + const systemInfo = await getSystemInfo() - reportError(err, { severity: 'error' }) + console.log(chalk.dim(err.stack || err)) + console.log(chalk.dim(systemInfo)) + reportError(err, { severity: 'error' }) + } process.exit(1) }) @@ -230,5 +255,13 @@ export const createMainCommand = () => { }) .action(mainCommand) + program.commands.forEach((cmd) => { + const cmdName = cmd.name() + if (cmdName in CI_FORCED_COMMANDS) { + const { options, description } = CI_FORCED_COMMANDS[cmdName as keyof typeof CI_FORCED_COMMANDS] + cmd.option(options, description) + } + }) + return program } diff --git a/src/commands/sites/sites-create-template.ts b/src/commands/sites/sites-create-template.ts index ff2747f1086..aad249cb708 100644 --- a/src/commands/sites/sites-create-template.ts +++ b/src/commands/sites/sites-create-template.ts @@ -1,82 +1,38 @@ import { OptionValues } from 'commander' import inquirer from 'inquirer' import pick from 'lodash/pick.js' -// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'pars... Remove this comment to see the full error message -import parseGitHubUrl from 'parse-github-url' import { render } from 'prettyjson' - -import { chalk, error, getTerminalLink, log, logJson, warn } from '../../utils/command-helpers.js' +import { v4 as uuid } from 'uuid' + +import { + chalk, + error, + getTerminalLink, + log, + logJson, + warn, + APIError, + GitHubAPIError, + GitHubRepoResponse, +} from '../../utils/command-helpers.js' import execa from '../../utils/execa.js' import getRepoData from '../../utils/get-repo-data.js' import { getGitHubToken } from '../../utils/init/config-github.js' import { configureRepo } from '../../utils/init/config.js' -import { createRepo, getTemplatesFromGitHub, validateTemplate } from '../../utils/sites/utils.js' +import { deployedSiteExists, getGitHubLink, getTemplateName } from '../../utils/sites/create-template.js' +import { createRepo, validateTemplate } from '../../utils/sites/utils.js' import { track } from '../../utils/telemetry/index.js' +import { Account, SiteInfo } from '../../utils/types.js' import BaseCommand from '../base-command.js' import { getSiteNameInput } from './sites-create.js' -// @ts-expect-error TS(7006) FIXME: Parameter 'token' implicitly has an 'any' type. -export const fetchTemplates = async (token) => { - const templatesFromGithubOrg = await getTemplatesFromGitHub(token) - - return ( - // @ts-expect-error TS(18046) - 'templatesFromGithubOrg' if of type 'unknown' - templatesFromGithubOrg - // @ts-expect-error TS(7006) FIXME: Parameter 'repo' implicitly has an 'any' type. - .filter((repo) => !repo.archived && !repo.disabled) - // @ts-expect-error TS(7006) FIXME: Parameter 'template' implicitly has an 'any' type. - .map((template) => ({ - name: template.name, - sourceCodeUrl: template.html_url, - slug: template.full_name, - })) - ) -} - -// @ts-expect-error TS(7031) FIXME: Binding element 'ghToken' implicitly has an 'any' ... Remove this comment to see the full error message -const getTemplateName = async ({ ghToken, options, repository }) => { - if (repository) { - const { repo } = parseGitHubUrl(repository) - return repo || `netlify-templates/${repository}` - } - - if (options.url) { - const urlFromOptions = new URL(options.url) - return urlFromOptions.pathname.slice(1) - } - - const templates = await fetchTemplates(ghToken) - - log(`Choose one of our starter templates. Netlify will create a new repo for this template in your GitHub account.`) - - const { templateName } = await inquirer.prompt([ - { - type: 'list', - name: 'templateName', - message: 'Template:', - // @ts-expect-error TS(7006) FIXME: Parameter 'template' implicitly has an 'any' type. - choices: templates.map((template) => ({ - value: template.slug, - name: template.name, - })), - }, - ]) - - return templateName -} - -// @ts-expect-error TS(7031) FIXME: Binding element 'options' implicitly has an 'any' ... Remove this comment to see the full error message -const getGitHubLink = ({ options, templateName }) => options.url || `https://github.com/${templateName}` - export const sitesCreateTemplate = async (repository: string, options: OptionValues, command: BaseCommand) => { const { api } = command.netlify - await command.authenticate() const { globalConfig } = command.netlify const ghToken = await getGitHubToken({ globalConfig }) - const templateName = await getTemplateName({ ghToken, options, repository }) const { exists, isTemplate } = await validateTemplate({ templateName, ghToken }) if (!exists) { @@ -94,7 +50,6 @@ export const sitesCreateTemplate = async (repository: string, options: OptionVal error(`${getTerminalLink(chalk.bold(templateName), githubLink)} is not a valid GitHub template`) return } - const accounts = await api.listAccountsForUser() let { accountSlug } = options @@ -105,8 +60,7 @@ export const sitesCreateTemplate = async (repository: string, options: OptionVal type: 'list', name: 'accountSlug', message: 'Team:', - // @ts-expect-error TS(7006) FIXME: Parameter 'account' implicitly has an 'any' type. - choices: accounts.map((account) => ({ + choices: accounts.map((account: Account) => ({ value: account.slug, name: account.name, })), @@ -116,91 +70,108 @@ export const sitesCreateTemplate = async (repository: string, options: OptionVal } const { name: nameFlag } = options - let site + let site: SiteInfo let repoResp: Awaited> // Allow the user to reenter site name if selected one isn't available - // @ts-expect-error TS(7006) FIXME: Parameter 'name' implicitly has an 'any' type. - const inputSiteName = async (name) => { + const inputSiteName = async (name?: string, hasExistingRepo?: boolean): Promise<[SiteInfo, GitHubRepoResponse]> => { const { name: inputName } = await getSiteNameInput(name) - try { - const siteName = inputName.trim() + const siteName = inputName.trim() - // Create new repo from template - repoResp = await createRepo(templateName, ghToken, siteName || templateName) + if (siteName && (await deployedSiteExists(siteName))) { + log('A site with that name already exists') + return inputSiteName() + } - // @ts-expect-error TS(18046) - 'repoResp' if of type 'unknown' - if (repoResp.errors) { - // @ts-expect-error TS(18046) - 'repoResp' if of type 'unknown' - if (repoResp.errors[0].includes('Name already exists on this account')) { - warn( - `Oh no! We found already a repository with this name. It seems you have already created a template with the name ${templateName}. Please try to run the command again and provide a different name.`, + try { + const sites: SiteInfo[] = await api.listSites({ name: siteName, filter: 'all' }) + const siteFoundByName = sites.find((filteredSite) => filteredSite.name === siteName) + if (siteFoundByName) { + log('A site with that name already exists on your account') + return inputSiteName() + } + } catch (error_) { + error(error_) + } + + if (!hasExistingRepo) { + try { + // Create new repo from template + let gitHubInputName = siteName || templateName + repoResp = await createRepo(templateName, ghToken, gitHubInputName) + if (repoResp.errors && repoResp.errors[0].includes('Name already exists on this account')) { + if (gitHubInputName === templateName) { + gitHubInputName += `-${uuid().split('-')[0]}` + repoResp = await createRepo(templateName, ghToken, gitHubInputName) + } else { + warn(`It seems you have already created a repository with the name ${gitHubInputName}.`) + return inputSiteName() + } + } + if (!repoResp.id) { + throw new GitHubAPIError((repoResp as GitHubAPIError).status, (repoResp as GitHubAPIError).message) + } + hasExistingRepo = true + } catch (error_) { + if ((error_ as GitHubAPIError).status === '404') { + error( + `Could not create repository: ${ + (error_ as GitHubAPIError).message + }. Ensure that your GitHub personal access token grants permission to create repositories`, ) - // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. - await inputSiteName() } else { - throw new Error( - // @ts-expect-error TS(18046) - 'repoResp' if of type 'unknown' - `Oops! Seems like something went wrong trying to create the repository. We're getting the following error: '${repoResp.errors[0]}'. You can try to re-run this command again or open an issue in our repository: https://github.com/netlify/cli/issues`, + error( + `Something went wrong trying to create the repository. We're getting the following error: '${ + (error_ as GitHubAPIError).message + }'. You can try to re-run this command again or open an issue in our repository: https://github.com/netlify/cli/issues`, ) } - } else { - site = await api.createSiteInTeam({ - accountSlug, - body: { - repo: { - provider: 'github', - // @ts-expect-error TS(18046) - 'repoResp' if of type 'unknown' - repo: repoResp.full_name, - // @ts-expect-error TS(18046) - 'repoResp' if of type 'unknown' - private: repoResp.private, - // @ts-expect-error TS(18046) - 'repoResp' if of type 'unknown' - branch: repoResp.default_branch, - }, - name: siteName, - }, - }) } + } + + try { + site = await api.createSiteInTeam({ + accountSlug, + body: { + repo: { + provider: 'github', + repo: repoResp.full_name, + private: repoResp.private, + branch: repoResp.default_branch, + }, + name: siteName, + }, + }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 422 || error_.message === 'Duplicate repo') { - warn( - `${name}.netlify.app already exists or a repository named ${name} already exists on this account. Please try a different slug.`, - ) - // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. - await inputSiteName() - } else { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`createSiteInTeam error: ${error_.status}: ${error_.message}`) + if ((error_ as APIError).status === 422) { + log(`createSiteInTeam error: ${(error_ as APIError).status}: ${(error_ as APIError).message}`) + log('Cannot create a site with that name. Site name may already exist. Please try a new name.') + return inputSiteName(undefined, hasExistingRepo) } + error(`createSiteInTeam error: ${(error_ as APIError).status}: ${(error_ as APIError).message}`) } + return [site, repoResp] } - await inputSiteName(nameFlag) + ;[site, repoResp] = await inputSiteName(nameFlag) log() log(chalk.greenBright.bold.underline(`Site Created`)) log() - // @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'. const siteUrl = site.ssl_url || site.url log( render({ - // @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'. 'Admin URL': site.admin_url, URL: siteUrl, - // @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'. 'Site ID': site.id, - // @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'. 'Repo URL': site.build_settings.repo_url, }), ) track('sites_createdFromTemplate', { - // @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'. siteId: site.id, - // @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'. adminUrl: site.admin_url, siteUrl, }) @@ -208,22 +179,22 @@ export const sitesCreateTemplate = async (repository: string, options: OptionVal const { cloneConfirm } = await inquirer.prompt({ type: 'confirm', name: 'cloneConfirm', - message: `Do you want to clone the repository?`, + message: `Do you want to clone the repository to your local machine?`, default: true, }) if (cloneConfirm) { log() - // @ts-expect-error TS(18046) - 'repoResp' if of type 'unknown' - await execa('git', ['clone', repoResp.clone_url, `${repoResp.name}`]) - // @ts-expect-error TS(18046) - 'repoResp' if of type 'unknown' + + if (repoResp.clone_url) { + await execa('git', ['clone', repoResp.clone_url, `${repoResp.name}`]) + } + log(`🚀 Repository cloned successfully. You can find it under the ${chalk.magenta(repoResp.name)} folder`) } if (options.withCi) { log('Configuring CI') - // @ts-expect-error TS(2345) FIXME: Argument of type '{ workingDir: any; }' is not ass... Remove this comment to see the full error message const repoData = await getRepoData({ workingDir: command.workingDir }) - // @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'. await configureRepo({ command, siteId: site.id, repoData, manual: options.manual }) } diff --git a/src/commands/sites/sites-create.ts b/src/commands/sites/sites-create.ts index c7a21f3d81a..2f97b2e8334 100644 --- a/src/commands/sites/sites-create.ts +++ b/src/commands/sites/sites-create.ts @@ -3,15 +3,15 @@ import inquirer from 'inquirer' import pick from 'lodash/pick.js' import prettyjson from 'prettyjson' -import { chalk, error, log, logJson, warn } from '../../utils/command-helpers.js' +import { chalk, error, log, logJson, warn, APIError } from '../../utils/command-helpers.js' import getRepoData from '../../utils/get-repo-data.js' import { configureRepo } from '../../utils/init/config.js' import { track } from '../../utils/telemetry/index.js' +import { Account } from '../../utils/types.js' import BaseCommand from '../base-command.js' import { link } from '../link/link.js' -// @ts-expect-error TS(7006) FIXME: Parameter 'name' implicitly has an 'any' type. -export const getSiteNameInput = async (name) => { +export const getSiteNameInput = async (name: string | undefined): Promise<{ name: string }> => { if (!name) { const { name: nameInput } = await inquirer.prompt([ { @@ -22,7 +22,7 @@ export const getSiteNameInput = async (name) => { /^[a-zA-Z\d-]+$/.test(input || undefined) || 'Only alphanumeric characters and hyphens are allowed', }, ]) - name = nameInput || '' + name = typeof nameInput === 'string' ? nameInput : '' } return { name } @@ -33,16 +33,17 @@ export const sitesCreate = async (options: OptionValues, command: BaseCommand) = await command.authenticate() - const accounts = await api.listAccountsForUser() + const accounts: Account[] = await api.listAccountsForUser() - let { accountSlug } = options + let { accountSlug }: { accountSlug?: string } = options if (!accountSlug) { - const { accountSlug: accountSlugInput } = await inquirer.prompt([ + const { accountSlug: accountSlugInput }: { accountSlug: string } = await inquirer.prompt< + Promise<{ accountSlug: string }> + >([ { type: 'list', name: 'accountSlug', message: 'Team:', - // @ts-expect-error TS(7006) FIXME: Parameter 'account' implicitly has an 'any' type. choices: accounts.map((account) => ({ value: account.slug, name: account.name, @@ -55,13 +56,11 @@ export const sitesCreate = async (options: OptionValues, command: BaseCommand) = let site // Allow the user to reenter site name if selected one isn't available - // @ts-expect-error TS(7006) FIXME: Parameter 'name' implicitly has an 'any' type. - const inputSiteName = async (name) => { + const inputSiteName = async (name?: string) => { const { name: siteName } = await getSiteNameInput(name) - const body = {} + const body: { name?: string } = {} if (typeof siteName === 'string') { - // @ts-expect-error TS(2339) FIXME: Property 'name' does not exist on type '{}'. body.name = siteName.trim() } try { @@ -70,14 +69,11 @@ export const sitesCreate = async (options: OptionValues, command: BaseCommand) = body, }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 422) { + if ((error_ as APIError).status === 422) { warn(`${siteName}.netlify.app already exists. Please try a different slug.`) - // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. await inputSiteName() } else { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`createSiteInTeam error: ${error_.status}: ${error_.message}`) + error(`createSiteInTeam error: ${(error_ as APIError).status}: ${(error_ as APIError).message}`) } } } @@ -109,7 +105,6 @@ export const sitesCreate = async (options: OptionValues, command: BaseCommand) = if (options.withCi) { log('Configuring CI') - // @ts-expect-error TS(2345) FIXME: Argument of type '{ workingDir: any; }' is not ass... Remove this comment to see the full error message const repoData = await getRepoData({ workingDir: command.workingDir }) // @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'. await configureRepo({ command, siteId: site.id, repoData, manual: options.manual }) diff --git a/src/commands/sites/sites-delete.ts b/src/commands/sites/sites-delete.ts index 70956c893c3..512fab816ff 100644 --- a/src/commands/sites/sites-delete.ts +++ b/src/commands/sites/sites-delete.ts @@ -1,7 +1,7 @@ import { OptionValues } from 'commander' import inquirer from 'inquirer' -import { chalk, error, exit, log } from '../../utils/command-helpers.js' +import { chalk, error, exit, log, APIError } from '../../utils/command-helpers.js' import BaseCommand from '../base-command.js' export const sitesDelete = async (siteId: string, options: OptionValues, command: BaseCommand) => { @@ -17,9 +17,10 @@ export const sitesDelete = async (siteId: string, options: OptionValues, command try { siteData = await api.getSite({ siteId }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if ((error_ as APIError).status === 404) { error(`No site with id ${siteId} found. Please verify the siteId & try again.`) + } else { + error(error_) } } @@ -74,12 +75,10 @@ export const sitesDelete = async (siteId: string, options: OptionValues, command try { await api.deleteSite({ site_id: siteId }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if ((error_ as APIError).status === 404) { error(`No site with id ${siteId} found. Please verify the siteId & try again.`) } else { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`Delete Site error: ${error_.status}: ${error_.message}`) + error(`Delete Site error: ${(error_ as APIError).status}: ${(error_ as APIError).message}`) } } log(`Site "${siteId}" successfully deleted!`) diff --git a/src/commands/sites/sites-list.ts b/src/commands/sites/sites-list.ts index 9fb382d62f8..19b2e9fee92 100644 --- a/src/commands/sites/sites-list.ts +++ b/src/commands/sites/sites-list.ts @@ -3,6 +3,7 @@ import { OptionValues } from 'commander' import { listSites } from '../../lib/api.js' import { startSpinner, stopSpinner } from '../../lib/spinner.js' import { chalk, log, logJson } from '../../utils/command-helpers.js' +import { SiteInfo } from '../../utils/types.js' import BaseCommand from '../base-command.js' export const sitesList = async (options: OptionValues, command: BaseCommand) => { @@ -21,9 +22,8 @@ export const sitesList = async (options: OptionValues, command: BaseCommand) => } if (sites && sites.length !== 0) { - // @ts-expect-error TS(7006) FIXME: Parameter 'site' implicitly has an 'any' type. const logSites = sites.map((site) => { - const siteInfo = { + const siteInfo: Pick & { repo_url?: string } = { id: site.id, name: site.name, ssl_url: site.ssl_url, @@ -31,7 +31,6 @@ export const sitesList = async (options: OptionValues, command: BaseCommand) => } if (site.build_settings && site.build_settings.repo_url) { - // @ts-expect-error TS(2339) FIXME: Property 'repo_url' does not exist on type '{ id: ... Remove this comment to see the full error message siteInfo.repo_url = site.build_settings.repo_url } @@ -40,9 +39,8 @@ export const sitesList = async (options: OptionValues, command: BaseCommand) => // Json response for piping commands if (options.json) { - // @ts-expect-error TS(7006) FIXME: Parameter 'site' implicitly has an 'any' type. const redactedSites = sites.map((site) => { - if (site && site.build_settings) { + if (site?.build_settings?.env) { delete site.build_settings.env } return site @@ -59,7 +57,6 @@ export const sitesList = async (options: OptionValues, command: BaseCommand) => Count: ${logSites.length} `) - // @ts-expect-error TS(7006) FIXME: Parameter 'logSite' implicitly has an 'any' type. logSites.forEach((logSite) => { log(`${chalk.greenBright(logSite.name)} - ${logSite.id}`) log(` ${chalk.whiteBright.bold('url:')} ${chalk.yellowBright(logSite.ssl_url)}`) diff --git a/src/commands/sites/sites.ts b/src/commands/sites/sites.ts index 908c8940fc3..cd8d1652bd4 100644 --- a/src/commands/sites/sites.ts +++ b/src/commands/sites/sites.ts @@ -81,7 +81,6 @@ export const createSitesCommand = (program: BaseCommand) => { .command('sites:delete') .description('Delete a site\nThis command will permanently delete the site on Netlify. Use with caution.') .argument('', 'Site ID to delete.') - .option('-f, --force', 'delete without prompting (useful for CI)') .addExamples(['netlify sites:delete 1234-3262-1211']) .action(async (siteId: string, options: OptionValues, command: BaseCommand) => { const { sitesDelete } = await import('./sites-delete.js') diff --git a/src/commands/status/status.ts b/src/commands/status/status.ts index 15cf6b745f5..1912c6be418 100644 --- a/src/commands/status/status.ts +++ b/src/commands/status/status.ts @@ -2,13 +2,12 @@ import clean from 'clean-deep' import { OptionValues } from 'commander' import prettyjson from 'prettyjson' -import { chalk, error, exit, getToken, log, logJson, warn } from '../../utils/command-helpers.js' +import { chalk, error, exit, getToken, log, logJson, warn, APIError } from '../../utils/command-helpers.js' import BaseCommand from '../base-command.js' export const status = async (options: OptionValues, command: BaseCommand) => { const { api, globalConfig, site, siteInfo } = command.netlify const current = globalConfig.get('userId') - // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. const [accessToken] = await getToken() if (!accessToken) { @@ -31,9 +30,10 @@ export const status = async (options: OptionValues, command: BaseCommand) => { // eslint-disable-next-line @typescript-eslint/no-extra-semi ;[accounts, user] = await Promise.all([api.listAccountsForUser(), api.getCurrentUser()]) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 401) { + if ((error_ as APIError).status === 401) { error('Your session has expired. Please try to re-authenticate by running `netlify logout` and `netlify login`.') + } else { + error(error_) } } diff --git a/src/commands/types.d.ts b/src/commands/types.d.ts index dce9224fe7c..bfeb5c28cfe 100644 --- a/src/commands/types.d.ts +++ b/src/commands/types.d.ts @@ -73,3 +73,11 @@ export type NetlifyOptions = { state: StateConfig frameworksAPIPaths: FrameworksAPIPaths } + +export interface AddressInUseError extends Error { + code: 'EADDRINUSE' + errno: number + syscall: 'listen' + address: string + port: number +} diff --git a/src/commands/unlink/unlink.ts b/src/commands/unlink/unlink.ts index 6db9ff499ad..c7534b227ca 100644 --- a/src/commands/unlink/unlink.ts +++ b/src/commands/unlink/unlink.ts @@ -21,9 +21,9 @@ export const unlink = async (options: OptionValues, command: BaseCommand) => { siteId: siteData.id || siteId, }) - if (site) { + if (site && site.configPath) { log(`Unlinked ${site.configPath} from ${siteData ? siteData.name : siteId}`) } else { - log('Unlinked site') + log(`Unlinked from ${siteData ? siteData.name : siteId}`) } } diff --git a/src/commands/watch/watch.ts b/src/commands/watch/watch.ts index 071ccce8b02..2bf7a016878 100644 --- a/src/commands/watch/watch.ts +++ b/src/commands/watch/watch.ts @@ -114,7 +114,6 @@ export const watch = async (options: OptionValues, command: BaseCommand) => { ) console.timeEnd('Deploy time') } catch (error_) { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } diff --git a/src/lib/api.ts b/src/lib/api.ts index 2b2804bd43e..14502613f70 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1,4 +1,5 @@ import { warn } from '../utils/command-helpers.js' +import { SiteInfo } from '../utils/types.js' // @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message export const cancelDeploy = async ({ api, deployId }) => { @@ -13,8 +14,9 @@ export const cancelDeploy = async ({ api, deployId }) => { const FIRST_PAGE = 1 const MAX_PAGES = 10 const MAX_PER_PAGE = 100 + // @ts-expect-error TS(7023) FIXME: 'listSites' implicitly has return type 'any' becau... Remove this comment to see the full error message -export const listSites = async ({ api, options }) => { +export const listSites = async ({ api, options }): SiteInfo[] => { const { maxPages = MAX_PAGES, page = FIRST_PAGE, ...rest } = options const sites = await api.listSites({ page, per_page: MAX_PER_PAGE, ...rest }) // TODO: use pagination headers when js-client returns them diff --git a/src/lib/exec-fetcher.ts b/src/lib/exec-fetcher.ts index 3a1910f0877..ea703eaa157 100644 --- a/src/lib/exec-fetcher.ts +++ b/src/lib/exec-fetcher.ts @@ -148,8 +148,6 @@ export const fetchLatestVersion = async ({ destination, execName, extension, lat Please open up an issue on our CLI repository so that we can support it: ${issueLink}`) } - - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } diff --git a/src/utils/addons/prepare.ts b/src/utils/addons/prepare.ts index 3a31e07b077..2b0342429df 100644 --- a/src/utils/addons/prepare.ts +++ b/src/utils/addons/prepare.ts @@ -1,4 +1,4 @@ -import { chalk, error, exit, log, warn } from '../command-helpers.js' +import { APIError, chalk, error, exit, log, warn } from '../command-helpers.js' export const ADDON_VALIDATION = { EXISTS: 'EXISTS', @@ -55,12 +55,10 @@ export const getAddonManifest = async ({ addonName, api }) => { try { manifest = await api.showServiceManifest({ addonName }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (typeof error_.message === 'string' && error_.message.includes('Not Found')) { + if ((error_ as APIError).message.includes('Not Found')) { error(`No add-on "${addonName}" found. Please double check your add-on name and try again`) } else { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(error_.message) + error((error_ as APIError).message) } } return manifest @@ -72,8 +70,7 @@ export const getSiteData = async ({ api, siteId }) => { try { siteData = await api.getSite({ siteId }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`Failed getting list of site data: ${error_.message}`) + error(`Failed getting list of site data: ${(error_ as APIError).message}`) } return siteData } @@ -84,8 +81,7 @@ export const getAddons = async ({ api, siteId }) => { try { addons = await api.listServiceInstancesForSite({ siteId }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`Failed getting list of addons: ${error_.message}`) + error(`Failed getting list of addons: ${(error_ as APIError).message}`) } return addons } diff --git a/src/utils/command-helpers.ts b/src/utils/command-helpers.ts index c71311bb7a2..b24ee3d4f77 100644 --- a/src/utils/command-helpers.ts +++ b/src/utils/command-helpers.ts @@ -1,5 +1,6 @@ import { once } from 'events' import os from 'os' +import fs from 'fs' import process from 'process' import { format, inspect } from 'util' @@ -8,6 +9,7 @@ import chokidar from 'chokidar' import decache from 'decache' import WSL from 'is-wsl' import debounce from 'lodash/debounce.js' +import { NetlifyAPI } from 'netlify' import terminalLink from 'terminal-link' import { clearSpinner, startSpinner } from '../lib/spinner.js' @@ -15,6 +17,7 @@ import { clearSpinner, startSpinner } from '../lib/spinner.js' import getGlobalConfig from './get-global-config.js' import getPackageJson from './get-package-json.js' import { reportError } from './telemetry/report-error.js' +import { TokenLocation } from './types.js' /** The parsed process argv without the binary only arguments and flags */ const argv = process.argv.slice(2) @@ -92,8 +95,14 @@ const TOKEN_TIMEOUT = 3e5 * @param {object} config.ticket * @returns */ -// @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message -export const pollForToken = async ({ api, ticket }) => { + +export const pollForToken = async ({ + api, + ticket, +}: { + api: NetlifyAPI + ticket: { id: string; client_id: string; authorized: boolean; created_at: string } +}) => { const spinner = startSpinner({ text: 'Waiting for authorization...' }) try { const accessToken = await api.getAccessToken(ticket, { timeout: TOKEN_TIMEOUT }) @@ -112,21 +121,21 @@ export const pollForToken = async ({ api, ticket }) => { )}, then run ${chalk.cyanBright('netlify login')} again.`, ) } else { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } finally { clearSpinner({ spinner }) } } - /** * Get a netlify token * @param {string} [tokenFromOptions] optional token from the provided --auth options * @returns {Promise<[null|string, 'flag' | 'env' |'config' |'not found']>} */ -// @ts-expect-error TS(7006) FIXME: Parameter 'tokenFromOptions' implicitly has an 'an... Remove this comment to see the full error message -export const getToken = async (tokenFromOptions) => { + +export type tokenTuple = [string | null, TokenLocation] + +export const getToken = async (tokenFromOptions?: string): Promise => { // 1. First honor command flag --auth if (tokenFromOptions) { return [tokenFromOptions, 'flag'] @@ -186,7 +195,7 @@ export const warn = (message = '') => { } /** Throws an error or logs it */ -export const error = (message: Error | string = '', options: { exit?: boolean } = {}) => { +export const error = (message: unknown | Error | string = '', options: { exit?: boolean } = {}) => { const err = message instanceof Error ? message @@ -300,3 +309,44 @@ export const nonNullable = (value: T): value is NonNullable => value !== n export const noOp = () => { // no-op } + +export interface APIError extends Error { + status: number + message: string +} + +export class GitHubAPIError extends Error { + status: string + + constructor(status: string, message: string) { + super(message) + this.status = status + this.name = 'GitHubAPIError' + } +} + +export interface GitHubRepoResponse { + status?: string + message?: string + id?: number + name?: string + clone_url?: string + full_name?: string + private?: boolean + default_branch?: string + errors?: string[] + is_template?: boolean +} + +export const checkFileForLine = (filename: string, line: string) => { + let filecontent = '' + try { + filecontent = fs.readFileSync(filename, 'utf8') + } catch (error_) { + error(error_) + } + return !!filecontent.match(`${line}`) +} + +export const TABTAB_CONFIG_LINE = '[[ -f ~/.config/tabtab/__tabtab.zsh ]] && . ~/.config/tabtab/__tabtab.zsh || true' +export const AUTOLOAD_COMPINIT = 'autoload -U compinit; compinit' diff --git a/src/utils/dev.ts b/src/utils/dev.ts index 3d2fd51d335..5e08de4aafd 100644 --- a/src/utils/dev.ts +++ b/src/utils/dev.ts @@ -5,7 +5,7 @@ import isEmpty from 'lodash/isEmpty.js' import { supportsBackgroundFunctions } from '../lib/account.js' -import { NETLIFYDEVLOG, chalk, error, log, warn } from './command-helpers.js' +import { NETLIFYDEVLOG, chalk, error, log, warn, APIError } from './command-helpers.js' import { loadDotEnvFiles } from './dot-env.js' // Possible sources of environment variables. For the purpose of printing log messages only. Order does not matter. @@ -52,8 +52,7 @@ const getAccounts = async ({ api }) => { const accounts = await api.listAccountsForUser() return accounts } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`Failed retrieving user account: ${error_.message}. ${ERROR_CALL_TO_ACTION}`) + error(`Failed retrieving user account: ${(error_ as APIError).message}. ${ERROR_CALL_TO_ACTION}`) } } @@ -63,8 +62,11 @@ const getAddons = async ({ api, site }) => { const addons = await api.listServiceInstancesForSite({ siteId: site.id }) return addons } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`Failed retrieving addons for site ${chalk.yellow(site.id)}: ${error_.message}. ${ERROR_CALL_TO_ACTION}`) + error( + `Failed retrieving addons for site ${chalk.yellow(site.id)}: ${ + (error_ as APIError).message + }. ${ERROR_CALL_TO_ACTION}`, + ) } } diff --git a/src/utils/framework-server.ts b/src/utils/framework-server.ts index 1b7e97b77bd..b7652a806e2 100644 --- a/src/utils/framework-server.ts +++ b/src/utils/framework-server.ts @@ -70,7 +70,6 @@ export const startFrameworkServer = async function ({ stopSpinner({ error: true, spinner }) log(NETLIFYDEVERR, `Netlify Dev could not start or connect to localhost:${settings.frameworkPort}.`) log(NETLIFYDEVERR, `Please make sure your framework server is running on port ${settings.frameworkPort}`) - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) exit(1) } diff --git a/src/utils/get-repo-data.ts b/src/utils/get-repo-data.ts index 942548f4a0c..578a452d239 100644 --- a/src/utils/get-repo-data.ts +++ b/src/utils/get-repo-data.ts @@ -17,8 +17,8 @@ import { log } from './command-helpers.js' * @param {string} config.workingDir * @returns */ -// @ts-expect-error TS(7031) FIXME: Binding element 'remoteName' implicitly has an 'an... Remove this comment to see the full error message -const getRepoData = async function ({ remoteName, workingDir }) { + +const getRepoData = async function ({ remoteName, workingDir }: { remoteName?: string; workingDir: string }) { try { const [gitConfig, gitDirectory] = await Promise.all([ util.promisify(gitconfiglocal)(workingDir), diff --git a/src/utils/get-site.ts b/src/utils/get-site.ts index 5a2cac0c6ac..abb78435c6c 100644 --- a/src/utils/get-site.ts +++ b/src/utils/get-site.ts @@ -1,4 +1,4 @@ -import { error } from './command-helpers.js' +import { APIError, error } from './command-helpers.js' // @ts-expect-error TS(7006) FIXME: Parameter 'api' implicitly has an 'any' type. export const getSiteByName = async (api, siteName) => { @@ -12,7 +12,11 @@ export const getSiteByName = async (api, siteName) => { } return siteFoundByName - } catch { - error('Site not found. Please rerun "netlify link"') + } catch (error_) { + if ((error_ as APIError).status === 401) { + error(`${(error_ as APIError).message}: could not retrieve site`) + } else { + error('Site not found. Please rerun "netlify link"') + } } } diff --git a/src/utils/gh-auth.ts b/src/utils/gh-auth.ts index 2e6bce40f5b..1739b91e6b9 100644 --- a/src/utils/gh-auth.ts +++ b/src/utils/gh-auth.ts @@ -90,7 +90,7 @@ export const authWithNetlify = async () => { const getPersonalAccessToken = async () => { const { token } = await inquirer.prompt([ { - type: 'input', + type: 'password', name: 'token', message: 'Your GitHub personal access token:', filter: (input) => input.trim(), diff --git a/src/utils/hooks/requires-site-info.ts b/src/utils/hooks/requires-site-info.ts index 69b38e1a5f1..6065dd3c831 100644 --- a/src/utils/hooks/requires-site-info.ts +++ b/src/utils/hooks/requires-site-info.ts @@ -1,4 +1,5 @@ -import { error, warn } from '../command-helpers.js' +import { error, warn, APIError } from '../command-helpers.js' + /** * A preAction hook that errors out if siteInfo is an empty object * @param {*} command @@ -15,17 +16,15 @@ const requiresSiteInfo = async (command) => { await api.getSite({ siteId }) } catch (error_) { // unauthorized - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 401) { + if ((error_ as APIError).status === 401) { warn(`Log in with a different account or re-link to a site you have permission for`) return error(`Not authorized to view the currently linked site (${siteId})`) } // missing - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if ((error_ as APIError).status === 404) { return error(`The site this folder is linked to can't be found`) } - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message + return error(error_) } } diff --git a/src/utils/prompts/blob-delete-prompts.ts b/src/utils/prompts/blob-delete-prompts.ts new file mode 100644 index 00000000000..cf319586208 --- /dev/null +++ b/src/utils/prompts/blob-delete-prompts.ts @@ -0,0 +1,14 @@ +import { log } from '../command-helpers.js' + +import { confirmPrompt } from './confirm-prompt.js' +import { destructiveCommandMessages } from './prompt-messages.js' + +export const promptBlobDelete = async (key: string, storeName: string): Promise => { + const warningMessage = destructiveCommandMessages.blobDelete.generateWarning(key, storeName) + + log() + log(warningMessage) + log() + log(destructiveCommandMessages.overwriteNotice) + await confirmPrompt(destructiveCommandMessages.blobDelete.overwriteConfirmation) +} diff --git a/src/utils/prompts/blob-set-prompt.ts b/src/utils/prompts/blob-set-prompt.ts new file mode 100644 index 00000000000..59ad163dfbb --- /dev/null +++ b/src/utils/prompts/blob-set-prompt.ts @@ -0,0 +1,14 @@ +import { log } from '../command-helpers.js' + +import { confirmPrompt } from './confirm-prompt.js' +import { destructiveCommandMessages } from './prompt-messages.js' + +export const promptBlobSetOverwrite = async (key: string, storeName: string): Promise => { + const warningMessage = destructiveCommandMessages.blobSet.generateWarning(key, storeName) + + log() + log(warningMessage) + log() + log(destructiveCommandMessages.overwriteNotice) + await confirmPrompt(destructiveCommandMessages.blobSet.overwriteConfirmation) +} diff --git a/src/utils/prompts/confirm-prompt.ts b/src/utils/prompts/confirm-prompt.ts new file mode 100644 index 00000000000..fa6ea6dc89f --- /dev/null +++ b/src/utils/prompts/confirm-prompt.ts @@ -0,0 +1,21 @@ +import inquirer from 'inquirer' + +import { log, exit } from '../command-helpers.js' + +export const confirmPrompt = async (message: string): Promise => { + try { + const { confirm } = await inquirer.prompt({ + type: 'confirm', + name: 'confirm', + message, + default: false, + }) + log() + if (!confirm) { + exit() + } + } catch (error) { + console.error(error) + exit() + } +} diff --git a/src/utils/prompts/env-clone-prompt.ts b/src/utils/prompts/env-clone-prompt.ts new file mode 100644 index 00000000000..69a5719afc6 --- /dev/null +++ b/src/utils/prompts/env-clone-prompt.ts @@ -0,0 +1,34 @@ +import { log } from '../command-helpers.js' +import { EnvVar } from '../types.js' + +import { confirmPrompt } from './confirm-prompt.js' +import { destructiveCommandMessages } from './prompt-messages.js' + +export const generateEnvVarsList = (envVarsToDelete: EnvVar[]) => envVarsToDelete.map((envVar) => envVar.key) + +/** + * Prompts the user to confirm overwriting environment variables on a site. + * + * @param {string} siteId - The ID of the site. + * @param {EnvVar[]} existingEnvVars - The environment variables that already exist on the site. + * @returns {Promise} A promise that resolves when the user has confirmed the overwriting of the variables. + */ +export async function promptEnvCloneOverwrite(siteId: string, existingEnvVars: EnvVar[]): Promise { + const { generateWarning } = destructiveCommandMessages.envClone + + const existingEnvVarKeys = generateEnvVarsList(existingEnvVars) + const warningMessage = generateWarning(siteId) + + log() + log(warningMessage) + log() + log(destructiveCommandMessages.envClone.noticeEnvVars) + log() + existingEnvVarKeys.forEach((envVar) => { + log(envVar) + }) + log() + log(destructiveCommandMessages.overwriteNotice) + + await confirmPrompt(destructiveCommandMessages.envClone.overwriteConfirmation) +} diff --git a/src/utils/prompts/env-set-prompts.ts b/src/utils/prompts/env-set-prompts.ts new file mode 100644 index 00000000000..981867bced7 --- /dev/null +++ b/src/utils/prompts/env-set-prompts.ts @@ -0,0 +1,14 @@ +import { log } from '../command-helpers.js' + +import { confirmPrompt } from './confirm-prompt.js' +import { destructiveCommandMessages } from './prompt-messages.js' + +export const promptOverwriteEnvVariable = async (key: string): Promise => { + const warningMessage = destructiveCommandMessages.envSet.generateWarning(key) + + log() + log(warningMessage) + log() + log(destructiveCommandMessages.overwriteNotice) + await confirmPrompt(destructiveCommandMessages.envSet.overwriteConfirmation) +} diff --git a/src/utils/prompts/env-unset-prompts.ts b/src/utils/prompts/env-unset-prompts.ts new file mode 100644 index 00000000000..73af7886889 --- /dev/null +++ b/src/utils/prompts/env-unset-prompts.ts @@ -0,0 +1,21 @@ +import { log } from '../command-helpers.js' + +import { confirmPrompt } from './confirm-prompt.js' +import { destructiveCommandMessages } from './prompt-messages.js' + +/** + * Logs a warning and prompts user to confirm overwriting an existing environment variable + * + * @param {string} key - The key of the environment variable that already exists + * @returns {Promise} A promise that resolves when the user has confirmed overwriting the variable + */ +export const promptOverwriteEnvVariable = async (existingKey: string): Promise => { + const { generateWarning } = destructiveCommandMessages.envUnset + + const warningMessage = generateWarning(existingKey) + + log(warningMessage) + log() + log(destructiveCommandMessages.overwriteNotice) + await confirmPrompt(destructiveCommandMessages.envUnset.overwriteConfirmation) +} diff --git a/src/utils/prompts/prompt-messages.ts b/src/utils/prompts/prompt-messages.ts new file mode 100644 index 00000000000..29014136499 --- /dev/null +++ b/src/utils/prompts/prompt-messages.ts @@ -0,0 +1,46 @@ +import { chalk } from '../command-helpers.js' + +export const destructiveCommandMessages = { + overwriteNotice: `${chalk.yellowBright('Notice')}: To overwrite without this warning, you can use the --force flag.`, + + blobSet: { + generateWarning: (key: string, storeName: string) => + `${chalk.redBright('Warning')}: The blob key ${chalk.cyan(key)} already exists in store ${chalk.cyan( + storeName, + )}!`, + overwriteConfirmation: 'Do you want to proceed with overwriting this blob key existing value?', + }, + + blobDelete: { + generateWarning: (key: string, storeName: string) => + `${chalk.redBright('Warning')}: The following blob key ${chalk.cyan(key)} will be deleted from store ${chalk.cyan( + storeName, + )}!`, + overwriteConfirmation: 'Do you want to proceed with deleting the value at this key?', + }, + + envSet: { + generateWarning: (variableName: string) => + `${chalk.redBright('Warning')}: The environment variable ${chalk.bgBlueBright(variableName)} already exists!`, + overwriteConfirmation: 'Do you want to overwrite it?', + }, + + envUnset: { + generateWarning: (variableName: string) => + `${chalk.redBright('Warning')}: The environment variable ${chalk.bgBlueBright( + variableName, + )} will be removed from all contexts!`, + overwriteConfirmation: 'Do you want to remove it?', + }, + + envClone: { + generateWarning: (siteId: string) => + `${chalk.redBright( + 'Warning', + )}: The following environment variables are already set on the site with ID ${chalk.bgBlueBright( + siteId, + )}. They will be overwritten!`, + noticeEnvVars: `${chalk.yellowBright('Notice')}: The following variables will be overwritten:`, + overwriteConfirmation: 'The environment variables already exist. Do you want to overwrite them?', + }, +} diff --git a/src/utils/run-program.ts b/src/utils/run-program.ts new file mode 100644 index 00000000000..eadbbc940be --- /dev/null +++ b/src/utils/run-program.ts @@ -0,0 +1,16 @@ +import { injectForceFlagIfScripted } from './scripted-commands.js' +import { BaseCommand } from '../commands/index.js' +import { CI_FORCED_COMMANDS } from '../commands/main.js' + +// This function is used to run the program with the correct flags +export const runProgram = async (program: BaseCommand, argv: string[]) => { + const cmdName = argv[2] + // checks if the command has a force option + const isValidForceCommand = cmdName in CI_FORCED_COMMANDS + + if (isValidForceCommand) { + injectForceFlagIfScripted(argv) + } + + await program.parseAsync(argv) +} diff --git a/src/utils/scripted-commands.ts b/src/utils/scripted-commands.ts new file mode 100644 index 00000000000..d9bbf29d6da --- /dev/null +++ b/src/utils/scripted-commands.ts @@ -0,0 +1,22 @@ +import process from 'process' +import { isCI } from 'ci-info' + +export const shouldForceFlagBeInjected = (argv: string[]): boolean => { + // Is the command run in a non-interactive shell or CI/CD environment? + const scriptedCommand = Boolean(!process.stdin.isTTY || isCI || process.env.CI) + + // Is the `--force` flag not already present? + const noForceFlag = !argv.includes('--force') + + // ENV Variable used to tests prompts in CI/CD enviroment + const testingPrompts = process.env.TESTING_PROMPTS !== 'true' + + // Prevents prompts from blocking scripted commands + return Boolean(scriptedCommand && testingPrompts && noForceFlag) +} + +export const injectForceFlagIfScripted = (argv: string[]) => { + if (shouldForceFlagBeInjected(argv)) { + argv.push('--force') + } +} diff --git a/src/utils/sites/create-template.ts b/src/utils/sites/create-template.ts new file mode 100644 index 00000000000..5fcd4857263 --- /dev/null +++ b/src/utils/sites/create-template.ts @@ -0,0 +1,70 @@ +import { OptionValues } from 'commander' +import inquirer from 'inquirer' +// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'pars... Remove this comment to see the full error message +import parseGitHubUrl from 'parse-github-url' + +import { log } from '../command-helpers.js' +import { Template, GitHubRepo } from '../types.js' + +import { getTemplatesFromGitHub } from './utils.js' + +export const fetchTemplates = async (token: string): Promise => { + const templatesFromGitHubOrg: GitHubRepo[] = await getTemplatesFromGitHub(token) + + return templatesFromGitHubOrg + .filter((repo: GitHubRepo) => !repo.archived && !repo.disabled) + .map((template: GitHubRepo) => ({ + name: template.name, + sourceCodeUrl: template.html_url, + slug: template.full_name, + })) +} + +export const getTemplateName = async ({ + ghToken, + options, + repository, +}: { + ghToken: string + options: OptionValues + repository: string +}) => { + if (repository) { + const { repo } = parseGitHubUrl(repository) + return repo || `netlify-templates/${repository}` + } + + if (options.url) { + const urlFromOptions = new URL(options.url) + return urlFromOptions.pathname.slice(1) + } + + const templates = await fetchTemplates(ghToken) + + log(`Choose one of our starter templates. Netlify will create a new repo for this template in your GitHub account.`) + + const { templateName } = await inquirer.prompt([ + { + type: 'list', + name: 'templateName', + message: 'Template:', + choices: templates.map((template) => ({ + value: template.slug, + name: template.name, + })), + }, + ]) + + return templateName +} + +export const deployedSiteExists = async (name: string): Promise => { + const resp = await fetch(`https://${name}.netlify.app`, { + method: 'GET', + }) + + return resp.status === 200 +} + +export const getGitHubLink = ({ options, templateName }: { options: OptionValues; templateName: string }) => + options.url || `https://github.com/${templateName}` diff --git a/src/utils/sites/utils.ts b/src/utils/sites/utils.ts index 79c980a2e45..4f9a7c7218d 100644 --- a/src/utils/sites/utils.ts +++ b/src/utils/sites/utils.ts @@ -1,7 +1,9 @@ import fetch from 'node-fetch' -// @ts-expect-error TS(7006) FIXME: Parameter 'token' implicitly has an 'any' type. -export const getTemplatesFromGitHub = async (token) => { +import { log, GitHubRepoResponse, error } from '../command-helpers.js' +import { GitHubRepo, Template } from '../types.js' + +export const getTemplatesFromGitHub = async (token: string): Promise => { const getPublicGitHubReposFromOrg = new URL(`https://api.github.com/orgs/netlify-templates/repos`) // GitHub returns 30 by default and we want to avoid our limit // due to our archived repositories at any given time @@ -12,19 +14,22 @@ export const getTemplatesFromGitHub = async (token) => { // @ts-expect-error TS(2345) FIXME: Argument of type 'number' is not assignable to par... Remove this comment to see the full error message getPublicGitHubReposFromOrg.searchParams.set('per_page', REPOS_PER_PAGE) - const templates = await fetch(getPublicGitHubReposFromOrg, { - method: 'GET', - headers: { - Authorization: `token ${token}`, - }, - }) - const allTemplates = await templates.json() - + let allTemplates: GitHubRepo[] = [] + try { + const templates = await fetch(getPublicGitHubReposFromOrg, { + method: 'GET', + headers: { + Authorization: `token ${token}`, + }, + }) + allTemplates = (await templates.json()) as GitHubRepo[] + } catch (error_) { + error(error_) + } return allTemplates } -// @ts-expect-error TS(7031) FIXME: Binding element 'ghToken' implicitly has an 'any' ... Remove this comment to see the full error message -export const validateTemplate = async ({ ghToken, templateName }) => { +export const validateTemplate = async ({ ghToken, templateName }: { ghToken: string; templateName: string }) => { const response = await fetch(`https://api.github.com/repos/${templateName}`, { headers: { Authorization: `token ${ghToken}`, @@ -39,13 +44,16 @@ export const validateTemplate = async ({ ghToken, templateName }) => { throw new Error(`Error fetching template ${templateName}: ${await response.text()}`) } - const data = await response.json() + const data = (await response.json()) as GitHubRepoResponse - // @ts-expect-error TS(18046) - 'data' is of type 'unknown' return { exists: true, isTemplate: data.is_template } } -export const createRepo = async (templateName: string, ghToken: string, siteName: string) => { +export const createRepo = async ( + templateName: string, + ghToken: string, + siteName: string, +): Promise => { const resp = await fetch(`https://api.github.com/repos/${templateName}/generate`, { method: 'POST', headers: { @@ -58,5 +66,5 @@ export const createRepo = async (templateName: string, ghToken: string, siteName const data = await resp.json() - return data + return data as GitHubRepoResponse } diff --git a/src/utils/types.ts b/src/utils/types.ts index 95f1b5266ba..f424abcce82 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -62,3 +62,145 @@ export interface Request extends IncomingMessage { } export type Rewriter = (req: Request) => Match | null + +export interface SiteInfo { + account_name: string + account_slug: string + admin_url: string + build_settings: { + allowed_branches: string[] + cmd: string + deploy_key_id: string + dir: string + env?: Record + id: number + private_logs: boolean + provider: string + public_repo: boolean + repo_branch: string + repo_path: string + repo_url: string + } + capabilities: Record + created_at: string + custom_domain: string + deploy_hook: string + deploy_url: string + domain_aliases: string[] + force_ssl: boolean + git_provider: string + id: string + managed_dns: boolean + name: string + notification_email: string + password: string + plan: string + processing_settings: { + css: { + bundle: boolean + minify: boolean + } + html: Record + images: Record + js: { + bundle: boolean + minify: boolean + } + skip: boolean + } + published_deploy: { + admin_url: string + branch: string + build_id: string + commit_ref: string + commit_url: string + context: string + created_at: string + deploy_ssl_url: string + deploy_url: string + draft: boolean + error_message: string + id: string + locked: boolean + name: string + published_at: string + required: string[] + required_functions: string[] + review_id: number + review_url: string + screenshot_url: string + site_id: string + skipped: boolean + ssl_url: string + state: string + title: string + updated_at: string + url: string + user_id: string + } + screenshot_url: string + session_id: string + ssl: boolean + ssl_url: string + state: string + updated_at: string + url: string + user_id: string +} + +export type TokenLocation = 'env' | 'flag' | 'config' | 'not found' + +export type EnvVar = { + key: string + scopes: string[] + values: EnvVarValue[] + updated_at: string + is_secret: boolean +} + +type EnvVarValue = { + id: string + context: string +} + +export interface Account { + id: string + name: string + slug: string + type: string + capabilities: { + sites: { + included: number + used: number + } + collaborators: { + included: number + used: number + } + } + billing_name: string + billing_email: string + billing_details: string + billing_period: string + payment_method_id: string + type_name: string + type_id: string + owner_ids: string[] + roles_allowed: string[] + created_at: string + updated_at: string +} + +export interface GitHubRepo { + name: string + html_url: string + full_name: string + archived: boolean + disabled: boolean +} + +export interface Template { + name: string + sourceCodeUrl: string + slug: string +} diff --git a/tests/integration/commands/addons/addons.test.js b/tests/integration/commands/addons/addons.test.js index ff9f17b31e0..ad8c7ee2f80 100644 --- a/tests/integration/commands/addons/addons.test.js +++ b/tests/integration/commands/addons/addons.test.js @@ -150,7 +150,7 @@ describe.concurrent('command-addons', () => { ] await withMockApi(deleteRoutes, async ({ apiUrl }) => { - const cliResponse = await callCli(['addons:delete', 'demo', '-f'], getCLIOptions({ builder, apiUrl })) + const cliResponse = await callCli(['addons:delete', 'demo', '--force'], getCLIOptions({ builder, apiUrl })) t.expect(cliResponse.includes('Addon "demo" deleted')).toBe(true) }) }) diff --git a/tests/integration/commands/blobs/blobs-delete.test.ts b/tests/integration/commands/blobs/blobs-delete.test.ts new file mode 100644 index 00000000000..c0c347d3624 --- /dev/null +++ b/tests/integration/commands/blobs/blobs-delete.test.ts @@ -0,0 +1,249 @@ +import process from 'process' + +import { getStore } from '@netlify/blobs' +import chalk from 'chalk' +import { describe, expect, test, vi, beforeEach, afterAll } from 'vitest' + +import { log } from '../../../../src/utils/command-helpers.js' +import { destructiveCommandMessages } from '../.././../../src/utils/prompts/prompt-messages.js' +import { reportError } from '../../../../src/utils/telemetry/report-error.js' +import { Route } from '../../utils/mock-api-vitest.js' +import { getEnvironmentVariables, withMockApi, setTTYMode, setCI, setTestingPrompts } from '../../utils/mock-api.js' +import { runMockProgram } from '../../utils/mock-program.js' +import { mockPrompt, spyOnMockPrompt } from '../../utils/inquirer-mock-prompt.js' + +const siteInfo = { + account_slug: 'test-account', + id: 'site_id', + name: 'site-name', + feature_flags: { + edge_functions_npm_support: true, + }, + functions_config: { timeout: 1 }, +} + +vi.mock('../../../../src/utils/command-helpers.js', async () => ({ + ...(await vi.importActual('../../../../src/utils/command-helpers.js')), + log: vi.fn(), + printError: vi.fn(), +})) + +vi.mock('@netlify/blobs', () => ({ + getStore: vi.fn(), +})) + +vi.mock('../../../../src/utils/telemetry/report-error.js', () => ({ + reportError: vi.fn(), +})) + +const routes: Route[] = [ + { path: 'sites/site_id', response: siteInfo }, + + { path: 'sites/site_id/service-instances', response: [] }, + { + path: 'accounts', + response: [{ slug: siteInfo.account_slug }], + }, +] + +const OLD_ENV = process.env + +describe('blobs:delete command', () => { + describe('prompt messages for blobs:delete command', () => { + const storeName = 'my-store' + const key = 'my-key' + + const { overwriteNotice } = destructiveCommandMessages + const { generateWarning, overwriteConfirmation } = destructiveCommandMessages.blobDelete + + const warningMessage = generateWarning(key, storeName) + + const successMessage = `${chalk.greenBright('Success')}: Blob ${chalk.yellow( + key, + )} deleted from store ${chalk.yellow(storeName)}` + + beforeEach(() => { + vi.resetModules() + vi.clearAllMocks() + Object.defineProperty(process, 'env', { value: {} }) + }) + + afterAll(() => { + vi.resetModules() + vi.restoreAllMocks() + + Object.defineProperty(process, 'env', { + value: OLD_ENV, + }) + }) + + describe('user is prompted to confirm when deleting a blob key', () => { + beforeEach(() => { + setTestingPrompts('true') + }) + + test('should log warning message and prompt for confirmation', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const mockDelete = vi.fn().mockResolvedValue('true') + + ;(getStore as any).mockReturnValue({ + delete: mockDelete, + }) + + const promptSpy = mockPrompt({ confirm: true }) + + await runMockProgram(['', '', 'blobs:delete', storeName, key]) + + expect(promptSpy).toHaveBeenCalledWith({ + type: 'confirm', + name: 'confirm', + message: expect.stringContaining(overwriteConfirmation), + default: false, + }) + + expect(log).toHaveBeenCalledWith(warningMessage) + expect(log).toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + + test('should exit if user responds with no to confirmation prompt', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const mockDelete = vi.fn().mockResolvedValue('true') + + ;(getStore as any).mockReturnValue({ + delete: mockDelete, + }) + + const promptSpy = mockPrompt({ confirm: false }) + + try { + await runMockProgram(['', '', 'blobs:delete', storeName, key]) + } catch (error) { + // We expect the process to exit, so this is fine + expect(error.message).toContain('process.exit unexpectedly called') + } + + expect(promptSpy).toHaveBeenCalledWith({ + type: 'confirm', + name: 'confirm', + message: expect.stringContaining(overwriteConfirmation), + default: false, + }) + + expect(log).toHaveBeenCalledWith(warningMessage) + expect(log).toHaveBeenCalledWith(overwriteNotice) + expect(log).not.toHaveBeenCalledWith(successMessage) + }) + }) + + test('should not log warning message and prompt for confirmation if --force flag is passed', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const mockDelete = vi.fn().mockResolvedValue('true') + + ;(getStore as any).mockReturnValue({ + delete: mockDelete, + }) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'blobs:delete', storeName, key, '--force']) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + + test('should log error message if delete fails', async () => { + try { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + vi.mocked(reportError).mockResolvedValue() + + const mockDelete = vi.fn().mockRejectedValue(new Error('Could not delete blob')) + + ;(getStore as any).mockReturnValue({ + delete: mockDelete, + }) + + const promptSpy = spyOnMockPrompt() + + try { + await runMockProgram(['', '', 'blobs:delete', storeName, key, '--force']) + } catch (error) { + expect(error.message).toContain( + `Could not delete blob ${chalk.yellow(key)} from store ${chalk.yellow(storeName)}`, + ) + } + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).not.toHaveBeenCalledWith(successMessage) + }) + } catch (error) { + console.error(error) + } + }) + }) + + describe('should not show prompts if in non-interactive shell or CI/CD', () => { + test('should not show prompt for non-interactive shell', async () => { + setTTYMode(false) + + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const mockDelete = vi.fn().mockResolvedValue('true') + + ;(getStore as any).mockReturnValue({ + delete: mockDelete, + }) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'blobs:delete', storeName, key]) + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + + test('should not show prompt for CI/CD', async () => { + setCI(true) + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const mockDelete = vi.fn().mockResolvedValue('true') + + ;(getStore as any).mockReturnValue({ + delete: mockDelete, + }) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'blobs:delete', storeName, key]) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + }) + }) +}) diff --git a/tests/integration/commands/blobs/blobs-set.test.ts b/tests/integration/commands/blobs/blobs-set.test.ts new file mode 100644 index 00000000000..ba30fb98b3f --- /dev/null +++ b/tests/integration/commands/blobs/blobs-set.test.ts @@ -0,0 +1,284 @@ +import process from 'process' + +import { getStore } from '@netlify/blobs' +import chalk from 'chalk' +import inquirer from 'inquirer' +import { describe, expect, test, vi, beforeEach, afterAll } from 'vitest' + +import { log } from '../../../../src/utils/command-helpers.js' +import { destructiveCommandMessages } from '../.././../../src/utils/prompts/prompt-messages.js' +import { reportError } from '../../../../src/utils/telemetry/report-error.js' +import { Route } from '../../utils/mock-api-vitest.js' +import { getEnvironmentVariables, withMockApi, setTTYMode, setCI, setTestingPrompts } from '../../utils/mock-api.js' +import { runMockProgram } from '../../utils/mock-program.js' +import { mockPrompt, spyOnMockPrompt } from '../../utils/inquirer-mock-prompt.js' + +const siteInfo = { + account_slug: 'test-account', + id: 'site_id', + name: 'site-name', + feature_flags: { + edge_functions_npm_support: true, + }, + functions_config: { timeout: 1 }, +} + +vi.mock('../../../../src/utils/command-helpers.js', async () => ({ + ...(await vi.importActual('../../../../src/utils/command-helpers.js')), + log: vi.fn(), +})) + +vi.mock('@netlify/blobs', () => ({ + getStore: vi.fn(), +})) + +vi.mock('../../../../src/utils/telemetry/report-error.js', () => ({ + reportError: vi.fn(), +})) + +const routes: Route[] = [ + { path: 'sites/site_id', response: siteInfo }, + + { path: 'sites/site_id/service-instances', response: [] }, + { + path: 'accounts', + response: [{ slug: siteInfo.account_slug }], + }, +] + +const OLD_ENV = process.env + +describe('blobs:set command', () => { + describe('prompt messages for blobs:set command', () => { + const storeName = 'my-store' + const key = 'my-key' + const value = 'my-value' + const newValue = 'my-new-value' + + const { overwriteNotice } = destructiveCommandMessages + const { generateWarning, overwriteConfirmation } = destructiveCommandMessages.blobSet + + const warningMessage = generateWarning(key, storeName) + + const successMessage = `${chalk.greenBright('Success')}: Blob ${chalk.yellow(key)} set in store ${chalk.yellow( + storeName, + )}` + + beforeEach(() => { + vi.resetModules() + vi.clearAllMocks() + + Object.defineProperty(process, 'env', { value: {} }) + }) + + afterAll(() => { + vi.resetModules() + vi.restoreAllMocks() + + Object.defineProperty(process, 'env', { + value: OLD_ENV, + }) + }) + + describe('user is prompted to confirm when setting a a blob key that already exists', () => { + beforeEach(() => { + setTestingPrompts('true') + }) + + test('should not log warnings and prompt if blob key does not exist', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const mockGet = vi.fn().mockResolvedValue('') + const mockSet = vi.fn().mockResolvedValue('true') + + ;(getStore as any).mockReturnValue({ + get: mockGet, + set: mockSet, + }) + + const promptSpy = vi.spyOn(inquirer, 'prompt').mockResolvedValue({ wantsToSet: true }) + + await runMockProgram(['', '', 'blobs:set', storeName, key, value]) + + expect(promptSpy).not.toHaveBeenCalled() + expect(log).toHaveBeenCalledWith(successMessage) + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + }) + }) + + test('should log warnings and prompt if blob key already exists', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + // Mocking the store.get method to return a value (simulating that the key already exists) + const mockGet = vi.fn().mockResolvedValue(value) + const mockSet = vi.fn().mockResolvedValue('true') + + ;(getStore as any).mockReturnValue({ + get: mockGet, + set: mockSet, + }) + + const promptSpy = mockPrompt({ confirm: true }) + + await runMockProgram(['', '', 'blobs:set', storeName, key, newValue]) + + expect(promptSpy).toHaveBeenCalledWith({ + type: 'confirm', + name: 'confirm', + message: expect.stringContaining(overwriteConfirmation), + default: false, + }) + + expect(log).toHaveBeenCalledWith(successMessage) + expect(log).toHaveBeenCalledWith(warningMessage) + expect(log).toHaveBeenCalledWith(overwriteNotice) + }) + }) + + test('should exit if user responds with no to confirmation prompt', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + // Mocking the store.get method to return a value (simulating that the key already exists) + const mockGet = vi.fn().mockResolvedValue('my-value') + const mockSet = vi.fn().mockResolvedValue('true') + + ;(getStore as any).mockReturnValue({ + get: mockGet, + set: mockSet, + }) + + const promptSpy = mockPrompt({ confirm: false }) + + try { + await runMockProgram(['', '', 'blobs:set', storeName, key, newValue]) + } catch (error) { + // We expect the process to exit, so this is fine + expect(error.message).toContain('process.exit unexpectedly called with "0"') + } + + expect(promptSpy).toHaveBeenCalledWith({ + type: 'confirm', + name: 'confirm', + message: expect.stringContaining(overwriteConfirmation), + default: false, + }) + + expect(log).toHaveBeenCalledWith(warningMessage) + expect(log).toHaveBeenCalledWith(overwriteNotice) + expect(log).not.toHaveBeenCalledWith(successMessage) + }) + }) + + test('should not log warnings and prompt if blob key already exists and --force flag is passed', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + // Mocking the store.get method to return a value (simulating that the key already exists) + const mockGet = vi.fn().mockResolvedValue('my-value') + const mockSet = vi.fn().mockResolvedValue('true') + + ;(getStore as any).mockReturnValue({ + get: mockGet, + set: mockSet, + }) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'blobs:set', storeName, key, newValue, '--force']) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + + test('should log error message if adding a key fails', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const mockSet = vi.fn().mockRejectedValue('') + vi.mocked(reportError).mockResolvedValue() + ;(getStore as any).mockReturnValue({ + set: mockSet, + }) + + const promptSpy = spyOnMockPrompt() + + try { + await runMockProgram(['', '', 'blobs:set', storeName, key, newValue, '--force']) + } catch (error) { + expect(error.message).toContain( + `Could not set blob ${chalk.yellow(key)} in store ${chalk.yellow(storeName)}`, + ) + } + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).not.toHaveBeenCalledWith(successMessage) + }) + }) + }) + + describe('prompts should not show in a non-interactive shell or in a ci/cd enviroment', () => { + test('should not show prompt in an non-interactive shell', async () => { + setTTYMode(false) + + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + // Mocking the store.get method to return a value (simulating that the key already exists) + const mockGet = vi.fn().mockResolvedValue('my-value') + const mockSet = vi.fn().mockResolvedValue('true') + + ;(getStore as any).mockReturnValue({ + get: mockGet, + set: mockSet, + }) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'blobs:set', storeName, key, newValue]) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + + test('should not show prompt in a ci/cd environment', async () => { + setCI(true) + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + // Mocking the store.get method to return a value (simulating that the key already exists) + const mockGet = vi.fn().mockResolvedValue('my-value') + const mockSet = vi.fn().mockResolvedValue('true') + + ;(getStore as any).mockReturnValue({ + get: mockGet, + set: mockSet, + }) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'blobs:set', storeName, key, newValue]) + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + }) + }) +}) diff --git a/tests/integration/commands/blobs/blobs.test.ts b/tests/integration/commands/blobs/blobs.test.ts index 80491b72b14..493a01c8f01 100644 --- a/tests/integration/commands/blobs/blobs.test.ts +++ b/tests/integration/commands/blobs/blobs.test.ts @@ -64,12 +64,14 @@ describe('blobs:* commands', () => { }) setupFixtureTests('empty-project', { mockApi: { routes } }, () => { + const expectedSucusesMessage = 'Success: Blob my-key set in store my-store' + test('should set, get, list, and delete blobs', async ({ fixture }) => { expect( - await fixture.callCli(['blobs:set', 'my-store', 'my-key', 'Hello world'], { + await fixture.callCli(['blobs:set', 'my-store', 'my-key', 'Hello world', '--force'], { offline: false, }), - ).toBe('') + ).toBe(expectedSucusesMessage) expect( await fixture.callCli(['blobs:get', 'my-store', 'my-key'], { @@ -98,10 +100,10 @@ describe('blobs:* commands', () => { expect(listResult.directories).toEqual([]) expect( - await fixture.callCli(['blobs:delete', 'my-store', 'my-key'], { + await fixture.callCli(['blobs:delete', 'my-store', 'my-key', '--force'], { offline: false, }), - ).toBe('') + ).toBe('Success: Blob my-key deleted from store my-store') expect( await fixture.callCli(['blobs:list', 'my-store', '--json'], { diff --git a/tests/integration/commands/completion/completion-install.test.ts b/tests/integration/commands/completion/completion-install.test.ts new file mode 100644 index 00000000000..0266cf7b71a --- /dev/null +++ b/tests/integration/commands/completion/completion-install.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, test, beforeAll, afterAll } from 'vitest' +import fs from 'fs' +import { rm } from 'fs/promises' +import { temporaryDirectory } from 'tempy' +import { handleQuestions, CONFIRM, DOWN, NO, answerWithValue } from '../../utils/handle-questions.js' +import execa from 'execa' +import { cliPath } from '../../utils/cli-path.js' +import { join } from 'path' +import { TABTAB_CONFIG_LINE, AUTOLOAD_COMPINIT } from '../../../../src/utils/command-helpers.js' + +describe('completion:install command', () => { + let tempDir + let zshConfigPath + let options + + beforeAll(() => { + tempDir = temporaryDirectory() + zshConfigPath = join(tempDir, '.zshrc') + options = { cwd: tempDir, env: { HOME: tempDir } } + }) + + afterAll(async () => { + await rm(tempDir, { force: true, recursive: true }) + }) + + test.skipIf(process.env.SHELL !== '/bin/zsh')( + 'should add compinit to .zshrc when user confirms prompt', + async (t) => { + fs.writeFileSync(zshConfigPath, TABTAB_CONFIG_LINE) + const childProcess = execa(cliPath, ['completion:install'], options) + + handleQuestions(childProcess, [ + { + question: 'Which Shell do you use ?', + answer: answerWithValue(DOWN), + }, + { + question: 'We will install completion to ~/.zshrc, is it ok ?', + answer: CONFIRM, + }, + { + question: 'Would you like to add it?', + answer: CONFIRM, + }, + ]) + + await childProcess + const content = fs.readFileSync(zshConfigPath, 'utf8') + expect(content).toContain(AUTOLOAD_COMPINIT) + }, + ) + + test.skipIf(process.env.SHELL !== '/bin/zsh')( + 'should not add compinit to .zshrc when user does not confirm prompt', + async (t) => { + fs.writeFileSync(zshConfigPath, TABTAB_CONFIG_LINE) + const childProcess = execa(cliPath, ['completion:install'], options) + + handleQuestions(childProcess, [ + { + question: 'Which Shell do you use ?', + answer: answerWithValue(DOWN), + }, + { + question: 'We will install completion to ~/.zshrc, is it ok ?', + answer: CONFIRM, + }, + { + question: 'Would you like to add it?', + answer: answerWithValue(NO), + }, + ]) + + await childProcess + const content = fs.readFileSync(zshConfigPath, 'utf8') + expect(content).not.toContain(AUTOLOAD_COMPINIT) + }, + ) +}) diff --git a/tests/integration/commands/dev/dev.exec.test.js b/tests/integration/commands/dev/dev.exec.test.js index 068c480d079..42a8c0834eb 100644 --- a/tests/integration/commands/dev/dev.exec.test.js +++ b/tests/integration/commands/dev/dev.exec.test.js @@ -5,7 +5,7 @@ import { test } from 'vitest' import { callCli } from '../../utils/call-cli.js' import { getCLIOptions, withMockApi } from '../../utils/mock-api.js' import { withSiteBuilder } from '../../utils/site-builder.ts' -import routes from '../env/api-routes.ts' +import { routes } from '../env/api-routes.ts' test('should pass .env variables to exec command', async (t) => { await withSiteBuilder(t, async (builder) => { diff --git a/tests/integration/commands/dev/v2-api.test.ts b/tests/integration/commands/dev/v2-api.test.ts index 931acf10975..33267d1434b 100644 --- a/tests/integration/commands/dev/v2-api.test.ts +++ b/tests/integration/commands/dev/v2-api.test.ts @@ -66,7 +66,7 @@ describe.runIf(gte(version, '18.13.0')).concurrent('v2 api', () => { expect(context.requestId).toEqual(response.headers.get('x-nf-request-id')) expect(context.site.url).toEqual(`http://localhost:${devServer.port}`) expect(context.server.region).toEqual('dev') - expect(context.ip).toEqual('::1') + expect(['::1', '127.0.0.1'].includes(context.ip)).toBe(true) expect(context.geo.city).toEqual('Mock City') expect(context.cookies).toEqual({ foo: 'bar' }) expect(context.account.id).toEqual('mock-account-id') diff --git a/tests/integration/commands/env/api-routes.ts b/tests/integration/commands/env/api-routes.ts index 83bb95d00d8..4b35e358a03 100644 --- a/tests/integration/commands/env/api-routes.ts +++ b/tests/integration/commands/env/api-routes.ts @@ -8,7 +8,26 @@ const siteInfo = { id: 'site_id', name: 'site-name', } -const existingVar = { + +export const secondSiteInfo = { + account_slug: 'test-account-2', + build_settings: { + env: {}, + }, + id: 'site_id_2', + name: 'site-name-2', +} + +const thirdSiteInfo = { + account_slug: 'test-account-3', + build_settings: { + env: {}, + }, + id: 'site_id_3', + name: 'site-name-3', +} + +export const existingVar = { key: 'EXISTING_VAR', scopes: ['builds', 'functions'], values: [ @@ -23,7 +42,10 @@ const existingVar = { value: 'envelope-dev-value', }, ], + updated_at: '2020-01-01T00:00:00Z', + is_secret: false, } + const otherVar = { key: 'OTHER_VAR', scopes: ['builds', 'functions', 'runtime', 'post_processing'], @@ -35,9 +57,14 @@ const otherVar = { }, ], } + const response = [existingVar, otherVar] -const routes = [ +const secondSiteResponse = [existingVar] + +export const routes = [ { path: 'sites/site_id', response: siteInfo }, + { path: 'sites/site_id_2', response: secondSiteInfo }, + { path: 'sites/site_id_3', response: thirdSiteInfo }, { path: 'sites/site_id/service-instances', response: [] }, { path: 'accounts', @@ -59,11 +86,33 @@ const routes = [ path: 'accounts/test-account/env', response, }, + { + path: 'accounts/test-account-2/env/EXISTING_VAR', + response: existingVar, + }, + { + path: 'accounts/test-account-2/env', + response: secondSiteResponse, + }, + { + path: 'accounts/test-account-3/env', + response: [{}], + }, { path: 'accounts/test-account/env', method: HTTPMethod.POST, response: {}, }, + { + path: 'accounts/test-account-2/env', + method: HTTPMethod.POST, + response: {}, + }, + { + path: 'accounts/test-account-3/env', + method: HTTPMethod.POST, + response: {}, + }, { path: 'accounts/test-account/env/EXISTING_VAR', method: HTTPMethod.PUT, @@ -79,6 +128,16 @@ const routes = [ method: HTTPMethod.DELETE, response: {}, }, + { + path: 'accounts/test-account-2/env/EXISTING_VAR', + method: HTTPMethod.DELETE, + response: {}, + }, + { + path: 'accounts/test-account-3/env/EXISTING_VAR', + method: HTTPMethod.DELETE, + response: {}, + }, { path: 'accounts/test-account/env/EXISTING_VAR/value/1234', method: HTTPMethod.DELETE, diff --git a/tests/integration/commands/env/env-clone.test.ts b/tests/integration/commands/env/env-clone.test.ts new file mode 100644 index 00000000000..42c1d33cafb --- /dev/null +++ b/tests/integration/commands/env/env-clone.test.ts @@ -0,0 +1,190 @@ +import process from 'process' + +import chalk from 'chalk' +import { describe, expect, test, vi, beforeEach, afterEach, beforeAll, afterAll } from 'vitest' + +import { log } from '../../../../src/utils/command-helpers.js' +import { generateEnvVarsList } from '../.././../../src/utils/prompts/env-clone-prompt.js' +import { destructiveCommandMessages } from '../.././../../src/utils/prompts/prompt-messages.js' +import { getEnvironmentVariables, withMockApi, setTTYMode, setCI, setTestingPrompts } from '../../utils/mock-api.js' + +import { existingVar, routes, secondSiteInfo } from './api-routes.js' +import { runMockProgram } from '../../utils/mock-program.js' +import { mockPrompt, spyOnMockPrompt } from '../../utils/inquirer-mock-prompt.js' + +vi.mock('../../../../src/utils/command-helpers.js', async () => ({ + ...(await vi.importActual('../../../../src/utils/command-helpers.js')), + log: vi.fn(), +})) + +const OLD_ENV = process.env + +describe('env:clone command', () => { + describe('prompt messages for env:clone', () => { + const sharedEnvVars = [existingVar, existingVar] + const siteIdTwo = secondSiteInfo.id + + const { overwriteNotice } = destructiveCommandMessages + const { generateWarning, noticeEnvVars, overwriteConfirmation } = destructiveCommandMessages.envClone + + const envVarsList = generateEnvVarsList(sharedEnvVars) + const warningMessage = generateWarning(siteIdTwo) + + const successMessage = `Successfully cloned environment variables from ${chalk.green('site-name')} to ${chalk.green( + 'site-name-2', + )}` + + beforeEach(() => { + vi.resetModules() + vi.clearAllMocks() + + Object.defineProperty(process, 'env', { value: {} }) + }) + + afterAll(() => { + vi.resetModules() + vi.restoreAllMocks() + + Object.defineProperty(process, 'env', { + value: OLD_ENV, + }) + }) + + describe('user is prompted to confirm when setting an env var that already exists', () => { + beforeEach(() => { + setTestingPrompts('true') + }) + + test('should log warnings and prompts if enviroment variable already exists', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = mockPrompt({ confirm: true }) + + await runMockProgram(['', '', 'env:clone', '-t', siteIdTwo]) + + expect(promptSpy).toHaveBeenCalledWith({ + type: 'confirm', + name: 'confirm', + message: expect.stringContaining(overwriteConfirmation), + default: false, + }) + + expect(log).toHaveBeenCalledWith(warningMessage) + expect(log).toHaveBeenCalledWith(noticeEnvVars) + envVarsList.forEach((envVar) => { + expect(log).toHaveBeenCalledWith(envVar) + }) + expect(log).toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + + test('should skip warnings and prompts if --force flag is passed', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'env:clone', '--force', '-t', siteIdTwo]) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + envVarsList.forEach((envVar) => { + expect(log).not.toHaveBeenCalledWith(envVar) + }) + expect(log).not.toHaveBeenCalledWith(noticeEnvVars) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + + test('should exit user reponds is no to confirmatnion prompt', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = mockPrompt({ confirm: false }) + + try { + await runMockProgram(['', '', 'env:clone', '-t', siteIdTwo]) + } catch (error) { + // We expect the process to exit, so this is fine + expect(error.message).toContain('process.exit unexpectedly called') + } + + expect(promptSpy).toHaveBeenCalled() + + expect(log).toHaveBeenCalledWith(warningMessage) + expect(log).toHaveBeenCalledWith(noticeEnvVars) + envVarsList.forEach((envVar) => { + expect(log).toHaveBeenCalledWith(envVar) + }) + expect(log).toHaveBeenCalledWith(overwriteNotice) + expect(log).not.toHaveBeenCalledWith(successMessage) + }) + }) + + test('should not run prompts if sites have no enviroment variables in common', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + const successMessageSite3 = `Successfully cloned environment variables from ${chalk.green( + 'site-name', + )} to ${chalk.green('site-name-3')}` + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'env:clone', '-t', 'site_id_3']) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(noticeEnvVars) + envVarsList.forEach((envVar) => { + expect(log).not.toHaveBeenCalledWith(envVar) + }) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessageSite3) + }) + }) + }) + + describe('should not run prompts if in non-interactive shell or CI/CD environment', async () => { + test('should not show prompt in an non-interactive shell', async () => { + setTTYMode(false) + + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'env:clone', '-t', siteIdTwo]) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + + test('should not show prompt in a ci/cd enviroment', async () => { + setCI(true) + + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'env:clone', '-t', siteIdTwo]) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + }) + }) +}) diff --git a/tests/integration/commands/env/env-set.test.ts b/tests/integration/commands/env/env-set.test.ts index 2eb4b9c2a68..933d9071287 100644 --- a/tests/integration/commands/env/env-set.test.ts +++ b/tests/integration/commands/env/env-set.test.ts @@ -1,131 +1,129 @@ -import { describe, expect, test } from 'vitest' +import process from 'process' +import chalk from 'chalk' +import { describe, expect, test, vi, beforeEach, afterAll } from 'vitest' + +import { log } from '../../../../src/utils/command-helpers.js' +import { destructiveCommandMessages } from '../.././../../src/utils/prompts/prompt-messages.js' import { FixtureTestContext, setupFixtureTests } from '../../utils/fixture.js' +import { getEnvironmentVariables, withMockApi, setTTYMode, setCI, setTestingPrompts } from '../../utils/mock-api.js' +import { runMockProgram } from '../../utils/mock-program.js' +import { routes } from './api-routes.js' +import { mockPrompt, spyOnMockPrompt } from '../../utils/inquirer-mock-prompt.js' + +vi.mock('../../../../src/utils/command-helpers.js', async () => ({ + ...(await vi.importActual('../../../../src/utils/command-helpers.js')), + log: vi.fn(), +})) -import routes from './api-routes.js' +const OLD_ENV = process.env -describe('env:list command', () => { +describe('env:set command', () => { setupFixtureTests('empty-project', { mockApi: { routes } }, () => { test('should create and return new var in the dev context', async ({ fixture, mockApi }) => { - const cliResponse = await fixture.callCli(['env:set', 'NEW_VAR', 'new-value', '--context', 'dev', '--json'], { - offline: false, - parseJson: true, - }) - + const cliResponse = await fixture.callCli( + ['env:set', 'NEW_VAR', 'new-value', '--context', 'dev', '--json', '--force'], + { + offline: false, + parseJson: true, + }, + ) expect(cliResponse).toEqual({ EXISTING_VAR: 'envelope-dev-value', OTHER_VAR: 'envelope-all-value', NEW_VAR: 'new-value', }) - const postRequest = mockApi?.requests.find( (request) => request.method === 'POST' && request.path === '/api/v1/accounts/test-account/env', ) - expect(postRequest.body[0].key).toBe('NEW_VAR') expect(postRequest.body[0].values[0].context).toBe('dev') expect(postRequest.body[0].values[0].value).toBe('new-value') }) - test('should update an existing var in the dev context', async ({ fixture, mockApi }) => { const cliResponse = await fixture.callCli( - ['env:set', 'EXISTING_VAR', 'envelope-new-value', '--context', 'dev', '--json'], + ['env:set', 'EXISTING_VAR', 'envelope-new-value', '--context', 'dev', '--json', '--force'], { offline: false, parseJson: true, }, ) - expect(cliResponse).toEqual({ EXISTING_VAR: 'envelope-new-value', OTHER_VAR: 'envelope-all-value', }) - const patchRequest = mockApi?.requests.find( (request) => request.method === 'PATCH' && request.path === '/api/v1/accounts/test-account/env/EXISTING_VAR', ) - expect(patchRequest.body.value).toBe('envelope-new-value') expect(patchRequest.body.context).toBe('dev') }) - test('should support variadic options', async ({ fixture, mockApi }) => { const cliResponse = await fixture.callCli( - ['env:set', 'EXISTING_VAR', 'multiple', '--context', 'deploy-preview', 'production', '--json'], + ['env:set', 'EXISTING_VAR', 'multiple', '--context', 'deploy-preview', 'production', '--json', '--force'], { offline: false, parseJson: true, }, ) - expect(cliResponse).toEqual({ EXISTING_VAR: 'multiple', OTHER_VAR: 'envelope-all-value', }) - const patchRequests = mockApi?.requests.filter( (request) => request.method === 'PATCH' && request.path === '/api/v1/accounts/test-account/env/EXISTING_VAR', ) - expect(patchRequests).toHaveLength(2) - // The order of the request might not be always the same, so we need to find the request const dpRequest = patchRequests?.find((request) => request.body.context === 'deploy-preview') expect(dpRequest).not.toBeUndefined() expect(dpRequest.body.value).toBe('multiple') - const prodRequest = patchRequests?.find((request) => request.body.context === 'production') expect(prodRequest).not.toBeUndefined() expect(prodRequest.body.value).toBe('multiple') }) - test('should update existing var without flags', async ({ fixture, mockApi }) => { - const cliResponse = await fixture.callCli(['env:set', 'EXISTING_VAR', 'new-envelope-value', '--json'], { - offline: false, - parseJson: true, - }) - + const cliResponse = await fixture.callCli( + ['env:set', 'EXISTING_VAR', 'new-envelope-value', '--json', '--force'], + { + offline: false, + parseJson: true, + }, + ) expect(cliResponse).toEqual({ EXISTING_VAR: 'new-envelope-value', OTHER_VAR: 'envelope-all-value', }) - const putRequest = mockApi?.requests.find( (request) => request.method === 'PUT' && request.path === '/api/v1/accounts/test-account/env/EXISTING_VAR', ) - expect(putRequest.body.key).toBe('EXISTING_VAR') expect(putRequest.body.values[0].context).toBe('all') expect(putRequest.body.values[0].value).toBe('new-envelope-value') }) - test('should set the scope of an existing env var without needing a value', async ({ fixture, mockApi, }) => { const cliResponse = await fixture.callCli( - ['env:set', 'EXISTING_VAR', '--scope', 'runtime', 'post-processing', '--json'], + ['env:set', 'EXISTING_VAR', '--scope', 'runtime', 'post-processing', '--json', '--force'], { offline: false, parseJson: true, }, ) - expect(cliResponse).toEqual({ EXISTING_VAR: 'envelope-dev-value', OTHER_VAR: 'envelope-all-value', }) - const putRequest = mockApi?.requests.find( (request) => request.method === 'PUT' && request.path === '/api/v1/accounts/test-account/env/EXISTING_VAR', ) - expect(putRequest.body.values[0].context).toBe('production') expect(putRequest.body.values[1].context).toBe('dev') expect(putRequest.body.scopes[0]).toBe('runtime') expect(putRequest.body.scopes[1]).toBe('post-processing') }) - test('should create new secret values for multiple contexts', async ({ fixture, mockApi }) => { const cliResponse = await fixture.callCli( [ @@ -138,23 +136,21 @@ describe('env:list command', () => { 'deploy-preview', 'branch-deploy', '--json', + '--force', ], { offline: false, parseJson: true, }, ) - expect(cliResponse).toEqual({ TOTALLY_NEW_SECRET: 'shhhhhhecret', EXISTING_VAR: 'envelope-prod-value', OTHER_VAR: 'envelope-all-value', }) - const postRequest = mockApi?.requests.find( (request) => request.method === 'POST' && request.path === '/api/v1/accounts/test-account/env', ) - expect(postRequest.body).toHaveLength(1) expect(postRequest.body[0].key).toBe('TOTALLY_NEW_SECRET') expect(postRequest.body[0].is_secret).toBe(true) @@ -162,47 +158,39 @@ describe('env:list command', () => { expect(postRequest.body[0].values[0].value).toBe('shhhhhhecret') expect(postRequest.body[0].values).toHaveLength(3) }) - test('should update a single value for production context', async ({ fixture, mockApi }) => { const cliResponse = await fixture.callCli( - ['env:set', 'EXISTING_VAR', 'envelope-new-value', '--secret', '--context', 'production', '--json'], + ['env:set', 'EXISTING_VAR', 'envelope-new-value', '--secret', '--context', 'production', '--json', '--force'], { offline: false, parseJson: true, }, ) - expect(cliResponse).toEqual({ EXISTING_VAR: 'envelope-new-value', OTHER_VAR: 'envelope-all-value', }) - const patchRequest = mockApi?.requests.find( (request) => request.method === 'PATCH' && request.path === '/api/v1/accounts/test-account/env/EXISTING_VAR', ) - expect(patchRequest.body.context).toBe('production') expect(patchRequest.body.value).toBe('envelope-new-value') }) - test('should convert an `all` env var to a secret when no value is passed', async ({ fixture, mockApi, }) => { - const cliResponse = await fixture.callCli(['env:set', 'OTHER_VAR', '--secret', '--json'], { + const cliResponse = await fixture.callCli(['env:set', 'OTHER_VAR', '--secret', '--json', '--force'], { offline: false, parseJson: true, }) - expect(cliResponse).toEqual({ EXISTING_VAR: 'envelope-dev-value', OTHER_VAR: 'envelope-all-value', }) - const putRequest = mockApi?.requests.find( (request) => request.method === 'PUT' && request.path === '/api/v1/accounts/test-account/env/OTHER_VAR', ) - expect(putRequest.body.is_secret).toBe(true) expect(putRequest.body.values.length).toBe(4) expect(putRequest.body.values[0].context).toBe('production') @@ -216,25 +204,21 @@ describe('env:list command', () => { expect(putRequest.body.scopes[1]).toBe('functions') expect(putRequest.body.scopes[2]).toBe('runtime') }) - test('should convert an env var with many values to a secret when no value is passed', async ({ fixture, mockApi, }) => { - const cliResponse = await fixture.callCli(['env:set', 'EXISTING_VAR', '--secret', '--json'], { + const cliResponse = await fixture.callCli(['env:set', 'EXISTING_VAR', '--secret', '--json', '--force'], { offline: false, parseJson: true, }) - expect(cliResponse).toEqual({ EXISTING_VAR: 'envelope-dev-value', OTHER_VAR: 'envelope-all-value', }) - const putRequest = mockApi?.requests.find( (request) => request.method === 'PUT' && request.path === '/api/v1/accounts/test-account/env/EXISTING_VAR', ) - expect(putRequest.body.is_secret).toBe(true) expect(putRequest.body.values.length).toBe(2) expect(putRequest.body.values[0].context).toBe('production') @@ -245,26 +229,24 @@ describe('env:list command', () => { expect(putRequest.body.scopes[0]).toBe('builds') expect(putRequest.body.scopes[1]).toBe('functions') }) - describe('errors', () => { test.concurrent( 'should error when a value is passed without --context', async ({ fixture }) => { await expect( - fixture.callCli(['env:set', 'TOTALLY_NEW', 'cool-value', '--secret'], { + fixture.callCli(['env:set', 'TOTALLY_NEW', 'cool-value', '--secret', '--force'], { offline: false, parseJson: false, }), ).rejects.toThrowError(`please specify a non-development context`) }, ) - test.concurrent( 'should error when set with a post-processing --scope', async ({ fixture }) => { await expect( fixture.callCli( - ['env:set', 'TOTALLY_NEW', 'cool-value', '--secret', '--scope', 'builds', 'post-processing'], + ['env:set', 'TOTALLY_NEW', 'cool-value', '--secret', '--scope', 'builds', 'post-processing', '--force'], { offline: false, parseJson: false, @@ -273,12 +255,11 @@ describe('env:list command', () => { ).rejects.toThrowError(`Secret values cannot be used within the post-processing scope.`) }, ) - test.concurrent( 'should error when --scope and --context are passed on an existing env var', async ({ fixture }) => { await expect( - fixture.callCli(['env:set', 'EXISTING_VAR', '--scope', 'functions', '--context', 'production'], { + fixture.callCli(['env:set', 'EXISTING_VAR', '--scope', 'functions', '--context', 'production', '--force'], { offline: false, parseJson: false, }), @@ -287,4 +268,153 @@ describe('env:list command', () => { ) }) }) + + describe('prompt messages for env:set command', () => { + const existingVar = 'EXISTING_VAR' + const newEnvValue = 'value' + const { overwriteNotice } = destructiveCommandMessages + const { generateWarning, overwriteConfirmation } = destructiveCommandMessages.envSet + + const warningMessage = generateWarning(existingVar) + + const successMessage = `Set environment variable ${chalk.yellow( + `${existingVar}=${newEnvValue}`, + )} in the ${chalk.magenta('all')} context` + + beforeEach(() => { + vi.resetModules() + vi.clearAllMocks() + + Object.defineProperty(process, 'env', { value: {} }) + }) + + afterAll(() => { + vi.resetModules() + vi.restoreAllMocks() + + Object.defineProperty(process, 'env', { + value: OLD_ENV, + }) + }) + describe('user is prompted to confirmOverwrite when setting an env var that already exists', () => { + beforeEach(() => { + setTestingPrompts('true') + }) + + test('should log warnings and prompts if enviroment variable already exists', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = mockPrompt({ confirm: true }) + + await runMockProgram(['', '', 'env:set', existingVar, newEnvValue]) + + expect(promptSpy).toHaveBeenCalledWith({ + type: 'confirm', + name: 'confirm', + message: expect.stringContaining(overwriteConfirmation), + default: false, + }) + + expect(log).toHaveBeenCalledWith(warningMessage) + expect(log).toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + + test('should skip warnings and prompts if enviroment variable does not exist', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'env:set', 'NEW_ENV_VAR', 'NEW_VALUE']) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith( + `Set environment variable ${chalk.yellow(`${'NEW_ENV_VAR'}=${'NEW_VALUE'}`)} in the ${chalk.magenta( + 'all', + )} context`, + ) + }) + }) + + test('should skip warnings and prompts if --force flag is passed', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'env:set', existingVar, newEnvValue, '--force']) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + + test('should exit user responds is no to confirmatnion prompt', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = mockPrompt({ confirm: false }) + + try { + await runMockProgram(['', '', 'env:set', existingVar, newEnvValue]) + } catch (error) { + // We expect the process to exit, so this is fine + expect(error.message).toContain('process.exit unexpectedly called') + } + + expect(promptSpy).toHaveBeenCalled() + + expect(log).toHaveBeenCalledWith(warningMessage) + expect(log).toHaveBeenCalledWith(overwriteNotice) + expect(log).not.toHaveBeenCalledWith(successMessage) + }) + }) + }) + + describe('prompts should not show in an non-interactive shell or in a ci/cd enviroment', () => { + test('should not show prompt in an non-interactive shell', async () => { + setTTYMode(false) + + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'env:set', existingVar, newEnvValue]) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + + test('should not show prompt in a ci/cd enviroment', async () => { + setCI('true') + + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = spyOnMockPrompt() + await runMockProgram(['', '', 'env:set', existingVar, newEnvValue]) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(successMessage) + }) + }) + }) + }) }) diff --git a/tests/integration/commands/env/env-unset.test.ts b/tests/integration/commands/env/env-unset.test.ts index 7a5d5aae73d..86003c4708a 100644 --- a/tests/integration/commands/env/env-unset.test.ts +++ b/tests/integration/commands/env/env-unset.test.ts @@ -1,13 +1,28 @@ -import { describe, expect, test } from 'vitest' +import process from 'process' +import chalk from 'chalk' +import { describe, expect, test, vi, beforeEach, afterAll } from 'vitest' + +import { log } from '../../../../src/utils/command-helpers.js' +import { destructiveCommandMessages } from '../.././../../src/utils/prompts/prompt-messages.js' import { FixtureTestContext, setupFixtureTests } from '../../utils/fixture.js' +import { getEnvironmentVariables, withMockApi, setTTYMode, setCI, setTestingPrompts } from '../../utils/mock-api.js' + +import { routes } from './api-routes.js' +import { runMockProgram } from '../../utils/mock-program.js' +import { mockPrompt, spyOnMockPrompt } from '../../utils/inquirer-mock-prompt.js' -import routes from './api-routes.js' +vi.mock('../../../../src/utils/command-helpers.js', async () => ({ + ...(await vi.importActual('../../../../src/utils/command-helpers.js')), + log: vi.fn(), +})) + +const OLD_ENV = process.env describe('env:unset command', () => { setupFixtureTests('empty-project', { mockApi: { routes } }, () => { test('should remove existing variable', async ({ fixture, mockApi }) => { - const cliResponse = await fixture.callCli(['env:unset', '--json', 'EXISTING_VAR'], { + const cliResponse = await fixture.callCli(['env:unset', '--json', 'EXISTING_VAR', '--force'], { offline: false, parseJson: true, }) @@ -22,10 +37,13 @@ describe('env:unset command', () => { }) test('should remove existing variable value', async ({ fixture, mockApi }) => { - const cliResponse = await fixture.callCli(['env:unset', 'EXISTING_VAR', '--context', 'production', '--json'], { - offline: false, - parseJson: true, - }) + const cliResponse = await fixture.callCli( + ['env:unset', 'EXISTING_VAR', '--context', 'production', '--json', '--force'], + { + offline: false, + parseJson: true, + }, + ) expect(cliResponse).toEqual({ OTHER_VAR: 'envelope-all-value', @@ -37,10 +55,13 @@ describe('env:unset command', () => { }) test('should split up an `all` value', async ({ fixture, mockApi }) => { - const cliResponse = await fixture.callCli(['env:unset', 'OTHER_VAR', '--context', 'branch-deploy', '--json'], { - offline: false, - parseJson: true, - }) + const cliResponse = await fixture.callCli( + ['env:unset', 'OTHER_VAR', '--context', 'branch-deploy', '--json', '--force'], + { + offline: false, + parseJson: true, + }, + ) expect(cliResponse).toEqual({}) @@ -55,4 +76,148 @@ describe('env:unset command', () => { expect(patchRequests).toHaveLength(3) }) }) + + describe('prompt messages for env:unset command', () => { + const { overwriteNotice } = destructiveCommandMessages + const { generateWarning, overwriteConfirmation } = destructiveCommandMessages.envUnset + + // already exists as value in withMockApi + const existingVar = 'EXISTING_VAR' + const warningMessage = generateWarning(existingVar) + const expectedSuccessMessage = `Unset environment variable ${chalk.yellow(`${existingVar}`)} in the ${chalk.magenta( + 'all', + )} context` + + beforeEach(() => { + vi.resetModules() + vi.clearAllMocks() + + Object.defineProperty(process, 'env', { value: {} }) + }) + + afterAll(() => { + vi.resetModules() + vi.restoreAllMocks() + + Object.defineProperty(process, 'env', { + value: OLD_ENV, + }) + }) + + describe('user is prompted to confirm when unsetting an env var that already exists', () => { + beforeEach(() => { + setTestingPrompts('true') + }) + + test('should log warnings and prompts if enviroment variable already exists', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = mockPrompt({ confirm: true }) + + await runMockProgram(['', '', 'env:unset', existingVar]) + + expect(promptSpy).toHaveBeenCalledWith({ + type: 'confirm', + name: 'confirm', + message: expect.stringContaining(overwriteConfirmation), + default: false, + }) + + expect(log).toHaveBeenCalledWith(warningMessage) + expect(log).toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(expectedSuccessMessage) + }) + }) + + test('should skip warnings and prompts if --force flag is passed', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'env:unset', existingVar, '--force']) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(expectedSuccessMessage) + }) + }) + + test('should exit user reponds is no to confirmatnion prompt', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = mockPrompt({ confirm: false }) + + try { + await runMockProgram(['', '', 'env:unset', existingVar]) + } catch (error) { + // We expect the process to exit, so this is fine + expect(error.message).toContain('process.exit unexpectedly called') + } + + expect(promptSpy).toHaveBeenCalled() + + expect(log).toHaveBeenCalledWith(warningMessage) + expect(log).toHaveBeenCalledWith(overwriteNotice) + expect(log).not.toHaveBeenCalledWith(expectedSuccessMessage) + }) + }) + + test('should not run prompts if enviroment variable does not exist', async () => { + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'env:unset', 'NEW_ENV_VAR']) + + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).not.toHaveBeenCalledWith(expectedSuccessMessage) + }) + }) + }) + + describe('prompts should not show in an non-interactive shell or in a ci/cd enviroment', () => { + test('prompts should not show in an non-interactive shell', async () => { + setTTYMode(false) + + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'env:unset', existingVar]) + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(expectedSuccessMessage) + }) + }) + + test('prompts should not show in a ci/cd enviroment', async () => { + setCI(true) + + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const promptSpy = spyOnMockPrompt() + + await runMockProgram(['', '', 'env:unset', existingVar]) + expect(promptSpy).not.toHaveBeenCalled() + + expect(log).not.toHaveBeenCalledWith(warningMessage) + expect(log).not.toHaveBeenCalledWith(overwriteNotice) + expect(log).toHaveBeenCalledWith(expectedSuccessMessage) + }) + }) + }) + }) }) diff --git a/tests/integration/commands/env/env.test.js b/tests/integration/commands/env/env.test.js index c1eba42850e..84353d0f837 100644 --- a/tests/integration/commands/env/env.test.js +++ b/tests/integration/commands/env/env.test.js @@ -244,7 +244,7 @@ describe('commands/env', () => { await withMockApi(setRoutes, async ({ apiUrl }) => { const cliResponse = await callCli( - ['env:set', '--json', 'NEW_VAR', 'new-value'], + ['env:set', '--json', 'NEW_VAR', 'new-value', '--force'], getCLIOptions({ builder, apiUrl }), true, ) @@ -270,7 +270,7 @@ describe('commands/env', () => { await withMockApi(setRoutes, async ({ apiUrl }) => { const cliResponse = await callCli( - ['env:set', '--json', 'EXISTING_VAR', 'new-value'], + ['env:set', '--json', 'EXISTING_VAR', 'new-value', '--force'], getCLIOptions({ builder, apiUrl }), true, ) @@ -297,7 +297,7 @@ describe('commands/env', () => { await withMockApi(unsetRoutes, async ({ apiUrl }) => { const cliResponse = await callCli( - ['env:unset', '--json', 'EXISTING_VAR'], + ['env:unset', '--json', 'EXISTING_VAR', '--force'], getCLIOptions({ builder, apiUrl }), true, ) @@ -396,7 +396,10 @@ describe('commands/env', () => { { path: 'sites/site_id_a', response: { ...siteInfo, build_settings: { env: {} } } }, ] await withMockApi(createRoutes, async ({ apiUrl }) => { - const cliResponse = await callCli(['env:clone', '--to', 'site_id_a'], getCLIOptions({ builder, apiUrl })) + const cliResponse = await callCli( + ['env:clone', '--to', 'site_id_a', '--force'], + getCLIOptions({ builder, apiUrl }), + ) t.expect(normalize(cliResponse)).toMatchSnapshot() }) @@ -409,7 +412,7 @@ describe('commands/env', () => { const createRoutes = [{ path: 'sites/site_id', response: { ...siteInfo, build_settings: { env: {} } } }] await withMockApi(createRoutes, async ({ apiUrl }) => { const { stderr: cliResponse } = await callCli( - ['env:clone', '--to', 'to-site'], + ['env:clone', '--to', 'to-site', '--force'], getCLIOptions({ builder, apiUrl }), ).catch((error) => error) @@ -423,7 +426,7 @@ describe('commands/env', () => { await builder.build() await withMockApi([], async ({ apiUrl }) => { const { stderr: cliResponse } = await callCli( - ['env:clone', '--from', 'from-site', '--to', 'to-site'], + ['env:clone', '--from', 'from-site', '--to', 'to-site', '--force'], getCLIOptions({ builder, apiUrl }), ).catch((error) => error) @@ -438,11 +441,12 @@ describe('commands/env', () => { await withSiteBuilder(t, async (builder) => { await builder.build() - const cliResponse = await callCli(['env:clone', '--to', 'site_id_a'], { + const cliResponse = await callCli(['env:clone', '--to', 'site_id_a', '--force'], { cwd: builder.directory, extendEnv: false, PATH: process.env.PATH, }) + t.expect(normalize(cliResponse)).toMatchSnapshot() }) }) @@ -469,7 +473,10 @@ describe('commands/env', () => { await withSiteBuilder(t, async (builder) => { await builder.build() await withMockApi(cloneRoutes, async ({ apiUrl, requests }) => { - const cliResponse = await callCli(['env:clone', '--to', 'site_id_a'], getCLIOptions({ apiUrl, builder })) + const cliResponse = await callCli( + ['env:clone', '--to', 'site_id_a', '--force'], + getCLIOptions({ apiUrl, builder }), + ) t.expect(normalize(cliResponse)).toMatchSnapshot() diff --git a/tests/integration/commands/envelope/envelope.test.js b/tests/integration/commands/envelope/envelope.test.js index f020d69e9b7..d63eb898cc6 100644 --- a/tests/integration/commands/envelope/envelope.test.js +++ b/tests/integration/commands/envelope/envelope.test.js @@ -220,7 +220,7 @@ describe.concurrent('command/envelope', () => { await builder.build() await withMockApi(cloneRoutes, async ({ apiUrl, requests }) => { const cliResponse = await callCli( - ['env:clone', '--from', 'site_id_a', '--to', 'site_id_b'], + ['env:clone', '--from', 'site_id_a', '--to', 'site_id_b', '--force'], getCLIOptions({ apiUrl, builder }), ) diff --git a/tests/integration/commands/integration/deploy.test.ts b/tests/integration/commands/integration/deploy.test.ts index a4c546e0d8e..dd3358d1ce0 100644 --- a/tests/integration/commands/integration/deploy.test.ts +++ b/tests/integration/commands/integration/deploy.test.ts @@ -1,6 +1,6 @@ import process from 'process' -import { beforeEach, describe, expect, test, vi } from 'vitest' +import { afterAll, beforeEach, describe, expect, test, vi } from 'vitest' import BaseCommand from '../../../../src/commands/base-command.js' import { deploy as siteDeploy } from '../../../../src/commands/deploy/deploy.js' @@ -24,10 +24,26 @@ describe('integration:deploy areScopesEqual', () => { }) }) +const originalEnv = process.env + describe(`integration:deploy`, () => { beforeEach(() => { - vi.resetAllMocks() + vi.resetModules() + vi.clearAllMocks() + Object.defineProperty(process, 'env', { + value: originalEnv, + }) }) + + afterAll(() => { + vi.resetModules() + vi.restoreAllMocks() + + Object.defineProperty(process, 'env', { + value: originalEnv, + }) + }) + test('deploys an integration', async (t) => { vi.mock(`../../../../src/commands/deploy/deploy.js`, () => ({ deploy: vi.fn(() => console.log(`yay it was mocked!`)), diff --git a/tests/integration/commands/link/link.test.ts b/tests/integration/commands/link/link.test.ts index d798aced7fe..f3160807e53 100644 --- a/tests/integration/commands/link/link.test.ts +++ b/tests/integration/commands/link/link.test.ts @@ -46,3 +46,60 @@ describe('link command', () => { }, ) }) + +describe('link command with multiple sites', () => { + const siteInfo1 = { + id: 'site_id1', + name: 'next-app-playground', + } + + const siteInfo2 = { + id: 'site_id2', + name: 'app', + } + + const routes = [ + { + path: 'sites', + response: [siteInfo1, siteInfo2], + }, + ] + + test('should prefer exact name match when available', async (t) => { + await withSiteBuilder(t, async (builder) => { + await builder.build() + + await withMockApi( + routes, + async ({ apiUrl }) => { + const stdout = await callCli( + ['link', '--name', 'app'], + getCLIOptions({ builder, apiUrl, env: { NETLIFY_SITE_ID: '' } }), + ) + + expect(stdout).toContain('Linked to app') + }, + true, + ) + }) + }) + + test('should use first site when name flag is not an exact match', async (t) => { + await withSiteBuilder(t, async (builder) => { + await builder.build() + + await withMockApi( + routes, + async ({ apiUrl }) => { + const stdout = await callCli( + ['link', '--name', 'ap'], + getCLIOptions({ builder, apiUrl, env: { NETLIFY_SITE_ID: '' } }), + ) + + expect(stdout).toContain('Linked to next-app-playground') + }, + true, + ) + }) + }) +}) diff --git a/tests/integration/commands/logs/build.test.ts b/tests/integration/commands/logs/build.test.ts index dc1e654f039..51b3d57990e 100644 --- a/tests/integration/commands/logs/build.test.ts +++ b/tests/integration/commands/logs/build.test.ts @@ -1,10 +1,14 @@ -import { Mock, afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { Mock, afterAll, afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import BaseCommand from '../../../../src/commands/base-command.js' import { createLogsBuildCommand } from '../../../../src/commands/logs/index.js' import { getWebSocket } from '../../../../src/utils/websockets/index.js' import { startMockApi } from '../../utils/mock-api-vitest.js' import { getEnvironmentVariables } from '../../utils/mock-api.js' +import { callCli } from '../../utils/call-cli.js' +import { getCLIOptions, withMockApi } from '../../utils/mock-api.js' +import { withSiteBuilder } from '../../utils/site-builder.ts' +import { join } from 'path' vi.mock('../../../../src/utils/websockets/index.js', () => ({ getWebSocket: vi.fn(), @@ -45,11 +49,14 @@ const routes = [ }, ] +const originalEnv = { ...process.env } + describe('logs:deploy command', () => { let program: BaseCommand afterEach(() => { vi.clearAllMocks() + process.env = { ...originalEnv } }) beforeEach(() => { @@ -58,6 +65,13 @@ describe('logs:deploy command', () => { createLogsBuildCommand(program) }) + afterAll(() => { + vi.restoreAllMocks() + vi.resetModules() + + process.env = { ...originalEnv } + }) + test('should setup the deploy stream correctly', async ({}) => { const { apiUrl } = await startMockApi({ routes }) const spyWebsocket = getWebSocket as unknown as Mock @@ -107,4 +121,21 @@ describe('logs:deploy command', () => { expect(body.site_id).toEqual('site_id') expect(body.access_token).toEqual(env.NETLIFY_AUTH_TOKEN) }) + + test('should instruct user to link a site if one is not linked', async (t) => { + await withSiteBuilder(t, async (builder) => { + const projectPath = join('projects', 'project1') + await builder.withNetlifyToml({ config: {}, pathPrefix: projectPath }).build() + + await withMockApi( + routes, + async ({ apiUrl }) => { + const options = getCLIOptions({ builder, apiUrl, env: { NETLIFY_SITE_ID: '' } }) + const stdout = await callCli(['logs:deploy'], { ...options, cwd: join(builder.directory, projectPath) }) + expect(stdout).toContain('You must link a site before attempting to view deploy logs') + }, + true, + ) + }) + }) }) diff --git a/tests/integration/commands/logs/functions.test.ts b/tests/integration/commands/logs/functions.test.ts index 278dba7423f..f7267d9eda7 100644 --- a/tests/integration/commands/logs/functions.test.ts +++ b/tests/integration/commands/logs/functions.test.ts @@ -1,4 +1,4 @@ -import { Mock, afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { Mock, afterAll, afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import BaseCommand from '../../../../src/commands/base-command.js' import { createLogsFunctionCommand } from '../../../../src/commands/logs/index.js' @@ -65,10 +65,13 @@ const routes = [ ] describe('logs:function command', () => { + const originalEnv = process.env + let program: BaseCommand afterEach(() => { vi.clearAllMocks() + process.env = { ...originalEnv } }) beforeEach(() => { @@ -77,6 +80,13 @@ describe('logs:function command', () => { createLogsFunctionCommand(program) }) + afterAll(() => { + vi.restoreAllMocks() + vi.resetModules() + + process.env = { ...originalEnv } + }) + test('should setup the functions stream correctly', async ({}) => { const { apiUrl } = await startMockApi({ routes }) const spyWebsocket = getWebSocket as unknown as Mock diff --git a/tests/integration/commands/sites/sites-create-template.test.ts b/tests/integration/commands/sites/sites-create-template.test.ts new file mode 100644 index 00000000000..17906c75be2 --- /dev/null +++ b/tests/integration/commands/sites/sites-create-template.test.ts @@ -0,0 +1,148 @@ +import process from 'process' + +import inquirer from 'inquirer' +import { beforeEach, afterEach, describe, expect, test, vi, afterAll } from 'vitest' + +import BaseCommand from '../../../../src/commands/base-command.ts' +import { createSitesFromTemplateCommand } from '../../../../src/commands/sites/sites.ts' +import { deployedSiteExists, fetchTemplates, getTemplateName } from '../../../../src/utils/sites/create-template.ts' +import { getTemplatesFromGitHub, validateTemplate, createRepo } from '../../../../src/utils/sites/utils.ts' +import { getEnvironmentVariables, withMockApi } from '../../utils/mock-api.js' + +vi.mock('../../../../src/utils/init/config-github.ts') +vi.mock('../../../../src/utils/sites/utils.ts') +vi.mock('../../../../src/utils/sites/create-template.ts') +vi.mock('inquirer') + +inquirer.registerPrompt = vi.fn() +inquirer.prompt.registerPrompt = vi.fn() + +const siteInfo = { + admin_url: 'https://app.netlify.com/sites/site-name/overview', + ssl_url: 'https://site-name.netlify.app/', + id: 'site_id', + name: 'site-name', + build_settings: { repo_url: 'https://github.com/owner/repo' }, +} + +const routes = [ + { + path: 'accounts', + response: [{ slug: 'test-account' }], + }, + { + path: 'sites', + response: [{ name: 'test-name' }], + }, + { + path: 'test-account/sites', + response: siteInfo, + method: 'post', + }, +] + +const OLD_ENV = process.env + +describe('sites:create-template', () => { + beforeEach(async () => { + inquirer.prompt = Object.assign( + vi + .fn() + .mockImplementationOnce(() => Promise.resolve({ accountSlug: 'test-account' })) + .mockImplementationOnce(() => Promise.resolve({ name: 'test-name' })), + { + prompts: inquirer.prompt?.prompts || {}, + registerPrompt: inquirer.prompt?.registerPrompt || vi.fn(), + restoreDefaultPrompts: inquirer.prompt?.restoreDefaultPrompts || vi.fn(), + }, + ) + vi.mocked(fetchTemplates).mockResolvedValue([ + { + name: 'mockTemplateName', + sourceCodeUrl: 'mockUrl', + slug: 'mockSlug', + }, + ]) + vi.mocked(getTemplatesFromGitHub).mockResolvedValue([ + { + name: 'mock-name', + html_url: 'mock-url', + full_name: 'mock-full-name', + archived: false, + disabled: false, + }, + ]) + vi.mocked(getTemplateName).mockResolvedValue('mockTemplateName') + vi.mocked(validateTemplate).mockResolvedValue({ + exists: true, + isTemplate: true, + }) + vi.mocked(createRepo).mockResolvedValue({ + id: 1, + full_name: 'mockName', + private: true, + default_branch: 'mockBranch', + }) + }) + + afterEach(() => { + vi.clearAllMocks() + }) + + afterAll(() => { + vi.resetModules() + vi.restoreAllMocks() + + Object.defineProperty(process, 'env', { + value: OLD_ENV, + }) + }) + + test('it should ask for a new site name if site with that name already exists on a globally deployed site', async (t) => { + const stdoutwriteSpy = vi.spyOn(process.stdout, 'write') + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const program = new BaseCommand('netlify') + + vi.mocked(deployedSiteExists).mockResolvedValue(true) + + createSitesFromTemplateCommand(program) + + await program.parseAsync([ + '', + '', + 'sites:create-template', + '--account-slug', + 'test-account', + '--name', + 'test-name', + ]) + }) + expect(stdoutwriteSpy).toHaveBeenCalledWith('A site with that name already exists\n') + }) + + test('it should ask for a new site name if site with that name already exists on account', async (t) => { + const stdoutwriteSpy = vi.spyOn(process.stdout, 'write') + await withMockApi(routes, async ({ apiUrl }) => { + Object.assign(process.env, getEnvironmentVariables({ apiUrl })) + + const program = new BaseCommand('netlify') + + vi.mocked(deployedSiteExists).mockResolvedValue(false) + + createSitesFromTemplateCommand(program) + + await program.parseAsync([ + '', + '', + 'sites:create-template', + '--account-slug', + 'test-account', + '--name', + 'test-name', + ]) + }) + expect(stdoutwriteSpy).toHaveBeenCalledWith('A site with that name already exists on your account\n') + }) +}) diff --git a/tests/integration/commands/sites/sites.test.ts b/tests/integration/commands/sites/sites.test.ts index f0dfbc0a605..a27e49b3b7e 100644 --- a/tests/integration/commands/sites/sites.test.ts +++ b/tests/integration/commands/sites/sites.test.ts @@ -5,9 +5,9 @@ import { render } from 'prettyjson' import { afterAll, beforeEach, describe, expect, test, vi } from 'vitest' import BaseCommand from '../../../../src/commands/base-command.js' -import { fetchTemplates } from '../../../../src/commands/sites/sites-create-template.js' import { createSitesCreateCommand, createSitesFromTemplateCommand } from '../../../../src/commands/sites/sites.js' import { getGitHubToken } from '../../../../src/utils/init/config-github.js' +import { fetchTemplates } from '../../../../src/utils/sites/create-template.js' import { createRepo, getTemplatesFromGitHub } from '../../../../src/utils/sites/utils.js' import { getEnvironmentVariables, withMockApi } from '../../utils/mock-api.js' @@ -39,6 +39,7 @@ vi.mock('../../../../src/utils/sites/utils.js', () => ({ full_name: 'Next starter', private: false, branch: 'main', + id: 1, })), validateTemplate: vi.fn().mockImplementation(() => ({ exists: true, diff --git a/tests/integration/utils/inquirer-mock-prompt.ts b/tests/integration/utils/inquirer-mock-prompt.ts new file mode 100644 index 00000000000..e4121f886fc --- /dev/null +++ b/tests/integration/utils/inquirer-mock-prompt.ts @@ -0,0 +1,23 @@ +// tests/utils/inquirer-mock.ts +import inquirer from 'inquirer' +import { vi } from 'vitest' + +export const mockPrompt = (response = { confirm: true }) => { + // Create the mock function + const mockFn = vi.fn().mockResolvedValue(response) + + // Preserve the original properties of inquirer.prompt + Object.assign(mockFn, inquirer.prompt) + + // Create the spy with our prepared mock + const spy = vi.spyOn(inquirer, 'prompt').mockImplementation(mockFn) + + inquirer.registerPrompt = vi.fn() + inquirer.prompt.registerPrompt = vi.fn() + + return spy +} + +export const spyOnMockPrompt = () => { + return vi.spyOn(inquirer, 'prompt') +} diff --git a/tests/integration/utils/mock-api.js b/tests/integration/utils/mock-api.js index d330902a80b..ae3b27710a2 100644 --- a/tests/integration/utils/mock-api.js +++ b/tests/integration/utils/mock-api.js @@ -1,3 +1,4 @@ +import process from 'process' import { isDeepStrictEqual, promisify } from 'util' import express from 'express' @@ -78,12 +79,45 @@ export const withMockApi = async (routes, testHandler, silent = false) => { } } +// `CI` set to "true" to mock commands run from terminal command line +// `SHLVL` used to overwrite prompts for scripted commands in production/dev +// environments see `scriptedCommand` property of `BaseCommand` export const getEnvironmentVariables = ({ apiUrl }) => ({ NETLIFY_AUTH_TOKEN: 'fake-token', NETLIFY_SITE_ID: 'site_id', NETLIFY_API_URL: apiUrl, }) +/** + * Set the `isTTY` property of `process.stdin` to the given boolean value. + * This function is used to establish flexible testing environments. + * Falsey value is for noninteractive shell (-force flags overide user prompts) + * Truthy value is for interactive shell + */ +export const setTTYMode = (bool) => { + process.stdin.isTTY = bool +} + +/** + * Sets the `TESTING_PROMPTS` environment variable to the specified value. + * This is used to make sure prompts are shown in the needed test sin ci/cd enviroments + * If this is set to 'true', then prompts will be shown in for destructive commands even in non-interactive shells + * or CI/CD enviroment + * + * @param {string} value - The value to set for the `TESTING_PROMPTS` environment variable. + */ +export const setTestingPrompts = (value) => { + process.env.TESTING_PROMPTS = value +} + +/** + * Simulates a Continuous Integration environment by toggling the `CI` + * environment variable. Truthy value is + */ +export const setCI = (value) => { + process.env.CI = value +} + /** * * @param {*} param0 diff --git a/tests/integration/utils/mock-program.ts b/tests/integration/utils/mock-program.ts new file mode 100644 index 00000000000..0456e6b454a --- /dev/null +++ b/tests/integration/utils/mock-program.ts @@ -0,0 +1,9 @@ +import { runProgram } from '../../../src/utils/run-program.js' +import { createMainCommand } from '../../../src/commands/index.js' + +export const runMockProgram = async (argv) => { + // inject the force flag if the command is a non-interactive shell or Ci enviroment + const program = createMainCommand() + + await runProgram(program, argv) +}