diff --git a/README.md b/README.md index 683f539ec..a83114e48 100644 --- a/README.md +++ b/README.md @@ -92,15 +92,14 @@ If there is no Known Good Release for the requested package manager, Corepack looks up the npm registry for the latest available version and cache it for future use. -The Known Good Releases can be updated system-wide using the `--activate` flag -from the `corepack prepare` and `corepack hydrate` commands. +The Known Good Releases can be updated system-wide using `corepack install -g`. ## Offline Workflow The utility commands detailed in the next section. - Either you can use the network while building your container image, in which - case you'll simply run `corepack prepare` to make sure that your image + case you'll simply run `corepack pack` to make sure that your image includes the Last Known Good release for the specified package manager. - If you want to have _all_ Last Known Good releases for all package managers, @@ -108,10 +107,10 @@ The utility commands detailed in the next section. - Or you're publishing your project to a system where the network is unavailable, in which case you'll preemptively generate a package manager - archive from your local computer (using `corepack prepare -o`) before storing + archive from your local computer (using `corepack pack -o`) before storing it somewhere your container will be able to access (for example within your repository). After that it'll just be a matter of running - `corepack hydrate ` to setup the cache. + `corepack install -g --cache-only ` to setup the cache. ## Utility Commands @@ -171,29 +170,52 @@ echo "function npx { corepack npx `$args }" >> $PROFILE This command will detect where Node.js is installed and will remove the shims from there. -### `corepack prepare [... name@version]` +### `corepack install` -| Option | Description | -| ------------- | ----------------------------------------------------------------------- | -| `--all` | Prepare the "Last Known Good" version of all supported package managers | -| `-o,--output` | Also generate an archive containing the package managers | -| `--activate` | Also update the "Last Known Good" release | +Download and install the package manager configured in the local project. +This command doesn't change the global version used when running the package +manager from outside the project (use the \`-g,--global\` flag if you wish +to do this). -This command will download the given package managers (or the one configured for -the local project if no argument is passed in parameter) and store it within the -Corepack cache. If the `-o,--output` flag is set (optionally with a path as -parameter), an archive will also be generated that can be used by the -`corepack hydrate` command. +### `corepack install <-g,--global> [--all] [... name@version]` -### `corepack hydrate ` +| Option | Description | +| --------------------- | ------------------------------------------ | +| `--all` | Install all Last Known Good releases | + +Install the selected package managers and install them on the system. + +Package managers thus installed will be configured as the new default when +calling their respective binaries outside of projects defining the +`packageManager` field. + +### `corepack pack [--all] [... name@version]` + +| Option | Description | +| --------------------- | ------------------------------------------ | +| `--all` | Pack all Last Known Good releases | +| `--json ` | Print the output folder rather than logs | +| `-o,--output ` | Path where to generate the archive | + +Download the selected package managers and store them inside a tarball +suitable for use with `corepack install -g`. + +### `corepack use ` + +When run, this command will retrieve the latest release matching the provided +descriptor, assign it to the project's package.json file, and automatically +perform an install. + +### `corepack up` -| Option | Description | -| ------------ | ----------------------------------------- | -| `--activate` | Also update the "Last Known Good" release | +Retrieve the latest available version for the current major release line of +the package manager used in the local project, and update the project to use +it. -This command will retrieve the given package manager from the specified archive -and will install it within the Corepack cache, ready to be used without further -network interaction. +Unlike `corepack use` this command doesn't take a package manager name nor a +version range, as it will always select the latest available version from the +same major line. Should you need to upgrade to a new major, use an explicit +`corepack use {name}@latest` call. ## Environment Variables @@ -204,7 +226,7 @@ network interaction. - `COREPACK_ENABLE_NETWORK` can be set to `0` to prevent Corepack from accessing the network (in which case you'll be responsible for hydrating the package manager versions that will be required for the projects you'll run, using - `corepack hydrate`). + `corepack install -g --cache-only`). - `COREPACK_ENABLE_STRICT` can be set to `0` to prevent Corepack from throwing error if the package manager does not correspond to the one defined for the diff --git a/config.json b/config.json index 7a549bc97..3c59279a0 100644 --- a/config.json +++ b/config.json @@ -27,6 +27,9 @@ "registry": { "type": "npm", "package": "npm" + }, + "commands": { + "use": ["npm", "install"] } } } @@ -62,6 +65,9 @@ "registry": { "type": "npm", "package": "pnpm" + }, + "commands": { + "use": ["pnpm", "install"] } }, ">=6.0.0": { @@ -73,6 +79,9 @@ "registry": { "type": "npm", "package": "pnpm" + }, + "commands": { + "use": ["pnpm", "install"] } } } @@ -102,6 +111,9 @@ "registry": { "type": "npm", "package": "yarn" + }, + "commands": { + "use": ["yarn", "install"] } }, ">=2.0.0": { @@ -118,6 +130,9 @@ "tags": "latest", "versions": "tags" } + }, + "commands": { + "use": ["yarn", "install"] } } } diff --git a/package.json b/package.json index 764559e2b..e29482824 100644 --- a/package.json +++ b/package.json @@ -24,14 +24,14 @@ "@jest/globals": "^29.0.0", "@types/debug": "^4.1.5", "@types/jest": "^29.0.0", - "@types/node": "^20.0.0", + "@types/node": "^20.4.6", "@types/semver": "^7.1.0", "@types/tar": "^6.0.0", "@types/which": "^3.0.0", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "@yarnpkg/eslint-config": "^0.6.0-rc.7", - "@yarnpkg/fslib": "^2.1.0", + "@yarnpkg/fslib": "^3.0.0-rc.48", "@zkochan/cmd-shim": "^6.0.0", "babel-plugin-dynamic-import-node": "^2.3.3", "clipanion": "^3.0.1", diff --git a/sources/Engine.ts b/sources/Engine.ts index 71613bf6b..8cf551434 100644 --- a/sources/Engine.ts +++ b/sources/Engine.ts @@ -12,6 +12,7 @@ import * as semverUtils from './semverUtil import {Config, Descriptor, Locator} from './types'; import {SupportedPackageManagers, SupportedPackageManagerSet} from './types'; +export type PreparedPackageManagerInfo = Awaited>; export class Engine { constructor(public config: Config = defaultConfig as Config) { @@ -33,6 +34,19 @@ export class Engine { return null; } + getPackageManagerSpecFor(locator: Locator) { + const definition = this.config.definitions[locator.name]; + if (typeof definition === `undefined`) + throw new UsageError(`This package manager (${locator.name}) isn't supported by this corepack build`); + + const ranges = Object.keys(definition.ranges).reverse(); + const range = ranges.find(range => semverUtils.satisfiesWithPrereleases(locator.reference, range)); + if (typeof range === `undefined`) + throw new Error(`Assertion failed: Specified resolution (${locator.reference}) isn't supported by any of ${ranges.join(`, `)}`); + + return definition.ranges[range]; + } + getBinariesFor(name: SupportedPackageManagers) { const binNames = new Set(); @@ -111,25 +125,23 @@ export class Engine { } async ensurePackageManager(locator: Locator) { - const definition = this.config.definitions[locator.name]; - if (typeof definition === `undefined`) - throw new UsageError(`This package manager (${locator.name}) isn't supported by this corepack build`); - - const ranges = Object.keys(definition.ranges).reverse(); - const range = ranges.find(range => semverUtils.satisfiesWithPrereleases(locator.reference, range)); - if (typeof range === `undefined`) - throw new Error(`Assertion failed: Specified resolution (${locator.reference}) isn't supported by any of ${ranges.join(`, `)}`); + const spec = this.getPackageManagerSpecFor(locator); - const installedLocation = await corepackUtils.installVersion(folderUtils.getInstallFolder(), locator, { - spec: definition.ranges[range], + const packageManagerInfo = await corepackUtils.installVersion(folderUtils.getInstallFolder(), locator, { + spec, }); return { - location: installedLocation, - spec: definition.ranges[range], + ...packageManagerInfo, + locator, + spec, }; } + async fetchAvailableVersions() { + + } + async resolveDescriptor(descriptor: Descriptor, {allowTags = false, useCache = true}: {allowTags?: boolean, useCache?: boolean} = {}) { const definition = this.config.definitions[descriptor.name]; if (typeof definition === `undefined`) @@ -138,7 +150,7 @@ export class Engine { let finalDescriptor = descriptor; if (!semver.valid(descriptor.range) && !semver.validRange(descriptor.range)) { if (!allowTags) - throw new UsageError(`Packages managers can't be referended via tags in this context`); + throw new UsageError(`Packages managers can't be referenced via tags in this context`); // We only resolve tags from the latest registry entry const ranges = Object.keys(definition.ranges); @@ -165,28 +177,16 @@ export class Engine { if (semver.valid(finalDescriptor.range)) return {name: finalDescriptor.name, reference: finalDescriptor.range}; - const candidateRangeDefinitions = Object.keys(definition.ranges).filter(range => { - return semverUtils.satisfiesWithPrereleases(finalDescriptor.range, range); - }); - - const tagResolutions = await Promise.all(candidateRangeDefinitions.map(async range => { - return [range, await corepackUtils.fetchAvailableVersions(definition.ranges[range].registry)] as const; + const versions = await Promise.all(Object.keys(definition.ranges).map(async range => { + const versions = await corepackUtils.fetchAvailableVersions(definition.ranges[range].registry); + return versions.filter(version => semverUtils.satisfiesWithPrereleases(version, finalDescriptor.range)); })); - // If a version is available under multiple strategies (for example if - // Yarn is published to both the v1 package and git), we only care - // about the latest one - const resolutionMap = new Map(); - for (const [range, resolutions] of tagResolutions) - for (const entry of resolutions) - resolutionMap.set(entry, range); - - const candidates = [...resolutionMap.keys()]; - const maxSatisfying = semver.maxSatisfying(candidates, finalDescriptor.range); - if (maxSatisfying === null) + const highestVersion = [...new Set(versions.flat())].sort(semver.rcompare); + if (highestVersion.length === 0) return null; - return {name: finalDescriptor.name, reference: maxSatisfying}; + return {name: finalDescriptor.name, reference: highestVersion[0]}; } private getLastKnownGoodFile() { diff --git a/sources/commands/Base.ts b/sources/commands/Base.ts new file mode 100644 index 000000000..01139766b --- /dev/null +++ b/sources/commands/Base.ts @@ -0,0 +1,64 @@ +import {Command, UsageError} from 'clipanion'; +import fs from 'fs'; + +import {PreparedPackageManagerInfo} from '../Engine'; +import * as corepackUtils from '../corepackUtils'; +import {Context} from '../main'; +import * as nodeUtils from '../nodeUtils'; +import * as specUtils from '../specUtils'; + +export abstract class BaseCommand extends Command { + async resolvePatternsToDescriptors({all, patterns}: {all: boolean, patterns: Array}) { + if (all && patterns.length > 0) + throw new UsageError(`The --all option cannot be used along with an explicit package manager specification`); + + const resolvedSpecs = all + ? await this.context.engine.getDefaultDescriptors() + : patterns.map(pattern => specUtils.parseSpec(pattern, `CLI arguments`, {enforceExactVersion: false})); + + if (resolvedSpecs.length === 0) { + const lookup = await specUtils.loadSpec(this.context.cwd); + switch (lookup.type) { + case `NoProject`: + throw new UsageError(`Couldn't find a project in the local directory - please explicit the package manager to pack, or run this command from a valid project`); + + case `NoSpec`: + throw new UsageError(`The local project doesn't feature a 'packageManager' field - please explicit the package manager to pack, or update the manifest to reference it`); + + default: { + return [lookup.spec]; + } + } + } + + return resolvedSpecs; + } + + async setLocalPackageManager(info: PreparedPackageManagerInfo) { + const lookup = await specUtils.loadSpec(this.context.cwd); + + const content = lookup.target !== `NoProject` + ? await fs.promises.readFile(lookup.target, `utf8`) + : ``; + + const {data, indent} = nodeUtils.readPackageJson(content); + + const previousPackageManager = data.packageManager ?? `unknown`; + data.packageManager = `${info.locator.name}@${info.locator.reference}+${info.hash}`; + + const newContent = nodeUtils.normalizeLineEndings(content, `${JSON.stringify(data, null, indent)}\n`); + await fs.promises.writeFile(lookup.target, newContent, `utf8`); + + const command = this.context.engine.getPackageManagerSpecFor(info.locator).commands?.use ?? null; + if (command === null) + return 0; + + // Adding it into the environment avoids breaking package managers that + // don't expect those options. + process.env.COREPACK_MIGRATE_FROM = previousPackageManager; + this.context.stdout.write(`\n`); + + const [binaryName, ...args] = command; + return await corepackUtils.runVersion(info.locator, info, binaryName, args); + } +} diff --git a/sources/commands/InstallGlobal.ts b/sources/commands/InstallGlobal.ts new file mode 100644 index 000000000..788fb1f1b --- /dev/null +++ b/sources/commands/InstallGlobal.ts @@ -0,0 +1,132 @@ +import {Command, Option, UsageError} from 'clipanion'; +import fs from 'fs'; +import path from 'path'; + +import * as folderUtils from '../folderUtils'; +import * as specUtils from '../specUtils'; +import {Descriptor, Locator, isSupportedPackageManager} from '../types'; + +import {BaseCommand} from './Base'; + +export class InstallGlobalCommand extends BaseCommand { + static paths = [ + [`install`], + ]; + + static usage = Command.Usage({ + description: `Install package managers on the system`, + details: ` + Download the selected package managers and install them on the system. + + Package managers thus installed will be configured as the new default when calling their respective binaries outside of projects defining the 'packageManager' field. + `, + examples: [[ + `Install the latest version of Yarn 1.x and make it globally available`, + `corepack install -g yarn@^1`, + ], [ + `Install the latest version of all available package managers, and make them globally available`, + `corepack install -g --all`, + ]], + }); + + global = Option.Boolean(`-g,--global`, { + required: true, + }); + + all = Option.Boolean(`--all`, false, { + description: `If true, all available default package managers will be installed`, + }); + + cacheOnly = Option.Boolean(`--cache-only`, false, { + description: `If true, the package managers will only be cached, not set as new defaults`, + }); + + args = Option.Rest(); + + async execute() { + if (this.args.length === 0 && !this.all) + throw new UsageError(`No package managers specified; use --all to install all available package managers, or specify one or more package managers to proceed`); + + if (!this.all) { + for (const arg of this.args) { + if (arg.endsWith(`.tgz`)) { + await this.installFromTarball(path.resolve(this.context.cwd, arg)); + } else { + await this.installFromDescriptor(specUtils.parseSpec(arg, `CLI arguments`, {enforceExactVersion: false})); + } + } + } else { + for (const descriptor of await this.context.engine.getDefaultDescriptors()) { + await this.installFromDescriptor(descriptor); + } + } + } + + log(locator: Locator) { + if (this.cacheOnly) { + this.context.stdout.write(`Adding ${locator.name}@${locator.reference} to the cache...\n`); + } else { + this.context.stdout.write(`Installing ${locator.name}@${locator.reference}...\n`); + } + } + + async installFromDescriptor(descriptor: Descriptor) { + const resolved = await this.context.engine.resolveDescriptor(descriptor, {allowTags: true, useCache: false}); + if (resolved === null) + throw new UsageError(`Failed to successfully resolve '${descriptor.range}' to a valid ${descriptor.name} release`); + + this.log(resolved); + await this.context.engine.ensurePackageManager(resolved); + + if (!this.cacheOnly) { + await this.context.engine.activatePackageManager(resolved); + } + } + + async installFromTarball(p: string) { + const installFolder = folderUtils.getInstallFolder(); + + const archiveEntries = new Map>(); + const {default: tar} = await import(`tar`); + + let hasShortEntries = false; + + await tar.t({file: p, onentry: entry => { + const segments = entry.path.split(/\//g); + if (segments.length > 0 && segments[segments.length - 1] !== `.corepack`) + return; + + + if (segments.length < 3) { + hasShortEntries = true; + } else { + let references = archiveEntries.get(segments[0]); + if (typeof references === `undefined`) + archiveEntries.set(segments[0], references = new Set()); + + references.add(segments[1]); + } + }}); + + if (hasShortEntries || archiveEntries.size < 1) + throw new UsageError(`Invalid archive format; did it get generated by 'corepack pack'?`); + + for (const [name, references] of archiveEntries) { + for (const reference of references) { + if (!isSupportedPackageManager(name)) + throw new UsageError(`Unsupported package manager '${name}'`); + + this.log({name, reference}); + + // Recreate the folder in case it was deleted somewhere else: + await fs.promises.mkdir(installFolder, {recursive: true}); + + await tar.x({file: p, cwd: installFolder}, [`${name}/${reference}`]); + + if (!this.cacheOnly) { + await this.context.engine.activatePackageManager({name, reference}); + } + } + } + } +} diff --git a/sources/commands/InstallLocal.ts b/sources/commands/InstallLocal.ts new file mode 100644 index 000000000..ddcdb13d6 --- /dev/null +++ b/sources/commands/InstallLocal.ts @@ -0,0 +1,34 @@ +import {Command, UsageError} from 'clipanion'; + +import {BaseCommand} from './Base'; + +export class InstallLocalCommand extends BaseCommand { + static paths = [ + [`install`], + ]; + + static usage = Command.Usage({ + description: `Install the package manager configured in the local project`, + details: ` + Download and install the package manager configured in the local project. This command doesn't change the global version used when running the package manager from outside the project (use the \`-g,--global\` flag if you wish to do this). + `, + examples: [[ + `Install the project's package manager in the cache`, + `corepack install`, + ]], + }); + + async execute() { + const [descriptor] = await this.resolvePatternsToDescriptors({ + all: false, + patterns: [], + }); + + const resolved = await this.context.engine.resolveDescriptor(descriptor, {allowTags: true}); + if (resolved === null) + throw new UsageError(`Failed to successfully resolve '${descriptor.range}' to a valid ${descriptor.name} release`); + + this.context.stdout.write(`Adding ${resolved.name}@${resolved.reference} to the cache...\n`); + await this.context.engine.ensurePackageManager(resolved); + } +} diff --git a/sources/commands/Pack.ts b/sources/commands/Pack.ts new file mode 100644 index 000000000..665fc3505 --- /dev/null +++ b/sources/commands/Pack.ts @@ -0,0 +1,87 @@ +import {Command, Option, UsageError} from 'clipanion'; +import {mkdir} from 'fs/promises'; +import path from 'path'; + +import * as folderUtils from '../folderUtils'; + +import {BaseCommand} from './Base'; + +export class PackCommand extends BaseCommand { + static paths = [ + [`pack`], + ]; + + static usage = Command.Usage({ + description: `Store package managers in a tarball`, + details: ` + Download the selected package managers and store them inside a tarball suitable for use with \`corepack install -g\`. + `, + examples: [[ + `Pack the package manager defined in the package.json file`, + `corepack pack`, + ], [ + `Pack the latest version of Yarn 1.x inside a file named corepack.tgz`, + `corepack pack yarn@^1`, + ], [ + `Pack the latest versions of all supported package managers inside a file named everything.tgz`, + `corepack pack --all -o everything.tgz`, + ]], + }); + + all = Option.Boolean(`--all`, false, { + description: `If true, all available default package managers will be installed`, + }); + + json = Option.Boolean(`--json`, false, { + description: `If true, the path to the generated tarball will be printed on stdout`, + }); + + output = Option.String(`-o,--output`, { + description: `Where the tarball should be generated; by default "corepack.tgz"`, + }); + + patterns = Option.Rest(); + + async execute() { + const descriptors = await this.resolvePatternsToDescriptors({ + all: this.all, + patterns: this.patterns, + }); + + const installLocations: Array = []; + + for (const descriptor of descriptors) { + const resolved = await this.context.engine.resolveDescriptor(descriptor, {allowTags: true, useCache: false}); + if (resolved === null) + throw new UsageError(`Failed to successfully resolve '${descriptor.range}' to a valid ${descriptor.name} release`); + + this.context.stdout.write(`Adding ${resolved.name}@${resolved.reference} to the cache...\n`); + const packageManagerInfo = await this.context.engine.ensurePackageManager(resolved); + + await this.context.engine.activatePackageManager(packageManagerInfo.locator); + installLocations.push(packageManagerInfo.location); + } + + const baseInstallFolder = folderUtils.getInstallFolder(); + const outputPath = path.resolve(this.context.cwd, this.output ?? `corepack.tgz`); + + if (!this.json) { + this.context.stdout.write(`\n`); + this.context.stdout.write(`Packing the selected tools in ${path.basename(outputPath)}...\n`); + } + + const {default: tar} = await import(`tar`); + + // Recreate the folder in case it was deleted somewhere else: + await mkdir(baseInstallFolder, {recursive: true}); + await tar.c({gzip: true, cwd: baseInstallFolder, file: path.resolve(outputPath)}, installLocations.map(location => { + return path.relative(baseInstallFolder, location); + })); + + if (this.json) { + this.context.stdout.write(`${JSON.stringify(outputPath)}\n`); + } else { + this.context.stdout.write(`All done!\n`); + } + } +} diff --git a/sources/commands/Up.ts b/sources/commands/Up.ts new file mode 100644 index 000000000..696ce2d07 --- /dev/null +++ b/sources/commands/Up.ts @@ -0,0 +1,54 @@ +import {Command, UsageError} from 'clipanion'; +import semver from 'semver'; + +import {BaseCommand} from './Base'; + +export class UpCommand extends BaseCommand { + static paths = [ + [`up`], + ]; + + static usage = Command.Usage({ + description: `Update the package manager used in the current project`, + details: ` + Retrieve the latest available version for the current major release line + of the package manager used in the local project, and update the project + to use it. + + Unlike \`corepack use\` this command doesn't take a package manager name + nor a version range, as it will always select the latest available + version from the same major line. Should you need to upgrade to a new + major, use an explicit \`corepack use '{name}@*'\` call. + `, + examples: [[ + `Configure the project to use the latest Yarn release`, + `corepack up`, + ]], + }); + + async execute() { + const [descriptor] = await this.resolvePatternsToDescriptors({ + all: false, + patterns: [], + }); + + if (!semver.valid(descriptor.range) && !semver.validRange(descriptor.range)) + throw new UsageError(`The 'corepack up' command can only be used when your project's packageManager field is set to a semver version or semver range`); + + const resolved = await this.context.engine.resolveDescriptor(descriptor, {useCache: false}); + if (!resolved) + throw new UsageError(`Failed to successfully resolve '${descriptor.range}' to a valid ${descriptor.name} release`); + + const majorVersion = semver.major(resolved?.reference); + const majorDescriptor = {name: descriptor.name, range: `^${majorVersion}.0.0`}; + + const highestVersion = await this.context.engine.resolveDescriptor(majorDescriptor, {useCache: false}); + if (!highestVersion) + throw new UsageError(`Failed to find the highest release for ${descriptor.name} ${majorVersion}.x`); + + this.context.stdout.write(`Installing ${highestVersion.name}@${highestVersion.reference} in the project...\n`); + + const packageManagerInfo = await this.context.engine.ensurePackageManager(highestVersion); + await this.setLocalPackageManager(packageManagerInfo); + } +} diff --git a/sources/commands/Use.ts b/sources/commands/Use.ts new file mode 100644 index 000000000..aa75db9ae --- /dev/null +++ b/sources/commands/Use.ts @@ -0,0 +1,40 @@ +import {Command, Option, UsageError} from 'clipanion'; + +import {BaseCommand} from './Base'; + +export class UseCommand extends BaseCommand { + static paths = [ + [`use`], + ]; + + static usage = Command.Usage({ + description: `Define the package manager to use for the current project`, + details: ` + When run, this command will retrieve the latest release matching the + provided descriptor, assign it to the project's package.json file, and + automatically perform an install. + `, + examples: [[ + `Configure the project to use the latest Yarn release`, + `corepack use 'yarn@*'`, + ]], + }); + + pattern = Option.String(); + + async execute() { + const [descriptor] = await this.resolvePatternsToDescriptors({ + all: false, + patterns: [this.pattern], + }); + + const resolved = await this.context.engine.resolveDescriptor(descriptor, {allowTags: true, useCache: false}); + if (resolved === null) + throw new UsageError(`Failed to successfully resolve '${descriptor.range}' to a valid ${descriptor.name} release`); + + this.context.stdout.write(`Installing ${resolved.name}@${resolved.reference} in the project...\n`); + + const packageManagerInfo = await this.context.engine.ensurePackageManager(resolved); + await this.setLocalPackageManager(packageManagerInfo); + } +} diff --git a/sources/commands/Hydrate.ts b/sources/commands/deprecated/Hydrate.ts similarity index 79% rename from sources/commands/Hydrate.ts rename to sources/commands/deprecated/Hydrate.ts index 6f8910d50..2d7f1d23f 100644 --- a/sources/commands/Hydrate.ts +++ b/sources/commands/deprecated/Hydrate.ts @@ -2,26 +2,15 @@ import {Command, Option, UsageError} from 'clipanion'; import {mkdir} from 'fs/promises'; import path from 'path'; -import * as folderUtils from '../folderUtils'; -import {Context} from '../main'; -import {isSupportedPackageManager} from '../types'; +import * as folderUtils from '../../folderUtils'; +import {Context} from '../../main'; +import {isSupportedPackageManager} from '../../types'; export class HydrateCommand extends Command { static paths = [ [`hydrate`], ]; - static usage = Command.Usage({ - description: `Import a package manager into the cache`, - details: ` - This command unpacks a package manager archive into the cache. The archive must have been generated by the \`corepack prepare\` command - no other will work. - `, - examples: [[ - `Import a package manager in the cache`, - `$0 hydrate corepack.tgz`, - ]], - }); - activate = Option.Boolean(`--activate`, false, { description: `If true, this release will become the default one for this package manager`, }); diff --git a/sources/commands/Prepare.ts b/sources/commands/deprecated/Prepare.ts similarity index 73% rename from sources/commands/Prepare.ts rename to sources/commands/deprecated/Prepare.ts index 73c5b9306..c9acc5107 100644 --- a/sources/commands/Prepare.ts +++ b/sources/commands/deprecated/Prepare.ts @@ -2,41 +2,16 @@ import {Command, Option, UsageError} from 'clipanion'; import {mkdir} from 'fs/promises'; import path from 'path'; -import * as folderUtils from '../folderUtils'; -import {Context} from '../main'; -import * as specUtils from '../specUtils'; -import {Descriptor} from '../types'; +import * as folderUtils from '../../folderUtils'; +import {Context} from '../../main'; +import * as specUtils from '../../specUtils'; +import {Descriptor} from '../../types'; export class PrepareCommand extends Command { static paths = [ [`prepare`], ]; - static usage = Command.Usage({ - description: `Generate a package manager archive`, - details: ` - This command makes sure that the specified package managers are installed in the local cache. Calling this command explicitly unless you operate in an environment without network access (in which case you'd have to call \`prepare\` while building your image, to make sure all tools are available for later use). - - When the \`-o,--output\` flag is set, Corepack will also compress the resulting package manager into a format suitable for \`corepack hydrate\`, and will store it at the specified location on the disk. - `, - examples: [[ - `Prepare the package manager from the active project`, - `$0 prepare`, - ], [ - `Prepare a specific Yarn version`, - `$0 prepare yarn@2.2.2`, - ], [ - `Prepare the latest available pnpm version`, - `$0 prepare pnpm@latest --activate`, - ], [ - `Generate an archive for a specific Yarn version`, - `$0 prepare yarn@2.2.2 -o`, - ], [ - `Generate a named archive`, - `$0 prepare yarn@2.2.2 --output=yarn.tgz`, - ]], - }); - activate = Option.Boolean(`--activate`, false, { description: `If true, this release will become the default one for this package manager`, }); diff --git a/sources/corepackUtils.ts b/sources/corepackUtils.ts index 48549e4cf..a08b9ded2 100644 --- a/sources/corepackUtils.ts +++ b/sources/corepackUtils.ts @@ -14,7 +14,7 @@ import * as nodeUtils from './nodeUtils import * as npmRegistryUtils from './npmRegistryUtils'; import {RegistrySpec, Descriptor, Locator, PackageManagerSpec} from './types'; -export async function fetchLatestStableVersion(spec: RegistrySpec) { +export async function fetchLatestStableVersion(spec: RegistrySpec): Promise { switch (spec.type) { case `npm`: { return await npmRegistryUtils.fetchLatestStableVersion(spec.package); @@ -101,9 +101,20 @@ export async function installVersion(installTarget: string, locator: Locator, {s const {version, build} = semver.parse(locator.reference)!; const installFolder = path.join(installTarget, locator.name, version); - if (fs.existsSync(installFolder)) { + const corepackFile = path.join(installFolder, `.corepack`); + + // Older versions of Corepack didn't generate the `.corepack` file; in + // that case we just download the package manager anew. + if (fs.existsSync(corepackFile)) { + const corepackContent = await fs.promises.readFile(corepackFile, `utf8`); + const corepackData = JSON.parse(corepackContent); + debugUtils.log(`Reusing ${locator.name}@${locator.reference}`); - return installFolder; + + return { + hash: corepackData.hash as string, + location: installFolder, + }; } const defaultNpmRegistryURL = spec.url.replace(`{}`, version); @@ -137,16 +148,29 @@ export async function installVersion(installTarget: string, locator: Locator, {s stream.pipe(sendTo); - const hash = build[0] - ? stream.pipe(createHash(build[0])) - : null; - + const algo = build[0] ?? `sha256`; + const hash = stream.pipe(createHash(algo)); await once(sendTo, `finish`); - const actualHash = hash?.digest(`hex`); - if (actualHash !== build[1]) + const actualHash = hash.digest(`hex`); + if (build[1] && actualHash !== build[1]) throw new Error(`Mismatch hashes. Expected ${build[1]}, got ${actualHash}`); + const serializedHash = `${algo}.${actualHash}`; + + await fs.promises.writeFile(path.join(tmpFolder, `.corepack`), JSON.stringify({ + locator, + hash: serializedHash, + })); + + // The target folder may exist if a previous version of Corepack installed + // it but didn't create the `.corepack` file. In this case we need to + // remove it first. + await fs.promises.rm(installFolder, { + recursive: true, + force: true, + }); + await fs.promises.mkdir(path.dirname(installFolder), {recursive: true}); try { await fs.promises.rename(tmpFolder, installFolder); @@ -164,7 +188,11 @@ export async function installVersion(installTarget: string, locator: Locator, {s } debugUtils.log(`Install finished`); - return installFolder; + + return { + location: installFolder, + hash: serializedHash, + }; } /** diff --git a/sources/main.ts b/sources/main.ts index 6f78a9e17..9b880ee05 100644 --- a/sources/main.ts +++ b/sources/main.ts @@ -5,8 +5,13 @@ import {version as corepackVersion} from '../packag import {Engine} from './Engine'; import {DisableCommand} from './commands/Disable'; import {EnableCommand} from './commands/Enable'; -import {HydrateCommand} from './commands/Hydrate'; -import {PrepareCommand} from './commands/Prepare'; +import {InstallGlobalCommand} from './commands/InstallGlobal'; +import {InstallLocalCommand} from './commands/InstallLocal'; +import {PackCommand} from './commands/Pack'; +import {UpCommand} from './commands/Up'; +import {UseCommand} from './commands/Use'; +import {HydrateCommand} from './commands/deprecated/Hydrate'; +import {PrepareCommand} from './commands/deprecated/Prepare'; import * as corepackUtils from './corepackUtils'; import * as miscUtils from './miscUtils'; import * as specUtils from './specUtils'; @@ -99,6 +104,8 @@ export async function runMain(argv: Array) { const [firstArg, ...restArgs] = argv; const request = getPackageManagerRequestFromCli(firstArg, context); + let code: number; + if (!request) { // If the first argument doesn't match any supported package manager, we fallback to the standard Corepack CLI const cli = new Cli({ @@ -110,12 +117,19 @@ export async function runMain(argv: Array) { cli.register(Builtins.HelpCommand); cli.register(Builtins.VersionCommand); - cli.register(EnableCommand); cli.register(DisableCommand); + cli.register(EnableCommand); + cli.register(InstallGlobalCommand); + cli.register(InstallLocalCommand); + cli.register(PackCommand); + cli.register(UpCommand); + cli.register(UseCommand); + + // Deprecated commands cli.register(HydrateCommand); cli.register(PrepareCommand); - await cli.runExit(argv, context); + code = await cli.run(argv, context); } else { // Otherwise, we create a single-command CLI to run the specified package manager (we still use Clipanion in order to pretty-print usage errors). const cli = new Cli({ @@ -131,9 +145,10 @@ export async function runMain(argv: Array) { } }); - const code = await cli.run(restArgs, context); - if (code !== 0) { - process.exitCode ??= code; - } + code = await cli.run(restArgs, context); + } + + if (code !== 0) { + process.exitCode ??= code; } } diff --git a/sources/nodeUtils.ts b/sources/nodeUtils.ts index 14b7c820e..35956fbc4 100644 --- a/sources/nodeUtils.ts +++ b/sources/nodeUtils.ts @@ -1,3 +1,46 @@ +import os from 'os'; + export interface NodeError extends Error { code: string; } + +function getEndOfLine(content: string) { + const matches = content.match(/\r?\n/g); + if (matches === null) + return os.EOL; + + const crlf = matches.filter(nl => nl === `\r\n`).length; + const lf = matches.length - crlf; + + return crlf > lf ? `\r\n` : `\n`; +} + +export function normalizeLineEndings(originalContent: string, newContent: string) { + return newContent.replace(/\r?\n/g, getEndOfLine(originalContent)); +} + +function getIndent(content: string) { + const indentMatch = content.match(/^[ \t]+/m); + + if (indentMatch) { + return indentMatch[0]; + } else { + return ` `; + } +} + +function stripBOM(content: string) { + if (content.charCodeAt(0) === 0xFEFF) { + return content.slice(1); + } else { + return content; + } +} + + +export function readPackageJson(content: string) { + return { + data: JSON.parse(stripBOM(content) || `{}`), + indent: getIndent(content), + }; +} diff --git a/sources/npmRegistryUtils.ts b/sources/npmRegistryUtils.ts index 443552d9d..ec2445096 100644 --- a/sources/npmRegistryUtils.ts +++ b/sources/npmRegistryUtils.ts @@ -31,11 +31,12 @@ export async function fetchAsJson(packageName: string) { export async function fetchLatestStableVersion(packageName: string) { const metadata = await fetchAsJson(packageName); + const {latest} = metadata[`dist-tags`]; - if (latest === undefined) throw new Error(`${packageName} does not have a "latest" tag.`); + if (latest === undefined) + throw new Error(`${packageName} does not have a "latest" tag.`); const {shasum} = metadata.versions[latest].dist; - return `${latest}+sha1.${shasum}`; } diff --git a/sources/specUtils.ts b/sources/specUtils.ts index 3702781d0..f1bc1f434 100644 --- a/sources/specUtils.ts +++ b/sources/specUtils.ts @@ -77,7 +77,7 @@ export async function findProjectSpec(initialCwd: string, locator: Locator, {tra export type LoadSpecResult = | {type: `NoProject`, target: string} | {type: `NoSpec`, target: string} - | {type: `Found`, spec: Descriptor}; + | {type: `Found`, target: string, spec: Descriptor}; export async function loadSpec(initialCwd: string): Promise { let nextCwd = initialCwd; @@ -121,6 +121,7 @@ export async function loadSpec(initialCwd: string): Promise { return { type: `Found`, + target: selection.manifestPath, spec: parseSpec(rawPmSpec, path.relative(initialCwd, selection.manifestPath)), }; } diff --git a/sources/types.ts b/sources/types.ts index 8ed2cfd19..bc02792fa 100644 --- a/sources/types.ts +++ b/sources/types.ts @@ -47,6 +47,9 @@ export interface PackageManagerSpec { url: string; bin: BinSpec | BinList; registry: RegistrySpec; + commands?: { + use?: Array; + }; } /** diff --git a/tests/Up.test.ts b/tests/Up.test.ts new file mode 100644 index 000000000..1c78afdc5 --- /dev/null +++ b/tests/Up.test.ts @@ -0,0 +1,34 @@ +import {describe, beforeEach, it, expect} from '@jest/globals'; +import {ppath, xfs, npath} from '@yarnpkg/fslib'; +import process from 'node:process'; + +import {runCli} from './_runCli'; + +beforeEach(async () => { + process.env.COREPACK_HOME = npath.fromPortablePath(await xfs.mktempPromise()); + process.env.COREPACK_DEFAULT_TO_LATEST = `0`; +}); + +describe(`UpCommand`, () => { + it(`should upgrade the package manager from the current project`, async () => { + await xfs.mktempPromise(async cwd => { + await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), { + packageManager: `yarn@2.1.0`, + }); + + await expect(runCli(cwd, [`up`])).resolves.toMatchObject({ + exitCode: 0, + stderr: ``, + }); + + await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({ + packageManager: `yarn@2.4.3+sha256.8c1575156cfa42112242cc5cfbbd1049da9448ffcdb5c55ce996883610ea983f`, + }); + + await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({ + exitCode: 0, + stdout: `2.4.3\n`, + }); + }); + }); +}); diff --git a/tests/Use.test.ts b/tests/Use.test.ts new file mode 100644 index 000000000..e9a58d414 --- /dev/null +++ b/tests/Use.test.ts @@ -0,0 +1,32 @@ +import {describe, beforeEach, it, expect} from '@jest/globals'; +import {ppath, xfs, npath} from '@yarnpkg/fslib'; +import process from 'node:process'; + +import {runCli} from './_runCli'; + +beforeEach(async () => { + process.env.COREPACK_HOME = npath.fromPortablePath(await xfs.mktempPromise()); + process.env.COREPACK_DEFAULT_TO_LATEST = `0`; +}); + +describe(`UseCommand`, () => { + it(`should set the package manager in the current project`, async () => { + await xfs.mktempPromise(async cwd => { + await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), { + }); + + await expect(runCli(cwd, [`use`, `yarn@1.22.4`])).resolves.toMatchObject({ + exitCode: 0, + }); + + await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({ + packageManager: `yarn@1.22.4+sha256.bc5316aa110b2f564a71a3d6e235be55b98714660870c5b6b2d2d3f12587fb58`, + }); + + await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({ + exitCode: 0, + stdout: `1.22.4\n`, + }); + }); + }); +}); diff --git a/tests/main.test.ts b/tests/main.test.ts index cfdf0cd2c..f72a97fb6 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -6,8 +6,12 @@ import config from '../config.json'; import {runCli} from './_runCli'; +let corepackHome!: PortablePath; + beforeEach(async () => { - process.env.COREPACK_HOME = npath.fromPortablePath(await xfs.mktempPromise()); + corepackHome = await xfs.mktempPromise(); + + process.env.COREPACK_HOME = npath.fromPortablePath(corepackHome); process.env.COREPACK_DEFAULT_TO_LATEST = `0`; }); @@ -191,9 +195,9 @@ it(`should use the pinned version when local projects don't list any spec`, asyn }); }); -it(`should allow updating the pinned version using the "prepare" command`, async () => { +it(`should allow updating the pinned version using the "corepack install -g" command`, async () => { await xfs.mktempPromise(async cwd => { - await expect(runCli(cwd, [`prepare`, `--activate`, `yarn@1.0.0`])).resolves.toMatchObject({ + await expect(runCli(cwd, [`install`, `-g`, `yarn@1.0.0`])).resolves.toMatchObject({ exitCode: 0, stderr: ``, }); @@ -210,9 +214,9 @@ it(`should allow updating the pinned version using the "prepare" command`, async }); }); -it(`should allow to call "prepare" with a tag`, async () => { +it(`should allow to call "corepack install -g" with a tag`, async () => { await xfs.mktempPromise(async cwd => { - await expect(runCli(cwd, [`prepare`, `--activate`, `npm@latest-7`])).resolves.toMatchObject({ + await expect(runCli(cwd, [`install`, `-g`, `npm@latest-7`])).resolves.toMatchObject({ exitCode: 0, stderr: ``, }); @@ -229,13 +233,13 @@ it(`should allow to call "prepare" with a tag`, async () => { }); }); -it(`should allow to call "prepare" without arguments within a configured project`, async () => { +it(`should allow to call "corepack install" without arguments within a configured project`, async () => { await xfs.mktempPromise(async cwd => { await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), { packageManager: `yarn@1.0.0`, }); - await expect(runCli(cwd, [`prepare`, `--activate`])).resolves.toMatchObject({ + await expect(runCli(cwd, [`install`])).resolves.toMatchObject({ exitCode: 0, stderr: ``, }); @@ -314,13 +318,13 @@ it(`should always use fallback version when project spec env is disabled`, async }); }); -it(`should allow to call "prepare" with --all to prepare all package managers`, async () => { +it(`should allow to call "corepack install -g --all" to prepare all package managers`, async () => { await xfs.mktempPromise(async cwd => { await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), { // empty package.json file }); - await expect(runCli(cwd, [`prepare`, `--all`])).resolves.toMatchObject({ + await expect(runCli(cwd, [`install`, `-g`, `--all`])).resolves.toMatchObject({ exitCode: 0, stderr: ``, }); @@ -373,9 +377,9 @@ it(`should support disabling the network accesses from the environment`, async ( it(`should support hydrating package managers from cached archives`, async () => { await xfs.mktempPromise(async cwd => { - await expect(runCli(cwd, [`prepare`, `yarn@2.2.2`, `-o`])).resolves.toMatchObject({ - exitCode: 0, + await expect(runCli(cwd, [`pack`, `yarn@2.2.2`])).resolves.toMatchObject({ stderr: ``, + exitCode: 0, }); // Use a new cache @@ -385,8 +389,7 @@ it(`should support hydrating package managers from cached archives`, async () => process.env.COREPACK_ENABLE_NETWORK = `0`; try { - await expect(runCli(cwd, [`hydrate`, `corepack.tgz`])).resolves.toMatchObject({ - stdout: `Hydrating yarn@2.2.2...\nAll done!\n`, + await expect(runCli(cwd, [`install`, `-g`, `corepack.tgz`])).resolves.toMatchObject({ stderr: ``, exitCode: 0, }); @@ -408,7 +411,7 @@ it(`should support hydrating package managers from cached archives`, async () => it(`should support hydrating package managers if cache folder was removed`, async () => { await xfs.mktempPromise(async cwd => { - await expect(runCli(cwd, [`prepare`, `yarn@2.2.2`, `-o`])).resolves.toMatchObject({ + await expect(runCli(cwd, [`pack`, `yarn@2.2.2`])).resolves.toMatchObject({ exitCode: 0, stderr: ``, }); @@ -423,8 +426,7 @@ it(`should support hydrating package managers if cache folder was removed`, asyn process.env.COREPACK_ENABLE_NETWORK = `0`; try { - await expect(runCli(cwd, [`hydrate`, `corepack.tgz`])).resolves.toMatchObject({ - stdout: `Hydrating yarn@2.2.2...\nAll done!\n`, + await expect(runCli(cwd, [`install`, `-g`, `corepack.tgz`])).resolves.toMatchObject({ stderr: ``, exitCode: 0, }); @@ -446,7 +448,7 @@ it(`should support hydrating package managers if cache folder was removed`, asyn it(`should support hydrating multiple package managers from cached archives`, async () => { await xfs.mktempPromise(async cwd => { - await expect(runCli(cwd, [`prepare`, `yarn@2.2.2`, `pnpm@5.8.0`, `-o`])).resolves.toMatchObject({ + await expect(runCli(cwd, [`pack`, `yarn@2.2.2`, `pnpm@5.8.0`])).resolves.toMatchObject({ exitCode: 0, stderr: ``, }); @@ -458,8 +460,7 @@ it(`should support hydrating multiple package managers from cached archives`, as process.env.COREPACK_ENABLE_NETWORK = `0`; try { - await expect(runCli(cwd, [`hydrate`, `corepack.tgz`])).resolves.toMatchObject({ - stdout: `Hydrating yarn@2.2.2...\nHydrating pnpm@5.8.0...\nAll done!\n`, + await expect(runCli(cwd, [`install`, `-g`, `corepack.tgz`])).resolves.toMatchObject({ stderr: ``, exitCode: 0, }); @@ -545,10 +546,11 @@ it(`should not override the package manager exit code`, async () => { packageManager: `yarn@2.2.2`, }); - const yarnPath = ppath.join(npath.toPortablePath(process.env.COREPACK_HOME!), `yarn/2.2.2/yarn.js` as PortablePath); + const yarnFolder = ppath.join(corepackHome, `yarn/2.2.2`); + await xfs.mkdirPromise(yarnFolder, {recursive: true}); + await xfs.writeJsonPromise(ppath.join(yarnFolder, `.corepack`), {}); - await xfs.mkdirPromise(ppath.dirname(yarnPath), {recursive: true}); - await xfs.writeFilePromise(yarnPath, ` + await xfs.writeFilePromise(ppath.join(yarnFolder, `yarn.js`), ` process.exitCode = 42; `); @@ -569,10 +571,11 @@ it(`should not preserve the process.exitCode when a package manager throws`, asy packageManager: `yarn@2.2.2`, }); - const yarnPath = ppath.join(npath.toPortablePath(process.env.COREPACK_HOME!), `yarn/2.2.2/yarn.js` as PortablePath); + const yarnFolder = ppath.join(corepackHome, `yarn/2.2.2`); + await xfs.mkdirPromise(yarnFolder, {recursive: true}); + await xfs.writeJsonPromise(ppath.join(yarnFolder, `.corepack`), {}); - await xfs.mkdirPromise(ppath.dirname(yarnPath), {recursive: true}); - await xfs.writeFilePromise(yarnPath, ` + await xfs.writeFilePromise(ppath.join(yarnFolder, `yarn.js`), ` process.exitCode = 42; throw new Error('foo'); `); @@ -591,10 +594,11 @@ it(`should not set the exit code after successfully launching the package manage packageManager: `yarn@2.2.2`, }); - const yarnPath = ppath.join(npath.toPortablePath(process.env.COREPACK_HOME!), `yarn/2.2.2/yarn.js` as PortablePath); + const yarnFolder = ppath.join(corepackHome, `yarn/2.2.2`); + await xfs.mkdirPromise(yarnFolder, {recursive: true}); + await xfs.writeJsonPromise(ppath.join(yarnFolder, `.corepack`), {}); - await xfs.mkdirPromise(ppath.dirname(yarnPath), {recursive: true}); - await xfs.writeFilePromise(yarnPath, ` + await xfs.writeFilePromise(ppath.join(yarnFolder, `yarn.js`), ` process.once('beforeExit', () => { if (process.exitCode === undefined) { process.exitCode = 42; @@ -616,14 +620,16 @@ it(`should support package managers in ESM format`, async () => { packageManager: `yarn@2.2.2`, }); - const yarnDir = ppath.join(npath.toPortablePath(process.env.COREPACK_HOME!), `yarn/2.2.2` as PortablePath); + const yarnFolder = ppath.join(corepackHome, `yarn/2.2.2`); + await xfs.mkdirPromise(yarnFolder, {recursive: true}); + await xfs.writeJsonPromise(ppath.join(yarnFolder, `.corepack`), {}); - await xfs.mkdirPromise(yarnDir, {recursive: true}); - await xfs.writeFilePromise(ppath.join(yarnDir, `yarn.js` as PortablePath), ` + await xfs.writeFilePromise(ppath.join(yarnFolder, `yarn.js`), ` import 'fs'; console.log(42); `); - await xfs.writeJsonPromise(ppath.join(yarnDir, `package.json` as PortablePath), { + + await xfs.writeJsonPromise(ppath.join(yarnFolder, `package.json`), { type: `module`, }); diff --git a/tests/nock/3cCMRJMrFyNpV7MlFGUXYw-1.dat b/tests/nock/3cCMRJMrFyNpV7MlFGUXYw-1.dat new file mode 100644 index 000000000..5f084a5b4 Binary files /dev/null and b/tests/nock/3cCMRJMrFyNpV7MlFGUXYw-1.dat differ diff --git a/tests/nock/3cCMRJMrFyNpV7MlFGUXYw-2.dat b/tests/nock/3cCMRJMrFyNpV7MlFGUXYw-2.dat new file mode 100644 index 000000000..5403d9e58 Binary files /dev/null and b/tests/nock/3cCMRJMrFyNpV7MlFGUXYw-2.dat differ diff --git a/tests/nock/AL__3okpCdfjA6kGuG2rFQ-1.dat b/tests/nock/AL__3okpCdfjA6kGuG2rFQ-1.dat new file mode 100644 index 000000000..7b0db13d0 Binary files /dev/null and b/tests/nock/AL__3okpCdfjA6kGuG2rFQ-1.dat differ diff --git a/tests/nock/AL__3okpCdfjA6kGuG2rFQ-2.dat b/tests/nock/AL__3okpCdfjA6kGuG2rFQ-2.dat new file mode 100644 index 000000000..5403d9e58 Binary files /dev/null and b/tests/nock/AL__3okpCdfjA6kGuG2rFQ-2.dat differ diff --git a/tests/nock/VurwVdvlR5Rs7xQVs9UCVw-1.dat b/tests/nock/VurwVdvlR5Rs7xQVs9UCVw-1.dat new file mode 100644 index 000000000..36fd8aa39 Binary files /dev/null and b/tests/nock/VurwVdvlR5Rs7xQVs9UCVw-1.dat differ diff --git a/tests/nock/VurwVdvlR5Rs7xQVs9UCVw-2.dat b/tests/nock/VurwVdvlR5Rs7xQVs9UCVw-2.dat new file mode 100644 index 000000000..5403d9e58 Binary files /dev/null and b/tests/nock/VurwVdvlR5Rs7xQVs9UCVw-2.dat differ diff --git a/tests/nock/VurwVdvlR5Rs7xQVs9UCVw-3.dat b/tests/nock/VurwVdvlR5Rs7xQVs9UCVw-3.dat new file mode 100644 index 000000000..5403d9e58 Binary files /dev/null and b/tests/nock/VurwVdvlR5Rs7xQVs9UCVw-3.dat differ diff --git a/tests/nock/VurwVdvlR5Rs7xQVs9UCVw-4.dat b/tests/nock/VurwVdvlR5Rs7xQVs9UCVw-4.dat new file mode 100644 index 000000000..5403d9e58 Binary files /dev/null and b/tests/nock/VurwVdvlR5Rs7xQVs9UCVw-4.dat differ diff --git a/tests/nock/bNE0FYc3WlnFGzjHaIdf5A-1.dat b/tests/nock/bNE0FYc3WlnFGzjHaIdf5A-1.dat new file mode 100644 index 000000000..97069565a Binary files /dev/null and b/tests/nock/bNE0FYc3WlnFGzjHaIdf5A-1.dat differ diff --git a/tests/nock/bNE0FYc3WlnFGzjHaIdf5A-2.dat b/tests/nock/bNE0FYc3WlnFGzjHaIdf5A-2.dat new file mode 100644 index 000000000..5403d9e58 Binary files /dev/null and b/tests/nock/bNE0FYc3WlnFGzjHaIdf5A-2.dat differ diff --git a/tests/nock/fskWDnBnd-E_LlkU7xvjwQ-1.dat b/tests/nock/fskWDnBnd-E_LlkU7xvjwQ-1.dat new file mode 100644 index 000000000..903f4d06c Binary files /dev/null and b/tests/nock/fskWDnBnd-E_LlkU7xvjwQ-1.dat differ diff --git a/tests/nock/fskWDnBnd-E_LlkU7xvjwQ-2.dat b/tests/nock/fskWDnBnd-E_LlkU7xvjwQ-2.dat new file mode 100644 index 000000000..5403d9e58 Binary files /dev/null and b/tests/nock/fskWDnBnd-E_LlkU7xvjwQ-2.dat differ diff --git a/tests/nock/vA1ZjUdN7-YPy8p6BF8_RQ-1.dat b/tests/nock/vA1ZjUdN7-YPy8p6BF8_RQ-1.dat new file mode 100644 index 000000000..903f4d06c Binary files /dev/null and b/tests/nock/vA1ZjUdN7-YPy8p6BF8_RQ-1.dat differ diff --git a/tests/nock/vA1ZjUdN7-YPy8p6BF8_RQ-2.dat b/tests/nock/vA1ZjUdN7-YPy8p6BF8_RQ-2.dat new file mode 100644 index 000000000..5403d9e58 Binary files /dev/null and b/tests/nock/vA1ZjUdN7-YPy8p6BF8_RQ-2.dat differ diff --git a/yarn.lock b/yarn.lock index 89d3e3894..8259386a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1238,13 +1238,6 @@ __metadata: languageName: node linkType: hard -"@types/emscripten@npm:^1.39.6": - version: 1.39.6 - resolution: "@types/emscripten@npm:1.39.6" - checksum: 0b2219f4c4a5126433135c331047be50bdfabac1cc39b37a8a2d8aec9cdfa62148bc80a485956924751d5007254f82ad8fc56f001999d79bdeea298a4ec4c712 - languageName: node - linkType: hard - "@types/graceful-fs@npm:^4.1.3": version: 4.1.6 resolution: "@types/graceful-fs@npm:4.1.6" @@ -1303,13 +1296,20 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:^20.0.0": +"@types/node@npm:*": version: 20.2.1 resolution: "@types/node@npm:20.2.1" checksum: d8431e663a93580f9b8f87c5f3db3cdaa32d3ffbe806820975d90f8d2c571928b67e35314dc3abe9a6cd590329fd8030ba7295e824262d5604d922bce6750348 languageName: node linkType: hard +"@types/node@npm:^20.4.6": + version: 20.4.6 + resolution: "@types/node@npm:20.4.6" + checksum: 0eadb1f9691ffd4d0ea33d85436a9ea15145b396b8258db3b3de966e908cf513da54cc8132e1b4f786b7cc2e6b298248fe2b07be90e72db861aca675c0ad283d + languageName: node + linkType: hard + "@types/prettier@npm:^2.1.5": version: 2.7.2 resolution: "@types/prettier@npm:2.7.2" @@ -1504,23 +1504,12 @@ __metadata: languageName: node linkType: hard -"@yarnpkg/fslib@npm:^2.1.0": - version: 2.10.3 - resolution: "@yarnpkg/fslib@npm:2.10.3" - dependencies: - "@yarnpkg/libzip": "npm:^2.3.0" - tslib: "npm:^1.13.0" - checksum: d7f0c9de3f4cf6134e63147dcfa4d333c716ee2242675e7d6523ebaa952af0683b5a3538da08726a0872fc68dac0cb095a9c8f2692fceaf473095ce199f346b7 - languageName: node - linkType: hard - -"@yarnpkg/libzip@npm:^2.3.0": - version: 2.3.0 - resolution: "@yarnpkg/libzip@npm:2.3.0" +"@yarnpkg/fslib@npm:^3.0.0-rc.48": + version: 3.0.0-rc.48 + resolution: "@yarnpkg/fslib@npm:3.0.0-rc.48" dependencies: - "@types/emscripten": "npm:^1.39.6" - tslib: "npm:^1.13.0" - checksum: c8d403d4411e30716d2d72c763eca620d96c75348402f943c1137e70ed4f9ed65d624109233c0e98b70708bfe3f28bdf7d49207190c2472a7aea66b91cb23a7a + tslib: "npm:^2.4.0" + checksum: 3b0dbacf6277041d6de89c905503486b8280b5c1bc8428066c72e2e27a7821407a6591ebd2e107c306539bbfc33ca9cbc02154b1e24c02894e231388a1865355 languageName: node linkType: hard @@ -2179,14 +2168,14 @@ __metadata: "@jest/globals": "npm:^29.0.0" "@types/debug": "npm:^4.1.5" "@types/jest": "npm:^29.0.0" - "@types/node": "npm:^20.0.0" + "@types/node": "npm:^20.4.6" "@types/semver": "npm:^7.1.0" "@types/tar": "npm:^6.0.0" "@types/which": "npm:^3.0.0" "@typescript-eslint/eslint-plugin": "npm:^5.0.0" "@typescript-eslint/parser": "npm:^5.0.0" "@yarnpkg/eslint-config": "npm:^0.6.0-rc.7" - "@yarnpkg/fslib": "npm:^2.1.0" + "@yarnpkg/fslib": "npm:^3.0.0-rc.48" "@zkochan/cmd-shim": "npm:^6.0.0" babel-plugin-dynamic-import-node: "npm:^2.3.3" clipanion: "npm:^3.0.1" @@ -5655,7 +5644,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.13.0, tslib@npm:^1.8.1": +"tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: 441af59dc42ad4ae57140e62cb362369620c6076845c2c2b0ecc863c1d719ce24fdbc301e9053433fef43075e061bf84b702318ff1204b496a5bba10baf9eb9f @@ -5669,6 +5658,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.4.0": + version: 2.6.1 + resolution: "tslib@npm:2.6.1" + checksum: 401069a2e407204fb442e3367efd49ed8698a93a6ce998d8fae5764439d3e395550f036426a22e7b024b4b8593728044bef79187c97df321718631c71664aa5a + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0"