From 77de275e6e28fb658fe24d92ff41a2c03a346d0b Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Wed, 12 Nov 2025 12:48:39 +0000 Subject: [PATCH 01/88] For Exec Defaults workspace folder to current directory if not specified --- src/spec-node/devContainersSpecCLI.ts | 4 ++-- src/test/cli.exec.base.ts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/spec-node/devContainersSpecCLI.ts b/src/spec-node/devContainersSpecCLI.ts index 59136695d..8acaef6e8 100644 --- a/src/spec-node/devContainersSpecCLI.ts +++ b/src/spec-node/devContainersSpecCLI.ts @@ -1209,7 +1209,7 @@ function execOptions(y: Argv) { 'docker-compose-path': { type: 'string', description: 'Docker Compose CLI path.' }, 'container-data-folder': { type: 'string', description: 'Container data folder where user data inside the container will be stored.' }, 'container-system-data-folder': { type: 'string', description: 'Container system data folder where system data inside the container will be stored.' }, - 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' }, + 'workspace-folder': { type: 'string', description: 'Workspace folder path. Defaults to current directory if not specified. The devcontainer.json will be looked up relative to this path.' }, 'mount-workspace-git-root': { type: 'boolean', default: true, description: 'Mount the workspace using its Git root.' }, 'container-id': { type: 'string', description: 'Id of the container to run the user commands for.' }, 'id-label': { type: 'string', description: 'Id label(s) of the format name=value. If no --container-id is given the id labels will be used to look up the container. If no --id-label is given, one will be inferred from the --workspace-folder path.' }, @@ -1243,7 +1243,7 @@ function execOptions(y: Argv) { throw new Error('Unmatched argument format: remote-env must match ='); } if (!argv['container-id'] && !idLabels?.length && !argv['workspace-folder']) { - throw new Error('Missing required argument: One of --container-id, --id-label or --workspace-folder is required.'); + argv['workspace-folder'] = process.cwd(); } return true; }); diff --git a/src/test/cli.exec.base.ts b/src/test/cli.exec.base.ts index 10e876595..9ba470af8 100644 --- a/src/test/cli.exec.base.ts +++ b/src/test/cli.exec.base.ts @@ -82,6 +82,22 @@ export function describeTests1({ text, options }: BuildKitOption) { assert.strictEqual(env.FOO, 'BAR'); assert.strictEqual(env.BAZ, ''); }); + it('should exec with default workspace folder (current directory)', async () => { + const originalCwd = process.cwd(); + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + process.chdir(testFolder); + + try { + // Exec without --workspace-folder should use current directory as default + const execRes = await shellExec(`${absoluteCli} exec echo "default workspace test"`); + assert.strictEqual(execRes.error, null); + assert.match(execRes.stdout, /default workspace test/); + } finally { + // Restore original directory + process.chdir(originalCwd); + } + }); }); describe(`with valid (image) config containing features [${text}]`, () => { let containerId: string | null = null; From 070aa87b9829aa05daa6cd0e69835f2915b6632b Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Wed, 12 Nov 2025 13:18:51 +0000 Subject: [PATCH 02/88] update error message --- src/spec-node/devContainersSpecCLI.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spec-node/devContainersSpecCLI.ts b/src/spec-node/devContainersSpecCLI.ts index 8acaef6e8..9ada4519f 100644 --- a/src/spec-node/devContainersSpecCLI.ts +++ b/src/spec-node/devContainersSpecCLI.ts @@ -1209,7 +1209,7 @@ function execOptions(y: Argv) { 'docker-compose-path': { type: 'string', description: 'Docker Compose CLI path.' }, 'container-data-folder': { type: 'string', description: 'Container data folder where user data inside the container will be stored.' }, 'container-system-data-folder': { type: 'string', description: 'Container system data folder where system data inside the container will be stored.' }, - 'workspace-folder': { type: 'string', description: 'Workspace folder path. Defaults to current directory if not specified. The devcontainer.json will be looked up relative to this path.' }, + 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path. If --container-id, --id-label, and --workspace-folder are not provided, this defaults to the current directory.' }, 'mount-workspace-git-root': { type: 'boolean', default: true, description: 'Mount the workspace using its Git root.' }, 'container-id': { type: 'string', description: 'Id of the container to run the user commands for.' }, 'id-label': { type: 'string', description: 'Id label(s) of the format name=value. If no --container-id is given the id labels will be used to look up the container. If no --id-label is given, one will be inferred from the --workspace-folder path.' }, From 260a884f7db745714b38735c3ae979f40dbeadb8 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Mon, 17 Nov 2025 08:29:31 +0000 Subject: [PATCH 03/88] devcontainer outdated command CWD as default folder --- src/spec-node/devContainersSpecCLI.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/spec-node/devContainersSpecCLI.ts b/src/spec-node/devContainersSpecCLI.ts index 9ada4519f..5e2bdf64e 100644 --- a/src/spec-node/devContainersSpecCLI.ts +++ b/src/spec-node/devContainersSpecCLI.ts @@ -1107,14 +1107,20 @@ async function readConfiguration({ function outdatedOptions(y: Argv) { return y.options({ 'user-data-folder': { type: 'string', description: 'Host path to a directory that is intended to be persisted and share state between sessions.' }, - 'workspace-folder': { type: 'string', required: true, description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' }, + 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path. If --workspace-folder is not provided, defaults to the current directory.' }, 'config': { type: 'string', description: 'devcontainer.json path. The default is to use .devcontainer/devcontainer.json or, if that does not exist, .devcontainer.json in the workspace folder.' }, 'output-format': { choices: ['text' as 'text', 'json' as 'json'], default: 'text', description: 'Output format.' }, 'log-level': { choices: ['info' as 'info', 'debug' as 'debug', 'trace' as 'trace'], default: 'info' as 'info', description: 'Log level for the --terminal-log-file. When set to trace, the log level for --log-file will also be set to trace.' }, 'log-format': { choices: ['text' as 'text', 'json' as 'json'], default: 'text' as 'text', description: 'Log format.' }, 'terminal-columns': { type: 'number', implies: ['terminal-rows'], description: 'Number of columns to render the output for. This is required for some of the subprocesses to correctly render their output.' }, 'terminal-rows': { type: 'number', implies: ['terminal-columns'], description: 'Number of rows to render the output for. This is required for some of the subprocesses to correctly render their output.' }, - }); + }) + .check(argv => { + if (!argv['workspace-folder']) { + argv['workspace-folder'] = process.cwd(); + } + return true; + }); } type OutdatedArgs = UnpackArgv>; From afc2a34dceff23fb25b6d887bbfb99529f8ec582 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Mon, 17 Nov 2025 08:46:24 +0000 Subject: [PATCH 04/88] upgradecommand CWD as default folder --- src/spec-node/upgradeCommand.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/spec-node/upgradeCommand.ts b/src/spec-node/upgradeCommand.ts index 3336087c7..b3a9b1cc9 100644 --- a/src/spec-node/upgradeCommand.ts +++ b/src/spec-node/upgradeCommand.ts @@ -23,7 +23,7 @@ import { mapNodeArchitectureToGOARCH, mapNodeOSToGOOS } from '../spec-configurat export function featuresUpgradeOptions(y: Argv) { return y .options({ - 'workspace-folder': { type: 'string', description: 'Workspace folder.', demandOption: true }, + 'workspace-folder': { type: 'string', description: 'Workspace folder. If --workspace-folder is not provided defaults to the current directory.' }, 'docker-path': { type: 'string', description: 'Path to docker executable.', default: 'docker' }, 'docker-compose-path': { type: 'string', description: 'Path to docker-compose executable.', default: 'docker-compose' }, 'config': { type: 'string', description: 'devcontainer.json path. The default is to use .devcontainer/devcontainer.json or, if that does not exist, .devcontainer.json in the workspace folder.' }, @@ -37,7 +37,6 @@ export function featuresUpgradeOptions(y: Argv) { if (argv.feature && !argv['target-version'] || !argv.feature && argv['target-version']) { throw new Error('The \'--target-version\' and \'--feature\' flag must be used together.'); } - if (argv['target-version']) { const targetVersion = argv['target-version']; if (!targetVersion.match(/^\d+(\.\d+(\.\d+)?)?$/)) { @@ -70,7 +69,7 @@ async function featuresUpgrade({ }; let output: Log | undefined; try { - const workspaceFolder = path.resolve(process.cwd(), workspaceFolderArg); + const workspaceFolder = workspaceFolderArg ? path.resolve(process.cwd(), workspaceFolderArg) : process.cwd(); const configFile = configArg ? URI.file(path.resolve(process.cwd(), configArg)) : undefined; const cliHost = await getCLIHost(workspaceFolder, loadNativeModule, true); const extensionPath = path.join(__dirname, '..', '..'); From bbb3c3a1e619a3d08edd1b947c05dcd01418247e Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Mon, 17 Nov 2025 09:20:27 +0000 Subject: [PATCH 05/88] cwd default for build, runusercommands, readconfig --- src/spec-node/devContainersSpecCLI.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/spec-node/devContainersSpecCLI.ts b/src/spec-node/devContainersSpecCLI.ts index 5e2bdf64e..fd8070b59 100644 --- a/src/spec-node/devContainersSpecCLI.ts +++ b/src/spec-node/devContainersSpecCLI.ts @@ -507,7 +507,7 @@ function buildOptions(y: Argv) { 'user-data-folder': { type: 'string', description: 'Host path to a directory that is intended to be persisted and share state between sessions.' }, 'docker-path': { type: 'string', description: 'Docker CLI path.' }, 'docker-compose-path': { type: 'string', description: 'Docker Compose CLI path.' }, - 'workspace-folder': { type: 'string', required: true, description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' }, + 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path. If not provided, defaults to the current directory.' }, 'config': { type: 'string', description: 'devcontainer.json path. The default is to use .devcontainer/devcontainer.json or, if that does not exist, .devcontainer.json in the workspace folder.' }, 'log-level': { choices: ['info' as 'info', 'debug' as 'debug', 'trace' as 'trace'], default: 'info' as 'info', description: 'Log level.' }, 'log-format': { choices: ['text' as 'text', 'json' as 'json'], default: 'text' as 'text', description: 'Log format.' }, @@ -526,6 +526,12 @@ function buildOptions(y: Argv) { 'experimental-lockfile': { type: 'boolean', default: false, hidden: true, description: 'Write lockfile' }, 'experimental-frozen-lockfile': { type: 'boolean', default: false, hidden: true, description: 'Ensure lockfile remains unchanged' }, 'omit-syntax-directive': { type: 'boolean', default: false, hidden: true, description: 'Omit Dockerfile syntax directives' }, + }) + .check(argv => { + if (!argv['workspace-folder']) { + argv['workspace-folder'] = process.cwd(); + } + return true; }); } @@ -752,7 +758,7 @@ function runUserCommandsOptions(y: Argv) { 'docker-compose-path': { type: 'string', description: 'Docker Compose CLI path.' }, 'container-data-folder': { type: 'string', description: 'Container data folder where user data inside the container will be stored.' }, 'container-system-data-folder': { type: 'string', description: 'Container system data folder where system data inside the container will be stored.' }, - 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' }, + 'workspace-folder': { type: 'string', description: 'Workspace folder path.The devcontainer.json will be looked up relative to this path. If --container-id, --id-label, and --workspace-folder are not provided, this defaults to the current directory.' }, 'mount-workspace-git-root': { type: 'boolean', default: true, description: 'Mount the workspace using its Git root.' }, 'container-id': { type: 'string', description: 'Id of the container to run the user commands for.' }, 'id-label': { type: 'string', description: 'Id label(s) of the format name=value. If no --container-id is given the id labels will be used to look up the container. If no --id-label is given, one will be inferred from the --workspace-folder path.' }, @@ -785,7 +791,7 @@ function runUserCommandsOptions(y: Argv) { throw new Error('Unmatched argument format: remote-env must match ='); } if (!argv['container-id'] && !idLabels?.length && !argv['workspace-folder']) { - throw new Error('Missing required argument: One of --container-id, --id-label or --workspace-folder is required.'); + argv['workspace-folder'] = process.cwd(); } return true; }); @@ -954,7 +960,7 @@ function readConfigurationOptions(y: Argv) { 'user-data-folder': { type: 'string', description: 'Host path to a directory that is intended to be persisted and share state between sessions.' }, 'docker-path': { type: 'string', description: 'Docker CLI path.' }, 'docker-compose-path': { type: 'string', description: 'Docker Compose CLI path.' }, - 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' }, + 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path. If --container-id, --id-label, and --workspace-folder are not provided, this defaults to the current directory.' }, 'mount-workspace-git-root': { type: 'boolean', default: true, description: 'Mount the workspace using its Git root.' }, 'container-id': { type: 'string', description: 'Id of the container to run the user commands for.' }, 'id-label': { type: 'string', description: 'Id label(s) of the format name=value. If no --container-id is given the id labels will be used to look up the container. If no --id-label is given, one will be inferred from the --workspace-folder path.' }, @@ -975,7 +981,7 @@ function readConfigurationOptions(y: Argv) { throw new Error('Unmatched argument format: id-label must match ='); } if (!argv['container-id'] && !idLabels?.length && !argv['workspace-folder']) { - throw new Error('Missing required argument: One of --container-id, --id-label or --workspace-folder is required.'); + argv['workspace-folder'] = process.cwd(); } return true; }); From 7a3282a3ca85945080741f58bcf83a063f9e1233 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:19:28 +0000 Subject: [PATCH 06/88] Type 'undefined' is not assignable to type 'string'. --- src/spec-node/devContainersSpecCLI.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/spec-node/devContainersSpecCLI.ts b/src/spec-node/devContainersSpecCLI.ts index fd8070b59..4bd7af245 100644 --- a/src/spec-node/devContainersSpecCLI.ts +++ b/src/spec-node/devContainersSpecCLI.ts @@ -580,7 +580,7 @@ async function doBuild({ await Promise.all(disposables.map(d => d())); }; try { - const workspaceFolder = path.resolve(process.cwd(), workspaceFolderArg); + const workspaceFolder = workspaceFolderArg ? path.resolve(process.cwd(), workspaceFolderArg) : process.cwd(); const configFile: URI | undefined = configParam ? URI.file(path.resolve(process.cwd(), configParam)) : undefined; const overrideConfigFile: URI | undefined = /* overrideConfig ? URI.file(path.resolve(process.cwd(), overrideConfig)) : */ undefined; const addCacheFroms = addCacheFrom ? (Array.isArray(addCacheFrom) ? addCacheFrom as string[] : [addCacheFrom]) : []; @@ -1151,7 +1151,7 @@ async function outdated({ }; let output: Log | undefined; try { - const workspaceFolder = path.resolve(process.cwd(), workspaceFolderArg); + const workspaceFolder = workspaceFolderArg ? path.resolve(process.cwd(), workspaceFolderArg) : process.cwd(); const configFile = configParam ? URI.file(path.resolve(process.cwd(), configParam)) : undefined; const cliHost = await getCLIHost(workspaceFolder, loadNativeModule, logFormat === 'text'); const extensionPath = path.join(__dirname, '..', '..'); From 633f21a3fdd1825753f69a4c4a8f5530a48b3170 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:27:04 +0000 Subject: [PATCH 07/88] Remove check --- src/spec-node/devContainersSpecCLI.ts | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/spec-node/devContainersSpecCLI.ts b/src/spec-node/devContainersSpecCLI.ts index 4bd7af245..fcc4f53d5 100644 --- a/src/spec-node/devContainersSpecCLI.ts +++ b/src/spec-node/devContainersSpecCLI.ts @@ -526,12 +526,6 @@ function buildOptions(y: Argv) { 'experimental-lockfile': { type: 'boolean', default: false, hidden: true, description: 'Write lockfile' }, 'experimental-frozen-lockfile': { type: 'boolean', default: false, hidden: true, description: 'Ensure lockfile remains unchanged' }, 'omit-syntax-directive': { type: 'boolean', default: false, hidden: true, description: 'Omit Dockerfile syntax directives' }, - }) - .check(argv => { - if (!argv['workspace-folder']) { - argv['workspace-folder'] = process.cwd(); - } - return true; }); } @@ -1120,13 +1114,7 @@ function outdatedOptions(y: Argv) { 'log-format': { choices: ['text' as 'text', 'json' as 'json'], default: 'text' as 'text', description: 'Log format.' }, 'terminal-columns': { type: 'number', implies: ['terminal-rows'], description: 'Number of columns to render the output for. This is required for some of the subprocesses to correctly render their output.' }, 'terminal-rows': { type: 'number', implies: ['terminal-columns'], description: 'Number of rows to render the output for. This is required for some of the subprocesses to correctly render their output.' }, - }) - .check(argv => { - if (!argv['workspace-folder']) { - argv['workspace-folder'] = process.cwd(); - } - return true; - }); + }); } type OutdatedArgs = UnpackArgv>; From b3821cbc2f4bfea155693de1b8c49bd3e6c53cf2 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:27:44 +0000 Subject: [PATCH 08/88] template apply --- src/spec-node/templatesCLI/apply.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/spec-node/templatesCLI/apply.ts b/src/spec-node/templatesCLI/apply.ts index ed410a8c7..24fc33d88 100644 --- a/src/spec-node/templatesCLI/apply.ts +++ b/src/spec-node/templatesCLI/apply.ts @@ -6,11 +6,12 @@ import * as jsonc from 'jsonc-parser'; import { UnpackArgv } from '../devContainersSpecCLI'; import { fetchTemplate, SelectedTemplate, TemplateFeatureOption, TemplateOptions } from '../../spec-configuration/containerTemplatesOCI'; import { runAsyncHandler } from '../utils'; +import path from 'path'; export function templateApplyOptions(y: Argv) { return y .options({ - 'workspace-folder': { type: 'string', alias: 'w', demandOption: true, default: '.', description: 'Target workspace folder to apply Template' }, + 'workspace-folder': { type: 'string', alias: 'w', default: '.', description: 'Target workspace folder to apply Template' }, 'template-id': { type: 'string', alias: 't', demandOption: true, description: 'Reference to a Template in a supported OCI registry' }, 'template-args': { type: 'string', alias: 'a', default: '{}', description: 'Arguments to replace within the provided Template, provided as JSON' }, 'features': { type: 'string', alias: 'f', default: '[]', description: 'Features to add to the provided Template, provided as JSON.' }, @@ -30,7 +31,7 @@ export function templateApplyHandler(args: TemplateApplyArgs) { } async function templateApply({ - 'workspace-folder': workspaceFolder, + 'workspace-folder': workspaceFolderArg, 'template-id': templateId, 'template-args': templateArgs, 'features': featuresArgs, @@ -42,6 +43,7 @@ async function templateApply({ const dispose = async () => { await Promise.all(disposables.map(d => d())); }; + const workspaceFolder = workspaceFolderArg ? path.resolve(process.cwd(), workspaceFolderArg) : process.cwd(); const pkg = getPackageConfig(); From ac701cc86cb19c207440b7080b91b2345477d689 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:38:06 +0000 Subject: [PATCH 09/88] feature resolve-dependencies --- src/spec-node/featuresCLI/resolveDependencies.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/spec-node/featuresCLI/resolveDependencies.ts b/src/spec-node/featuresCLI/resolveDependencies.ts index 93e569ff6..7996a5281 100644 --- a/src/spec-node/featuresCLI/resolveDependencies.ts +++ b/src/spec-node/featuresCLI/resolveDependencies.ts @@ -30,7 +30,7 @@ export function featuresResolveDependenciesOptions(y: Argv) { return y .options({ 'log-level': { choices: ['error' as 'error', 'info' as 'info', 'debug' as 'debug', 'trace' as 'trace'], default: 'error' as 'error', description: 'Log level.' }, - 'workspace-folder': { type: 'string', description: 'Workspace folder to use for the configuration.', demandOption: true }, + 'workspace-folder': { type: 'string', description: 'Workspace folder to use for the configuration.' }, }); } @@ -41,7 +41,7 @@ export function featuresResolveDependenciesHandler(args: featuresResolveDependen } async function featuresResolveDependencies({ - 'workspace-folder': workspaceFolder, + 'workspace-folder': workspaceFolderArg, 'log-level': inputLogLevel, }: featuresResolveDependenciesArgs) { const disposables: (() => Promise | undefined)[] = []; @@ -62,6 +62,8 @@ async function featuresResolveDependencies({ let jsonOutput: JsonOutput = {}; + const workspaceFolder = workspaceFolderArg ? path.resolve(process.cwd(), workspaceFolderArg) : process.cwd(); + // Detect path to dev container config let configPath = path.join(workspaceFolder, '.devcontainer.json'); if (!(await isLocalFile(configPath))) { From 7533b0be72edc199923a9679aac162658101a67a Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Wed, 19 Nov 2025 08:13:15 +0000 Subject: [PATCH 10/88] update cli version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index abbde6d76..183b7c685 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.80.2", + "version": "0.80.3", "bin": { "devcontainer": "devcontainer.js" }, From 3fbe2bcc83275fb504cca71aac3a7e50bb783473 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Fri, 21 Nov 2025 08:16:08 +0000 Subject: [PATCH 11/88] exec test cases --- src/test/cli.exec.base.ts | 57 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/test/cli.exec.base.ts b/src/test/cli.exec.base.ts index 9ba470af8..57ba48c0c 100644 --- a/src/test/cli.exec.base.ts +++ b/src/test/cli.exec.base.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import * as path from 'path'; +import * as os from 'os'; import { BuildKitOption, commandMarkerTests, devContainerDown, devContainerStop, devContainerUp, pathExists, shellBufferExec, shellExec, shellPtyExec } from './testUtils'; const pkg = require('../../package.json'); @@ -209,7 +210,7 @@ export function describeTests1({ text, options }: BuildKitOption) { } export function describeTests2({ text, options }: BuildKitOption) { - + describe('Dev Containers CLI', function () { this.timeout('300s'); @@ -422,6 +423,60 @@ export function describeTests2({ text, options }: BuildKitOption) { await shellExec(`docker rm -f ${response.containerId}`); }); + + describe.only('Command exec with default workspace', () => { + it('should fail gracefully when no config in current directory and no container-id', async () => { + const tempDir = path.join(os.tmpdir(), 'devcontainer-exec-test-' + Date.now()); + await shellExec(`mkdir -p ${tempDir}`); + const originalCwd = process.cwd(); + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + try { + process.chdir(tempDir); + let success = false; + try { + // Test exec without --workspace-folder (should default to current directory with no config) + await shellExec(`${absoluteCli} exec echo "test"`); + success = true; + } catch (error) { + console.log('Caught error as expected: ', error.stderr); + // Should fail because there's no container or config + assert.equal(error.error.code, 1, 'Should fail with exit code 1'); + } + assert.equal(success, false, 'expect non-successful call'); + } finally { + process.chdir(originalCwd); + await shellExec(`rm -rf ${tempDir}`); + } + }); + + describe('with valid config in current directory', () => { + let containerId: string | null = null; + const testFolder = `${__dirname}/configs/image`; + + beforeEach(async () => { + containerId = (await devContainerUp(cli, testFolder, options)).containerId; + }); + + afterEach(async () => await devContainerDown({ containerId })); + + it('should execute command successfully when using current directory', async () => { + const originalCwd = process.cwd(); + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + try { + process.chdir(testFolder); + // Test exec without --workspace-folder (should default to current directory) + const res = await shellExec(`${absoluteCli} exec echo "hello world"`); + assert.strictEqual(res.error, null); + assert.match(res.stdout, /hello world/); + } finally { + process.chdir(originalCwd); + } + }); + }); + + }); }); }); } From 621c930ed7a3de6317a99088aa05ef526d0d2d72 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Fri, 21 Nov 2025 08:19:10 +0000 Subject: [PATCH 12/88] remove only --- src/test/cli.exec.base.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/cli.exec.base.ts b/src/test/cli.exec.base.ts index 57ba48c0c..681aac775 100644 --- a/src/test/cli.exec.base.ts +++ b/src/test/cli.exec.base.ts @@ -210,7 +210,7 @@ export function describeTests1({ text, options }: BuildKitOption) { } export function describeTests2({ text, options }: BuildKitOption) { - + describe('Dev Containers CLI', function () { this.timeout('300s'); @@ -424,7 +424,7 @@ export function describeTests2({ text, options }: BuildKitOption) { await shellExec(`docker rm -f ${response.containerId}`); }); - describe.only('Command exec with default workspace', () => { + describe('Command exec with default workspace', () => { it('should fail gracefully when no config in current directory and no container-id', async () => { const tempDir = path.join(os.tmpdir(), 'devcontainer-exec-test-' + Date.now()); await shellExec(`mkdir -p ${tempDir}`); From fc5e5703ae6e60fa2ce0de858596156f20bf8758 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Fri, 21 Nov 2025 09:57:59 +0000 Subject: [PATCH 13/88] devcontainer outdated test --- src/test/container-features/lockfile.test.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/container-features/lockfile.test.ts b/src/test/container-features/lockfile.test.ts index 57034e6f2..fb3894218 100644 --- a/src/test/container-features/lockfile.test.ts +++ b/src/test/container-features/lockfile.test.ts @@ -258,4 +258,24 @@ describe('Lockfile', function () { await cleanup(); } }); + + it('outdated command should work with default workspace folder', async () => { + const workspaceFolder = path.join(__dirname, 'configs/lockfile-outdated-command'); + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + + const originalCwd = process.cwd(); + try { + process.chdir(workspaceFolder); + const res = await shellExec(`${absoluteCli} outdated --output-format json`); + const response = JSON.parse(res.stdout); + + // Should have same structure as the test with explicit workspace-folder + assert.ok(response.features); + assert.ok(response.features['ghcr.io/devcontainers/features/git:1.0']); + assert.strictEqual(response.features['ghcr.io/devcontainers/features/git:1.0'].current, '1.0.4'); + } finally { + process.chdir(originalCwd); + } + }); }); \ No newline at end of file From 3e34e78d0223f3bd9035e13e57cce959258ea691 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:10:15 +0000 Subject: [PATCH 14/88] upgrade command test --- src/test/container-features/lockfile.test.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/container-features/lockfile.test.ts b/src/test/container-features/lockfile.test.ts index fb3894218..96fcd4045 100644 --- a/src/test/container-features/lockfile.test.ts +++ b/src/test/container-features/lockfile.test.ts @@ -278,4 +278,24 @@ describe('Lockfile', function () { process.chdir(originalCwd); } }); + + it('upgrade command should work with default workspace folder', async () => { + const workspaceFolder = path.join(__dirname, 'configs/lockfile-upgrade-command'); + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + + const lockfilePath = path.join(workspaceFolder, '.devcontainer-lock.json'); + await cpLocal(path.join(workspaceFolder, 'outdated.devcontainer-lock.json'), lockfilePath); + + const originalCwd = process.cwd(); + try { + process.chdir(workspaceFolder); + await shellExec(`${absoluteCli} upgrade`); + const actual = await readLocalFile(lockfilePath); + const expected = await readLocalFile(path.join(workspaceFolder, 'upgraded.devcontainer-lock.json')); + assert.equal(actual.toString(), expected.toString()); + } finally { + process.chdir(originalCwd); + } + }); }); \ No newline at end of file From 202aa2d90af3ae55f341630a41ebe8cb039c8a25 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:19:26 +0000 Subject: [PATCH 15/88] run-user-commands test --- src/test/cli.test.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/test/cli.test.ts b/src/test/cli.test.ts index 522c9073d..d9f16dde3 100644 --- a/src/test/cli.test.ts +++ b/src/test/cli.test.ts @@ -68,6 +68,37 @@ describe('Dev Containers CLI', function () { await shellExec(`docker rm -f ${upResponse.containerId}`); }); + + it('run-user-commands should run with default workspace folder (current directory)', async () => { + const testFolder = `${__dirname}/configs/image`; + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + + // First, ensure container is up + const upRes = await shellExec(`${cli} up --workspace-folder ${testFolder} --skip-post-create`); + const upResponse = JSON.parse(upRes.stdout); + assert.strictEqual(upResponse.outcome, 'success'); + const containerId = upResponse.containerId; + + const originalCwd = process.cwd(); + try { + // Change to workspace folder + process.chdir(testFolder); + + // Run user commands without --workspace-folder should use current directory as default + const runRes = await shellExec(`${absoluteCli} run-user-commands`); + const runResponse = JSON.parse(runRes.stdout); + assert.strictEqual(runResponse.outcome, 'success'); + + // Verify that the postCreateCommand was executed + await shellExec(`docker exec ${containerId} test -f /postCreateCommand.txt`); + } finally { + // Restore original directory + process.chdir(originalCwd); + // Clean up container + await shellExec(`docker rm -f ${containerId}`); + } + }); }); describe('Command read-configuration', () => { From d2d47d6fdcfbc13fee9f6d1cb98b9882d3876992 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Mon, 24 Nov 2025 07:13:14 +0000 Subject: [PATCH 16/88] Read-user commands test --- src/test/cli.test.ts | 60 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/test/cli.test.ts b/src/test/cli.test.ts index d9f16dde3..f409cb0fd 100644 --- a/src/test/cli.test.ts +++ b/src/test/cli.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import * as path from 'path'; +import * as os from 'os'; import { devContainerDown, devContainerUp, shellExec } from './testUtils'; const pkg = require('../../package.json'); @@ -99,6 +100,28 @@ describe('Dev Containers CLI', function () { await shellExec(`docker rm -f ${containerId}`); } }); + + it('run-user-commands should fail gracefully when no config in current directory and no container-id', async () => { + const tempDir = path.join(os.tmpdir(), 'devcontainer-run-test-' + Date.now()); + await shellExec(`mkdir -p ${tempDir}`); + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + const originalCwd = process.cwd(); + try { + process.chdir(tempDir); + let success = false; + try { + await shellExec(`${absoluteCli} run-user-commands`); + success = true; + } catch (error) { + assert.equal(error.error.code, 1, 'Should fail with exit code 1'); + } + assert.equal(success, false, 'expect non-successful call'); + } finally { + process.chdir(originalCwd); + await shellExec(`rm -rf ${tempDir}`); + } + }); }); describe('Command read-configuration', () => { @@ -155,5 +178,42 @@ describe('Dev Containers CLI', function () { const response = JSON.parse(res.stdout); assert.strictEqual(response.configuration.remoteEnv.SUBFOLDER_CONFIG_REMOTE_ENV, 'true'); }); + + it('should use current directory for read-configuration when no workspace-folder provided', async () => { + const testFolder = `${__dirname}/configs/image`; + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + const originalCwd = process.cwd(); + try { + process.chdir(testFolder); + const res = await shellExec(`${absoluteCli} read-configuration`); + const response = JSON.parse(res.stdout); + assert.equal(response.configuration.image, 'ubuntu:latest'); + } finally { + process.chdir(originalCwd); + } + }); + + it('should fail gracefully when no workspace-folder and no config in current directory', async () => { + const tempDir = path.join(os.tmpdir(), 'devcontainer-test-' + Date.now()); + await shellExec(`mkdir -p ${tempDir}`); + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + const originalCwd = process.cwd(); + try { + process.chdir(tempDir); + let success = false; + try { + await shellExec(`${absoluteCli} read-configuration`); + success = true; + } catch (error) { + assert.equal(error.error.code, 1, 'Should fail with exit code 1'); + } + assert.equal(success, false, 'expect non-successful call'); + } finally { + process.chdir(originalCwd); + await shellExec(`rm -rf ${tempDir}`); + } + }); }); }); \ No newline at end of file From 98c33163095d7884b4af54d4b747561e2affd2fe Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Mon, 24 Nov 2025 07:28:57 +0000 Subject: [PATCH 17/88] build test --- src/test/cli.build.test.ts | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/test/cli.build.test.ts b/src/test/cli.build.test.ts index 0dfae0427..c57e2f4d5 100644 --- a/src/test/cli.build.test.ts +++ b/src/test/cli.build.test.ts @@ -433,5 +433,49 @@ describe('Dev Containers CLI', function () { const details = JSON.parse((await shellExec(`docker inspect ${response.imageName}`)).stdout)[0] as ImageDetails; assert.strictEqual(details.Config.Labels?.test_build_options, 'success'); }); + + it('should use current directory for build when no workspace-folder provided', async function () { + const testFolder = `${__dirname}/configs/image`; // Use simpler config without features + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + const originalCwd = process.cwd(); + console.log(`Original cwd: ${originalCwd}`); + console.log(`Changing to test folder: ${testFolder}`); + try { + process.chdir(testFolder); + const res = await shellExec(`${absoluteCli} build`); + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'success'); + assert.ok(response.imageName); + } finally { + process.chdir(originalCwd); + } + }); + + it('should fail gracefully when no workspace-folder and no config in current directory', async function () { + const tempDir = path.join(os.tmpdir(), 'devcontainer-build-test-' + Date.now()); + await shellExec(`mkdir -p ${tempDir}`); + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + const originalCwd = process.cwd(); + try { + process.chdir(tempDir); + let success = false; + try { + await shellExec(`${absoluteCli} build`); + success = true; + } catch (error) { + assert.equal(error.error.code, 1, 'Should fail with exit code 1'); + const res = JSON.parse(error.stdout); + assert.equal(res.outcome, 'error'); + assert.match(res.message, /Dev container config .* not found/); + } + assert.equal(success, false, 'expect non-successful call'); + } finally { + process.chdir(originalCwd); + await shellExec(`rm -rf ${tempDir}`); + } + }); + }); }); From bcb365297e266f4bc1c14477ecbe2d944555c10f Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Mon, 24 Nov 2025 07:51:54 +0000 Subject: [PATCH 18/88] devcontainer up cwd and test --- src/spec-node/devContainersSpecCLI.ts | 10 +++--- src/test/cli.up.test.ts | 48 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/spec-node/devContainersSpecCLI.ts b/src/spec-node/devContainersSpecCLI.ts index fcc4f53d5..2183318e6 100644 --- a/src/spec-node/devContainersSpecCLI.ts +++ b/src/spec-node/devContainersSpecCLI.ts @@ -103,7 +103,7 @@ function provisionOptions(y: Argv) { 'docker-compose-path': { type: 'string', description: 'Docker Compose CLI path.' }, 'container-data-folder': { type: 'string', description: 'Container data folder where user data inside the container will be stored.' }, 'container-system-data-folder': { type: 'string', description: 'Container system data folder where system data inside the container will be stored.' }, - 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' }, + 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path. If not provided, defaults to the current directory.' }, 'workspace-mount-consistency': { choices: ['consistent' as 'consistent', 'cached' as 'cached', 'delegated' as 'delegated'], default: 'cached' as 'cached', description: 'Workspace mount consistency.' }, 'gpu-availability': { choices: ['all' as 'all', 'detect' as 'detect', 'none' as 'none'], default: 'detect' as 'detect', description: 'Availability of GPUs in case the dev container requires any. `all` expects a GPU to be available.' }, 'mount-workspace-git-root': { type: 'boolean', default: true, description: 'Mount the workspace using its Git root.' }, @@ -148,11 +148,9 @@ function provisionOptions(y: Argv) { if (idLabels?.some(idLabel => !/.+=.+/.test(idLabel))) { throw new Error('Unmatched argument format: id-label must match ='); } - if (!(argv['workspace-folder'] || argv['id-label'])) { - throw new Error('Missing required argument: workspace-folder or id-label'); - } - if (!(argv['workspace-folder'] || argv['override-config'])) { - throw new Error('Missing required argument: workspace-folder or override-config'); + // Default workspace-folder to current directory if not provided and no id-label or override-config + if (!argv['workspace-folder'] && !argv['id-label'] && !argv['override-config']) { + argv['workspace-folder'] = process.cwd(); } const mounts = (argv.mount && (Array.isArray(argv.mount) ? argv.mount : [argv.mount])) as string[] | undefined; if (mounts?.some(mount => !mountRegex.test(mount))) { diff --git a/src/test/cli.up.test.ts b/src/test/cli.up.test.ts index 94515e89a..688a4c84d 100644 --- a/src/test/cli.up.test.ts +++ b/src/test/cli.up.test.ts @@ -310,4 +310,52 @@ describe('Dev Containers CLI', function () { await shellExec(`docker rm -f ${response.containerId}`); }); }); + + describe('Command up with default workspace', () => { + it('should create and start container using current directory config', async () => { + const testFolder = `${__dirname}/configs/image`; + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + const originalCwd = process.cwd(); + let containerId: string | null = null; + try { + process.chdir(testFolder); + const res = await shellExec(`${absoluteCli} up`); + const response = JSON.parse(res.stdout); + containerId = response.containerId; + assert.equal(response.outcome, 'success'); + assert.ok(containerId); + } finally { + process.chdir(originalCwd); + if (containerId) { + await shellExec(`docker rm -f ${containerId}`); + } + } + }); + + it('should fail gracefully when no config in current directory', async () => { + const tempDir = path.join(os.tmpdir(), 'devcontainer-up-test-' + Date.now()); + await shellExec(`mkdir -p ${tempDir}`); + const absoluteTmpPath = path.resolve(__dirname, 'tmp'); + const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; + const originalCwd = process.cwd(); + try { + process.chdir(tempDir); + let success = false; + try { + await shellExec(`${absoluteCli} up`); + success = true; + } catch (error) { + assert.equal(error.error.code, 1, 'Should fail with exit code 1'); + const res = JSON.parse(error.stdout); + assert.equal(res.outcome, 'error'); + assert.match(res.message, /Dev container config .* not found/); + } + assert.equal(success, false, 'expect non-successful call'); + } finally { + process.chdir(originalCwd); + await shellExec(`rm -rf ${tempDir}`); + } + }); + }); }); \ No newline at end of file From 88c27339b2132533ff3e03ad113afe3fbca7d082 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 07:15:27 +0000 Subject: [PATCH 19/88] Bump the all group across 1 directory with 8 updates Bumps the all group with 8 updates in the / directory: | Package | From | To | | --- | --- | --- | | [js-yaml](https://github.com/nodeca/js-yaml) | `4.1.0` | `4.1.1` | | [semver](https://github.com/npm/node-semver) | `7.7.2` | `7.7.3` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `18.19.127` | `18.19.130` | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.44.1` | `8.47.0` | | [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.44.1` | `8.47.0` | | [esbuild](https://github.com/evanw/esbuild) | `0.25.10` | `0.27.0` | | [mocha](https://github.com/mochajs/mocha) | `11.7.2` | `11.7.5` | | [typescript](https://github.com/microsoft/TypeScript) | `5.9.2` | `5.9.3` | Updates `js-yaml` from 4.1.0 to 4.1.1 - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1) Updates `semver` from 7.7.2 to 7.7.3 - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v7.7.2...v7.7.3) Updates `@types/node` from 18.19.127 to 18.19.130 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@typescript-eslint/eslint-plugin` from 8.44.1 to 8.47.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.47.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 8.44.1 to 8.47.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.47.0/packages/parser) Updates `esbuild` from 0.25.10 to 0.27.0 - [Release notes](https://github.com/evanw/esbuild/releases) - [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md) - [Commits](https://github.com/evanw/esbuild/compare/v0.25.10...v0.27.0) Updates `mocha` from 11.7.2 to 11.7.5 - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/v11.7.5/CHANGELOG.md) - [Commits](https://github.com/mochajs/mocha/compare/v11.7.2...v11.7.5) Updates `typescript` from 5.9.2 to 5.9.3 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.2...v5.9.3) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 4.1.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all - dependency-name: semver dependency-version: 7.7.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all - dependency-name: "@types/node" dependency-version: 18.19.130 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all - dependency-name: "@typescript-eslint/eslint-plugin" dependency-version: 8.47.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all - dependency-name: "@typescript-eslint/parser" dependency-version: 8.47.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all - dependency-name: esbuild dependency-version: 0.27.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all - dependency-name: mocha dependency-version: 11.7.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all - dependency-name: typescript dependency-version: 5.9.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 473 ++++++++++++++++++++++++++------------------------- 2 files changed, 238 insertions(+), 237 deletions(-) diff --git a/package.json b/package.json index abbde6d76..07aeda6bb 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "@typescript-eslint/parser": "^8.26.0", "chai": "^4.5.0", "copyfiles": "^2.4.1", - "esbuild": "^0.25.0", + "esbuild": "^0.27.0", "eslint": "^8.57.0", "event-stream": "^4.0.1", "gulp-eslint": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index bfd781f71..c7771cf81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23,135 +23,135 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@esbuild/aix-ppc64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz#ee6b7163a13528e099ecf562b972f2bcebe0aa97" - integrity sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw== - -"@esbuild/android-arm64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz#115fc76631e82dd06811bfaf2db0d4979c16e2cb" - integrity sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg== - -"@esbuild/android-arm@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.10.tgz#8d5811912da77f615398611e5bbc1333fe321aa9" - integrity sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w== - -"@esbuild/android-x64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.10.tgz#e3e96516b2d50d74105bb92594c473e30ddc16b1" - integrity sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg== - -"@esbuild/darwin-arm64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz#6af6bb1d05887dac515de1b162b59dc71212ed76" - integrity sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA== - -"@esbuild/darwin-x64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz#99ae82347fbd336fc2d28ffd4f05694e6e5b723d" - integrity sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg== - -"@esbuild/freebsd-arm64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz#0c6d5558a6322b0bdb17f7025c19bd7d2359437d" - integrity sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg== - -"@esbuild/freebsd-x64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz#8c35873fab8c0857a75300a3dcce4324ca0b9844" - integrity sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA== - -"@esbuild/linux-arm64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz#3edc2f87b889a15b4cedaf65f498c2bed7b16b90" - integrity sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ== - -"@esbuild/linux-arm@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz#86501cfdfb3d110176d80c41b27ed4611471cde7" - integrity sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg== - -"@esbuild/linux-ia32@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz#e6589877876142537c6864680cd5d26a622b9d97" - integrity sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ== - -"@esbuild/linux-loong64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz#11119e18781f136d8083ea10eb6be73db7532de8" - integrity sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg== - -"@esbuild/linux-mips64el@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz#3052f5436b0c0c67a25658d5fc87f045e7def9e6" - integrity sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA== - -"@esbuild/linux-ppc64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz#2f098920ee5be2ce799f35e367b28709925a8744" - integrity sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA== - -"@esbuild/linux-riscv64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz#fa51d7fd0a22a62b51b4b94b405a3198cf7405dd" - integrity sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA== - -"@esbuild/linux-s390x@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz#a27642e36fc282748fdb38954bd3ef4f85791e8a" - integrity sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew== - -"@esbuild/linux-x64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz#9d9b09c0033d17529570ced6d813f98315dfe4e9" - integrity sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA== - -"@esbuild/netbsd-arm64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz#25c09a659c97e8af19e3f2afd1c9190435802151" - integrity sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A== - -"@esbuild/netbsd-x64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz#7fa5f6ffc19be3a0f6f5fd32c90df3dc2506937a" - integrity sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig== - -"@esbuild/openbsd-arm64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz#8faa6aa1afca0c6d024398321d6cb1c18e72a1c3" - integrity sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw== - -"@esbuild/openbsd-x64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz#a42979b016f29559a8453d32440d3c8cd420af5e" - integrity sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw== - -"@esbuild/openharmony-arm64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz#fd87bfeadd7eeb3aa384bbba907459ffa3197cb1" - integrity sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag== - -"@esbuild/sunos-x64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz#3a18f590e36cb78ae7397976b760b2b8c74407f4" - integrity sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ== - -"@esbuild/win32-arm64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz#e71741a251e3fd971408827a529d2325551f530c" - integrity sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw== - -"@esbuild/win32-ia32@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz#c6f010b5d3b943d8901a0c87ea55f93b8b54bf94" - integrity sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw== - -"@esbuild/win32-x64@0.25.10": - version "0.25.10" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz#e4b3e255a1b4aea84f6e1d2ae0b73f826c3785bd" - integrity sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw== +"@esbuild/aix-ppc64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz#1d8be43489a961615d49e037f1bfa0f52a773737" + integrity sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A== + +"@esbuild/android-arm64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz#bd1763194aad60753fa3338b1ba9bda974b58724" + integrity sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ== + +"@esbuild/android-arm@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.0.tgz#69c7b57f02d3b3618a5ba4f82d127b57665dc397" + integrity sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ== + +"@esbuild/android-x64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.0.tgz#6ea22b5843acb23243d0126c052d7d3b6a11ca90" + integrity sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q== + +"@esbuild/darwin-arm64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz#5ad7c02bc1b1a937a420f919afe40665ba14ad1e" + integrity sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg== + +"@esbuild/darwin-x64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz#48470c83c5fd6d1fc7c823c2c603aeee96e101c9" + integrity sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g== + +"@esbuild/freebsd-arm64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz#d5a8effd8b0be7be613cd1009da34d629d4c2457" + integrity sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw== + +"@esbuild/freebsd-x64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz#9bde638bda31aa244d6d64dbafafb41e6e799bcc" + integrity sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g== + +"@esbuild/linux-arm64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz#96008c3a207d8ca495708db714c475ea5bf7e2af" + integrity sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ== + +"@esbuild/linux-arm@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz#9b47cb0f222e567af316e978c7f35307db97bc0e" + integrity sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ== + +"@esbuild/linux-ia32@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz#d1e1e38d406cbdfb8a49f4eca0c25bbc344e18cc" + integrity sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw== + +"@esbuild/linux-loong64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz#c13bc6a53e3b69b76f248065bebee8415b44dfce" + integrity sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg== + +"@esbuild/linux-mips64el@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz#05f8322eb0a96ce1bfbc59691abe788f71e2d217" + integrity sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg== + +"@esbuild/linux-ppc64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz#6fc5e7af98b4fb0c6a7f0b73ba837ce44dc54980" + integrity sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA== + +"@esbuild/linux-riscv64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz#508afa9f69a3f97368c0bf07dd894a04af39d86e" + integrity sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ== + +"@esbuild/linux-s390x@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz#21fda656110ee242fc64f87a9e0b0276d4e4ec5b" + integrity sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w== + +"@esbuild/linux-x64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz#1758a85dcc09b387fd57621643e77b25e0ccba59" + integrity sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw== + +"@esbuild/netbsd-arm64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz#a0131159f4db6e490da35cc4bb51ef0d03b7848a" + integrity sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w== + +"@esbuild/netbsd-x64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz#6f4877d7c2ba425a2b80e4330594e0b43caa2d7d" + integrity sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA== + +"@esbuild/openbsd-arm64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz#cbefbd4c2f375cebeb4f965945be6cf81331bd01" + integrity sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ== + +"@esbuild/openbsd-x64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz#31fa9e8649fc750d7c2302c8b9d0e1547f57bc84" + integrity sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A== + +"@esbuild/openharmony-arm64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz#03727780f1fdf606e7b56193693a715d9f1ee001" + integrity sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA== + +"@esbuild/sunos-x64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz#866a35f387234a867ced35af8906dfffb073b9ff" + integrity sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA== + +"@esbuild/win32-arm64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz#53de43a9629b8a34678f28cd56cc104db1b67abb" + integrity sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg== + +"@esbuild/win32-ia32@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz#924d2aed8692fea5d27bfb6500f9b8b9c1a34af4" + integrity sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ== + +"@esbuild/win32-x64@0.27.0": + version "0.27.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz#64995295227e001f2940258617c6674efb3ac48d" + integrity sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg== "@eslint-community/eslint-utils@^4.2.0": version "4.4.1" @@ -359,9 +359,9 @@ undici-types "~6.20.0" "@types/node@^18.19.127": - version "18.19.127" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.127.tgz#7c2e47fa79ad7486134700514d4a975c4607f09d" - integrity sha512-gSjxjrnKXML/yo0BO099uPixMqfpJU0TKYjpfLU7TrtA2WWDki412Np/RSTPRil1saKBhvVVKzVx/p/6p94nVA== + version "18.19.130" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.130.tgz#da4c6324793a79defb7a62cba3947ec5add00d59" + integrity sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg== dependencies: undici-types "~5.26.4" @@ -413,15 +413,15 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^8.26.0": - version "8.44.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.1.tgz#011a2b5913d297b3d9d77f64fb78575bab01a1b3" - integrity sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw== + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz#c53edeec13a79483f4ca79c298d5231b02e9dc17" + integrity sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.44.1" - "@typescript-eslint/type-utils" "8.44.1" - "@typescript-eslint/utils" "8.44.1" - "@typescript-eslint/visitor-keys" "8.44.1" + "@typescript-eslint/scope-manager" "8.47.0" + "@typescript-eslint/type-utils" "8.47.0" + "@typescript-eslint/utils" "8.47.0" + "@typescript-eslint/visitor-keys" "8.47.0" graphemer "^1.4.0" ignore "^7.0.0" natural-compare "^1.4.0" @@ -435,23 +435,23 @@ "@typescript-eslint/utils" "5.62.0" "@typescript-eslint/parser@^8.26.0": - version "8.44.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.44.1.tgz#d4c85791389462823596ad46e2b90d34845e05eb" - integrity sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw== - dependencies: - "@typescript-eslint/scope-manager" "8.44.1" - "@typescript-eslint/types" "8.44.1" - "@typescript-eslint/typescript-estree" "8.44.1" - "@typescript-eslint/visitor-keys" "8.44.1" + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.47.0.tgz#51b14ab2be2057ec0f57073b9ff3a9c078b0a964" + integrity sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ== + dependencies: + "@typescript-eslint/scope-manager" "8.47.0" + "@typescript-eslint/types" "8.47.0" + "@typescript-eslint/typescript-estree" "8.47.0" + "@typescript-eslint/visitor-keys" "8.47.0" debug "^4.3.4" -"@typescript-eslint/project-service@8.44.1": - version "8.44.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.44.1.tgz#1bccd9796d25032b190f355f55c5fde061158abb" - integrity sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA== +"@typescript-eslint/project-service@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.47.0.tgz#b8afc65e0527568018af911b702dcfbfdca16471" + integrity sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.44.1" - "@typescript-eslint/types" "^8.44.1" + "@typescript-eslint/tsconfig-utils" "^8.47.0" + "@typescript-eslint/types" "^8.47.0" debug "^4.3.4" "@typescript-eslint/scope-manager@5.62.0": @@ -462,27 +462,27 @@ "@typescript-eslint/types" "5.62.0" "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/scope-manager@8.44.1": - version "8.44.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.44.1.tgz#31c27f92e4aed8d0f4d6fe2b9e5187d1d8797bd7" - integrity sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg== +"@typescript-eslint/scope-manager@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz#d1c36a973a5499fed3a99e2e6a66aec5c9b1e542" + integrity sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg== dependencies: - "@typescript-eslint/types" "8.44.1" - "@typescript-eslint/visitor-keys" "8.44.1" + "@typescript-eslint/types" "8.47.0" + "@typescript-eslint/visitor-keys" "8.47.0" -"@typescript-eslint/tsconfig-utils@8.44.1", "@typescript-eslint/tsconfig-utils@^8.44.1": - version "8.44.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.1.tgz#e1d9d047078fac37d3e638484ab3b56215963342" - integrity sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ== +"@typescript-eslint/tsconfig-utils@8.47.0", "@typescript-eslint/tsconfig-utils@^8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz#4f178b62813538759e0989dd081c5474fad39b84" + integrity sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g== -"@typescript-eslint/type-utils@8.44.1": - version "8.44.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.44.1.tgz#be9d31e0f911d17ee8ac99921bb74cf1f9df3906" - integrity sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g== +"@typescript-eslint/type-utils@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz#b9b0141d99bd5bece3811d7eee68a002597ffa55" + integrity sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A== dependencies: - "@typescript-eslint/types" "8.44.1" - "@typescript-eslint/typescript-estree" "8.44.1" - "@typescript-eslint/utils" "8.44.1" + "@typescript-eslint/types" "8.47.0" + "@typescript-eslint/typescript-estree" "8.47.0" + "@typescript-eslint/utils" "8.47.0" debug "^4.3.4" ts-api-utils "^2.1.0" @@ -491,10 +491,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/types@8.44.1", "@typescript-eslint/types@^8.44.1": - version "8.44.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.44.1.tgz#85d1cad1290a003ff60420388797e85d1c3f76ff" - integrity sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ== +"@typescript-eslint/types@8.47.0", "@typescript-eslint/types@^8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.47.0.tgz#c7fc9b6642d03505f447a8392934b9d1850de5af" + integrity sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A== "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" @@ -509,15 +509,15 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@8.44.1": - version "8.44.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.1.tgz#4f17650e5adabecfcc13cd8c517937a4ef5cd424" - integrity sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A== +"@typescript-eslint/typescript-estree@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz#86416dad58db76c4b3bd6a899b1381f9c388489a" + integrity sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg== dependencies: - "@typescript-eslint/project-service" "8.44.1" - "@typescript-eslint/tsconfig-utils" "8.44.1" - "@typescript-eslint/types" "8.44.1" - "@typescript-eslint/visitor-keys" "8.44.1" + "@typescript-eslint/project-service" "8.47.0" + "@typescript-eslint/tsconfig-utils" "8.47.0" + "@typescript-eslint/types" "8.47.0" + "@typescript-eslint/visitor-keys" "8.47.0" debug "^4.3.4" fast-glob "^3.3.2" is-glob "^4.0.3" @@ -539,15 +539,15 @@ eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/utils@8.44.1", "@typescript-eslint/utils@^8.13.0": - version "8.44.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.44.1.tgz#f23d48eb90791a821dc17d4f67bb96faeb75d63d" - integrity sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg== +"@typescript-eslint/utils@8.47.0", "@typescript-eslint/utils@^8.13.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.47.0.tgz#d6c30690431dbfdab98fc027202af12e77c91419" + integrity sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ== dependencies: "@eslint-community/eslint-utils" "^4.7.0" - "@typescript-eslint/scope-manager" "8.44.1" - "@typescript-eslint/types" "8.44.1" - "@typescript-eslint/typescript-estree" "8.44.1" + "@typescript-eslint/scope-manager" "8.47.0" + "@typescript-eslint/types" "8.47.0" + "@typescript-eslint/typescript-estree" "8.47.0" "@typescript-eslint/visitor-keys@5.62.0": version "5.62.0" @@ -557,12 +557,12 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@8.44.1": - version "8.44.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.1.tgz#1d96197a7fcceaba647b3bd6a8594df8dc4deb5a" - integrity sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw== +"@typescript-eslint/visitor-keys@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz#35f36ed60a170dfc9d4d738e78387e217f24c29f" + integrity sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ== dependencies: - "@typescript-eslint/types" "8.44.1" + "@typescript-eslint/types" "8.47.0" eslint-visitor-keys "^4.2.1" "@ungap/structured-clone@^1.2.0": @@ -1314,37 +1314,37 @@ es-to-primitive@^1.3.0: is-date-object "^1.0.5" is-symbol "^1.0.4" -esbuild@^0.25.0: - version "0.25.10" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.10.tgz#37f5aa5cd14500f141be121c01b096ca83ac34a9" - integrity sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ== +esbuild@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.0.tgz#db983bed6f76981361c92f50cf6a04c66f7b3e1d" + integrity sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA== optionalDependencies: - "@esbuild/aix-ppc64" "0.25.10" - "@esbuild/android-arm" "0.25.10" - "@esbuild/android-arm64" "0.25.10" - "@esbuild/android-x64" "0.25.10" - "@esbuild/darwin-arm64" "0.25.10" - "@esbuild/darwin-x64" "0.25.10" - "@esbuild/freebsd-arm64" "0.25.10" - "@esbuild/freebsd-x64" "0.25.10" - "@esbuild/linux-arm" "0.25.10" - "@esbuild/linux-arm64" "0.25.10" - "@esbuild/linux-ia32" "0.25.10" - "@esbuild/linux-loong64" "0.25.10" - "@esbuild/linux-mips64el" "0.25.10" - "@esbuild/linux-ppc64" "0.25.10" - "@esbuild/linux-riscv64" "0.25.10" - "@esbuild/linux-s390x" "0.25.10" - "@esbuild/linux-x64" "0.25.10" - "@esbuild/netbsd-arm64" "0.25.10" - "@esbuild/netbsd-x64" "0.25.10" - "@esbuild/openbsd-arm64" "0.25.10" - "@esbuild/openbsd-x64" "0.25.10" - "@esbuild/openharmony-arm64" "0.25.10" - "@esbuild/sunos-x64" "0.25.10" - "@esbuild/win32-arm64" "0.25.10" - "@esbuild/win32-ia32" "0.25.10" - "@esbuild/win32-x64" "0.25.10" + "@esbuild/aix-ppc64" "0.27.0" + "@esbuild/android-arm" "0.27.0" + "@esbuild/android-arm64" "0.27.0" + "@esbuild/android-x64" "0.27.0" + "@esbuild/darwin-arm64" "0.27.0" + "@esbuild/darwin-x64" "0.27.0" + "@esbuild/freebsd-arm64" "0.27.0" + "@esbuild/freebsd-x64" "0.27.0" + "@esbuild/linux-arm" "0.27.0" + "@esbuild/linux-arm64" "0.27.0" + "@esbuild/linux-ia32" "0.27.0" + "@esbuild/linux-loong64" "0.27.0" + "@esbuild/linux-mips64el" "0.27.0" + "@esbuild/linux-ppc64" "0.27.0" + "@esbuild/linux-riscv64" "0.27.0" + "@esbuild/linux-s390x" "0.27.0" + "@esbuild/linux-x64" "0.27.0" + "@esbuild/netbsd-arm64" "0.27.0" + "@esbuild/netbsd-x64" "0.27.0" + "@esbuild/openbsd-arm64" "0.27.0" + "@esbuild/openbsd-x64" "0.27.0" + "@esbuild/openharmony-arm64" "0.27.0" + "@esbuild/sunos-x64" "0.27.0" + "@esbuild/win32-arm64" "0.27.0" + "@esbuild/win32-ia32" "0.27.0" + "@esbuild/win32-x64" "0.27.0" escalade@^3.1.1: version "3.2.0" @@ -2431,9 +2431,9 @@ js-yaml@^3.13.1: esprima "^4.0.0" js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + version "4.1.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== dependencies: argparse "^2.0.1" @@ -2660,9 +2660,9 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== mocha@^11.1.0: - version "11.7.2" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.2.tgz#3c0079fe5cc2f8ea86d99124debcc42bb1ab22b5" - integrity sha512-lkqVJPmqqG/w5jmmFtiRvtA2jkDyNVUcefFJKb2uyX4dekk8Okgqop3cgbFiaIvj8uCRJVTP5x9dfxGyXm2jvQ== + version "11.7.5" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.5.tgz#58f5bbfa5e0211ce7e5ee6128107cefc2515a627" + integrity sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig== dependencies: browser-stdout "^1.3.1" chokidar "^4.0.1" @@ -2672,6 +2672,7 @@ mocha@^11.1.0: find-up "^5.0.0" glob "^10.4.5" he "^1.2.0" + is-path-inside "^3.0.3" js-yaml "^4.1.0" log-symbols "^4.1.0" minimatch "^9.0.5" @@ -3325,9 +3326,9 @@ semver@^6.1.2: integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.3.7, semver@^7.6.0, semver@^7.7.1: - version "7.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" - integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + version "7.7.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" + integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== serialize-javascript@^6.0.2: version "6.0.2" @@ -3958,9 +3959,9 @@ typescript-formatter@^7.2.2: editorconfig "^0.15.0" typescript@^5.8.2: - version "5.9.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6" - integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A== + version "5.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== unbox-primitive@^1.1.0: version "1.1.0" From f61c25fa305e6535bf802c36ff564253a937e914 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 07:23:23 +0000 Subject: [PATCH 20/88] Bump actions/checkout from 4 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/dev-containers.yml | 6 +++--- .github/workflows/publish-dev-containers.yml | 2 +- .github/workflows/test-docker-v29.yml | 2 +- .github/workflows/test-plan-item-validator.yml | 2 +- .github/workflows/test-windows.yml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dev-containers.yml b/.github/workflows/dev-containers.yml index 2fa3bae1f..b635d259c 100644 --- a/.github/workflows/dev-containers.yml +++ b/.github/workflows/dev-containers.yml @@ -13,7 +13,7 @@ jobs: name: CLI runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: actions/setup-node@v5 with: node-version: '18.x' @@ -62,7 +62,7 @@ jobs: ] steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v5 with: @@ -95,7 +95,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v5 with: diff --git a/.github/workflows/publish-dev-containers.yml b/.github/workflows/publish-dev-containers.yml index 931aa725e..3dcea343b 100644 --- a/.github/workflows/publish-dev-containers.yml +++ b/.github/workflows/publish-dev-containers.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v5 with: diff --git a/.github/workflows/test-docker-v29.yml b/.github/workflows/test-docker-v29.yml index 4560e0694..1c5ebad17 100644 --- a/.github/workflows/test-docker-v29.yml +++ b/.github/workflows/test-docker-v29.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: actions/setup-node@v5 with: diff --git a/.github/workflows/test-plan-item-validator.yml b/.github/workflows/test-plan-item-validator.yml index 22f75ba44..c1d218aba 100644 --- a/.github/workflows/test-plan-item-validator.yml +++ b/.github/workflows/test-plan-item-validator.yml @@ -9,7 +9,7 @@ jobs: steps: - name: Checkout Actions if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: 'microsoft/vscode-github-triage-actions' ref: stable diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 0fe0fcda8..111110448 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -46,7 +46,7 @@ jobs: ] steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v5 with: From a348b3ffb5b1a0954be41ccd4e43e2245e7d025e Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Mon, 24 Nov 2025 08:22:27 +0000 Subject: [PATCH 21/88] features resolve-dependencies and template apply test --- .../featuresCLICommands.test.ts | 113 ++++++++++++++++++ .../templatesCLICommands.test.ts | 54 +++++++++ 2 files changed, 167 insertions(+) diff --git a/src/test/container-features/featuresCLICommands.test.ts b/src/test/container-features/featuresCLICommands.test.ts index 1d20d5b1a..340abdb21 100644 --- a/src/test/container-features/featuresCLICommands.test.ts +++ b/src/test/container-features/featuresCLICommands.test.ts @@ -425,6 +425,119 @@ describe('CLI features subcommands', async function () { }); }); + describe('features resolve-dependencies', function () { + + it('should resolve dependencies when workspace-folder defaults to current directory', async function () { + // Create a test config with features that have dependencies + const testConfigPath = path.resolve(__dirname, 'configs/feature-dependencies/dependsOn/oci-ab'); + const originalCwd = process.cwd(); + + try { + // Change to test config directory to test default workspace folder behavior + process.chdir(testConfigPath); + + // Use absolute path to CLI to prevent npm ENOENT errors + const absoluteTmpPath = path.resolve(originalCwd, tmp); + const absoluteCliPath = `npx --prefix ${absoluteTmpPath} devcontainer`; + + // First check if the config file exists + const configExists = require('fs').existsSync('.devcontainer/devcontainer.json') || + require('fs').existsSync('.devcontainer.json'); + assert.isTrue(configExists, 'Test config file should exist'); + + let result; + try { + result = await shellExec(`${absoluteCliPath} features resolve-dependencies --log-level trace`); + } catch (error: any) { + // If command fails, log details for debugging + console.error('Command failed:', error); + if (error.stderr) { + console.error('STDERR:', error.stderr); + } + if (error.stdout) { + console.error('STDOUT:', error.stdout); + } + throw error; + } + + // Verify the command succeeded + assert.isDefined(result); + assert.isString(result.stdout); + assert.isNotEmpty(result.stdout.trim(), 'Command should produce output'); + + // Parse the JSON output to verify it contains expected structure + let jsonOutput; + try { + // Try parsing stdout directly first + jsonOutput = JSON.parse(result.stdout.trim()); + } catch (parseError) { + // If direct parsing fails, try extracting JSON from mixed output + const lines = result.stdout.split('\n'); + + // Find the last occurrence of '{' that starts a complete JSON object + let jsonStartIndex = -1; + let jsonEndIndex = -1; + let braceCount = 0; + + // Work backwards from the end to find the complete JSON + for (let i = lines.length - 1; i >= 0; i--) { + const line = lines[i].trim(); + if (line === '}' && jsonEndIndex === -1) { + jsonEndIndex = i; + braceCount = 1; + } else if (jsonEndIndex !== -1) { + // Count braces to find matching opening + for (const char of line) { + if (char === '}') { + braceCount++; + } else if (char === '{') { + braceCount--; + } + } + if (braceCount === 0 && line === '{') { + jsonStartIndex = i; + break; + } + } + } + + if (jsonStartIndex >= 0 && jsonEndIndex >= 0) { + // Extract just the JSON lines + const jsonLines = lines.slice(jsonStartIndex, jsonEndIndex + 1); + const jsonString = jsonLines.join('\n'); + try { + jsonOutput = JSON.parse(jsonString); + } catch (innerError) { + console.error('Failed to parse extracted JSON:', jsonString.substring(0, 500) + '...'); + throw new Error(`Failed to parse extracted JSON: ${innerError}`); + } + } else { + console.error('Could not find complete JSON in output'); + console.error('Last 10 lines:', lines.slice(-10)); + throw new Error(`Failed to find complete JSON in output: ${parseError}`); + } + } + + assert.isDefined(jsonOutput, 'Should have valid JSON output'); + assert.property(jsonOutput, 'installOrder'); + assert.isArray(jsonOutput.installOrder); + + // Verify the install order contains the expected features + const installOrder = jsonOutput.installOrder; + assert.isAbove(installOrder.length, 0, 'Install order should contain at least one feature'); + + // Each item should have id and options + installOrder.forEach((item: any) => { + assert.property(item, 'id'); + assert.property(item, 'options'); + }); + + } finally { + process.chdir(originalCwd); + } + }); + }); + describe('features package', function () { it('features package subcommand by collection', async function () { diff --git a/src/test/container-templates/templatesCLICommands.test.ts b/src/test/container-templates/templatesCLICommands.test.ts index babb4f8f2..1a91e2a2d 100644 --- a/src/test/container-templates/templatesCLICommands.test.ts +++ b/src/test/container-templates/templatesCLICommands.test.ts @@ -62,6 +62,60 @@ describe('tests apply command', async function () { // Assert that the Feature included in the command was added. assert.match(file, /"ghcr.io\/devcontainers\/features\/azure-cli:1": {\n/); }); + + it('templates apply subcommand with default workspace folder', async function () { + const testOutputPath = path.resolve(__dirname, 'tmp-default-workspace'); + const originalCwd = process.cwd(); + + try { + // Create and change to test output directory to test default workspace folder behavior + await shellExec(`rm -rf ${testOutputPath}`); + await shellExec(`mkdir -p ${testOutputPath}`); + process.chdir(testOutputPath); + + // Use absolute path to CLI to prevent npm ENOENT errors + const absoluteTmpPath = path.resolve(originalCwd, tmp); + const absoluteCliPath = `npx --prefix ${absoluteTmpPath} devcontainer`; + + let success = false; + let result: ExecResult | undefined = undefined; + + try { + // Run without --workspace-folder to test default behavior + result = await shellExec(`${absoluteCliPath} templates apply \ + --template-id ghcr.io/devcontainers/templates/docker-from-docker:latest \ + --template-args '{ "installZsh": "false", "upgradePackages": "true", "dockerVersion": "20.10", "moby": "true", "enableNonRootDocker": "true" }' \ + --log-level trace`); + success = true; + + } catch (error) { + assert.fail('templates apply sub-command should not throw when using default workspace folder'); + } + + assert.isTrue(success); + assert.isDefined(result); + assert.strictEqual(result.stdout.trim(), '{"files":["./.devcontainer/devcontainer.json"]}'); + + // Verify the file was created in the current working directory (default workspace folder) + const file = (await readLocalFile(path.join(testOutputPath, '.devcontainer', 'devcontainer.json'))).toString(); + + assert.match(file, /"name": "Docker from Docker"/); + assert.match(file, /"installZsh": "false"/); + assert.match(file, /"upgradePackages": "true"/); + assert.match(file, /"version": "20.10"/); + assert.match(file, /"moby": "true"/); + assert.match(file, /"enableNonRootDocker": "true"/); + + // Assert that the Features included in the template were not removed. + assert.match(file, /"ghcr.io\/devcontainers\/features\/common-utils:1": {\n/); + assert.match(file, /"ghcr.io\/devcontainers\/features\/docker-from-docker:1": {\n/); + + } finally { + process.chdir(originalCwd); + // Clean up test directory + await shellExec(`rm -rf ${testOutputPath}`); + } + }); }); describe('tests packageTemplates()', async function () { From 7716a885bb2e850cc222d7499c4bbdd662adb84e Mon Sep 17 00:00:00 2001 From: Kaniska Date: Fri, 28 Nov 2025 16:53:14 +0000 Subject: [PATCH 22/88] Adding precautionary check for dockerhub registry availability in devcontainer cli --- src/spec-node/containerFeatures.ts | 101 +++++++++++++++++++++++------ src/spec-node/utils.ts | 6 +- 2 files changed, 85 insertions(+), 22 deletions(-) diff --git a/src/spec-node/containerFeatures.ts b/src/spec-node/containerFeatures.ts index c7e42a56a..d7774d762 100644 --- a/src/spec-node/containerFeatures.ts +++ b/src/spec-node/containerFeatures.ts @@ -11,11 +11,12 @@ import { LogLevel, makeLog } from '../spec-utils/log'; import { FeaturesConfig, getContainerFeaturesBaseDockerFile, getFeatureInstallWrapperScript, getFeatureLayers, getFeatureMainValue, getFeatureValueObject, generateFeaturesConfig, Feature, generateContainerEnvs } from '../spec-configuration/containerFeaturesConfiguration'; import { readLocalFile } from '../spec-utils/pfs'; import { includeAllConfiguredFeatures } from '../spec-utils/product'; -import { createFeaturesTempFolder, DockerResolverParameters, getCacheFolder, getFolderImageName, getEmptyContextFolder, SubstitutedConfig } from './utils'; +import { createFeaturesTempFolder, DockerResolverParameters, getCacheFolder, getFolderImageName, getEmptyContextFolder, SubstitutedConfig, retry } from './utils'; import { isEarlierVersion, parseVersion, runCommandNoPty } from '../spec-common/commonUtils'; import { getDevcontainerMetadata, getDevcontainerMetadataLabel, getImageBuildInfoFromImage, ImageBuildInfo, ImageMetadataEntry, imageMetadataLabel, MergedDevContainerConfig } from './imageMetadata'; import { supportsBuildContexts } from './dockerfileUtils'; import { ContainerError } from '../spec-common/errors'; +import { requestResolveHeaders } from '../spec-utils/httpRequest'; // Escapes environment variable keys. // @@ -154,7 +155,7 @@ export async function getExtendImageBuildInfo(params: DockerResolverParameters, } }; } - return { featureBuildInfo: getImageBuildOptions(params, config, dstFolder, baseName, imageBuildInfo) }; + return { featureBuildInfo: await getImageBuildOptions(params, config, dstFolder, baseName, imageBuildInfo) }; } // Generates the end configuration. @@ -193,24 +194,25 @@ export interface ImageBuildOptions { securityOpts: string[]; } -function getImageBuildOptions(params: DockerResolverParameters, config: SubstitutedConfig, dstFolder: string, baseName: string, imageBuildInfo: ImageBuildInfo): ImageBuildOptions { - const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; - return { - dstFolder, - dockerfileContent: ` +async function getImageBuildOptions(params: DockerResolverParameters, config: SubstitutedConfig, dstFolder: string, baseName: string, imageBuildInfo: ImageBuildInfo): Promise { + const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; + const dockerHubAccessible = syntax ? await ensureDockerfileFrontendAccessible(params) : false; + return { + dstFolder, + dockerfileContent: ` FROM $_DEV_CONTAINERS_BASE_IMAGE AS dev_containers_target_stage ${getDevcontainerMetadataLabel(getDevcontainerMetadata(imageBuildInfo.metadata, config, { featureSets: [] }, [], getOmitDevcontainerPropertyOverride(params.common)))} `, - overrideTarget: 'dev_containers_target_stage', - dockerfilePrefixContent: `${syntax ? `# syntax=${syntax}` : ''} - ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder + overrideTarget: 'dev_containers_target_stage', + dockerfilePrefixContent: `${dockerHubAccessible && syntax ? `# syntax=${syntax}` : ''} + ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder `, - buildArgs: { - _DEV_CONTAINERS_BASE_IMAGE: baseName, - } as Record, - buildKitContexts: {} as Record, - securityOpts: [], - }; + buildArgs: { + _DEV_CONTAINERS_BASE_IMAGE: baseName, + } as Record, + buildKitContexts: {} as Record, + securityOpts: [], + }; } function getOmitDevcontainerPropertyOverride(resolverParams: { omitConfigRemotEnvFromMetadata?: boolean }): (keyof DevContainerConfig & keyof ImageMetadataEntry)[] { @@ -221,6 +223,62 @@ function getOmitDevcontainerPropertyOverride(resolverParams: { omitConfigRemotEn return []; } +async function checkDockerfileFrontendAccessibleOrThrow(params: DockerResolverParameters): Promise { + const { output } = params.common; + + const tokenRes = await requestResolveHeaders({ + type: 'GET', + url: 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:docker/dockerfile:pull&tag=1.4', + headers: { 'user-agent': 'devcontainer' } + }, output); + if (!tokenRes || tokenRes.statusCode !== 200) { + throw new Error('Token fetch failed: status ' + (tokenRes?.statusCode ?? 'unknown')); + } + + let body: any; + try { + body = JSON.parse(tokenRes.resBody.toString()); + } catch (e) { + throw new Error('Token parse failed: ' + (e instanceof Error ? e.message : String(e))); + } + const token: string | undefined = body?.token || body?.access_token; + if (!token) { + throw new Error('Token missing in auth response'); + } + + const manifestRes = await requestResolveHeaders({ + type: 'GET', + url: 'https://registry-1.docker.io/v2/docker/dockerfile/manifests/1.4', + headers: { + 'user-agent': 'devcontainer', + 'authorization': `Bearer ${token}`, + 'accept': 'application/vnd.docker.distribution.manifest.v2+json' + } + }, output); + if (!manifestRes || manifestRes.statusCode !== 200) { + throw new Error('Manifest fetch failed: status ' + (manifestRes?.statusCode ?? 'unknown')); + } +} + +async function ensureDockerfileFrontendAccessible(params: DockerResolverParameters): Promise { + const { output } = params.common; + try { + await retry( + async () => { await checkDockerfileFrontendAccessibleOrThrow(params); }, + { maxRetries: 5, retryIntervalMilliseconds: 2000, output } + ); + output.write('Dockerfile frontend is accessible in DockerHub registry.', LogLevel.Info); + return true; + } catch (err) { + output.write( + 'Dockerfile frontend check failed after retries: ' + + (err instanceof Error ? err.message : String(err)), + LogLevel.Warning + ); + return false; + } +} + async function getFeaturesBuildOptions(params: DockerResolverParameters, devContainerConfig: SubstitutedConfig, featuresConfig: FeaturesConfig, baseName: string, imageBuildInfo: ImageBuildInfo, composeServiceUser: string | undefined): Promise { const { common } = params; const { cliHost, output } = common; @@ -262,11 +320,12 @@ async function getFeaturesBuildOptions(params: DockerResolverParameters, devCont .replace('#{devcontainerMetadata}', getDevcontainerMetadataLabel(imageMetadata)) .replace('#{containerEnvMetadata}', generateContainerEnvs(devContainerConfig.config.containerEnv, true)) ; - const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; - const omitSyntaxDirective = common.omitSyntaxDirective; // Can be removed when https://github.com/moby/buildkit/issues/4556 is fixed - const dockerfilePrefixContent = `${omitSyntaxDirective ? '' : - useBuildKitBuildContexts && !(imageBuildInfo.dockerfile && supportsBuildContexts(imageBuildInfo.dockerfile)) ? '# syntax=docker/dockerfile:1.4' : - syntax ? `# syntax=${syntax}` : ''} + const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; + const omitSyntaxDirective = common.omitSyntaxDirective; // Can be removed when https://github.com/moby/buildkit/issues/4556 is fixed + const dockerHubAccessible = !omitSyntaxDirective ? await ensureDockerfileFrontendAccessible(params) : false; + const dockerfilePrefixContent = `${omitSyntaxDirective ? '' : + useBuildKitBuildContexts && dockerHubAccessible && !(imageBuildInfo.dockerfile && supportsBuildContexts(imageBuildInfo.dockerfile)) ? '# syntax=docker/dockerfile:1.4' : + syntax ? `# syntax=${syntax}` : ''} ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder `; diff --git a/src/spec-node/utils.ts b/src/spec-node/utils.ts index 81f6b6b17..49e17e96e 100644 --- a/src/spec-node/utils.ts +++ b/src/spec-node/utils.ts @@ -46,7 +46,11 @@ export async function retry(fn: () => Promise, options: { retryIntervalMil return await fn(); } catch (err) { lastError = err; - output.write(`Retrying (Attempt ${i}) with error '${toErrorText(err)}'`, LogLevel.Warning); + output.write( + `Retrying (Attempt ${i}) with error + '${toErrorText(String(err && (err.stack || err.message) || err))}'`, + LogLevel.Warning + ); await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds)); } } From 8ad5a7591d94b6c6e03aae812b5e51c9e65ff43b Mon Sep 17 00:00:00 2001 From: Kaniska Date: Mon, 1 Dec 2025 12:44:17 +0000 Subject: [PATCH 23/88] Using constants --- src/spec-node/containerFeatures.ts | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/spec-node/containerFeatures.ts b/src/spec-node/containerFeatures.ts index d7774d762..453642f21 100644 --- a/src/spec-node/containerFeatures.ts +++ b/src/spec-node/containerFeatures.ts @@ -18,6 +18,14 @@ import { supportsBuildContexts } from './dockerfileUtils'; import { ContainerError } from '../spec-common/errors'; import { requestResolveHeaders } from '../spec-utils/httpRequest'; +// Constants for DockerHub registry access check +const DOCKERHUB_AUTH_URL = 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:docker/dockerfile:pull&tag=1.4'; +const DOCKERHUB_REGISTRY_URL = 'https://registry-1.docker.io/v2/docker/dockerfile/manifests/1.4'; +const DEVCONTAINER_USER_AGENT = 'devcontainer'; +const DOCKER_MANIFEST_ACCEPT_HEADER = 'application/vnd.docker.distribution.manifest.v2+json'; +const DOCKERFILE_FRONTEND_CHECK_MAX_RETRIES = 5; +const DOCKERFILE_FRONTEND_CHECK_RETRY_INTERVAL_MS = 2000; + // Escapes environment variable keys. // // Environment variables must contain: @@ -223,13 +231,13 @@ function getOmitDevcontainerPropertyOverride(resolverParams: { omitConfigRemotEn return []; } -async function checkDockerfileFrontendAccessibleOrThrow(params: DockerResolverParameters): Promise { +async function checkDockerfileFrontendAccessible(params: DockerResolverParameters): Promise { const { output } = params.common; const tokenRes = await requestResolveHeaders({ type: 'GET', - url: 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:docker/dockerfile:pull&tag=1.4', - headers: { 'user-agent': 'devcontainer' } + url: DOCKERHUB_AUTH_URL, + headers: { 'user-agent': DEVCONTAINER_USER_AGENT } }, output); if (!tokenRes || tokenRes.statusCode !== 200) { throw new Error('Token fetch failed: status ' + (tokenRes?.statusCode ?? 'unknown')); @@ -248,11 +256,11 @@ async function checkDockerfileFrontendAccessibleOrThrow(params: DockerResolverPa const manifestRes = await requestResolveHeaders({ type: 'GET', - url: 'https://registry-1.docker.io/v2/docker/dockerfile/manifests/1.4', + url: DOCKERHUB_REGISTRY_URL, headers: { - 'user-agent': 'devcontainer', + 'user-agent': DEVCONTAINER_USER_AGENT, 'authorization': `Bearer ${token}`, - 'accept': 'application/vnd.docker.distribution.manifest.v2+json' + 'accept': DOCKER_MANIFEST_ACCEPT_HEADER } }, output); if (!manifestRes || manifestRes.statusCode !== 200) { @@ -264,10 +272,10 @@ async function ensureDockerfileFrontendAccessible(params: DockerResolverParamete const { output } = params.common; try { await retry( - async () => { await checkDockerfileFrontendAccessibleOrThrow(params); }, - { maxRetries: 5, retryIntervalMilliseconds: 2000, output } + async () => { await checkDockerfileFrontendAccessible(params); }, + { maxRetries: DOCKERFILE_FRONTEND_CHECK_MAX_RETRIES, retryIntervalMilliseconds: DOCKERFILE_FRONTEND_CHECK_RETRY_INTERVAL_MS, output } ); - output.write('Dockerfile frontend is accessible in DockerHub registry.', LogLevel.Info); + output.write('Dockerfile frontend is accessible in DockerHub registry.', LogLevel.Info); return true; } catch (err) { output.write( From a30599041539ab1704e03a388c02a1b4e2eeb1f0 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Mon, 1 Dec 2025 20:39:10 +0530 Subject: [PATCH 24/88] Update src/spec-node/containerFeatures.ts Co-authored-by: Sam Byng --- src/spec-node/containerFeatures.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spec-node/containerFeatures.ts b/src/spec-node/containerFeatures.ts index 453642f21..c4cc7b36f 100644 --- a/src/spec-node/containerFeatures.ts +++ b/src/spec-node/containerFeatures.ts @@ -18,7 +18,7 @@ import { supportsBuildContexts } from './dockerfileUtils'; import { ContainerError } from '../spec-common/errors'; import { requestResolveHeaders } from '../spec-utils/httpRequest'; -// Constants for DockerHub registry access check +// Constants for DockerHub registry + Dockerfile v1.4 image access check const DOCKERHUB_AUTH_URL = 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:docker/dockerfile:pull&tag=1.4'; const DOCKERHUB_REGISTRY_URL = 'https://registry-1.docker.io/v2/docker/dockerfile/manifests/1.4'; const DEVCONTAINER_USER_AGENT = 'devcontainer'; From 4eb05274d51f218f11e4c32501829bc2b3aa5e88 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Mon, 1 Dec 2025 16:26:15 +0000 Subject: [PATCH 25/88] Making the functions generic --- src/spec-node/containerFeatures.ts | 71 ++-------------------------- src/spec-node/utils.ts | 76 +++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 69 deletions(-) diff --git a/src/spec-node/containerFeatures.ts b/src/spec-node/containerFeatures.ts index c4cc7b36f..124e2415f 100644 --- a/src/spec-node/containerFeatures.ts +++ b/src/spec-node/containerFeatures.ts @@ -11,20 +11,11 @@ import { LogLevel, makeLog } from '../spec-utils/log'; import { FeaturesConfig, getContainerFeaturesBaseDockerFile, getFeatureInstallWrapperScript, getFeatureLayers, getFeatureMainValue, getFeatureValueObject, generateFeaturesConfig, Feature, generateContainerEnvs } from '../spec-configuration/containerFeaturesConfiguration'; import { readLocalFile } from '../spec-utils/pfs'; import { includeAllConfiguredFeatures } from '../spec-utils/product'; -import { createFeaturesTempFolder, DockerResolverParameters, getCacheFolder, getFolderImageName, getEmptyContextFolder, SubstitutedConfig, retry } from './utils'; +import { createFeaturesTempFolder, DockerResolverParameters, getCacheFolder, getFolderImageName, getEmptyContextFolder, SubstitutedConfig, ensureDockerfileFrontendAccessible } from './utils'; import { isEarlierVersion, parseVersion, runCommandNoPty } from '../spec-common/commonUtils'; import { getDevcontainerMetadata, getDevcontainerMetadataLabel, getImageBuildInfoFromImage, ImageBuildInfo, ImageMetadataEntry, imageMetadataLabel, MergedDevContainerConfig } from './imageMetadata'; import { supportsBuildContexts } from './dockerfileUtils'; import { ContainerError } from '../spec-common/errors'; -import { requestResolveHeaders } from '../spec-utils/httpRequest'; - -// Constants for DockerHub registry + Dockerfile v1.4 image access check -const DOCKERHUB_AUTH_URL = 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:docker/dockerfile:pull&tag=1.4'; -const DOCKERHUB_REGISTRY_URL = 'https://registry-1.docker.io/v2/docker/dockerfile/manifests/1.4'; -const DEVCONTAINER_USER_AGENT = 'devcontainer'; -const DOCKER_MANIFEST_ACCEPT_HEADER = 'application/vnd.docker.distribution.manifest.v2+json'; -const DOCKERFILE_FRONTEND_CHECK_MAX_RETRIES = 5; -const DOCKERFILE_FRONTEND_CHECK_RETRY_INTERVAL_MS = 2000; // Escapes environment variable keys. // @@ -204,7 +195,7 @@ export interface ImageBuildOptions { async function getImageBuildOptions(params: DockerResolverParameters, config: SubstitutedConfig, dstFolder: string, baseName: string, imageBuildInfo: ImageBuildInfo): Promise { const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; - const dockerHubAccessible = syntax ? await ensureDockerfileFrontendAccessible(params) : false; + const dockerHubAccessible = syntax ? await ensureDockerfileFrontendAccessible(params, 'docker/dockerfile', '1.4') : false; return { dstFolder, dockerfileContent: ` @@ -231,62 +222,6 @@ function getOmitDevcontainerPropertyOverride(resolverParams: { omitConfigRemotEn return []; } -async function checkDockerfileFrontendAccessible(params: DockerResolverParameters): Promise { - const { output } = params.common; - - const tokenRes = await requestResolveHeaders({ - type: 'GET', - url: DOCKERHUB_AUTH_URL, - headers: { 'user-agent': DEVCONTAINER_USER_AGENT } - }, output); - if (!tokenRes || tokenRes.statusCode !== 200) { - throw new Error('Token fetch failed: status ' + (tokenRes?.statusCode ?? 'unknown')); - } - - let body: any; - try { - body = JSON.parse(tokenRes.resBody.toString()); - } catch (e) { - throw new Error('Token parse failed: ' + (e instanceof Error ? e.message : String(e))); - } - const token: string | undefined = body?.token || body?.access_token; - if (!token) { - throw new Error('Token missing in auth response'); - } - - const manifestRes = await requestResolveHeaders({ - type: 'GET', - url: DOCKERHUB_REGISTRY_URL, - headers: { - 'user-agent': DEVCONTAINER_USER_AGENT, - 'authorization': `Bearer ${token}`, - 'accept': DOCKER_MANIFEST_ACCEPT_HEADER - } - }, output); - if (!manifestRes || manifestRes.statusCode !== 200) { - throw new Error('Manifest fetch failed: status ' + (manifestRes?.statusCode ?? 'unknown')); - } -} - -async function ensureDockerfileFrontendAccessible(params: DockerResolverParameters): Promise { - const { output } = params.common; - try { - await retry( - async () => { await checkDockerfileFrontendAccessible(params); }, - { maxRetries: DOCKERFILE_FRONTEND_CHECK_MAX_RETRIES, retryIntervalMilliseconds: DOCKERFILE_FRONTEND_CHECK_RETRY_INTERVAL_MS, output } - ); - output.write('Dockerfile frontend is accessible in DockerHub registry.', LogLevel.Info); - return true; - } catch (err) { - output.write( - 'Dockerfile frontend check failed after retries: ' + - (err instanceof Error ? err.message : String(err)), - LogLevel.Warning - ); - return false; - } -} - async function getFeaturesBuildOptions(params: DockerResolverParameters, devContainerConfig: SubstitutedConfig, featuresConfig: FeaturesConfig, baseName: string, imageBuildInfo: ImageBuildInfo, composeServiceUser: string | undefined): Promise { const { common } = params; const { cliHost, output } = common; @@ -330,7 +265,7 @@ async function getFeaturesBuildOptions(params: DockerResolverParameters, devCont ; const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; const omitSyntaxDirective = common.omitSyntaxDirective; // Can be removed when https://github.com/moby/buildkit/issues/4556 is fixed - const dockerHubAccessible = !omitSyntaxDirective ? await ensureDockerfileFrontendAccessible(params) : false; + const dockerHubAccessible = !omitSyntaxDirective ? await ensureDockerfileFrontendAccessible(params, 'docker/dockerfile', '1.4') : false; const dockerfilePrefixContent = `${omitSyntaxDirective ? '' : useBuildKitBuildContexts && dockerHubAccessible && !(imageBuildInfo.dockerfile && supportsBuildContexts(imageBuildInfo.dockerfile)) ? '# syntax=docker/dockerfile:1.4' : syntax ? `# syntax=${syntax}` : ''} diff --git a/src/spec-node/utils.ts b/src/spec-node/utils.ts index 49e17e96e..018cf63b7 100644 --- a/src/spec-node/utils.ts +++ b/src/spec-node/utils.ts @@ -28,7 +28,7 @@ import { ImageMetadataEntry, MergedDevContainerConfig } from './imageMetadata'; import { getImageIndexEntryForPlatform, getManifest, getRef } from '../spec-configuration/containerCollectionsOCI'; import { requestEnsureAuthenticated } from '../spec-configuration/httpOCIRegistry'; import { configFileLabel, findDevContainer, hostFolderLabel } from './singleContainer'; - +import { requestResolveHeaders } from '../spec-utils/httpRequest'; export { getConfigFilePath, getDockerfilePath, isDockerFileConfig } from '../spec-configuration/configuration'; export { uriToFsPath, parentURI } from '../spec-configuration/configurationCommonUtils'; @@ -37,6 +37,12 @@ export type BindMountConsistency = 'consistent' | 'cached' | 'delegated' | undef export type GPUAvailability = 'all' | 'detect' | 'none'; +// Constants for DockerHub registry + image access check +const DEVCONTAINER_USER_AGENT = 'devcontainer'; +const DOCKER_MANIFEST_ACCEPT_HEADER = 'application/vnd.docker.distribution.manifest.v2+json'; +const DOCKERFILE_FRONTEND_CHECK_MAX_RETRIES = 5; +const DOCKERFILE_FRONTEND_CHECK_RETRY_INTERVAL_MS = 2000; + // Generic retry function export async function retry(fn: () => Promise, options: { retryIntervalMilliseconds: number; maxRetries: number; output: Log }): Promise { const { retryIntervalMilliseconds, maxRetries, output } = options; @@ -603,3 +609,71 @@ export function runAsyncHandler(handler: () => Promise) { } })(); } + +// Helper functions to construct DockerHub URLs +function getDockerHubAuthUrl(imageName: string, version: string): string { + return `https://auth.docker.io/token?service=registry.docker.io&scope=repository:${imageName}:pull&tag=${version}`; +} + +function getDockerHubRegistryUrl(imageName: string, version: string): string { + return `https://registry-1.docker.io/v2/${imageName}/manifests/${version}`; +} + +async function checkDockerfileFrontendAccessible(params: DockerResolverParameters, imageName: string, version: string): Promise { + const { output } = params.common; + + const authUrl = getDockerHubAuthUrl(imageName, version); + const registryUrl = getDockerHubRegistryUrl(imageName, version); + + const tokenRes = await requestResolveHeaders({ + type: 'GET', + url: authUrl, + headers: { 'user-agent': DEVCONTAINER_USER_AGENT } + }, output); + if (!tokenRes || tokenRes.statusCode !== 200) { + throw new Error('Token fetch failed: status ' + (tokenRes?.statusCode ?? 'unknown')); + } + + let body: any; + try { + body = JSON.parse(tokenRes.resBody.toString()); + } catch (e) { + throw new Error('Token parse failed: ' + (e instanceof Error ? e.message : String(e))); + } + const token: string | undefined = body?.token || body?.access_token; + if (!token) { + throw new Error('Token missing in auth response'); + } + + const manifestRes = await requestResolveHeaders({ + type: 'GET', + url: registryUrl, + headers: { + 'user-agent': DEVCONTAINER_USER_AGENT, + 'authorization': `Bearer ${token}`, + 'accept': DOCKER_MANIFEST_ACCEPT_HEADER + } + }, output); + if (!manifestRes || manifestRes.statusCode !== 200) { + throw new Error('Manifest fetch failed: status ' + (manifestRes?.statusCode ?? 'unknown')); + } +} + +export async function ensureDockerfileFrontendAccessible(params: DockerResolverParameters, imageName: string, version: string): Promise { + const { output } = params.common; + try { + await retry( + async () => { await checkDockerfileFrontendAccessible(params, imageName, version); }, + { maxRetries: DOCKERFILE_FRONTEND_CHECK_MAX_RETRIES, retryIntervalMilliseconds: DOCKERFILE_FRONTEND_CHECK_RETRY_INTERVAL_MS, output } + ); + output.write('Dockerfile frontend is accessible in DockerHub registry.', LogLevel.Info); + return true; + } catch (err) { + output.write( + 'Dockerfile frontend check failed after retries: ' + + (err instanceof Error ? err.message : String(err)), + LogLevel.Warning + ); + return false; + } +} From 30f0b341b47eede2e0e58966c404969bfca7f61e Mon Sep 17 00:00:00 2001 From: Kaniska Date: Mon, 1 Dec 2025 16:40:52 +0000 Subject: [PATCH 26/88] Make function names generic --- src/spec-node/containerFeatures.ts | 6 +++--- src/spec-node/utils.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/spec-node/containerFeatures.ts b/src/spec-node/containerFeatures.ts index 124e2415f..f8912254a 100644 --- a/src/spec-node/containerFeatures.ts +++ b/src/spec-node/containerFeatures.ts @@ -11,7 +11,7 @@ import { LogLevel, makeLog } from '../spec-utils/log'; import { FeaturesConfig, getContainerFeaturesBaseDockerFile, getFeatureInstallWrapperScript, getFeatureLayers, getFeatureMainValue, getFeatureValueObject, generateFeaturesConfig, Feature, generateContainerEnvs } from '../spec-configuration/containerFeaturesConfiguration'; import { readLocalFile } from '../spec-utils/pfs'; import { includeAllConfiguredFeatures } from '../spec-utils/product'; -import { createFeaturesTempFolder, DockerResolverParameters, getCacheFolder, getFolderImageName, getEmptyContextFolder, SubstitutedConfig, ensureDockerfileFrontendAccessible } from './utils'; +import { createFeaturesTempFolder, DockerResolverParameters, getCacheFolder, getFolderImageName, getEmptyContextFolder, SubstitutedConfig, ensureDockerHubImageAccessible } from './utils'; import { isEarlierVersion, parseVersion, runCommandNoPty } from '../spec-common/commonUtils'; import { getDevcontainerMetadata, getDevcontainerMetadataLabel, getImageBuildInfoFromImage, ImageBuildInfo, ImageMetadataEntry, imageMetadataLabel, MergedDevContainerConfig } from './imageMetadata'; import { supportsBuildContexts } from './dockerfileUtils'; @@ -195,7 +195,7 @@ export interface ImageBuildOptions { async function getImageBuildOptions(params: DockerResolverParameters, config: SubstitutedConfig, dstFolder: string, baseName: string, imageBuildInfo: ImageBuildInfo): Promise { const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; - const dockerHubAccessible = syntax ? await ensureDockerfileFrontendAccessible(params, 'docker/dockerfile', '1.4') : false; + const dockerHubAccessible = syntax ? await ensureDockerHubImageAccessible(params, 'docker/dockerfile', '1.4') : false; return { dstFolder, dockerfileContent: ` @@ -265,7 +265,7 @@ async function getFeaturesBuildOptions(params: DockerResolverParameters, devCont ; const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; const omitSyntaxDirective = common.omitSyntaxDirective; // Can be removed when https://github.com/moby/buildkit/issues/4556 is fixed - const dockerHubAccessible = !omitSyntaxDirective ? await ensureDockerfileFrontendAccessible(params, 'docker/dockerfile', '1.4') : false; + const dockerHubAccessible = !omitSyntaxDirective ? await ensureDockerHubImageAccessible(params, 'docker/dockerfile', '1.4') : false; const dockerfilePrefixContent = `${omitSyntaxDirective ? '' : useBuildKitBuildContexts && dockerHubAccessible && !(imageBuildInfo.dockerfile && supportsBuildContexts(imageBuildInfo.dockerfile)) ? '# syntax=docker/dockerfile:1.4' : syntax ? `# syntax=${syntax}` : ''} diff --git a/src/spec-node/utils.ts b/src/spec-node/utils.ts index 018cf63b7..0dbed5171 100644 --- a/src/spec-node/utils.ts +++ b/src/spec-node/utils.ts @@ -619,7 +619,7 @@ function getDockerHubRegistryUrl(imageName: string, version: string): string { return `https://registry-1.docker.io/v2/${imageName}/manifests/${version}`; } -async function checkDockerfileFrontendAccessible(params: DockerResolverParameters, imageName: string, version: string): Promise { +async function checkDockerHubImageAccessible(params: DockerResolverParameters, imageName: string, version: string): Promise { const { output } = params.common; const authUrl = getDockerHubAuthUrl(imageName, version); @@ -659,11 +659,11 @@ async function checkDockerfileFrontendAccessible(params: DockerResolverParameter } } -export async function ensureDockerfileFrontendAccessible(params: DockerResolverParameters, imageName: string, version: string): Promise { +export async function ensureDockerHubImageAccessible(params: DockerResolverParameters, imageName: string, version: string): Promise { const { output } = params.common; try { await retry( - async () => { await checkDockerfileFrontendAccessible(params, imageName, version); }, + async () => { await checkDockerHubImageAccessible(params, imageName, version); }, { maxRetries: DOCKERFILE_FRONTEND_CHECK_MAX_RETRIES, retryIntervalMilliseconds: DOCKERFILE_FRONTEND_CHECK_RETRY_INTERVAL_MS, output } ); output.write('Dockerfile frontend is accessible in DockerHub registry.', LogLevel.Info); From 4cd749d36c7844fb2d266361a8a213143bd8b688 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Tue, 2 Dec 2025 09:46:46 +0000 Subject: [PATCH 27/88] update comments --- CHANGELOG.md | 5 +++++ package.json | 2 +- src/spec-node/devContainersSpecCLI.ts | 2 +- src/spec-node/featuresCLI/resolveDependencies.ts | 2 +- src/spec-node/templatesCLI/apply.ts | 2 +- src/test/container-features/featuresCLICommands.test.ts | 8 ++++---- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5751ccb15..0a69586fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ Notable changes. +## December 2025 + +### [0.81.0] +- devcontainer commands now use current directory as default workspace folder when not specified (https://github.com/devcontainers/cli/pull/1104) + ## November 2025 ### [0.80.2] diff --git a/package.json b/package.json index af8e375dd..9678bfb52 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.80.3", + "version": "0.81.0", "bin": { "devcontainer": "devcontainer.js" }, diff --git a/src/spec-node/devContainersSpecCLI.ts b/src/spec-node/devContainersSpecCLI.ts index 2183318e6..58f249759 100644 --- a/src/spec-node/devContainersSpecCLI.ts +++ b/src/spec-node/devContainersSpecCLI.ts @@ -103,7 +103,7 @@ function provisionOptions(y: Argv) { 'docker-compose-path': { type: 'string', description: 'Docker Compose CLI path.' }, 'container-data-folder': { type: 'string', description: 'Container data folder where user data inside the container will be stored.' }, 'container-system-data-folder': { type: 'string', description: 'Container system data folder where system data inside the container will be stored.' }, - 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path. If not provided, defaults to the current directory.' }, + 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path. If --id-label, --override-config, and --workspace-folder are not provided, this defaults to the current directory.' }, 'workspace-mount-consistency': { choices: ['consistent' as 'consistent', 'cached' as 'cached', 'delegated' as 'delegated'], default: 'cached' as 'cached', description: 'Workspace mount consistency.' }, 'gpu-availability': { choices: ['all' as 'all', 'detect' as 'detect', 'none' as 'none'], default: 'detect' as 'detect', description: 'Availability of GPUs in case the dev container requires any. `all` expects a GPU to be available.' }, 'mount-workspace-git-root': { type: 'boolean', default: true, description: 'Mount the workspace using its Git root.' }, diff --git a/src/spec-node/featuresCLI/resolveDependencies.ts b/src/spec-node/featuresCLI/resolveDependencies.ts index 7996a5281..d052ecfaa 100644 --- a/src/spec-node/featuresCLI/resolveDependencies.ts +++ b/src/spec-node/featuresCLI/resolveDependencies.ts @@ -30,7 +30,7 @@ export function featuresResolveDependenciesOptions(y: Argv) { return y .options({ 'log-level': { choices: ['error' as 'error', 'info' as 'info', 'debug' as 'debug', 'trace' as 'trace'], default: 'error' as 'error', description: 'Log level.' }, - 'workspace-folder': { type: 'string', description: 'Workspace folder to use for the configuration.' }, + 'workspace-folder': { type: 'string', description: 'Workspace folder to use for the configuration.If --workspace-folder is not provided, this defaults to the current directory' }, }); } diff --git a/src/spec-node/templatesCLI/apply.ts b/src/spec-node/templatesCLI/apply.ts index 24fc33d88..0fb25c932 100644 --- a/src/spec-node/templatesCLI/apply.ts +++ b/src/spec-node/templatesCLI/apply.ts @@ -11,7 +11,7 @@ import path from 'path'; export function templateApplyOptions(y: Argv) { return y .options({ - 'workspace-folder': { type: 'string', alias: 'w', default: '.', description: 'Target workspace folder to apply Template' }, + 'workspace-folder': { type: 'string', alias: 'w', description: 'Target workspace folder to apply Template. If --workspace-folder is not provided, this defaults to the current directory' }, 'template-id': { type: 'string', alias: 't', demandOption: true, description: 'Reference to a Template in a supported OCI registry' }, 'template-args': { type: 'string', alias: 'a', default: '{}', description: 'Arguments to replace within the provided Template, provided as JSON' }, 'features': { type: 'string', alias: 'f', default: '[]', description: 'Features to add to the provided Template, provided as JSON.' }, diff --git a/src/test/container-features/featuresCLICommands.test.ts b/src/test/container-features/featuresCLICommands.test.ts index 340abdb21..2dd2bd0e8 100644 --- a/src/test/container-features/featuresCLICommands.test.ts +++ b/src/test/container-features/featuresCLICommands.test.ts @@ -1,15 +1,15 @@ import { assert } from 'chai'; import path from 'path'; +import { existsSync } from 'fs'; import { createPlainLog, LogLevel, makeLog } from '../../spec-utils/log'; import { isLocalFile, readLocalFile } from '../../spec-utils/pfs'; import { ExecResult, shellExec } from '../testUtils'; import { getSemanticTags } from '../../spec-node/collectionCommonUtils/publishCommandImpl'; import { getRef, getPublishedTags, getVersionsStrictSorted } from '../../spec-configuration/containerCollectionsOCI'; import { generateFeaturesDocumentation } from '../../spec-node/collectionCommonUtils/generateDocsCommandImpl'; +import pkg from '../../../package.json'; export const output = makeLog(createPlainLog(text => process.stdout.write(text), () => LogLevel.Trace)); -const pkg = require('../../../package.json'); - describe('CLI features subcommands', async function () { this.timeout('240s'); @@ -441,8 +441,8 @@ describe('CLI features subcommands', async function () { const absoluteCliPath = `npx --prefix ${absoluteTmpPath} devcontainer`; // First check if the config file exists - const configExists = require('fs').existsSync('.devcontainer/devcontainer.json') || - require('fs').existsSync('.devcontainer.json'); + const configExists = existsSync('.devcontainer/devcontainer.json') || + existsSync('.devcontainer.json'); assert.isTrue(configExists, 'Test config file should exist'); let result; From f2a9b103a9014dbf2592d8f4fae12122f7c54575 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Mon, 8 Dec 2025 15:05:55 +0530 Subject: [PATCH 28/88] Skip addition of `#syntax` directive when docker engine version >= `23.0.0` (#1118) * Adding check to skip addition of `#syntax` directive when docker engine version < `23.0.0` * Updated workflow runner-image --- .github/workflows/test-docker-v20.yml | 53 +++++++++++++++++++ src/spec-node/containerFeatures.ts | 14 ++--- src/spec-node/devContainers.ts | 13 ++++- src/spec-node/utils.ts | 76 +-------------------------- src/spec-shutdown/dockerUtils.ts | 18 +++++++ 5 files changed, 92 insertions(+), 82 deletions(-) create mode 100644 .github/workflows/test-docker-v20.yml diff --git a/.github/workflows/test-docker-v20.yml b/.github/workflows/test-docker-v20.yml new file mode 100644 index 000000000..e38817420 --- /dev/null +++ b/.github/workflows/test-docker-v20.yml @@ -0,0 +1,53 @@ +name: Docker v20 Tests for dockerfile frontend test + +on: + push: + branches: ['main', 'directive-syntax-further-changes'] + pull_request: + branches: ['main'] + +jobs: + test-docker-v20: + name: Docker v20.10 Compatibility + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-node@v5 + with: + node-version: '18.x' + + - name: Install Docker v20.10 + run: | + sudo apt-get remove -y docker-ce docker-ce-cli containerd.io || true + sudo apt-get update + sudo apt-get install -y \ + ca-certificates \ + curl \ + gnupg \ + lsb-release + sudo mkdir -p /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg + echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + sudo apt-get update + sudo apt-get install -y docker-ce=5:20.10.* docker-ce-cli=5:20.10.* containerd.io + sudo systemctl restart docker + + - name: Verify Docker version, Install and Test + run: | + # Verify + docker version + DOCKER_VERSION=$(docker version --format '{{.Server.Version}}') + if [[ ! "$DOCKER_VERSION" =~ ^20\.10\. ]]; then + echo "ERROR: Expected Docker v20.10.x but got $DOCKER_VERSION" + exit 1 + fi + yarn install --frozen-lockfile + yarn type-check + yarn package + yarn test-matrix --forbid-only src/test/cli.up.test.ts + env: + CI: true \ No newline at end of file diff --git a/src/spec-node/containerFeatures.ts b/src/spec-node/containerFeatures.ts index f8912254a..cc957c592 100644 --- a/src/spec-node/containerFeatures.ts +++ b/src/spec-node/containerFeatures.ts @@ -11,7 +11,7 @@ import { LogLevel, makeLog } from '../spec-utils/log'; import { FeaturesConfig, getContainerFeaturesBaseDockerFile, getFeatureInstallWrapperScript, getFeatureLayers, getFeatureMainValue, getFeatureValueObject, generateFeaturesConfig, Feature, generateContainerEnvs } from '../spec-configuration/containerFeaturesConfiguration'; import { readLocalFile } from '../spec-utils/pfs'; import { includeAllConfiguredFeatures } from '../spec-utils/product'; -import { createFeaturesTempFolder, DockerResolverParameters, getCacheFolder, getFolderImageName, getEmptyContextFolder, SubstitutedConfig, ensureDockerHubImageAccessible } from './utils'; +import { createFeaturesTempFolder, DockerResolverParameters, getCacheFolder, getFolderImageName, getEmptyContextFolder, SubstitutedConfig } from './utils'; import { isEarlierVersion, parseVersion, runCommandNoPty } from '../spec-common/commonUtils'; import { getDevcontainerMetadata, getDevcontainerMetadataLabel, getImageBuildInfoFromImage, ImageBuildInfo, ImageMetadataEntry, imageMetadataLabel, MergedDevContainerConfig } from './imageMetadata'; import { supportsBuildContexts } from './dockerfileUtils'; @@ -195,7 +195,6 @@ export interface ImageBuildOptions { async function getImageBuildOptions(params: DockerResolverParameters, config: SubstitutedConfig, dstFolder: string, baseName: string, imageBuildInfo: ImageBuildInfo): Promise { const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; - const dockerHubAccessible = syntax ? await ensureDockerHubImageAccessible(params, 'docker/dockerfile', '1.4') : false; return { dstFolder, dockerfileContent: ` @@ -203,7 +202,7 @@ FROM $_DEV_CONTAINERS_BASE_IMAGE AS dev_containers_target_stage ${getDevcontainerMetadataLabel(getDevcontainerMetadata(imageBuildInfo.metadata, config, { featureSets: [] }, [], getOmitDevcontainerPropertyOverride(params.common)))} `, overrideTarget: 'dev_containers_target_stage', - dockerfilePrefixContent: `${dockerHubAccessible && syntax ? `# syntax=${syntax}` : ''} + dockerfilePrefixContent: `${syntax ? `# syntax=${syntax}` : ''} ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder `, buildArgs: { @@ -242,7 +241,10 @@ async function getFeaturesBuildOptions(params: DockerResolverParameters, devCont const useBuildKitBuildContexts = buildKitVersionParsed ? !isEarlierVersion(buildKitVersionParsed, minRequiredVersion) : false; const buildContentImageName = 'dev_container_feature_content_temp'; const disableSELinuxLabels = useBuildKitBuildContexts && await isUsingSELinuxLabels(params); - + // Access Docker engine version + const dockerEngineVersionParsed = params.dockerEngineVersion?.versionMatch ? parseVersion(params.dockerEngineVersion.versionMatch) : undefined; + const minDockerEngineVersion = [23, 0, 0]; + const skipDefaultSyntax = dockerEngineVersionParsed ? !isEarlierVersion(dockerEngineVersionParsed, minDockerEngineVersion) : false; const omitPropertyOverride = params.common.skipPersistingCustomizationsFromFeatures ? ['customizations'] : []; const imageMetadata = getDevcontainerMetadata(imageBuildInfo.metadata, devContainerConfig, featuresConfig, omitPropertyOverride, getOmitDevcontainerPropertyOverride(params.common)); const { containerUser, remoteUser } = findContainerUsers(imageMetadata, composeServiceUser, imageBuildInfo.user); @@ -265,9 +267,9 @@ async function getFeaturesBuildOptions(params: DockerResolverParameters, devCont ; const syntax = imageBuildInfo.dockerfile?.preamble.directives.syntax; const omitSyntaxDirective = common.omitSyntaxDirective; // Can be removed when https://github.com/moby/buildkit/issues/4556 is fixed - const dockerHubAccessible = !omitSyntaxDirective ? await ensureDockerHubImageAccessible(params, 'docker/dockerfile', '1.4') : false; const dockerfilePrefixContent = `${omitSyntaxDirective ? '' : - useBuildKitBuildContexts && dockerHubAccessible && !(imageBuildInfo.dockerfile && supportsBuildContexts(imageBuildInfo.dockerfile)) ? '# syntax=docker/dockerfile:1.4' : + skipDefaultSyntax ? (syntax ? `# syntax=${syntax}` : '') : + useBuildKitBuildContexts && !(imageBuildInfo.dockerfile && supportsBuildContexts(imageBuildInfo.dockerfile)) ? '# syntax=docker/dockerfile:1.4' : syntax ? `# syntax=${syntax}` : ''} ARG _DEV_CONTAINERS_BASE_IMAGE=placeholder `; diff --git a/src/spec-node/devContainers.ts b/src/spec-node/devContainers.ts index 493d0dc22..a6d546566 100644 --- a/src/spec-node/devContainers.ts +++ b/src/spec-node/devContainers.ts @@ -17,7 +17,7 @@ import { LogLevel, LogDimensions, toErrorText, createCombinedLog, createTerminal import { dockerComposeCLIConfig } from './dockerCompose'; import { Mount } from '../spec-configuration/containerFeaturesConfiguration'; import { getPackageConfig, PackageConfiguration } from '../spec-utils/product'; -import { dockerBuildKitVersion, isPodman } from '../spec-shutdown/dockerUtils'; +import { dockerBuildKitVersion, dockerEngineVersion, isPodman } from '../spec-shutdown/dockerUtils'; import { Event } from '../spec-utils/event'; @@ -205,6 +205,16 @@ export async function createDockerParams(options: ProvisionOptions, disposables: output, platformInfo })); + + const dockerEngineVer = await dockerEngineVersion({ + cliHost, + dockerCLI: dockerPath, + dockerComposeCLI, + env: cliHost.env, + output, + platformInfo + }); + return { common, parsedAuthority, @@ -225,6 +235,7 @@ export async function createDockerParams(options: ProvisionOptions, disposables: updateRemoteUserUIDDefault, additionalCacheFroms: options.additionalCacheFroms, buildKitVersion, + dockerEngineVersion: dockerEngineVer, isTTY: process.stdout.isTTY || options.logFormat === 'json', experimentalLockfile, experimentalFrozenLockfile, diff --git a/src/spec-node/utils.ts b/src/spec-node/utils.ts index 0dbed5171..902888866 100644 --- a/src/spec-node/utils.ts +++ b/src/spec-node/utils.ts @@ -28,7 +28,6 @@ import { ImageMetadataEntry, MergedDevContainerConfig } from './imageMetadata'; import { getImageIndexEntryForPlatform, getManifest, getRef } from '../spec-configuration/containerCollectionsOCI'; import { requestEnsureAuthenticated } from '../spec-configuration/httpOCIRegistry'; import { configFileLabel, findDevContainer, hostFolderLabel } from './singleContainer'; -import { requestResolveHeaders } from '../spec-utils/httpRequest'; export { getConfigFilePath, getDockerfilePath, isDockerFileConfig } from '../spec-configuration/configuration'; export { uriToFsPath, parentURI } from '../spec-configuration/configurationCommonUtils'; @@ -37,12 +36,6 @@ export type BindMountConsistency = 'consistent' | 'cached' | 'delegated' | undef export type GPUAvailability = 'all' | 'detect' | 'none'; -// Constants for DockerHub registry + image access check -const DEVCONTAINER_USER_AGENT = 'devcontainer'; -const DOCKER_MANIFEST_ACCEPT_HEADER = 'application/vnd.docker.distribution.manifest.v2+json'; -const DOCKERFILE_FRONTEND_CHECK_MAX_RETRIES = 5; -const DOCKERFILE_FRONTEND_CHECK_RETRY_INTERVAL_MS = 2000; - // Generic retry function export async function retry(fn: () => Promise, options: { retryIntervalMilliseconds: number; maxRetries: number; output: Log }): Promise { const { retryIntervalMilliseconds, maxRetries, output } = options; @@ -124,6 +117,7 @@ export interface DockerResolverParameters { updateRemoteUserUIDDefault: UpdateRemoteUserUIDDefault; additionalCacheFroms: string[]; buildKitVersion: { versionString: string; versionMatch?: string } | undefined; + dockerEngineVersion: { versionString: string; versionMatch?: string } | undefined; isTTY: boolean; experimentalLockfile?: boolean; experimentalFrozenLockfile?: boolean; @@ -609,71 +603,3 @@ export function runAsyncHandler(handler: () => Promise) { } })(); } - -// Helper functions to construct DockerHub URLs -function getDockerHubAuthUrl(imageName: string, version: string): string { - return `https://auth.docker.io/token?service=registry.docker.io&scope=repository:${imageName}:pull&tag=${version}`; -} - -function getDockerHubRegistryUrl(imageName: string, version: string): string { - return `https://registry-1.docker.io/v2/${imageName}/manifests/${version}`; -} - -async function checkDockerHubImageAccessible(params: DockerResolverParameters, imageName: string, version: string): Promise { - const { output } = params.common; - - const authUrl = getDockerHubAuthUrl(imageName, version); - const registryUrl = getDockerHubRegistryUrl(imageName, version); - - const tokenRes = await requestResolveHeaders({ - type: 'GET', - url: authUrl, - headers: { 'user-agent': DEVCONTAINER_USER_AGENT } - }, output); - if (!tokenRes || tokenRes.statusCode !== 200) { - throw new Error('Token fetch failed: status ' + (tokenRes?.statusCode ?? 'unknown')); - } - - let body: any; - try { - body = JSON.parse(tokenRes.resBody.toString()); - } catch (e) { - throw new Error('Token parse failed: ' + (e instanceof Error ? e.message : String(e))); - } - const token: string | undefined = body?.token || body?.access_token; - if (!token) { - throw new Error('Token missing in auth response'); - } - - const manifestRes = await requestResolveHeaders({ - type: 'GET', - url: registryUrl, - headers: { - 'user-agent': DEVCONTAINER_USER_AGENT, - 'authorization': `Bearer ${token}`, - 'accept': DOCKER_MANIFEST_ACCEPT_HEADER - } - }, output); - if (!manifestRes || manifestRes.statusCode !== 200) { - throw new Error('Manifest fetch failed: status ' + (manifestRes?.statusCode ?? 'unknown')); - } -} - -export async function ensureDockerHubImageAccessible(params: DockerResolverParameters, imageName: string, version: string): Promise { - const { output } = params.common; - try { - await retry( - async () => { await checkDockerHubImageAccessible(params, imageName, version); }, - { maxRetries: DOCKERFILE_FRONTEND_CHECK_MAX_RETRIES, retryIntervalMilliseconds: DOCKERFILE_FRONTEND_CHECK_RETRY_INTERVAL_MS, output } - ); - output.write('Dockerfile frontend is accessible in DockerHub registry.', LogLevel.Info); - return true; - } catch (err) { - output.write( - 'Dockerfile frontend check failed after retries: ' + - (err instanceof Error ? err.message : String(err)), - LogLevel.Warning - ); - return false; - } -} diff --git a/src/spec-shutdown/dockerUtils.ts b/src/spec-shutdown/dockerUtils.ts index 10205c420..3aa4d8166 100644 --- a/src/spec-shutdown/dockerUtils.ts +++ b/src/spec-shutdown/dockerUtils.ts @@ -259,6 +259,24 @@ export async function dockerBuildKitVersion(params: DockerCLIParameters | Partia } } +export async function dockerEngineVersion(params: DockerCLIParameters | PartialExecParameters | DockerResolverParameters): Promise<{ versionString: string; versionMatch?: string } | undefined> { + try { + const execParams = { + ...toExecParameters(params), + print: true, + }; + const result = await dockerCLI(execParams, 'version', '--format', '{{.Server.Version}}'); + const versionString = result.stdout.toString().trim(); + const versionMatch = versionString.match(/(?[0-9]+)\.(?[0-9]+)\.(?[0-9]+)/); + if (!versionMatch) { + return { versionString }; + } + return { versionString, versionMatch: versionMatch[0] }; + } catch { + return undefined; + } +} + export async function dockerCLI(params: DockerCLIParameters | PartialExecParameters | DockerResolverParameters, ...args: string[]) { const partial = toExecParameters(params); return runCommandNoPty({ From dd912d41d4a8fe55005575e0a9e9e3815d456c3a Mon Sep 17 00:00:00 2001 From: Sam Byng Date: Thu, 4 Dec 2025 10:35:10 +0000 Subject: [PATCH 29/88] Bump CLI to 0.80.3 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5751ccb15..d94f28685 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ Notable changes. +## December 2025 + +### [0.80.3] +- Fix: Adding precautionary check for DockerHub registry availability during container startup. In failure scenarios, skip syntax injection of `dockerfile:1.4`. (Note: dockerfile >=1.4 is default for Docker Engine versions [>=23.0.0](https://docs.docker.com/engine/release-notes/23.0/#2300)). (https://github.com/devcontainers/cli/pull/1113) + ## November 2025 ### [0.80.2] diff --git a/package.json b/package.json index 07aeda6bb..af8e375dd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.80.2", + "version": "0.80.3", "bin": { "devcontainer": "devcontainer.js" }, From 5c84cc4a1a4a926439f2a91ce9afa41d184b2d54 Mon Sep 17 00:00:00 2001 From: Sam Byng Date: Mon, 8 Dec 2025 10:28:56 +0000 Subject: [PATCH 30/88] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d94f28685..a3d3b7caf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ Notable changes. ## December 2025 ### [0.80.3] -- Fix: Adding precautionary check for DockerHub registry availability during container startup. In failure scenarios, skip syntax injection of `dockerfile:1.4`. (Note: dockerfile >=1.4 is default for Docker Engine versions [>=23.0.0](https://docs.docker.com/engine/release-notes/23.0/#2300)). (https://github.com/devcontainers/cli/pull/1113) +- Fix: Skip download and injection of `dockerfile:1.4` syntax for Docker Engine versions [>=23.0.0](https://docs.docker.com/engine/release-notes/23.0/#2300)) - `dockerfile:1.4` or a subsequent version is already used by the docker engine package. (https://github.com/devcontainers/cli/pull/1113) ## November 2025 From 33a34adc852a25643e4ce3e31cc2324f9cd3a2e5 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Thu, 8 Jan 2026 10:17:57 +0100 Subject: [PATCH 31/88] Mount a worktree's common folder for Git operations --- src/spec-node/singleContainer.ts | 6 +- src/spec-node/utils.ts | 41 ++- src/test/workspaceConfiguration.test.ts | 433 ++++++++++++++++++++++++ 3 files changed, 475 insertions(+), 5 deletions(-) create mode 100644 src/test/workspaceConfiguration.test.ts diff --git a/src/spec-node/singleContainer.ts b/src/spec-node/singleContainer.ts index 07d111cc2..2f4a8a3fc 100644 --- a/src/spec-node/singleContainer.ts +++ b/src/spec-node/singleContainer.ts @@ -51,7 +51,7 @@ export async function openDockerfileDevContainer(params: DockerResolverParameter // collapsedFeaturesConfig = async () => res.collapsedFeaturesConfig; try { - await spawnDevContainer(params, config, mergedConfig, updatedImageName, idLabels, workspaceConfig.workspaceMount, res.imageDetails, containerUser, res.labels || {}); + await spawnDevContainer(params, config, mergedConfig, updatedImageName, idLabels, workspaceConfig.workspaceMount, workspaceConfig.additionalMountString, res.imageDetails, containerUser, res.labels || {}); } finally { // In 'finally' because 'docker run' can fail after creating the container. // Trying to get it here, so we can offer 'Rebuild Container' as an action later. @@ -348,7 +348,7 @@ export async function extraRunArgs(common: ResolverParameters, params: DockerRes return extraArguments; } -export async function spawnDevContainer(params: DockerResolverParameters, config: DevContainerFromDockerfileConfig | DevContainerFromImageConfig, mergedConfig: MergedDevContainerConfig, imageName: string, labels: string[], workspaceMount: string | undefined, imageDetails: () => Promise, containerUser: string | undefined, extraLabels: Record) { +export async function spawnDevContainer(params: DockerResolverParameters, config: DevContainerFromDockerfileConfig | DevContainerFromImageConfig, mergedConfig: MergedDevContainerConfig, imageName: string, labels: string[], workspaceMount: string | undefined, additionalMountString: string | undefined, imageDetails: () => Promise, containerUser: string | undefined, extraLabels: Record) { const { common } = params; common.progress(ResolverProgress.StartingContainer); @@ -357,6 +357,7 @@ export async function spawnDevContainer(params: DockerResolverParameters, config const exposed = ([]).concat(...exposedPorts.map(port => ['-p', typeof port === 'number' ? `127.0.0.1:${port}:${port}` : port])); const cwdMount = workspaceMount ? ['--mount', workspaceMount] : []; + const additionalMount = additionalMountString ? ['--mount', additionalMountString] : []; const envObj = mergedConfig.containerEnv || {}; const containerEnv = Object.keys(envObj) @@ -409,6 +410,7 @@ while sleep 1 & wait $!; do :; done`, '-']; // `wait $!` allows for the `trap` t '-a', 'STDERR', ...exposed, ...cwdMount, + ...additionalMount, ...featureMounts, ...getLabels(labels), ...containerEnv, diff --git a/src/spec-node/utils.ts b/src/spec-node/utils.ts index 902888866..577b2f970 100644 --- a/src/spec-node/utils.ts +++ b/src/spec-node/utils.ts @@ -347,6 +347,7 @@ export async function getHostMountFolder(cliHost: CLIHost, folderPath: string, m export interface WorkspaceConfiguration { workspaceMount: string | undefined; workspaceFolder: string | undefined; + additionalMountString: string | undefined; } export async function getWorkspaceConfiguration(cliHost: CLIHost, workspace: Workspace | undefined, config: DevContainerConfig, mountWorkspaceGitRoot: boolean, output: Log, consistency?: BindMountConsistency): Promise { @@ -354,17 +355,50 @@ export async function getWorkspaceConfiguration(cliHost: CLIHost, workspace: Wor return { workspaceFolder: getRemoteWorkspaceFolder(config), workspaceMount: undefined, + additionalMountString: undefined, }; } let { workspaceFolder, workspaceMount } = config; + let additionalMountString: string | undefined; if (workspace && (!workspaceFolder || !('workspaceMount' in config))) { const hostMountFolder = await getHostMountFolder(cliHost, workspace.rootFolderPath, mountWorkspaceGitRoot, output); + + // Check if .git is a file (worktree) with a relative gitdir path + let containerMountFolder = path.posix.join('/workspaces', cliHost.path.basename(hostMountFolder)); + if (mountWorkspaceGitRoot) { + const dotGitPath = cliHost.path.join(hostMountFolder, '.git'); + if (await cliHost.isFile(dotGitPath)) { + const dotGitContent = (await cliHost.readFile(dotGitPath)).toString(); + const match = /^gitdir:\s*(.+)$/m.exec(dotGitContent); + if (match) { + const gitdir = match[1]; + // Only handle if gitdir is a relative path + if (!cliHost.path.isAbsolute(gitdir)) { + // gitdir points to .git/worktrees//, common dir is .git/ (two levels up) + const gitCommonDir = cliHost.path.resolve(hostMountFolder, gitdir, '..', '..'); + // Collect path segments from hostMountFolder up to the parent of gitCommonDir + const segments: string[] = []; + for (let current = hostMountFolder; !gitCommonDir.startsWith(current + cliHost.path.sep) && current !== cliHost.path.dirname(current); current = cliHost.path.dirname(current)) { + segments.unshift(cliHost.path.basename(current)); + } + containerMountFolder = path.posix.join('/workspaces', ...segments); + // Calculate where the common dir should be mounted in the container + const containerGitdir = cliHost.platform === 'win32' ? gitdir.replace(/\\/g, '/') : gitdir; + const containerGitCommonDir = path.posix.resolve(containerMountFolder, containerGitdir, '..', '..'); + const cons = cliHost.platform !== 'linux' ? `,consistency=${consistency || 'consistent'}` : ''; + const srcQuote = gitCommonDir.indexOf(',') !== -1 ? '"' : ''; + const tgtQuote = containerGitCommonDir.indexOf(',') !== -1 ? '"' : ''; + additionalMountString = `type=bind,${srcQuote}source=${gitCommonDir}${srcQuote},${tgtQuote}target=${containerGitCommonDir}${tgtQuote}${cons}`; + } + } + } + } + if (!workspaceFolder) { - const rel = cliHost.path.relative(cliHost.path.dirname(hostMountFolder), workspace.rootFolderPath); - workspaceFolder = `/workspaces/${cliHost.platform === 'win32' ? rel.replace(/\\/g, '/') : rel}`; + const rel = cliHost.path.relative(hostMountFolder, workspace.rootFolderPath); + workspaceFolder = path.posix.join(containerMountFolder, cliHost.platform === 'win32' ? rel.replace(/\\/g, '/') : rel); } if (!('workspaceMount' in config)) { - const containerMountFolder = `/workspaces/${cliHost.path.basename(hostMountFolder)}`; const cons = cliHost.platform !== 'linux' ? `,consistency=${consistency || 'consistent'}` : ''; // Podman does not tolerate consistency= const srcQuote = hostMountFolder.indexOf(',') !== -1 ? '"' : ''; const tgtQuote = containerMountFolder.indexOf(',') !== -1 ? '"' : ''; @@ -374,6 +408,7 @@ export async function getWorkspaceConfiguration(cliHost: CLIHost, workspace: Wor return { workspaceFolder, workspaceMount, + additionalMountString, }; } diff --git a/src/test/workspaceConfiguration.test.ts b/src/test/workspaceConfiguration.test.ts new file mode 100644 index 000000000..9aca56b88 --- /dev/null +++ b/src/test/workspaceConfiguration.test.ts @@ -0,0 +1,433 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { assert } from 'chai'; +import * as path from 'path'; +import { getWorkspaceConfiguration } from '../spec-node/utils'; +import { CLIHost } from '../spec-common/cliHost'; +import { Workspace } from '../spec-utils/workspaces'; +import { nullLog } from '../spec-utils/log'; + +function createMockCLIHost(options: { + platform: NodeJS.Platform; + files?: Record; + useFileHost?: boolean; // Use FileHost path in findGitRootFolder (for testing parent folder git root) +}): CLIHost { + const { platform, files = {}, useFileHost = false } = options; + const pathModule = platform === 'win32' ? path.win32 : path.posix; + const baseHost = { + type: 'local' as const, + platform, + arch: 'x64' as const, + path: pathModule, + cwd: platform === 'win32' ? 'C:\\' : '/', + env: {}, + ptyExec: () => { throw new Error('Not implemented'); }, + homedir: async () => platform === 'win32' ? 'C:\\Users\\test' : '/home/test', + tmpdir: async () => platform === 'win32' ? 'C:\\tmp' : '/tmp', + isFile: async (filepath: string) => filepath in files, + isFolder: async () => false, + readFile: async (filepath: string) => { + if (filepath in files) { + return Buffer.from(files[filepath]); + } + throw new Error(`File not found: ${filepath}`); + }, + writeFile: async () => { }, + rename: async () => { }, + mkdirp: async () => { }, + readDir: async () => [], + getUsername: async () => 'test', + toCommonURI: async () => undefined, + connect: () => { throw new Error('Not implemented'); }, + }; + // If useFileHost is true, don't include exec so findGitRootFolder uses the FileHost code path + if (useFileHost) { + return baseHost as unknown as CLIHost; + } + return { + ...baseHost, + exec: () => { throw new Error('Not implemented'); }, + } as CLIHost; +} + +function createWorkspace(rootFolderPath: string, configFolderPath?: string): Workspace { + return { + isWorkspaceFile: false, + workspaceOrFolderPath: rootFolderPath, + rootFolderPath, + configFolderPath: configFolderPath || rootFolderPath, + }; +} + +type TestPlatform = 'linux' | 'darwin' | 'win32'; +const platforms: TestPlatform[] = ['linux', 'darwin', 'win32']; + +describe('getWorkspaceConfiguration', function () { + + for (const platform of platforms) { + describe(`platform: ${platform}`, function () { + + describe('basic workspace mounting', function () { + + it('should mount workspace at /workspaces/', async () => { + const p = { + linux: { projectPath: '/home/user/project', consistency: '' }, + darwin: { projectPath: '/Users/user/project', consistency: ',consistency=consistent' }, + win32: { projectPath: 'C:\\Users\\user\\project', consistency: ',consistency=consistent' }, + }[platform]; + + const cliHost = createMockCLIHost({ platform }); + const workspace = createWorkspace(p.projectPath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + false, + nullLog + ); + + assert.strictEqual(result.workspaceFolder, '/workspaces/project'); + assert.strictEqual(result.workspaceMount, `type=bind,source=${p.projectPath},target=/workspaces/project${p.consistency}`); + assert.isUndefined(result.additionalMountString); + }); + + }); + + describe('git worktree handling', function () { + + it('should not add additional mount when .git is not a file', async () => { + const p = { + linux: { projectPath: '/home/user/project' }, + darwin: { projectPath: '/Users/user/project' }, + win32: { projectPath: 'C:\\Users\\user\\project' }, + }[platform]; + + const cliHost = createMockCLIHost({ + platform, + files: {} + }); + const workspace = createWorkspace(p.projectPath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + true, + nullLog + ); + + assert.isUndefined(result.additionalMountString); + }); + + it('should not add additional mount when gitdir is an absolute path', async () => { + const p = { + linux: { projectPath: '/home/user/project', gitFile: '/home/user/project/.git', absoluteGitdir: 'gitdir: /absolute/path/to/.git/worktrees/project' }, + darwin: { projectPath: '/Users/user/project', gitFile: '/Users/user/project/.git', absoluteGitdir: 'gitdir: /absolute/path/to/.git/worktrees/project' }, + win32: { projectPath: 'C:\\Users\\user\\project', gitFile: 'C:\\Users\\user\\project\\.git', absoluteGitdir: 'gitdir: C:/absolute/path/to/.git/worktrees/project' }, + }[platform]; + + const cliHost = createMockCLIHost({ + platform, + files: { + [p.gitFile]: p.absoluteGitdir + } + }); + const workspace = createWorkspace(p.projectPath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + true, + nullLog + ); + + assert.isUndefined(result.additionalMountString); + }); + + it('should add additional mount when gitdir is a relative path', async () => { + const p = { + linux: { worktreePath: '/home/user/worktrees/feature', gitFile: '/home/user/worktrees/feature/.git', gitdir: 'gitdir: ../../repo/.git/worktrees/feature', repoGitPath: '/home/user/repo/.git', consistency: '' }, + darwin: { worktreePath: '/Users/user/worktrees/feature', gitFile: '/Users/user/worktrees/feature/.git', gitdir: 'gitdir: ../../repo/.git/worktrees/feature', repoGitPath: '/Users/user/repo/.git', consistency: ',consistency=consistent' }, + win32: { worktreePath: 'C:\\Users\\user\\worktrees\\feature', gitFile: 'C:\\Users\\user\\worktrees\\feature\\.git', gitdir: 'gitdir: ../../repo/.git/worktrees/feature', repoGitPath: 'C:\\Users\\user\\repo\\.git', consistency: ',consistency=consistent' }, + }[platform]; + + const cliHost = createMockCLIHost({ + platform, + files: { + [p.gitFile]: p.gitdir + } + }); + const workspace = createWorkspace(p.worktreePath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + true, + nullLog + ); + + assert.strictEqual(result.workspaceFolder, '/workspaces/worktrees/feature'); + assert.strictEqual(result.workspaceMount, `type=bind,source=${p.worktreePath},target=/workspaces/worktrees/feature${p.consistency}`); + assert.strictEqual(result.additionalMountString, `type=bind,source=${p.repoGitPath},target=/workspaces/repo/.git${p.consistency}`); + }); + + it('should handle gitdir with single level up', async () => { + const p = { + linux: { worktreePath: '/home/user/repo-worktree', gitFile: '/home/user/repo-worktree/.git', gitdir: 'gitdir: ../repo/.git/worktrees/worktree', repoGitPath: '/home/user/repo/.git', consistency: '' }, + darwin: { worktreePath: '/Users/user/repo-worktree', gitFile: '/Users/user/repo-worktree/.git', gitdir: 'gitdir: ../repo/.git/worktrees/worktree', repoGitPath: '/Users/user/repo/.git', consistency: ',consistency=consistent' }, + win32: { worktreePath: 'C:\\Users\\user\\repo-worktree', gitFile: 'C:\\Users\\user\\repo-worktree\\.git', gitdir: 'gitdir: ../repo/.git/worktrees/worktree', repoGitPath: 'C:\\Users\\user\\repo\\.git', consistency: ',consistency=consistent' }, + }[platform]; + + const cliHost = createMockCLIHost({ + platform, + files: { + [p.gitFile]: p.gitdir + } + }); + const workspace = createWorkspace(p.worktreePath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + true, + nullLog + ); + + assert.strictEqual(result.workspaceFolder, '/workspaces/repo-worktree'); + assert.strictEqual(result.additionalMountString, `type=bind,source=${p.repoGitPath},target=/workspaces/repo/.git${p.consistency}`); + }); + + it('should handle worktree two levels deep from common parent with main repo', async () => { + const p = { + linux: { worktreePath: '/home/user/projects/worktrees/feature', gitFile: '/home/user/projects/worktrees/feature/.git', gitdir: 'gitdir: ../../repos/main/.git/worktrees/feature', repoGitPath: '/home/user/projects/repos/main/.git', consistency: '' }, + darwin: { worktreePath: '/Users/user/projects/worktrees/feature', gitFile: '/Users/user/projects/worktrees/feature/.git', gitdir: 'gitdir: ../../repos/main/.git/worktrees/feature', repoGitPath: '/Users/user/projects/repos/main/.git', consistency: ',consistency=consistent' }, + win32: { worktreePath: 'C:\\Users\\user\\projects\\worktrees\\feature', gitFile: 'C:\\Users\\user\\projects\\worktrees\\feature\\.git', gitdir: 'gitdir: ../../repos/main/.git/worktrees/feature', repoGitPath: 'C:\\Users\\user\\projects\\repos\\main\\.git', consistency: ',consistency=consistent' }, + }[platform]; + + const cliHost = createMockCLIHost({ + platform, + files: { + [p.gitFile]: p.gitdir + } + }); + const workspace = createWorkspace(p.worktreePath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + true, + nullLog + ); + + assert.strictEqual(result.workspaceFolder, '/workspaces/worktrees/feature'); + assert.strictEqual(result.workspaceMount, `type=bind,source=${p.worktreePath},target=/workspaces/worktrees/feature${p.consistency}`); + assert.strictEqual(result.additionalMountString, `type=bind,source=${p.repoGitPath},target=/workspaces/repos/main/.git${p.consistency}`); + }); + + it('should handle worktree two levels deep with workspace in subfolder', async () => { + const p = { + linux: { worktreePath: '/home/user/projects/worktrees/feature', gitConfigFile: '/home/user/projects/worktrees/feature/.git/config', gitFile: '/home/user/projects/worktrees/feature/.git', gitdir: 'gitdir: ../../repos/main/.git/worktrees/feature', subfolderPath: '/home/user/projects/worktrees/feature/packages/app', repoGitPath: '/home/user/projects/repos/main/.git', consistency: '' }, + darwin: { worktreePath: '/Users/user/projects/worktrees/feature', gitConfigFile: '/Users/user/projects/worktrees/feature/.git/config', gitFile: '/Users/user/projects/worktrees/feature/.git', gitdir: 'gitdir: ../../repos/main/.git/worktrees/feature', subfolderPath: '/Users/user/projects/worktrees/feature/packages/app', repoGitPath: '/Users/user/projects/repos/main/.git', consistency: ',consistency=consistent' }, + win32: { worktreePath: 'C:\\Users\\user\\projects\\worktrees\\feature', gitConfigFile: 'C:\\Users\\user\\projects\\worktrees\\feature\\.git\\config', gitFile: 'C:\\Users\\user\\projects\\worktrees\\feature\\.git', gitdir: 'gitdir: ../../repos/main/.git/worktrees/feature', subfolderPath: 'C:\\Users\\user\\projects\\worktrees\\feature\\packages\\app', repoGitPath: 'C:\\Users\\user\\projects\\repos\\main\\.git', consistency: ',consistency=consistent' }, + }[platform]; + + const cliHost = createMockCLIHost({ + platform, + files: { + [p.gitConfigFile]: '[core]', + [p.gitFile]: p.gitdir + }, + useFileHost: true + }); + const workspace = createWorkspace(p.subfolderPath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + true, + nullLog + ); + + assert.strictEqual(result.workspaceFolder, '/workspaces/worktrees/feature/packages/app'); + assert.strictEqual(result.workspaceMount, `type=bind,source=${p.worktreePath},target=/workspaces/worktrees/feature${p.consistency}`); + assert.strictEqual(result.additionalMountString, `type=bind,source=${p.repoGitPath},target=/workspaces/repos/main/.git${p.consistency}`); + }); + + }); + + describe('git root in parent folder', function () { + + it('should mount from git root when .git/config is in parent folder', async () => { + const p = { + linux: { repoPath: '/home/user/repo', gitConfigFile: '/home/user/repo/.git/config', subfolderPath: '/home/user/repo/packages/frontend', consistency: '' }, + darwin: { repoPath: '/Users/user/repo', gitConfigFile: '/Users/user/repo/.git/config', subfolderPath: '/Users/user/repo/packages/frontend', consistency: ',consistency=consistent' }, + win32: { repoPath: 'C:\\Users\\user\\repo', gitConfigFile: 'C:\\Users\\user\\repo\\.git\\config', subfolderPath: 'C:\\Users\\user\\repo\\packages\\frontend', consistency: ',consistency=consistent' }, + }[platform]; + + const cliHost = createMockCLIHost({ + platform, + files: { + [p.gitConfigFile]: '[core]' + }, + useFileHost: true + }); + const workspace = createWorkspace(p.subfolderPath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + true, + nullLog + ); + + assert.strictEqual(result.workspaceMount, `type=bind,source=${p.repoPath},target=/workspaces/repo${p.consistency}`); + assert.strictEqual(result.workspaceFolder, '/workspaces/repo/packages/frontend'); + assert.isUndefined(result.additionalMountString); + }); + + it('should mount workspace folder when mountWorkspaceGitRoot is false even with .git in parent', async () => { + const p = { + linux: { gitConfigFile: '/home/user/repo/.git/config', subfolderPath: '/home/user/repo/packages/frontend', consistency: '' }, + darwin: { gitConfigFile: '/Users/user/repo/.git/config', subfolderPath: '/Users/user/repo/packages/frontend', consistency: ',consistency=consistent' }, + win32: { gitConfigFile: 'C:\\Users\\user\\repo\\.git\\config', subfolderPath: 'C:\\Users\\user\\repo\\packages\\frontend', consistency: ',consistency=consistent' }, + }[platform]; + + const cliHost = createMockCLIHost({ + platform, + files: { + [p.gitConfigFile]: '[core]' + }, + useFileHost: true + }); + const workspace = createWorkspace(p.subfolderPath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + false, + nullLog + ); + + assert.strictEqual(result.workspaceMount, `type=bind,source=${p.subfolderPath},target=/workspaces/frontend${p.consistency}`); + assert.strictEqual(result.workspaceFolder, '/workspaces/frontend'); + }); + + it('should handle deeply nested workspace in git repo', async () => { + const p = { + linux: { monorepoPath: '/home/user/monorepo', gitConfigFile: '/home/user/monorepo/.git/config', subfolderPath: '/home/user/monorepo/packages/apps/web', consistency: '' }, + darwin: { monorepoPath: '/Users/user/monorepo', gitConfigFile: '/Users/user/monorepo/.git/config', subfolderPath: '/Users/user/monorepo/packages/apps/web', consistency: ',consistency=consistent' }, + win32: { monorepoPath: 'C:\\Users\\user\\monorepo', gitConfigFile: 'C:\\Users\\user\\monorepo\\.git\\config', subfolderPath: 'C:\\Users\\user\\monorepo\\packages\\apps\\web', consistency: ',consistency=consistent' }, + }[platform]; + + const cliHost = createMockCLIHost({ + platform, + files: { + [p.gitConfigFile]: '[core]' + }, + useFileHost: true + }); + const workspace = createWorkspace(p.subfolderPath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + true, + nullLog + ); + + assert.strictEqual(result.workspaceMount, `type=bind,source=${p.monorepoPath},target=/workspaces/monorepo${p.consistency}`); + assert.strictEqual(result.workspaceFolder, '/workspaces/monorepo/packages/apps/web'); + }); + + it('should handle worktree with git root in parent folder', async () => { + const p = { + linux: { repoPath: '/home/user/repo', gitConfigFile: '/home/user/repo/.git/config', gitFile: '/home/user/repo/.git', gitdir: 'gitdir: ../main-repo/.git/worktrees/repo', mainRepoGitPath: '/home/user/main-repo/.git', subfolderPath: '/home/user/repo/packages/lib', consistency: '' }, + darwin: { repoPath: '/Users/user/repo', gitConfigFile: '/Users/user/repo/.git/config', gitFile: '/Users/user/repo/.git', gitdir: 'gitdir: ../main-repo/.git/worktrees/repo', mainRepoGitPath: '/Users/user/main-repo/.git', subfolderPath: '/Users/user/repo/packages/lib', consistency: ',consistency=consistent' }, + win32: { repoPath: 'C:\\Users\\user\\repo', gitConfigFile: 'C:\\Users\\user\\repo\\.git\\config', gitFile: 'C:\\Users\\user\\repo\\.git', gitdir: 'gitdir: ../main-repo/.git/worktrees/repo', mainRepoGitPath: 'C:\\Users\\user\\main-repo\\.git', subfolderPath: 'C:\\Users\\user\\repo\\packages\\lib', consistency: ',consistency=consistent' }, + }[platform]; + + const cliHost = createMockCLIHost({ + platform, + files: { + [p.gitConfigFile]: '[core]', + [p.gitFile]: p.gitdir + }, + useFileHost: true + }); + const workspace = createWorkspace(p.subfolderPath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + true, + nullLog + ); + + assert.strictEqual(result.workspaceFolder, '/workspaces/repo/packages/lib'); + assert.strictEqual(result.additionalMountString, `type=bind,source=${p.mainRepoGitPath},target=/workspaces/main-repo/.git${p.consistency}`); + }); + + }); + + describe('config overrides', function () { + + it('should use workspaceFolder from config when provided', async () => { + const p = { + linux: { projectPath: '/home/user/project' }, + darwin: { projectPath: '/Users/user/project' }, + win32: { projectPath: 'C:\\Users\\user\\project' }, + }[platform]; + + const cliHost = createMockCLIHost({ platform }); + const workspace = createWorkspace(p.projectPath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + { workspaceFolder: '/custom/path' }, + false, + nullLog + ); + + assert.strictEqual(result.workspaceFolder, '/custom/path'); + }); + + it('should use workspaceMount from config when provided', async () => { + const p = { + linux: { projectPath: '/home/user/project' }, + darwin: { projectPath: '/Users/user/project' }, + win32: { projectPath: 'C:\\Users\\user\\project' }, + }[platform]; + + const cliHost = createMockCLIHost({ platform }); + const workspace = createWorkspace(p.projectPath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + { workspaceMount: 'type=bind,source=/custom,target=/workspace' }, + false, + nullLog + ); + + assert.strictEqual(result.workspaceMount, 'type=bind,source=/custom,target=/workspace'); + }); + + }); + + }); + } + +}); From 72610c5caf622fc73f929ac7fb2e7245e750e4cc Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Thu, 8 Jan 2026 14:59:35 +0100 Subject: [PATCH 32/88] Add CLI option to mount a worktree's common folder --- src/spec-node/configContainer.ts | 6 +- src/spec-node/devContainers.ts | 4 +- src/spec-node/devContainersSpecCLI.ts | 25 +++++-- .../featuresCLI/resolveDependencies.ts | 2 +- src/spec-node/featuresCLI/testCommandImpl.ts | 2 + src/spec-node/featuresCLI/utils.ts | 1 + src/spec-node/upgradeCommand.ts | 2 +- src/spec-node/utils.ts | 5 +- src/test/workspaceConfiguration.test.ts | 73 +++++++++++++++++++ 9 files changed, 106 insertions(+), 14 deletions(-) diff --git a/src/spec-node/configContainer.ts b/src/spec-node/configContainer.ts index ee6b93cb3..b4e159657 100644 --- a/src/spec-node/configContainer.ts +++ b/src/spec-node/configContainer.ts @@ -46,7 +46,7 @@ async function resolveWithLocalFolder(params: DockerResolverParameters, parsedAu ? (await getDevContainerConfigPathIn(cliHost, workspace.configFolderPath) || (overrideConfigFile ? getDefaultDevContainerConfigPath(cliHost, workspace.configFolderPath) : undefined)) : overrideConfigFile; - const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, params.mountWorkspaceGitRoot, output, workspaceMountConsistencyDefault, overrideConfigFile) || undefined; + const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, params.mountWorkspaceGitRoot, params.mountGitWorktreeCommonDir, output, workspaceMountConsistencyDefault, overrideConfigFile) || undefined; if (!configs) { if (configPath || workspace) { throw new ContainerError({ description: `Dev container config (${uriToFsPath(configPath || getDefaultDevContainerConfigPath(cliHost, workspace!.configFolderPath), cliHost.platform)}) not found.` }); @@ -79,7 +79,7 @@ async function resolveWithLocalFolder(params: DockerResolverParameters, parsedAu return result; } -export async function readDevContainerConfigFile(cliHost: CLIHost, workspace: Workspace | undefined, configFile: URI, mountWorkspaceGitRoot: boolean, output: Log, consistency?: BindMountConsistency, overrideConfigFile?: URI) { +export async function readDevContainerConfigFile(cliHost: CLIHost, workspace: Workspace | undefined, configFile: URI, mountWorkspaceGitRoot: boolean, mountGitWorktreeCommonDir: boolean, output: Log, consistency?: BindMountConsistency, overrideConfigFile?: URI) { const documents = createDocuments(cliHost); const content = await documents.readDocument(overrideConfigFile ?? configFile); if (!content) { @@ -90,7 +90,7 @@ export async function readDevContainerConfigFile(cliHost: CLIHost, workspace: Wo if (!updated || typeof updated !== 'object' || Array.isArray(updated)) { throw new ContainerError({ description: `Dev container config (${uriToFsPath(configFile, cliHost.platform)}) must contain a JSON object literal.` }); } - const workspaceConfig = await getWorkspaceConfiguration(cliHost, workspace, updated, mountWorkspaceGitRoot, output, consistency); + const workspaceConfig = await getWorkspaceConfiguration(cliHost, workspace, updated, mountWorkspaceGitRoot, mountGitWorktreeCommonDir, output, consistency); const substitute0: SubstituteConfig = value => substitute({ platform: cliHost.platform, localWorkspaceFolder: workspace?.rootFolderPath, diff --git a/src/spec-node/devContainers.ts b/src/spec-node/devContainers.ts index a6d546566..8a2e6b042 100644 --- a/src/spec-node/devContainers.ts +++ b/src/spec-node/devContainers.ts @@ -30,6 +30,7 @@ export interface ProvisionOptions { workspaceMountConsistency?: BindMountConsistency; gpuAvailability?: GPUAvailability; mountWorkspaceGitRoot: boolean; + mountGitWorktreeCommonDir: boolean; configFile: URI | undefined; overrideConfigFile: URI | undefined; logLevel: LogLevel; @@ -102,7 +103,7 @@ export async function launch(options: ProvisionOptions, providedIdLabels: string } export async function createDockerParams(options: ProvisionOptions, disposables: (() => Promise | undefined)[]): Promise { - const { persistedFolder, additionalMounts, updateRemoteUserUIDDefault, containerDataFolder, containerSystemDataFolder, workspaceMountConsistency, gpuAvailability, mountWorkspaceGitRoot, remoteEnv, experimentalLockfile, experimentalFrozenLockfile, omitLoggerHeader, secretsP } = options; + const { persistedFolder, additionalMounts, updateRemoteUserUIDDefault, containerDataFolder, containerSystemDataFolder, workspaceMountConsistency, gpuAvailability, mountWorkspaceGitRoot, mountGitWorktreeCommonDir, remoteEnv, experimentalLockfile, experimentalFrozenLockfile, omitLoggerHeader, secretsP } = options; let parsedAuthority: DevContainerAuthority | undefined; if (options.workspaceFolder) { parsedAuthority = { hostPath: options.workspaceFolder } as DevContainerAuthority; @@ -225,6 +226,7 @@ export async function createDockerParams(options: ProvisionOptions, disposables: workspaceMountConsistencyDefault: workspaceMountConsistency, gpuAvailability: gpuAvailability || 'detect', mountWorkspaceGitRoot, + mountGitWorktreeCommonDir, updateRemoteUserUIDOnMacOS: false, cacheMount: 'bind', removeOnStartup: options.removeExistingContainer, diff --git a/src/spec-node/devContainersSpecCLI.ts b/src/spec-node/devContainersSpecCLI.ts index 59136695d..5d0bafd91 100644 --- a/src/spec-node/devContainersSpecCLI.ts +++ b/src/spec-node/devContainersSpecCLI.ts @@ -107,6 +107,7 @@ function provisionOptions(y: Argv) { 'workspace-mount-consistency': { choices: ['consistent' as 'consistent', 'cached' as 'cached', 'delegated' as 'delegated'], default: 'cached' as 'cached', description: 'Workspace mount consistency.' }, 'gpu-availability': { choices: ['all' as 'all', 'detect' as 'detect', 'none' as 'none'], default: 'detect' as 'detect', description: 'Availability of GPUs in case the dev container requires any. `all` expects a GPU to be available.' }, 'mount-workspace-git-root': { type: 'boolean', default: true, description: 'Mount the workspace using its Git root.' }, + 'mount-git-worktree-common-dir': { type: 'boolean', default: false, description: 'Mount the Git worktree common dir for Git operations to work in the container. This requires the worktree to be created with relative paths (`git worktree add --relative-paths`).' }, 'id-label': { type: 'string', description: 'Id label(s) of the format name=value. These will be set on the container and used to query for an existing container. If no --id-label is given, one will be inferred from the --workspace-folder path.' }, 'config': { type: 'string', description: 'devcontainer.json path. The default is to use .devcontainer/devcontainer.json or, if that does not exist, .devcontainer.json in the workspace folder.' }, 'override-config': { type: 'string', description: 'devcontainer.json path to override any devcontainer.json in the workspace folder (or built-in configuration). This is required when there is no devcontainer.json otherwise.' }, @@ -182,6 +183,7 @@ async function provision({ 'workspace-mount-consistency': workspaceMountConsistency, 'gpu-availability': gpuAvailability, 'mount-workspace-git-root': mountWorkspaceGitRoot, + 'mount-git-worktree-common-dir': mountGitWorktreeCommonDir, 'id-label': idLabel, config, 'override-config': overrideConfig, @@ -237,6 +239,7 @@ async function provision({ workspaceMountConsistency, gpuAvailability, mountWorkspaceGitRoot, + mountGitWorktreeCommonDir, configFile: config ? URI.file(path.resolve(process.cwd(), config)) : undefined, overrideConfigFile: overrideConfig ? URI.file(path.resolve(process.cwd(), overrideConfig)) : undefined, logLevel: mapLogLevel(logLevel), @@ -420,6 +423,7 @@ async function doSetUp({ containerSystemDataFolder, workspaceFolder: undefined, mountWorkspaceGitRoot: false, + mountGitWorktreeCommonDir: false, configFile, overrideConfigFile: undefined, logLevel: mapLogLevel(logLevel), @@ -456,7 +460,7 @@ async function doSetUp({ const { common } = params; const { cliHost, output } = common; - const configs = configFile && await readDevContainerConfigFile(cliHost, undefined, configFile, params.mountWorkspaceGitRoot, output, undefined, undefined); + const configs = configFile && await readDevContainerConfigFile(cliHost, undefined, configFile, params.mountWorkspaceGitRoot, params.mountGitWorktreeCommonDir, output, undefined, undefined); if (configFile && !configs) { throw new ContainerError({ description: `Dev container config (${uriToFsPath(configFile, cliHost.platform)}) not found.` }); } @@ -586,6 +590,7 @@ async function doBuild({ containerSystemDataFolder: undefined, workspaceFolder, mountWorkspaceGitRoot: false, + mountGitWorktreeCommonDir: false, configFile, overrideConfigFile, logLevel: mapLogLevel(logLevel), @@ -626,7 +631,7 @@ async function doBuild({ ? (await getDevContainerConfigPathIn(cliHost, workspace.configFolderPath) || (overrideConfigFile ? getDefaultDevContainerConfigPath(cliHost, workspace.configFolderPath) : undefined)) : overrideConfigFile; - const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, params.mountWorkspaceGitRoot, output, undefined, overrideConfigFile) || undefined; + const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, params.mountWorkspaceGitRoot, params.mountGitWorktreeCommonDir, output, undefined, overrideConfigFile) || undefined; if (!configs) { throw new ContainerError({ description: `Dev container config (${uriToFsPath(configFile || getDefaultDevContainerConfigPath(cliHost, workspace!.configFolderPath), cliHost.platform)}) not found.` }); } @@ -754,6 +759,7 @@ function runUserCommandsOptions(y: Argv) { 'container-system-data-folder': { type: 'string', description: 'Container system data folder where system data inside the container will be stored.' }, 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' }, 'mount-workspace-git-root': { type: 'boolean', default: true, description: 'Mount the workspace using its Git root.' }, + 'mount-git-worktree-common-dir': { type: 'boolean', default: false, description: 'Mount the Git worktree common dir for Git operations to work in the container. This requires the worktree to be created with relative paths (`git worktree add --relative-paths`).' }, 'container-id': { type: 'string', description: 'Id of the container to run the user commands for.' }, 'id-label': { type: 'string', description: 'Id label(s) of the format name=value. If no --container-id is given the id labels will be used to look up the container. If no --id-label is given, one will be inferred from the --workspace-folder path.' }, 'config': { type: 'string', description: 'devcontainer.json path. The default is to use .devcontainer/devcontainer.json or, if that does not exist, .devcontainer.json in the workspace folder.' }, @@ -814,6 +820,7 @@ async function doRunUserCommands({ 'container-system-data-folder': containerSystemDataFolder, 'workspace-folder': workspaceFolderArg, 'mount-workspace-git-root': mountWorkspaceGitRoot, + 'mount-git-worktree-common-dir': mountGitWorktreeCommonDir, 'container-id': containerId, 'id-label': idLabel, config: configParam, @@ -857,6 +864,7 @@ async function doRunUserCommands({ containerSystemDataFolder, workspaceFolder, mountWorkspaceGitRoot, + mountGitWorktreeCommonDir, configFile, overrideConfigFile, logLevel: mapLogLevel(logLevel), @@ -900,7 +908,7 @@ async function doRunUserCommands({ ? (await getDevContainerConfigPathIn(cliHost, workspace.configFolderPath) || (overrideConfigFile ? getDefaultDevContainerConfigPath(cliHost, workspace.configFolderPath) : undefined)) : overrideConfigFile; - const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, params.mountWorkspaceGitRoot, output, undefined, overrideConfigFile) || undefined; + const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, params.mountWorkspaceGitRoot, params.mountGitWorktreeCommonDir, output, undefined, overrideConfigFile) || undefined; if ((configFile || workspaceFolder || overrideConfigFile) && !configs) { throw new ContainerError({ description: `Dev container config (${uriToFsPath(configFile || getDefaultDevContainerConfigPath(cliHost, workspace!.configFolderPath), cliHost.platform)}) not found.` }); } @@ -956,6 +964,7 @@ function readConfigurationOptions(y: Argv) { 'docker-compose-path': { type: 'string', description: 'Docker Compose CLI path.' }, 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' }, 'mount-workspace-git-root': { type: 'boolean', default: true, description: 'Mount the workspace using its Git root.' }, + 'mount-git-worktree-common-dir': { type: 'boolean', default: false, description: 'Mount the Git worktree common dir for Git operations to work in the container. This requires the worktree to be created with relative paths (`git worktree add --relative-paths`).' }, 'container-id': { type: 'string', description: 'Id of the container to run the user commands for.' }, 'id-label': { type: 'string', description: 'Id label(s) of the format name=value. If no --container-id is given the id labels will be used to look up the container. If no --id-label is given, one will be inferred from the --workspace-folder path.' }, 'config': { type: 'string', description: 'devcontainer.json path. The default is to use .devcontainer/devcontainer.json or, if that does not exist, .devcontainer.json in the workspace folder.' }, @@ -993,6 +1002,7 @@ async function readConfiguration({ 'docker-compose-path': dockerComposePath, 'workspace-folder': workspaceFolderArg, 'mount-workspace-git-root': mountWorkspaceGitRoot, + 'mount-git-worktree-common-dir': mountGitWorktreeCommonDir, config: configParam, 'override-config': overrideConfig, 'container-id': containerId, @@ -1033,7 +1043,7 @@ async function readConfiguration({ ? (await getDevContainerConfigPathIn(cliHost, workspace.configFolderPath) || (overrideConfigFile ? getDefaultDevContainerConfigPath(cliHost, workspace.configFolderPath) : undefined)) : overrideConfigFile; - const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, mountWorkspaceGitRoot, output, undefined, overrideConfigFile) || undefined; + const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, mountWorkspaceGitRoot, mountGitWorktreeCommonDir, output, undefined, overrideConfigFile) || undefined; if ((configFile || workspaceFolder || overrideConfigFile) && !configs) { throw new ContainerError({ description: `Dev container config (${uriToFsPath(configFile || getDefaultDevContainerConfigPath(cliHost, workspace!.configFolderPath), cliHost.platform)}) not found.` }); } @@ -1154,7 +1164,7 @@ async function outdated({ const workspace = workspaceFromPath(cliHost.path, workspaceFolder); const configPath = configFile ? configFile : await getDevContainerConfigPathIn(cliHost, workspace.configFolderPath); - const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, true, output) || undefined; + const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, true, false, output) || undefined; if (!configs) { throw new ContainerError({ description: `Dev container config (${uriToFsPath(configFile || getDefaultDevContainerConfigPath(cliHost, workspace!.configFolderPath), cliHost.platform)}) not found.` }); } @@ -1211,6 +1221,7 @@ function execOptions(y: Argv) { 'container-system-data-folder': { type: 'string', description: 'Container system data folder where system data inside the container will be stored.' }, 'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' }, 'mount-workspace-git-root': { type: 'boolean', default: true, description: 'Mount the workspace using its Git root.' }, + 'mount-git-worktree-common-dir': { type: 'boolean', default: false, description: 'Mount the Git worktree common dir for Git operations to work in the container. This requires the worktree to be created with relative paths (`git worktree add --relative-paths`).' }, 'container-id': { type: 'string', description: 'Id of the container to run the user commands for.' }, 'id-label': { type: 'string', description: 'Id label(s) of the format name=value. If no --container-id is given the id labels will be used to look up the container. If no --id-label is given, one will be inferred from the --workspace-folder path.' }, 'config': { type: 'string', description: 'devcontainer.json path. The default is to use .devcontainer/devcontainer.json or, if that does not exist, .devcontainer.json in the workspace folder.' }, @@ -1272,6 +1283,7 @@ export async function doExec({ 'container-system-data-folder': containerSystemDataFolder, 'workspace-folder': workspaceFolderArg, 'mount-workspace-git-root': mountWorkspaceGitRoot, + 'mount-git-worktree-common-dir': mountGitWorktreeCommonDir, 'container-id': containerId, 'id-label': idLabel, config: configParam, @@ -1304,6 +1316,7 @@ export async function doExec({ containerSystemDataFolder, workspaceFolder, mountWorkspaceGitRoot, + mountGitWorktreeCommonDir, configFile, overrideConfigFile, logLevel: mapLogLevel(logLevel), @@ -1344,7 +1357,7 @@ export async function doExec({ ? (await getDevContainerConfigPathIn(cliHost, workspace.configFolderPath) || (overrideConfigFile ? getDefaultDevContainerConfigPath(cliHost, workspace.configFolderPath) : undefined)) : overrideConfigFile; - const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, params.mountWorkspaceGitRoot, output, undefined, overrideConfigFile) || undefined; + const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, params.mountWorkspaceGitRoot, params.mountGitWorktreeCommonDir, output, undefined, overrideConfigFile) || undefined; if ((configFile || workspaceFolder || overrideConfigFile) && !configs) { throw new ContainerError({ description: `Dev container config (${uriToFsPath(configFile || getDefaultDevContainerConfigPath(cliHost, workspace!.configFolderPath), cliHost.platform)}) not found.` }); } diff --git a/src/spec-node/featuresCLI/resolveDependencies.ts b/src/spec-node/featuresCLI/resolveDependencies.ts index 93e569ff6..887cac6ae 100644 --- a/src/spec-node/featuresCLI/resolveDependencies.ts +++ b/src/spec-node/featuresCLI/resolveDependencies.ts @@ -77,7 +77,7 @@ async function featuresResolveDependencies({ const cliHost = await getCLIHost(cwd, loadNativeModule, true); const workspace = workspaceFromPath(cliHost.path, workspaceFolder); const configFile: URI = URI.file(path.resolve(process.cwd(), configPath)); - const configs = await readDevContainerConfigFile(cliHost, workspace, configFile, false, output, undefined, undefined); + const configs = await readDevContainerConfigFile(cliHost, workspace, configFile, false, false, output, undefined, undefined); if (configFile && !configs) { throw new ContainerError({ description: `Dev container config (${uriToFsPath(configFile, cliHost.platform)}) not found.` }); diff --git a/src/spec-node/featuresCLI/testCommandImpl.ts b/src/spec-node/featuresCLI/testCommandImpl.ts index 558162443..fc9f092fb 100644 --- a/src/spec-node/featuresCLI/testCommandImpl.ts +++ b/src/spec-node/featuresCLI/testCommandImpl.ts @@ -553,6 +553,7 @@ async function launchProject(params: DockerResolverParameters, workspaceFolder: additionalLabels: [], logLevel: common.getLogLevel(), mountWorkspaceGitRoot: true, + mountGitWorktreeCommonDir: false, remoteEnv: common.remoteEnv, skipFeatureAutoMapping: common.skipFeatureAutoMapping, skipPersistingCustomizationsFromFeatures: common.skipPersistingCustomizationsFromFeatures, @@ -632,6 +633,7 @@ async function generateDockerParams(workspaceFolder: string, args: FeaturesTestC containerDataFolder: undefined, containerSystemDataFolder: undefined, mountWorkspaceGitRoot: false, + mountGitWorktreeCommonDir: false, configFile: undefined, overrideConfigFile: undefined, logLevel, diff --git a/src/spec-node/featuresCLI/utils.ts b/src/spec-node/featuresCLI/utils.ts index d72cb1705..c981a4c2e 100644 --- a/src/spec-node/featuresCLI/utils.ts +++ b/src/spec-node/featuresCLI/utils.ts @@ -40,6 +40,7 @@ export const staticExecParams = { 'terminal-columns': undefined, 'container-id': undefined, 'mount-workspace-git-root': true, + 'mount-git-worktree-common-dir': false, 'log-level': 'info' as 'info', 'log-format': 'text' as 'text', 'default-user-env-probe': 'loginInteractiveShell' as 'loginInteractiveShell', diff --git a/src/spec-node/upgradeCommand.ts b/src/spec-node/upgradeCommand.ts index 3336087c7..8e660136f 100644 --- a/src/spec-node/upgradeCommand.ts +++ b/src/spec-node/upgradeCommand.ts @@ -193,7 +193,7 @@ function upgradeFeatureKeyInConfig(configText: string, current: string, updated: } async function getConfig(configPath: URI | undefined, cliHost: CLIHost, workspace: Workspace, output: Log, configFile: URI | undefined): Promise { - const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, true, output) || undefined; + const configs = configPath && await readDevContainerConfigFile(cliHost, workspace, configPath, true, false, output) || undefined; if (!configs) { throw new ContainerError({ description: `Dev container config (${uriToFsPath(configFile || getDefaultDevContainerConfigPath(cliHost, workspace!.configFolderPath), cliHost.platform)}) not found.` }); } diff --git a/src/spec-node/utils.ts b/src/spec-node/utils.ts index 577b2f970..1fb1ec1cc 100644 --- a/src/spec-node/utils.ts +++ b/src/spec-node/utils.ts @@ -107,6 +107,7 @@ export interface DockerResolverParameters { workspaceMountConsistencyDefault: BindMountConsistency; gpuAvailability: GPUAvailability; mountWorkspaceGitRoot: boolean; + mountGitWorktreeCommonDir: boolean; updateRemoteUserUIDOnMacOS: boolean; cacheMount: 'volume' | 'bind' | 'none'; removeOnStartup?: boolean | string; @@ -350,7 +351,7 @@ export interface WorkspaceConfiguration { additionalMountString: string | undefined; } -export async function getWorkspaceConfiguration(cliHost: CLIHost, workspace: Workspace | undefined, config: DevContainerConfig, mountWorkspaceGitRoot: boolean, output: Log, consistency?: BindMountConsistency): Promise { +export async function getWorkspaceConfiguration(cliHost: CLIHost, workspace: Workspace | undefined, config: DevContainerConfig, mountWorkspaceGitRoot: boolean, mountGitWorktreeCommonDir: boolean, output: Log, consistency?: BindMountConsistency): Promise { if ('dockerComposeFile' in config) { return { workspaceFolder: getRemoteWorkspaceFolder(config), @@ -365,7 +366,7 @@ export async function getWorkspaceConfiguration(cliHost: CLIHost, workspace: Wor // Check if .git is a file (worktree) with a relative gitdir path let containerMountFolder = path.posix.join('/workspaces', cliHost.path.basename(hostMountFolder)); - if (mountWorkspaceGitRoot) { + if (mountWorkspaceGitRoot && mountGitWorktreeCommonDir) { const dotGitPath = cliHost.path.join(hostMountFolder, '.git'); if (await cliHost.isFile(dotGitPath)) { const dotGitContent = (await cliHost.readFile(dotGitPath)).toString(); diff --git a/src/test/workspaceConfiguration.test.ts b/src/test/workspaceConfiguration.test.ts index 9aca56b88..1e0b7e992 100644 --- a/src/test/workspaceConfiguration.test.ts +++ b/src/test/workspaceConfiguration.test.ts @@ -86,6 +86,7 @@ describe('getWorkspaceConfiguration', function () { workspace, {}, false, + false, nullLog ); @@ -116,6 +117,7 @@ describe('getWorkspaceConfiguration', function () { workspace, {}, true, + true, nullLog ); @@ -142,12 +144,73 @@ describe('getWorkspaceConfiguration', function () { workspace, {}, true, + true, nullLog ); assert.isUndefined(result.additionalMountString); }); + it('should not add additional mount when mountGitWorktreeCommonDir is false', async () => { + const p = { + linux: { worktreePath: '/home/user/worktrees/feature', gitFile: '/home/user/worktrees/feature/.git', gitdir: 'gitdir: ../../repo/.git/worktrees/feature', consistency: '' }, + darwin: { worktreePath: '/Users/user/worktrees/feature', gitFile: '/Users/user/worktrees/feature/.git', gitdir: 'gitdir: ../../repo/.git/worktrees/feature', consistency: ',consistency=consistent' }, + win32: { worktreePath: 'C:\\Users\\user\\worktrees\\feature', gitFile: 'C:\\Users\\user\\worktrees\\feature\\.git', gitdir: 'gitdir: ../../repo/.git/worktrees/feature', consistency: ',consistency=consistent' }, + }[platform]; + + const cliHost = createMockCLIHost({ + platform, + files: { + [p.gitFile]: p.gitdir + } + }); + const workspace = createWorkspace(p.worktreePath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + true, + false, + nullLog + ); + + assert.strictEqual(result.workspaceFolder, '/workspaces/feature'); + assert.strictEqual(result.workspaceMount, `type=bind,source=${p.worktreePath},target=/workspaces/feature${p.consistency}`); + assert.isUndefined(result.additionalMountString); + }); + + it('should not add additional mount when mountGitWorktreeCommonDir is false with workspace in subfolder', async () => { + const p = { + linux: { worktreePath: '/home/user/worktrees/feature', gitConfigFile: '/home/user/worktrees/feature/.git/config', gitFile: '/home/user/worktrees/feature/.git', gitdir: 'gitdir: ../../repo/.git/worktrees/feature', subfolderPath: '/home/user/worktrees/feature/packages/app', consistency: '' }, + darwin: { worktreePath: '/Users/user/worktrees/feature', gitConfigFile: '/Users/user/worktrees/feature/.git/config', gitFile: '/Users/user/worktrees/feature/.git', gitdir: 'gitdir: ../../repo/.git/worktrees/feature', subfolderPath: '/Users/user/worktrees/feature/packages/app', consistency: ',consistency=consistent' }, + win32: { worktreePath: 'C:\\Users\\user\\worktrees\\feature', gitConfigFile: 'C:\\Users\\user\\worktrees\\feature\\.git\\config', gitFile: 'C:\\Users\\user\\worktrees\\feature\\.git', gitdir: 'gitdir: ../../repo/.git/worktrees/feature', subfolderPath: 'C:\\Users\\user\\worktrees\\feature\\packages\\app', consistency: ',consistency=consistent' }, + }[platform]; + + const cliHost = createMockCLIHost({ + platform, + files: { + [p.gitConfigFile]: '[core]', + [p.gitFile]: p.gitdir + }, + useFileHost: true + }); + const workspace = createWorkspace(p.subfolderPath); + + const result = await getWorkspaceConfiguration( + cliHost, + workspace, + {}, + true, + false, + nullLog + ); + + assert.strictEqual(result.workspaceFolder, '/workspaces/feature/packages/app'); + assert.strictEqual(result.workspaceMount, `type=bind,source=${p.worktreePath},target=/workspaces/feature${p.consistency}`); + assert.isUndefined(result.additionalMountString); + }); + it('should add additional mount when gitdir is a relative path', async () => { const p = { linux: { worktreePath: '/home/user/worktrees/feature', gitFile: '/home/user/worktrees/feature/.git', gitdir: 'gitdir: ../../repo/.git/worktrees/feature', repoGitPath: '/home/user/repo/.git', consistency: '' }, @@ -168,6 +231,7 @@ describe('getWorkspaceConfiguration', function () { workspace, {}, true, + true, nullLog ); @@ -196,6 +260,7 @@ describe('getWorkspaceConfiguration', function () { workspace, {}, true, + true, nullLog ); @@ -223,6 +288,7 @@ describe('getWorkspaceConfiguration', function () { workspace, {}, true, + true, nullLog ); @@ -253,6 +319,7 @@ describe('getWorkspaceConfiguration', function () { workspace, {}, true, + true, nullLog ); @@ -286,6 +353,7 @@ describe('getWorkspaceConfiguration', function () { workspace, {}, true, + true, nullLog ); @@ -315,6 +383,7 @@ describe('getWorkspaceConfiguration', function () { workspace, {}, false, + false, nullLog ); @@ -343,6 +412,7 @@ describe('getWorkspaceConfiguration', function () { workspace, {}, true, + true, nullLog ); @@ -372,6 +442,7 @@ describe('getWorkspaceConfiguration', function () { workspace, {}, true, + true, nullLog ); @@ -398,6 +469,7 @@ describe('getWorkspaceConfiguration', function () { workspace, { workspaceFolder: '/custom/path' }, false, + false, nullLog ); @@ -419,6 +491,7 @@ describe('getWorkspaceConfiguration', function () { workspace, { workspaceMount: 'type=bind,source=/custom,target=/workspace' }, false, + false, nullLog ); From 168981231edb50b952469d7e77f4a1a4c9e20455 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Thu, 8 Jan 2026 16:49:54 +0100 Subject: [PATCH 33/88] 0.81.0 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3d3b7caf..58e94c9c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ Notable changes. +## January 2026 + +### [0.81.0] +- Add option to mount a worktree's common folder. (https://github.com/devcontainers/cli/pull/1127) + ## December 2025 ### [0.80.3] diff --git a/package.json b/package.json index af8e375dd..9678bfb52 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.80.3", + "version": "0.81.0", "bin": { "devcontainer": "devcontainer.js" }, From e7b5c337378e71d445066dfa6bb96de85f9c2c94 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 12 Jan 2026 10:02:09 +0100 Subject: [PATCH 34/88] Update dependencies with security fixes --- CHANGELOG.md | 3 +++ package.json | 2 +- yarn.lock | 12 ++++++------ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58e94c9c3..ec2128f36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Notable changes. ## January 2026 +### [0.81.1] +- Update js-yaml and glob dependencies. (https://github.com/devcontainers/cli/pull/1128) + ### [0.81.0] - Add option to mount a worktree's common folder. (https://github.com/devcontainers/cli/pull/1127) diff --git a/package.json b/package.json index 9678bfb52..be3d6f2fe 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.81.0", + "version": "0.81.1", "bin": { "devcontainer": "devcontainer.js" }, diff --git a/yarn.lock b/yarn.lock index c7771cf81..5e6f91a7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1863,9 +1863,9 @@ glob-stream@^8.0.3: streamx "^2.12.5" glob@^10.3.7, glob@^10.4.5: - version "10.4.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" - integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + version "10.5.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" + integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== dependencies: foreground-child "^3.1.0" jackspeak "^3.1.2" @@ -2423,9 +2423,9 @@ js-tokens@^4.0.0: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + version "3.14.2" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0" + integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg== dependencies: argparse "^1.0.7" esprima "^4.0.0" From 2b01a9091efcbe1676f5bf7a912be90f7e67fca3 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 21 Jan 2026 09:23:35 +0100 Subject: [PATCH 35/88] Apply suggestion from @mr-brobot Co-authored-by: Josh Wiley --- src/spec-node/featuresCLI/resolveDependencies.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spec-node/featuresCLI/resolveDependencies.ts b/src/spec-node/featuresCLI/resolveDependencies.ts index d052ecfaa..58dfeec26 100644 --- a/src/spec-node/featuresCLI/resolveDependencies.ts +++ b/src/spec-node/featuresCLI/resolveDependencies.ts @@ -30,7 +30,7 @@ export function featuresResolveDependenciesOptions(y: Argv) { return y .options({ 'log-level': { choices: ['error' as 'error', 'info' as 'info', 'debug' as 'debug', 'trace' as 'trace'], default: 'error' as 'error', description: 'Log level.' }, - 'workspace-folder': { type: 'string', description: 'Workspace folder to use for the configuration.If --workspace-folder is not provided, this defaults to the current directory' }, + 'workspace-folder': { type: 'string', description: 'Workspace folder to use for the configuration. If --workspace-folder is not provided, this defaults to the current directory' }, }); } From 010aab5900517d6e2b09588a00ac59dceb25bfd2 Mon Sep 17 00:00:00 2001 From: Mathiyarasy <157102811+Mathiyarasy@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:07:25 +0000 Subject: [PATCH 36/88] remove comments --- src/test/cli.build.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/cli.build.test.ts b/src/test/cli.build.test.ts index c57e2f4d5..51e079eb6 100644 --- a/src/test/cli.build.test.ts +++ b/src/test/cli.build.test.ts @@ -435,7 +435,7 @@ describe('Dev Containers CLI', function () { }); it('should use current directory for build when no workspace-folder provided', async function () { - const testFolder = `${__dirname}/configs/image`; // Use simpler config without features + const testFolder = `${__dirname}/configs/image`; const absoluteTmpPath = path.resolve(__dirname, 'tmp'); const absoluteCli = `npx --prefix ${absoluteTmpPath} devcontainer`; const originalCwd = process.cwd(); From 8732255a0bff8e2a4a78279fb49bffd361504248 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 26 Jan 2026 09:30:42 +0100 Subject: [PATCH 37/88] Update tar for security fixes --- package.json | 2 +- .../containerCollectionsOCI.ts | 10 ++- .../containerFeaturesConfiguration.ts | 5 +- src/spec-configuration/typings/zlib-zstd.d.ts | 6 ++ .../packageCommandImpl.ts | 2 +- src/spec-node/typings/zlib-zstd.d.ts | 6 ++ yarn.lock | 76 ++++++++----------- 7 files changed, 54 insertions(+), 53 deletions(-) create mode 100644 src/spec-configuration/typings/zlib-zstd.d.ts create mode 100644 src/spec-node/typings/zlib-zstd.d.ts diff --git a/package.json b/package.json index 90b5d8a8f..97e73fa11 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "semver": "^7.7.1", "shell-quote": "^1.8.2", "stream-to-pull-stream": "^1.7.3", - "tar": "^6.2.1", + "tar": "^7.5.4", "text-table": "^0.2.0", "vscode-uri": "^3.1.0", "yargs": "~17.7.2" diff --git a/src/spec-configuration/containerCollectionsOCI.ts b/src/spec-configuration/containerCollectionsOCI.ts index 082bb0daa..a2e4ad55f 100644 --- a/src/spec-configuration/containerCollectionsOCI.ts +++ b/src/spec-configuration/containerCollectionsOCI.ts @@ -563,8 +563,9 @@ export async function getBlob(params: CommonParams, url: string, ociCacheDir: st { file: tempTarballPath, cwd: destCachePath, - filter: (tPath: string, stat: tar.FileStat) => { - output.write(`Testing '${tPath}'(${stat.type})`, LogLevel.Trace); + filter: (tPath, stat) => { + const entryType = 'type' in stat ? stat.type : (stat.isFile() ? 'File' : stat.isDirectory() ? 'Directory' : 'Other'); + output.write(`Testing '${tPath}'(${entryType})`, LogLevel.Trace); const cleanedPath = tPath .replace(/\\/g, '/') .replace(/^\.\//, ''); @@ -574,7 +575,8 @@ export async function getBlob(params: CommonParams, url: string, ociCacheDir: st return false; // Skip } - if (stat.type.toString() === 'File') { + const isFile = 'type' in stat ? stat.type === 'File' : stat.isFile(); + if (isFile) { files.push(tPath); } @@ -594,7 +596,7 @@ export async function getBlob(params: CommonParams, url: string, ociCacheDir: st { file: tempTarballPath, cwd: ociCacheDir, - filter: (tPath: string, _: tar.FileStat) => { + filter: (tPath, _) => { return tPath === `./${metadataFile}`; } }); diff --git a/src/spec-configuration/containerFeaturesConfiguration.ts b/src/spec-configuration/containerFeaturesConfiguration.ts index 662ed3b65..78d6c8018 100644 --- a/src/spec-configuration/containerFeaturesConfiguration.ts +++ b/src/spec-configuration/containerFeaturesConfiguration.ts @@ -10,6 +10,7 @@ import * as tar from 'tar'; import * as crypto from 'crypto'; import * as semver from 'semver'; import * as os from 'os'; +import * as fs from 'fs'; import { DevContainerConfig, DevContainerFeature, VSCodeCustomizations } from './configuration'; import { mkdirpLocal, readLocalFile, rmLocal, writeLocalFile, cpDirectoryLocal, isLocalFile } from '../spec-utils/pfs'; @@ -1097,7 +1098,7 @@ export async function fetchContentsAtTarballUri(params: { output: Log; env: Node } // Filter what gets emitted from the tar.extract(). - const filter = (file: string, _: tar.FileStat) => { + const filter = (file: string, _: fs.Stats | tar.ReadEntry) => { // Don't include .dotfiles or the archive itself. if (file.startsWith('./.') || file === `./${V1_ASSET_NAME}` || file === './.') { return false; @@ -1128,7 +1129,7 @@ export async function fetchContentsAtTarballUri(params: { output: Log; env: Node { file: tempTarballPath, cwd: featCachePath, - filter: (path: string, _: tar.FileStat) => { + filter: (path, _) => { return path === `./${metadataFile}`; } }); diff --git a/src/spec-configuration/typings/zlib-zstd.d.ts b/src/spec-configuration/typings/zlib-zstd.d.ts new file mode 100644 index 000000000..6614b3eab --- /dev/null +++ b/src/spec-configuration/typings/zlib-zstd.d.ts @@ -0,0 +1,6 @@ +// Stub types for Zstd compression classes added in Node.js 23.8.0 +// Required for minizlib's type definitions which reference these types +declare module 'zlib' { + interface ZstdCompress extends NodeJS.ReadWriteStream {} + interface ZstdDecompress extends NodeJS.ReadWriteStream {} +} diff --git a/src/spec-node/collectionCommonUtils/packageCommandImpl.ts b/src/spec-node/collectionCommonUtils/packageCommandImpl.ts index 271c0a049..cd54533c3 100644 --- a/src/spec-node/collectionCommonUtils/packageCommandImpl.ts +++ b/src/spec-node/collectionCommonUtils/packageCommandImpl.ts @@ -1,4 +1,4 @@ -import tar from 'tar'; +import * as tar from 'tar'; import * as jsonc from 'jsonc-parser'; import * as os from 'os'; import * as recursiveDirReader from 'recursive-readdir'; diff --git a/src/spec-node/typings/zlib-zstd.d.ts b/src/spec-node/typings/zlib-zstd.d.ts new file mode 100644 index 000000000..6614b3eab --- /dev/null +++ b/src/spec-node/typings/zlib-zstd.d.ts @@ -0,0 +1,6 @@ +// Stub types for Zstd compression classes added in Node.js 23.8.0 +// Required for minizlib's type definitions which reference these types +declare module 'zlib' { + interface ZstdCompress extends NodeJS.ReadWriteStream {} + interface ZstdDecompress extends NodeJS.ReadWriteStream {} +} diff --git a/yarn.lock b/yarn.lock index 5e6f91a7c..bc92a9650 100644 --- a/yarn.lock +++ b/yarn.lock @@ -230,6 +230,13 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" +"@isaacs/fs-minipass@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32" + integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== + dependencies: + minipass "^7.0.4" + "@jridgewell/resolve-uri@^3.0.3": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" @@ -931,10 +938,10 @@ chokidar@^4.0.1: dependencies: readdirp "^4.0.1" -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +chownr@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" + integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== cli-cursor@^3.1.0: version "3.1.0" @@ -1735,13 +1742,6 @@ from@^0.1.7: resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs-mkdirp-stream@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-2.0.1.tgz#1e82575c4023929ad35cf69269f84f1a8c973aa7" @@ -2617,35 +2617,22 @@ minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass@^3.0.0: - version "3.3.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" - minipass@^4.0.0: version "4.2.8" resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== -minipass@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" - integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4, minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== +minizlib@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.1.0.tgz#6ad76c3a8f10227c9b51d1c9ac8e30b27f5a251c" + integrity sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw== dependencies: - minipass "^3.0.0" - yallist "^4.0.0" + minipass "^7.1.2" mkdirp@^0.5.1: version "0.5.6" @@ -2654,7 +2641,7 @@ mkdirp@^0.5.1: dependencies: minimist "^1.2.6" -mkdirp@^1.0.3, mkdirp@^1.0.4: +mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -3752,17 +3739,16 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -tar@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" - integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== +tar@^7.5.4: + version "7.5.6" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.6.tgz#2db7a210748a82f0a89cc31527b90d3a24984fb7" + integrity sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA== dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^5.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" + "@isaacs/fs-minipass" "^4.0.0" + chownr "^3.0.0" + minipass "^7.1.2" + minizlib "^3.1.0" + yallist "^5.0.0" teex@^1.0.1: version "1.0.1" @@ -4213,10 +4199,10 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yallist@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533" + integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== yargs-parser@^20.2.2: version "20.2.9" From de26f3b88c302ca1c4d85256af5e96c7e73f02e8 Mon Sep 17 00:00:00 2001 From: ross-p-smith Date: Thu, 29 Jan 2026 22:22:25 +0000 Subject: [PATCH 38/88] Added BUILDKIT_INLINE_CACHE for containerFeature path --- src/spec-node/containerFeatures.ts | 5 ++++- src/spec-node/singleContainer.ts | 6 ++++-- src/spec-node/utils.ts | 7 +++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/spec-node/containerFeatures.ts b/src/spec-node/containerFeatures.ts index cc957c592..e05822d1b 100644 --- a/src/spec-node/containerFeatures.ts +++ b/src/spec-node/containerFeatures.ts @@ -11,7 +11,7 @@ import { LogLevel, makeLog } from '../spec-utils/log'; import { FeaturesConfig, getContainerFeaturesBaseDockerFile, getFeatureInstallWrapperScript, getFeatureLayers, getFeatureMainValue, getFeatureValueObject, generateFeaturesConfig, Feature, generateContainerEnvs } from '../spec-configuration/containerFeaturesConfiguration'; import { readLocalFile } from '../spec-utils/pfs'; import { includeAllConfiguredFeatures } from '../spec-utils/product'; -import { createFeaturesTempFolder, DockerResolverParameters, getCacheFolder, getFolderImageName, getEmptyContextFolder, SubstitutedConfig } from './utils'; +import { createFeaturesTempFolder, DockerResolverParameters, getCacheFolder, getFolderImageName, getEmptyContextFolder, SubstitutedConfig, isBuildxCacheToInline } from './utils'; import { isEarlierVersion, parseVersion, runCommandNoPty } from '../spec-common/commonUtils'; import { getDevcontainerMetadata, getDevcontainerMetadataLabel, getImageBuildInfoFromImage, ImageBuildInfo, ImageMetadataEntry, imageMetadataLabel, MergedDevContainerConfig } from './imageMetadata'; import { supportsBuildContexts } from './dockerfileUtils'; @@ -85,6 +85,9 @@ export async function extendImage(params: DockerResolverParameters, config: Subs if (params.buildxCacheTo) { args.push('--cache-to', params.buildxCacheTo); } + if (!isBuildxCacheToInline(params.buildxCacheTo)) { + args.push('--build-arg', 'BUILDKIT_INLINE_CACHE=1'); + } if (!params.buildNoCache) { params.additionalCacheFroms.forEach(cacheFrom => args.push('--cache-from', cacheFrom)); } diff --git a/src/spec-node/singleContainer.ts b/src/spec-node/singleContainer.ts index 2f4a8a3fc..dc38ef1fc 100644 --- a/src/spec-node/singleContainer.ts +++ b/src/spec-node/singleContainer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ -import { createContainerProperties, startEventSeen, ResolverResult, getTunnelInformation, getDockerfilePath, getDockerContextPath, DockerResolverParameters, isDockerFileConfig, uriToWSLFsPath, WorkspaceConfiguration, getFolderImageName, inspectDockerImage, logUMask, SubstitutedConfig, checkDockerSupportForGPU, isBuildKitImagePolicyError } from './utils'; +import { createContainerProperties, startEventSeen, ResolverResult, getTunnelInformation, getDockerfilePath, getDockerContextPath, DockerResolverParameters, isDockerFileConfig, uriToWSLFsPath, WorkspaceConfiguration, getFolderImageName, inspectDockerImage, logUMask, SubstitutedConfig, checkDockerSupportForGPU, isBuildKitImagePolicyError, isBuildxCacheToInline } from './utils'; import { ContainerProperties, setupInContainer, ResolverProgress, ResolverParameters } from '../spec-common/injectHeadless'; import { ContainerError, toErrorText } from '../spec-common/errors'; import { ContainerDetails, listContainers, DockerCLIParameters, inspectContainers, dockerCLI, dockerPtyCLI, toPtyExecParameters, ImageDetails, toExecParameters, removeContainer } from '../spec-shutdown/dockerUtils'; @@ -209,7 +209,9 @@ async function buildAndExtendImage(buildParams: DockerResolverParameters, config if (buildParams.buildxCacheTo) { args.push('--cache-to', buildParams.buildxCacheTo); } - args.push('--build-arg', 'BUILDKIT_INLINE_CACHE=1'); + if (!isBuildxCacheToInline(buildParams.buildxCacheTo)) { + args.push('--build-arg', 'BUILDKIT_INLINE_CACHE=1'); + } } else { args.push('build'); } diff --git a/src/spec-node/utils.ts b/src/spec-node/utils.ts index 1fb1ec1cc..5c0fb374c 100644 --- a/src/spec-node/utils.ts +++ b/src/spec-node/utils.ts @@ -93,6 +93,13 @@ export async function logUMask(params: DockerResolverParameters): Promise Date: Tue, 3 Feb 2026 13:24:05 +0000 Subject: [PATCH 39/88] Add unit test for the behaviour --- src/test/utils.test.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/test/utils.test.ts diff --git a/src/test/utils.test.ts b/src/test/utils.test.ts new file mode 100644 index 000000000..b266b9d2a --- /dev/null +++ b/src/test/utils.test.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; + +import { isBuildxCacheToInline } from '../spec-node/utils'; + +describe('Utils', function () { + describe('isBuildxCacheToInline', function () { + it('returns false for undefined or empty', () => { + assert.strictEqual(isBuildxCacheToInline(undefined), false); + assert.strictEqual(isBuildxCacheToInline(''), false); + }); + + it('returns true for inline cache type', () => { + assert.strictEqual(isBuildxCacheToInline('type=inline'), true); + assert.strictEqual(isBuildxCacheToInline('type = inline'), true); + assert.strictEqual(isBuildxCacheToInline('type=INLINE'), true); + assert.strictEqual(isBuildxCacheToInline('mode=max,type=inline,compression=zstd'), true); + }); + + it('returns false for non-inline cache type', () => { + assert.strictEqual(isBuildxCacheToInline('type=registry'), false); + assert.strictEqual(isBuildxCacheToInline('type=local'), false); + assert.strictEqual(isBuildxCacheToInline('inline'), false); + }); + }); +}); From 3fd3e9609f33867f25ffaaa2e7a7033244f115f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 18:02:44 +0000 Subject: [PATCH 40/88] Bump lodash from 4.17.21 to 4.17.23 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.17.23 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index bc92a9650..c6d46d2b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2518,9 +2518,9 @@ lodash.merge@^4.6.2: integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash@^4.17.14, lodash@^4.17.19: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + version "4.17.23" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" + integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== log-symbols@^4.1.0: version "4.1.0" From d3f8f7a06760e89df9e31f2bd751b51ad53c0f27 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Thu, 5 Feb 2026 08:33:39 +0100 Subject: [PATCH 41/88] Remove request body limit --- src/spec-utils/httpRequest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spec-utils/httpRequest.ts b/src/spec-utils/httpRequest.ts index 43df09d78..162c55cc5 100644 --- a/src/spec-utils/httpRequest.ts +++ b/src/spec-utils/httpRequest.ts @@ -87,7 +87,7 @@ export async function requestResolveHeaders(options: { type: string; url: string const parsed = new url.URL(options.url); const reqOptions: RequestOptions & tls.CommonConnectionOptions & FollowOptions = { hostname: parsed.hostname, - maxBodyLength: 100 * 1024 * 1024, + maxBodyLength: Infinity, port: parsed.port, path: parsed.pathname + parsed.search, method: options.type, From dbb58883a644d713c208f862b22649b5ca701c96 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Feb 2026 13:31:49 +0100 Subject: [PATCH 42/88] Add install script --- .github/workflows/dev-containers.yml | 14 +- README.md | 34 +- scripts/install.sh | 672 +++++++++++++++++++++++++++ scripts/install.test.sh | 580 +++++++++++++++++++++++ 4 files changed, 1297 insertions(+), 3 deletions(-) create mode 100755 scripts/install.sh create mode 100755 scripts/install.test.sh diff --git a/.github/workflows/dev-containers.yml b/.github/workflows/dev-containers.yml index b635d259c..d1f43995e 100644 --- a/.github/workflows/dev-containers.yml +++ b/.github/workflows/dev-containers.yml @@ -120,9 +120,21 @@ jobs: FEATURES_TEST__AZURE_REGISTRY_SCOPED_CREDENTIAL: ${{ secrets.FEATURES_TEST__AZURE_REGISTRY_SCOPED_CREDENTIAL }} + install-script: + name: Install Script + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v6 + - name: Run install.sh tests + run: sh scripts/install.test.sh + tests: name: Tests - needs: [tests-matrix, features-registry-compatibility] + needs: [tests-matrix, features-registry-compatibility, install-script] runs-on: ubuntu-latest steps: - name: Done diff --git a/README.md b/README.md index d59523f83..622decd57 100644 --- a/README.md +++ b/README.md @@ -22,12 +22,42 @@ This CLI is in active development. Current status: ## Try it out -We'd love for you to try out the dev container CLI and let us know what you think. You can quickly try it out in just a few simple steps, either by installing its npm package or building the CLI repo from sources (see "[Build from sources](#build-from-sources)"). +We'd love for you to try out the dev container CLI and let us know what you think. You can quickly try it out in just a few simple steps, either by using the install script, installing its npm package, or building the CLI repo from sources (see "[Build from sources](#build-from-sources)"). -To install the npm package you will need Python and C/C++ installed to build one of the dependencies (see, e.g., [here](https://github.com/microsoft/vscode/wiki/How-to-Contribute) for instructions). +### Install script + +You can install the CLI with a standalone script that downloads a bundled Node.js runtime, so no pre-installed Node.js is required. It works on Linux and macOS (x64 and arm64): + +```bash +curl -fsSL https://raw.githubusercontent.com/devcontainers/cli/main/scripts/install.sh | sh +``` + +Then add the install location to your PATH: + +```bash +export PATH="$HOME/.devcontainers/bin:$PATH" +``` + +You can also specify a version, a custom install directory, or update/uninstall an existing installation: + +```bash +# Install a specific version +sh install.sh --version 0.82.0 + +# Install to a custom directory +sh install.sh --prefix ~/.local/devcontainers + +# Update to latest +sh install.sh --update + +# Uninstall +sh install.sh --uninstall +``` ### npm install +To install the npm package you will need Python and C/C++ installed to build one of the dependencies (see, e.g., [here](https://github.com/microsoft/vscode/wiki/How-to-Contribute) for instructions). + ```bash npm install -g @devcontainers/cli ``` diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 000000000..ab048dbc9 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,672 @@ +#!/bin/sh +# install.sh - Install @devcontainers/cli with bundled Node.js +# +# Usage: +# curl -fsSL https://raw.githubusercontent.com/devcontainers/cli/main/scripts/install.sh | sh +# wget -qO- https://raw.githubusercontent.com/devcontainers/cli/main/scripts/install.sh | sh +# +# Options: +# --prefix DIR Installation directory (default: ~/.devcontainers) +# --version VER CLI version to install (default: latest) +# --node-version VER Node.js major version (default: 20) +# --update Update existing installation to latest versions +# --uninstall Remove the installation +# --help Show this help message +# +# Environment: +# DEVCONTAINERS_INSTALL_DIR Override default installation directory + +set -e + +# Default configuration +INSTALL_PREFIX="${DEVCONTAINERS_INSTALL_DIR:-$HOME/.devcontainers}" +CLI_VERSION="latest" +NODE_MAJOR_VERSION="20" +UPDATE_MODE=false +UNINSTALL_MODE=false + +# Terminal colors (disabled if not a tty) +setup_colors() { + if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[0;33m' + BLUE='\033[0;34m' + BOLD='\033[1m' + RESET='\033[0m' + else + RED='' + GREEN='' + YELLOW='' + BLUE='' + BOLD='' + RESET='' + fi +} + +say() { + printf '%b\n' "${GREEN}>${RESET} $1" +} + +warn() { + printf '%b\n' "${YELLOW}warning${RESET}: $1" >&2 +} + +error() { + printf '%b\n' "${RED}error${RESET}: $1" >&2 +} + +# Print usage information +usage() { + cat << 'EOF' +Install @devcontainers/cli with bundled Node.js + +Usage: + curl -fsSL https://raw.githubusercontent.com/devcontainers/cli/main/scripts/install.sh | sh + sh install.sh [OPTIONS] + +Options: + --prefix DIR Installation directory (default: ~/.devcontainers) + --version VER CLI version to install (default: latest) + --node-version VER Node.js major version (default: 20) + --update Update existing installation to latest versions + --uninstall Remove the installation + --help Show this help message + +Environment: + DEVCONTAINERS_INSTALL_DIR Override default installation directory + +Examples: + # Install latest version + curl -fsSL https://raw.githubusercontent.com/devcontainers/cli/main/scripts/install.sh | sh + + # Install specific version + sh install.sh --version 0.82.0 + + # Install to custom directory + sh install.sh --prefix ~/.local/devcontainers + + # Update existing installation + sh install.sh --update + + # Uninstall + sh install.sh --uninstall + +After installation, add to your shell profile: + export PATH="$HOME/.devcontainers/bin:$PATH" +EOF +} + +# Parse command-line arguments +parse_args() { + while [ $# -gt 0 ]; do + case "$1" in + --prefix) + INSTALL_PREFIX="$2" + shift 2 + ;; + --prefix=*) + INSTALL_PREFIX="${1#*=}" + shift + ;; + --version) + CLI_VERSION="$2" + shift 2 + ;; + --version=*) + CLI_VERSION="${1#*=}" + shift + ;; + --node-version) + NODE_MAJOR_VERSION="$2" + shift 2 + ;; + --node-version=*) + NODE_MAJOR_VERSION="${1#*=}" + shift + ;; + --update) + UPDATE_MODE=true + shift + ;; + --uninstall) + UNINSTALL_MODE=true + shift + ;; + --help|-h) + usage + exit 0 + ;; + *) + error "Unknown option: $1" + usage + exit 1 + ;; + esac + done +} + +# Detect platform (OS and architecture) +detect_platform() { + # OS detection + case "$(uname -s)" in + Linux*) + PLATFORM="linux" + ;; + Darwin*) + PLATFORM="darwin" + ;; + CYGWIN*|MINGW*|MSYS*) + error "Windows is not supported by this installer." + error "Please use WSL (Windows Subsystem for Linux) or install via npm:" + error " npm install -g @devcontainers/cli" + exit 1 + ;; + *) + error "Unsupported operating system: $(uname -s)" + exit 1 + ;; + esac + + # Architecture detection + case "$(uname -m)" in + x86_64|amd64) + ARCH="x64" + ;; + aarch64|arm64) + ARCH="arm64" + ;; + armv7l|armv6l) + error "32-bit ARM is not supported." + exit 1 + ;; + *) + error "Unsupported architecture: $(uname -m)" + exit 1 + ;; + esac + + # macOS: Detect if running under Rosetta 2 and prefer native arm64 + if [ "$PLATFORM" = "darwin" ] && [ "$ARCH" = "x64" ]; then + if sysctl -n sysctl.proc_translated 2>/dev/null | grep -q 1; then + say "Detected Rosetta 2 translation, using native arm64 binary" + ARCH="arm64" + fi + fi +} + +# Check for required tools +check_prerequisites() { + # Check for curl or wget + if command -v curl >/dev/null 2>&1; then + DOWNLOADER="curl" + elif command -v wget >/dev/null 2>&1; then + DOWNLOADER="wget" + else + error "Either 'curl' or 'wget' is required but neither was found." + exit 1 + fi + + # Check for tar + if ! command -v tar >/dev/null 2>&1; then + error "'tar' is required but not found." + exit 1 + fi + + # Check if we can write to the install directory + if [ -e "$INSTALL_PREFIX" ]; then + if [ ! -d "$INSTALL_PREFIX" ]; then + error "Installation path exists but is not a directory: $INSTALL_PREFIX" + exit 1 + fi + if [ ! -w "$INSTALL_PREFIX" ]; then + error "No write permission for installation directory: $INSTALL_PREFIX" + exit 1 + fi + else + # Check if we can create the directory + PARENT_DIR="$(dirname "$INSTALL_PREFIX")" + if [ ! -w "$PARENT_DIR" ]; then + error "No write permission to create installation directory: $INSTALL_PREFIX" + exit 1 + fi + fi +} + +# Download a file using curl or wget +download() { + url="$1" + output="$2" + + if [ "$DOWNLOADER" = "curl" ]; then + curl -fSL --retry 3 --retry-delay 2 -o "$output" "$url" + else + wget --tries=3 --waitretry=2 -q -O "$output" "$url" + fi +} + +# Fetch content from a URL (for API calls) +fetch() { + url="$1" + + if [ "$DOWNLOADER" = "curl" ]; then + curl -fsSL "$url" + else + wget -qO- "$url" + fi +} + +# Resolve "latest" CLI version from npm registry +resolve_cli_version() { + if [ "$CLI_VERSION" = "latest" ]; then + say "Resolving latest CLI version..." + version=$(fetch "https://registry.npmjs.org/@devcontainers/cli/latest" | \ + sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + if [ -z "$version" ]; then + error "Failed to resolve latest CLI version from npm registry" + exit 1 + fi + CLI_VERSION="$version" + fi + say "CLI version: $CLI_VERSION" +} + +# Resolve full Node.js version from major version +resolve_node_version() { + say "Resolving Node.js v$NODE_MAJOR_VERSION LTS version..." + + # Get the latest version for the major version + index_url="https://nodejs.org/dist/index.json" + version=$(fetch "$index_url" | \ + sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"v\('"$NODE_MAJOR_VERSION"'\.[^"]*\)".*/\1/p' | head -1) + + if [ -z "$version" ]; then + error "Failed to resolve Node.js v$NODE_MAJOR_VERSION version" + exit 1 + fi + + NODE_VERSION="$version" + say "Node.js version: v$NODE_VERSION" +} + +# Get Node.js download URL +get_node_url() { + # Prefer .tar.xz if available, fall back to .tar.gz + echo "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-${PLATFORM}-${ARCH}.tar.xz" +} + +# Get CLI download URL from npm registry +get_cli_url() { + echo "https://registry.npmjs.org/@devcontainers/cli/-/cli-${CLI_VERSION}.tgz" +} + +# Install Node.js +install_node() { + node_dir="$INSTALL_PREFIX/node" + version_dir="$node_dir/v$NODE_VERSION" + + # Check if already installed + if [ -d "$version_dir" ] && [ -x "$version_dir/bin/node" ]; then + say "Node.js v$NODE_VERSION is already installed" + else + say "Downloading Node.js v$NODE_VERSION..." + + tmp_dir=$(mktemp -d) + trap 'rm -rf "$tmp_dir"' EXIT + + node_url=$(get_node_url) + tarball="$tmp_dir/node.tar.xz" + + if ! download "$node_url" "$tarball"; then + # Try .tar.gz if .tar.xz failed + node_url="${node_url%.xz}.gz" + tarball="$tmp_dir/node.tar.gz" + say "Trying .tar.gz format..." + download "$node_url" "$tarball" + fi + + say "Extracting Node.js..." + mkdir -p "$node_dir" + + # Extract to temp first, then move + extract_dir="$tmp_dir/extracted" + mkdir -p "$extract_dir" + + case "$tarball" in + *.xz) + # Try xz decompression + if command -v xz >/dev/null 2>&1; then + xz -d -c "$tarball" | tar -xf - -C "$extract_dir" + else + # Some tar implementations support -J for xz + tar -xJf "$tarball" -C "$extract_dir" 2>/dev/null || { + error "xz decompression not available. Please install xz-utils." + exit 1 + } + fi + ;; + *.gz) + tar -xzf "$tarball" -C "$extract_dir" + ;; + esac + + # Move extracted directory to version directory + mv "$extract_dir"/node-v*/* "$extract_dir"/ + rmdir "$extract_dir"/node-v* 2>/dev/null || true + mkdir -p "$version_dir" + mv "$extract_dir"/* "$version_dir"/ + + trap - EXIT + rm -rf "$tmp_dir" + fi + + # Update current symlink + say "Activating Node.js v$NODE_VERSION..." + ln -sfn "v$NODE_VERSION" "$node_dir/current" + + # Save metadata + mkdir -p "$INSTALL_PREFIX/.metadata" + echo "$NODE_VERSION" > "$INSTALL_PREFIX/.metadata/node-version" +} + +# Install CLI +install_cli() { + cli_dir="$INSTALL_PREFIX/cli" + version_dir="$cli_dir/$CLI_VERSION" + + # Check if already installed + if [ -d "$version_dir/package" ] && [ -f "$version_dir/package/devcontainer.js" ]; then + say "CLI v$CLI_VERSION is already installed" + else + say "Downloading CLI v$CLI_VERSION..." + + tmp_dir=$(mktemp -d) + trap 'rm -rf "$tmp_dir"' EXIT + + cli_url=$(get_cli_url) + tarball="$tmp_dir/cli.tgz" + + download "$cli_url" "$tarball" + + say "Extracting CLI..." + mkdir -p "$version_dir" + tar -xzf "$tarball" -C "$version_dir" + + trap - EXIT + rm -rf "$tmp_dir" + fi + + # Update current symlink + say "Activating CLI v$CLI_VERSION..." + ln -sfn "$CLI_VERSION" "$cli_dir/current" + + # Save metadata + mkdir -p "$INSTALL_PREFIX/.metadata" + echo "$CLI_VERSION" > "$INSTALL_PREFIX/.metadata/installed-version" +} + +# Create wrapper script +create_wrapper() { + bin_dir="$INSTALL_PREFIX/bin" + wrapper="$bin_dir/devcontainer" + + say "Creating wrapper script..." + mkdir -p "$bin_dir" + + cat > "$wrapper" << 'WRAPPER_EOF' +#!/bin/sh +# devcontainer CLI wrapper - generated by install.sh +# https://github.com/devcontainers/cli + +set -e + +# Resolve the installation directory +# Handle both direct execution and symlinked scenarios +if [ -L "$0" ]; then + # Follow symlink + SCRIPT_PATH="$(readlink "$0" 2>/dev/null || readlink -f "$0" 2>/dev/null || echo "$0")" +else + SCRIPT_PATH="$0" +fi + +# Get absolute path to script directory +SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)" +INSTALL_DIR="$(dirname "$SCRIPT_DIR")" + +# Paths to bundled components +NODE_BIN="$INSTALL_DIR/node/current/bin/node" +CLI_ENTRY="$INSTALL_DIR/cli/current/package/devcontainer.js" + +# Verify Node.js exists +if [ ! -x "$NODE_BIN" ]; then + echo "Error: Node.js not found at $NODE_BIN" >&2 + echo "Installation may be corrupted. Please reinstall:" >&2 + echo " curl -fsSL https://raw.githubusercontent.com/devcontainers/cli/main/scripts/install.sh | sh" >&2 + exit 1 +fi + +# Verify CLI exists +if [ ! -f "$CLI_ENTRY" ]; then + echo "Error: CLI not found at $CLI_ENTRY" >&2 + echo "Installation may be corrupted. Please reinstall:" >&2 + echo " curl -fsSL https://raw.githubusercontent.com/devcontainers/cli/main/scripts/install.sh | sh" >&2 + exit 1 +fi + +# Execute the CLI with bundled Node.js +exec "$NODE_BIN" "$CLI_ENTRY" "$@" +WRAPPER_EOF + + chmod +x "$wrapper" +} + +# Verify installation +verify_installation() { + say "Verifying installation..." + + node_bin="$INSTALL_PREFIX/node/current/bin/node" + cli_entry="$INSTALL_PREFIX/cli/current/package/devcontainer.js" + wrapper="$INSTALL_PREFIX/bin/devcontainer" + + if [ ! -x "$node_bin" ]; then + error "Node.js binary not found or not executable" + exit 1 + fi + + if [ ! -f "$cli_entry" ]; then + error "CLI entry point not found" + exit 1 + fi + + if [ ! -x "$wrapper" ]; then + error "Wrapper script not found or not executable" + exit 1 + fi + + # Try to get version + version=$("$wrapper" --version 2>/dev/null || true) + if [ -n "$version" ]; then + say "Installed: devcontainer $version" + else + warn "Could not verify CLI version, but files are in place" + fi +} + +# Check for existing installation and warn about conflicts +check_existing() { + # Check for existing devcontainer in PATH + existing=$(command -v devcontainer 2>/dev/null || true) + if [ -n "$existing" ]; then + # Check if it's our installation + case "$existing" in + "$INSTALL_PREFIX"*) + # It's our installation, that's fine + ;; + *) + warn "Found existing devcontainer at: $existing" + warn "After installation, ensure $INSTALL_PREFIX/bin is first in your PATH" + ;; + esac + fi + + # Check for existing installation directory + if [ -d "$INSTALL_PREFIX" ] && [ ! "$UPDATE_MODE" = true ]; then + if [ -f "$INSTALL_PREFIX/.metadata/installed-version" ]; then + current_version=$(cat "$INSTALL_PREFIX/.metadata/installed-version") + say "Found existing installation: v$current_version" + say "Use --update to update, or --uninstall to remove first" + fi + fi +} + +# Update existing installation +do_update() { + if [ ! -d "$INSTALL_PREFIX" ] || [ ! -f "$INSTALL_PREFIX/.metadata/installed-version" ]; then + error "No existing installation found at $INSTALL_PREFIX" + error "Run without --update to perform a fresh installation" + exit 1 + fi + + current_cli=$(cat "$INSTALL_PREFIX/.metadata/installed-version" 2>/dev/null || echo "unknown") + current_node=$(cat "$INSTALL_PREFIX/.metadata/node-version" 2>/dev/null || echo "unknown") + + say "Current installation:" + say " CLI: v$current_cli" + say " Node.js: v$current_node" + + # Resolve latest versions + CLI_VERSION="latest" + resolve_cli_version + resolve_node_version + + # Update components + if [ "$current_cli" = "$CLI_VERSION" ]; then + say "CLI is already up to date" + else + say "Updating CLI: v$current_cli -> v$CLI_VERSION" + install_cli + fi + + if [ "$current_node" = "$NODE_VERSION" ]; then + say "Node.js is already up to date" + else + say "Updating Node.js: v$current_node -> v$NODE_VERSION" + install_node + fi + + # Recreate wrapper in case it changed + create_wrapper + verify_installation +} + +# Uninstall +do_uninstall() { + if [ ! -d "$INSTALL_PREFIX" ]; then + say "Nothing to uninstall at $INSTALL_PREFIX" + exit 0 + fi + + say "Uninstalling from $INSTALL_PREFIX..." + rm -rf "$INSTALL_PREFIX" + say "Uninstallation complete" + say "" + say "Don't forget to remove the PATH entry from your shell profile:" + say " export PATH=\"$INSTALL_PREFIX/bin:\$PATH\"" +} + +# Print post-installation instructions +print_instructions() { + bin_path="$INSTALL_PREFIX/bin" + + echo "" + say "${BOLD}Installation complete!${RESET}" + echo "" + + # Check if already in PATH + case ":$PATH:" in + *":$bin_path:"*) + say "The installation directory is already in your PATH." + say "You can now use: devcontainer --help" + ;; + *) + say "Add the following to your shell profile to use devcontainer:" + echo "" + echo " export PATH=\"$bin_path:\$PATH\"" + echo "" + + # Detect shell and suggest profile file + shell_name=$(basename "${SHELL:-/bin/sh}") + case "$shell_name" in + bash) + if [ -f "$HOME/.bash_profile" ]; then + say "For bash, add to: ~/.bash_profile" + else + say "For bash, add to: ~/.bashrc" + fi + ;; + zsh) + say "For zsh, add to: ~/.zshrc" + ;; + fish) + say "For fish, run:" + echo " fish_add_path $bin_path" + ;; + *) + say "Add to your shell's profile file" + ;; + esac + echo "" + say "Then restart your shell or run:" + echo " export PATH=\"$bin_path:\$PATH\"" + ;; + esac + + echo "" + say "To update:" + echo " curl -fsSL https://raw.githubusercontent.com/devcontainers/cli/main/scripts/install.sh | sh -s -- --update" + say "To uninstall:" + echo " curl -fsSL https://raw.githubusercontent.com/devcontainers/cli/main/scripts/install.sh | sh -s -- --uninstall" + say "Or simply: rm -rf $INSTALL_PREFIX" +} + +# Main function +main() { + setup_colors + parse_args "$@" + + echo "" + say "${BOLD}@devcontainers/cli installer${RESET}" + echo "" + + # Handle uninstall + if [ "$UNINSTALL_MODE" = true ]; then + do_uninstall + exit 0 + fi + + detect_platform + say "Platform: $PLATFORM-$ARCH" + say "Install directory: $INSTALL_PREFIX" + + check_prerequisites + check_existing + + # Handle update + if [ "$UPDATE_MODE" = true ]; then + do_update + print_instructions + exit 0 + fi + + # Fresh installation + resolve_cli_version + resolve_node_version + + install_node + install_cli + create_wrapper + verify_installation + print_instructions +} + +main "$@" diff --git a/scripts/install.test.sh b/scripts/install.test.sh new file mode 100755 index 000000000..68c9fb51f --- /dev/null +++ b/scripts/install.test.sh @@ -0,0 +1,580 @@ +#!/bin/sh +# install.test.sh - Tests for install.sh +# +# Usage: +# sh scripts/install.test.sh +# +# Can be run in CI or locally. Uses a temp directory for all installs. +# Requires network access to download Node.js and the CLI package. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INSTALL_SCRIPT="$SCRIPT_DIR/install.sh" + +# ── Test framework ──────────────────────────────────────────────── + +TESTS_RUN=0 +TESTS_PASSED=0 +TESTS_FAILED=0 +FAILED_NAMES="" + +# Colors (disabled in non-tty / CI) +if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then + C_RED='\033[0;31m' + C_GREEN='\033[0;32m' + C_YELLOW='\033[0;33m' + C_BOLD='\033[1m' + C_RESET='\033[0m' +else + C_RED='' + C_GREEN='' + C_YELLOW='' + C_BOLD='' + C_RESET='' +fi + +pass() { + TESTS_PASSED=$((TESTS_PASSED + 1)) + printf '%b\n' " ${C_GREEN}✓${C_RESET} $1" +} + +fail() { + TESTS_FAILED=$((TESTS_FAILED + 1)) + FAILED_NAMES="$FAILED_NAMES\n - $1" + printf '%b\n' " ${C_RED}✗${C_RESET} $1" + if [ -n "${2:-}" ]; then + printf ' %s\n' "$2" + fi +} + +assert_eq() { + expected="$1" + actual="$2" + msg="$3" + TESTS_RUN=$((TESTS_RUN + 1)) + if [ "$expected" = "$actual" ]; then + pass "$msg" + else + fail "$msg" "expected: '$expected', got: '$actual'" + fi +} + +assert_contains() { + haystack="$1" + needle="$2" + msg="$3" + TESTS_RUN=$((TESTS_RUN + 1)) + case "$haystack" in + *"$needle"*) + pass "$msg" + ;; + *) + fail "$msg" "expected output to contain: '$needle'" + ;; + esac +} + +assert_file_exists() { + path="$1" + msg="$2" + TESTS_RUN=$((TESTS_RUN + 1)) + if [ -f "$path" ]; then + pass "$msg" + else + fail "$msg" "file not found: $path" + fi +} + +assert_dir_exists() { + path="$1" + msg="$2" + TESTS_RUN=$((TESTS_RUN + 1)) + if [ -d "$path" ]; then + pass "$msg" + else + fail "$msg" "directory not found: $path" + fi +} + +assert_executable() { + path="$1" + msg="$2" + TESTS_RUN=$((TESTS_RUN + 1)) + if [ -x "$path" ]; then + pass "$msg" + else + fail "$msg" "not executable: $path" + fi +} + +assert_symlink() { + path="$1" + msg="$2" + TESTS_RUN=$((TESTS_RUN + 1)) + if [ -L "$path" ]; then + pass "$msg" + else + fail "$msg" "not a symlink: $path" + fi +} + +assert_exit_code() { + expected="$1" + actual="$2" + msg="$3" + TESTS_RUN=$((TESTS_RUN + 1)) + if [ "$expected" = "$actual" ]; then + pass "$msg" + else + fail "$msg" "expected exit code $expected, got $actual" + fi +} + +# ── Setup / teardown ───────────────────────────────────────────── + +TEST_TMPDIR="" +setup() { + TEST_TMPDIR="$(mktemp -d)" +} + +teardown() { + if [ -n "$TEST_TMPDIR" ] && [ -d "$TEST_TMPDIR" ]; then + rm -rf "$TEST_TMPDIR" + fi +} + +# ── Tests: --help ───────────────────────────────────────────────── + +test_help_flag() { + printf '%b\n' "${C_BOLD}--help flag${C_RESET}" + setup + + output=$(sh "$INSTALL_SCRIPT" --help 2>&1) || true + + assert_contains "$output" "Install @devcontainers/cli" "--help shows description" + assert_contains "$output" "--prefix" "--help shows --prefix option" + assert_contains "$output" "--version" "--help shows --version option" + assert_contains "$output" "--node-version" "--help shows --node-version option" + assert_contains "$output" "--update" "--help shows --update option" + assert_contains "$output" "--uninstall" "--help shows --uninstall option" + assert_contains "$output" "DEVCONTAINERS_INSTALL_DIR" "--help shows env var" + + teardown +} + +test_help_short_flag() { + printf '%b\n' "${C_BOLD}-h flag${C_RESET}" + setup + + output=$(sh "$INSTALL_SCRIPT" -h 2>&1) || true + assert_contains "$output" "Install @devcontainers/cli" "-h shows help" + + teardown +} + +# ── Tests: argument parsing errors ──────────────────────────────── + +test_unknown_option() { + printf '%b\n' "${C_BOLD}Unknown option${C_RESET}" + setup + + output=$(sh "$INSTALL_SCRIPT" --bogus 2>&1) && rc=0 || rc=$? + assert_exit_code "1" "$rc" "exits with code 1 on unknown option" + assert_contains "$output" "Unknown option" "reports unknown option" + + teardown +} + +# ── Tests: --uninstall on missing dir ───────────────────────────── + +test_uninstall_no_dir() { + printf '%b\n' "${C_BOLD}Uninstall with no existing installation${C_RESET}" + setup + + prefix="$TEST_TMPDIR/nonexistent" + output=$(sh "$INSTALL_SCRIPT" --prefix "$prefix" --uninstall 2>&1) && rc=0 || rc=$? + assert_exit_code "0" "$rc" "exits 0 when nothing to uninstall" + assert_contains "$output" "Nothing to uninstall" "reports nothing to uninstall" + + teardown +} + +# ── Tests: --update on missing installation ─────────────────────── + +test_update_no_installation() { + printf '%b\n' "${C_BOLD}Update with no existing installation${C_RESET}" + setup + + prefix="$TEST_TMPDIR/empty" + mkdir -p "$prefix" + output=$(sh "$INSTALL_SCRIPT" --prefix "$prefix" --update 2>&1) && rc=0 || rc=$? + assert_exit_code "1" "$rc" "exits 1 when no installation found" + assert_contains "$output" "No existing installation" "reports missing installation" + + teardown +} + +# ── Tests: DEVCONTAINERS_INSTALL_DIR env var ────────────────────── + +test_env_var_prefix() { + printf '%b\n' "${C_BOLD}DEVCONTAINERS_INSTALL_DIR env var${C_RESET}" + setup + + # The env var should be reflected in the help output or the + # install run. We just test that the script picks it up by + # running --uninstall (lightweight) against a nonexistent path. + prefix="$TEST_TMPDIR/from-env" + output=$(DEVCONTAINERS_INSTALL_DIR="$prefix" sh "$INSTALL_SCRIPT" --uninstall 2>&1) && rc=0 || rc=$? + assert_exit_code "0" "$rc" "exits 0 with env-var prefix" + assert_contains "$output" "Nothing to uninstall" "uses env-var prefix path" + + teardown +} + +# ── Tests: --prefix flag overrides env var ──────────────────────── + +test_prefix_overrides_env() { + printf '%b\n' "${C_BOLD}--prefix overrides DEVCONTAINERS_INSTALL_DIR${C_RESET}" + setup + + env_dir="$TEST_TMPDIR/env-dir" + flag_dir="$TEST_TMPDIR/flag-dir" + output=$(DEVCONTAINERS_INSTALL_DIR="$env_dir" sh "$INSTALL_SCRIPT" --prefix "$flag_dir" --uninstall 2>&1) && rc=0 || rc=$? + assert_exit_code "0" "$rc" "exits 0" + # The output should reference flag_dir, not env_dir + assert_contains "$output" "Nothing to uninstall" "--prefix is used over env var" + + teardown +} + +# ── Tests: full install with a specific version ─────────────────── + +test_full_install() { + printf '%b\n' "${C_BOLD}Full install (specific CLI version)${C_RESET}" + setup + + prefix="$TEST_TMPDIR/devcontainers" + + # Use a known CLI version to make the test deterministic + cli_version="0.75.0" + + output=$(sh "$INSTALL_SCRIPT" --prefix "$prefix" --version "$cli_version" 2>&1) && rc=0 || rc=$? + assert_exit_code "0" "$rc" "install exits 0" + + # Directory structure + assert_dir_exists "$prefix/bin" "bin/ directory created" + assert_dir_exists "$prefix/node" "node/ directory created" + assert_dir_exists "$prefix/cli" "cli/ directory created" + assert_dir_exists "$prefix/.metadata" ".metadata/ directory created" + + # Wrapper script + assert_file_exists "$prefix/bin/devcontainer" "wrapper script exists" + assert_executable "$prefix/bin/devcontainer" "wrapper script is executable" + + # Symlinks + assert_symlink "$prefix/node/current" "node/current is a symlink" + assert_symlink "$prefix/cli/current" "cli/current is a symlink" + + # Node.js binary + assert_executable "$prefix/node/current/bin/node" "node binary is executable" + + # CLI entry point + assert_file_exists "$prefix/cli/current/package/devcontainer.js" "CLI entry point exists" + + # Metadata + assert_file_exists "$prefix/.metadata/installed-version" "CLI version metadata written" + assert_file_exists "$prefix/.metadata/node-version" "Node version metadata written" + + installed_version=$(cat "$prefix/.metadata/installed-version") + assert_eq "$cli_version" "$installed_version" "metadata records correct CLI version" + + # Wrapper executes successfully + version_output=$("$prefix/bin/devcontainer" --version 2>/dev/null) && wrc=0 || wrc=$? + assert_exit_code "0" "$wrc" "wrapper --version exits 0" + assert_contains "$version_output" "$cli_version" "wrapper reports installed version" + + teardown +} + +# ── Tests: idempotent install ───────────────────────────────────── + +test_idempotent_install() { + printf '%b\n' "${C_BOLD}Idempotent install (run twice)${C_RESET}" + setup + + prefix="$TEST_TMPDIR/devcontainers" + cli_version="0.75.0" + + # First install + sh "$INSTALL_SCRIPT" --prefix "$prefix" --version "$cli_version" >/dev/null 2>&1 + + # Second install – same version, should succeed and say "already installed" + output=$(sh "$INSTALL_SCRIPT" --prefix "$prefix" --version "$cli_version" 2>&1) && rc=0 || rc=$? + assert_exit_code "0" "$rc" "second install exits 0" + assert_contains "$output" "already installed" "detects existing Node.js or CLI" + + # Still works + version_output=$("$prefix/bin/devcontainer" --version 2>/dev/null) && wrc=0 || wrc=$? + assert_exit_code "0" "$wrc" "wrapper still works after second install" + + teardown +} + +# ── Tests: uninstall after install ──────────────────────────────── + +test_uninstall_after_install() { + printf '%b\n' "${C_BOLD}Uninstall removes installation${C_RESET}" + setup + + prefix="$TEST_TMPDIR/devcontainers" + cli_version="0.75.0" + + # Install + sh "$INSTALL_SCRIPT" --prefix "$prefix" --version "$cli_version" >/dev/null 2>&1 + + # Uninstall + output=$(sh "$INSTALL_SCRIPT" --prefix "$prefix" --uninstall 2>&1) && rc=0 || rc=$? + assert_exit_code "0" "$rc" "uninstall exits 0" + assert_contains "$output" "Uninstallation complete" "reports completion" + + # Directory should be gone + TESTS_RUN=$((TESTS_RUN + 1)) + if [ ! -d "$prefix" ]; then + pass "install directory removed" + else + fail "install directory removed" "directory still exists: $prefix" + fi + + teardown +} + +# ── Tests: update existing installation ─────────────────────────── + +test_update_existing() { + printf '%b\n' "${C_BOLD}Update existing installation${C_RESET}" + setup + + prefix="$TEST_TMPDIR/devcontainers" + + # Install an older version first + old_version="0.72.0" + sh "$INSTALL_SCRIPT" --prefix "$prefix" --version "$old_version" >/dev/null 2>&1 + + installed=$(cat "$prefix/.metadata/installed-version") + assert_eq "$old_version" "$installed" "initial version installed" + + # Update to a slightly newer specific version + new_version="0.75.0" + # Fake update by doing a fresh install with --version (--update resolves "latest") + sh "$INSTALL_SCRIPT" --prefix "$prefix" --version "$new_version" >/dev/null 2>&1 + + updated=$(cat "$prefix/.metadata/installed-version") + assert_eq "$new_version" "$updated" "version updated in metadata" + + # Wrapper reports new version + version_output=$("$prefix/bin/devcontainer" --version 2>/dev/null) && wrc=0 || wrc=$? + assert_exit_code "0" "$wrc" "wrapper works after version change" + assert_contains "$version_output" "$new_version" "wrapper reports new version" + + teardown +} + +# ── Tests: wrapper handles missing node gracefully ──────────────── + +test_wrapper_missing_node() { + printf '%b\n' "${C_BOLD}Wrapper error when Node.js missing${C_RESET}" + setup + + prefix="$TEST_TMPDIR/devcontainers" + cli_version="0.75.0" + + # Install + sh "$INSTALL_SCRIPT" --prefix "$prefix" --version "$cli_version" >/dev/null 2>&1 + + # Remove node binary + rm -rf "$prefix/node" + + output=$("$prefix/bin/devcontainer" --version 2>&1) && rc=0 || rc=$? + assert_exit_code "1" "$rc" "wrapper exits 1 when node missing" + assert_contains "$output" "Node.js not found" "wrapper reports missing Node.js" + + teardown +} + +# ── Tests: wrapper handles missing CLI gracefully ───────────────── + +test_wrapper_missing_cli() { + printf '%b\n' "${C_BOLD}Wrapper error when CLI missing${C_RESET}" + setup + + prefix="$TEST_TMPDIR/devcontainers" + cli_version="0.75.0" + + # Install + sh "$INSTALL_SCRIPT" --prefix "$prefix" --version "$cli_version" >/dev/null 2>&1 + + # Remove CLI + rm -rf "$prefix/cli" + + output=$("$prefix/bin/devcontainer" --version 2>&1) && rc=0 || rc=$? + assert_exit_code "1" "$rc" "wrapper exits 1 when CLI missing" + assert_contains "$output" "CLI not found" "wrapper reports missing CLI" + + teardown +} + +# ── Tests: install via symlinked wrapper ────────────────────────── + +test_wrapper_via_symlink() { + printf '%b\n' "${C_BOLD}Wrapper works when invoked via symlink${C_RESET}" + setup + + prefix="$TEST_TMPDIR/devcontainers" + cli_version="0.75.0" + + sh "$INSTALL_SCRIPT" --prefix "$prefix" --version "$cli_version" >/dev/null 2>&1 + + # Create a symlink to the wrapper in a different directory + link_dir="$TEST_TMPDIR/links" + mkdir -p "$link_dir" + ln -s "$prefix/bin/devcontainer" "$link_dir/devcontainer" + + version_output=$("$link_dir/devcontainer" --version 2>/dev/null) && wrc=0 || wrc=$? + assert_exit_code "0" "$wrc" "symlinked wrapper exits 0" + assert_contains "$version_output" "$cli_version" "symlinked wrapper reports version" + + teardown +} + +# ── Tests: install to path with spaces ──────────────────────────── + +test_path_with_spaces() { + printf '%b\n' "${C_BOLD}Install to path with spaces${C_RESET}" + setup + + prefix="$TEST_TMPDIR/my dev containers" + + cli_version="0.75.0" + output=$(sh "$INSTALL_SCRIPT" --prefix "$prefix" --version "$cli_version" 2>&1) && rc=0 || rc=$? + assert_exit_code "0" "$rc" "install to spaced path exits 0" + + assert_file_exists "$prefix/bin/devcontainer" "wrapper exists in spaced path" + + version_output=$("$prefix/bin/devcontainer" --version 2>/dev/null) && wrc=0 || wrc=$? + assert_exit_code "0" "$wrc" "wrapper works from spaced path" + assert_contains "$version_output" "$cli_version" "reports correct version from spaced path" + + teardown +} + +# ── Tests: non-writable prefix ──────────────────────────────────── + +test_non_writable_prefix() { + printf '%b\n' "${C_BOLD}Error on non-writable prefix${C_RESET}" + setup + + # Skip if running as root (root can write anywhere) + if [ "$(id -u)" = "0" ]; then + TESTS_RUN=$((TESTS_RUN + 1)) + pass "skipped (running as root)" + teardown + return + fi + + prefix="/usr/local/no-permission-test-devcontainers-$$" + output=$(sh "$INSTALL_SCRIPT" --prefix "$prefix" --version "0.75.0" 2>&1) && rc=0 || rc=$? + assert_exit_code "1" "$rc" "exits 1 for non-writable prefix" + assert_contains "$output" "No write permission" "reports permission error" + + teardown +} + +# ── Tests: --prefix= form (equals delimiter) ───────────────────── + +test_prefix_equals_form() { + printf '%b\n' "${C_BOLD}--prefix=DIR form${C_RESET}" + setup + + prefix="$TEST_TMPDIR/eq-form" + # Just verify parsing works – use uninstall for a lightweight check + output=$(sh "$INSTALL_SCRIPT" "--prefix=$prefix" --uninstall 2>&1) && rc=0 || rc=$? + assert_exit_code "0" "$rc" "--prefix=DIR is accepted" + assert_contains "$output" "Nothing to uninstall" "--prefix=DIR path is used" + + teardown +} + +test_version_equals_form() { + printf '%b\n' "${C_BOLD}--version=VER form${C_RESET}" + setup + + prefix="$TEST_TMPDIR/ver-eq" + output=$(sh "$INSTALL_SCRIPT" "--prefix=$prefix" "--version=0.75.0" 2>&1) && rc=0 || rc=$? + assert_exit_code "0" "$rc" "--version=VER install exits 0" + assert_contains "$output" "0.75.0" "version from --version=VER is used" + + teardown +} + +# ── Run all tests ───────────────────────────────────────────────── + +printf '%b\n' "" +printf '%b\n' "${C_BOLD}install.sh test suite${C_RESET}" +printf '%b\n' "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +printf '%b\n' "" + +# Fast tests (no network required) +test_help_flag +printf '\n' +test_help_short_flag +printf '\n' +test_unknown_option +printf '\n' +test_uninstall_no_dir +printf '\n' +test_update_no_installation +printf '\n' +test_env_var_prefix +printf '\n' +test_prefix_overrides_env +printf '\n' +test_prefix_equals_form +printf '\n' +test_non_writable_prefix +printf '\n' + +# Integration tests (require network, download Node.js + CLI) +printf '%b\n' "${C_YELLOW}Integration tests (requires network)${C_RESET}" +printf '\n' +test_full_install +printf '\n' +test_idempotent_install +printf '\n' +test_uninstall_after_install +printf '\n' +test_update_existing +printf '\n' +test_wrapper_missing_node +printf '\n' +test_wrapper_missing_cli +printf '\n' +test_wrapper_via_symlink +printf '\n' +test_path_with_spaces +printf '\n' +test_version_equals_form +printf '\n' + +# ── Summary ─────────────────────────────────────────────────────── + +printf '%b\n' "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +if [ "$TESTS_FAILED" -eq 0 ]; then + printf '%b\n' "${C_GREEN}${C_BOLD}All $TESTS_RUN tests passed${C_RESET}" +else + printf '%b\n' "${C_RED}${C_BOLD}$TESTS_FAILED of $TESTS_RUN tests failed${C_RESET}" + printf '%b\n' "$FAILED_NAMES" +fi +printf '%b\n' "" + +exit "$TESTS_FAILED" From c6332d238281ca95c270d322fc559b9b6bae6be8 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Feb 2026 15:00:31 +0100 Subject: [PATCH 43/88] 0.83.0 --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b4f88234..598cb3e03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ Notable changes. +## February 2026 + +### [0.83.0] +- Add install script. (https://github.com/devcontainers/cli/pull/1142) +- Remove request body limit. (https://github.com/devcontainers/cli/pull/1141) +- Add BUILDKIT_INLINE_CACHE for container Feature path. (https://github.com/devcontainers/cli/pull/1135) + ## January 2026 ### [0.82.0] diff --git a/package.json b/package.json index 97e73fa11..81a4d54ba 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.82.0", + "version": "0.83.0", "bin": { "devcontainer": "devcontainer.js" }, From 707221bf01c0dbfc94bb5d59e4c6f7ec4f40058c Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Feb 2026 19:16:39 +0100 Subject: [PATCH 44/88] Consistent naming --- scripts/install.sh | 36 ++++++++++++++++++------------------ scripts/install.test.sh | 6 +++--- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/scripts/install.sh b/scripts/install.sh index ab048dbc9..e44a36712 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -7,7 +7,7 @@ # # Options: # --prefix DIR Installation directory (default: ~/.devcontainers) -# --version VER CLI version to install (default: latest) +# --version VER Dev Containers CLI version to install (default: latest) # --node-version VER Node.js major version (default: 20) # --update Update existing installation to latest versions # --uninstall Remove the installation @@ -59,7 +59,7 @@ error() { # Print usage information usage() { cat << 'EOF' -Install @devcontainers/cli with bundled Node.js +Install the Dev Containers CLI with bundled Node.js Usage: curl -fsSL https://raw.githubusercontent.com/devcontainers/cli/main/scripts/install.sh | sh @@ -67,7 +67,7 @@ Usage: Options: --prefix DIR Installation directory (default: ~/.devcontainers) - --version VER CLI version to install (default: latest) + --version VER Dev Containers CLI version to install (default: latest) --node-version VER Node.js major version (default: 20) --update Update existing installation to latest versions --uninstall Remove the installation @@ -259,16 +259,16 @@ fetch() { # Resolve "latest" CLI version from npm registry resolve_cli_version() { if [ "$CLI_VERSION" = "latest" ]; then - say "Resolving latest CLI version..." + say "Resolving latest Dev Containers CLI version..." version=$(fetch "https://registry.npmjs.org/@devcontainers/cli/latest" | \ sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) if [ -z "$version" ]; then - error "Failed to resolve latest CLI version from npm registry" + error "Failed to resolve latest Dev Containers CLI version from npm registry" exit 1 fi CLI_VERSION="$version" fi - say "CLI version: $CLI_VERSION" + say "Dev Containers CLI version: $CLI_VERSION" } # Resolve full Node.js version from major version @@ -376,9 +376,9 @@ install_cli() { # Check if already installed if [ -d "$version_dir/package" ] && [ -f "$version_dir/package/devcontainer.js" ]; then - say "CLI v$CLI_VERSION is already installed" + say "Dev Containers CLI v$CLI_VERSION is already installed" else - say "Downloading CLI v$CLI_VERSION..." + say "Downloading Dev Containers CLI v$CLI_VERSION..." tmp_dir=$(mktemp -d) trap 'rm -rf "$tmp_dir"' EXIT @@ -388,7 +388,7 @@ install_cli() { download "$cli_url" "$tarball" - say "Extracting CLI..." + say "Extracting Dev Containers CLI..." mkdir -p "$version_dir" tar -xzf "$tarball" -C "$version_dir" @@ -397,7 +397,7 @@ install_cli() { fi # Update current symlink - say "Activating CLI v$CLI_VERSION..." + say "Activating Dev Containers CLI v$CLI_VERSION..." ln -sfn "$CLI_VERSION" "$cli_dir/current" # Save metadata @@ -415,7 +415,7 @@ create_wrapper() { cat > "$wrapper" << 'WRAPPER_EOF' #!/bin/sh -# devcontainer CLI wrapper - generated by install.sh +# Dev Containers CLI wrapper - generated by install.sh # https://github.com/devcontainers/cli set -e @@ -447,7 +447,7 @@ fi # Verify CLI exists if [ ! -f "$CLI_ENTRY" ]; then - echo "Error: CLI not found at $CLI_ENTRY" >&2 + echo "Error: Dev Containers CLI not found at $CLI_ENTRY" >&2 echo "Installation may be corrupted. Please reinstall:" >&2 echo " curl -fsSL https://raw.githubusercontent.com/devcontainers/cli/main/scripts/install.sh | sh" >&2 exit 1 @@ -474,7 +474,7 @@ verify_installation() { fi if [ ! -f "$cli_entry" ]; then - error "CLI entry point not found" + error "Dev Containers CLI entry point not found" exit 1 fi @@ -488,7 +488,7 @@ verify_installation() { if [ -n "$version" ]; then say "Installed: devcontainer $version" else - warn "Could not verify CLI version, but files are in place" + warn "Could not verify Dev Containers CLI version, but files are in place" fi } @@ -531,7 +531,7 @@ do_update() { current_node=$(cat "$INSTALL_PREFIX/.metadata/node-version" 2>/dev/null || echo "unknown") say "Current installation:" - say " CLI: v$current_cli" + say " Dev Containers CLI: v$current_cli" say " Node.js: v$current_node" # Resolve latest versions @@ -541,9 +541,9 @@ do_update() { # Update components if [ "$current_cli" = "$CLI_VERSION" ]; then - say "CLI is already up to date" + say "Dev Containers CLI is already up to date" else - say "Updating CLI: v$current_cli -> v$CLI_VERSION" + say "Updating Dev Containers CLI: v$current_cli -> v$CLI_VERSION" install_cli fi @@ -635,7 +635,7 @@ main() { parse_args "$@" echo "" - say "${BOLD}@devcontainers/cli installer${RESET}" + say "${BOLD}Dev Containers CLI installer${RESET}" echo "" # Handle uninstall diff --git a/scripts/install.test.sh b/scripts/install.test.sh index 68c9fb51f..2a73b883e 100755 --- a/scripts/install.test.sh +++ b/scripts/install.test.sh @@ -152,7 +152,7 @@ test_help_flag() { output=$(sh "$INSTALL_SCRIPT" --help 2>&1) || true - assert_contains "$output" "Install @devcontainers/cli" "--help shows description" + assert_contains "$output" "Install the Dev Containers CLI" "--help shows description" assert_contains "$output" "--prefix" "--help shows --prefix option" assert_contains "$output" "--version" "--help shows --version option" assert_contains "$output" "--node-version" "--help shows --node-version option" @@ -168,7 +168,7 @@ test_help_short_flag() { setup output=$(sh "$INSTALL_SCRIPT" -h 2>&1) || true - assert_contains "$output" "Install @devcontainers/cli" "-h shows help" + assert_contains "$output" "Install the Dev Containers CLI" "-h shows help" teardown } @@ -419,7 +419,7 @@ test_wrapper_missing_cli() { output=$("$prefix/bin/devcontainer" --version 2>&1) && rc=0 || rc=$? assert_exit_code "1" "$rc" "wrapper exits 1 when CLI missing" - assert_contains "$output" "CLI not found" "wrapper reports missing CLI" + assert_contains "$output" "Dev Containers CLI not found" "wrapper reports missing Dev Containers CLI" teardown } From 3f45f2186981bc3f16de3402f823632beacfb11e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 18:02:49 +0000 Subject: [PATCH 45/88] Bump tar from 7.5.6 to 7.5.7 Bumps [tar](https://github.com/isaacs/node-tar) from 7.5.6 to 7.5.7. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v7.5.6...v7.5.7) --- updated-dependencies: - dependency-name: tar dependency-version: 7.5.7 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c6d46d2b9..4d74689e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3740,9 +3740,9 @@ table@^5.2.3: string-width "^3.0.0" tar@^7.5.4: - version "7.5.6" - resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.6.tgz#2db7a210748a82f0a89cc31527b90d3a24984fb7" - integrity sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA== + version "7.5.7" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.7.tgz#adf99774008ba1c89819f15dbd6019c630539405" + integrity sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ== dependencies: "@isaacs/fs-minipass" "^4.0.0" chownr "^3.0.0" From 1a55f1ab999a2ea2273405dbb0367523ab051f34 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 16 Feb 2026 09:43:55 +0100 Subject: [PATCH 46/88] 0.83.1 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 598cb3e03..605480c58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Notable changes. ## February 2026 +### [0.83.1] +- Bump tar from 7.5.6 to 7.5.7. (https://github.com/devcontainers/cli/pull/1140) + ### [0.83.0] - Add install script. (https://github.com/devcontainers/cli/pull/1142) - Remove request body limit. (https://github.com/devcontainers/cli/pull/1141) diff --git a/package.json b/package.json index 81a4d54ba..ae348ed35 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.83.0", + "version": "0.83.1", "bin": { "devcontainer": "devcontainer.js" }, From f3637b9ef47916ae064d6cbb39d8d6acec63a7c2 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Tue, 17 Feb 2026 10:20:48 +0100 Subject: [PATCH 47/88] Disable containerd image store --- .github/workflows/dev-containers.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/dev-containers.yml b/.github/workflows/dev-containers.yml index d1f43995e..14f44861d 100644 --- a/.github/workflows/dev-containers.yml +++ b/.github/workflows/dev-containers.yml @@ -69,6 +69,20 @@ jobs: node-version: '18.x' registry-url: 'https://npm.pkg.github.com' scope: '@microsoft' + - name: Disable containerd image store + run: | + # Workaround for https://github.com/moby/moby/issues/52050 + DAEMON_JSON="/etc/docker/daemon.json" + if [ -f "$DAEMON_JSON" ]; then + sudo jq '. + {"features": {"containerd-snapshotter": false}}' "$DAEMON_JSON" \ + | sudo tee "${DAEMON_JSON}.tmp" > /dev/null + sudo mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON" + else + echo '{"features": {"containerd-snapshotter": false}}' \ + | sudo tee "$DAEMON_JSON" > /dev/null + fi + cat "$DAEMON_JSON" + sudo systemctl restart docker - name: Tools Info run: | docker info From c145cfa3400015119096e7e1487d96c3c3d4ec70 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Tue, 17 Feb 2026 10:20:56 +0100 Subject: [PATCH 48/88] Pin buildx version --- .github/workflows/test-docker-v20.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-docker-v20.yml b/.github/workflows/test-docker-v20.yml index e38817420..8e8c64dfc 100644 --- a/.github/workflows/test-docker-v20.yml +++ b/.github/workflows/test-docker-v20.yml @@ -33,7 +33,11 @@ jobs: "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update - sudo apt-get install -y docker-ce=5:20.10.* docker-ce-cli=5:20.10.* containerd.io + # Pin buildx < 0.31.0 to avoid API version 1.52 incompatibility with Docker 20.10 (max API 1.41) + # See https://github.com/docker/buildx/issues/3654 + BUILDX_VERSION=$(apt-cache madison docker-buildx-plugin | awk '{print $3}' | grep -v '^0\.3[1-9]\.' | head -1) + echo "Installing docker-buildx-plugin=$BUILDX_VERSION" + sudo apt-get install -y docker-ce=5:20.10.* docker-ce-cli=5:20.10.* containerd.io docker-buildx-plugin="$BUILDX_VERSION" docker-compose-plugin sudo systemctl restart docker - name: Verify Docker version, Install and Test From 19397f9d9b2ecfaf839c4ae8db3d101565097fb9 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Feb 2026 15:25:34 +0100 Subject: [PATCH 49/88] Add permissions to workflows --- .github/workflows/dev-containers.yml | 4 ++++ .github/workflows/publish-dev-containers.yml | 4 ++++ .github/workflows/test-docker-v20.yml | 3 +++ .github/workflows/test-docker-v29.yml | 3 +++ .github/workflows/test-plan-item-validator.yml | 4 ++++ .github/workflows/test-windows.yml | 4 ++++ 6 files changed, 22 insertions(+) diff --git a/.github/workflows/dev-containers.yml b/.github/workflows/dev-containers.yml index 14f44861d..21016f744 100644 --- a/.github/workflows/dev-containers.yml +++ b/.github/workflows/dev-containers.yml @@ -8,6 +8,10 @@ on: branches: - '**' +permissions: + contents: read + packages: read + jobs: cli: name: CLI diff --git a/.github/workflows/publish-dev-containers.yml b/.github/workflows/publish-dev-containers.yml index 3dcea343b..2b59b331e 100644 --- a/.github/workflows/publish-dev-containers.yml +++ b/.github/workflows/publish-dev-containers.yml @@ -5,6 +5,10 @@ on: tags: - 'v*' +permissions: + contents: read + actions: read + jobs: main: runs-on: ubuntu-latest diff --git a/.github/workflows/test-docker-v20.yml b/.github/workflows/test-docker-v20.yml index 8e8c64dfc..6b0efe84e 100644 --- a/.github/workflows/test-docker-v20.yml +++ b/.github/workflows/test-docker-v20.yml @@ -6,6 +6,9 @@ on: pull_request: branches: ['main'] +permissions: + contents: read + jobs: test-docker-v20: name: Docker v20.10 Compatibility diff --git a/.github/workflows/test-docker-v29.yml b/.github/workflows/test-docker-v29.yml index 1c5ebad17..244a9658f 100644 --- a/.github/workflows/test-docker-v29.yml +++ b/.github/workflows/test-docker-v29.yml @@ -6,6 +6,9 @@ on: pull_request: branches: ['main'] +permissions: + contents: read + jobs: test-docker-v29: name: Docker v29.0.0 Compatibility diff --git a/.github/workflows/test-plan-item-validator.yml b/.github/workflows/test-plan-item-validator.yml index c1d218aba..9c9739b6f 100644 --- a/.github/workflows/test-plan-item-validator.yml +++ b/.github/workflows/test-plan-item-validator.yml @@ -3,6 +3,10 @@ on: issues: types: [edited] +permissions: + contents: read + issues: write + jobs: main: runs-on: ubuntu-latest diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 111110448..63bff3620 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -11,6 +11,10 @@ on: pull_request: branches: - '**' + +permissions: + contents: read + jobs: tests-matrix: name: Tests Matrix (Windows) From fd14a8249092ed8ffb726d25fddf3d6fb07b1485 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Tue, 17 Feb 2026 22:48:55 +0100 Subject: [PATCH 50/88] Improved logging for image inspect errors --- CHANGELOG.md | 3 +++ package.json | 2 +- src/spec-node/utils.ts | 42 ++++++++++++++++++++++++++++-------------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 605480c58..39e580671 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Notable changes. ## February 2026 +### [0.83.2] +- Improved logging for image inspect errors. (https://github.com/devcontainers/cli/pull/1152) + ### [0.83.1] - Bump tar from 7.5.6 to 7.5.7. (https://github.com/devcontainers/cli/pull/1140) diff --git a/package.json b/package.json index ae348ed35..f787ee267 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.83.1", + "version": "0.83.2", "bin": { "devcontainer": "devcontainer.js" }, diff --git a/src/spec-node/utils.ts b/src/spec-node/utils.ts index 5c0fb374c..bdf5289f0 100644 --- a/src/spec-node/utils.ts +++ b/src/spec-node/utils.ts @@ -243,28 +243,42 @@ export function isBuildKitImagePolicyError(err: any): boolean { export async function inspectDockerImage(params: DockerResolverParameters | DockerCLIParameters, imageName: string, pullImageOnError: boolean) { try { return await inspectImage(params, imageName); - } catch (err) { + } catch (inspectErr) { + const output = 'cliHost' in params ? params.output : params.common.output; if (!pullImageOnError) { - throw err; + logErrorStdoutStderr(inspectErr, output); + throw inspectErr; } - const output = 'cliHost' in params ? params.output : params.common.output; try { return await inspectImageInRegistry(output, params.platformInfo, imageName); - } catch (err2) { - output.write(`Error fetching image details: ${err2?.message}`); + } catch (inspectErr2) { + output.write(`Error fetching image details: ${inspectErr2?.message}`, LogLevel.Info); } try { await retry(async () => dockerPtyCLI(params, 'pull', imageName), { maxRetries: 5, retryIntervalMilliseconds: 1000, output }); - } catch (_err) { - if (err.stdout) { - output.write(err.stdout.toString()); - } - if (err.stderr) { - output.write(toErrorText(err.stderr.toString())); - } - throw err; + } catch (pullErr) { + logErrorStdoutStderr(inspectErr, output); + logErrorStdoutStderr(pullErr, output); + throw pullErr; + } + try { + return await inspectImage(params, imageName); + } catch (inspectErr3) { + logErrorStdoutStderr(inspectErr3, output); + throw inspectErr3; } - return inspectImage(params, imageName); + } +} + +function logErrorStdoutStderr(err: any, output: Log) { + if (err?.message) { + output.write(err.message, LogLevel.Error); + } + if (err?.stdout) { + output.write(err.stdout.toString(), LogLevel.Error); + } + if (err?.stderr) { + output.write(toErrorText(err.stderr.toString()), LogLevel.Error); } } From f8beb3ff55ef2d8f6e634b494570d211afbb1a50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 07:34:33 +0000 Subject: [PATCH 51/88] Bump tar from 7.5.7 to 7.5.8 Bumps [tar](https://github.com/isaacs/node-tar) from 7.5.7 to 7.5.8. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v7.5.7...v7.5.8) --- updated-dependencies: - dependency-name: tar dependency-version: 7.5.8 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4d74689e5..8c3dc3652 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3740,9 +3740,9 @@ table@^5.2.3: string-width "^3.0.0" tar@^7.5.4: - version "7.5.7" - resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.7.tgz#adf99774008ba1c89819f15dbd6019c630539405" - integrity sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ== + version "7.5.8" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.8.tgz#6bbdfdb487914803d401bab6a2abb100aaa253d5" + integrity sha512-SYkBtK99u0yXa+IWL0JRzzcl7RxNpvX/U08Z+8DKnysfno7M+uExnTZH8K+VGgShf2qFPKtbNr9QBl8n7WBP6Q== dependencies: "@isaacs/fs-minipass" "^4.0.0" chownr "^3.0.0" From 12c771de16ad4c53bf1a220fb0afd06a8bfcdec0 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 23 Feb 2026 08:55:57 +0100 Subject: [PATCH 52/88] 0.83.3 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39e580671..86c933e0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Notable changes. ## February 2026 +### [0.83.3] +- Bump tar from 7.5.7 to 7.5.8. (https://github.com/devcontainers/cli/pull/1160) + ### [0.83.2] - Improved logging for image inspect errors. (https://github.com/devcontainers/cli/pull/1152) diff --git a/package.json b/package.json index f787ee267..960c755c6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.83.2", + "version": "0.83.3", "bin": { "devcontainer": "devcontainer.js" }, From d4b7623b364b6c274ed82c7b0ca9776189e51974 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Mar 2026 11:48:35 +0100 Subject: [PATCH 53/88] Node.js 18 EOL --- .github/dependabot.yml | 2 +- .github/workflows/dev-containers.yml | 6 +++--- .github/workflows/publish-dev-containers.yml | 2 +- .github/workflows/test-docker-v20.yml | 2 +- .github/workflows/test-docker-v29.yml | 2 +- .github/workflows/test-windows.yml | 2 +- package.json | 4 ++-- yarn.lock | 20 ++++++++++---------- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bfecf0a18..22a757ec5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -22,7 +22,7 @@ updates: - dependency-name: "@types/chai" update-types: ["version-update:semver-major"] # chai 4 to avoid esm - dependency-name: "@types/node" - update-types: ["version-update:semver-major"] # Keep Node 18 compatibility + update-types: ["version-update:semver-major"] # Keep Node 20 compatibility - dependency-name: "@types/tar" update-types: ["version-update:semver-major"] # tar 6 for source compatibility - dependency-name: "chai" diff --git a/.github/workflows/dev-containers.yml b/.github/workflows/dev-containers.yml index 21016f744..aadadf1d5 100644 --- a/.github/workflows/dev-containers.yml +++ b/.github/workflows/dev-containers.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v6 - uses: actions/setup-node@v5 with: - node-version: '18.x' + node-version: '20.x' registry-url: 'https://npm.pkg.github.com' scope: '@microsoft' - name: Install Dependencies @@ -70,7 +70,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v5 with: - node-version: '18.x' + node-version: '20.x' registry-url: 'https://npm.pkg.github.com' scope: '@microsoft' - name: Disable containerd image store @@ -117,7 +117,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v5 with: - node-version: '18.x' + node-version: '20.x' registry-url: 'https://npm.pkg.github.com' scope: '@microsoft' - name: Install Dependencies diff --git a/.github/workflows/publish-dev-containers.yml b/.github/workflows/publish-dev-containers.yml index 2b59b331e..f4ac97d0c 100644 --- a/.github/workflows/publish-dev-containers.yml +++ b/.github/workflows/publish-dev-containers.yml @@ -18,7 +18,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v5 with: - node-version: '18.x' + node-version: '20.x' registry-url: 'https://registry.npmjs.org' scope: '@devcontainers' - name: Verify Versions diff --git a/.github/workflows/test-docker-v20.yml b/.github/workflows/test-docker-v20.yml index 6b0efe84e..8c3aa21e8 100644 --- a/.github/workflows/test-docker-v20.yml +++ b/.github/workflows/test-docker-v20.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/setup-node@v5 with: - node-version: '18.x' + node-version: '20.x' - name: Install Docker v20.10 run: | diff --git a/.github/workflows/test-docker-v29.yml b/.github/workflows/test-docker-v29.yml index 244a9658f..e3db37304 100644 --- a/.github/workflows/test-docker-v29.yml +++ b/.github/workflows/test-docker-v29.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/setup-node@v5 with: - node-version: '18.x' + node-version: '20.x' - name: Install Docker v29.0.0 run: | diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 63bff3620..3f4d323f2 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -54,7 +54,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v5 with: - node-version: '18.x' + node-version: '20.x' registry-url: 'https://npm.pkg.github.com' scope: '@microsoft' - name: Install Dependencies diff --git a/package.json b/package.json index 960c755c6..b4f8b20df 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ }, "license": "MIT", "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": ">=20.0.0" }, "scripts": { "compile": "npm-run-all clean-dist compile-dev", @@ -61,7 +61,7 @@ "@types/js-yaml": "^4.0.9", "@types/mocha": "^10.0.10", "@types/ncp": "^2.0.8", - "@types/node": "^18.19.127", + "@types/node": "^20.19.37", "@types/pull-stream": "^3.6.7", "@types/recursive-readdir": "^2.2.4", "@types/semver": "^7.5.8", diff --git a/yarn.lock b/yarn.lock index 8c3dc3652..78b784602 100644 --- a/yarn.lock +++ b/yarn.lock @@ -365,12 +365,12 @@ dependencies: undici-types "~6.20.0" -"@types/node@^18.19.127": - version "18.19.130" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.130.tgz#da4c6324793a79defb7a62cba3947ec5add00d59" - integrity sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg== +"@types/node@^20.19.37": + version "20.19.37" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.37.tgz#b4fb4033408dd97becce63ec932c9ec57a9e2919" + integrity sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw== dependencies: - undici-types "~5.26.4" + undici-types "~6.21.0" "@types/pull-stream@^3.6.7": version "3.6.7" @@ -3964,16 +3964,16 @@ unc-path-regex@^0.1.2: resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - undici-types@~6.20.0: version "6.20.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + untildify@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" From bdb0b674fbe527182f94855c85dac394195076e4 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Mar 2026 11:56:38 +0100 Subject: [PATCH 54/88] Update ESLint --- .eslintignore | 1 - .eslintrc.js | 64 - .github/dependabot.yml | 2 - .../eslint/code-no-unexternalized-strings.js | 111 -- .../eslint/code-no-unexternalized-strings.ts | 126 -- build/eslint/code-no-unused-expressions.js | 141 --- build/hygiene.js | 50 +- eslint.config.js | 61 + package.json | 8 +- yarn.lock | 1066 ++++------------- 10 files changed, 301 insertions(+), 1329 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.js delete mode 100644 build/eslint/code-no-unexternalized-strings.js delete mode 100644 build/eslint/code-no-unexternalized-strings.ts delete mode 100644 build/eslint/code-no-unused-expressions.js create mode 100644 eslint.config.js diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 4197b94e5..000000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -**/node_modules/** \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 52b265e31..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,64 +0,0 @@ -module.exports = { - 'env': { - 'browser': true, - 'node': true - }, - 'parser': '@typescript-eslint/parser', - 'parserOptions': { - 'sourceType': 'module' - }, - 'plugins': [ - '@typescript-eslint', - '@stylistic' - ], - 'rules': { - // '@typescript-eslint/class-name-casing': 'warn', https://github.com/typescript-eslint/typescript-eslint/issues/2077 - '@stylistic/member-delimiter-style': [ - 'warn', - { - 'multiline': { - 'delimiter': 'semi', - 'requireLast': true - }, - 'singleline': { - 'delimiter': 'semi', - 'requireLast': false - } - } - ], - 'semi': [ - 'warn', - 'always' - ], - 'constructor-super': 'warn', - 'curly': 'warn', - 'eqeqeq': [ - 'warn', - 'always' - ], - 'no-async-promise-executor': 'warn', - 'no-buffer-constructor': 'warn', - 'no-caller': 'warn', - 'no-debugger': 'warn', - 'no-duplicate-case': 'warn', - 'no-duplicate-imports': 'warn', - 'no-eval': 'warn', - 'no-extra-semi': 'warn', - 'no-new-wrappers': 'warn', - 'no-redeclare': 'off', - 'no-sparse-arrays': 'warn', - 'no-throw-literal': 'warn', - 'no-unsafe-finally': 'warn', - 'no-unused-labels': 'warn', - '@typescript-eslint/no-redeclare': 'warn', - 'code-no-unexternalized-strings': 'warn', - 'no-throw-literal': 'warn', - 'no-var': 'warn', - 'code-no-unused-expressions': [ - 'warn', - { - 'allowTernary': true - } - ], - } -}; diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 22a757ec5..404e45827 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -27,8 +27,6 @@ updates: update-types: ["version-update:semver-major"] # tar 6 for source compatibility - dependency-name: "chai" update-types: ["version-update:semver-major"] # chai 4 to avoid esm - - dependency-name: "eslint" - update-types: ["version-update:semver-major"] # eslint 8 for `--rulesdir` - dependency-name: "rimraf" update-types: ["version-update:semver-major"] # rimraf 5 for Node 18 compatibility - dependency-name: "tar" diff --git a/build/eslint/code-no-unexternalized-strings.js b/build/eslint/code-no-unexternalized-strings.js deleted file mode 100644 index 28fce5b9a..000000000 --- a/build/eslint/code-no-unexternalized-strings.js +++ /dev/null @@ -1,111 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -var _a; -const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); -function isStringLiteral(node) { - return !!node && node.type === experimental_utils_1.AST_NODE_TYPES.Literal && typeof node.value === 'string'; -} -function isDoubleQuoted(node) { - return node.raw[0] === '"' && node.raw[node.raw.length - 1] === '"'; -} -module.exports = new (_a = class NoUnexternalizedStrings { - constructor() { - this.meta = { - messages: { - doubleQuoted: 'Only use double-quoted strings for externalized strings.', - badKey: 'The key \'{{key}}\' doesn\'t conform to a valid localize identifier.', - duplicateKey: 'Duplicate key \'{{key}}\' with different message value.', - badMessage: 'Message argument to \'{{message}}\' must be a string literal.' - } - }; - } - create(context) { - const externalizedStringLiterals = new Map(); - const doubleQuotedStringLiterals = new Set(); - function collectDoubleQuotedStrings(node) { - if (isStringLiteral(node) && isDoubleQuoted(node)) { - doubleQuotedStringLiterals.add(node); - } - } - function visitLocalizeCall(node) { - // localize(key, message) - const [keyNode, messageNode] = node.arguments; - // (1) - // extract key so that it can be checked later - let key; - if (isStringLiteral(keyNode)) { - doubleQuotedStringLiterals.delete(keyNode); //todo@joh reconsider - key = keyNode.value; - } - else if (keyNode.type === experimental_utils_1.AST_NODE_TYPES.ObjectExpression) { - for (let property of keyNode.properties) { - if (property.type === experimental_utils_1.AST_NODE_TYPES.Property && !property.computed) { - if (property.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier && property.key.name === 'key') { - if (isStringLiteral(property.value)) { - doubleQuotedStringLiterals.delete(property.value); //todo@joh reconsider - key = property.value.value; - break; - } - } - } - } - } - if (typeof key === 'string') { - let array = externalizedStringLiterals.get(key); - if (!array) { - array = []; - externalizedStringLiterals.set(key, array); - } - array.push({ call: node, message: messageNode }); - } - // (2) - // remove message-argument from doubleQuoted list and make - // sure it is a string-literal - doubleQuotedStringLiterals.delete(messageNode); - if (!isStringLiteral(messageNode)) { - context.report({ - loc: messageNode.loc, - messageId: 'badMessage', - data: { message: context.getSourceCode().getText(node) } - }); - } - } - function reportBadStringsAndBadKeys() { - // (1) - // report all strings that are in double quotes - for (const node of doubleQuotedStringLiterals) { - context.report({ loc: node.loc, messageId: 'doubleQuoted' }); - } - for (const [key, values] of externalizedStringLiterals) { - // (2) - // report all invalid NLS keys - if (!key.match(NoUnexternalizedStrings._rNlsKeys)) { - for (let value of values) { - context.report({ loc: value.call.loc, messageId: 'badKey', data: { key } }); - } - } - // (2) - // report all invalid duplicates (same key, different message) - if (values.length > 1) { - for (let i = 1; i < values.length; i++) { - if (context.getSourceCode().getText(values[i - 1].message) !== context.getSourceCode().getText(values[i].message)) { - context.report({ loc: values[i].call.loc, messageId: 'duplicateKey', data: { key } }); - } - } - } - } - } - return { - ['Literal']: (node) => collectDoubleQuotedStrings(node), - ['ExpressionStatement[directive] Literal:exit']: (node) => doubleQuotedStringLiterals.delete(node), - ['CallExpression[callee.type="MemberExpression"][callee.object.name="nls"][callee.property.name="localize"]:exit']: (node) => visitLocalizeCall(node), - ['CallExpression[callee.name="localize"][arguments.length>=2]:exit']: (node) => visitLocalizeCall(node), - ['Program:exit']: reportBadStringsAndBadKeys, - }; - } - }, - _a._rNlsKeys = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/, - _a); diff --git a/build/eslint/code-no-unexternalized-strings.ts b/build/eslint/code-no-unexternalized-strings.ts deleted file mode 100644 index 29db884cd..000000000 --- a/build/eslint/code-no-unexternalized-strings.ts +++ /dev/null @@ -1,126 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as eslint from 'eslint'; -import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; - -function isStringLiteral(node: TSESTree.Node | null | undefined): node is TSESTree.StringLiteral { - return !!node && node.type === AST_NODE_TYPES.Literal && typeof node.value === 'string'; -} - -function isDoubleQuoted(node: TSESTree.StringLiteral): boolean { - return node.raw[0] === '"' && node.raw[node.raw.length - 1] === '"'; -} - -export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { - - private static _rNlsKeys = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/; - - readonly meta: eslint.Rule.RuleMetaData = { - messages: { - doubleQuoted: 'Only use double-quoted strings for externalized strings.', - badKey: 'The key \'{{key}}\' doesn\'t conform to a valid localize identifier.', - duplicateKey: 'Duplicate key \'{{key}}\' with different message value.', - badMessage: 'Message argument to \'{{message}}\' must be a string literal.' - } - }; - - create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - - const externalizedStringLiterals = new Map(); - const doubleQuotedStringLiterals = new Set(); - - function collectDoubleQuotedStrings(node: TSESTree.Literal) { - if (isStringLiteral(node) && isDoubleQuoted(node)) { - doubleQuotedStringLiterals.add(node); - } - } - - function visitLocalizeCall(node: TSESTree.CallExpression) { - - // localize(key, message) - const [keyNode, messageNode] = (node).arguments; - - // (1) - // extract key so that it can be checked later - let key: string | undefined; - if (isStringLiteral(keyNode)) { - doubleQuotedStringLiterals.delete(keyNode); //todo@joh reconsider - key = keyNode.value; - - } else if (keyNode.type === AST_NODE_TYPES.ObjectExpression) { - for (let property of keyNode.properties) { - if (property.type === AST_NODE_TYPES.Property && !property.computed) { - if (property.key.type === AST_NODE_TYPES.Identifier && property.key.name === 'key') { - if (isStringLiteral(property.value)) { - doubleQuotedStringLiterals.delete(property.value); //todo@joh reconsider - key = property.value.value; - break; - } - } - } - } - } - if (typeof key === 'string') { - let array = externalizedStringLiterals.get(key); - if (!array) { - array = []; - externalizedStringLiterals.set(key, array); - } - array.push({ call: node, message: messageNode }); - } - - // (2) - // remove message-argument from doubleQuoted list and make - // sure it is a string-literal - doubleQuotedStringLiterals.delete(messageNode); - if (!isStringLiteral(messageNode)) { - context.report({ - loc: messageNode.loc, - messageId: 'badMessage', - data: { message: context.getSourceCode().getText(node) } - }); - } - } - - function reportBadStringsAndBadKeys() { - // (1) - // report all strings that are in double quotes - for (const node of doubleQuotedStringLiterals) { - context.report({ loc: node.loc, messageId: 'doubleQuoted' }); - } - - for (const [key, values] of externalizedStringLiterals) { - - // (2) - // report all invalid NLS keys - if (!key.match(NoUnexternalizedStrings._rNlsKeys)) { - for (let value of values) { - context.report({ loc: value.call.loc, messageId: 'badKey', data: { key } }); - } - } - - // (2) - // report all invalid duplicates (same key, different message) - if (values.length > 1) { - for (let i = 1; i < values.length; i++) { - if (context.getSourceCode().getText(values[i - 1].message) !== context.getSourceCode().getText(values[i].message)) { - context.report({ loc: values[i].call.loc, messageId: 'duplicateKey', data: { key } }); - } - } - } - } - } - - return { - ['Literal']: (node: any) => collectDoubleQuotedStrings(node), - ['ExpressionStatement[directive] Literal:exit']: (node: any) => doubleQuotedStringLiterals.delete(node), - ['CallExpression[callee.type="MemberExpression"][callee.object.name="nls"][callee.property.name="localize"]:exit']: (node: any) => visitLocalizeCall(node), - ['CallExpression[callee.name="localize"][arguments.length>=2]:exit']: (node: any) => visitLocalizeCall(node), - ['Program:exit']: reportBadStringsAndBadKeys, - }; - } -}; - diff --git a/build/eslint/code-no-unused-expressions.js b/build/eslint/code-no-unused-expressions.js deleted file mode 100644 index 80ae9a757..000000000 --- a/build/eslint/code-no-unused-expressions.js +++ /dev/null @@ -1,141 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// FORKED FROM https://github.com/eslint/eslint/blob/b23ad0d789a909baf8d7c41a35bc53df932eaf30/lib/rules/no-unused-expressions.js -// and added support for `OptionalCallExpression`, see https://github.com/facebook/create-react-app/issues/8107 and https://github.com/eslint/eslint/issues/12642 - -/** - * @fileoverview Flag expressions in statement position that do not side effect - * @author Michael Ficarra - */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: 'suggestion', - - docs: { - description: 'disallow unused expressions', - category: 'Best Practices', - recommended: false, - url: 'https://eslint.org/docs/rules/no-unused-expressions' - }, - - schema: [ - { - type: 'object', - properties: { - allowShortCircuit: { - type: 'boolean', - default: false - }, - allowTernary: { - type: 'boolean', - default: false - }, - allowTaggedTemplates: { - type: 'boolean', - default: false - } - }, - additionalProperties: false - } - ] - }, - - create(context) { - const config = context.options[0] || {}, - allowShortCircuit = config.allowShortCircuit || false, - allowTernary = config.allowTernary || false, - allowTaggedTemplates = config.allowTaggedTemplates || false; - - /** - * @param {ASTNode} node any node - * @returns {boolean} whether the given node structurally represents a directive - */ - function looksLikeDirective(node) { - return node.type === 'ExpressionStatement' && - node.expression.type === 'Literal' && typeof node.expression.value === 'string'; - } - - /** - * @param {Function} predicate ([a] -> Boolean) the function used to make the determination - * @param {a[]} list the input list - * @returns {a[]} the leading sequence of members in the given list that pass the given predicate - */ - function takeWhile(predicate, list) { - for (let i = 0; i < list.length; ++i) { - if (!predicate(list[i])) { - return list.slice(0, i); - } - } - return list.slice(); - } - - /** - * @param {ASTNode} node a Program or BlockStatement node - * @returns {ASTNode[]} the leading sequence of directive nodes in the given node's body - */ - function directives(node) { - return takeWhile(looksLikeDirective, node.body); - } - - /** - * @param {ASTNode} node any node - * @param {ASTNode[]} ancestors the given node's ancestors - * @returns {boolean} whether the given node is considered a directive in its current position - */ - function isDirective(node, ancestors) { - const parent = ancestors[ancestors.length - 1], - grandparent = ancestors[ancestors.length - 2]; - - return (parent.type === 'Program' || parent.type === 'BlockStatement' && - (/Function/u.test(grandparent.type))) && - directives(parent).indexOf(node) >= 0; - } - - /** - * Determines whether or not a given node is a valid expression. Recurses on short circuit eval and ternary nodes if enabled by flags. - * @param {ASTNode} node any node - * @returns {boolean} whether the given node is a valid expression - */ - function isValidExpression(node) { - if (allowTernary) { - - // Recursive check for ternary and logical expressions - if (node.type === 'ConditionalExpression') { - return isValidExpression(node.consequent) && isValidExpression(node.alternate); - } - } - - if (allowShortCircuit) { - if (node.type === 'LogicalExpression') { - return isValidExpression(node.right); - } - } - - if (allowTaggedTemplates && node.type === 'TaggedTemplateExpression') { - return true; - } - - return /^(?:Assignment|OptionalCall|Call|New|Update|Yield|Await)Expression$/u.test(node.type) || - (node.type === 'UnaryExpression' && ['delete', 'void'].indexOf(node.operator) >= 0); - } - - return { - ExpressionStatement(node) { - if (!isValidExpression(node.expression) && !isDirective(node, context.getAncestors())) { - context.report({ node, message: 'Expected an assignment or function call and instead saw an expression.' }); - } - } - }; - - } -}; diff --git a/build/hygiene.js b/build/hygiene.js index e3326f555..4959e988e 100644 --- a/build/hygiene.js +++ b/build/hygiene.js @@ -5,15 +5,30 @@ 'use strict'; -const filter = require('gulp-filter'); const es = require('event-stream'); const tsfmt = require('typescript-formatter'); -const gulpeslint = require('gulp-eslint'); +const { ESLint } = require('eslint'); const VinylFile = require('vinyl'); const vfs = require('vinyl-fs'); const path = require('path'); const fs = require('fs'); -const pall = require('p-all'); +const pall = require('p-all').default; +const { minimatch } = require('minimatch'); + +function fileFilter(patterns) { + return es.through(function (file) { + const rel = file.relative; + const match = patterns.every(p => { + if (p.startsWith('!')) { + return !minimatch(rel, p.slice(1), { dot: true }); + } + return true; + }) && patterns.some(p => !p.startsWith('!') && minimatch(rel, p, { dot: true })); + if (match) { + this.emit('data', file); + } + }); +} /** * Hygiene works by creating cascading subsets of all our files and @@ -175,21 +190,26 @@ function hygiene(some) { } const result = input - .pipe(filter(f => !f.stat.isDirectory())) - .pipe(filter(indentationFilter)) + .pipe(es.through(function (f) { if (!f.stat.isDirectory()) this.emit('data', f); })) + .pipe(fileFilter(indentationFilter)) .pipe(indentation) - .pipe(filter(copyrightFilter)) + .pipe(fileFilter(copyrightFilter)) .pipe(copyrights) - .pipe(filter(tsHygieneFilter)) + .pipe(fileFilter(tsHygieneFilter)) .pipe(formatting) - .pipe(gulpeslint({ - configFile: '.eslintrc.js', - rulePaths: ['./build/eslint'] - })) - .pipe(gulpeslint.formatEach('compact')) - .pipe(gulpeslint.result(result => { - errorCount += result.warningCount; - errorCount += result.errorCount; + .pipe(es.map(function (file, cb) { + const eslint = new ESLint(); + eslint.lintText(file.contents.toString('utf8'), { + filePath: file.path, + }).then(results => { + for (const result of results) { + errorCount += result.warningCount + result.errorCount; + for (const message of result.messages) { + console.error(`${file.relative}:${message.line}:${message.column}: ${message.message} [${message.ruleId}]`); + } + } + cb(null, file); + }).catch(err => cb(err)); })); let count = 0; diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..ba3976c32 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const typescriptParser = require('@typescript-eslint/parser'); +const typescriptPlugin = require('@typescript-eslint/eslint-plugin'); +const stylisticPlugin = require('@stylistic/eslint-plugin'); + +module.exports = [ + { + ignores: ['**/node_modules/**'], + }, + { + files: ['src/**/*.ts'], + languageOptions: { + parser: typescriptParser, + sourceType: 'module', + }, + plugins: { + '@typescript-eslint': typescriptPlugin, + '@stylistic': stylisticPlugin, + }, + rules: { + '@stylistic/member-delimiter-style': [ + 'warn', + { + multiline: { + delimiter: 'semi', + requireLast: true, + }, + singleline: { + delimiter: 'semi', + requireLast: false, + }, + }, + ], + 'semi': ['warn', 'always'], + 'constructor-super': 'warn', + 'curly': 'warn', + 'eqeqeq': ['warn', 'always'], + 'no-async-promise-executor': 'warn', + 'no-buffer-constructor': 'warn', + 'no-caller': 'warn', + 'no-debugger': 'warn', + 'no-duplicate-case': 'warn', + 'no-duplicate-imports': 'warn', + 'no-eval': 'warn', + 'no-extra-semi': 'warn', + 'no-new-wrappers': 'warn', + 'no-redeclare': 'off', + 'no-sparse-arrays': 'warn', + 'no-throw-literal': 'warn', + 'no-unsafe-finally': 'warn', + 'no-unused-labels': 'warn', + '@typescript-eslint/no-redeclare': 'warn', + 'no-var': 'warn', + 'no-unused-expressions': ['warn', { allowTernary: true }], + }, + }, +]; diff --git a/package.json b/package.json index b4f8b20df..b3be0d9da 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "tsc-b": "tsc -b", "tsc-b-w": "tsc -b -w", "precommit": "node build/hygiene.js", - "lint": "eslint -c .eslintrc.js --rulesdir ./build/eslint --max-warnings 0 --ext .ts ./src", + "lint": "eslint --max-warnings 0 ./src", "npm-pack": "npm pack", "clean": "npm-run-all clean-dist clean-built", "clean-dist": "rimraf dist", @@ -70,15 +70,13 @@ "@types/text-table": "^0.2.5", "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.26.0", - "@typescript-eslint/experimental-utils": "^5.62.0", "@typescript-eslint/parser": "^8.26.0", "chai": "^4.5.0", "copyfiles": "^2.4.1", "esbuild": "^0.27.0", - "eslint": "^8.57.0", + "eslint": "^9.39.3", "event-stream": "^4.0.1", - "gulp-eslint": "^6.0.0", - "gulp-filter": "^9.0.1", + "minimatch": "^10.0.1", "mocha": "^11.1.0", "npm-run-all": "^4.1.5", "p-all": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index 78b784602..9039fae8e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,20 +2,6 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": - version "7.26.2" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" - integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== - dependencies: - "@babel/helper-validator-identifier" "^7.25.9" - js-tokens "^4.0.0" - picocolors "^1.0.0" - -"@babel/helper-validator-identifier@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" - integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== - "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -153,13 +139,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz#64995295227e001f2940258617c6674efb3ac48d" integrity sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg== -"@eslint-community/eslint-utils@^4.2.0": - version "4.4.1" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" - integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== - dependencies: - eslint-visitor-keys "^3.4.3" - "@eslint-community/eslint-utils@^4.7.0": version "4.7.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz#607084630c6c033992a082de6e6fbc1a8b52175a" @@ -167,30 +146,78 @@ dependencies: eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/eslint-utils@^4.8.0": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" + integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.10.0": version "4.12.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== -"@eslint/eslintrc@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" - integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== +"@eslint-community/regexpp@^4.12.1": + version "4.12.2" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" + integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== + +"@eslint/config-array@^0.21.1": + version "0.21.1" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.1.tgz#7d1b0060fea407f8301e932492ba8c18aff29713" + integrity sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA== dependencies: - ajv "^6.12.4" + "@eslint/object-schema" "^2.1.7" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/config-helpers@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz#1bd006ceeb7e2e55b2b773ab318d300e1a66aeda" + integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== + dependencies: + "@eslint/core" "^0.17.0" + +"@eslint/core@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.17.0.tgz#77225820413d9617509da9342190a2019e78761c" + integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.3.1": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.4.tgz#e402b1920f7c1f5a15342caa432b1348cacbb641" + integrity sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ== + dependencies: + ajv "^6.14.0" debug "^4.3.2" - espree "^9.6.0" - globals "^13.19.0" + espree "^10.0.1" + globals "^14.0.0" ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" + js-yaml "^4.1.1" + minimatch "^3.1.3" strip-json-comments "^3.1.1" -"@eslint/js@8.57.1": - version "8.57.1" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" - integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== +"@eslint/js@9.39.3": + version "9.39.3" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.3.tgz#c6168736c7e0c43ead49654ed06a4bcb3833363d" + integrity sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw== + +"@eslint/object-schema@^2.1.7": + version "2.1.7" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" + integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== + +"@eslint/plugin-kit@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz#9779e3fd9b7ee33571a57435cf4335a1794a6cb2" + integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== + dependencies: + "@eslint/core" "^0.17.0" + levn "^0.4.1" "@gulpjs/to-absolute-glob@^4.0.0": version "4.0.0" @@ -199,24 +226,28 @@ dependencies: is-negated-glob "^1.0.0" -"@humanwhocodes/config-array@^0.13.0": - version "0.13.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" - integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.7" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.7.tgz#822cb7b3a12c5a240a24f621b5a2413e27a45f26" + integrity sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== dependencies: - "@humanwhocodes/object-schema" "^2.0.3" - debug "^4.3.1" - minimatch "^3.0.5" + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.4.0" "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^2.0.3": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" - integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" + integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== "@isaacs/cliui@^8.0.2": version "8.0.2" @@ -268,7 +299,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": +"@nodelib/fs.walk@^1.2.3": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -329,6 +360,11 @@ dependencies: chalk "*" +"@types/estree@^1.0.6": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + "@types/follow-redirects@^1.14.4": version "1.14.4" resolved "https://registry.yarnpkg.com/@types/follow-redirects/-/follow-redirects-1.14.4.tgz#ca054d72ef574c77949fc5fff278b430fcd508ec" @@ -341,7 +377,7 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== -"@types/json-schema@^7.0.9": +"@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -384,7 +420,7 @@ dependencies: "@types/node" "*" -"@types/semver@^7.3.12", "@types/semver@^7.5.8": +"@types/semver@^7.5.8": version "7.7.1" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.1.tgz#3ce3af1a5524ef327d2da9e4fd8b6d95c8d70528" integrity sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA== @@ -434,13 +470,6 @@ natural-compare "^1.4.0" ts-api-utils "^2.1.0" -"@typescript-eslint/experimental-utils@^5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz#14559bf73383a308026b427a4a6129bae2146741" - integrity sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw== - dependencies: - "@typescript-eslint/utils" "5.62.0" - "@typescript-eslint/parser@^8.26.0": version "8.47.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.47.0.tgz#51b14ab2be2057ec0f57073b9ff3a9c078b0a964" @@ -461,14 +490,6 @@ "@typescript-eslint/types" "^8.47.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" - integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== - dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" - "@typescript-eslint/scope-manager@8.47.0": version "8.47.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz#d1c36a973a5499fed3a99e2e6a66aec5c9b1e542" @@ -493,29 +514,11 @@ debug "^4.3.4" ts-api-utils "^2.1.0" -"@typescript-eslint/types@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" - integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== - "@typescript-eslint/types@8.47.0", "@typescript-eslint/types@^8.47.0": version "8.47.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.47.0.tgz#c7fc9b6642d03505f447a8392934b9d1850de5af" integrity sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A== -"@typescript-eslint/typescript-estree@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" - integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== - dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" - "@typescript-eslint/typescript-estree@8.47.0": version "8.47.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz#86416dad58db76c4b3bd6a899b1381f9c388489a" @@ -532,20 +535,6 @@ semver "^7.6.0" ts-api-utils "^2.1.0" -"@typescript-eslint/utils@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" - integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@types/json-schema" "^7.0.9" - "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/typescript-estree" "5.62.0" - eslint-scope "^5.1.1" - semver "^7.3.7" - "@typescript-eslint/utils@8.47.0", "@typescript-eslint/utils@^8.13.0": version "8.47.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.47.0.tgz#d6c30690431dbfdab98fc027202af12e77c91419" @@ -556,14 +545,6 @@ "@typescript-eslint/types" "8.47.0" "@typescript-eslint/typescript-estree" "8.47.0" -"@typescript-eslint/visitor-keys@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" - integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== - dependencies: - "@typescript-eslint/types" "5.62.0" - eslint-visitor-keys "^3.3.0" - "@typescript-eslint/visitor-keys@8.47.0": version "8.47.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz#35f36ed60a170dfc9d4d738e78387e217f24c29f" @@ -572,12 +553,7 @@ "@typescript-eslint/types" "8.47.0" eslint-visitor-keys "^4.2.1" -"@ungap/structured-clone@^1.2.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" - integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== - -acorn-jsx@^5.2.0, acorn-jsx@^5.3.2: +acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== @@ -589,22 +565,22 @@ acorn-walk@^8.1.1: dependencies: acorn "^8.11.0" -acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.11.0, acorn@^8.14.0, acorn@^8.4.1, acorn@^8.9.0: +acorn@^8.11.0, acorn@^8.14.0, acorn@^8.4.1: version "8.14.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== +acorn@^8.15.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + agent-base@^7.1.0, agent-base@^7.1.2: version "7.1.3" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== -ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -614,31 +590,15 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-colors@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" - integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== - dependencies: - ansi-wrap "^0.1.0" - -ansi-escapes@^4.2.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== +ajv@^6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.14.0.tgz#fd067713e228210636ebb08c60bd3765d6dbe73a" + integrity sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw== dependencies: - type-fest "^0.21.3" - -ansi-gray@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" - integrity sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw== - dependencies: - ansi-wrap "0.1.0" - -ansi-regex@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" - integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" ansi-regex@^5.0.1: version "5.0.1" @@ -650,7 +610,7 @@ ansi-regex@^6.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== -ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -669,11 +629,6 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -ansi-wrap@0.1.0, ansi-wrap@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" - integrity sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw== - anymatch@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -687,28 +642,11 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== - array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" @@ -717,21 +655,6 @@ array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: call-bound "^1.0.3" is-array-buffer "^3.0.5" -array-differ@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-4.0.0.tgz#aa3c891c653523290c880022f45b06a42051b026" - integrity sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw== - -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array-union@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-3.0.1.tgz#da52630d327f8b88cfbfb57728e2af5cd9b6b975" - integrity sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw== - arraybuffer.prototype.slice@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" @@ -750,11 +673,6 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== - ast-types@^0.13.4: version "0.13.4" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" @@ -762,11 +680,6 @@ ast-types@^0.13.4: dependencies: tslib "^2.0.1" -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - async-function@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" @@ -789,6 +702,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== + bare-events@^2.2.0: version "2.5.4" resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.5.4.tgz#16143d435e1ed9eafd1ab85f12b89b3357a41745" @@ -828,6 +746,13 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +brace-expansion@^5.0.2: + version "5.0.4" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.4.tgz#614daaecd0a688f660bbbc909a8748c3d80d4336" + integrity sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg== + dependencies: + balanced-match "^4.0.2" + braces@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -902,7 +827,7 @@ chalk@*, chalk@^5.4.1: resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== -chalk@^2.1.0, chalk@^2.4.1: +chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -919,11 +844,6 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - check-error@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" @@ -943,18 +863,6 @@ chownr@^3.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -1002,11 +910,6 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - commander@^2.19.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -1061,7 +964,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.2, cross-spawn@^7.0.6: +cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -1102,7 +1005,7 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -debug@4, debug@^4.0.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: +debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -1121,7 +1024,7 @@ deep-eql@^4.1.3: dependencies: type-detect "^4.0.0" -deep-is@^0.1.3, deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -1163,20 +1066,6 @@ diff@^7.0.0: resolved "https://registry.yarnpkg.com/diff/-/diff-7.0.0.tgz#3fb34d387cd76d803f6eebea67b921dab0182a9a" integrity sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw== -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - dunder-proto@^1.0.0, dunder-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" @@ -1206,11 +1095,6 @@ editorconfig@^0.15.0: semver "^5.6.0" sigmund "^1.0.1" -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -1379,35 +1263,15 @@ escodegen@^2.1.0: optionalDependencies: source-map "~0.6.1" -eslint-scope@^5.0.0, eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== +eslint-scope@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" + integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-utils@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" - integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-visitor-keys@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== @@ -1422,92 +1286,54 @@ eslint-visitor-keys@^4.2.1: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== -eslint@^6.0.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" - integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.10.0" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" - doctrine "^3.0.0" - eslint-scope "^5.0.0" - eslint-utils "^1.4.3" - eslint-visitor-keys "^1.1.0" - espree "^6.1.2" - esquery "^1.0.1" - esutils "^2.0.2" - file-entry-cache "^5.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" - globals "^12.1.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - inquirer "^7.0.0" - is-glob "^4.0.0" - js-yaml "^3.13.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.14" - minimatch "^3.0.4" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.3" - progress "^2.0.0" - regexpp "^2.0.1" - semver "^6.1.2" - strip-ansi "^5.2.0" - strip-json-comments "^3.0.1" - table "^5.2.3" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - -eslint@^8.57.0: - version "8.57.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" - integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.57.1" - "@humanwhocodes/config-array" "^0.13.0" +eslint@^9.39.3: + version "9.39.3" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.3.tgz#08d63df1533d7743c0907b32a79a7e134e63ee2f" + integrity sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg== + dependencies: + "@eslint-community/eslint-utils" "^4.8.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.21.1" + "@eslint/config-helpers" "^0.4.2" + "@eslint/core" "^0.17.0" + "@eslint/eslintrc" "^3.3.1" + "@eslint/js" "9.39.3" + "@eslint/plugin-kit" "^0.4.1" + "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - "@ungap/structured-clone" "^1.2.0" + "@humanwhocodes/retry" "^0.4.2" + "@types/estree" "^1.0.6" ajv "^6.12.4" chalk "^4.0.0" - cross-spawn "^7.0.2" + cross-spawn "^7.0.6" debug "^4.3.2" - doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.2.2" - eslint-visitor-keys "^3.4.3" - espree "^9.6.1" - esquery "^1.4.2" + eslint-scope "^8.4.0" + eslint-visitor-keys "^4.2.1" + espree "^10.4.0" + esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" + file-entry-cache "^8.0.0" find-up "^5.0.0" glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" + +espree@^10.0.1, espree@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" + integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== + dependencies: + acorn "^8.15.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.1" espree@^10.3.0: version "10.3.0" @@ -1518,33 +1344,15 @@ espree@^10.3.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^4.2.0" -espree@^6.1.2: - version "6.2.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" - integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== - dependencies: - acorn "^7.1.1" - acorn-jsx "^5.2.0" - eslint-visitor-keys "^1.1.0" - -espree@^9.6.0, espree@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" - integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== - dependencies: - acorn "^8.9.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" - -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.1, esquery@^1.4.2: - version "1.6.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" - integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== +esquery@^1.5.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.7.0.tgz#08d048f261f0ddedb5bae95f46809463d9c9496d" + integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== dependencies: estraverse "^5.1.0" @@ -1555,11 +1363,6 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" @@ -1583,33 +1386,6 @@ event-stream@^4.0.1: stream-combiner "^0.2.2" through "^2.3.8" -extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -fancy-log@^1.3.2: - version "1.3.3" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" - integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== - dependencies: - ansi-gray "^0.1.1" - color-support "^1.1.3" - parse-node-version "^1.0.0" - time-stamp "^1.0.0" - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -1620,7 +1396,7 @@ fast-fifo@^1.3.2: resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== -fast-glob@^3.2.9, fast-glob@^3.3.2: +fast-glob@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== @@ -1636,7 +1412,7 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== @@ -1648,26 +1424,12 @@ fastq@^1.13.0, fastq@^1.6.0: dependencies: reusify "^1.0.4" -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== - dependencies: - flat-cache "^2.0.1" - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== dependencies: - flat-cache "^3.0.4" + flat-cache "^4.0.0" fill-range@^7.1.1: version "7.1.1" @@ -1684,34 +1446,19 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== - dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" - -flat-cache@^3.0.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" - integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== dependencies: flatted "^3.2.9" - keyv "^4.5.3" - rimraf "^3.0.2" + keyv "^4.5.4" flat@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -flatted@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== - flatted@^3.2.9: version "3.3.3" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" @@ -1772,11 +1519,6 @@ function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: hasown "^2.0.2" is-callable "^1.2.7" -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== - functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" @@ -1834,7 +1576,7 @@ get-uri@^6.0.1: data-uri-to-buffer "^6.0.2" debug "^4.3.4" -glob-parent@^5.0.0, glob-parent@^5.1.2: +glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -1874,7 +1616,7 @@ glob@^10.3.7, glob@^10.4.5: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^7.0.5, glob@^7.1.3: +glob@^7.0.5: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -1886,19 +1628,10 @@ glob@^7.0.5, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^12.1.0: - version "12.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" - integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== - dependencies: - type-fest "^0.8.1" - -globals@^13.19.0: - version "13.24.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" - integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== - dependencies: - type-fest "^0.20.2" +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== globalthis@^1.0.4: version "1.0.4" @@ -1908,18 +1641,6 @@ globalthis@^1.0.4: define-properties "^1.2.1" gopd "^1.0.1" -globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - gopd@^1.0.1, gopd@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" @@ -1935,26 +1656,6 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== -gulp-eslint@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/gulp-eslint/-/gulp-eslint-6.0.0.tgz#7d402bb45f8a67652b868277011812057370a832" - integrity sha512-dCVPSh1sA+UVhn7JSQt7KEb4An2sQNbOdB3PA8UCfxsoPlAKjJHxYHGXdXC7eb+V1FAnilSFFqslPrq037l1ig== - dependencies: - eslint "^6.0.0" - fancy-log "^1.3.2" - plugin-error "^1.0.1" - -gulp-filter@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/gulp-filter/-/gulp-filter-9.0.1.tgz#26e9c578be732423028bebde750f764b7b8d6293" - integrity sha512-knVYL8h9bfYIeft3VokVTkuaWJkQJMrFCS3yVjZQC6BGg+1dZFoeUY++B9D2X4eFpeNTx9StWK0qnDby3NO3PA== - dependencies: - multimatch "^7.0.0" - plugin-error "^2.0.1" - slash "^5.1.0" - streamfilter "^3.0.0" - to-absolute-glob "^3.0.0" - has-bigints@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" @@ -2029,13 +1730,6 @@ https-proxy-agent@^7.0.6: agent-base "^7.1.2" debug "4" -iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" @@ -2048,11 +1742,6 @@ ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - ignore@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" @@ -2063,7 +1752,7 @@ ignore@^7.0.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.4.tgz#a12c70d0f2607c5bf508fb65a40c75f037d7a078" integrity sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A== -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1: version "3.3.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== @@ -2089,25 +1778,6 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inquirer@^7.0.0: - version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.19" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.6.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - internal-slot@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" @@ -2125,14 +1795,6 @@ ip-address@^9.0.5: jsbn "1.1.0" sprintf-js "^1.1.3" -is-absolute@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" - integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== - dependencies: - is-relative "^1.0.0" - is-windows "^1.0.1" - is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" @@ -2202,13 +1864,6 @@ is-date-object@^1.0.5, is-date-object@^1.1.0: call-bound "^1.0.2" has-tostringtag "^1.0.2" -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -2221,11 +1876,6 @@ is-finalizationregistry@^1.1.0: dependencies: call-bound "^1.0.3" -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -2281,13 +1931,6 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - is-regex@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" @@ -2298,13 +1941,6 @@ is-regex@^1.2.1: has-tostringtag "^1.0.2" hasown "^2.0.2" -is-relative@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" - integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== - dependencies: - is-unc-path "^1.0.0" - is-set@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" @@ -2341,13 +1977,6 @@ is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: dependencies: which-typed-array "^1.1.16" -is-unc-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" - integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== - dependencies: - unc-path-regex "^0.1.2" - is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" @@ -2378,11 +2007,6 @@ is-weakset@^2.0.3: call-bound "^1.0.3" get-intrinsic "^1.2.6" -is-windows@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -2403,11 +2027,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - jackspeak@^3.1.2: version "3.4.3" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" @@ -2417,20 +2036,7 @@ jackspeak@^3.1.2: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.2" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0" - integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@^4.1.0: +js-yaml@^4.1.0, js-yaml@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== @@ -2467,7 +2073,7 @@ jsonc-parser@^3.3.1: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz#f2a524b4f7fd11e3d791e559977ad60b98b798b4" integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== -keyv@^4.5.3: +keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -2479,14 +2085,6 @@ lead@^4.0.0: resolved "https://registry.yarnpkg.com/lead/-/lead-4.0.0.tgz#5317a49effb0e7ec3a0c8fb9c1b24fb716aab939" integrity sha512-DpMa59o5uGUWWjruMp71e6knmwKU3jRBBn1kjuLWN9EeIOxNeSAwvHf03WIl8g/ZMR2oSQC9ej3yeLBwdDc/pg== -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -2517,11 +2115,6 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.14, lodash@^4.17.19: - version "4.17.23" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" - integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== - log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" @@ -2580,7 +2173,7 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== -merge2@^1.3.0, merge2@^1.4.1: +merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -2593,10 +2186,12 @@ micromatch@^4.0.8: braces "^3.0.3" picomatch "^2.3.1" -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +minimatch@^10.0.1: + version "10.2.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.4.tgz#465b3accbd0218b8281f5301e27cedc697f96fde" + integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg== + dependencies: + brace-expansion "^5.0.2" minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" @@ -2605,18 +2200,20 @@ minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatc dependencies: brace-expansion "^1.1.7" -minimatch@^9.0.3, minimatch@^9.0.4, minimatch@^9.0.5: +minimatch@^3.1.3: + version "3.1.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" + integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.4, minimatch@^9.0.5: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" -minimist@^1.2.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - minipass@^4.0.0: version "4.2.8" resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" @@ -2634,13 +2231,6 @@ minizlib@^3.1.0: dependencies: minipass "^7.1.2" -mkdirp@^0.5.1: - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" @@ -2678,20 +2268,6 @@ ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multimatch@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-7.0.0.tgz#d0a1bf144db9106b8d19e3cb8cabec1a8986c27f" - integrity sha512-SYU3HBAdF4psHEL/+jXDKHO95/m5P2RvboHT2Y0WtTttvJLP4H/2WS9WlQPFvF6C8d6SpLw8vjCnQOnVIVOSJQ== - dependencies: - array-differ "^4.0.0" - array-union "^3.0.1" - minimatch "^9.0.3" - -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - nan@^2.17.0: version "2.22.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.2.tgz#6b504fd029fb8f38c0990e52ad5c26772fdacfbb" @@ -2798,25 +2374,6 @@ once@^1.3.0, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -optionator@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -2829,11 +2386,6 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== - own-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" @@ -2911,11 +2463,6 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-node-version@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" - integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== - path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -2956,11 +2503,6 @@ path-type@^3.0.0: dependencies: pify "^3.0.0" -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - pathval@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" @@ -2973,7 +2515,7 @@ pause-stream@^0.0.11: dependencies: through "~2.3" -picocolors@^1.0.0, picocolors@^1.1.1: +picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -2998,23 +2540,6 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== -plugin-error@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" - integrity sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA== - dependencies: - ansi-colors "^1.0.1" - arr-diff "^4.0.0" - arr-union "^3.1.0" - extend-shallow "^3.0.2" - -plugin-error@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-2.0.1.tgz#f2ac92bac8c85e3e23492d76d0c3ca12f30eb00b" - integrity sha512-zMakqvIDyY40xHOvzXka0kUvf40nYIuwRE8dWhti2WtjQZ31xAgBZBhxsK7vK3QbRXS1Xms/LO7B5cuAsfB2Gg== - dependencies: - ansi-colors "^1.0.1" - possible-typed-array-names@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" @@ -3025,21 +2550,11 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - proxy-agent@^6.5.0: version "6.5.0" resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.5.0.tgz#9e49acba8e4ee234aacb539f89ed9c23d02f232d" @@ -3095,7 +2610,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -readable-stream@^3.0.6, readable-stream@^3.4.0: +readable-stream@^3.4.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -3165,11 +2680,6 @@ regexp.prototype.flags@^1.5.3: gopd "^1.2.0" set-function-name "^2.0.2" -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== - remove-trailing-separator@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -3206,33 +2716,11 @@ resolve@^1.10.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - reusify@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - rimraf@^5.0.10: version "5.0.10" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.10.tgz#23b9843d3dc92db71f96e1a2ce92e39fd2a8221c" @@ -3240,11 +2728,6 @@ rimraf@^5.0.10: dependencies: glob "^10.3.7" -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -3252,13 +2735,6 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^6.6.0: - version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - safe-array-concat@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" @@ -3297,7 +2773,7 @@ safe-regex-test@^1.1.0: es-errors "^1.3.0" is-regex "^1.2.1" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -3307,12 +2783,7 @@ safe-regex-test@^1.1.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.1.2: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^7.3.7, semver@^7.6.0, semver@^7.7.1: +semver@^7.6.0, semver@^7.7.1: version "7.7.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== @@ -3429,35 +2900,11 @@ sigmund@^1.0.1: resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" integrity sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g== -signal-exit@^3.0.2: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - signal-exit@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -slash@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" - integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== - -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" @@ -3523,11 +2970,6 @@ sprintf-js@^1.1.3: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== - stream-combiner@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.2.2.tgz#aec8cbac177b56b6f4fa479ced8c1912cee52858" @@ -3551,13 +2993,6 @@ stream-to-pull-stream@^1.7.3: looper "^3.0.0" pull-stream "^3.2.3" -streamfilter@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/streamfilter/-/streamfilter-3.0.0.tgz#8c61b08179a6c336c6efccc5df30861b7a9675e7" - integrity sha512-kvKNfXCmUyC8lAXSSHCIXBUlo/lhsLcCU/OmzACZYpRUdtKIH68xYhm/+HI15jFJYtNJGYtCgn2wmIiExY1VwA== - dependencies: - readable-stream "^3.0.6" - streamx@^2.12.0, streamx@^2.12.5, streamx@^2.13.2, streamx@^2.14.0: version "2.22.0" resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.22.0.tgz#cd7b5e57c95aaef0ff9b2aef7905afa62ec6e4a7" @@ -3577,15 +3012,6 @@ streamx@^2.12.0, streamx@^2.12.5, streamx@^2.13.2, streamx@^2.14.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -3672,13 +3098,6 @@ string_decoder@~1.1.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -3698,7 +3117,7 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== -strip-json-comments@^3.0.1, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -3729,16 +3148,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -table@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - tar@^7.5.4: version "7.5.8" resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.8.tgz#6bbdfdb487914803d401bab6a2abb100aaa253d5" @@ -3777,31 +3186,11 @@ through2@^2.0.1: readable-stream "~2.3.6" xtend "~4.0.1" -through@2, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.4: +through@2, through@^2.3.8, through@~2.3, through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -time-stamp@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" - integrity sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw== - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -to-absolute-glob@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-3.0.0.tgz#d13663608718fd44bf68fa0f034abdd4fd157eca" - integrity sha512-loO/XEWTRqpfcpI7+Jr2RR2Umaaozx1t6OSVWtMi0oy5F/Fxg3IC+D/TToDnxyAGs7uZBGT/6XmyDUxgsObJXA== - dependencies: - is-absolute "^1.0.0" - is-negated-glob "^1.0.0" - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -3840,23 +3229,11 @@ ts-node@^10.9.2: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tslib@^1.8.1, tslib@^1.9.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - tslib@^2.0.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -3864,33 +3241,11 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== - dependencies: - prelude-ls "~1.1.2" - type-detect@^4.0.0, type-detect@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c" integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - typed-array-buffer@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" @@ -3959,11 +3314,6 @@ unbox-primitive@^1.1.0: has-symbols "^1.1.0" which-boxed-primitive "^1.1.1" -unc-path-regex@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== - undici-types@~6.20.0: version "6.20.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" @@ -3996,11 +3346,6 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-compile-cache@^2.0.3: - version "2.4.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz#cdada8bec61e15865f05d097c5f4fd30e94dc128" - integrity sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw== - validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -4135,7 +3480,7 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -word-wrap@^1.2.5, word-wrap@~1.2.3: +word-wrap@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== @@ -4177,13 +3522,6 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" From a38a1ded91322774525e37e97d7808a4a6df1001 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Mar 2026 12:03:22 +0100 Subject: [PATCH 55/88] Update @stylistic/eslint-plugin --- .github/dependabot.yml | 2 -- eslint.config.js => eslint.config.mjs | 8 ++--- package.json | 2 +- yarn.lock | 50 +++++++++++---------------- 4 files changed, 26 insertions(+), 36 deletions(-) rename eslint.config.js => eslint.config.mjs (87%) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 404e45827..301c9bdee 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,8 +17,6 @@ updates: patterns: - "*" ignore: - - dependency-name: "@stylistic/eslint-plugin" - update-types: ["version-update:semver-major"] # stylistic 3 to avoid esm - dependency-name: "@types/chai" update-types: ["version-update:semver-major"] # chai 4 to avoid esm - dependency-name: "@types/node" diff --git a/eslint.config.js b/eslint.config.mjs similarity index 87% rename from eslint.config.js rename to eslint.config.mjs index ba3976c32..e44a85adf 100644 --- a/eslint.config.js +++ b/eslint.config.mjs @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -const typescriptParser = require('@typescript-eslint/parser'); -const typescriptPlugin = require('@typescript-eslint/eslint-plugin'); -const stylisticPlugin = require('@stylistic/eslint-plugin'); +import typescriptParser from '@typescript-eslint/parser'; +import typescriptPlugin from '@typescript-eslint/eslint-plugin'; +import stylisticPlugin from '@stylistic/eslint-plugin'; -module.exports = [ +export default [ { ignores: ['**/node_modules/**'], }, diff --git a/package.json b/package.json index b3be0d9da..9d1d7617d 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "scripts/updateUID.Dockerfile" ], "devDependencies": { - "@stylistic/eslint-plugin": "^3.0.1", + "@stylistic/eslint-plugin": "^5.10.0", "@types/chai": "^4.3.20", "@types/chalk": "^2.2.4", "@types/follow-redirects": "^1.14.4", diff --git a/yarn.lock b/yarn.lock index 9039fae8e..53ecdc8f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -146,7 +146,7 @@ dependencies: eslint-visitor-keys "^3.4.3" -"@eslint-community/eslint-utils@^4.8.0": +"@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1": version "4.9.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== @@ -312,16 +312,17 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@stylistic/eslint-plugin@^3.0.1": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz#a9f655c518f76bfc5feb46b467d0f06e511b289d" - integrity sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g== +"@stylistic/eslint-plugin@^5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz#471bbd9f7a27ceaac4a217e7f5b3890855e5640c" + integrity sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ== dependencies: - "@typescript-eslint/utils" "^8.13.0" - eslint-visitor-keys "^4.2.0" - espree "^10.3.0" + "@eslint-community/eslint-utils" "^4.9.1" + "@typescript-eslint/types" "^8.56.0" + eslint-visitor-keys "^4.2.1" + espree "^10.4.0" estraverse "^5.3.0" - picomatch "^4.0.2" + picomatch "^4.0.3" "@tootallnate/quickjs-emscripten@^0.23.0": version "0.23.0" @@ -519,6 +520,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.47.0.tgz#c7fc9b6642d03505f447a8392934b9d1850de5af" integrity sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A== +"@typescript-eslint/types@^8.56.0": + version "8.56.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.56.1.tgz#975e5942bf54895291337c91b9191f6eb0632ab9" + integrity sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw== + "@typescript-eslint/typescript-estree@8.47.0": version "8.47.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz#86416dad58db76c4b3bd6a899b1381f9c388489a" @@ -535,7 +541,7 @@ semver "^7.6.0" ts-api-utils "^2.1.0" -"@typescript-eslint/utils@8.47.0", "@typescript-eslint/utils@^8.13.0": +"@typescript-eslint/utils@8.47.0": version "8.47.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.47.0.tgz#d6c30690431dbfdab98fc027202af12e77c91419" integrity sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ== @@ -565,7 +571,7 @@ acorn-walk@^8.1.1: dependencies: acorn "^8.11.0" -acorn@^8.11.0, acorn@^8.14.0, acorn@^8.4.1: +acorn@^8.11.0, acorn@^8.4.1: version "8.14.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== @@ -1276,11 +1282,6 @@ eslint-visitor-keys@^3.4.3: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint-visitor-keys@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" - integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== - eslint-visitor-keys@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" @@ -1335,15 +1336,6 @@ espree@^10.0.1, espree@^10.4.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^4.2.1" -espree@^10.3.0: - version "10.3.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" - integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== - dependencies: - acorn "^8.14.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^4.2.0" - esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -2525,10 +2517,10 @@ picomatch@^2.0.4, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -picomatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" - integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== +picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== pidtree@^0.3.0: version "0.3.1" From 84e70d9f17119ae0a5980262d3dce425b8a8af92 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Mar 2026 12:06:01 +0100 Subject: [PATCH 56/88] Update rimraf --- .github/dependabot.yml | 2 -- package.json | 2 +- yarn.lock | 44 ++++++++++++++++++++++++++++++++++-------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 301c9bdee..e25548647 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -25,8 +25,6 @@ updates: update-types: ["version-update:semver-major"] # tar 6 for source compatibility - dependency-name: "chai" update-types: ["version-update:semver-major"] # chai 4 to avoid esm - - dependency-name: "rimraf" - update-types: ["version-update:semver-major"] # rimraf 5 for Node 18 compatibility - dependency-name: "tar" update-types: ["version-update:semver-major"] # tar 6 for source compatibility schedule: diff --git a/package.json b/package.json index 9d1d7617d..859e7362a 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "mocha": "^11.1.0", "npm-run-all": "^4.1.5", "p-all": "^5.0.0", - "rimraf": "^5.0.10", + "rimraf": "^6.1.3", "ts-node": "^10.9.2", "typescript": "^5.8.2", "typescript-formatter": "^7.2.2", diff --git a/yarn.lock b/yarn.lock index 53ecdc8f2..118cbfad9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1596,7 +1596,7 @@ glob-stream@^8.0.3: normalize-path "^3.0.0" streamx "^2.12.5" -glob@^10.3.7, glob@^10.4.5: +glob@^10.4.5: version "10.5.0" resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== @@ -1608,6 +1608,15 @@ glob@^10.3.7, glob@^10.4.5: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" +glob@^13.0.3: + version "13.0.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-13.0.6.tgz#078666566a425147ccacfbd2e332deb66a2be71d" + integrity sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw== + dependencies: + minimatch "^10.2.2" + minipass "^7.1.3" + path-scurry "^2.0.2" + glob@^7.0.5: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -2132,6 +2141,11 @@ lru-cache@^10.2.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@^11.0.0: + version "11.2.6" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.6.tgz#356bf8a29e88a7a2945507b31f6429a65a192c58" + integrity sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ== + lru-cache@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -2178,7 +2192,7 @@ micromatch@^4.0.8: braces "^3.0.3" picomatch "^2.3.1" -minimatch@^10.0.1: +minimatch@^10.0.1, minimatch@^10.2.2: version "10.2.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.4.tgz#465b3accbd0218b8281f5301e27cedc697f96fde" integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg== @@ -2216,6 +2230,11 @@ minipass@^4.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== +minipass@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== + minizlib@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.1.0.tgz#6ad76c3a8f10227c9b51d1c9ac8e30b27f5a251c" @@ -2435,7 +2454,7 @@ pac-resolver@^7.0.1: degenerator "^5.0.0" netmask "^2.0.2" -package-json-from-dist@^1.0.0: +package-json-from-dist@^1.0.0, package-json-from-dist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== @@ -2488,6 +2507,14 @@ path-scurry@^1.11.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.2.tgz#6be0d0ee02a10d9e0de7a98bae65e182c9061f85" + integrity sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -2713,12 +2740,13 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== -rimraf@^5.0.10: - version "5.0.10" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.10.tgz#23b9843d3dc92db71f96e1a2ce92e39fd2a8221c" - integrity sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ== +rimraf@^6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-6.1.3.tgz#afbee236b3bd2be331d4e7ce4493bac1718981af" + integrity sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA== dependencies: - glob "^10.3.7" + glob "^13.0.3" + package-json-from-dist "^1.0.1" run-parallel@^1.1.9: version "1.2.0" From d3b26ad8160dd72a438d0d7e51756c9a8f21fbbb Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Mar 2026 12:08:17 +0100 Subject: [PATCH 57/88] Cleanup --- .github/dependabot.yml | 4 ---- package.json | 1 - yarn.lock | 13 ------------- 3 files changed, 18 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e25548647..e5607567c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -21,11 +21,7 @@ updates: update-types: ["version-update:semver-major"] # chai 4 to avoid esm - dependency-name: "@types/node" update-types: ["version-update:semver-major"] # Keep Node 20 compatibility - - dependency-name: "@types/tar" - update-types: ["version-update:semver-major"] # tar 6 for source compatibility - dependency-name: "chai" update-types: ["version-update:semver-major"] # chai 4 to avoid esm - - dependency-name: "tar" - update-types: ["version-update:semver-major"] # tar 6 for source compatibility schedule: interval: "weekly" diff --git a/package.json b/package.json index 859e7362a..41a5a2b52 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,6 @@ "@types/recursive-readdir": "^2.2.4", "@types/semver": "^7.5.8", "@types/shell-quote": "^1.7.5", - "@types/tar": "^6.1.13", "@types/text-table": "^0.2.5", "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.26.0", diff --git a/yarn.lock b/yarn.lock index 118cbfad9..a3283d67e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -431,14 +431,6 @@ resolved "https://registry.yarnpkg.com/@types/shell-quote/-/shell-quote-1.7.5.tgz#6db4704742d307cd6d604e124e3ad6cd5ed943f3" integrity sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw== -"@types/tar@^6.1.13": - version "6.1.13" - resolved "https://registry.yarnpkg.com/@types/tar/-/tar-6.1.13.tgz#9b5801c02175344101b4b91086ab2bbc8e93a9b6" - integrity sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw== - dependencies: - "@types/node" "*" - minipass "^4.0.0" - "@types/text-table@^0.2.5": version "0.2.5" resolved "https://registry.yarnpkg.com/@types/text-table/-/text-table-0.2.5.tgz#f9c609b81c943e9fc8d73ef82ad2f2a78be5f53b" @@ -2220,11 +2212,6 @@ minimatch@^9.0.4, minimatch@^9.0.5: dependencies: brace-expansion "^2.0.1" -minipass@^4.0.0: - version "4.2.8" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" - integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== - "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4, minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" From a7ed090bbf5b3b634c65e553386d4d479dd1e64e Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Mar 2026 12:50:13 +0100 Subject: [PATCH 58/88] Update dependencies --- .github/dependabot.yml | 4 + package.json | 39 +- yarn.lock | 1169 +++++++++++++++++----------------------- 3 files changed, 529 insertions(+), 683 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e5607567c..66f323861 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -23,5 +23,9 @@ updates: update-types: ["version-update:semver-major"] # Keep Node 20 compatibility - dependency-name: "chai" update-types: ["version-update:semver-major"] # chai 4 to avoid esm + - dependency-name: "yargs" + update-types: ["version-update:semver-major"] # yargs 17 for esbuild CJS bundling + - dependency-name: "node-pty" + update-types: ["version-update:semver-minor"] # node-pty 1.1 has broken spawn-helper permissions (microsoft/node-pty#866) schedule: interval: "weekly" diff --git a/package.json b/package.json index 41a5a2b52..475c2a28e 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,6 @@ "devDependencies": { "@stylistic/eslint-plugin": "^5.10.0", "@types/chai": "^4.3.20", - "@types/chalk": "^2.2.4", "@types/follow-redirects": "^1.14.4", "@types/js-yaml": "^4.0.9", "@types/mocha": "^10.0.10", @@ -64,42 +63,42 @@ "@types/node": "^20.19.37", "@types/pull-stream": "^3.6.7", "@types/recursive-readdir": "^2.2.4", - "@types/semver": "^7.5.8", + "@types/semver": "^7.7.1", "@types/shell-quote": "^1.7.5", "@types/text-table": "^0.2.5", - "@types/yargs": "^17.0.33", - "@typescript-eslint/eslint-plugin": "^8.26.0", - "@typescript-eslint/parser": "^8.26.0", + "@types/yargs": "^17.0.35", + "@typescript-eslint/eslint-plugin": "^8.56.1", + "@typescript-eslint/parser": "^8.56.1", "chai": "^4.5.0", "copyfiles": "^2.4.1", - "esbuild": "^0.27.0", - "eslint": "^9.39.3", + "esbuild": "^0.27.3", + "eslint": "^10.0.2", "event-stream": "^4.0.1", - "minimatch": "^10.0.1", - "mocha": "^11.1.0", + "minimatch": "^10.2.4", + "mocha": "^11.7.5", "npm-run-all": "^4.1.5", - "p-all": "^5.0.0", + "p-all": "^5.0.1", "rimraf": "^6.1.3", "ts-node": "^10.9.2", - "typescript": "^5.8.2", + "typescript": "^5.9.3", "typescript-formatter": "^7.2.2", - "vinyl": "^3.0.0", - "vinyl-fs": "^4.0.0" + "vinyl": "^3.0.1", + "vinyl-fs": "^4.0.2" }, "dependencies": { - "chalk": "^5.4.1", - "follow-redirects": "^1.15.9", - "js-yaml": "^4.1.0", + "chalk": "^5.6.2", + "follow-redirects": "^1.15.11", + "js-yaml": "^4.1.1", "jsonc-parser": "^3.3.1", "ncp": "^2.0.0", - "node-pty": "^1.0.0", + "node-pty": "~1.0.0", "proxy-agent": "^6.5.0", "pull-stream": "^3.7.0", "recursive-readdir": "^2.2.3", - "semver": "^7.7.1", - "shell-quote": "^1.8.2", + "semver": "^7.7.4", + "shell-quote": "^1.8.3", "stream-to-pull-stream": "^1.7.3", - "tar": "^7.5.4", + "tar": "^7.5.10", "text-table": "^0.2.0", "vscode-uri": "^3.1.0", "yargs": "~17.7.2" diff --git a/yarn.lock b/yarn.lock index a3283d67e..1cbecd028 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,142 +9,135 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@esbuild/aix-ppc64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz#1d8be43489a961615d49e037f1bfa0f52a773737" - integrity sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A== - -"@esbuild/android-arm64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz#bd1763194aad60753fa3338b1ba9bda974b58724" - integrity sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ== - -"@esbuild/android-arm@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.0.tgz#69c7b57f02d3b3618a5ba4f82d127b57665dc397" - integrity sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ== - -"@esbuild/android-x64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.0.tgz#6ea22b5843acb23243d0126c052d7d3b6a11ca90" - integrity sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q== - -"@esbuild/darwin-arm64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz#5ad7c02bc1b1a937a420f919afe40665ba14ad1e" - integrity sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg== - -"@esbuild/darwin-x64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz#48470c83c5fd6d1fc7c823c2c603aeee96e101c9" - integrity sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g== - -"@esbuild/freebsd-arm64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz#d5a8effd8b0be7be613cd1009da34d629d4c2457" - integrity sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw== - -"@esbuild/freebsd-x64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz#9bde638bda31aa244d6d64dbafafb41e6e799bcc" - integrity sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g== - -"@esbuild/linux-arm64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz#96008c3a207d8ca495708db714c475ea5bf7e2af" - integrity sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ== - -"@esbuild/linux-arm@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz#9b47cb0f222e567af316e978c7f35307db97bc0e" - integrity sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ== - -"@esbuild/linux-ia32@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz#d1e1e38d406cbdfb8a49f4eca0c25bbc344e18cc" - integrity sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw== - -"@esbuild/linux-loong64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz#c13bc6a53e3b69b76f248065bebee8415b44dfce" - integrity sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg== - -"@esbuild/linux-mips64el@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz#05f8322eb0a96ce1bfbc59691abe788f71e2d217" - integrity sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg== - -"@esbuild/linux-ppc64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz#6fc5e7af98b4fb0c6a7f0b73ba837ce44dc54980" - integrity sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA== - -"@esbuild/linux-riscv64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz#508afa9f69a3f97368c0bf07dd894a04af39d86e" - integrity sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ== - -"@esbuild/linux-s390x@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz#21fda656110ee242fc64f87a9e0b0276d4e4ec5b" - integrity sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w== - -"@esbuild/linux-x64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz#1758a85dcc09b387fd57621643e77b25e0ccba59" - integrity sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw== - -"@esbuild/netbsd-arm64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz#a0131159f4db6e490da35cc4bb51ef0d03b7848a" - integrity sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w== - -"@esbuild/netbsd-x64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz#6f4877d7c2ba425a2b80e4330594e0b43caa2d7d" - integrity sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA== - -"@esbuild/openbsd-arm64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz#cbefbd4c2f375cebeb4f965945be6cf81331bd01" - integrity sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ== - -"@esbuild/openbsd-x64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz#31fa9e8649fc750d7c2302c8b9d0e1547f57bc84" - integrity sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A== - -"@esbuild/openharmony-arm64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz#03727780f1fdf606e7b56193693a715d9f1ee001" - integrity sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA== - -"@esbuild/sunos-x64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz#866a35f387234a867ced35af8906dfffb073b9ff" - integrity sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA== - -"@esbuild/win32-arm64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz#53de43a9629b8a34678f28cd56cc104db1b67abb" - integrity sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg== - -"@esbuild/win32-ia32@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz#924d2aed8692fea5d27bfb6500f9b8b9c1a34af4" - integrity sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ== - -"@esbuild/win32-x64@0.27.0": - version "0.27.0" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz#64995295227e001f2940258617c6674efb3ac48d" - integrity sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg== - -"@eslint-community/eslint-utils@^4.7.0": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz#607084630c6c033992a082de6e6fbc1a8b52175a" - integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw== - dependencies: - eslint-visitor-keys "^3.4.3" +"@esbuild/aix-ppc64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz#815b39267f9bffd3407ea6c376ac32946e24f8d2" + integrity sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg== + +"@esbuild/android-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz#19b882408829ad8e12b10aff2840711b2da361e8" + integrity sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg== + +"@esbuild/android-arm@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.3.tgz#90be58de27915efa27b767fcbdb37a4470627d7b" + integrity sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA== + +"@esbuild/android-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.3.tgz#d7dcc976f16e01a9aaa2f9b938fbec7389f895ac" + integrity sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ== + +"@esbuild/darwin-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz#9f6cac72b3a8532298a6a4493ed639a8988e8abd" + integrity sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg== + +"@esbuild/darwin-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz#ac61d645faa37fd650340f1866b0812e1fb14d6a" + integrity sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg== + +"@esbuild/freebsd-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz#b8625689d73cf1830fe58c39051acdc12474ea1b" + integrity sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w== + +"@esbuild/freebsd-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz#07be7dd3c9d42fe0eccd2ab9f9ded780bc53bead" + integrity sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA== + +"@esbuild/linux-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz#bf31918fe5c798586460d2b3d6c46ed2c01ca0b6" + integrity sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg== + +"@esbuild/linux-arm@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz#28493ee46abec1dc3f500223cd9f8d2df08f9d11" + integrity sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw== + +"@esbuild/linux-ia32@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz#750752a8b30b43647402561eea764d0a41d0ee29" + integrity sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg== + +"@esbuild/linux-loong64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz#a5a92813a04e71198c50f05adfaf18fc1e95b9ed" + integrity sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA== + +"@esbuild/linux-mips64el@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz#deb45d7fd2d2161eadf1fbc593637ed766d50bb1" + integrity sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw== + +"@esbuild/linux-ppc64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz#6f39ae0b8c4d3d2d61a65b26df79f6e12a1c3d78" + integrity sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA== + +"@esbuild/linux-riscv64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz#4c5c19c3916612ec8e3915187030b9df0b955c1d" + integrity sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ== + +"@esbuild/linux-s390x@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz#9ed17b3198fa08ad5ccaa9e74f6c0aff7ad0156d" + integrity sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw== + +"@esbuild/linux-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz#12383dcbf71b7cf6513e58b4b08d95a710bf52a5" + integrity sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA== + +"@esbuild/netbsd-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz#dd0cb2fa543205fcd931df44f4786bfcce6df7d7" + integrity sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA== + +"@esbuild/netbsd-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz#028ad1807a8e03e155153b2d025b506c3787354b" + integrity sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA== + +"@esbuild/openbsd-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz#e3c16ff3490c9b59b969fffca87f350ffc0e2af5" + integrity sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw== + +"@esbuild/openbsd-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz#c5a4693fcb03d1cbecbf8b422422468dfc0d2a8b" + integrity sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ== + +"@esbuild/openharmony-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz#082082444f12db564a0775a41e1991c0e125055e" + integrity sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g== + +"@esbuild/sunos-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz#5ab036c53f929e8405c4e96e865a424160a1b537" + integrity sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA== + +"@esbuild/win32-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz#38de700ef4b960a0045370c171794526e589862e" + integrity sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA== + +"@esbuild/win32-ia32@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz#451b93dc03ec5d4f38619e6cd64d9f9eff06f55c" + integrity sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q== + +"@esbuild/win32-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz#0eaf705c941a218a43dba8e09f1df1d6cd2f1f17" + integrity sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA== "@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1": version "4.9.1" @@ -153,70 +146,45 @@ dependencies: eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.10.0": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" - integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== - -"@eslint-community/regexpp@^4.12.1": +"@eslint-community/regexpp@^4.12.2": version "4.12.2" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== -"@eslint/config-array@^0.21.1": - version "0.21.1" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.1.tgz#7d1b0060fea407f8301e932492ba8c18aff29713" - integrity sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA== +"@eslint/config-array@^0.23.2": + version "0.23.2" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.23.2.tgz#db85beeff7facc685a5775caacb1c845669b9470" + integrity sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A== dependencies: - "@eslint/object-schema" "^2.1.7" + "@eslint/object-schema" "^3.0.2" debug "^4.3.1" - minimatch "^3.1.2" + minimatch "^10.2.1" -"@eslint/config-helpers@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz#1bd006ceeb7e2e55b2b773ab318d300e1a66aeda" - integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== +"@eslint/config-helpers@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.5.2.tgz#314c7b03d02a371ad8c0a7f6821d5a8a8437ba9d" + integrity sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ== dependencies: - "@eslint/core" "^0.17.0" + "@eslint/core" "^1.1.0" -"@eslint/core@^0.17.0": - version "0.17.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.17.0.tgz#77225820413d9617509da9342190a2019e78761c" - integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== +"@eslint/core@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-1.1.0.tgz#51f5cd970e216fbdae6721ac84491f57f965836d" + integrity sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw== dependencies: "@types/json-schema" "^7.0.15" -"@eslint/eslintrc@^3.3.1": - version "3.3.4" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.4.tgz#e402b1920f7c1f5a15342caa432b1348cacbb641" - integrity sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ== - dependencies: - ajv "^6.14.0" - debug "^4.3.2" - espree "^10.0.1" - globals "^14.0.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.1" - minimatch "^3.1.3" - strip-json-comments "^3.1.1" +"@eslint/object-schema@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.2.tgz#c59c6a94aa4b428ed7f1615b6a4495c0a21f7a22" + integrity sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw== -"@eslint/js@9.39.3": - version "9.39.3" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.3.tgz#c6168736c7e0c43ead49654ed06a4bcb3833363d" - integrity sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw== - -"@eslint/object-schema@^2.1.7": - version "2.1.7" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" - integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== - -"@eslint/plugin-kit@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz#9779e3fd9b7ee33571a57435cf4335a1794a6cb2" - integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== +"@eslint/plugin-kit@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz#e0cb12ec66719cb2211ad36499fb516f2a63899d" + integrity sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ== dependencies: - "@eslint/core" "^0.17.0" + "@eslint/core" "^1.1.0" levn "^0.4.1" "@gulpjs/to-absolute-glob@^4.0.0": @@ -274,9 +242,9 @@ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/sourcemap-codec@^1.4.10": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" - integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== "@jridgewell/trace-mapping@0.3.9": version "0.3.9" @@ -286,27 +254,6 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -330,9 +277,9 @@ integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== "@tsconfig/node10@^1.0.7": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" - integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + version "1.0.12" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.12.tgz#be57ceac1e4692b41be9de6be8c32a106636dba4" + integrity sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ== "@tsconfig/node12@^1.0.7": version "1.0.11" @@ -354,14 +301,12 @@ resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.20.tgz#cb291577ed342ca92600430841a00329ba05cecc" integrity sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ== -"@types/chalk@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@types/chalk/-/chalk-2.2.4.tgz#32cd215134f191402f4909b250c28efa1f3a9c01" - integrity sha512-pb/QoGqtCpH2famSp72qEsXkNzcErlVmiXlQ/ww+5AddD8TmmYS7EWg5T20YiNCAiTgs8pMf2G8SJG5h/ER1ZQ== - dependencies: - chalk "*" +"@types/esrecurse@^4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@types/esrecurse/-/esrecurse-4.3.1.tgz#6f636af962fbe6191b830bd676ba5986926bccec" + integrity sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw== -"@types/estree@^1.0.6": +"@types/estree@^1.0.6", "@types/estree@^1.0.8": version "1.0.8" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== @@ -396,11 +341,11 @@ "@types/node" "*" "@types/node@*": - version "22.13.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.9.tgz#5d9a8f7a975a5bd3ef267352deb96fb13ec02eca" - integrity sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw== + version "25.3.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.3.5.tgz#beccb5915561f7a9970ace547ad44d6cdbf39b46" + integrity sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA== dependencies: - undici-types "~6.20.0" + undici-types "~7.18.0" "@types/node@^20.19.37": version "20.19.37" @@ -421,7 +366,7 @@ dependencies: "@types/node" "*" -"@types/semver@^7.5.8": +"@types/semver@^7.7.1": version "7.7.1" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.1.tgz#3ce3af1a5524ef327d2da9e4fd8b6d95c8d70528" integrity sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA== @@ -441,115 +386,108 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== -"@types/yargs@^17.0.33": - version "17.0.33" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" - integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== +"@types/yargs@^17.0.35": + version "17.0.35" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.35.tgz#07013e46aa4d7d7d50a49e15604c1c5340d4eb24" + integrity sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^8.26.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz#c53edeec13a79483f4ca79c298d5231b02e9dc17" - integrity sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA== - dependencies: - "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.47.0" - "@typescript-eslint/type-utils" "8.47.0" - "@typescript-eslint/utils" "8.47.0" - "@typescript-eslint/visitor-keys" "8.47.0" - graphemer "^1.4.0" - ignore "^7.0.0" +"@typescript-eslint/eslint-plugin@^8.56.1": + version "8.56.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz#b1ce606d87221daec571e293009675992f0aae76" + integrity sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A== + dependencies: + "@eslint-community/regexpp" "^4.12.2" + "@typescript-eslint/scope-manager" "8.56.1" + "@typescript-eslint/type-utils" "8.56.1" + "@typescript-eslint/utils" "8.56.1" + "@typescript-eslint/visitor-keys" "8.56.1" + ignore "^7.0.5" natural-compare "^1.4.0" - ts-api-utils "^2.1.0" + ts-api-utils "^2.4.0" -"@typescript-eslint/parser@^8.26.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.47.0.tgz#51b14ab2be2057ec0f57073b9ff3a9c078b0a964" - integrity sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ== +"@typescript-eslint/parser@^8.56.1": + version "8.56.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.56.1.tgz#21d13b3d456ffb08614c1d68bb9a4f8d9237cdc7" + integrity sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg== dependencies: - "@typescript-eslint/scope-manager" "8.47.0" - "@typescript-eslint/types" "8.47.0" - "@typescript-eslint/typescript-estree" "8.47.0" - "@typescript-eslint/visitor-keys" "8.47.0" - debug "^4.3.4" + "@typescript-eslint/scope-manager" "8.56.1" + "@typescript-eslint/types" "8.56.1" + "@typescript-eslint/typescript-estree" "8.56.1" + "@typescript-eslint/visitor-keys" "8.56.1" + debug "^4.4.3" -"@typescript-eslint/project-service@8.47.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.47.0.tgz#b8afc65e0527568018af911b702dcfbfdca16471" - integrity sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA== +"@typescript-eslint/project-service@8.56.1": + version "8.56.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.56.1.tgz#65c8d645f028b927bfc4928593b54e2ecd809244" + integrity sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.47.0" - "@typescript-eslint/types" "^8.47.0" - debug "^4.3.4" + "@typescript-eslint/tsconfig-utils" "^8.56.1" + "@typescript-eslint/types" "^8.56.1" + debug "^4.4.3" -"@typescript-eslint/scope-manager@8.47.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz#d1c36a973a5499fed3a99e2e6a66aec5c9b1e542" - integrity sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg== +"@typescript-eslint/scope-manager@8.56.1": + version "8.56.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz#254df93b5789a871351335dd23e20bc164060f24" + integrity sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w== dependencies: - "@typescript-eslint/types" "8.47.0" - "@typescript-eslint/visitor-keys" "8.47.0" + "@typescript-eslint/types" "8.56.1" + "@typescript-eslint/visitor-keys" "8.56.1" -"@typescript-eslint/tsconfig-utils@8.47.0", "@typescript-eslint/tsconfig-utils@^8.47.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz#4f178b62813538759e0989dd081c5474fad39b84" - integrity sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g== +"@typescript-eslint/tsconfig-utils@8.56.1", "@typescript-eslint/tsconfig-utils@^8.56.1": + version "8.56.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz#1afa830b0fada5865ddcabdc993b790114a879b7" + integrity sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ== -"@typescript-eslint/type-utils@8.47.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz#b9b0141d99bd5bece3811d7eee68a002597ffa55" - integrity sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A== +"@typescript-eslint/type-utils@8.56.1": + version "8.56.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz#7a6c4fabf225d674644931e004302cbbdd2f2e24" + integrity sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg== dependencies: - "@typescript-eslint/types" "8.47.0" - "@typescript-eslint/typescript-estree" "8.47.0" - "@typescript-eslint/utils" "8.47.0" - debug "^4.3.4" - ts-api-utils "^2.1.0" - -"@typescript-eslint/types@8.47.0", "@typescript-eslint/types@^8.47.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.47.0.tgz#c7fc9b6642d03505f447a8392934b9d1850de5af" - integrity sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A== + "@typescript-eslint/types" "8.56.1" + "@typescript-eslint/typescript-estree" "8.56.1" + "@typescript-eslint/utils" "8.56.1" + debug "^4.4.3" + ts-api-utils "^2.4.0" -"@typescript-eslint/types@^8.56.0": +"@typescript-eslint/types@8.56.1", "@typescript-eslint/types@^8.56.0", "@typescript-eslint/types@^8.56.1": version "8.56.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.56.1.tgz#975e5942bf54895291337c91b9191f6eb0632ab9" integrity sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw== -"@typescript-eslint/typescript-estree@8.47.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz#86416dad58db76c4b3bd6a899b1381f9c388489a" - integrity sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg== - dependencies: - "@typescript-eslint/project-service" "8.47.0" - "@typescript-eslint/tsconfig-utils" "8.47.0" - "@typescript-eslint/types" "8.47.0" - "@typescript-eslint/visitor-keys" "8.47.0" - debug "^4.3.4" - fast-glob "^3.3.2" - is-glob "^4.0.3" - minimatch "^9.0.4" - semver "^7.6.0" - ts-api-utils "^2.1.0" +"@typescript-eslint/typescript-estree@8.56.1": + version "8.56.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz#3b9e57d8129a860c50864c42188f761bdef3eab0" + integrity sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg== + dependencies: + "@typescript-eslint/project-service" "8.56.1" + "@typescript-eslint/tsconfig-utils" "8.56.1" + "@typescript-eslint/types" "8.56.1" + "@typescript-eslint/visitor-keys" "8.56.1" + debug "^4.4.3" + minimatch "^10.2.2" + semver "^7.7.3" + tinyglobby "^0.2.15" + ts-api-utils "^2.4.0" -"@typescript-eslint/utils@8.47.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.47.0.tgz#d6c30690431dbfdab98fc027202af12e77c91419" - integrity sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ== +"@typescript-eslint/utils@8.56.1": + version "8.56.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.56.1.tgz#5a86acaf9f1b4c4a85a42effb217f73059f6deb7" + integrity sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA== dependencies: - "@eslint-community/eslint-utils" "^4.7.0" - "@typescript-eslint/scope-manager" "8.47.0" - "@typescript-eslint/types" "8.47.0" - "@typescript-eslint/typescript-estree" "8.47.0" + "@eslint-community/eslint-utils" "^4.9.1" + "@typescript-eslint/scope-manager" "8.56.1" + "@typescript-eslint/types" "8.56.1" + "@typescript-eslint/typescript-estree" "8.56.1" -"@typescript-eslint/visitor-keys@8.47.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz#35f36ed60a170dfc9d4d738e78387e217f24c29f" - integrity sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ== +"@typescript-eslint/visitor-keys@8.56.1": + version "8.56.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz#50e03475c33a42d123dc99e63acf1841c0231f87" + integrity sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw== dependencies: - "@typescript-eslint/types" "8.47.0" - eslint-visitor-keys "^4.2.1" + "@typescript-eslint/types" "8.56.1" + eslint-visitor-keys "^5.0.0" acorn-jsx@^5.3.2: version "5.3.2" @@ -557,36 +495,21 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: - version "8.3.4" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" - integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + version "8.3.5" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.5.tgz#8a6b8ca8fc5b34685af15dabb44118663c296496" + integrity sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw== dependencies: acorn "^8.11.0" -acorn@^8.11.0, acorn@^8.4.1: - version "8.14.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" - integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== - -acorn@^8.15.0: +acorn@^8.11.0, acorn@^8.15.0, acorn@^8.16.0, acorn@^8.4.1: version "8.16.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== agent-base@^7.1.0, agent-base@^7.1.2: - version "7.1.3" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" - integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== - -ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" + version "7.1.4" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" + integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== ajv@^6.14.0: version "6.14.0" @@ -603,10 +526,10 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-regex@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" - integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== +ansi-regex@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== ansi-styles@^3.2.1: version "3.2.1" @@ -623,9 +546,9 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: color-convert "^2.0.1" ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== anymatch@^3.1.3: version "3.1.3" @@ -691,9 +614,9 @@ available-typed-arrays@^1.0.7: possible-typed-array-names "^1.0.0" b4a@^1.6.4: - version "1.6.7" - resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.7.tgz#a99587d4ebbfbd5a6e3b21bdb5d5fa385767abe4" - integrity sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg== + version "1.8.0" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.8.0.tgz#1ca3ba0edc9469aaabef5647e769a83d50180b1a" + integrity sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg== balanced-match@^1.0.0: version "1.0.2" @@ -705,10 +628,10 @@ balanced-match@^4.0.2: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== -bare-events@^2.2.0: - version "2.5.4" - resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.5.4.tgz#16143d435e1ed9eafd1ab85f12b89b3357a41745" - integrity sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA== +bare-events@^2.7.0: + version "2.8.2" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.8.2.tgz#7b3e10bd8e1fc80daf38bb516921678f566ab89f" + integrity sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ== base64-js@^1.3.1: version "1.5.1" @@ -716,9 +639,9 @@ base64-js@^1.3.1: integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== basic-ftp@^5.0.2: - version "5.0.5" - resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0" - integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg== + version "5.2.0" + resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.2.0.tgz#7c2dff63c918bde60e6bad1f2ff93dcf5137a40a" + integrity sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw== bl@^5.0.0: version "5.1.0" @@ -737,7 +660,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -brace-expansion@^2.0.1: +brace-expansion@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== @@ -751,13 +674,6 @@ brace-expansion@^5.0.2: dependencies: balanced-match "^4.0.2" -braces@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== - dependencies: - fill-range "^7.1.1" - browser-stdout@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -789,7 +705,7 @@ call-bind@^1.0.7, call-bind@^1.0.8: get-intrinsic "^1.2.4" set-function-length "^1.2.2" -call-bound@^1.0.2, call-bound@^1.0.3: +call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== @@ -797,11 +713,6 @@ call-bound@^1.0.2, call-bound@^1.0.3: call-bind-apply-helpers "^1.0.2" get-intrinsic "^1.3.0" -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - camelcase@^6.0.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" @@ -820,11 +731,6 @@ chai@^4.5.0: pathval "^1.1.1" type-detect "^4.1.0" -chalk@*, chalk@^5.4.1: - version "5.6.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" - integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== - chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -834,7 +740,7 @@ chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -842,6 +748,11 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.6.2: + version "5.6.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" + integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== + check-error@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" @@ -1003,10 +914,10 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: - version "4.4.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" - integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== +debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" @@ -1055,9 +966,9 @@ degenerator@^5.0.0: esprima "^4.0.1" diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + version "4.0.4" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.4.tgz#7a6dbfda325f25f07517e9b518f897c08332e07d" + integrity sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ== diff@^7.0.0: version "7.0.0" @@ -1104,33 +1015,33 @@ emoji-regex@^9.2.2: integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + version "1.3.4" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414" + integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== dependencies: is-arrayish "^0.2.1" es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9: - version "1.23.9" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.9.tgz#5b45994b7de78dada5c1bebf1379646b32b9d606" - integrity sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA== + version "1.24.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.1.tgz#f0c131ed5ea1bb2411134a8dd94def09c46c7899" + integrity sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw== dependencies: array-buffer-byte-length "^1.0.2" arraybuffer.prototype.slice "^1.0.4" available-typed-arrays "^1.0.7" call-bind "^1.0.8" - call-bound "^1.0.3" + call-bound "^1.0.4" data-view-buffer "^1.0.2" data-view-byte-length "^1.0.2" data-view-byte-offset "^1.0.1" es-define-property "^1.0.1" es-errors "^1.3.0" - es-object-atoms "^1.0.0" + es-object-atoms "^1.1.1" es-set-tostringtag "^2.1.0" es-to-primitive "^1.3.0" function.prototype.name "^1.1.8" - get-intrinsic "^1.2.7" - get-proto "^1.0.0" + get-intrinsic "^1.3.0" + get-proto "^1.0.1" get-symbol-description "^1.1.0" globalthis "^1.0.4" gopd "^1.2.0" @@ -1142,21 +1053,24 @@ es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9: is-array-buffer "^3.0.5" is-callable "^1.2.7" is-data-view "^1.0.2" + is-negative-zero "^2.0.3" is-regex "^1.2.1" + is-set "^2.0.3" is-shared-array-buffer "^1.0.4" is-string "^1.1.1" is-typed-array "^1.1.15" - is-weakref "^1.1.0" + is-weakref "^1.1.1" math-intrinsics "^1.1.0" - object-inspect "^1.13.3" + object-inspect "^1.13.4" object-keys "^1.1.1" object.assign "^4.1.7" own-keys "^1.0.1" - regexp.prototype.flags "^1.5.3" + regexp.prototype.flags "^1.5.4" safe-array-concat "^1.1.3" safe-push-apply "^1.0.0" safe-regex-test "^1.1.0" set-proto "^1.0.0" + stop-iteration-iterator "^1.1.0" string.prototype.trim "^1.2.10" string.prototype.trimend "^1.0.9" string.prototype.trimstart "^1.0.8" @@ -1165,7 +1079,7 @@ es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9: typed-array-byte-offset "^1.0.4" typed-array-length "^1.0.7" unbox-primitive "^1.1.0" - which-typed-array "^1.1.18" + which-typed-array "^1.1.19" es-define-property@^1.0.0, es-define-property@^1.0.1: version "1.0.1" @@ -1203,37 +1117,37 @@ es-to-primitive@^1.3.0: is-date-object "^1.0.5" is-symbol "^1.0.4" -esbuild@^0.27.0: - version "0.27.0" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.0.tgz#db983bed6f76981361c92f50cf6a04c66f7b3e1d" - integrity sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA== +esbuild@^0.27.3: + version "0.27.3" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.3.tgz#5859ca8e70a3af956b26895ce4954d7e73bd27a8" + integrity sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg== optionalDependencies: - "@esbuild/aix-ppc64" "0.27.0" - "@esbuild/android-arm" "0.27.0" - "@esbuild/android-arm64" "0.27.0" - "@esbuild/android-x64" "0.27.0" - "@esbuild/darwin-arm64" "0.27.0" - "@esbuild/darwin-x64" "0.27.0" - "@esbuild/freebsd-arm64" "0.27.0" - "@esbuild/freebsd-x64" "0.27.0" - "@esbuild/linux-arm" "0.27.0" - "@esbuild/linux-arm64" "0.27.0" - "@esbuild/linux-ia32" "0.27.0" - "@esbuild/linux-loong64" "0.27.0" - "@esbuild/linux-mips64el" "0.27.0" - "@esbuild/linux-ppc64" "0.27.0" - "@esbuild/linux-riscv64" "0.27.0" - "@esbuild/linux-s390x" "0.27.0" - "@esbuild/linux-x64" "0.27.0" - "@esbuild/netbsd-arm64" "0.27.0" - "@esbuild/netbsd-x64" "0.27.0" - "@esbuild/openbsd-arm64" "0.27.0" - "@esbuild/openbsd-x64" "0.27.0" - "@esbuild/openharmony-arm64" "0.27.0" - "@esbuild/sunos-x64" "0.27.0" - "@esbuild/win32-arm64" "0.27.0" - "@esbuild/win32-ia32" "0.27.0" - "@esbuild/win32-x64" "0.27.0" + "@esbuild/aix-ppc64" "0.27.3" + "@esbuild/android-arm" "0.27.3" + "@esbuild/android-arm64" "0.27.3" + "@esbuild/android-x64" "0.27.3" + "@esbuild/darwin-arm64" "0.27.3" + "@esbuild/darwin-x64" "0.27.3" + "@esbuild/freebsd-arm64" "0.27.3" + "@esbuild/freebsd-x64" "0.27.3" + "@esbuild/linux-arm" "0.27.3" + "@esbuild/linux-arm64" "0.27.3" + "@esbuild/linux-ia32" "0.27.3" + "@esbuild/linux-loong64" "0.27.3" + "@esbuild/linux-mips64el" "0.27.3" + "@esbuild/linux-ppc64" "0.27.3" + "@esbuild/linux-riscv64" "0.27.3" + "@esbuild/linux-s390x" "0.27.3" + "@esbuild/linux-x64" "0.27.3" + "@esbuild/netbsd-arm64" "0.27.3" + "@esbuild/netbsd-x64" "0.27.3" + "@esbuild/openbsd-arm64" "0.27.3" + "@esbuild/openbsd-x64" "0.27.3" + "@esbuild/openharmony-arm64" "0.27.3" + "@esbuild/sunos-x64" "0.27.3" + "@esbuild/win32-arm64" "0.27.3" + "@esbuild/win32-ia32" "0.27.3" + "@esbuild/win32-x64" "0.27.3" escalade@^3.1.1: version "3.2.0" @@ -1261,11 +1175,13 @@ escodegen@^2.1.0: optionalDependencies: source-map "~0.6.1" -eslint-scope@^8.4.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" - integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== +eslint-scope@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-9.1.1.tgz#f6a209486e38bd28356b5feb07d445cc99c89967" + integrity sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw== dependencies: + "@types/esrecurse" "^4.3.1" + "@types/estree" "^1.0.8" esrecurse "^4.3.0" estraverse "^5.2.0" @@ -1279,32 +1195,34 @@ eslint-visitor-keys@^4.2.1: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== -eslint@^9.39.3: - version "9.39.3" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.3.tgz#08d63df1533d7743c0907b32a79a7e134e63ee2f" - integrity sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg== +eslint-visitor-keys@^5.0.0, eslint-visitor-keys@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be" + integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== + +eslint@^10.0.2: + version "10.0.2" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.0.2.tgz#1009263467591810320f2e1ad52b8a750d1acbab" + integrity sha512-uYixubwmqJZH+KLVYIVKY1JQt7tysXhtj21WSvjcSmU5SVNzMus1bgLe+pAt816yQ8opKfheVVoPLqvVMGejYw== dependencies: "@eslint-community/eslint-utils" "^4.8.0" - "@eslint-community/regexpp" "^4.12.1" - "@eslint/config-array" "^0.21.1" - "@eslint/config-helpers" "^0.4.2" - "@eslint/core" "^0.17.0" - "@eslint/eslintrc" "^3.3.1" - "@eslint/js" "9.39.3" - "@eslint/plugin-kit" "^0.4.1" + "@eslint-community/regexpp" "^4.12.2" + "@eslint/config-array" "^0.23.2" + "@eslint/config-helpers" "^0.5.2" + "@eslint/core" "^1.1.0" + "@eslint/plugin-kit" "^0.6.0" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" "@humanwhocodes/retry" "^0.4.2" "@types/estree" "^1.0.6" - ajv "^6.12.4" - chalk "^4.0.0" + ajv "^6.14.0" cross-spawn "^7.0.6" debug "^4.3.2" escape-string-regexp "^4.0.0" - eslint-scope "^8.4.0" - eslint-visitor-keys "^4.2.1" - espree "^10.4.0" - esquery "^1.5.0" + eslint-scope "^9.1.1" + eslint-visitor-keys "^5.0.1" + espree "^11.1.1" + esquery "^1.7.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^8.0.0" @@ -1314,12 +1232,11 @@ eslint@^9.39.3: imurmurhash "^0.1.4" is-glob "^4.0.0" json-stable-stringify-without-jsonify "^1.0.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" + minimatch "^10.2.1" natural-compare "^1.4.0" optionator "^0.9.3" -espree@^10.0.1, espree@^10.4.0: +espree@^10.4.0: version "10.4.0" resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== @@ -1328,12 +1245,21 @@ espree@^10.0.1, espree@^10.4.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^4.2.1" +espree@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-11.1.1.tgz#866f6bc9ccccd6f28876b7a6463abb281b9cb847" + integrity sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ== + dependencies: + acorn "^8.16.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^5.0.1" + esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.5.0: +esquery@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.7.0.tgz#08d048f261f0ddedb5bae95f46809463d9c9496d" integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== @@ -1370,6 +1296,13 @@ event-stream@^4.0.1: stream-combiner "^0.2.2" through "^2.3.8" +events-universal@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/events-universal/-/events-universal-1.0.1.tgz#b56a84fd611b6610e0a2d0f09f80fdf931e2dfe6" + integrity sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw== + dependencies: + bare-events "^2.7.0" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -1380,17 +1313,6 @@ fast-fifo@^1.3.2: resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== -fast-glob@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" - integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.8" - fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -1401,13 +1323,18 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fastq@^1.13.0, fastq@^1.6.0: - version "1.19.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" - integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== +fastq@^1.13.0: + version "1.20.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.20.1.tgz#ca750a10dc925bc8b18839fd203e3ef4b3ced675" + integrity sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw== dependencies: reusify "^1.0.4" +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + file-entry-cache@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" @@ -1415,13 +1342,6 @@ file-entry-cache@^8.0.0: dependencies: flat-cache "^4.0.0" -fill-range@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== - dependencies: - to-regex-range "^5.0.1" - find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -1444,16 +1364,16 @@ flat@^5.0.2: integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== flatted@^3.2.9: - version "3.3.3" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" - integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + version "3.3.4" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.4.tgz#0986e681008f0f13f58e18656c47967682db5ff6" + integrity sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA== -follow-redirects@^1.15.9: +follow-redirects@^1.15.11: version "1.15.11" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== -for-each@^0.3.3: +for-each@^0.3.3, for-each@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== @@ -1508,6 +1428,11 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +generator-function@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/generator-function/-/generator-function-2.0.1.tgz#0e75dd410d1243687a0ba2e951b94eedb8f737a2" + integrity sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g== + get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -1534,7 +1459,7 @@ get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@ hasown "^2.0.2" math-intrinsics "^1.1.0" -get-proto@^1.0.0, get-proto@^1.0.1: +get-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== @@ -1552,21 +1477,14 @@ get-symbol-description@^1.1.0: get-intrinsic "^1.2.6" get-uri@^6.0.1: - version "6.0.4" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.4.tgz#6daaee9e12f9759e19e55ba313956883ef50e0a7" - integrity sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ== + version "6.0.5" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.5.tgz#714892aa4a871db671abc5395e5e9447bc306a16" + integrity sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg== dependencies: basic-ftp "^5.0.2" data-uri-to-buffer "^6.0.2" debug "^4.3.4" -glob-parent@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" @@ -1621,11 +1539,6 @@ glob@^7.0.5: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" - integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== - globalthis@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" @@ -1644,11 +1557,6 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.10, graceful-fs@^4.2.11, graceful-fs@^4.2.8 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -graphemer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" - integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== - has-bigints@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" @@ -1740,18 +1648,10 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -ignore@^7.0.0: - version "7.0.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.4.tgz#a12c70d0f2607c5bf508fb65a40c75f037d7a078" - integrity sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A== - -import-fresh@^3.2.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" - integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" +ignore@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" + integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== imurmurhash@^0.1.4: version "0.1.4" @@ -1780,13 +1680,10 @@ internal-slot@^1.1.0: hasown "^2.0.2" side-channel "^1.1.0" -ip-address@^9.0.5: - version "9.0.5" - resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" - integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== - dependencies: - jsbn "1.1.0" - sprintf-js "^1.1.3" +ip-address@^10.0.1: + version "10.1.0" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.1.0.tgz#d8dcffb34d0e02eb241427444a6e23f5b0595aa4" + integrity sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q== is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: version "3.0.5" @@ -1833,7 +1730,7 @@ is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.16.0: +is-core-module@^2.16.1: version "2.16.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== @@ -1875,16 +1772,17 @@ is-fullwidth-code-point@^3.0.0: integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-generator-function@^1.0.10: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.0.tgz#bf3eeda931201394f57b5dba2800f91a238309ca" - integrity sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ== + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.2.tgz#ae3b61e3d5ea4e4839b90bad22b02335051a17d5" + integrity sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA== dependencies: - call-bound "^1.0.3" - get-proto "^1.0.0" + call-bound "^1.0.4" + generator-function "^2.0.0" + get-proto "^1.0.1" has-tostringtag "^1.0.2" safe-regex-test "^1.1.0" -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: +is-glob@^4.0.0, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -1901,6 +1799,11 @@ is-negated-glob@^1.0.0: resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" integrity sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug== +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + is-number-object@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" @@ -1909,11 +1812,6 @@ is-number-object@^1.1.1: call-bound "^1.0.3" has-tostringtag "^1.0.2" -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" @@ -1985,7 +1883,7 @@ is-weakmap@^2.0.2: resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== -is-weakref@^1.0.2, is-weakref@^1.1.0: +is-weakref@^1.0.2, is-weakref@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== @@ -2036,11 +1934,6 @@ js-yaml@^4.1.0, js-yaml@^4.1.1: dependencies: argparse "^2.0.1" -jsbn@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" - integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== - json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -2103,11 +1996,6 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" @@ -2171,34 +2059,14 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== -merge2@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -micromatch@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" - integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== - dependencies: - braces "^3.0.3" - picomatch "^2.3.1" - -minimatch@^10.0.1, minimatch@^10.2.2: +minimatch@^10.2.1, minimatch@^10.2.2, minimatch@^10.2.4: version "10.2.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.4.tgz#465b3accbd0218b8281f5301e27cedc697f96fde" integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg== dependencies: brace-expansion "^5.0.2" -minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^3.1.3: +minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: version "3.1.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== @@ -2206,18 +2074,13 @@ minimatch@^3.1.3: brace-expansion "^1.1.7" minimatch@^9.0.4, minimatch@^9.0.5: - version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + version "9.0.9" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e" + integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== dependencies: - brace-expansion "^2.0.1" - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4, minipass@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + brace-expansion "^2.0.2" -minipass@^7.1.3: +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4, minipass@^7.1.2, minipass@^7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== @@ -2234,7 +2097,7 @@ mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mocha@^11.1.0: +mocha@^11.7.5: version "11.7.5" resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.5.tgz#58f5bbfa5e0211ce7e5ee6128107cefc2515a627" integrity sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig== @@ -2267,9 +2130,9 @@ ms@^2.1.3: integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== nan@^2.17.0: - version "2.22.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.2.tgz#6b504fd029fb8f38c0990e52ad5c26772fdacfbb" - integrity sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ== + version "2.25.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.25.0.tgz#937ed345e63d9481362a7942d49c4860d27eeabd" + integrity sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g== natural-compare@^1.4.0: version "1.4.0" @@ -2291,7 +2154,7 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-pty@^1.0.0: +node-pty@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.0.0.tgz#7daafc0aca1c4ca3de15c61330373af4af5861fd" integrity sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA== @@ -2343,7 +2206,7 @@ npm-run-all@^4.1.5: shell-quote "^1.6.1" string.prototype.padend "^3.0.0" -object-inspect@^1.13.3: +object-inspect@^1.13.3, object-inspect@^1.13.4: version "1.13.4" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== @@ -2393,7 +2256,7 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" -p-all@^5.0.0: +p-all@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/p-all/-/p-all-5.0.1.tgz#688c4032f32d321fc69ef3d896aa5b2fe0b8406b" integrity sha512-LMT7WX9ZSaq3J1zjloApkIVmtz0ZdMFSIqbuiEa3txGYPLjUPOvgOPOx3nFjo+f37ZYL+1aY666I2SG7GVwLOA== @@ -2446,13 +2309,6 @@ package-json-from-dist@^1.0.0, package-json-from-dist@^1.0.1: resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" @@ -2526,7 +2382,7 @@ picocolors@^1.1.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== -picomatch@^2.0.4, picomatch@^2.3.1: +picomatch@^2.0.4: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -2595,11 +2451,6 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -2674,7 +2525,7 @@ reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: get-proto "^1.0.1" which-builtin-type "^1.2.1" -regexp.prototype.flags@^1.5.3: +regexp.prototype.flags@^1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== @@ -2701,11 +2552,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - resolve-options@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-2.0.0.tgz#a1a57a9949db549dd075de3f5550675f02f1e4c5" @@ -2714,11 +2560,11 @@ resolve-options@^2.0.0: value-or-function "^4.0.0" resolve@^1.10.0: - version "1.22.10" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" - integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + version "1.22.11" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== dependencies: - is-core-module "^2.16.0" + is-core-module "^2.16.1" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -2735,13 +2581,6 @@ rimraf@^6.1.3: glob "^13.0.3" package-json-from-dist "^1.0.1" -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - safe-array-concat@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" @@ -2790,10 +2629,10 @@ safe-regex-test@^1.1.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^7.6.0, semver@^7.7.1: - version "7.7.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" - integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== +semver@^7.7.3, semver@^7.7.4: + version "7.7.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== serialize-javascript@^6.0.2: version "6.0.2" @@ -2857,7 +2696,7 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.6.1, shell-quote@^1.8.2: +shell-quote@^1.6.1, shell-quote@^1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b" integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== @@ -2927,11 +2766,11 @@ socks-proxy-agent@^8.0.5: socks "^2.8.3" socks@^2.8.3: - version "2.8.4" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.4.tgz#07109755cdd4da03269bda4725baa061ab56d5cc" - integrity sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ== + version "2.8.7" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.7.tgz#e2fb1d9a603add75050a2067db8c381a0b5669ea" + integrity sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A== dependencies: - ip-address "^9.0.5" + ip-address "^10.0.1" smart-buffer "^4.2.0" source-map@~0.6.1: @@ -2961,9 +2800,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.21" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz#6d6e980c9df2b6fc905343a3b2d702a6239536c3" - integrity sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg== + version "3.0.23" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz#b069e687b1291a32f126893ed76a27a745ee2133" + integrity sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw== split@^1.0.1: version "1.0.1" @@ -2972,10 +2811,13 @@ split@^1.0.1: dependencies: through "2" -sprintf-js@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" - integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== +stop-iteration-iterator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" + integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== + dependencies: + es-errors "^1.3.0" + internal-slot "^1.1.0" stream-combiner@^0.2.2: version "0.2.2" @@ -3001,14 +2843,13 @@ stream-to-pull-stream@^1.7.3: pull-stream "^3.2.3" streamx@^2.12.0, streamx@^2.12.5, streamx@^2.13.2, streamx@^2.14.0: - version "2.22.0" - resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.22.0.tgz#cd7b5e57c95aaef0ff9b2aef7905afa62ec6e4a7" - integrity sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw== + version "2.23.0" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.23.0.tgz#7d0f3d00d4a6c5de5728aecd6422b4008d66fd0b" + integrity sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg== dependencies: + events-universal "^1.0.0" fast-fifo "^1.3.2" text-decoder "^1.1.0" - optionalDependencies: - bare-events "^2.2.0" "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" @@ -3113,11 +2954,11 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: ansi-regex "^5.0.1" strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + version "7.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3" + integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== dependencies: - ansi-regex "^6.0.1" + ansi-regex "^6.2.2" strip-bom@^3.0.0: version "3.0.0" @@ -3155,10 +2996,10 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -tar@^7.5.4: - version "7.5.8" - resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.8.tgz#6bbdfdb487914803d401bab6a2abb100aaa253d5" - integrity sha512-SYkBtK99u0yXa+IWL0JRzzcl7RxNpvX/U08Z+8DKnysfno7M+uExnTZH8K+VGgShf2qFPKtbNr9QBl8n7WBP6Q== +tar@^7.5.10: + version "7.5.10" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.10.tgz#2281541123f5507db38bc6eb22619f4bbaef73ad" + integrity sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw== dependencies: "@isaacs/fs-minipass" "^4.0.0" chownr "^3.0.0" @@ -3174,9 +3015,9 @@ teex@^1.0.1: streamx "^2.12.5" text-decoder@^1.1.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.3.tgz#b19da364d981b2326d5f43099c310cc80d770c65" - integrity sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA== + version "1.2.7" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.7.tgz#5d073a9a74b9c0a9d28dfadcab96b604af57d8ba" + integrity sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ== dependencies: b4a "^1.6.4" @@ -3198,12 +3039,13 @@ through@2, through@^2.3.8, through@~2.3, through@~2.3.4: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== +tinyglobby@^0.2.15: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== dependencies: - is-number "^7.0.0" + fdir "^6.5.0" + picomatch "^4.0.3" to-through@^3.0.0: version "3.0.0" @@ -3212,10 +3054,10 @@ to-through@^3.0.0: dependencies: streamx "^2.12.5" -ts-api-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" - integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== +ts-api-utils@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.4.0.tgz#2690579f96d2790253bdcf1ca35d569ad78f9ad8" + integrity sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA== ts-node@^10.9.2: version "10.9.2" @@ -3306,7 +3148,7 @@ typescript-formatter@^7.2.2: commandpost "^1.0.0" editorconfig "^0.15.0" -typescript@^5.8.2: +typescript@^5.9.3: version "5.9.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== @@ -3321,16 +3163,16 @@ unbox-primitive@^1.1.0: has-symbols "^1.1.0" which-boxed-primitive "^1.1.1" -undici-types@~6.20.0: - version "6.20.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" - integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== - undici-types@~6.21.0: version "6.21.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== +undici-types@~7.18.0: + version "7.18.2" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.18.2.tgz#29357a89e7b7ca4aef3bf0fd3fd0cd73884229e9" + integrity sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w== + untildify@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" @@ -3374,7 +3216,7 @@ vinyl-contents@^2.0.0: bl "^5.0.0" vinyl "^3.0.0" -vinyl-fs@^4.0.0: +vinyl-fs@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-4.0.2.tgz#d46557653e4a7109f29d626a9cf478680c7f8c70" integrity sha512-XRFwBLLTl8lRAOYiBqxY279wY46tVxLaRhSwo3GzKEuLz1giffsOquWWboD/haGf5lx+JyTigCFfe7DWHoARIA== @@ -3461,15 +3303,16 @@ which-collection@^1.0.2: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-typed-array@^1.1.16, which-typed-array@^1.1.18: - version "1.1.18" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.18.tgz#df2389ebf3fbb246a71390e90730a9edb6ce17ad" - integrity sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA== +which-typed-array@^1.1.16, which-typed-array@^1.1.19: + version "1.1.20" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.20.tgz#3fdb7adfafe0ea69157b1509f3a1cd892bd1d122" + integrity sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg== dependencies: available-typed-arrays "^1.0.7" call-bind "^1.0.8" - call-bound "^1.0.3" - for-each "^0.3.3" + call-bound "^1.0.4" + for-each "^0.3.5" + get-proto "^1.0.1" gopd "^1.2.0" has-tostringtag "^1.0.2" @@ -3493,9 +3336,9 @@ word-wrap@^1.2.5: integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== workerpool@^9.2.0: - version "9.3.2" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.3.2.tgz#4c045a8b437ae1bc70c646af11929a8b4d238656" - integrity sha512-Xz4Nm9c+LiBHhDR5bDLnNzmj6+5F+cyEAWPMkbs2awq/dYazR/efelZzUAjB/y3kNHL+uzkHvxVVpaOfGCPV7A== + version "9.3.4" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.3.4.tgz#f6c92395b2141afd78e2a889e80cb338fe9fca41" + integrity sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg== "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" From c66bf6bc7973fcfdba1f3a623e53b3fcf9151e23 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Mar 2026 12:53:46 +0100 Subject: [PATCH 59/88] Update version --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c933e0b..be31d1d0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ Notable changes. +## March 2026 + +### [0.84.0] +- Dependencies update. (https://github.com/devcontainers/cli/pull/1167) + ## February 2026 ### [0.83.3] diff --git a/package.json b/package.json index 475c2a28e..5aa304653 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.83.3", + "version": "0.84.0", "bin": { "devcontainer": "devcontainer.js" }, From 898e1423536868085f9f2c5236088399d4736eda Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Mar 2026 13:16:02 +0100 Subject: [PATCH 60/88] Adjust timeouts --- src/test/container-features/containerFeaturesOCIPush.test.ts | 2 +- src/test/container-features/containerFeaturesOrder.test.ts | 2 +- src/test/container-features/featureHelpers.test.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/container-features/containerFeaturesOCIPush.test.ts b/src/test/container-features/containerFeaturesOCIPush.test.ts index 76d743e65..b6672ffc9 100644 --- a/src/test/container-features/containerFeaturesOCIPush.test.ts +++ b/src/test/container-features/containerFeaturesOCIPush.test.ts @@ -307,7 +307,7 @@ registry`; // NOTE: // Test depends on https://github.com/orgs/codspace/packages/container/non-empty-config-layer%2Fcolor/225254837?tag=1.0.0 describe('Test OCI Push Helper Functions', function () { - this.timeout('10s'); + this.timeout('20s'); it('Generates the correct tgz manifest layer', async () => { const dataBytes = fs.readFileSync(`${testAssetsDir}/devcontainer-feature-color.tgz`); diff --git a/src/test/container-features/containerFeaturesOrder.test.ts b/src/test/container-features/containerFeaturesOrder.test.ts index e8b2cfdd9..8426a0946 100644 --- a/src/test/container-features/containerFeaturesOrder.test.ts +++ b/src/test/container-features/containerFeaturesOrder.test.ts @@ -47,7 +47,7 @@ async function setupInstallOrderTest(testWorkspaceFolder: string) { } describe('Feature Dependencies', function () { - this.timeout('10s'); + this.timeout('20s'); const baseTestConfigPath = `${__dirname}/configs/feature-dependencies`; describe('installsAfter', function () { diff --git a/src/test/container-features/featureHelpers.test.ts b/src/test/container-features/featureHelpers.test.ts index fb3c079ed..d4ceeb8a8 100644 --- a/src/test/container-features/featureHelpers.test.ts +++ b/src/test/container-features/featureHelpers.test.ts @@ -57,6 +57,7 @@ describe('validate processFeatureIdentifier', async function () { console.log(`workspaceRoot = ${workspaceRoot}, defaultConfigPath = ${defaultConfigPath}`); describe('VALID processFeatureIdentifier examples', async function () { + this.timeout('4s'); it('should process v1 local-cache', async function () { // Parsed out of a user's devcontainer.json From 347262404a8ea376097cd92cbd506543fe9cc88f Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Mar 2026 14:29:48 +0100 Subject: [PATCH 61/88] Pin compose plugin --- .github/workflows/test-docker-v20.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-docker-v20.yml b/.github/workflows/test-docker-v20.yml index 8c3aa21e8..0b35c363b 100644 --- a/.github/workflows/test-docker-v20.yml +++ b/.github/workflows/test-docker-v20.yml @@ -40,7 +40,10 @@ jobs: # See https://github.com/docker/buildx/issues/3654 BUILDX_VERSION=$(apt-cache madison docker-buildx-plugin | awk '{print $3}' | grep -v '^0\.3[1-9]\.' | head -1) echo "Installing docker-buildx-plugin=$BUILDX_VERSION" - sudo apt-get install -y docker-ce=5:20.10.* docker-ce-cli=5:20.10.* containerd.io docker-buildx-plugin="$BUILDX_VERSION" docker-compose-plugin + # Pin compose plugin to v2.x to avoid API incompatibility with Docker 20.10 + COMPOSE_VERSION=$(apt-cache madison docker-compose-plugin | awk '{print $3}' | grep '^2\.' | head -1) + echo "Installing docker-compose-plugin=$COMPOSE_VERSION" + sudo apt-get install -y docker-ce=5:20.10.* docker-ce-cli=5:20.10.* containerd.io docker-buildx-plugin="$BUILDX_VERSION" docker-compose-plugin="$COMPOSE_VERSION" sudo systemctl restart docker - name: Verify Docker version, Install and Test From 9207699460457b79cfeb960982aa40692f648ea1 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Mar 2026 14:45:58 +0100 Subject: [PATCH 62/88] Cleanup --- build/eslint/utils.js | 37 --------- build/eslint/utils.ts | 40 --------- build/eslint/vscode-dts-create-func.js | 35 -------- build/eslint/vscode-dts-create-func.ts | 40 --------- build/eslint/vscode-dts-event-naming.js | 81 ------------------ build/eslint/vscode-dts-event-naming.ts | 91 --------------------- build/eslint/vscode-dts-interface-naming.js | 30 ------- build/eslint/vscode-dts-interface-naming.ts | 35 -------- build/eslint/vscode-dts-literal-or-types.js | 23 ------ build/eslint/vscode-dts-literal-or-types.ts | 25 ------ 10 files changed, 437 deletions(-) delete mode 100644 build/eslint/utils.js delete mode 100644 build/eslint/utils.ts delete mode 100644 build/eslint/vscode-dts-create-func.js delete mode 100644 build/eslint/vscode-dts-create-func.ts delete mode 100644 build/eslint/vscode-dts-event-naming.js delete mode 100644 build/eslint/vscode-dts-event-naming.ts delete mode 100644 build/eslint/vscode-dts-interface-naming.js delete mode 100644 build/eslint/vscode-dts-interface-naming.ts delete mode 100644 build/eslint/vscode-dts-literal-or-types.js delete mode 100644 build/eslint/vscode-dts-literal-or-types.ts diff --git a/build/eslint/utils.js b/build/eslint/utils.js deleted file mode 100644 index c58e4e24b..000000000 --- a/build/eslint/utils.js +++ /dev/null @@ -1,37 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createImportRuleListener = void 0; -function createImportRuleListener(validateImport) { - function _checkImport(node) { - if (node && node.type === 'Literal' && typeof node.value === 'string') { - validateImport(node, node.value); - } - } - return { - // import ??? from 'module' - ImportDeclaration: (node) => { - _checkImport(node.source); - }, - // import('module').then(...) OR await import('module') - ['CallExpression[callee.type="Import"][arguments.length=1] > Literal']: (node) => { - _checkImport(node); - }, - // import foo = ... - ['TSImportEqualsDeclaration > TSExternalModuleReference > Literal']: (node) => { - _checkImport(node); - }, - // export ?? from 'module' - ExportAllDeclaration: (node) => { - _checkImport(node.source); - }, - // export {foo} from 'module' - ExportNamedDeclaration: (node) => { - _checkImport(node.source); - }, - }; -} -exports.createImportRuleListener = createImportRuleListener; diff --git a/build/eslint/utils.ts b/build/eslint/utils.ts deleted file mode 100644 index 428832e9c..000000000 --- a/build/eslint/utils.ts +++ /dev/null @@ -1,40 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; - -export function createImportRuleListener(validateImport: (node: TSESTree.Literal, value: string) => any): eslint.Rule.RuleListener { - - function _checkImport(node: TSESTree.Node | null) { - if (node && node.type === 'Literal' && typeof node.value === 'string') { - validateImport(node, node.value); - } - } - - return { - // import ??? from 'module' - ImportDeclaration: (node: any) => { - _checkImport((node).source); - }, - // import('module').then(...) OR await import('module') - ['CallExpression[callee.type="Import"][arguments.length=1] > Literal']: (node: any) => { - _checkImport(node); - }, - // import foo = ... - ['TSImportEqualsDeclaration > TSExternalModuleReference > Literal']: (node: any) => { - _checkImport(node); - }, - // export ?? from 'module' - ExportAllDeclaration: (node: any) => { - _checkImport((node).source); - }, - // export {foo} from 'module' - ExportNamedDeclaration: (node: any) => { - _checkImport((node).source); - }, - - }; -} diff --git a/build/eslint/vscode-dts-create-func.js b/build/eslint/vscode-dts-create-func.js deleted file mode 100644 index 5a27bf51c..000000000 --- a/build/eslint/vscode-dts-create-func.js +++ /dev/null @@ -1,35 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); -module.exports = new class ApiLiteralOrTypes { - constructor() { - this.meta = { - docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#creating-objects' }, - messages: { sync: '`createXYZ`-functions are constructor-replacements and therefore must return sync', } - }; - } - create(context) { - return { - ['TSDeclareFunction Identifier[name=/create.*/]']: (node) => { - var _a; - const decl = node.parent; - if (((_a = decl.returnType) === null || _a === void 0 ? void 0 : _a.typeAnnotation.type) !== experimental_utils_1.AST_NODE_TYPES.TSTypeReference) { - return; - } - if (decl.returnType.typeAnnotation.typeName.type !== experimental_utils_1.AST_NODE_TYPES.Identifier) { - return; - } - const ident = decl.returnType.typeAnnotation.typeName.name; - if (ident === 'Promise' || ident === 'Thenable') { - context.report({ - node, - messageId: 'sync' - }); - } - } - }; - } -}; diff --git a/build/eslint/vscode-dts-create-func.ts b/build/eslint/vscode-dts-create-func.ts deleted file mode 100644 index 295d099da..000000000 --- a/build/eslint/vscode-dts-create-func.ts +++ /dev/null @@ -1,40 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as eslint from 'eslint'; -import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; - -export = new class ApiLiteralOrTypes implements eslint.Rule.RuleModule { - - readonly meta: eslint.Rule.RuleMetaData = { - docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#creating-objects' }, - messages: { sync: '`createXYZ`-functions are constructor-replacements and therefore must return sync', } - }; - - create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - - return { - ['TSDeclareFunction Identifier[name=/create.*/]']: (node: any) => { - - const decl = (node).parent; - - if (decl.returnType?.typeAnnotation.type !== AST_NODE_TYPES.TSTypeReference) { - return; - } - if (decl.returnType.typeAnnotation.typeName.type !== AST_NODE_TYPES.Identifier) { - return; - } - - const ident = decl.returnType.typeAnnotation.typeName.name; - if (ident === 'Promise' || ident === 'Thenable') { - context.report({ - node, - messageId: 'sync' - }); - } - } - }; - } -}; diff --git a/build/eslint/vscode-dts-event-naming.js b/build/eslint/vscode-dts-event-naming.js deleted file mode 100644 index c93c18183..000000000 --- a/build/eslint/vscode-dts-event-naming.js +++ /dev/null @@ -1,81 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -var _a; -const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); -module.exports = new (_a = class ApiEventNaming { - constructor() { - this.meta = { - docs: { - url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#event-naming' - }, - messages: { - naming: 'Event names must follow this patten: `on[Did|Will]`', - verb: 'Unknown verb \'{{verb}}\' - is this really a verb? Iff so, then add this verb to the configuration', - subject: 'Unknown subject \'{{subject}}\' - This subject has not been used before but it should refer to something in the API', - unknown: 'UNKNOWN event declaration, lint-rule needs tweaking' - } - }; - } - create(context) { - const config = context.options[0]; - const allowed = new Set(config.allowed); - const verbs = new Set(config.verbs); - return { - ['TSTypeAnnotation TSTypeReference Identifier[name="Event"]']: (node) => { - var _a, _b; - const def = (_b = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.parent; - let ident; - if ((def === null || def === void 0 ? void 0 : def.type) === experimental_utils_1.AST_NODE_TYPES.Identifier) { - ident = def; - } - else if (((def === null || def === void 0 ? void 0 : def.type) === experimental_utils_1.AST_NODE_TYPES.TSPropertySignature || (def === null || def === void 0 ? void 0 : def.type) === experimental_utils_1.AST_NODE_TYPES.ClassProperty) && def.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { - ident = def.key; - } - if (!ident) { - // event on unknown structure... - return context.report({ - node, - message: 'unknown' - }); - } - if (allowed.has(ident.name)) { - // configured exception - return; - } - const match = ApiEventNaming._nameRegExp.exec(ident.name); - if (!match) { - context.report({ - node: ident, - messageId: 'naming' - }); - return; - } - // check that is spelled out (configured) as verb - if (!verbs.has(match[2].toLowerCase())) { - context.report({ - node: ident, - messageId: 'verb', - data: { verb: match[2] } - }); - } - // check that a subject (if present) has occurred - if (match[3]) { - const regex = new RegExp(match[3], 'ig'); - const parts = context.getSourceCode().getText().split(regex); - if (parts.length < 3) { - context.report({ - node: ident, - messageId: 'subject', - data: { subject: match[3] } - }); - } - } - } - }; - } - }, - _a._nameRegExp = /on(Did|Will)([A-Z][a-z]+)([A-Z][a-z]+)?/, - _a); diff --git a/build/eslint/vscode-dts-event-naming.ts b/build/eslint/vscode-dts-event-naming.ts deleted file mode 100644 index 6543c4586..000000000 --- a/build/eslint/vscode-dts-event-naming.ts +++ /dev/null @@ -1,91 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as eslint from 'eslint'; -import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; - -export = new class ApiEventNaming implements eslint.Rule.RuleModule { - - private static _nameRegExp = /on(Did|Will)([A-Z][a-z]+)([A-Z][a-z]+)?/; - - readonly meta: eslint.Rule.RuleMetaData = { - docs: { - url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#event-naming' - }, - messages: { - naming: 'Event names must follow this patten: `on[Did|Will]`', - verb: 'Unknown verb \'{{verb}}\' - is this really a verb? Iff so, then add this verb to the configuration', - subject: 'Unknown subject \'{{subject}}\' - This subject has not been used before but it should refer to something in the API', - unknown: 'UNKNOWN event declaration, lint-rule needs tweaking' - } - }; - - create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - - const config = <{ allowed: string[], verbs: string[] }>context.options[0]; - const allowed = new Set(config.allowed); - const verbs = new Set(config.verbs); - - return { - ['TSTypeAnnotation TSTypeReference Identifier[name="Event"]']: (node: any) => { - - const def = (node).parent?.parent?.parent; - let ident: TSESTree.Identifier | undefined; - - if (def?.type === AST_NODE_TYPES.Identifier) { - ident = def; - - } else if ((def?.type === AST_NODE_TYPES.TSPropertySignature || def?.type === AST_NODE_TYPES.ClassProperty) && def.key.type === AST_NODE_TYPES.Identifier) { - ident = def.key; - } - - if (!ident) { - // event on unknown structure... - return context.report({ - node, - message: 'unknown' - }); - } - - if (allowed.has(ident.name)) { - // configured exception - return; - } - - const match = ApiEventNaming._nameRegExp.exec(ident.name); - if (!match) { - context.report({ - node: ident, - messageId: 'naming' - }); - return; - } - - // check that is spelled out (configured) as verb - if (!verbs.has(match[2].toLowerCase())) { - context.report({ - node: ident, - messageId: 'verb', - data: { verb: match[2] } - }); - } - - // check that a subject (if present) has occurred - if (match[3]) { - const regex = new RegExp(match[3], 'ig'); - const parts = context.getSourceCode().getText().split(regex); - if (parts.length < 3) { - context.report({ - node: ident, - messageId: 'subject', - data: { subject: match[3] } - }); - } - } - } - }; - } -}; - diff --git a/build/eslint/vscode-dts-interface-naming.js b/build/eslint/vscode-dts-interface-naming.js deleted file mode 100644 index 70ca81082..000000000 --- a/build/eslint/vscode-dts-interface-naming.js +++ /dev/null @@ -1,30 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -var _a; -module.exports = new (_a = class ApiInterfaceNaming { - constructor() { - this.meta = { - messages: { - naming: 'Interfaces must not be prefixed with uppercase `I`', - } - }; - } - create(context) { - return { - ['TSInterfaceDeclaration Identifier']: (node) => { - const name = node.name; - if (ApiInterfaceNaming._nameRegExp.test(name)) { - context.report({ - node, - messageId: 'naming' - }); - } - } - }; - } - }, - _a._nameRegExp = /I[A-Z]/, - _a); diff --git a/build/eslint/vscode-dts-interface-naming.ts b/build/eslint/vscode-dts-interface-naming.ts deleted file mode 100644 index d9ec4e8c3..000000000 --- a/build/eslint/vscode-dts-interface-naming.ts +++ /dev/null @@ -1,35 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; - -export = new class ApiInterfaceNaming implements eslint.Rule.RuleModule { - - private static _nameRegExp = /I[A-Z]/; - - readonly meta: eslint.Rule.RuleMetaData = { - messages: { - naming: 'Interfaces must not be prefixed with uppercase `I`', - } - }; - - create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - - return { - ['TSInterfaceDeclaration Identifier']: (node: any) => { - - const name = (node).name; - if (ApiInterfaceNaming._nameRegExp.test(name)) { - context.report({ - node, - messageId: 'naming' - }); - } - } - }; - } -}; - diff --git a/build/eslint/vscode-dts-literal-or-types.js b/build/eslint/vscode-dts-literal-or-types.js deleted file mode 100644 index 02e6de876..000000000 --- a/build/eslint/vscode-dts-literal-or-types.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -module.exports = new class ApiLiteralOrTypes { - constructor() { - this.meta = { - docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#enums' }, - messages: { useEnum: 'Use enums, not literal-or-types', } - }; - } - create(context) { - return { - ['TSTypeAnnotation TSUnionType TSLiteralType']: (node) => { - context.report({ - node: node, - messageId: 'useEnum' - }); - } - }; - } -}; diff --git a/build/eslint/vscode-dts-literal-or-types.ts b/build/eslint/vscode-dts-literal-or-types.ts deleted file mode 100644 index 01a3eb215..000000000 --- a/build/eslint/vscode-dts-literal-or-types.ts +++ /dev/null @@ -1,25 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as eslint from 'eslint'; - -export = new class ApiLiteralOrTypes implements eslint.Rule.RuleModule { - - readonly meta: eslint.Rule.RuleMetaData = { - docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#enums' }, - messages: { useEnum: 'Use enums, not literal-or-types', } - }; - - create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - return { - ['TSTypeAnnotation TSUnionType TSLiteralType']: (node: any) => { - context.report({ - node: node, - messageId: 'useEnum' - }); - } - }; - } -}; From 9ee8cfbfb3f8f1fcb0658a4a927e0383da46654c Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 6 Mar 2026 18:25:10 +0100 Subject: [PATCH 63/88] Adjust timeouts --- src/test/cli.build.test.ts | 2 +- src/test/container-features/generateFeaturesConfig.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/cli.build.test.ts b/src/test/cli.build.test.ts index 51e079eb6..e2980ea19 100644 --- a/src/test/cli.build.test.ts +++ b/src/test/cli.build.test.ts @@ -14,7 +14,7 @@ import { envListToObj } from '../spec-node/utils'; const pkg = require('../../package.json'); describe('Dev Containers CLI', function () { - this.timeout('120s'); + this.timeout('240s'); const tmp = path.relative(process.cwd(), path.join(__dirname, 'tmp')); const cli = `npx --prefix ${tmp} devcontainer`; diff --git a/src/test/container-features/generateFeaturesConfig.test.ts b/src/test/container-features/generateFeaturesConfig.test.ts index 32647a71c..ac28931c8 100644 --- a/src/test/container-features/generateFeaturesConfig.test.ts +++ b/src/test/container-features/generateFeaturesConfig.test.ts @@ -88,7 +88,7 @@ RUN chmod -R 0755 /tmp/dev-container-features/hello_1 \\ }); it('should correctly return featuresConfig with customizations', async function () { - this.timeout('20s'); + this.timeout('40s'); const version = 'unittest'; const tmpFolder: string = path.join(await getLocalCacheFolder(), 'container-features', `${version}-${Date.now()}`); await mkdirpLocal(tmpFolder); From 0538e6134d4b44c76aee31f7f127c490fdf1fe63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 11:39:23 +0000 Subject: [PATCH 64/88] Bump tar from 7.5.10 to 7.5.11 Bumps [tar](https://github.com/isaacs/node-tar) from 7.5.10 to 7.5.11. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v7.5.10...v7.5.11) --- updated-dependencies: - dependency-name: tar dependency-version: 7.5.11 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1cbecd028..ec61aa8d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2997,9 +2997,9 @@ supports-preserve-symlinks-flag@^1.0.0: integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== tar@^7.5.10: - version "7.5.10" - resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.10.tgz#2281541123f5507db38bc6eb22619f4bbaef73ad" - integrity sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw== + version "7.5.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.11.tgz#1250fae45d98806b36d703b30973fa8e0a6d8868" + integrity sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ== dependencies: "@isaacs/fs-minipass" "^4.0.0" chownr "^3.0.0" From 903084e3d734cfce61626334d37fa6e2373fc3b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 07:27:44 +0000 Subject: [PATCH 65/88] Bump actions/upload-artifact from 5 to 7 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v5...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/dev-containers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev-containers.yml b/.github/workflows/dev-containers.yml index aadadf1d5..c9b490854 100644 --- a/.github/workflows/dev-containers.yml +++ b/.github/workflows/dev-containers.yml @@ -37,7 +37,7 @@ jobs: echo "TGZ=devcontainers-cli-${VERSION}.tgz" | tee -a $GITHUB_ENV echo "TGZ_UPLOAD=devcontainers-cli-${VERSION}-${GITHUB_SHA:0:8}.tgz" | tee -a $GITHUB_ENV - name: Store TGZ - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: ${{ env.TGZ_UPLOAD }} path: ${{ env.TGZ }} From 71fb1401a9706133637c03dfaf2ad7d595952c7d Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 16 Mar 2026 14:53:11 +0100 Subject: [PATCH 66/88] 0.84.1 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be31d1d0f..950241b95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Notable changes. ## March 2026 +### [0.84.1] +- Bump tar from 7.5.10 to 7.5.11 due to [CVE-2026-31802](https://github.com/advisories/GHSA-9ppj-qmqm-q256). (https://github.com/devcontainers/cli/pull/1174) + ### [0.84.0] - Dependencies update. (https://github.com/devcontainers/cli/pull/1167) diff --git a/package.json b/package.json index 5aa304653..a79e91782 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.84.0", + "version": "0.84.1", "bin": { "devcontainer": "devcontainer.js" }, From 217ae9c97b0c5f47a123d9dc291e94232dec3f5f Mon Sep 17 00:00:00 2001 From: Paul Taylor <178183+trxcllnt@users.noreply.github.com> Date: Tue, 17 Mar 2026 02:41:42 -0700 Subject: [PATCH 67/88] Inline buildx global build and target platform envvars when resolving base image and user (#1169) --- src/spec-node/configContainer.ts | 2 +- src/spec-node/devContainers.ts | 16 +++++-- src/spec-node/devContainersSpecCLI.ts | 12 +++--- src/spec-node/dockerCompose.ts | 4 +- src/spec-node/dockerfileUtils.ts | 24 +++++------ src/spec-node/imageMetadata.ts | 30 ++++++++++--- src/spec-node/upgradeCommand.ts | 10 +++-- src/spec-node/utils.ts | 5 ++- src/spec-shutdown/dockerUtils.ts | 3 +- src/test/dockerfileUtils.test.ts | 61 ++++++++++++++++++++------- src/test/testUtils.ts | 10 +++-- 11 files changed, 122 insertions(+), 55 deletions(-) diff --git a/src/spec-node/configContainer.ts b/src/spec-node/configContainer.ts index b4e159657..c0b21eb82 100644 --- a/src/spec-node/configContainer.ts +++ b/src/spec-node/configContainer.ts @@ -60,7 +60,7 @@ async function resolveWithLocalFolder(params: DockerResolverParameters, parsedAu const { dockerCLI, dockerComposeCLI } = params; const { env } = common; - const cliParams: DockerCLIParameters = { cliHost, dockerCLI, dockerComposeCLI, env, output, platformInfo: params.platformInfo }; + const cliParams: DockerCLIParameters = { cliHost, dockerCLI, dockerComposeCLI, env, output, buildPlatformInfo: params.buildPlatformInfo, targetPlatformInfo: params.targetPlatformInfo }; await ensureNoDisallowedFeatures(cliParams, config, additionalFeatures, idLabels); await runInitializeCommand({ ...params, common: { ...common, output: common.lifecycleHook.output } }, config.initializeCommand, common.lifecycleHook.onDidInput); diff --git a/src/spec-node/devContainers.ts b/src/spec-node/devContainers.ts index 8a2e6b042..a856890c6 100644 --- a/src/spec-node/devContainers.ts +++ b/src/spec-node/devContainers.ts @@ -172,7 +172,12 @@ export async function createDockerParams(options: ProvisionOptions, disposables: output: common.output, }, dockerPath, dockerComposePath); - const platformInfo = (() => { + const buildPlatformInfo = { + os: mapNodeOSToGOOS(cliHost.platform), + arch: mapNodeArchitectureToGOARCH(cliHost.arch), + }; + + const targetPlatformInfo = (() => { if (common.buildxPlatform) { const slash1 = common.buildxPlatform.indexOf('/'); const slash2 = common.buildxPlatform.indexOf('/', slash1 + 1); @@ -204,7 +209,8 @@ export async function createDockerParams(options: ProvisionOptions, disposables: dockerComposeCLI, env: cliHost.env, output, - platformInfo + buildPlatformInfo, + targetPlatformInfo })); const dockerEngineVer = await dockerEngineVersion({ @@ -213,7 +219,8 @@ export async function createDockerParams(options: ProvisionOptions, disposables: dockerComposeCLI, env: cliHost.env, output, - platformInfo + buildPlatformInfo, + targetPlatformInfo }); return { @@ -246,7 +253,8 @@ export async function createDockerParams(options: ProvisionOptions, disposables: additionalLabels: options.additionalLabels, buildxOutput: common.buildxOutput, buildxCacheTo: common.buildxCacheTo, - platformInfo + buildPlatformInfo, + targetPlatformInfo }; } diff --git a/src/spec-node/devContainersSpecCLI.ts b/src/spec-node/devContainersSpecCLI.ts index fbd501591..18c44136b 100644 --- a/src/spec-node/devContainersSpecCLI.ts +++ b/src/spec-node/devContainersSpecCLI.ts @@ -641,7 +641,7 @@ async function doBuild({ throw new ContainerError({ description: '--push true cannot be used with --output.' }); } - const buildParams: DockerCLIParameters = { cliHost, dockerCLI: params.dockerCLI, dockerComposeCLI, env, output, platformInfo: params.platformInfo }; + const buildParams: DockerCLIParameters = { cliHost, dockerCLI: params.dockerCLI, dockerComposeCLI, env, output, buildPlatformInfo: params.buildPlatformInfo, targetPlatformInfo: params.targetPlatformInfo }; await ensureNoDisallowedFeatures(buildParams, config, additionalFeatures, undefined); // Support multiple use of `--image-name` @@ -1058,16 +1058,18 @@ async function readConfiguration({ env: cliHost.env, output, }, dockerCLI, dockerComposePath || 'docker-compose'); + const buildPlatformInfo = { + os: mapNodeOSToGOOS(cliHost.platform), + arch: mapNodeArchitectureToGOARCH(cliHost.arch), + }; const params: DockerCLIParameters = { cliHost, dockerCLI, dockerComposeCLI, env: cliHost.env, output, - platformInfo: { - os: mapNodeOSToGOOS(cliHost.platform), - arch: mapNodeArchitectureToGOARCH(cliHost.arch), - } + buildPlatformInfo, + targetPlatformInfo: buildPlatformInfo }; const { container, idLabels } = await findContainerAndIdLabels(params, containerId, providedIdLabels, workspaceFolder, configPath?.fsPath); if (container) { diff --git a/src/spec-node/dockerCompose.ts b/src/spec-node/dockerCompose.ts index 4c20ebd68..8093464cc 100644 --- a/src/spec-node/dockerCompose.ts +++ b/src/spec-node/dockerCompose.ts @@ -27,7 +27,7 @@ const serviceLabel = 'com.docker.compose.service'; export async function openDockerComposeDevContainer(params: DockerResolverParameters, workspace: Workspace, config: SubstitutedConfig, idLabels: string[], additionalFeatures: Record>): Promise { const { common, dockerCLI, dockerComposeCLI } = params; const { cliHost, env, output } = common; - const buildParams: DockerCLIParameters = { cliHost, dockerCLI, dockerComposeCLI, env, output, platformInfo: params.platformInfo }; + const buildParams: DockerCLIParameters = { cliHost, dockerCLI, dockerComposeCLI, env, output, buildPlatformInfo: params.buildPlatformInfo, targetPlatformInfo: params.targetPlatformInfo }; return _openDockerComposeDevContainer(params, buildParams, workspace, config, getRemoteWorkspaceFolder(config.config), idLabels, additionalFeatures); } @@ -155,7 +155,7 @@ export async function buildAndExtendDockerCompose(configWithRaw: SubstitutedConf const { cliHost, env, output } = common; const { config } = configWithRaw; - const cliParams: DockerCLIParameters = { cliHost, dockerCLI, dockerComposeCLI: dockerComposeCLIFunc, env, output, platformInfo: params.platformInfo }; + const cliParams: DockerCLIParameters = { cliHost, dockerCLI, dockerComposeCLI: dockerComposeCLIFunc, env, output, buildPlatformInfo: params.buildPlatformInfo, targetPlatformInfo: params.targetPlatformInfo }; const composeConfig = await readDockerComposeConfig(cliParams, localComposeFiles, envFile); const composeService = composeConfig.services[config.service]; diff --git a/src/spec-node/dockerfileUtils.ts b/src/spec-node/dockerfileUtils.ts index 2a07023aa..532324f2b 100644 --- a/src/spec-node/dockerfileUtils.ts +++ b/src/spec-node/dockerfileUtils.ts @@ -81,7 +81,7 @@ export function extractDockerfile(dockerfile: string): Dockerfile { } as Dockerfile; } -export function findUserStatement(dockerfile: Dockerfile, buildArgs: Record, baseImageEnv: Record, target: string | undefined) { +export function findUserStatement(dockerfile: Dockerfile, buildArgs: Record, baseImageEnv: Record, globalBuildxPlatformArgs: Record = {}, target: string | undefined) { let stage: Stage | undefined = target ? dockerfile.stagesByLabel[target] : dockerfile.stages[dockerfile.stages.length - 1]; const seen = new Set(); while (stage) { @@ -92,15 +92,15 @@ export function findUserStatement(dockerfile: Dockerfile, buildArgs: Record i.instruction === 'USER'); if (i !== -1) { - return replaceVariables(dockerfile, buildArgs, baseImageEnv, stage.instructions[i].name, stage, i) || undefined; + return replaceVariables(dockerfile, buildArgs, baseImageEnv, globalBuildxPlatformArgs, stage.instructions[i].name, stage, i) || undefined; } - const image = replaceVariables(dockerfile, buildArgs, baseImageEnv, stage.from.image, dockerfile.preamble, dockerfile.preamble.instructions.length); + const image = replaceVariables(dockerfile, buildArgs, baseImageEnv, globalBuildxPlatformArgs, stage.from.image, dockerfile.preamble, dockerfile.preamble.instructions.length); stage = dockerfile.stagesByLabel[image]; } return undefined; } -export function findBaseImage(dockerfile: Dockerfile, buildArgs: Record, target: string | undefined) { +export function findBaseImage(dockerfile: Dockerfile, buildArgs: Record, target: string | undefined, globalBuildxPlatformArgs: Record = {}) { let stage: Stage | undefined = target ? dockerfile.stagesByLabel[target] : dockerfile.stages[dockerfile.stages.length - 1]; const seen = new Set(); while (stage) { @@ -109,7 +109,7 @@ export function findBaseImage(dockerfile: Dockerfile, buildArgs: Record, baseImageEnv: Record, str: string, stage: { from?: From; instructions: Instruction[] }, beforeInstructionIndex: number) { +function replaceVariables(dockerfile: Dockerfile, buildArgs: Record, baseImageEnv: Record, globalBuildxPlatformArgs: Record = {}, str: string, stage: { from?: From; instructions: Instruction[] }, beforeInstructionIndex: number) { return [...str.matchAll(argumentExpression)] .map(match => { const variable = match.groups!.variable; const isVarExp = match.groups!.isVarExp ? true : false; - let value = findValue(dockerfile, buildArgs, baseImageEnv, variable, stage, beforeInstructionIndex) || ''; + let value = findValue(dockerfile, buildArgs, baseImageEnv, globalBuildxPlatformArgs, variable, stage, beforeInstructionIndex) || ''; if (isVarExp) { // Handle replacing variable expressions (${var:+word}) if they exist const option = match.groups!.option; @@ -178,7 +178,7 @@ function replaceVariables(dockerfile: Dockerfile, buildArgs: Record str.substring(0, begin) + value + str.substring(end), str); } -function findValue(dockerfile: Dockerfile, buildArgs: Record, baseImageEnv: Record, variable: string, stage: { from?: From; instructions: Instruction[] }, beforeInstructionIndex: number): string | undefined { +function findValue(dockerfile: Dockerfile, buildArgs: Record, baseImageEnv: Record, globalBuildxPlatformArgs: Record = {}, variable: string, stage: { from?: From; instructions: Instruction[] }, beforeInstructionIndex: number): string | undefined { let considerArg = true; const seen = new Set(); while (true) { @@ -191,22 +191,22 @@ function findValue(dockerfile: Dockerfile, buildArgs: Record, ba if (i !== -1) { const instruction = stage.instructions[i]; if (instruction.instruction === 'ENV') { - return replaceVariables(dockerfile, buildArgs, baseImageEnv, instruction.value!, stage, i); + return replaceVariables(dockerfile, buildArgs, baseImageEnv, globalBuildxPlatformArgs, instruction.value!, stage, i); } if (instruction.instruction === 'ARG') { - return replaceVariables(dockerfile, buildArgs, baseImageEnv, buildArgs[instruction.name] ?? instruction.value, stage, i); + return replaceVariables(dockerfile, buildArgs, baseImageEnv, globalBuildxPlatformArgs, buildArgs[instruction.name] ?? instruction.value, stage, i); } } if (!stage.from) { - const value = baseImageEnv[variable]; + const value = baseImageEnv[variable] ?? globalBuildxPlatformArgs[variable]; if (typeof value === 'string') { return value; } return undefined; } - const image = replaceVariables(dockerfile, buildArgs, baseImageEnv, stage.from.image, dockerfile.preamble, dockerfile.preamble.instructions.length); + const image = replaceVariables(dockerfile, buildArgs, baseImageEnv, globalBuildxPlatformArgs, stage.from.image, dockerfile.preamble, dockerfile.preamble.instructions.length); stage = dockerfile.stagesByLabel[image] || dockerfile.preamble; beforeInstructionIndex = stage.instructions.length; considerArg = stage === dockerfile.preamble; diff --git a/src/spec-node/imageMetadata.ts b/src/spec-node/imageMetadata.ts index cb3cc5fa6..70a913965 100644 --- a/src/spec-node/imageMetadata.ts +++ b/src/spec-node/imageMetadata.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ContainerError } from '../spec-common/errors'; +import { PlatformInfo } from '../spec-common/commonUtils'; import { LifecycleCommand, LifecycleHooksInstallMap } from '../spec-common/injectHeadless'; import { DevContainerConfig, DevContainerConfigCommand, DevContainerFromDockerComposeConfig, DevContainerFromDockerfileConfig, DevContainerFromImageConfig, getDockerComposeFilePaths, getDockerfilePath, HostGPURequirements, HostRequirements, isDockerFileConfig, PortAttributes, UserEnvProbe } from '../spec-configuration/configuration'; import { Feature, FeaturesConfig, Mount, parseMount, SchemaFeatureLifecycleHooks } from '../spec-configuration/containerFeaturesConfiguration'; @@ -349,7 +350,7 @@ export async function getImageBuildInfo(params: DockerResolverParameters | Docke const cwdEnvFile = cliHost.path.join(cliHost.cwd, '.env'); const envFile = Array.isArray(config.dockerComposeFile) && config.dockerComposeFile.length === 0 && await cliHost.isFile(cwdEnvFile) ? cwdEnvFile : undefined; const composeFiles = await getDockerComposeFilePaths(cliHost, config, cliHost.env, cliHost.cwd); - const buildParams: DockerCLIParameters = { cliHost, dockerCLI, dockerComposeCLI, env: cliHost.env, output, platformInfo: params.platformInfo }; + const buildParams: DockerCLIParameters = { cliHost, dockerCLI, dockerComposeCLI, env: cliHost.env, output, buildPlatformInfo: params.buildPlatformInfo, targetPlatformInfo: params.targetPlatformInfo }; const composeConfig = await readDockerComposeConfig(buildParams, composeFiles, envFile); const services = Object.keys(composeConfig.services || {}); @@ -394,18 +395,37 @@ export async function getImageBuildInfoFromImage(params: DockerResolverParameter export async function getImageBuildInfoFromDockerfile(params: DockerResolverParameters | DockerCLIParameters, dockerfile: string, dockerBuildArgs: Record, targetStage: string | undefined, substitute: SubstituteConfig) { const { output } = 'output' in params ? params : params.common; const omitSyntaxDirective = 'common' in params ? !!params.common.omitSyntaxDirective : false; - return internalGetImageBuildInfoFromDockerfile(imageName => inspectDockerImage(params, imageName, true), dockerfile, dockerBuildArgs, targetStage, substitute, output, omitSyntaxDirective); + return internalGetImageBuildInfoFromDockerfile(imageName => inspectDockerImage(params, imageName, true), dockerfile, dockerBuildArgs, targetStage, substitute, output, omitSyntaxDirective, params.buildPlatformInfo, params.targetPlatformInfo); } -export async function internalGetImageBuildInfoFromDockerfile(inspectDockerImage: (imageName: string) => Promise, dockerfileText: string, dockerBuildArgs: Record, targetStage: string | undefined, substitute: SubstituteConfig, output: Log, omitSyntaxDirective: boolean): Promise { +export async function internalGetImageBuildInfoFromDockerfile(inspectDockerImage: (imageName: string) => Promise, dockerfileText: string, dockerBuildArgs: Record, targetStage: string | undefined, substitute: SubstituteConfig, output: Log, omitSyntaxDirective: boolean, buildPlatform: PlatformInfo, targetPlatform: PlatformInfo): Promise { const dockerfile = extractDockerfile(dockerfileText); if (dockerfile.preamble.directives.syntax && omitSyntaxDirective) { output.write(`Omitting syntax directive '${dockerfile.preamble.directives.syntax}' from Dockerfile.`, LogLevel.Trace); delete dockerfile.preamble.directives.syntax; } - const baseImage = findBaseImage(dockerfile, dockerBuildArgs, targetStage); + // https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#automatic-platform-args-in-the-global-scope + const globalBuildxPlatformArgs = { + // platform of the node performing the build. + BUILDPLATFORM: [buildPlatform.os, buildPlatform.arch, buildPlatform.variant].filter(Boolean).join("/"), + // OS component of BUILDPLATFORM + BUILDOS: buildPlatform.os, + // architecture component of BUILDPLATFORM + BUILDARCH: buildPlatform.arch, + // variant component of BUILDPLATFORM + BUILDVARIANT: buildPlatform.variant ?? "", + // platform of the build result. Eg linux/amd64, linux/arm/v7, windows/amd64. + TARGETPLATFORM: [targetPlatform.os, targetPlatform.arch, targetPlatform.variant].filter(Boolean).join("/"), + // OS component of TARGETPLATFORM + TARGETOS: targetPlatform.os, + // architecture component of TARGETPLATFORM + TARGETARCH: targetPlatform.arch, + // variant component of TARGETPLATFORM + TARGETVARIANT: targetPlatform.variant ?? "", + }; + const baseImage = findBaseImage(dockerfile, dockerBuildArgs, targetStage, globalBuildxPlatformArgs); const imageDetails = baseImage && await inspectDockerImage(baseImage) || undefined; - const dockerfileUser = findUserStatement(dockerfile, dockerBuildArgs, envListToObj(imageDetails?.Config.Env), targetStage); + const dockerfileUser = findUserStatement(dockerfile, dockerBuildArgs, envListToObj(imageDetails?.Config.Env), globalBuildxPlatformArgs, targetStage); const user = dockerfileUser || imageDetails?.Config.User || 'root'; const metadata = imageDetails ? getImageMetadata(imageDetails, substitute, output) : { config: [], raw: [], substitute }; return { diff --git a/src/spec-node/upgradeCommand.ts b/src/spec-node/upgradeCommand.ts index dfdf8e7e7..51410de07 100644 --- a/src/spec-node/upgradeCommand.ts +++ b/src/spec-node/upgradeCommand.ts @@ -86,16 +86,18 @@ async function featuresUpgrade({ env: cliHost.env, output, }, dockerPath, dockerComposePath); + const buildPlatformInfo = { + os: mapNodeOSToGOOS(cliHost.platform), + arch: mapNodeArchitectureToGOARCH(cliHost.arch), + }; const dockerParams: DockerCLIParameters = { cliHost, dockerCLI: dockerPath, dockerComposeCLI, env: cliHost.env, output, - platformInfo: { - os: mapNodeOSToGOOS(cliHost.platform), - arch: mapNodeArchitectureToGOARCH(cliHost.arch), - } + buildPlatformInfo, + targetPlatformInfo: buildPlatformInfo, }; const workspace = workspaceFromPath(cliHost.path, workspaceFolder); diff --git a/src/spec-node/utils.ts b/src/spec-node/utils.ts index bdf5289f0..74817d1a0 100644 --- a/src/spec-node/utils.ts +++ b/src/spec-node/utils.ts @@ -134,7 +134,8 @@ export interface DockerResolverParameters { additionalLabels: string[]; buildxOutput: string | undefined; buildxCacheTo: string | undefined; - platformInfo: PlatformInfo; + buildPlatformInfo: PlatformInfo; + targetPlatformInfo: PlatformInfo; } export interface ResolverResult { @@ -250,7 +251,7 @@ export async function inspectDockerImage(params: DockerResolverParameters | Dock throw inspectErr; } try { - return await inspectImageInRegistry(output, params.platformInfo, imageName); + return await inspectImageInRegistry(output, params.targetPlatformInfo, imageName); } catch (inspectErr2) { output.write(`Error fetching image details: ${inspectErr2?.message}`, LogLevel.Info); } diff --git a/src/spec-shutdown/dockerUtils.ts b/src/spec-shutdown/dockerUtils.ts index 3aa4d8166..9f0bce850 100644 --- a/src/spec-shutdown/dockerUtils.ts +++ b/src/spec-shutdown/dockerUtils.ts @@ -52,7 +52,8 @@ export interface DockerCLIParameters { dockerComposeCLI: () => Promise; env: NodeJS.ProcessEnv; output: Log; - platformInfo: PlatformInfo; + buildPlatformInfo: PlatformInfo; + targetPlatformInfo: PlatformInfo; } export interface PartialExecParameters { diff --git a/src/test/dockerfileUtils.test.ts b/src/test/dockerfileUtils.test.ts index 979375b91..1811f1ce2 100644 --- a/src/test/dockerfileUtils.test.ts +++ b/src/test/dockerfileUtils.test.ts @@ -178,7 +178,7 @@ FROM ubuntu:latest as dev const info = await internalGetImageBuildInfoFromDockerfile(async (imageName) => { assert.strictEqual(imageName, 'ubuntu:latest'); return details; - }, dockerfile, {}, undefined, testSubstitute, nullLog, false); + }, dockerfile, {}, undefined, testSubstitute, nullLog, false, { os: 'linux', arch: 'arm64' }, { os: 'linux', arch: 'amd64' }); assert.strictEqual(info.user, 'imageUser'); assert.strictEqual(info.metadata.config.length, 1); assert.strictEqual(info.metadata.config[0].id, 'testid-substituted'); @@ -206,11 +206,42 @@ USER dockerfileUserB const info = await internalGetImageBuildInfoFromDockerfile(async (imageName) => { assert.strictEqual(imageName, 'ubuntu:latest'); return details; - }, dockerfile, {}, undefined, testSubstitute, nullLog, false); + }, dockerfile, {}, undefined, testSubstitute, nullLog, false, { os: 'linux', arch: 'arm64' }, { os: 'linux', arch: 'amd64' }); assert.strictEqual(info.user, 'dockerfileUserB'); assert.strictEqual(info.metadata.config.length, 0); assert.strictEqual(info.metadata.raw.length, 0); }); + + it('for a USER in a multiarch image', async () => { + const dockerfile = ` +FROM ubuntu:latest as base-amd64 +USER amd64_user + +FROM ubuntu:latest as base-arm64 +USER arm64_user + +FROM base-\${TARGETARCH} +`; + const details: ImageDetails = { + Id: '123', + Config: { + User: 'imageUser', + Env: null, + Labels: null, + Entrypoint: null, + Cmd: null + }, + Os: 'linux', + Architecture: 'amd64' + }; + const info = await internalGetImageBuildInfoFromDockerfile(async (imageName) => { + assert.strictEqual(imageName, 'ubuntu:latest'); + return details; + }, dockerfile, {}, undefined, testSubstitute, nullLog, false, { os: 'linux', arch: 'arm64' }, { os: 'linux', arch: 'amd64' }); + assert.strictEqual(info.user, 'amd64_user'); + assert.strictEqual(info.metadata.config.length, 0); + assert.strictEqual(info.metadata.raw.length, 0); + }); }); describe('findBaseImage', () => { @@ -391,7 +422,7 @@ describe('findUserStatement', () => { USER user1 `; const extracted = extractDockerfile(dockerfile); - const user = findUserStatement(extracted, {}, {}, undefined); + const user = findUserStatement(extracted, {}, {}, {}, undefined); assert.strictEqual(user, 'user1'); }); @@ -401,7 +432,7 @@ ARG IMAGE_USER=user2 USER $IMAGE_USER `; const extracted = extractDockerfile(dockerfile); - const user = findUserStatement(extracted, {}, {}, undefined); + const user = findUserStatement(extracted, {}, {}, {}, undefined); assert.strictEqual(user, 'user2'); }); @@ -413,7 +444,7 @@ USER $IMAGE_USER const extracted = extractDockerfile(dockerfile); const user = findUserStatement(extracted, { IMAGE_USER: 'user3' - }, {}, undefined); + }, {}, {}, undefined); assert.strictEqual(user, 'user3'); }); @@ -429,7 +460,7 @@ FROM image4 as stage4 USER user4 `; const extracted = extractDockerfile(dockerfile); - const image = findUserStatement(extracted, {}, {}, 'stage2'); + const image = findUserStatement(extracted, {}, {}, {}, 'stage2'); assert.strictEqual(image, 'user3_2'); }); @@ -441,7 +472,7 @@ ARG USERNAME=user2 USER \${USERNAME} `; const extracted = extractDockerfile(dockerfile); - const user = findUserStatement(extracted, {}, {}, undefined); + const user = findUserStatement(extracted, {}, {}, {}, undefined); assert.strictEqual(user, 'user2'); }); @@ -455,7 +486,7 @@ FROM one as two USER \${USERNAME} `; const extracted = extractDockerfile(dockerfile); - const user = findUserStatement(extracted, {}, {}, undefined); + const user = findUserStatement(extracted, {}, {}, {}, undefined); assert.strictEqual(user, 'user1'); }); @@ -466,7 +497,7 @@ FROM debian USER \${USERNAME} `; const extracted = extractDockerfile(dockerfile); - const user = findUserStatement(extracted, {}, {}, undefined); + const user = findUserStatement(extracted, {}, {}, {}, undefined); assert.strictEqual(user, 'user1'); }); @@ -478,7 +509,7 @@ ARG USERNAME USER \${USERNAME} `; const extracted = extractDockerfile(dockerfile); - const user = findUserStatement(extracted, {}, {}, undefined); + const user = findUserStatement(extracted, {}, {}, {}, undefined); assert.strictEqual(user, 'user1'); }); @@ -488,7 +519,7 @@ FROM debian USER \${USERNAME} `; const extracted = extractDockerfile(dockerfile); - const user = findUserStatement(extracted, {}, {}, undefined); + const user = findUserStatement(extracted, {}, {}, {}, undefined); assert.strictEqual(user, undefined); }); @@ -500,7 +531,7 @@ ENV USERNAME=user2 USER \${USERNAME} `; const extracted = extractDockerfile(dockerfile); - const user = findUserStatement(extracted, {}, {}, undefined); + const user = findUserStatement(extracted, {}, {}, {}, undefined); assert.strictEqual(user, 'user2'); }); @@ -512,7 +543,7 @@ ENV USERNAME2=\${USERNAME1} USER \${USERNAME2} `; const extracted = extractDockerfile(dockerfile); - const user = findUserStatement(extracted, {}, {}, undefined); + const user = findUserStatement(extracted, {}, {}, {}, undefined); assert.strictEqual(user, 'user1'); }); @@ -524,7 +555,7 @@ ENV USERNAME2=user2 USER A\${USERNAME1}A\${USERNAME2}A `; const extracted = extractDockerfile(dockerfile); - const user = findUserStatement(extracted, {}, {}, undefined); + const user = findUserStatement(extracted, {}, {}, {}, undefined); assert.strictEqual(user, 'Auser1Auser2A'); }); @@ -534,7 +565,7 @@ FROM mybase USER \${USERNAME} `; const extracted = extractDockerfile(dockerfile); - const user = findUserStatement(extracted, {}, { USERNAME: 'user1' }, undefined); + const user = findUserStatement(extracted, {}, { USERNAME: 'user1' }, {}, undefined); assert.strictEqual(user, 'user1'); }); }); diff --git a/src/test/testUtils.ts b/src/test/testUtils.ts index cd48c5359..22597dce2 100644 --- a/src/test/testUtils.ts +++ b/src/test/testUtils.ts @@ -154,16 +154,18 @@ export async function createCLIParams(hostPath: string) { env: cliHost.env, output, }, 'docker', 'docker-compose'); + const buildPlatformInfo = { + os: mapNodeOSToGOOS(cliHost.platform), + arch: mapNodeArchitectureToGOARCH(cliHost.arch), + }; const cliParams: DockerCLIParameters = { cliHost, dockerCLI: 'docker', dockerComposeCLI, env: {}, output, - platformInfo: { - os: mapNodeOSToGOOS(cliHost.platform), - arch: mapNodeArchitectureToGOARCH(cliHost.arch), - } + buildPlatformInfo, + targetPlatformInfo: buildPlatformInfo, }; return cliParams; } From 60a458052361d9785fb2f799b64c21b2a93da91e Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 30 Mar 2026 10:29:27 +0200 Subject: [PATCH 68/88] Update dependencies with security fixes --- yarn.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/yarn.lock b/yarn.lock index ec61aa8d5..4bf26c39b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -653,24 +653,24 @@ bl@^5.0.0: readable-stream "^3.4.0" brace-expansion@^1.1.7: - version "1.1.12" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" - integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + version "1.1.13" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.13.tgz#d37875c01dc9eff988dd49d112a57cb67b54efe6" + integrity "sha1-03h1wB3J7/mI3UnREqV8tntU7+Y= sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==" dependencies: balanced-match "^1.0.0" concat-map "0.0.1" brace-expansion@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" - integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + version "2.0.3" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.3.tgz#0493338bdd58e319b1039c67cf7ee439892c01d9" + integrity "sha1-BJMzi91Y4xmxA5xnz37kOYksAdk= sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==" dependencies: balanced-match "^1.0.0" brace-expansion@^5.0.2: - version "5.0.4" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.4.tgz#614daaecd0a688f660bbbc909a8748c3d80d4336" - integrity sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg== + version "5.0.5" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb" + integrity "sha1-3MOjcRa3nz4bRtuZTO1dVw6TD9s= sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==" dependencies: balanced-match "^4.0.2" @@ -1364,9 +1364,9 @@ flat@^5.0.2: integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== flatted@^3.2.9: - version "3.3.4" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.4.tgz#0986e681008f0f13f58e18656c47967682db5ff6" - integrity sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA== + version "3.4.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726" + integrity "sha1-9cI8EH8PN96NvfJPE3IrO5jVJyY= sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==" follow-redirects@^1.15.11: version "1.15.11" @@ -2383,14 +2383,14 @@ picocolors@^1.1.1: integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + version "2.3.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601" + integrity "sha1-WpQpFeJrNy3A8OZ1MUmhbmscVgE= sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==" picomatch@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" - integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + version "4.0.4" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" + integrity "sha1-/W9eAKFDCG4HTf/kySS4+yk7BYk= sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==" pidtree@^0.3.0: version "0.3.1" From 218a1b912c53e30927f96a2f5e44eb259f813c0e Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 30 Mar 2026 10:31:58 +0200 Subject: [PATCH 69/88] 0.85.0 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 950241b95..ed1a68ecb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Notable changes. ## March 2026 +### [0.85.0] +- Inline buildx global build and target platform envvars when resolving base image and user. (https://github.com/devcontainers/cli/pull/1169) + ### [0.84.1] - Bump tar from 7.5.10 to 7.5.11 due to [CVE-2026-31802](https://github.com/advisories/GHSA-9ppj-qmqm-q256). (https://github.com/devcontainers/cli/pull/1174) diff --git a/package.json b/package.json index a79e91782..e783c0d12 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.84.1", + "version": "0.85.0", "bin": { "devcontainer": "devcontainer.js" }, From 85a61a0157580a62bb13fb0cad40c2c5c71e7dc8 Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Wed, 8 Apr 2026 16:45:04 +0200 Subject: [PATCH 70/88] Normalize drive letter to lowercase on Windows to match VSCode (#1183) * Normalize drive letter to lowercase on Windows to match VSCode * Increase timeouts for flaky tests * Address PR review nits --- src/spec-node/featuresCLI/testCommandImpl.ts | 5 +- src/spec-node/utils.ts | 89 +++++++++++++++++-- .../containerFeaturesOCI.test.ts | 4 +- .../container-features/featureHelpers.test.ts | 4 +- src/test/labelPathNormalization.test.ts | 29 ++++++ 5 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 src/test/labelPathNormalization.test.ts diff --git a/src/spec-node/featuresCLI/testCommandImpl.ts b/src/spec-node/featuresCLI/testCommandImpl.ts index fc9f092fb..d119885a8 100644 --- a/src/spec-node/featuresCLI/testCommandImpl.ts +++ b/src/spec-node/featuresCLI/testCommandImpl.ts @@ -6,7 +6,7 @@ import { CLIHost } from '../../spec-common/cliHost'; import { launch, ProvisionOptions, createDockerParams } from '../devContainers'; import { doExec } from '../devContainersSpecCLI'; import { LaunchResult, staticExecParams, staticProvisionParams, testLibraryScript } from './utils'; -import { DockerResolverParameters } from '../utils'; +import { DockerResolverParameters, normalizeDevContainerLabelPath } from '../utils'; import { DevContainerConfig } from '../../spec-configuration/configuration'; import { FeaturesTestCommandInput } from './test'; import { cpDirectoryLocal, rmLocal } from '../../spec-utils/pfs'; @@ -546,7 +546,8 @@ async function launchProject(params: DockerResolverParameters, workspaceFolder: const { common } = params; let response = {} as LaunchResult; - const idLabels = [`devcontainer.local_folder=${workspaceFolder}`, `devcontainer.is_test_run=true`]; + const normalizedWorkspaceFolder = normalizeDevContainerLabelPath(process.platform, workspaceFolder); + const idLabels = [`devcontainer.local_folder=${normalizedWorkspaceFolder}`, `devcontainer.is_test_run=true`]; const options: ProvisionOptions = { ...staticProvisionParams, workspaceFolder, diff --git a/src/spec-node/utils.ts b/src/spec-node/utils.ts index 74817d1a0..2fafbca41 100644 --- a/src/spec-node/utils.ts +++ b/src/spec-node/utils.ts @@ -15,7 +15,7 @@ import { CommonDevContainerConfig, ContainerProperties, getContainerProperties, import { Workspace } from '../spec-utils/workspaces'; import { URI } from 'vscode-uri'; import { ShellServer } from '../spec-common/shellServer'; -import { inspectContainer, inspectImage, getEvents, ContainerDetails, DockerCLIParameters, dockerExecFunction, dockerPtyCLI, dockerPtyExecFunction, toDockerImageName, DockerComposeCLI, ImageDetails, dockerCLI, removeContainer } from '../spec-shutdown/dockerUtils'; +import { inspectContainer, inspectContainers, inspectImage, getEvents, listContainers, ContainerDetails, DockerCLIParameters, dockerExecFunction, dockerPtyCLI, dockerPtyExecFunction, toDockerImageName, DockerComposeCLI, ImageDetails, dockerCLI, removeContainer } from '../spec-shutdown/dockerUtils'; import { getRemoteWorkspaceFolder } from './dockerCompose'; import { findGitRootFolder } from '../spec-common/git'; import { parentURI, uriToFsPath } from '../spec-configuration/configurationCommonUtils'; @@ -614,6 +614,71 @@ export function getEmptyContextFolder(common: ResolverParameters) { return common.cliHost.path.join(common.persistedFolder, 'empty-folder'); } +export function normalizeDevContainerLabelPath(platform: NodeJS.Platform, value: string): string { + if (platform !== 'win32') { + return value; + } + + // Normalize separators and dot segments, then explicitly lowercase the drive + // letter because devcontainer.local_folder / devcontainer.config_file labels + // should compare case-insensitively on Windows. + const normalized = path.win32.normalize(value); + if (normalized.length >= 2 && normalized[1] === ':') { + return normalized[0].toLowerCase() + normalized.slice(1); + } + + return normalized; +} + +async function findDevContainerByNormalizedLabels(params: DockerResolverParameters | DockerCLIParameters, normalizedWorkspaceFolder: string, normalizedConfigFile: string) { + if (process.platform !== 'win32') { + return undefined; + } + + const ids = await listContainers(params, true, [hostFolderLabel]); + if (!ids.length) { + return undefined; + } + + const details = await inspectContainers(params, ids); + return details + .filter(container => container.State.Status !== 'removing') + .find(container => { + const labels = container.Config.Labels || {}; + const containerWorkspaceFolder = labels[hostFolderLabel]; + const normalizedContainerWorkspaceFolder = containerWorkspaceFolder && normalizeDevContainerLabelPath('win32', containerWorkspaceFolder); + if (!normalizedContainerWorkspaceFolder || normalizedContainerWorkspaceFolder !== normalizedWorkspaceFolder) { + return false; + } + + const containerConfigFile = labels[configFileLabel]; + const normalizedContainerConfigFile = containerConfigFile && normalizeDevContainerLabelPath('win32', containerConfigFile); + return !!normalizedContainerConfigFile + && normalizedContainerConfigFile === normalizedConfigFile; + }); +} + +async function findLegacyDevContainerByNormalizedWorkspaceFolder(params: DockerResolverParameters | DockerCLIParameters, normalizedWorkspaceFolder: string) { + if (process.platform !== 'win32') { + return undefined; + } + + const ids = await listContainers(params, true, [hostFolderLabel]); + if (!ids.length) { + return undefined; + } + + const details = await inspectContainers(params, ids); + return details + .filter(container => container.State.Status !== 'removing') + .find(container => { + const labels = container.Config.Labels || {}; + const containerWorkspaceFolder = labels[hostFolderLabel]; + const normalizedContainerWorkspaceFolder = containerWorkspaceFolder && normalizeDevContainerLabelPath('win32', containerWorkspaceFolder); + return normalizedContainerWorkspaceFolder === normalizedWorkspaceFolder; + }); +} + export async function findContainerAndIdLabels(params: DockerResolverParameters | DockerCLIParameters, containerId: string | undefined, providedIdLabels: string[] | undefined, workspaceFolder: string | undefined, configFile: string | undefined, removeContainerWithOldLabels?: boolean | string) { if (providedIdLabels) { return { @@ -621,14 +686,26 @@ export async function findContainerAndIdLabels(params: DockerResolverParameters idLabels: providedIdLabels, }; } + + const normalizedWorkspaceFolder = workspaceFolder ? normalizeDevContainerLabelPath(process.platform, workspaceFolder) : workspaceFolder; + const normalizedConfigFile = configFile ? normalizeDevContainerLabelPath(process.platform, configFile) : configFile; + const oldLabels = [`${hostFolderLabel}=${normalizedWorkspaceFolder}`]; + const newLabels = [...oldLabels, `${configFileLabel}=${normalizedConfigFile}`]; + let container: ContainerDetails | undefined; if (containerId) { container = await inspectContainer(params, containerId); - } else if (workspaceFolder && configFile) { - container = await findDevContainer(params, [`${hostFolderLabel}=${workspaceFolder}`, `${configFileLabel}=${configFile}`]); + } else if (normalizedWorkspaceFolder && normalizedConfigFile) { + container = await findDevContainer(params, newLabels); + if (!container) { + container = await findDevContainerByNormalizedLabels(params, normalizedWorkspaceFolder, normalizedConfigFile); + } if (!container) { // Fall back to old labels. - container = await findDevContainer(params, [`${hostFolderLabel}=${workspaceFolder}`]); + container = await findDevContainer(params, oldLabels); + if (!container) { + container = await findLegacyDevContainerByNormalizedWorkspaceFolder(params, normalizedWorkspaceFolder); + } if (container) { if (container.Config.Labels?.[configFileLabel]) { // But ignore containers with new labels. @@ -645,9 +722,7 @@ export async function findContainerAndIdLabels(params: DockerResolverParameters } return { container, - idLabels: !container || container.Config.Labels?.[configFileLabel] ? - [`${hostFolderLabel}=${workspaceFolder}`, `${configFileLabel}=${configFile}`] : - [`${hostFolderLabel}=${workspaceFolder}`], + idLabels: !container || container.Config.Labels?.[configFileLabel] ? newLabels : oldLabels, }; } diff --git a/src/test/container-features/containerFeaturesOCI.test.ts b/src/test/container-features/containerFeaturesOCI.test.ts index 52e6fde10..9281a7498 100644 --- a/src/test/container-features/containerFeaturesOCI.test.ts +++ b/src/test/container-features/containerFeaturesOCI.test.ts @@ -255,7 +255,9 @@ describe('getRef()', async function () { }); describe('Test OCI Pull', async function () { - this.timeout('10s'); + // These tests fetch manifests/blobs from a live OCI registry, so allow + // extra time for auth/token exchange and transient network latency in CI. + this.timeout('60s'); it('Parse OCI identifier', async function () { const feat = getRef(output, 'ghcr.io/codspace/features/ruby:1'); diff --git a/src/test/container-features/featureHelpers.test.ts b/src/test/container-features/featureHelpers.test.ts index d4ceeb8a8..3e08c0648 100644 --- a/src/test/container-features/featureHelpers.test.ts +++ b/src/test/container-features/featureHelpers.test.ts @@ -57,7 +57,9 @@ describe('validate processFeatureIdentifier', async function () { console.log(`workspaceRoot = ${workspaceRoot}, defaultConfigPath = ${defaultConfigPath}`); describe('VALID processFeatureIdentifier examples', async function () { - this.timeout('4s'); + // These cases perform live OCI/GHCR requests, so allow extra time for + // registry auth/token exchange and transient network latency in CI. + this.timeout('20s'); it('should process v1 local-cache', async function () { // Parsed out of a user's devcontainer.json diff --git a/src/test/labelPathNormalization.test.ts b/src/test/labelPathNormalization.test.ts new file mode 100644 index 000000000..2997b5632 --- /dev/null +++ b/src/test/labelPathNormalization.test.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { assert } from 'chai'; +import { normalizeDevContainerLabelPath } from '../spec-node/utils'; + +describe('normalizeDevContainerLabelPath', function () { + it('lowercases Windows drive letters', function () { + assert.equal( + normalizeDevContainerLabelPath('win32', 'C:\\CodeBlocks\\remill'), + 'c:\\CodeBlocks\\remill' + ); + }); + + it('normalizes Windows path separators', function () { + assert.equal( + normalizeDevContainerLabelPath('win32', 'C:/CodeBlocks/remill/.devcontainer/devcontainer.json'), + 'c:\\CodeBlocks\\remill\\.devcontainer\\devcontainer.json' + ); + }); + + it('leaves non-Windows paths unchanged', function () { + assert.equal( + normalizeDevContainerLabelPath('linux', '/workspaces/remill'), + '/workspaces/remill' + ); + }); +}); From f9ea7fe4f0161748e2855d6295bc495783f84e06 Mon Sep 17 00:00:00 2001 From: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> Date: Thu, 9 Apr 2026 06:52:07 -0400 Subject: [PATCH 71/88] Node.js 20 upgrade for dev container (#1189) * dev container to node 20 Signed-off-by: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> * lock file Signed-off-by: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> --------- Signed-off-by: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> --- .devcontainer/Dockerfile | 2 +- .devcontainer/devcontainer-lock.json | 9 +++++++++ .devcontainer/devcontainer.json | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .devcontainer/devcontainer-lock.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 0e5ce7f6c..e95235992 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,6 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -ARG VARIANT="16-bullseye" +ARG VARIANT="20-bookworm" FROM mcr.microsoft.com/devcontainers/typescript-node:1-${VARIANT} RUN mkdir -p /workspaces && chown node:node /workspaces diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 000000000..55c2bfcef --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,9 @@ +{ + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "2.16.1", + "resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:ce078b7bf7d9ef3bcb9813b32103795d8d72172446890b64772cbe1dec6baafd", + "integrity": "sha256:ce078b7bf7d9ef3bcb9813b32103795d8d72172446890b64772cbe1dec6baafd" + } + } +} \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 7209d3cb6..03c54439d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,7 +7,7 @@ "build": { "dockerfile": "Dockerfile", "args": { - "VARIANT": "18-bookworm" + "VARIANT": "20-bookworm" } }, "mounts": [ From 997a2db1ec28eacfb718d07e7741dbd57af59564 Mon Sep 17 00:00:00 2001 From: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> Date: Thu, 9 Apr 2026 06:56:19 -0400 Subject: [PATCH 72/88] Developer contribution guide for Dev Containers CLI (#1188) Signed-off-by: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> Co-authored-by: Abdurrahmaan Iqbal --- docs/contributing-code.md | 168 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 docs/contributing-code.md diff --git a/docs/contributing-code.md b/docs/contributing-code.md new file mode 100644 index 000000000..cc61bd046 --- /dev/null +++ b/docs/contributing-code.md @@ -0,0 +1,168 @@ +# Contributing Code + +This guide covers everything you need to set up a development environment, build, test, and submit code changes to the Dev Containers CLI. For the proposal and specification process, see [CONTRIBUTING.md](../CONTRIBUTING.md). + +## Prerequisites + +- [Node.js](https://nodejs.org/) >= 20 +- [Docker](https://www.docker.com/) (required for running integration tests — they create real containers) +- [Git](https://git-scm.com/) +- [yarn](https://yarnpkg.com/) (used for dependency installation) + +## Setting up your development environment + +Fork and clone the repository: + +```sh +git clone https://github.com//cli.git +cd cli +``` + +### Option A: Dev Container (recommended) + +The repository includes a [dev container configuration](../.devcontainer/devcontainer.json) that provides a ready-to-go environment with Node.js, TypeScript, and Docker-in-Docker pre-configured. + +1. Open the cloned repository in VS Code. +2. When prompted, select **Reopen in Container** (requires the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)). Alternatively, open the repository in [GitHub Codespaces](https://github.com/features/codespaces). +3. The `postCreateCommand` automatically runs `yarn install` to install all dependencies. + +You are ready to build and test. + +### Option B: Local setup + +1. Install Node.js >= 20 and Docker. +2. Install dependencies: + + ```sh + yarn install + ``` + + Ensure Docker is running — it is needed for the integration test suite. + + Some tests build containers for non-native architectures (e.g., `linux/arm64` on an x64 host, or vice versa). To run these locally, register QEMU emulators: + + ```sh + docker run --privileged --rm tonistiigi/binfmt --install all + ``` + + This is needed once per boot (or per WSL session on Windows). On macOS with Docker Desktop, cross-architecture emulation is built in and this step is not required. + +3. *(Optional)* Install [Podman](https://podman.io/) if you want to run the Podman-specific tests. The CLI supports both Docker and Podman as container engines, and the test suite includes a separate set of tests (`cli.podman.test.ts`) that verify Podman compatibility using `--docker-path podman`. These tests will fail with `spawn podman ENOENT` if Podman is not installed — this is expected and does not indicate a code problem. The CI GitHub workflow runs these tests on `ubuntu-latest` where Podman is pre-installed. + +## Project structure + +The CLI is written in TypeScript and organized as multiple sub-projects using [TypeScript project references](https://www.typescriptlang.org/docs/handbook/project-references.html): + +| Sub-project | Path | Purpose | +| --- | --- | --- | +| `spec-common` | `src/spec-common/` | Shared utilities (async helpers, CLI host, process management, shell server) | +| `spec-configuration` | `src/spec-configuration/` | Configuration parsing, OCI registry interactions, Features/Templates configuration | +| `spec-node` | `src/spec-node/` | Core CLI logic — container lifecycle, Docker/Compose integration, Feature utilities | +| `spec-shutdown` | `src/spec-shutdown/` | Docker CLI wrapper utilities (container inspection, execution, lifecycle management) | +| `spec-utils` | `src/spec-utils/` | General utilities (logging, HTTP requests, filesystem helpers) | + +Key files: + +- `devcontainer.js` — Entry point that loads the bundled CLI from `dist/spec-node/devContainersSpecCLI.js`. +- `esbuild.js` — Build script that bundles the TypeScript output with esbuild. +- `src/test/` — Test files and fixture configurations under `src/test/configs/`. + +## Development workflow + +### 1. Build + +Start the dev build watchers — run these in separate terminals (or use the [VS Code build task](#vs-code-integration)): + +```sh +npm run watch # incremental esbuild (rebuilds on save) +npm run type-check-watch # tsc in watch mode (reports type errors) +``` + +For a one-shot build instead, run `npm run compile`. To remove all build output, run `npm run clean`. + +### 2. Run + +After building, invoke the CLI directly: + +```sh +node devcontainer.js --help +node devcontainer.js up --workspace-folder +node devcontainer.js build --workspace-folder +node devcontainer.js run-user-commands --workspace-folder +``` + +### 3. Test + +Tests use [Mocha](https://mochajs.org/) and [Chai](https://www.chaijs.com/) and require Docker because they create and tear down real containers. + +```sh +npm test # all tests +npm run test-container-features # Features tests only +npm run test-container-templates # Templates tests only +``` + +#### Adding tests + +- Place new test files in `src/test/` with a `.test.ts` suffix. +- Place test fixture `devcontainer.json` configurations under `src/test/configs//`. +- Use the helpers in `src/test/testUtils.ts` (`shellExec`, `devContainerUp`, `devContainerDown`) for container lifecycle management in tests. + +### 4. Validate and submit + +Before committing, run the same checks CI runs: + +```sh +npm run type-check # full type-check +npm run package # production build (minified) + pack into .tgz +npm run precommit # lint, formatting, copyright headers +npm test # full test suite (may take a very long time to run, consider running a subset of tests during development) +``` + +Then push your branch and open a pull request against `main`. Link any related [repo issues](https://github.com/devcontainers/cli/issues) or [specification issues](https://github.com/microsoft/dev-container-spec/issues) in the PR description. + +## VS Code integration + +The repository includes VS Code configuration in `.vscode/` for building, debugging, and testing. + +### Build task + +The default build task (**Ctrl+Shift+B** / **Cmd+Shift+B**) is **Build Dev Containers CLI**. It runs `npm run watch` and `npm run type-check-watch` in parallel so you get both bundled output and type errors as you edit. + +### Debug configurations + +Two launch configurations are provided in `.vscode/launch.json`: + +- **Launch CLI - up** — Runs the CLI's `up` command against `src/test/configs/example/`. Edit the `args` array to point at a different config or subcommand. +- **Launch Tests** — Runs the full Mocha test suite under the debugger. + +### Editor settings + +The workspace recommends the [ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) for inline lint feedback. The workspace settings (`.vscode/settings.json`) configure format-on-save, tab indentation, and the workspace TypeScript SDK. + +## Troubleshooting + +### Docker not available + +Tests will fail if Docker is not running. Make sure the Docker daemon is started. If using the dev container, Docker-in-Docker is configured automatically. + +### `node-pty` native module build failures + +The `node-pty` dependency includes native code. If you see build errors during `yarn install`, ensure you have the required build tools for your platform (e.g., `build-essential` on Debian/Ubuntu, Xcode Command Line Tools on macOS). + +### Leftover test containers + +If tests are interrupted, containers may be left running. Single-container tests label their containers with `devcontainer.local_folder`: + +```sh +docker rm -f $(docker ps -aq --filter "label=devcontainer.local_folder") +``` + +Compose-based tests also create sidecar containers (e.g., `db` services) that don't carry that label. To remove those, filter by the compose config path: + +```sh +docker rm -f $(docker ps -a --format '{{.ID}} {{.Label "com.docker.compose.project.config_files"}}' | grep src/test/configs | awk '{print $1}') +``` + +### Podman test failures + +If you don't have Podman installed, `cli.podman.test.ts` will fail with `spawn podman ENOENT`. This is safe to ignore — CI will run them. See [Local setup](#option-b-local-setup) for details on installing Podman or skipping these tests. From c6f3d472de0b472b922264093e90fc0f2b476180 Mon Sep 17 00:00:00 2001 From: Sandro Meier Date: Fri, 10 Apr 2026 16:59:07 +0200 Subject: [PATCH 73/88] fix: always write devcontainer.metadata label as JSON array (#1199) * fix: always write devcontainer.metadata label as JSON array When there is only one metadata entry (e.g. docker-compose devcontainer with no features), `getDevcontainerMetadataLabel` wrote a bare JSON object instead of an array. This violates the spec which states the label "can contain an array of json snippets" and causes tools like Zed that expect an array to fail when attaching to an existing container. Always wrap the metadata in an array regardless of the number of entries. Spec reference: https://containers.dev/implementors/json_reference/ Fixes #1054 * cleanup: remove unnecessary spaces * test: add test for single metadata entry always being an array --- src/spec-node/imageMetadata.ts | 8 +++----- src/test/imageMetadata.test.ts | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/spec-node/imageMetadata.ts b/src/spec-node/imageMetadata.ts index 70a913965..60884592e 100644 --- a/src/spec-node/imageMetadata.ts +++ b/src/spec-node/imageMetadata.ts @@ -497,11 +497,9 @@ export function getDevcontainerMetadataLabel(devContainerMetadata: SubstitutedCo if (!metadata.length) { return ''; } - const imageMetadataLabelValue = metadata.length !== 1 - ? `[${metadata - .map(feature => ` \\\n${toLabelString(feature)}`) - .join(',')} \\\n]` - : toLabelString(metadata[0]); + const imageMetadataLabelValue = `[${metadata + .map(feature => ` \\\n${toLabelString(feature)}`) + .join(',')} \\\n]`; return `LABEL ${imageMetadataLabel}="${imageMetadataLabelValue}"`; } diff --git a/src/test/imageMetadata.test.ts b/src/test/imageMetadata.test.ts index 24045ac4e..fb78df0f5 100644 --- a/src/test/imageMetadata.test.ts +++ b/src/test/imageMetadata.test.ts @@ -432,6 +432,22 @@ describe('Image Metadata', function () { assert.strictEqual(label.replace(/ \\\n/g, ''), `LABEL devcontainer.metadata="${JSON.stringify(expected).replace(/"/g, '\\"')}"`); }); + it('should create array label for single metadata entry (docker-compose with Dockerfile, no features)', () => { + // When there is only one metadata entry, the label should still be a JSON array. + // Regression test for https://github.com/devcontainers/cli/issues/1054 + const label = getDevcontainerMetadataLabel(configWithRaw([ + { + remoteUser: 'testUser', + } + ])); + const expected = [ + { + remoteUser: 'testUser', + } + ]; + assert.strictEqual(label.replace(/ \\\n/g, ''), `LABEL devcontainer.metadata="${JSON.stringify(expected).replace(/"/g, '\\"')}"`); + }); + it('should merge metadata from devcontainer.json and features', () => { const merged = mergeConfiguration({ configFilePath: URI.parse('file:///devcontainer.json'), From 3bdbd6686da843d8709519a993da89da8f4ae253 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 09:07:58 +0000 Subject: [PATCH 74/88] Bump basic-ftp from 5.2.0 to 5.2.2 Bumps [basic-ftp](https://github.com/patrickjuchli/basic-ftp) from 5.2.0 to 5.2.2. - [Release notes](https://github.com/patrickjuchli/basic-ftp/releases) - [Changelog](https://github.com/patrickjuchli/basic-ftp/blob/master/CHANGELOG.md) - [Commits](https://github.com/patrickjuchli/basic-ftp/compare/v5.2.0...v5.2.2) --- updated-dependencies: - dependency-name: basic-ftp dependency-version: 5.2.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4bf26c39b..d87961564 100644 --- a/yarn.lock +++ b/yarn.lock @@ -639,9 +639,9 @@ base64-js@^1.3.1: integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== basic-ftp@^5.0.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.2.0.tgz#7c2dff63c918bde60e6bad1f2ff93dcf5137a40a" - integrity sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw== + version "5.2.2" + resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.2.2.tgz#4cb2422deddf432896bdb3c9b8f13b944ad4842c" + integrity sha512-1tDrzKsdCg70WGvbFss/ulVAxupNauGnOlgpyjKzeQxzyllBLS0CGLV7tjIXTK3ZQA9/FBEm9qyFFN1bciA6pw== bl@^5.0.0: version "5.1.0" From 2d81ee3c9ed96a7312c18c7513a17933f8f66d41 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 13 Apr 2026 13:27:34 +0200 Subject: [PATCH 75/88] 0.86.0 (#1202) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed1a68ecb..569454ce4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ Notable changes. +## April 2026 + +### [0.86.0] +- Bump basic-ftp from 5.2.0 to 5.2.2. (https://github.com/devcontainers/cli/pull/1201) +- Always write devcontainer.metadata label as JSON array. (https://github.com/devcontainers/cli/pull/1199) +- Normalize drive letter to lowercase on Windows to match VSCode. (https://github.com/devcontainers/cli/pull/1183) + ## March 2026 ### [0.85.0] diff --git a/package.json b/package.json index e783c0d12..6ae8776a2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.85.0", + "version": "0.86.0", "bin": { "devcontainer": "devcontainer.js" }, From de15ca4f1db907a4e5e0d3dfa58a8ab2a18191ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 07:24:17 +0000 Subject: [PATCH 76/88] Bump dawidd6/action-download-artifact from 11 to 20 Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 11 to 20. - [Release notes](https://github.com/dawidd6/action-download-artifact/releases) - [Commits](https://github.com/dawidd6/action-download-artifact/compare/ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5...8305c0f1062bb0d184d09ef4493ecb9288447732) --- updated-dependencies: - dependency-name: dawidd6/action-download-artifact dependency-version: '20' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish-dev-containers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-dev-containers.yml b/.github/workflows/publish-dev-containers.yml index f4ac97d0c..7dae6ca8d 100644 --- a/.github/workflows/publish-dev-containers.yml +++ b/.github/workflows/publish-dev-containers.yml @@ -37,7 +37,7 @@ jobs: echo "TGZ=devcontainers-cli-${VERSION}.tgz" | tee -a $GITHUB_ENV echo "TGZ_UPLOAD=devcontainers-cli-${VERSION}-${GITHUB_SHA:0:8}.tgz" | tee -a $GITHUB_ENV - name: Download TGZ - uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11 + uses: dawidd6/action-download-artifact@8305c0f1062bb0d184d09ef4493ecb9288447732 # v20 with: workflow: dev-containers.yml workflow_conclusion: success From da642b4d875d9a48ff2b84f2495d7038e5e44620 Mon Sep 17 00:00:00 2001 From: Ishwar Date: Wed, 18 Mar 2026 10:20:04 +0530 Subject: [PATCH 77/88] refactor: replace substr with slice and remove dead code in singleContainer.ts --- src/spec-node/singleContainer.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/spec-node/singleContainer.ts b/src/spec-node/singleContainer.ts index dc38ef1fc..1c3669f74 100644 --- a/src/spec-node/singleContainer.ts +++ b/src/spec-node/singleContainer.ts @@ -20,7 +20,6 @@ export const configFileLabel = 'devcontainer.config_file'; export async function openDockerfileDevContainer(params: DockerResolverParameters, configWithRaw: SubstitutedConfig, workspaceConfig: WorkspaceConfiguration, idLabels: string[], additionalFeatures: Record>): Promise { const { common } = params; const { config } = configWithRaw; - // let collapsedFeaturesConfig: () => Promise; let container: ContainerDetails | undefined; let containerProperties: ContainerProperties | undefined; @@ -30,14 +29,7 @@ export async function openDockerfileDevContainer(params: DockerResolverParameter let imageMetadata: ImageMetadataEntry[]; let mergedConfig: MergedDevContainerConfig; if (container) { - // let _collapsedFeatureConfig: Promise; - // collapsedFeaturesConfig = async () => { - // return _collapsedFeatureConfig || (_collapsedFeatureConfig = (async () => { - // const allLabels = container?.Config.Labels || {}; - // const featuresConfig = await generateFeaturesConfig(params.common, (await createFeaturesTempFolder(params.common)), config, async () => allLabels, getContainerFeaturesFolder); - // return collapseFeaturesConfig(featuresConfig); - // })()); - // }; + await startExistingContainer(params, idLabels, container); imageMetadata = getImageMetadataFromContainer(container, configWithRaw, undefined, idLabels, common.output).config; mergedConfig = mergeConfiguration(config, imageMetadata); @@ -47,9 +39,6 @@ export async function openDockerfileDevContainer(params: DockerResolverParameter mergedConfig = mergeConfiguration(config, imageMetadata); const { containerUser } = mergedConfig; const updatedImageName = await updateRemoteUserUID(params, mergedConfig, res.updatedImageName[0], res.imageDetails, findUserArg(config.runArgs) || containerUser); - - // collapsedFeaturesConfig = async () => res.collapsedFeaturesConfig; - try { await spawnDevContainer(params, config, mergedConfig, updatedImageName, idLabels, workspaceConfig.workspaceMount, workspaceConfig.additionalMountString, res.imageDetails, containerUser, res.labels || {}); } finally { @@ -290,7 +279,7 @@ export function findUserArg(runArgs: string[] = []) { return runArgs[i + 1]; } if (runArg.startsWith('-u=') || runArg.startsWith('--user=')) { - return runArg.substr(runArg.indexOf('=') + 1); + return runArg.slice(runArg.indexOf('=') + 1); } } return undefined; From 3a283141162ac40b33d0697351d42f25d59d0c85 Mon Sep 17 00:00:00 2001 From: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> Date: Sun, 5 Apr 2026 21:25:02 -0400 Subject: [PATCH 78/88] fix newline issue Signed-off-by: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> --- .vscode/settings.json | 6 +-- src/spec-configuration/lockfile.ts | 12 +++++- .../expected.devcontainer-lock.json | 2 +- .../lockfile-frozen/.devcontainer-lock.json | 2 +- .../expected.devcontainer-lock.json | 2 +- .../upgraded.devcontainer-lock.json | 2 +- .../lockfile/expected.devcontainer-lock.json | 2 +- src/test/container-features/lockfile.test.ts | 40 ++++++++++++++++++- 8 files changed, 57 insertions(+), 11 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a5ac98e49..1963914f4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,13 +3,13 @@ "search.exclude": { "dist": true }, - "typescript.tsc.autoDetect": "off", + "js/ts.tsc.autoDetect": "off", "eslint.options": { "rulePaths": [ "./build/eslint" ] }, - "mochaExplorer.files": "test/**/*.test.ts", + "mochaExplorer.files": "src/test/**/*.test.ts", "mochaExplorer.require": "ts-node/register", "mochaExplorer.env": { "TS_NODE_PROJECT": "src/test/tsconfig.json" @@ -17,7 +17,7 @@ "files.associations": { "devcontainer-features.json": "jsonc" }, - "typescript.tsdk": "node_modules/typescript/lib", + "js/ts.tsdk.path": "node_modules/typescript/lib", "git.branchProtection": [ "main", "release/*" diff --git a/src/spec-configuration/lockfile.ts b/src/spec-configuration/lockfile.ts index 9de0ba0b2..53fbd0fb9 100644 --- a/src/spec-configuration/lockfile.ts +++ b/src/spec-configuration/lockfile.ts @@ -53,12 +53,20 @@ export async function writeLockfile(params: ContainerFeatureInternalParams, conf return; } - const newLockfileContentString = JSON.stringify(lockfile, null, 2); + const newLockfileContentString = JSON.stringify(lockfile, null, 2) + '\n'; const newLockfileContent = Buffer.from(newLockfileContentString); if (params.experimentalFrozenLockfile && !oldLockfileContent) { throw new Error('Lockfile does not exist.'); } - if (!oldLockfileContent || !newLockfileContent.equals(oldLockfileContent)) { + let oldLockfileNormalized: string | undefined; + if (oldLockfileContent) { + try { + oldLockfileNormalized = JSON.stringify(JSON.parse(oldLockfileContent.toString()), null, 2) + '\n'; + } catch { + // Empty or invalid JSON; treat as needing rewrite. + } + } + if (!oldLockfileNormalized || oldLockfileNormalized !== newLockfileContentString) { if (params.experimentalFrozenLockfile) { throw new Error('Lockfile does not match.'); } diff --git a/src/test/container-features/configs/lockfile-dependson/expected.devcontainer-lock.json b/src/test/container-features/configs/lockfile-dependson/expected.devcontainer-lock.json index a3ed4b5d2..90f784d43 100644 --- a/src/test/container-features/configs/lockfile-dependson/expected.devcontainer-lock.json +++ b/src/test/container-features/configs/lockfile-dependson/expected.devcontainer-lock.json @@ -27,4 +27,4 @@ ] } } -} \ No newline at end of file +} diff --git a/src/test/container-features/configs/lockfile-frozen/.devcontainer-lock.json b/src/test/container-features/configs/lockfile-frozen/.devcontainer-lock.json index 4d9bc604e..1e1764123 100644 --- a/src/test/container-features/configs/lockfile-frozen/.devcontainer-lock.json +++ b/src/test/container-features/configs/lockfile-frozen/.devcontainer-lock.json @@ -11,4 +11,4 @@ "integrity": "sha256:c9cc1ac636b9ef595512b5ca7ecb3a35b7d3499cb6f86372edec76ae0cd71d43" } } -} \ No newline at end of file +} diff --git a/src/test/container-features/configs/lockfile-outdated/expected.devcontainer-lock.json b/src/test/container-features/configs/lockfile-outdated/expected.devcontainer-lock.json index d4491d4dc..8d2fa74e0 100644 --- a/src/test/container-features/configs/lockfile-outdated/expected.devcontainer-lock.json +++ b/src/test/container-features/configs/lockfile-outdated/expected.devcontainer-lock.json @@ -11,4 +11,4 @@ "integrity": "sha256:c9cc1ac636b9ef595512b5ca7ecb3a35b7d3499cb6f86372edec76ae0cd71d43" } } -} \ No newline at end of file +} diff --git a/src/test/container-features/configs/lockfile-upgrade-command/upgraded.devcontainer-lock.json b/src/test/container-features/configs/lockfile-upgrade-command/upgraded.devcontainer-lock.json index bb0b7103b..f3916df75 100644 --- a/src/test/container-features/configs/lockfile-upgrade-command/upgraded.devcontainer-lock.json +++ b/src/test/container-features/configs/lockfile-upgrade-command/upgraded.devcontainer-lock.json @@ -21,4 +21,4 @@ "integrity": "sha256:9024deeca80347dea7603a3bb5b4951988f0bf5894ba036a6ee3f29c025692c6" } } -} \ No newline at end of file +} diff --git a/src/test/container-features/configs/lockfile/expected.devcontainer-lock.json b/src/test/container-features/configs/lockfile/expected.devcontainer-lock.json index dd3136f9d..8e85b136b 100644 --- a/src/test/container-features/configs/lockfile/expected.devcontainer-lock.json +++ b/src/test/container-features/configs/lockfile/expected.devcontainer-lock.json @@ -16,4 +16,4 @@ "integrity": "sha256:41607bd6aba3975adcd0641cc479e67b04abd21763ba8a41ea053bcc04a6a818" } } -} \ No newline at end of file +} diff --git a/src/test/container-features/lockfile.test.ts b/src/test/container-features/lockfile.test.ts index 96fcd4045..d7417b0fb 100644 --- a/src/test/container-features/lockfile.test.ts +++ b/src/test/container-features/lockfile.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import * as path from 'path'; import * as semver from 'semver'; import { shellExec } from '../testUtils'; -import { cpLocal, readLocalFile, rmLocal } from '../../spec-utils/pfs'; +import { cpLocal, readLocalFile, rmLocal, writeLocalFile } from '../../spec-utils/pfs'; const pkg = require('../../../package.json'); @@ -279,6 +279,44 @@ describe('Lockfile', function () { } }); + it('lockfile ends with trailing newline', async () => { + const workspaceFolder = path.join(__dirname, 'configs/lockfile'); + + const lockfilePath = path.join(workspaceFolder, '.devcontainer-lock.json'); + await rmLocal(lockfilePath, { force: true }); + + const res = await shellExec(`${cli} build --workspace-folder ${workspaceFolder} --experimental-lockfile`); + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'success'); + const actual = (await readLocalFile(lockfilePath)).toString(); + assert.ok(actual.endsWith('\n'), 'Lockfile should end with a trailing newline'); + }); + + it('frozen lockfile matches despite formatting differences', async () => { + const workspaceFolder = path.join(__dirname, 'configs/lockfile-frozen'); + const lockfilePath = path.join(workspaceFolder, '.devcontainer-lock.json'); + + // Read the existing lockfile, strip trailing newline to create a byte-different but semantically identical file + const original = (await readLocalFile(lockfilePath)).toString(); + const stripped = original.replace(/\n$/, ''); + assert.notEqual(original, stripped, 'Test setup: should have removed trailing newline'); + assert.deepEqual(JSON.parse(original), JSON.parse(stripped), 'Test setup: JSON content should be identical'); + + try { + await writeLocalFile(lockfilePath, Buffer.from(stripped)); + + // Frozen lockfile should succeed because JSON content is the same + const res = await shellExec(`${cli} build --workspace-folder ${workspaceFolder} --experimental-lockfile --experimental-frozen-lockfile`); + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'success', 'Frozen lockfile should not fail when only formatting differs'); + const actual = (await readLocalFile(lockfilePath)).toString(); + assert.strictEqual(actual, stripped, 'Frozen lockfile should remain unchanged when only formatting differs'); + } finally { + // Restore original lockfile + await writeLocalFile(lockfilePath, Buffer.from(original)); + } + }); + it('upgrade command should work with default workspace folder', async () => { const workspaceFolder = path.join(__dirname, 'configs/lockfile-upgrade-command'); const absoluteTmpPath = path.resolve(__dirname, 'tmp'); From 9c3426b4da5dfc065dc45c229dd4ffcc8235a0b0 Mon Sep 17 00:00:00 2001 From: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> Date: Sun, 5 Apr 2026 21:39:45 -0400 Subject: [PATCH 79/88] test tsconfig for mocha type support in editor Signed-off-by: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> --- src/test/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/tsconfig.json b/src/test/tsconfig.json index 0b742af1e..7059b13e4 100644 --- a/src/test/tsconfig.json +++ b/src/test/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "resolveJsonModule": true + "resolveJsonModule": true, + "types": ["node", "mocha"] }, "references": [ { From e7bf72b129bb17a7ea1d1dcbddac39df3a5b6466 Mon Sep 17 00:00:00 2001 From: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:30:08 -0400 Subject: [PATCH 80/88] additional tests Signed-off-by: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> --- src/spec-configuration/lockfile.ts | 4 ++ .../.devcontainer.json | 7 ++ src/test/container-features/lockfile.test.ts | 64 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/test/container-features/configs/lockfile-frozen-no-lockfile/.devcontainer.json diff --git a/src/spec-configuration/lockfile.ts b/src/spec-configuration/lockfile.ts index 53fbd0fb9..d6b230cb1 100644 --- a/src/spec-configuration/lockfile.ts +++ b/src/spec-configuration/lockfile.ts @@ -53,11 +53,15 @@ export async function writeLockfile(params: ContainerFeatureInternalParams, conf return; } + // Trailing newline per POSIX convention const newLockfileContentString = JSON.stringify(lockfile, null, 2) + '\n'; const newLockfileContent = Buffer.from(newLockfileContentString); if (params.experimentalFrozenLockfile && !oldLockfileContent) { throw new Error('Lockfile does not exist.'); } + // Normalize the existing lockfile through JSON.parse -> JSON.stringify to produce + // the same canonical format as newLockfileContentString, so that the string comparison + // below ignores cosmetic differences (indentation, key order, trailing whitespace, etc.). let oldLockfileNormalized: string | undefined; if (oldLockfileContent) { try { diff --git a/src/test/container-features/configs/lockfile-frozen-no-lockfile/.devcontainer.json b/src/test/container-features/configs/lockfile-frozen-no-lockfile/.devcontainer.json new file mode 100644 index 000000000..d98d20705 --- /dev/null +++ b/src/test/container-features/configs/lockfile-frozen-no-lockfile/.devcontainer.json @@ -0,0 +1,7 @@ +{ + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/codspace/features/flower:1": {}, + "ghcr.io/codspace/features/color:1": {} + } +} diff --git a/src/test/container-features/lockfile.test.ts b/src/test/container-features/lockfile.test.ts index d7417b0fb..8702429b1 100644 --- a/src/test/container-features/lockfile.test.ts +++ b/src/test/container-features/lockfile.test.ts @@ -336,4 +336,68 @@ describe('Lockfile', function () { process.chdir(originalCwd); } }); + + it('frozen lockfile fails when lockfile does not exist', async () => { + const workspaceFolder = path.join(__dirname, 'configs/lockfile-frozen-no-lockfile'); + const lockfilePath = path.join(workspaceFolder, '.devcontainer-lock.json'); + await rmLocal(lockfilePath, { force: true }); + + try { + throw await shellExec(`${cli} build --workspace-folder ${workspaceFolder} --experimental-lockfile --experimental-frozen-lockfile`); + } catch (res) { + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'error'); + assert.equal(response.message, 'Lockfile does not exist.'); + } + }); + + it('corrupt lockfile causes build error', async () => { + const workspaceFolder = path.join(__dirname, 'configs/lockfile'); + const lockfilePath = path.join(workspaceFolder, '.devcontainer-lock.json'); + const expectedPath = path.join(workspaceFolder, 'expected.devcontainer-lock.json'); + + try { + // Write invalid JSON to the lockfile + await writeLocalFile(lockfilePath, Buffer.from('this is not valid json{{{')); + + try { + throw await shellExec(`${cli} build --workspace-folder ${workspaceFolder} --experimental-lockfile`); + } catch (res) { + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'error'); + } + } finally { + // Restore from the known-good expected lockfile + await cpLocal(expectedPath, lockfilePath); + } + }); + + it('no lockfile flags and no existing lockfile is a no-op', async () => { + const workspaceFolder = path.join(__dirname, 'configs/lockfile'); + const lockfilePath = path.join(workspaceFolder, '.devcontainer-lock.json'); + const expectedPath = path.join(workspaceFolder, 'expected.devcontainer-lock.json'); + + try { + await rmLocal(lockfilePath, { force: true }); + + // Build without any lockfile flags + const res = await shellExec(`${cli} build --workspace-folder ${workspaceFolder}`); + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'success'); + + // Lockfile should not have been created + let exists = true; + await readLocalFile(lockfilePath).catch(err => { + if (err?.code === 'ENOENT') { + exists = false; + } else { + throw err; + } + }); + assert.equal(exists, false, 'Lockfile should not be created when no lockfile flags are set'); + } finally { + // Restore from the known-good expected lockfile + await cpLocal(expectedPath, lockfilePath); + } + }); }); \ No newline at end of file From 0689d5528f8e24fa04786c597565d73cb096802e Mon Sep 17 00:00:00 2001 From: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> Date: Thu, 9 Apr 2026 09:30:54 -0400 Subject: [PATCH 81/88] formatting Signed-off-by: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> --- src/test/tsconfig.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/tsconfig.json b/src/test/tsconfig.json index 7059b13e4..b5be01ce1 100644 --- a/src/test/tsconfig.json +++ b/src/test/tsconfig.json @@ -2,7 +2,10 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "resolveJsonModule": true, - "types": ["node", "mocha"] + "types": [ + "node", + "mocha" + ] }, "references": [ { From 539ecc5008cbcc586d441bec252021f181f33ba9 Mon Sep 17 00:00:00 2001 From: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> Date: Mon, 13 Apr 2026 14:05:21 -0400 Subject: [PATCH 82/88] add mocha test explorer to recommended extensions Signed-off-by: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> --- .vscode/extensions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 8b4c1e5ef..baea00e62 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { "recommendations": [ "dbaeumer.vscode-eslint", + "hbenl.vscode-mocha-test-adapter" ] } \ No newline at end of file From 539c04c716df8ce87fd6b13e367db7fd7b1d3586 Mon Sep 17 00:00:00 2001 From: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> Date: Tue, 14 Apr 2026 10:03:21 -0400 Subject: [PATCH 83/88] fix comment Signed-off-by: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> --- src/spec-configuration/lockfile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spec-configuration/lockfile.ts b/src/spec-configuration/lockfile.ts index d6b230cb1..02d0841c3 100644 --- a/src/spec-configuration/lockfile.ts +++ b/src/spec-configuration/lockfile.ts @@ -61,7 +61,7 @@ export async function writeLockfile(params: ContainerFeatureInternalParams, conf } // Normalize the existing lockfile through JSON.parse -> JSON.stringify to produce // the same canonical format as newLockfileContentString, so that the string comparison - // below ignores cosmetic differences (indentation, key order, trailing whitespace, etc.). + // below ignores cosmetic differences (indentation, trailing whitespace, etc.). let oldLockfileNormalized: string | undefined; if (oldLockfileContent) { try { From f4ee5d00ff4e5f49d2e74246fa9573e339a4f5c1 Mon Sep 17 00:00:00 2001 From: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> Date: Fri, 17 Apr 2026 10:17:09 -0400 Subject: [PATCH 84/88] CI timeout reliability improvements for flaky test failures (#1194) --- .github/workflows/dev-containers.yml | 4 ++++ .github/workflows/test-docker-v20.yml | 1 + .github/workflows/test-docker-v29.yml | 1 + .github/workflows/test-windows.yml | 1 + .mocharc.yml | 1 + .vscode/settings.json | 12 +++++++++++- package.json | 2 +- 7 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 .mocharc.yml diff --git a/.github/workflows/dev-containers.yml b/.github/workflows/dev-containers.yml index c9b490854..930d77d31 100644 --- a/.github/workflows/dev-containers.yml +++ b/.github/workflows/dev-containers.yml @@ -16,6 +16,7 @@ jobs: cli: name: CLI runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v5 @@ -44,6 +45,7 @@ jobs: tests-matrix: name: Tests Matrix runs-on: ubuntu-latest + timeout-minutes: 30 strategy: fail-fast: false matrix: @@ -111,6 +113,7 @@ jobs: # TODO: This should be expanded to run on different platforms # Most notably to test platform-specific credential helper behavior runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@v6 @@ -145,6 +148,7 @@ jobs: matrix: os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} + timeout-minutes: 10 steps: - uses: actions/checkout@v6 - name: Run install.sh tests diff --git a/.github/workflows/test-docker-v20.yml b/.github/workflows/test-docker-v20.yml index 0b35c363b..69f8d3569 100644 --- a/.github/workflows/test-docker-v20.yml +++ b/.github/workflows/test-docker-v20.yml @@ -13,6 +13,7 @@ jobs: test-docker-v20: name: Docker v20.10 Compatibility runs-on: ubuntu-22.04 + timeout-minutes: 20 steps: - uses: actions/checkout@v6 diff --git a/.github/workflows/test-docker-v29.yml b/.github/workflows/test-docker-v29.yml index e3db37304..de475f246 100644 --- a/.github/workflows/test-docker-v29.yml +++ b/.github/workflows/test-docker-v29.yml @@ -13,6 +13,7 @@ jobs: test-docker-v29: name: Docker v29.0.0 Compatibility runs-on: ubuntu-latest + timeout-minutes: 20 steps: - uses: actions/checkout@v6 diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 3f4d323f2..5ac9743d1 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -19,6 +19,7 @@ jobs: tests-matrix: name: Tests Matrix (Windows) runs-on: windows-latest + timeout-minutes: 15 strategy: fail-fast: false matrix: diff --git a/.mocharc.yml b/.mocharc.yml new file mode 100644 index 000000000..42bdad050 --- /dev/null +++ b/.mocharc.yml @@ -0,0 +1 @@ +timeout: 360000 # 6 minutes global safety net; individual suites override via this.timeout() diff --git a/.vscode/settings.json b/.vscode/settings.json index 1963914f4..cd5223cb5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,5 +24,15 @@ ], "editor.formatOnSave": true, "editor.formatOnSaveMode": "modifications", - "editor.insertSpaces": false + "editor.insertSpaces": false, + "[json]": { + "editor.insertSpaces": false, + "editor.tabSize": 4, + "editor.defaultFormatter": "vscode.json-language-features" + }, + "[jsonc]": { + "editor.insertSpaces": false, + "editor.tabSize": 4, + "editor.defaultFormatter": "vscode.json-language-features" + } } \ No newline at end of file diff --git a/package.json b/package.json index 6ae8776a2..e1a7bcc11 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "clean-dist": "rimraf dist", "clean-built": "rimraf built", "test": "env TS_NODE_PROJECT=src/test/tsconfig.json mocha -r ts-node/register --exit src/test/*.test.ts", - "test-matrix": "env TS_NODE_PROJECT=src/test/tsconfig.json mocha -r ts-node/register --exit", + "test-matrix": "env TS_NODE_PROJECT=src/test/tsconfig.json mocha -r ts-node/register --exit --retries 1", "test-container-features": "env TS_NODE_PROJECT=src/test/tsconfig.json mocha -r ts-node/register --exit src/test/container-features/*.test.ts", "test-container-features-cli": "env TS_NODE_PROJECT=src/test/tsconfig.json mocha -r ts-node/register --exit src/test/container-features/featuresCLICommands.test.ts", "test-container-templates": "env TS_NODE_PROJECT=src/test/tsconfig.json mocha -r ts-node/register --exit src/test/container-templates/*.test.ts" From 131882da9e7faf4618fc37007a35bb8e1981cfdf Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Wed, 29 Apr 2026 03:54:21 -0700 Subject: [PATCH 85/88] chore: fix CG alerts (#1216) --- package.json | 3 +++ yarn.lock | 39 +++++++++++++++------------------------ 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index e1a7bcc11..8ed5a9b75 100644 --- a/package.json +++ b/package.json @@ -102,5 +102,8 @@ "text-table": "^0.2.0", "vscode-uri": "^3.1.0", "yargs": "~17.7.2" + }, + "resolutions": { + "serialize-javascript": "^7.0.5" } } diff --git a/yarn.lock b/yarn.lock index d87961564..a1fcebea3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -639,9 +639,9 @@ base64-js@^1.3.1: integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== basic-ftp@^5.0.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.2.2.tgz#4cb2422deddf432896bdb3c9b8f13b944ad4842c" - integrity sha512-1tDrzKsdCg70WGvbFss/ulVAxupNauGnOlgpyjKzeQxzyllBLS0CGLV7tjIXTK3ZQA9/FBEm9qyFFN1bciA6pw== + version "5.3.1" + resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.3.1.tgz#3148ee9af43c0522514a4f973fecb1d3cbb6d71e" + integrity sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw== bl@^5.0.0: version "5.1.0" @@ -1369,9 +1369,9 @@ flatted@^3.2.9: integrity "sha1-9cI8EH8PN96NvfJPE3IrO5jVJyY= sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==" follow-redirects@^1.15.11: - version "1.15.11" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" - integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + version "1.16.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc" + integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw== for-each@^0.3.3, for-each@^0.3.5: version "0.3.5" @@ -2451,13 +2451,6 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -2592,16 +2585,16 @@ safe-array-concat@^1.1.3: has-symbols "^1.1.0" isarray "^2.0.5" -safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-push-apply@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" @@ -2634,12 +2627,10 @@ semver@^7.7.3, semver@^7.7.4: resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== -serialize-javascript@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" - integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== - dependencies: - randombytes "^2.1.0" +serialize-javascript@^6.0.2, serialize-javascript@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-7.0.5.tgz#c798cc0552ffbb08981914a42a8756e339d0d5b1" + integrity sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw== set-function-length@^1.2.2: version "1.2.2" From 6293ce5879399316f06287e42e710c0f8e5edfef Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Tue, 5 May 2026 16:09:43 +0000 Subject: [PATCH 86/88] Do not write features supplied via `--additional-features` to the lockfile (microsoft/vscode-remote-release#11616) --- CHANGELOG.md | 5 + package.json | 2 +- .../containerFeaturesConfiguration.ts | 2 +- src/spec-configuration/lockfile.ts | 7 +- src/test/configs/example/.devcontainer.json | 2 +- .../generateLockfile.test.ts | 172 ++++++++++++++++++ 6 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 src/test/container-features/generateLockfile.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 569454ce4..10f3d7099 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ Notable changes. +## May 2026 + +### [0.86.1] +- Do not write features supplied via `--additional-features` to the lockfile. (https://github.com/microsoft/vscode-remote-release/issues/11616) + ## April 2026 ### [0.86.0] diff --git a/package.json b/package.json index 8ed5a9b75..c96b33e32 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.86.0", + "version": "0.86.1", "bin": { "devcontainer": "devcontainer.js" }, diff --git a/src/spec-configuration/containerFeaturesConfiguration.ts b/src/spec-configuration/containerFeaturesConfiguration.ts index 78d6c8018..821e50a8e 100644 --- a/src/spec-configuration/containerFeaturesConfiguration.ts +++ b/src/spec-configuration/containerFeaturesConfiguration.ts @@ -508,7 +508,7 @@ export async function generateFeaturesConfig(params: ContainerFeatureInternalPar await fetchFeatures(params, featuresConfig, dstFolder, ociCacheDir, lockfile); await logFeatureAdvisories(params, featuresConfig); - await writeLockfile(params, config, await generateLockfile(featuresConfig), initLockfile); + await writeLockfile(params, config, await generateLockfile(featuresConfig, config, additionalFeatures), initLockfile); return featuresConfig; } diff --git a/src/spec-configuration/lockfile.ts b/src/spec-configuration/lockfile.ts index 02d0841c3..a1a14755b 100644 --- a/src/spec-configuration/lockfile.ts +++ b/src/spec-configuration/lockfile.ts @@ -13,10 +13,15 @@ export interface Lockfile { features: Record; } -export async function generateLockfile(featuresConfig: FeaturesConfig): Promise { +export async function generateLockfile(featuresConfig: FeaturesConfig, config?: DevContainerConfig, additionalFeatures?: Record>): Promise { + // Features supplied only via `--additional-features` (i.e., not present in `config.features`) + // should not be written to the lockfile. + const configFeatureKeys = new Set(Object.keys(config?.features || {})); + const excludeUserFeatureIds = new Set(Object.keys(additionalFeatures || {}).filter(key => !configFeatureKeys.has(key))); return featuresConfig.featureSets .map(f => [f, f.sourceInformation] as const) .filter((tup): tup is [FeatureSet, OCISourceInformation | DirectTarballSourceInformation] => ['oci', 'direct-tarball'].indexOf(tup[1].type) !== -1) + .filter(([, source]) => !excludeUserFeatureIds.has(source.userFeatureId)) .map(([set, source]) => { const dependsOn = Object.keys(set.features[0].dependsOn || {}); return { diff --git a/src/test/configs/example/.devcontainer.json b/src/test/configs/example/.devcontainer.json index b7e3ab7a0..5f96e6330 100644 --- a/src/test/configs/example/.devcontainer.json +++ b/src/test/configs/example/.devcontainer.json @@ -3,7 +3,7 @@ { "image": "mcr.microsoft.com/devcontainers/base:latest", "features": { - "ghcr.io/devcontainers/features/go:1": { + "ghcr.io/devcontainers/features/github-cli:1": { "version": "latest" } } diff --git a/src/test/container-features/generateLockfile.test.ts b/src/test/container-features/generateLockfile.test.ts new file mode 100644 index 000000000..4737a59a4 --- /dev/null +++ b/src/test/container-features/generateLockfile.test.ts @@ -0,0 +1,172 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { assert } from 'chai'; +import { URI } from 'vscode-uri'; +import { DevContainerConfig } from '../../spec-configuration/configuration'; +import { + DirectTarballSourceInformation, + FeatureSet, + FeaturesConfig, + OCISourceInformation, +} from '../../spec-configuration/containerFeaturesConfiguration'; +import { generateLockfile } from '../../spec-configuration/lockfile'; + +function makeOciFeatureSet(userFeatureId: string, version: string, digest: string): FeatureSet { + const sourceInformation: OCISourceInformation = { + type: 'oci', + userFeatureId, + userFeatureIdWithoutVersion: userFeatureId.split(':')[0], + manifestDigest: digest, + manifest: {} as any, + featureRef: { + registry: 'ghcr.io', + owner: 'devcontainers', + namespace: 'devcontainers/features', + path: `devcontainers/features/${userFeatureId.split('/').pop()!.split(':')[0]}`, + resource: `ghcr.io/${userFeatureId.split(':')[0]}`, + id: userFeatureId.split('/').pop()!.split(':')[0], + version, + tag: version, + }, + }; + return { + sourceInformation, + computedDigest: digest, + features: [ + { + id: sourceInformation.featureRef.id, + version, + value: true, + included: true, + }, + ], + }; +} + +function makeTarballFeatureSet(userFeatureId: string, tarballUri: string, digest: string): FeatureSet { + const sourceInformation: DirectTarballSourceInformation = { + type: 'direct-tarball', + userFeatureId, + tarballUri, + }; + return { + sourceInformation, + computedDigest: digest, + features: [ + { + id: 'mytarball', + version: '1.0.0', + value: true, + included: true, + }, + ], + }; +} + +const mockConfigFilePath = URI.file('/workspace/myProject/.devcontainer/devcontainer.json'); + +describe('generateLockfile', () => { + + it('includes all features when no additionalFeatures are provided', async () => { + const featureSets: FeatureSet[] = [ + makeOciFeatureSet('ghcr.io/devcontainers/features/node:1', '1.0.0', 'sha256:aaa'), + makeOciFeatureSet('ghcr.io/devcontainers/features/git:1', '1.0.0', 'sha256:bbb'), + ]; + const featuresConfig: FeaturesConfig = { featureSets }; + + const lockfile = await generateLockfile(featuresConfig); + + assert.deepEqual(Object.keys(lockfile.features).sort(), [ + 'ghcr.io/devcontainers/features/git:1', + 'ghcr.io/devcontainers/features/node:1', + ]); + }); + + it('excludes features supplied only via additionalFeatures', async () => { + const featureSets: FeatureSet[] = [ + makeOciFeatureSet('ghcr.io/devcontainers/features/node:1', '1.0.0', 'sha256:aaa'), + makeOciFeatureSet('ghcr.io/devcontainers/features/git:1', '1.0.0', 'sha256:bbb'), + ]; + const featuresConfig: FeaturesConfig = { featureSets }; + + const config: DevContainerConfig = { + configFilePath: mockConfigFilePath, + features: { + 'ghcr.io/devcontainers/features/node:1': {}, + }, + }; + const additionalFeatures = { + 'ghcr.io/devcontainers/features/git:1': true, + }; + + const lockfile = await generateLockfile(featuresConfig, config, additionalFeatures); + + assert.deepEqual(Object.keys(lockfile.features), ['ghcr.io/devcontainers/features/node:1']); + }); + + it('keeps features that appear in both config.features and additionalFeatures', async () => { + const featureSets: FeatureSet[] = [ + makeOciFeatureSet('ghcr.io/devcontainers/features/node:1', '1.0.0', 'sha256:aaa'), + ]; + const featuresConfig: FeaturesConfig = { featureSets }; + + const config: DevContainerConfig = { + configFilePath: mockConfigFilePath, + features: { + 'ghcr.io/devcontainers/features/node:1': {}, + }, + }; + const additionalFeatures = { + 'ghcr.io/devcontainers/features/node:1': true, + }; + + const lockfile = await generateLockfile(featuresConfig, config, additionalFeatures); + + assert.deepEqual(Object.keys(lockfile.features), ['ghcr.io/devcontainers/features/node:1']); + }); + + it('excludes additional-only direct-tarball features', async () => { + const featureSets: FeatureSet[] = [ + makeOciFeatureSet('ghcr.io/devcontainers/features/node:1', '1.0.0', 'sha256:aaa'), + makeTarballFeatureSet('https://example.com/devcontainer-feature-mytarball.tgz', 'https://example.com/devcontainer-feature-mytarball.tgz', 'sha256:ccc'), + ]; + const featuresConfig: FeaturesConfig = { featureSets }; + + const config: DevContainerConfig = { + configFilePath: mockConfigFilePath, + features: { + 'ghcr.io/devcontainers/features/node:1': {}, + }, + }; + const additionalFeatures = { + 'https://example.com/devcontainer-feature-mytarball.tgz': true, + }; + + const lockfile = await generateLockfile(featuresConfig, config, additionalFeatures); + + assert.deepEqual(Object.keys(lockfile.features), ['ghcr.io/devcontainers/features/node:1']); + }); + + it('excludes all features when config.features is empty and additionalFeatures provides them all', async () => { + const featureSets: FeatureSet[] = [ + makeOciFeatureSet('ghcr.io/devcontainers/features/node:1', '1.0.0', 'sha256:aaa'), + makeOciFeatureSet('ghcr.io/devcontainers/features/git:1', '1.0.0', 'sha256:bbb'), + ]; + const featuresConfig: FeaturesConfig = { featureSets }; + + const config: DevContainerConfig = { + configFilePath: mockConfigFilePath, + }; + const additionalFeatures = { + 'ghcr.io/devcontainers/features/node:1': true, + 'ghcr.io/devcontainers/features/git:1': true, + }; + + const lockfile = await generateLockfile(featuresConfig, config, additionalFeatures); + + assert.deepEqual(lockfile.features, {}); + }); +}); From 16dbc7d09fef8231380253ea7ce1d57c21062d17 Mon Sep 17 00:00:00 2001 From: Brooke Hamilton <45323234+brooke-hamilton@users.noreply.github.com> Date: Tue, 12 May 2026 04:55:56 -0400 Subject: [PATCH 87/88] Graduate lockfile from experimental to stable (#1212) --- .devcontainer/devcontainer-lock.json | 2 +- .devcontainer/devcontainer.json | 3 +- CHANGELOG.md | 6 + README.md | 4 + docs/contributing-code.md | 8 + package.json | 2 +- .../containerFeaturesConfiguration.ts | 10 +- src/spec-configuration/lockfile.ts | 14 +- src/spec-node/containerFeatures.ts | 4 +- src/spec-node/devContainers.ts | 10 +- src/spec-node/devContainersSpecCLI.ts | 54 ++++- src/spec-node/featureUtils.ts | 2 +- src/spec-node/upgradeCommand.ts | 2 +- src/spec-node/utils.ts | 4 +- src/test/configs/.gitignore | 6 + .../container-features/configs/.gitignore | 6 + .../lockfile-no-lockfile/.devcontainer.json | 7 + .../generateFeaturesConfig.test.ts | 2 +- src/test/container-features/lockfile.test.ts | 190 ++++++++++++++++-- 19 files changed, 286 insertions(+), 50 deletions(-) create mode 100644 src/test/configs/.gitignore create mode 100644 src/test/container-features/configs/.gitignore create mode 100644 src/test/container-features/configs/lockfile-no-lockfile/.devcontainer.json diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json index 55c2bfcef..53c5a53c2 100644 --- a/.devcontainer/devcontainer-lock.json +++ b/.devcontainer/devcontainer-lock.json @@ -6,4 +6,4 @@ "integrity": "sha256:ce078b7bf7d9ef3bcb9813b32103795d8d72172446890b64772cbe1dec6baafd" } } -} \ No newline at end of file +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 03c54439d..974aaedbb 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -27,7 +27,8 @@ "vscode": { "extensions": [ "dbaeumer.vscode-eslint", - "GitHub.vscode-pull-request-github" + "GitHub.vscode-pull-request-github", + "hbenl.vscode-mocha-test-adapter" ] }, "codespaces": { diff --git a/CHANGELOG.md b/CHANGELOG.md index 10f3d7099..66792dd19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ Notable changes. ## May 2026 +### [0.87.0] +- Graduate lockfile from experimental to stable: lockfiles are now generated by default on `build` and `up`. (https://github.com/devcontainers/cli/issues/1195) + - New `--no-lockfile` flag to opt out of lockfile generation. + - New `--frozen-lockfile` flag to ensure the lockfile exists and remains unchanged. + - `--experimental-lockfile` and `--experimental-frozen-lockfile` are deprecated (still accepted with a warning). + ### [0.86.1] - Do not write features supplied via `--additional-features` to the lockfile. (https://github.com/microsoft/vscode-remote-release/issues/11616) diff --git a/README.md b/README.md index 622decd57..39b043ba0 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,15 @@ This CLI is in active development. Current status: - [x] `devcontainer run-user-commands` - Runs lifecycle commands like `postCreateCommand` - [x] `devcontainer read-configuration` - Outputs current configuration for workspace - [x] `devcontainer exec` - Executes a command in a container with `userEnvProbe`, `remoteUser`, `remoteEnv`, and other properties applied +- [x] `devcontainer outdated` - Show outdated lockfile features +- [x] `devcontainer upgrade` - Upgrade lockfile features - [x] `devcontainer features <...>` - Tools to assist in authoring and testing [Dev Container Features](https://containers.dev/implementors/features/) - [x] `devcontainer templates <...>` - Tools to assist in authoring and testing [Dev Container Templates](https://containers.dev/implementors/templates/) - [ ] `devcontainer stop` - Stops containers - [ ] `devcontainer down` - Stops and deletes containers +Lockfiles (`.devcontainer-lock.json`) are generated by default when running `build` or `up` to pin feature versions for reproducible builds. Use `--no-lockfile` to opt out, or `--frozen-lockfile` to enforce an existing lockfile. + ## Try it out We'd love for you to try out the dev container CLI and let us know what you think. You can quickly try it out in just a few simple steps, either by using the install script, installing its npm package, or building the CLI repo from sources (see "[Build from sources](#build-from-sources)"). diff --git a/docs/contributing-code.md b/docs/contributing-code.md index cc61bd046..abfde9c5c 100644 --- a/docs/contributing-code.md +++ b/docs/contributing-code.md @@ -95,6 +95,14 @@ node devcontainer.js run-user-commands --workspace-folder Tests use [Mocha](https://mochajs.org/) and [Chai](https://www.chaijs.com/) and require Docker because they create and tear down real containers. +Before running tests, package the CLI into a tarball: + +```sh +npm run package +``` + +Tests install the CLI from the generated `devcontainers-cli-.tgz` and shell out to it as a subprocess. You must re-run `npm run package` after any code change so that the tarball reflects your latest changes. Running `npm run compile` alone is **not** sufficient — it builds the JavaScript output but does not create the tarball that the tests depend on. + ```sh npm test # all tests npm run test-container-features # Features tests only diff --git a/package.json b/package.json index c96b33e32..b62f6402e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@devcontainers/cli", "description": "Dev Containers CLI", - "version": "0.86.1", + "version": "0.87.0", "bin": { "devcontainer": "devcontainer.js" }, diff --git a/src/spec-configuration/containerFeaturesConfiguration.ts b/src/spec-configuration/containerFeaturesConfiguration.ts index 821e50a8e..5957d0896 100644 --- a/src/spec-configuration/containerFeaturesConfiguration.ts +++ b/src/spec-configuration/containerFeaturesConfiguration.ts @@ -193,8 +193,8 @@ export interface ContainerFeatureInternalParams { env: NodeJS.ProcessEnv; skipFeatureAutoMapping: boolean; platform: NodeJS.Platform; - experimentalLockfile?: boolean; - experimentalFrozenLockfile?: boolean; + noLockfile?: boolean; + frozenLockfile?: boolean; } // TODO: Move to node layer. @@ -485,7 +485,7 @@ export async function generateFeaturesConfig(params: ContainerFeatureInternalPar const ociCacheDir = await prepareOCICache(dstFolder); - const { lockfile, initLockfile } = await readLockfile(config); + const { lockfile } = params.noLockfile ? { lockfile: undefined } : await readLockfile(config); const processFeature = async (_userFeature: DevContainerFeature) => { return await processFeatureIdentifier(params, configPath, workspaceRoot, _userFeature, lockfile); @@ -508,7 +508,9 @@ export async function generateFeaturesConfig(params: ContainerFeatureInternalPar await fetchFeatures(params, featuresConfig, dstFolder, ociCacheDir, lockfile); await logFeatureAdvisories(params, featuresConfig); - await writeLockfile(params, config, await generateLockfile(featuresConfig, config, additionalFeatures), initLockfile); + if (!params.noLockfile) { + await writeLockfile(params, config, await generateLockfile(featuresConfig, config, additionalFeatures)); + } return featuresConfig; } diff --git a/src/spec-configuration/lockfile.ts b/src/spec-configuration/lockfile.ts index a1a14755b..e41ae2025 100644 --- a/src/spec-configuration/lockfile.ts +++ b/src/spec-configuration/lockfile.ts @@ -45,7 +45,11 @@ export async function generateLockfile(featuresConfig: FeaturesConfig, config?: }); } -export async function writeLockfile(params: ContainerFeatureInternalParams, config: DevContainerConfig, lockfile: Lockfile, forceInitLockfile?: boolean): Promise { +export async function writeLockfile(params: ContainerFeatureInternalParams, config: DevContainerConfig, lockfile: Lockfile): Promise { + if (params.noLockfile) { + return; + } + const lockfilePath = getLockfilePath(config); const oldLockfileContent = await readLocalFile(lockfilePath) .catch(err => { @@ -54,14 +58,10 @@ export async function writeLockfile(params: ContainerFeatureInternalParams, conf } }); - if (!forceInitLockfile && !oldLockfileContent && !params.experimentalLockfile && !params.experimentalFrozenLockfile) { - return; - } - // Trailing newline per POSIX convention const newLockfileContentString = JSON.stringify(lockfile, null, 2) + '\n'; const newLockfileContent = Buffer.from(newLockfileContentString); - if (params.experimentalFrozenLockfile && !oldLockfileContent) { + if (params.frozenLockfile && !oldLockfileContent) { throw new Error('Lockfile does not exist.'); } // Normalize the existing lockfile through JSON.parse -> JSON.stringify to produce @@ -76,7 +76,7 @@ export async function writeLockfile(params: ContainerFeatureInternalParams, conf } } if (!oldLockfileNormalized || oldLockfileNormalized !== newLockfileContentString) { - if (params.experimentalFrozenLockfile) { + if (params.frozenLockfile) { throw new Error('Lockfile does not match.'); } await writeLocalFile(lockfilePath, newLockfileContent); diff --git a/src/spec-node/containerFeatures.ts b/src/spec-node/containerFeatures.ts index e05822d1b..d8912967c 100644 --- a/src/spec-node/containerFeatures.ts +++ b/src/spec-node/containerFeatures.ts @@ -147,8 +147,8 @@ export async function getExtendImageBuildInfo(params: DockerResolverParameters, const platform = params.common.cliHost.platform; const cacheFolder = await getCacheFolder(params.common.cliHost); - const { experimentalLockfile, experimentalFrozenLockfile } = params; - const featuresConfig = await generateFeaturesConfig({ ...params.common, platform, cacheFolder, experimentalLockfile, experimentalFrozenLockfile }, dstFolder, config.config, additionalFeatures); + const { noLockfile, frozenLockfile } = params; + const featuresConfig = await generateFeaturesConfig({ ...params.common, platform, cacheFolder, noLockfile, frozenLockfile }, dstFolder, config.config, additionalFeatures); if (!featuresConfig) { if (canAddLabelsToContainer && !imageBuildInfo.dockerfile) { return { diff --git a/src/spec-node/devContainers.ts b/src/spec-node/devContainers.ts index a856890c6..d647bb614 100644 --- a/src/spec-node/devContainers.ts +++ b/src/spec-node/devContainers.ts @@ -68,8 +68,8 @@ export interface ProvisionOptions { installCommand?: string; targetPath?: string; }; - experimentalLockfile?: boolean; - experimentalFrozenLockfile?: boolean; + noLockfile?: boolean; + frozenLockfile?: boolean; secretsP?: Promise>; omitSyntaxDirective?: boolean; includeConfig?: boolean; @@ -103,7 +103,7 @@ export async function launch(options: ProvisionOptions, providedIdLabels: string } export async function createDockerParams(options: ProvisionOptions, disposables: (() => Promise | undefined)[]): Promise { - const { persistedFolder, additionalMounts, updateRemoteUserUIDDefault, containerDataFolder, containerSystemDataFolder, workspaceMountConsistency, gpuAvailability, mountWorkspaceGitRoot, mountGitWorktreeCommonDir, remoteEnv, experimentalLockfile, experimentalFrozenLockfile, omitLoggerHeader, secretsP } = options; + const { persistedFolder, additionalMounts, updateRemoteUserUIDDefault, containerDataFolder, containerSystemDataFolder, workspaceMountConsistency, gpuAvailability, mountWorkspaceGitRoot, mountGitWorktreeCommonDir, remoteEnv, noLockfile, frozenLockfile, omitLoggerHeader, secretsP } = options; let parsedAuthority: DevContainerAuthority | undefined; if (options.workspaceFolder) { parsedAuthority = { hostPath: options.workspaceFolder } as DevContainerAuthority; @@ -246,8 +246,8 @@ export async function createDockerParams(options: ProvisionOptions, disposables: buildKitVersion, dockerEngineVersion: dockerEngineVer, isTTY: process.stdout.isTTY || options.logFormat === 'json', - experimentalLockfile, - experimentalFrozenLockfile, + noLockfile, + frozenLockfile, buildxPlatform: common.buildxPlatform, buildxPush: common.buildxPush, additionalLabels: options.additionalLabels, diff --git a/src/spec-node/devContainersSpecCLI.ts b/src/spec-node/devContainersSpecCLI.ts index 18c44136b..832e9603f 100644 --- a/src/spec-node/devContainersSpecCLI.ts +++ b/src/spec-node/devContainersSpecCLI.ts @@ -140,6 +140,8 @@ function provisionOptions(y: Argv) { 'secrets-file': { type: 'string', description: 'Path to a json file containing secret environment variables as key-value pairs.' }, 'experimental-lockfile': { type: 'boolean', default: false, hidden: true, description: 'Write lockfile' }, 'experimental-frozen-lockfile': { type: 'boolean', default: false, hidden: true, description: 'Ensure lockfile remains unchanged' }, + 'no-lockfile': { type: 'boolean', default: false, description: 'Disable lockfile generation and verification.' }, + 'frozen-lockfile': { type: 'boolean', default: false, description: 'Ensure lockfile exists and remains unchanged; fail otherwise.' }, 'omit-syntax-directive': { type: 'boolean', default: false, hidden: true, description: 'Omit Dockerfile syntax directives' }, 'include-configuration': { type: 'boolean', default: false, description: 'Include configuration in result.' }, 'include-merged-configuration': { type: 'boolean', default: false, description: 'Include merged configuration in result.' }, @@ -161,6 +163,15 @@ function provisionOptions(y: Argv) { if (remoteEnvs?.some(remoteEnv => !/.+=.*/.test(remoteEnv))) { throw new Error('Unmatched argument format: remote-env must match ='); } + if (argv['no-lockfile'] && argv['frozen-lockfile']) { + throw new Error('--no-lockfile and --frozen-lockfile are mutually exclusive.'); + } + if (argv['no-lockfile'] && argv['experimental-frozen-lockfile']) { + throw new Error('--no-lockfile and --experimental-frozen-lockfile are mutually exclusive.'); + } + if (argv['no-lockfile'] && argv['experimental-lockfile']) { + throw new Error('--no-lockfile and --experimental-lockfile are mutually exclusive.'); + } return true; }); } @@ -213,11 +224,16 @@ async function provision({ 'secrets-file': secretsFile, 'experimental-lockfile': experimentalLockfile, 'experimental-frozen-lockfile': experimentalFrozenLockfile, + 'no-lockfile': noLockfile, + 'frozen-lockfile': frozenLockfile, 'omit-syntax-directive': omitSyntaxDirective, 'include-configuration': includeConfig, 'include-merged-configuration': includeMergedConfig, }: ProvisionArgs) { + warnDeprecatedLockfileFlags(experimentalLockfile, experimentalFrozenLockfile); + const effectiveFrozenLockfile = frozenLockfile || experimentalFrozenLockfile; + const workspaceFolder = workspaceFolderArg ? path.resolve(process.cwd(), workspaceFolderArg) : undefined; const addRemoteEnvs = addRemoteEnv ? (Array.isArray(addRemoteEnv) ? addRemoteEnv as string[] : [addRemoteEnv]) : []; const addCacheFroms = addCacheFrom ? (Array.isArray(addCacheFrom) ? addCacheFrom as string[] : [addCacheFrom]) : []; @@ -282,8 +298,8 @@ async function provision({ containerSessionDataFolder, skipPersistingCustomizationsFromFeatures: false, omitConfigRemotEnvFromMetadata, - experimentalLockfile, - experimentalFrozenLockfile, + noLockfile, + frozenLockfile: effectiveFrozenLockfile, omitSyntaxDirective, includeConfig, includeMergedConfig, @@ -527,8 +543,22 @@ function buildOptions(y: Argv) { 'skip-persisting-customizations-from-features': { type: 'boolean', default: false, hidden: true, description: 'Do not save customizations from referenced Features as image metadata' }, 'experimental-lockfile': { type: 'boolean', default: false, hidden: true, description: 'Write lockfile' }, 'experimental-frozen-lockfile': { type: 'boolean', default: false, hidden: true, description: 'Ensure lockfile remains unchanged' }, + 'no-lockfile': { type: 'boolean', default: false, description: 'Disable lockfile generation and verification.' }, + 'frozen-lockfile': { type: 'boolean', default: false, description: 'Ensure lockfile exists and remains unchanged; fail otherwise.' }, 'omit-syntax-directive': { type: 'boolean', default: false, hidden: true, description: 'Omit Dockerfile syntax directives' }, - }); + }) + .check(argv => { + if (argv['no-lockfile'] && argv['frozen-lockfile']) { + throw new Error('--no-lockfile and --frozen-lockfile are mutually exclusive.'); + } + if (argv['no-lockfile'] && argv['experimental-frozen-lockfile']) { + throw new Error('--no-lockfile and --experimental-frozen-lockfile are mutually exclusive.'); + } + if (argv['no-lockfile'] && argv['experimental-lockfile']) { + throw new Error('--no-lockfile and --experimental-lockfile are mutually exclusive.'); + } + return true; + }); } type BuildArgs = UnpackArgv>; @@ -569,8 +599,13 @@ async function doBuild({ 'skip-persisting-customizations-from-features': skipPersistingCustomizationsFromFeatures, 'experimental-lockfile': experimentalLockfile, 'experimental-frozen-lockfile': experimentalFrozenLockfile, + 'no-lockfile': noLockfile, + 'frozen-lockfile': frozenLockfile, 'omit-syntax-directive': omitSyntaxDirective, }: BuildArgs) { + warnDeprecatedLockfileFlags(experimentalLockfile, experimentalFrozenLockfile); + const effectiveFrozenLockfile = frozenLockfile || experimentalFrozenLockfile; + const disposables: (() => Promise | undefined)[] = []; const dispose = async () => { await Promise.all(disposables.map(d => d())); @@ -617,8 +652,8 @@ async function doBuild({ skipPostAttach: true, skipPersistingCustomizationsFromFeatures: skipPersistingCustomizationsFromFeatures, dotfiles: {}, - experimentalLockfile, - experimentalFrozenLockfile, + noLockfile, + frozenLockfile: effectiveFrozenLockfile, omitSyntaxDirective, }, disposables); @@ -1442,3 +1477,12 @@ async function readSecretsFromFile(params: { output?: Log; secretsFile?: string; }); } } + +function warnDeprecatedLockfileFlags(experimentalLockfile: boolean, experimentalFrozenLockfile: boolean) { + if (experimentalLockfile) { + process.stderr.write('Warning: --experimental-lockfile is deprecated. Lockfiles are now enabled by default.\n'); + } + if (experimentalFrozenLockfile) { + process.stderr.write('Warning: --experimental-frozen-lockfile is deprecated. Use --frozen-lockfile instead.\n'); + } +} diff --git a/src/spec-node/featureUtils.ts b/src/spec-node/featureUtils.ts index 0b3fabc01..a36205295 100644 --- a/src/spec-node/featureUtils.ts +++ b/src/spec-node/featureUtils.ts @@ -9,5 +9,5 @@ export async function readFeaturesConfig(params: DockerCLIParameters, pkg: Packa const { cwd, env, platform } = cliHost; const featuresTmpFolder = await createFeaturesTempFolder({ cliHost, package: pkg }); const cacheFolder = await getCacheFolder(cliHost); - return generateFeaturesConfig({ extensionPath, cacheFolder, cwd, output, env, skipFeatureAutoMapping, platform }, featuresTmpFolder, config, additionalFeatures); + return generateFeaturesConfig({ extensionPath, cacheFolder, cwd, output, env, skipFeatureAutoMapping, platform, noLockfile: true }, featuresTmpFolder, config, additionalFeatures); } \ No newline at end of file diff --git a/src/spec-node/upgradeCommand.ts b/src/spec-node/upgradeCommand.ts index 51410de07..8773fd5de 100644 --- a/src/spec-node/upgradeCommand.ts +++ b/src/spec-node/upgradeCommand.ts @@ -138,7 +138,7 @@ async function featuresUpgrade({ const lockfilePath = getLockfilePath(config); await writeLocalFile(lockfilePath, ''); // Update lockfile - await writeLockfile(params, config, lockfile, true); + await writeLockfile(params, config, lockfile); } catch (err) { if (output) { output.write(err && (err.stack || err.message) || String(err)); diff --git a/src/spec-node/utils.ts b/src/spec-node/utils.ts index 2fafbca41..515294e7b 100644 --- a/src/spec-node/utils.ts +++ b/src/spec-node/utils.ts @@ -127,8 +127,8 @@ export interface DockerResolverParameters { buildKitVersion: { versionString: string; versionMatch?: string } | undefined; dockerEngineVersion: { versionString: string; versionMatch?: string } | undefined; isTTY: boolean; - experimentalLockfile?: boolean; - experimentalFrozenLockfile?: boolean; + noLockfile?: boolean; + frozenLockfile?: boolean; buildxPlatform: string | undefined; buildxPush: boolean; additionalLabels: string[]; diff --git a/src/test/configs/.gitignore b/src/test/configs/.gitignore new file mode 100644 index 000000000..58dacec39 --- /dev/null +++ b/src/test/configs/.gitignore @@ -0,0 +1,6 @@ +# Lock files will be generated during test runs. Ignore them from git because +# they can point to different hashes when the feature is updated. +# To add a lock file when it is required for a test, add it to git with the command: +# git add --force devcontainer-lock.json +devcontainer-lock.json +.devcontainer-lock.json diff --git a/src/test/container-features/configs/.gitignore b/src/test/container-features/configs/.gitignore new file mode 100644 index 000000000..58dacec39 --- /dev/null +++ b/src/test/container-features/configs/.gitignore @@ -0,0 +1,6 @@ +# Lock files will be generated during test runs. Ignore them from git because +# they can point to different hashes when the feature is updated. +# To add a lock file when it is required for a test, add it to git with the command: +# git add --force devcontainer-lock.json +devcontainer-lock.json +.devcontainer-lock.json diff --git a/src/test/container-features/configs/lockfile-no-lockfile/.devcontainer.json b/src/test/container-features/configs/lockfile-no-lockfile/.devcontainer.json new file mode 100644 index 000000000..e54a9012f --- /dev/null +++ b/src/test/container-features/configs/lockfile-no-lockfile/.devcontainer.json @@ -0,0 +1,7 @@ +{ + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/codspace/features/flower:1.0.0": {}, + "ghcr.io/codspace/features/color:1.0.4": {} + } +} diff --git a/src/test/container-features/generateFeaturesConfig.test.ts b/src/test/container-features/generateFeaturesConfig.test.ts index ac28931c8..915c3e2da 100644 --- a/src/test/container-features/generateFeaturesConfig.test.ts +++ b/src/test/container-features/generateFeaturesConfig.test.ts @@ -21,7 +21,7 @@ describe('validate generateFeaturesConfig()', function () { const env = { 'SOME_KEY': 'SOME_VAL' }; const platform = process.platform; const cacheFolder = path.join(os.tmpdir(), `devcontainercli-test-${crypto.randomUUID()}`); - const params = { extensionPath: '', cwd: '', output, env, cacheFolder, persistedFolder: '', skipFeatureAutoMapping: false, platform }; + const params = { extensionPath: '', cwd: '', output, env, cacheFolder, persistedFolder: '', skipFeatureAutoMapping: false, platform, noLockfile: true }; it('should correctly return a featuresConfig with v2 local features', async function () { const version = 'unittest'; diff --git a/src/test/container-features/lockfile.test.ts b/src/test/container-features/lockfile.test.ts index 8702429b1..2e7f035ef 100644 --- a/src/test/container-features/lockfile.test.ts +++ b/src/test/container-features/lockfile.test.ts @@ -372,32 +372,184 @@ describe('Lockfile', function () { } }); - it('no lockfile flags and no existing lockfile is a no-op', async () => { - const workspaceFolder = path.join(__dirname, 'configs/lockfile'); - const lockfilePath = path.join(workspaceFolder, '.devcontainer-lock.json'); - const expectedPath = path.join(workspaceFolder, 'expected.devcontainer-lock.json'); + // -- Graduated lockfile tests -- + + async function isolateFixture(name: string): Promise { + const src = path.join(__dirname, 'configs', name); + const dst = path.join(__dirname, 'tmp-fixtures', `${name}-${process.hrtime.bigint()}`); + await shellExec(`mkdir -p ${path.dirname(dst)} && cp -r ${src} ${dst}`); + return dst; + } + + async function lockfileExists(p: string): Promise { + return readLocalFile(p).then(() => true, err => { + if (err?.code === 'ENOENT') { + return false; + } + throw err; + }); + } + + after(async () => { + await shellExec(`rm -rf ${path.join(__dirname, 'tmp-fixtures')}`); + }); + + it('auto-generates lockfile by default without any flags', async () => { + const tmpDir = await isolateFixture('lockfile'); + const lockfilePath = path.join(tmpDir, '.devcontainer-lock.json'); + // Remove the committed lockfile so we can verify auto-creation from scratch. + await rmLocal(lockfilePath, { force: true }); + + const res = await shellExec(`${cli} build --workspace-folder ${tmpDir}`); + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'success'); + + const actual = await readLocalFile(lockfilePath); + const expected = await readLocalFile(path.join(tmpDir, 'expected.devcontainer-lock.json')); + assert.equal(actual.toString(), expected.toString()); + }); + + it('--no-lockfile prevents lockfile creation', async () => { + const tmpDir = await isolateFixture('lockfile-no-lockfile'); + const lockfilePath = path.join(tmpDir, '.devcontainer-lock.json'); + + const res = await shellExec(`${cli} build --workspace-folder ${tmpDir} --no-lockfile`); + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'success'); + + assert.equal(await lockfileExists(lockfilePath), false, 'Lockfile should not be created when --no-lockfile is set'); + }); + + it('--no-lockfile ignores existing lockfile', async () => { + const tmpDir = await isolateFixture('lockfile-frozen'); + const lockfilePath = path.join(tmpDir, '.devcontainer-lock.json'); + + const lockfileBefore = (await readLocalFile(lockfilePath)).toString(); + + const res = await shellExec(`${cli} build --workspace-folder ${tmpDir} --no-lockfile`); + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'success'); + + const lockfileAfter = (await readLocalFile(lockfilePath)).toString(); + assert.equal(lockfileAfter, lockfileBefore, 'Lockfile should be unchanged when --no-lockfile is set'); + }); + + it('--frozen-lockfile succeeds with matching lockfile', async () => { + const tmpDir = await isolateFixture('lockfile-frozen'); + const lockfilePath = path.join(tmpDir, '.devcontainer-lock.json'); + const lockfileBefore = (await readLocalFile(lockfilePath)).toString(); + + const res = await shellExec(`${cli} build --workspace-folder ${tmpDir} --frozen-lockfile`); + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'success'); + + const lockfileAfter = (await readLocalFile(lockfilePath)).toString(); + assert.equal(lockfileAfter, lockfileBefore, 'Lockfile should be unchanged'); + }); + + it('--frozen-lockfile fails when lockfile missing', async () => { + const tmpDir = await isolateFixture('lockfile-no-lockfile'); try { - await rmLocal(lockfilePath, { force: true }); + throw await shellExec(`${cli} build --workspace-folder ${tmpDir} --frozen-lockfile`); + } catch (res) { + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'error'); + assert.equal(response.message, 'Lockfile does not exist.'); + } + }); + + for (const secondFlag of ['--frozen-lockfile', '--experimental-frozen-lockfile', '--experimental-lockfile']) { + it(`--no-lockfile and ${secondFlag} are mutually exclusive`, async () => { + const tmpDir = await isolateFixture('lockfile-no-lockfile'); + + try { + throw await shellExec(`${cli} build --workspace-folder ${tmpDir} --no-lockfile ${secondFlag}`); + } catch (res) { + assert.match(res.stderr, /mutually exclusive/i, 'Should fail with mutually exclusive error'); + } + }); + } + + for (const { fixture, flag } of [ + { fixture: 'lockfile', flag: '--experimental-lockfile' }, + { fixture: 'lockfile-frozen', flag: '--experimental-frozen-lockfile' }, + ]) { + it(`deprecation warning for ${flag}`, async () => { + const tmpDir = await isolateFixture(fixture); + + const res = await shellExec(`${cli} build --workspace-folder ${tmpDir} ${flag}`); + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'success'); + assert.ok(res.stderr.includes(`${flag} is deprecated`), 'Should emit deprecation warning'); + }); + } + + it('devcontainer up auto-generates lockfile by default', async () => { + const tmpDir = await isolateFixture('lockfile-no-lockfile'); + const lockfilePath = path.join(tmpDir, '.devcontainer-lock.json'); + const idLabel = `test-lockfile-up=${process.hrtime.bigint()}`; - // Build without any lockfile flags - const res = await shellExec(`${cli} build --workspace-folder ${workspaceFolder}`); + try { + const res = await shellExec(`${cli} up --workspace-folder ${tmpDir} --id-label ${idLabel}`); const response = JSON.parse(res.stdout); assert.equal(response.outcome, 'success'); - // Lockfile should not have been created - let exists = true; - await readLocalFile(lockfilePath).catch(err => { - if (err?.code === 'ENOENT') { - exists = false; - } else { - throw err; - } - }); - assert.equal(exists, false, 'Lockfile should not be created when no lockfile flags are set'); + const actual = await readLocalFile(lockfilePath); + assert.ok(actual.toString().trim().length > 0, 'Lockfile should have been created'); + const parsed = JSON.parse(actual.toString()); + assert.ok(parsed.features, 'Lockfile should contain features'); } finally { - // Restore from the known-good expected lockfile - await cpLocal(expectedPath, lockfilePath); + // Clean up by id-label so cleanup happens even if `up` failed before returning a containerId. + await shellExec(`docker rm -f $(docker ps -aq --filter label=${idLabel}) 2>/dev/null || true`, undefined, true, true); + } + }); + + it('devcontainer up --frozen-lockfile succeeds with matching lockfile', async () => { + const tmpDir = await isolateFixture('lockfile-frozen'); + const lockfilePath = path.join(tmpDir, '.devcontainer-lock.json'); + const lockfileBefore = (await readLocalFile(lockfilePath)).toString(); + const idLabel = `test-lockfile-up-frozen=${process.hrtime.bigint()}`; + + try { + const res = await shellExec(`${cli} up --workspace-folder ${tmpDir} --id-label ${idLabel} --frozen-lockfile`); + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'success'); + + const lockfileAfter = (await readLocalFile(lockfilePath)).toString(); + assert.equal(lockfileAfter, lockfileBefore, 'Lockfile should be unchanged'); + } finally { + await shellExec(`docker rm -f $(docker ps -aq --filter label=${idLabel}) 2>/dev/null || true`, undefined, true, true); + } + }); + + it('devcontainer up --frozen-lockfile fails when lockfile missing', async () => { + const tmpDir = await isolateFixture('lockfile-no-lockfile'); + const idLabel = `test-lockfile-up-frozen-fail=${process.hrtime.bigint()}`; + + try { + throw await shellExec(`${cli} up --workspace-folder ${tmpDir} --id-label ${idLabel} --frozen-lockfile`); + } catch (res) { + const response = JSON.parse(res.stdout); + assert.equal(response.outcome, 'error'); + assert.equal(response.message, 'Lockfile does not exist.'); + } finally { + await shellExec(`docker rm -f $(docker ps -aq --filter label=${idLabel}) 2>/dev/null || true`, undefined, true, true); } }); + + it('read-only commands do not create a lockfile', async () => { + const readConfigTmpDir = await isolateFixture('lockfile-no-lockfile'); + const readConfigLockfilePath = path.join(readConfigTmpDir, '.devcontainer-lock.json'); + + // read-configuration should not create a lockfile + await shellExec(`${cli} read-configuration --workspace-folder ${readConfigTmpDir} --include-features-configuration`, undefined, true); + assert.equal(await lockfileExists(readConfigLockfilePath), false, 'read-configuration should not create a lockfile'); + + const resolveDepsTmpDir = await isolateFixture('lockfile-no-lockfile'); + const resolveDepsLockfilePath = path.join(resolveDepsTmpDir, '.devcontainer-lock.json'); + + await shellExec(`${cli} features resolve-dependencies --workspace-folder ${resolveDepsTmpDir}`, undefined, true); + assert.equal(await lockfileExists(resolveDepsLockfilePath), false, 'features resolve-dependencies should not create a lockfile'); + }); }); \ No newline at end of file From 65f98a518a1f62355a08e6f38e9d6bfb9a0d8ac9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 09:04:01 +0000 Subject: [PATCH 88/88] Bump ip-address from 10.1.0 to 10.2.0 Bumps [ip-address](https://github.com/beaugunderson/ip-address) from 10.1.0 to 10.2.0. - [Commits](https://github.com/beaugunderson/ip-address/commits) --- updated-dependencies: - dependency-name: ip-address dependency-version: 10.2.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a1fcebea3..c70431cf8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1681,9 +1681,9 @@ internal-slot@^1.1.0: side-channel "^1.1.0" ip-address@^10.0.1: - version "10.1.0" - resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.1.0.tgz#d8dcffb34d0e02eb241427444a6e23f5b0595aa4" - integrity sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q== + version "10.2.0" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.2.0.tgz#805fc178b20c518bd4c8548b24fe30892d7f3206" + integrity sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA== is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: version "3.0.5"