From d132dddd50328165d47fd7c19e90502d34fc866c Mon Sep 17 00:00:00 2001 From: Ben Hancock Date: Tue, 5 Nov 2024 12:44:22 -0500 Subject: [PATCH 01/10] fix: make outputs more user-friendly for `sites:create-template` (#6915) * fix: hide github access token input in sites:create-template Co-authored-by: Dylan Spyer * fix: update error message to be more user-friendly Co-authored-by: Dylan Spyer --------- Co-authored-by: Dylan Spyer --- src/commands/sites/sites-create-template.ts | 2 +- src/utils/gh-auth.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/sites/sites-create-template.ts b/src/commands/sites/sites-create-template.ts index c4eb5e80b0e..aad249cb708 100644 --- a/src/commands/sites/sites-create-template.ts +++ b/src/commands/sites/sites-create-template.ts @@ -118,7 +118,7 @@ export const sitesCreateTemplate = async (repository: string, options: OptionVal error( `Could not create repository: ${ (error_ as GitHubAPIError).message - }. Ensure that your PAT grants permission to create repositories`, + }. Ensure that your GitHub personal access token grants permission to create repositories`, ) } else { error( 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(), From fd77a125448b67bb03aaee46589371f81bab676b Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 6 Nov 2024 11:02:21 -0700 Subject: [PATCH 02/10] feat: add confirmation prompts to unsafe cli commands (#6878) * feat: added logic handeling for the `env:set` command Will prompt the user if scope and/or context is not provided Co-authored-by: Thomas Lane * feat: prompt before setting env variable across context and scope Co-authored-by: Will * fix: prettier Co-authored-by: Will * fix: refactored prompts Co-authored-by: Will * fix: refactor prompts Co-authored-by: Will * feat: env:unset prompts user before unsetting env variable indiscriminantly across contexts Co-authored-by: Will * feat: created tests for env:set prompts Created several tests to check env:test prompts * build: refactored env:set promts and rewrote tests created a new directory in utils called prompts, to store all future prompts. rewrote the prompts to only check for destructive actions. added tests for each of the destructive prompts Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * feat: added prompt for env:clone and tests Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: prettier fix Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * build: added prompts and tests for blob command for blobl:set and blob:delete Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: updated tests in file to reflect new prompts * fix: updated documentation updated the documentation Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: updated error updated error handeling Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> Co-authored-by: Thomas Lane * fix: updated new lines in messages for consistence updated prompts spacing for consistencey Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: fixed prettier error Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * feat: env-set refactored refactored messages in env-set to a function that exports an object to be reused Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: reactored env:unset prompts Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: refactored prompts and tests messages Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> Co-authored-by: Thomas Lane * fix: another pass of refactoring env and blob commands Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> Co-authored-by: Thomas Lane * feat: added skip for non interactive shell and CI Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> Co-authored-by: Thomas Lane * feat: refactored code for tests realted to ci and prompts Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> Co-authored-by: Thomas Lane * fix: prettier fix Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: removed console.log statements Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: updated prompts based on pr feedback Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * feat: added force flag option to all commands scripted commands automatically given the force flag Co-authored-by: Will * fix: started updating tests to work with higher level --force flag for scritped commands Co-authored-by: Will * feat: refactored tests to use mockProgram Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * feat: refactor of run.js into components to add force flag Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: types.ts merge } deletion Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: fix default lint issue and typescript issue fixed lint issue that was casuing test in ci enviroment to fail Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: update blob to blobs Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: updated prompt tests for ci/cd enviroment Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: updated prompt tests to work correctly in ci/cd enviroments Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: updated types and env variables not being restored after tests Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: fixed tests Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: fixed flakey deploy test and added env cleanup to more tests * fix: removed a console.log() statement Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * fix: cleaned up unused functions and comments Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> * chore: cleanup comments minor bug fixes Co-authored-by: Will * chore: prettier, needed to update docs Co-authored-by: Will * chore: merged updates to main.ts Co-authored-by: Will * fix: reset env variable and mocks type error * fix: fixed test type error part 2 * fix: removed restModules from test * fix: updated test to have inquirer mocked correctly add the missing methods to the inquirer mock to fix all type errors * fix: reverted the force flags on lm commands to original behavior as the force option was for credential helper installation rather than skipping prompts Co-authored-by: Will --------- Co-authored-by: Thomas Lane Co-authored-by: t Co-authored-by: Thomas Lane <163203257+tlane25@users.noreply.github.com> Co-authored-by: Daniel Lew <51924260+DanielSLew@users.noreply.github.com> --- bin/run.js | 6 +- docs/commands/blobs.md | 2 + docs/commands/env.md | 3 + docs/commands/sites.md | 2 +- package-lock.json | 33 +- package.json | 2 +- src/commands/addons/addons.ts | 1 - src/commands/base-command.ts | 4 +- src/commands/blobs/blobs-delete.ts | 11 +- src/commands/blobs/blobs-set.ts | 17 +- src/commands/env/env-clone.ts | 21 +- src/commands/env/env-set.ts | 14 +- src/commands/env/env-unset.ts | 14 +- src/commands/init/index.ts | 1 - src/commands/integration/deploy.ts | 5 +- src/commands/main.ts | 20 ++ src/commands/sites/sites.ts | 1 - src/utils/prompts/blob-delete-prompts.ts | 14 + src/utils/prompts/blob-set-prompt.ts | 14 + src/utils/prompts/confirm-prompt.ts | 21 ++ src/utils/prompts/env-clone-prompt.ts | 34 +++ src/utils/prompts/env-set-prompts.ts | 14 + src/utils/prompts/env-unset-prompts.ts | 21 ++ src/utils/prompts/prompt-messages.ts | 46 +++ src/utils/run-program.ts | 16 + src/utils/scripted-commands.ts | 22 ++ src/utils/types.ts | 13 + .../commands/addons/addons.test.js | 2 +- .../commands/blobs/blobs-delete.test.ts | 249 +++++++++++++++ .../commands/blobs/blobs-set.test.ts | 284 ++++++++++++++++++ .../integration/commands/blobs/blobs.test.ts | 10 +- .../integration/commands/dev/dev.exec.test.js | 2 +- tests/integration/commands/dev/v2-api.test.ts | 2 +- tests/integration/commands/env/api-routes.ts | 63 +++- .../commands/env/env-clone.test.ts | 190 ++++++++++++ .../integration/commands/env/env-set.test.ts | 250 +++++++++++---- .../commands/env/env-unset.test.ts | 187 +++++++++++- tests/integration/commands/env/env.test.js | 23 +- .../commands/envelope/envelope.test.js | 2 +- .../commands/integration/deploy.test.ts | 20 +- tests/integration/commands/logs/build.test.ts | 14 +- .../commands/logs/functions.test.ts | 12 +- .../sites/sites-create-template.test.ts | 31 +- .../integration/utils/inquirer-mock-prompt.ts | 23 ++ tests/integration/utils/mock-api.js | 34 +++ tests/integration/utils/mock-program.ts | 9 + 46 files changed, 1639 insertions(+), 140 deletions(-) create mode 100644 src/utils/prompts/blob-delete-prompts.ts create mode 100644 src/utils/prompts/blob-set-prompt.ts create mode 100644 src/utils/prompts/confirm-prompt.ts create mode 100644 src/utils/prompts/env-clone-prompt.ts create mode 100644 src/utils/prompts/env-set-prompts.ts create mode 100644 src/utils/prompts/env-unset-prompts.ts create mode 100644 src/utils/prompts/prompt-messages.ts create mode 100644 src/utils/run-program.ts create mode 100644 src/utils/scripted-commands.ts create mode 100644 tests/integration/commands/blobs/blobs-delete.test.ts create mode 100644 tests/integration/commands/blobs/blobs-set.test.ts create mode 100644 tests/integration/commands/env/env-clone.test.ts create mode 100644 tests/integration/utils/inquirer-mock-prompt.ts create mode 100644 tests/integration/utils/mock-program.ts 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/package-lock.json b/package-lock.json index be678b062f9..4d05c832886 100644 --- a/package-lock.json +++ b/package-lock.json @@ -134,7 +134,7 @@ "@netlify/functions": "2.8.2", "@sindresorhus/slugify": "2.2.1", "@types/fs-extra": "11.0.4", - "@types/inquirer": "9.0.7", + "@types/inquirer": "^9.0.7", "@types/jsonwebtoken": "9.0.7", "@types/lodash": "4.17.13", "@types/node": "20.14.8", @@ -5643,6 +5643,7 @@ "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.7.tgz", "integrity": "sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==", "dev": true, + "license": "MIT", "dependencies": { "@types/through": "*", "rxjs": "^7.2.0" @@ -5653,15 +5654,17 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } }, "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==", - "dev": true + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "dev": true, + "license": "0BSD" }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", @@ -9018,6 +9021,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" }, @@ -14307,6 +14311,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 +14434,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 +14483,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 +14503,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 +14512,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 +14525,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 +14534,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" }, @@ -17173,7 +17184,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", @@ -19927,6 +19939,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 +19952,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 +19961,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" }, @@ -27457,9 +27472,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.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", "dev": true } } diff --git a/package.json b/package.json index 3a3437bef18..85afe2db539 100644 --- a/package.json +++ b/package.json @@ -191,7 +191,7 @@ "@netlify/functions": "2.8.2", "@sindresorhus/slugify": "2.2.1", "@types/fs-extra": "11.0.4", - "@types/inquirer": "9.0.7", + "@types/inquirer": "^9.0.7", "@types/jsonwebtoken": "9.0.7", "@types/lodash": "4.17.13", "@types/node": "20.14.8", 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/base-command.ts b/src/commands/base-command.ts index f8a7c469124..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)) 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/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/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 9a86d6ec23f..0b5c258cc5f 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 diff --git a/src/commands/main.ts b/src/commands/main.ts index 9f790266d17..99a356baf70 100644 --- a/src/commands/main.ts +++ b/src/commands/main.ts @@ -40,6 +40,18 @@ import { createWatchCommand } from './watch/index.js' import { AddressInUseError } from './types.js' const SUGGESTION_TIMEOUT = 1e4 +// 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).' }, +} + process.on('uncaughtException', async (err: AddressInUseError | Error) => { if ('code' in err && err.code === 'EADDRINUSE') { error( @@ -243,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.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/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/types.ts b/src/utils/types.ts index f28646ad5b9..f424abcce82 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -150,6 +150,19 @@ export interface SiteInfo { 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 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/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 ee0972b4f08..ef0e192f6ab 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/logs/build.test.ts b/tests/integration/commands/logs/build.test.ts index 956bab228a4..51b3d57990e 100644 --- a/tests/integration/commands/logs/build.test.ts +++ b/tests/integration/commands/logs/build.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 { createLogsBuildCommand } from '../../../../src/commands/logs/index.js' @@ -49,21 +49,29 @@ const routes = [ }, ] +const originalEnv = { ...process.env } + describe('logs:deploy command', () => { let program: BaseCommand - const originalEnv = { ...process.env } afterEach(() => { vi.clearAllMocks() + process.env = { ...originalEnv } }) beforeEach(() => { - process.env = { ...originalEnv } program = new BaseCommand('netlify') 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 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 index ed4211987fb..17906c75be2 100644 --- a/tests/integration/commands/sites/sites-create-template.test.ts +++ b/tests/integration/commands/sites/sites-create-template.test.ts @@ -1,7 +1,7 @@ import process from 'process' import inquirer from 'inquirer' -import { beforeEach, afterEach, describe, expect, test, vi } from 'vitest' +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' @@ -12,6 +12,10 @@ 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', @@ -37,12 +41,21 @@ const routes = [ }, ] +const OLD_ENV = process.env + describe('sites:create-template', () => { beforeEach(async () => { - vi.mock('inquirer') - vi.mocked(inquirer.prompt) - .mockImplementationOnce(() => Promise.resolve({ accountSlug: 'test-account' })) - .mockImplementationOnce(() => Promise.resolve({ name: 'test-name' })) + 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', @@ -73,8 +86,16 @@ describe('sites:create-template', () => { }) 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) => { 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) +} From a39de2687df7c9cb78caacf7a2ed2b82af290541 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 00:56:33 +0000 Subject: [PATCH 03/10] chore(deps): update dependency nock to v13.5.6 (#6917) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4d05c832886..96b37640615 100644 --- a/package-lock.json +++ b/package-lock.json @@ -157,7 +157,7 @@ "ini": "4.1.3", "is-ci": "3.0.1", "mock-fs": "5.4.1", - "nock": "13.5.5", + "nock": "13.5.6", "p-timeout": "6.1.3", "serialize-javascript": "6.0.2", "sinon": "18.0.1", @@ -17388,9 +17388,9 @@ "dev": true }, "node_modules/nock": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.5.tgz", - "integrity": "sha512-XKYnqUrCwXC8DGG1xX4YH5yNIrlh9c065uaMZZHUoeUUINTOyt+x/G+ezYk0Ft6ExSREVIs+qBJDK503viTfFA==", + "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", @@ -35886,9 +35886,9 @@ } }, "nock": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.5.tgz", - "integrity": "sha512-XKYnqUrCwXC8DGG1xX4YH5yNIrlh9c065uaMZZHUoeUUINTOyt+x/G+ezYk0Ft6ExSREVIs+qBJDK503viTfFA==", + "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", diff --git a/package.json b/package.json index 85afe2db539..986c35ce0fc 100644 --- a/package.json +++ b/package.json @@ -214,7 +214,7 @@ "ini": "4.1.3", "is-ci": "3.0.1", "mock-fs": "5.4.1", - "nock": "13.5.5", + "nock": "13.5.6", "p-timeout": "6.1.3", "serialize-javascript": "6.0.2", "sinon": "18.0.1", From ffb47f9771fc24a9ac54522ada633c2c2dad9c39 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 01:15:09 +0000 Subject: [PATCH 04/10] chore(deps): pin dependency @types/inquirer to 9.0.7 (#6916) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 19 ++++++++----------- package.json | 2 +- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 96b37640615..e47250477f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -134,7 +134,7 @@ "@netlify/functions": "2.8.2", "@sindresorhus/slugify": "2.2.1", "@types/fs-extra": "11.0.4", - "@types/inquirer": "^9.0.7", + "@types/inquirer": "9.0.7", "@types/jsonwebtoken": "9.0.7", "@types/lodash": "4.17.13", "@types/node": "20.14.8", @@ -5643,7 +5643,6 @@ "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.7.tgz", "integrity": "sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==", "dev": true, - "license": "MIT", "dependencies": { "@types/through": "*", "rxjs": "^7.2.0" @@ -5654,17 +5653,15 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } }, "node_modules/@types/inquirer/node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", - "dev": true, - "license": "0BSD" + "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": { "version": "2.0.4", @@ -27472,9 +27469,9 @@ } }, "tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true } } diff --git a/package.json b/package.json index 986c35ce0fc..505c8a0e1e7 100644 --- a/package.json +++ b/package.json @@ -191,7 +191,7 @@ "@netlify/functions": "2.8.2", "@sindresorhus/slugify": "2.2.1", "@types/fs-extra": "11.0.4", - "@types/inquirer": "^9.0.7", + "@types/inquirer": "9.0.7", "@types/jsonwebtoken": "9.0.7", "@types/lodash": "4.17.13", "@types/node": "20.14.8", From 9c4eb226c1b187e879266a87fce021dd7251c2a6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 01:26:30 +0000 Subject: [PATCH 05/10] fix(deps): update dependency @types/node to v20.17.6 (#6918) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .../typescript/hello-world/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/functions-templates/typescript/hello-world/package-lock.json b/functions-templates/typescript/hello-world/package-lock.json index 0505bfc3351..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.17.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.1.tgz", - "integrity": "sha512-j2VlPv1NnwPJbaCNv69FO/1z4lId0QmGvpT41YxitRtWlg96g/j8qcv2RKsLKe2F6OJgyXhupN1Xo17b2m139Q==", + "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.17.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.1.tgz", - "integrity": "sha512-j2VlPv1NnwPJbaCNv69FO/1z4lId0QmGvpT41YxitRtWlg96g/j8qcv2RKsLKe2F6OJgyXhupN1Xo17b2m139Q==", + "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" } From 6384710f5609bd18a8c7cfa9f31cd602e6723fa3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:40:18 +0000 Subject: [PATCH 06/10] chore(deps): update dawidd6/action-download-artifact action to v6 (#6921) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/benchmark-post.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 }} From 96f64efe20395c4643af43caac4673f8ac9d672b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 19:37:59 +0000 Subject: [PATCH 07/10] fix(deps): update netlify packages (#6922) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 48 +++++++++++++++++++++++------------------------ package.json | 4 ++-- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index e47250477f7..544db06fb73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,9 +13,9 @@ "@bugsnag/js": "7.25.0", "@fastify/static": "7.0.4", "@netlify/blobs": "8.1.0", - "@netlify/build": "29.56.0", + "@netlify/build": "29.56.1", "@netlify/build-info": "7.15.2", - "@netlify/config": "20.19.0", + "@netlify/config": "20.19.1", "@netlify/edge-bundler": "12.2.3", "@netlify/edge-functions": "2.9.0", "@netlify/local-functions-proxy": "1.1.1", @@ -2260,14 +2260,14 @@ } }, "node_modules/@netlify/build": { - "version": "29.56.0", - "resolved": "https://registry.npmjs.org/@netlify/build/-/build-29.56.0.tgz", - "integrity": "sha512-x2DUxBt1MigjAX+2xpDmWKp4BlTvg4GofaR2SWSz80D+zkfWRJp01de1O5mKH9QK3saQBQUVJyi+mrK64YNf4w==", + "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.93", @@ -2285,7 +2285,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", @@ -2820,9 +2820,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", @@ -13976,9 +13976,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" }, @@ -25386,14 +25386,14 @@ "integrity": "sha512-9hIbusvAZjSGBJ42OyFC2AxsEph1LuKQahMWFcPGEIsOqIYHhMRkYA7wSUMhH7naydjNmllpcp3pJLOK4RhFaQ==" }, "@netlify/build": { - "version": "29.56.0", - "resolved": "https://registry.npmjs.org/@netlify/build/-/build-29.56.0.tgz", - "integrity": "sha512-x2DUxBt1MigjAX+2xpDmWKp4BlTvg4GofaR2SWSz80D+zkfWRJp01de1O5mKH9QK3saQBQUVJyi+mrK64YNf4w==", + "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.93", @@ -25411,7 +25411,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", @@ -25747,9 +25747,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", @@ -33465,9 +33465,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" } diff --git a/package.json b/package.json index 505c8a0e1e7..d74f5c915eb 100644 --- a/package.json +++ b/package.json @@ -74,9 +74,9 @@ "@bugsnag/js": "7.25.0", "@fastify/static": "7.0.4", "@netlify/blobs": "8.1.0", - "@netlify/build": "29.56.0", + "@netlify/build": "29.56.1", "@netlify/build-info": "7.15.2", - "@netlify/config": "20.19.0", + "@netlify/config": "20.19.1", "@netlify/edge-bundler": "12.2.3", "@netlify/edge-functions": "2.9.0", "@netlify/local-functions-proxy": "1.1.1", From dd88e50c174cdce4f718323ef50e0e4b6bdac9e4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 01:38:58 +0000 Subject: [PATCH 08/10] chore(deps): update dependency @types/node-fetch to v2.6.12 (#6923) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 544db06fb73..c8d8538ca9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -138,7 +138,7 @@ "@types/jsonwebtoken": "9.0.7", "@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", @@ -5751,9 +5751,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": "*", @@ -27564,9 +27564,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": "*", diff --git a/package.json b/package.json index d74f5c915eb..f054a9ef4a0 100644 --- a/package.json +++ b/package.json @@ -195,7 +195,7 @@ "@types/jsonwebtoken": "9.0.7", "@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", From f89b7072de186cbd5b48cf1a59a6036d33116664 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:39:45 +0000 Subject: [PATCH 09/10] fix(deps): update dependency ora to v8.1.1 (#6919) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Karin Hendrikse <30577427+khendrikse@users.noreply.github.com> --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index c8d8538ca9c..0208f3f2b27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", @@ -18034,9 +18034,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", @@ -36343,9 +36343,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", diff --git a/package.json b/package.json index f054a9ef4a0..358ba43fa45 100644 --- a/package.json +++ b/package.json @@ -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", From 49c0b335feeca9cf81987aa2328edd039848c5a9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:53:32 +0000 Subject: [PATCH 10/10] chore(deps): update dependency verdaccio to v6.0.2 (#6924) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Karin Hendrikse <30577427+khendrikse@users.noreply.github.com> Co-authored-by: NikAiyer --- package-lock.json | 852 +++++++++++++--------------------------------- package.json | 2 +- 2 files changed, 241 insertions(+), 613 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0208f3f2b27..377653a09f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -165,7 +165,7 @@ "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": { @@ -6088,20 +6088,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 +6111,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 +6196,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 +6217,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 +6260,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 +6431,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 +6448,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 +6487,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 +6512,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 +6539,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 +6575,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 +6620,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 +6642,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 +6685,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" @@ -9484,32 +9372,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", @@ -9525,6 +9404,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", @@ -22207,28 +22115,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", @@ -22247,8 +22155,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" @@ -22262,14 +22170,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" }, @@ -22281,91 +22189,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", @@ -22379,40 +22202,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", @@ -22429,9 +22226,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" @@ -27802,29 +27599,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" @@ -27882,13 +27678,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", @@ -27896,12 +27692,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" @@ -27928,9 +27724,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", @@ -28051,22 +27847,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" @@ -28086,17 +27882,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", @@ -28104,12 +27900,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" @@ -28124,81 +27920,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", @@ -28219,25 +27940,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": { @@ -28257,14 +27966,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", @@ -28272,12 +27981,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" @@ -28304,18 +28013,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" @@ -30206,26 +29915,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", @@ -30240,6 +29943,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 } } }, @@ -39393,28 +39108,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", @@ -39433,8 +39148,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": { @@ -39464,93 +39179,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", @@ -39560,29 +39200,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", @@ -39592,9 +39220,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" diff --git a/package.json b/package.json index 358ba43fa45..b6842885fc3 100644 --- a/package.json +++ b/package.json @@ -222,7 +222,7 @@ "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": {