diff --git a/apps/sparo-lib/package.json b/apps/sparo-lib/package.json index 298ea68..d41e4b9 100644 --- a/apps/sparo-lib/package.json +++ b/apps/sparo-lib/package.json @@ -22,6 +22,7 @@ "@rushstack/terminal": "~0.8.1", "git-repo-info": "~2.1.1", "inversify": "~6.0.2", + "npm-package-arg": "~6.1.0", "reflect-metadata": "~0.2.1", "semver": "~7.6.0", "update-notifier": "~5.1.0", @@ -32,6 +33,7 @@ "@rushstack/heft-node-rig": "2.4.5", "@types/heft-jest": "1.0.6", "@types/node": "20.11.16", + "@types/npm-package-arg": "6.1.0", "@types/semver": "7.5.7", "@types/update-notifier": "6.0.8", "@types/yargs": "17.0.32", diff --git a/apps/sparo-lib/src/logic/DependencySpecifier.ts b/apps/sparo-lib/src/logic/DependencySpecifier.ts new file mode 100644 index 0000000..7d6ca5e --- /dev/null +++ b/apps/sparo-lib/src/logic/DependencySpecifier.ts @@ -0,0 +1,178 @@ +// This is copied from rush.js source code +// https://github.com/microsoft/rushstack/blob/312b8bc554e64d66b586c65499a512dbf1c329ff/libraries/rush-lib/src/logic/DependencySpecifier.ts + +import npmPackageArg from 'npm-package-arg'; +import { InternalError } from '@rushstack/node-core-library'; + +/** + * match workspace protocol in dependencies value declaration in `package.json` + * example: + * `"workspace:*"` + * `"workspace:alias@1.2.3"` + */ +const WORKSPACE_PREFIX_REGEX: RegExp = /^workspace:((?[^._/][^@]*)@)?(?.*)$/; + +/** + * resolve workspace protocol(from `@pnpm/workspace.spec-parser`). + * used by pnpm. see [pkgs-graph](https://github.com/pnpm/pnpm/blob/27c33f0319f86c45c1645d064cd9c28aada80780/workspace/pkgs-graph/src/index.ts#L49) + */ +class WorkspaceSpec { + public readonly alias?: string; + public readonly version: string; + public readonly versionSpecifier: string; + + public constructor(version: string, alias?: string) { + this.version = version; + this.alias = alias; + this.versionSpecifier = alias ? `${alias}@${version}` : version; + } + + public static tryParse(pref: string): WorkspaceSpec | undefined { + const parts: RegExpExecArray | null = WORKSPACE_PREFIX_REGEX.exec(pref); + if (parts?.groups) { + return new WorkspaceSpec(parts.groups.version, parts.groups.alias); + } + } + + public toString(): `workspace:${string}` { + return `workspace:${this.versionSpecifier}`; + } +} + +/** + * The parsed format of a provided version specifier. + */ +export enum DependencySpecifierType { + /** + * A git repository + */ + Git = 'Git', + + /** + * A tagged version, e.g. "example@latest" + */ + Tag = 'Tag', + + /** + * A specific version number, e.g. "example@1.2.3" + */ + Version = 'Version', + + /** + * A version range, e.g. "example@2.x" + */ + Range = 'Range', + + /** + * A local .tar.gz, .tar or .tgz file + */ + File = 'File', + + /** + * A local directory + */ + Directory = 'Directory', + + /** + * An HTTP url to a .tar.gz, .tar or .tgz file + */ + Remote = 'Remote', + + /** + * A package alias, e.g. "npm:other-package@^1.2.3" + */ + Alias = 'Alias', + + /** + * A package specified using workspace protocol, e.g. "workspace:^1.2.3" + */ + Workspace = 'Workspace' +} + +/** + * An NPM "version specifier" is a string that can appear as a package.json "dependencies" value. + * Example version specifiers: `^1.2.3`, `file:./blah.tgz`, `npm:other-package@~1.2.3`, and so forth. + * A "dependency specifier" is the version specifier information, combined with the dependency package name. + */ +export class DependencySpecifier { + /** + * The dependency package name, i.e. the key from a "dependencies" key/value table. + */ + public readonly packageName: string; + + /** + * The dependency version specifier, i.e. the value from a "dependencies" key/value table. + * Example values: `^1.2.3`, `file:./blah.tgz`, `npm:other-package@~1.2.3` + */ + public readonly versionSpecifier: string; + + /** + * The type of the `versionSpecifier`. + */ + public readonly specifierType: DependencySpecifierType; + + /** + * If `specifierType` is `alias`, then this is the parsed target dependency. + * For example, if version specifier i `"npm:other-package@^1.2.3"` then this is the parsed object for + * `other-package@^1.2.3`. + */ + public readonly aliasTarget: DependencySpecifier | undefined; + + public constructor(packageName: string, versionSpecifier: string) { + this.packageName = packageName; + this.versionSpecifier = versionSpecifier; + + // Workspace ranges are a feature from PNPM and Yarn. Set the version specifier + // to the trimmed version range. + const workspaceSpecResult: WorkspaceSpec | undefined = WorkspaceSpec.tryParse(versionSpecifier); + if (workspaceSpecResult) { + this.specifierType = DependencySpecifierType.Workspace; + this.versionSpecifier = workspaceSpecResult.versionSpecifier; + + if (workspaceSpecResult.alias) { + // "workspace:some-package@^1.2.3" should be resolved as alias + this.aliasTarget = new DependencySpecifier(workspaceSpecResult.alias, workspaceSpecResult.version); + } else { + this.aliasTarget = undefined; + } + + return; + } + + const result: npmPackageArg.Result = npmPackageArg.resolve(packageName, versionSpecifier); + this.specifierType = DependencySpecifier.getDependencySpecifierType(result.type); + + if (this.specifierType === DependencySpecifierType.Alias) { + const aliasResult: npmPackageArg.AliasResult = result as npmPackageArg.AliasResult; + if (!aliasResult.subSpec || !aliasResult.subSpec.name) { + throw new InternalError('Unexpected result from npm-package-arg'); + } + this.aliasTarget = new DependencySpecifier(aliasResult.subSpec.name, aliasResult.subSpec.rawSpec); + } else { + this.aliasTarget = undefined; + } + } + + public static getDependencySpecifierType(specifierType: string): DependencySpecifierType { + switch (specifierType) { + case 'git': + return DependencySpecifierType.Git; + case 'tag': + return DependencySpecifierType.Tag; + case 'version': + return DependencySpecifierType.Version; + case 'range': + return DependencySpecifierType.Range; + case 'file': + return DependencySpecifierType.File; + case 'directory': + return DependencySpecifierType.Directory; + case 'remote': + return DependencySpecifierType.Remote; + case 'alias': + return DependencySpecifierType.Alias; + default: + throw new InternalError(`Unexpected npm-package-arg result type "${specifierType}"`); + } + } +} diff --git a/apps/sparo-lib/src/logic/RushProjectSlim.ts b/apps/sparo-lib/src/logic/RushProjectSlim.ts new file mode 100644 index 0000000..80dba04 --- /dev/null +++ b/apps/sparo-lib/src/logic/RushProjectSlim.ts @@ -0,0 +1,117 @@ +import * as path from 'path'; +import * as semver from 'semver'; +import { JsonFile } from '@rushstack/node-core-library'; +import { DependencySpecifier, DependencySpecifierType } from './DependencySpecifier'; + +export interface IProjectJson { + packageName: string; + projectFolder: string; + decoupledLocalDependencies?: string[]; + cyclicDependencyProjects?: string[]; +} + +/** + * A slim version of RushConfigurationProject + */ +export class RushProjectSlim { + public packageName: string; + public projectFolder: string; + public relativeProjectFolder: string; + public packageJson: { + name: string; + version: string; + dependencies?: Record; + devDependencies?: Record; + optionalDependencies?: Record; + }; + public decoupledLocalDependencies: Set; + + private _packageNameToRushProjectSlim: Map; + private _dependencyProjects: Set | undefined; + private _consumingProjects: Set | undefined; + + public constructor( + projectJson: IProjectJson, + repoRootPath: string, + packageNameToRushProjectSlim: Map + ) { + this.packageName = projectJson.packageName; + this.projectFolder = path.resolve(repoRootPath, projectJson.projectFolder); + this.relativeProjectFolder = projectJson.projectFolder; + const packageJsonPath: string = path.resolve(this.projectFolder, 'package.json'); + this.packageJson = JsonFile.load(packageJsonPath); + this._packageNameToRushProjectSlim = packageNameToRushProjectSlim; + + this.decoupledLocalDependencies = new Set(); + if (projectJson.cyclicDependencyProjects || projectJson.decoupledLocalDependencies) { + if (projectJson.cyclicDependencyProjects && projectJson.decoupledLocalDependencies) { + throw new Error( + 'A project configuration cannot specify both "decoupledLocalDependencies" and "cyclicDependencyProjects". Please use "decoupledLocalDependencies" only -- the other name is deprecated.' + ); + } + for (const cyclicDependencyProject of projectJson.cyclicDependencyProjects || + projectJson.decoupledLocalDependencies || + []) { + this.decoupledLocalDependencies.add(cyclicDependencyProject); + } + } + } + + public get dependencyProjects(): ReadonlySet { + if (this._dependencyProjects) { + return this._dependencyProjects; + } + const dependencyProjects: Set = new Set(); + const { packageJson } = this; + for (const dependencySet of [ + packageJson.dependencies, + packageJson.devDependencies, + packageJson.optionalDependencies + ]) { + if (dependencySet) { + for (const [dependency, version] of Object.entries(dependencySet)) { + const dependencySpecifier: DependencySpecifier = new DependencySpecifier(dependency, version); + const dependencyName: string = + dependencySpecifier.aliasTarget?.packageName ?? dependencySpecifier.packageName; + // Skip if we can't find the local project or it's a cyclic dependency + const localProject: RushProjectSlim | undefined = + this._packageNameToRushProjectSlim.get(dependencyName); + if (localProject && !this.decoupledLocalDependencies.has(dependency)) { + // Set the value if it's a workspace project, or if we have a local project and the semver is satisfied + switch (dependencySpecifier.specifierType) { + case DependencySpecifierType.Version: + case DependencySpecifierType.Range: + if ( + semver.satisfies(localProject.packageJson.version, dependencySpecifier.versionSpecifier) + ) { + dependencyProjects.add(localProject); + } + break; + case DependencySpecifierType.Workspace: + dependencyProjects.add(localProject); + break; + } + } + } + } + } + this._dependencyProjects = dependencyProjects; + return this._dependencyProjects; + } + + public get consumingProjects(): ReadonlySet { + if (!this._consumingProjects) { + // Force initialize all dependencies relationship + for (const project of this._packageNameToRushProjectSlim.values()) { + project._consumingProjects = new Set(); + } + + for (const project of this._packageNameToRushProjectSlim.values()) { + for (const dependency of project.dependencyProjects) { + dependency._consumingProjects!.add(project); + } + } + } + return this._consumingProjects!; + } +} diff --git a/apps/sparo-lib/src/services/GitSparseCheckoutService.ts b/apps/sparo-lib/src/services/GitSparseCheckoutService.ts index ab7e00c..3d3f387 100644 --- a/apps/sparo-lib/src/services/GitSparseCheckoutService.ts +++ b/apps/sparo-lib/src/services/GitSparseCheckoutService.ts @@ -1,10 +1,10 @@ import * as path from 'path'; -import * as child_process from 'child_process'; import { inject } from 'inversify'; import { Service } from '../decorator'; import { GitService } from './GitService'; import { TerminalService } from './TerminalService'; -import { Executable, FileSystem, JsonFile, JsonSyntax } from '@rushstack/node-core-library'; +import { SelectionParameterService } from './SelectionParameterService'; +import { FileSystem, JsonFile, JsonSyntax } from '@rushstack/node-core-library'; import { Stopwatch } from '../logic/Stopwatch'; import type { ISelection } from '../logic/SparoProfile'; @@ -26,6 +26,7 @@ export interface IRushProject { export class GitSparseCheckoutService { @inject(GitService) private _gitService!: GitService; @inject(TerminalService) private _terminalService!: TerminalService; + @inject(SelectionParameterService) private _selectionParameterService!: SelectionParameterService; private _rushConfigLoaded: boolean = false; private _rushProjects: IRushProject[] = []; @@ -156,16 +157,46 @@ export class GitSparseCheckoutService { } } + // Validate incorrect package names const unfoundedPackages: string[] = []; - for (const selector of [...toSelectors, ...fromSelectors]) { - if (selector.indexOf(':') < 0) { - const packageName: string = selector; + + let additionalFullPackageNames: string[] = []; + for (const selectorArg of toSelectors) { + if (selectorArg.indexOf(':') < 0) { + const packageName: string = selectorArg; const result: string | undefined = this._findProjectByShorthandName(packageName); if (!result) { unfoundedPackages.push(packageName); + } else { + // Ensure full package name used + toSelectors.delete(packageName); + // DO NOT add back to selectors in this loop + additionalFullPackageNames.push(result); } } } + for (const fullPackageName of additionalFullPackageNames) { + toSelectors.add(fullPackageName); + } + additionalFullPackageNames = []; + for (const selectorArg of fromSelectors) { + if (selectorArg.indexOf(':') < 0) { + const packageName: string = selectorArg; + const result: string | undefined = this._findProjectByShorthandName(packageName); + if (!result) { + unfoundedPackages.push(packageName); + } else { + // Ensure full package name used + fromSelectors.delete(packageName); + // DO NOT add back to selectors in this loop + additionalFullPackageNames.push(result); + } + } + } + for (const fullPackageName of additionalFullPackageNames) { + fromSelectors.add(fullPackageName); + } + additionalFullPackageNames = []; if (unfoundedPackages.length > 0) { throw new Error(`These packages: ${unfoundedPackages.join(', ')} does not exist in rush.json`); @@ -175,11 +206,13 @@ export class GitSparseCheckoutService { if (toSelectors.size !== 0 || fromSelectors.size !== 0) { const stopwatch: Stopwatch = Stopwatch.start(); - targetFolders = this._getTargetFoldersByRushList({ toSelectors, fromSelectors }); - terminal.writeVerboseLine(`Run rush list command. (${stopwatch.toString()})`); + targetFolders = this._selectionParameterService.getSelectedFolders({ toSelectors, fromSelectors }); + terminal.writeVerboseLine(`Get selected folders. (${stopwatch.toString()})`); stopwatch.stop(); } else { - terminal.writeDebugLine('Skip rush list regarding the absence of from selectors and to selectors'); + terminal.writeDebugLine( + 'Skip getting selected folders regarding the absence of from selectors and to selectors' + ); } // include rule @@ -393,61 +426,4 @@ export class GitSparseCheckoutService { } return packageName; } - - private _getTargetFoldersByRushList({ - toSelectors, - fromSelectors - }: { - toSelectors: Iterable; - fromSelectors: Iterable; - }): string[] { - const { terminal } = this._terminalService; - - const args: string[] = ['list', '--json']; - - for (const toSelector of toSelectors) { - args.push('--to'); - args.push(toSelector); - } - for (const fromSelector of fromSelectors) { - args.push('--from'); - args.push(fromSelector); - } - - terminal.writeVerboseLine(`Run command: rush ${args.join(' ')}`); - - const result: child_process.SpawnSyncReturns = Executable.spawnSync('rush', args, { - stdio: ['pipe', 'pipe', 'pipe'] - }); - - if (result.status !== 0) { - throw new Error(`Failed to evaluate the Sparo profile's project selectors:\nstdout: ${result.stdout}\nstderr: ${result.stderr}`); - } - - const processedResult: string = this._processListResult(result.stdout.toString()); - - terminal.writeVerboseLine(processedResult); - - const { projects: targetDeps } = JSON.parse(processedResult) as { - projects: { path: string }[]; - }; - - return targetDeps.map((targetDep) => targetDep.path); - } - - private _processListResult(input: string): string { - const stringList: string[] = input.split('\n'); - let endOfInstallScript: number = -1; - - for (let i: number = 0; i < stringList.length; ++i) { - if (stringList[i][0] === '{') { - endOfInstallScript = i; - break; - } - } - - const jsonStringList: string[] = stringList.slice(endOfInstallScript); - - return jsonStringList.join('\n'); - } } diff --git a/apps/sparo-lib/src/services/SelectionParameterService.ts b/apps/sparo-lib/src/services/SelectionParameterService.ts new file mode 100644 index 0000000..0464e8a --- /dev/null +++ b/apps/sparo-lib/src/services/SelectionParameterService.ts @@ -0,0 +1,200 @@ +import * as child_process from 'child_process'; +import { inject } from 'inversify'; +import { Service } from '../decorator'; +import { TerminalService } from './TerminalService'; +import { Executable, JsonFile, Sort } from '@rushstack/node-core-library'; +import { GitService } from './GitService'; +import { RushProjectSlim } from '../logic/RushProjectSlim'; + +@Service() +export class SelectionParameterService { + @inject(GitService) private _gitService!: GitService; + @inject(TerminalService) private _terminalService!: TerminalService; + + public getSelectedFolders({ + toSelectors, + fromSelectors + }: { + toSelectors: Iterable; + fromSelectors: Iterable; + }): string[] { + let hasProtocolSelection: boolean = false; + if (!hasProtocolSelection) { + for (const toSelector of toSelectors) { + if (toSelector.indexOf(':') >= 0) { + hasProtocolSelection = true; + break; + } + } + } + if (!hasProtocolSelection) { + for (const fromSelector of fromSelectors) { + if (fromSelector.indexOf(':') >= 0) { + hasProtocolSelection = true; + break; + } + } + } + + if (!hasProtocolSelection) { + this._terminalService.terminal.writeDebugLine(`Opt in strategy without running rush list`); + // Selectors passed down here are ensured to contain full package names only + return this._getSelectedFoldersByNamedProjectsOnly({ + toSelectors, + fromSelectors + }); + } else { + return this._getSelectedFoldersByRushList({ + toSelectors, + fromSelectors + }); + } + } + + /** + * This function provides a quick way to get selected folders by + * avoiding call "rush list" command + */ + private _getSelectedFoldersByNamedProjectsOnly({ + toSelectors, + fromSelectors + }: { + toSelectors: Iterable; + fromSelectors: Iterable; + }): string[] { + let rushJson: { projects?: { packageName: string; projectFolder: string }[] } = {}; + const root: string = this._gitService.getRepoInfo().root; + try { + rushJson = JsonFile.load(`${root}/rush.json`); + } catch (e) { + // no-catch + } + + const packageNameToRushProjectSlim: Map = new Map(); + if (Array.isArray(rushJson.projects)) { + const { projects } = rushJson; + for (const project of projects) { + const rushProjectSlim: RushProjectSlim = new RushProjectSlim( + project, + root, + packageNameToRushProjectSlim + ); + packageNameToRushProjectSlim.set(project.packageName, rushProjectSlim); + } + } + + const selectedProjects: Set = new Set(); + const evalToSelectorForProject = ( + rushProjectSlim: RushProjectSlim, + visited: Set = new Set() + ): void => { + if (visited.has(rushProjectSlim)) { + return; + } + visited.add(rushProjectSlim); + selectedProjects.add(rushProjectSlim); + for (const dependencyProject of rushProjectSlim.dependencyProjects) { + evalToSelectorForProject(dependencyProject, visited); + } + }; + for (const toSelector of toSelectors) { + const rushProjectSlim: RushProjectSlim | undefined = packageNameToRushProjectSlim.get(toSelector); + if (!rushProjectSlim) { + throw new Error(`Can not found project definition for "${toSelector}"`); + } + evalToSelectorForProject(rushProjectSlim); + } + const evalFromSelectorForProject = ( + rushProjectSlim: RushProjectSlim, + visited: Set = new Set() + ): void => { + if (visited.has(rushProjectSlim)) { + return; + } + visited.add(rushProjectSlim); + selectedProjects.add(rushProjectSlim); + for (const dependencyProject of rushProjectSlim.dependencyProjects) { + evalToSelectorForProject(dependencyProject); + } + for (const consumingProject of rushProjectSlim.consumingProjects) { + evalFromSelectorForProject(consumingProject, visited); + } + }; + for (const fromSelector of fromSelectors) { + const rushProjectSlim: RushProjectSlim | undefined = packageNameToRushProjectSlim.get(fromSelector); + if (!rushProjectSlim) { + throw new Error(`Can not found project definition for "${fromSelector}"`); + } + evalFromSelectorForProject(rushProjectSlim); + } + + const { terminal } = this._terminalService; + + terminal.writeDebugLine(`Selected ${selectedProjects.size} projects:`); + Sort.sortSetBy(selectedProjects, (x) => x.packageName); + for (const project of selectedProjects) { + terminal.writeDebugLine(project.packageName); + } + + return Array.from(selectedProjects).map((x) => x.relativeProjectFolder); + } + + private _getSelectedFoldersByRushList({ + toSelectors, + fromSelectors + }: { + toSelectors: Iterable; + fromSelectors: Iterable; + }): string[] { + const { terminal } = this._terminalService; + + const args: string[] = ['list', '--json']; + + for (const toSelector of toSelectors) { + args.push('--to'); + args.push(toSelector); + } + for (const fromSelector of fromSelectors) { + args.push('--from'); + args.push(fromSelector); + } + + terminal.writeVerboseLine(`Run command: rush ${args.join(' ')}`); + + const result: child_process.SpawnSyncReturns = Executable.spawnSync('rush', args, { + stdio: ['pipe', 'pipe', 'pipe'] + }); + + if (result.status !== 0) { + throw new Error( + `Failed to evaluate the Sparo profile's project selectors:\nstdout: ${result.stdout}\nstderr: ${result.stderr}` + ); + } + + const processedResult: string = this._processListResult(result.stdout.toString()); + + terminal.writeVerboseLine(processedResult); + + const { projects: targetDeps } = JSON.parse(processedResult) as { + projects: { path: string }[]; + }; + + return targetDeps.map((targetDep) => targetDep.path); + } + + private _processListResult(input: string): string { + const stringList: string[] = input.split('\n'); + let endOfInstallScript: number = -1; + + for (let i: number = 0; i < stringList.length; ++i) { + if (stringList[i][0] === '{') { + endOfInstallScript = i; + break; + } + } + + const jsonStringList: string[] = stringList.slice(endOfInstallScript); + + return jsonStringList.join('\n'); + } +} diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 054b9a7..3e29801 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -55,6 +55,9 @@ importers: inversify: specifier: ~6.0.2 version: 6.0.2 + npm-package-arg: + specifier: ~6.1.0 + version: 6.1.0 reflect-metadata: specifier: ~0.2.1 version: 0.2.1 @@ -80,6 +83,9 @@ importers: '@types/node': specifier: 20.11.16 version: 20.11.16 + '@types/npm-package-arg': + specifier: 6.1.0 + version: 6.1.0 '@types/semver': specifier: 7.5.7 version: 7.5.7 @@ -3956,6 +3962,10 @@ packages: dependencies: undici-types: 5.26.5 + /@types/npm-package-arg@6.1.0: + resolution: {integrity: sha512-vbt5fb0y1svMhu++1lwtKmZL76d0uPChFlw7kEzyUmTwfmpHRcFb8i0R8ElT69q/L+QLgK2hgECivIAvaEDwag==} + dev: true + /@types/parse-json@4.0.2: resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} dev: false @@ -4890,6 +4900,10 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + /builtins@1.0.3: + resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} + dev: false + /bytes@3.0.0: resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} engines: {node: '>= 0.8'} @@ -7286,6 +7300,10 @@ packages: react-is: 16.13.1 dev: false + /hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: false + /hpack.js@2.1.6: resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} dependencies: @@ -9506,6 +9524,15 @@ packages: resolution: {integrity: sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA==} dev: false + /npm-package-arg@6.1.0: + resolution: {integrity: sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==} + dependencies: + hosted-git-info: 2.8.9 + osenv: 0.1.5 + semver: 5.7.2 + validate-npm-package-name: 3.0.0 + dev: false + /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -9629,6 +9656,24 @@ packages: type-check: 0.4.0 dev: true + /os-homedir@1.0.2: + resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} + engines: {node: '>=0.10.0'} + dev: false + + /os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + dev: false + + /osenv@0.1.5: + resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==} + deprecated: This package is no longer supported. + dependencies: + os-homedir: 1.0.2 + os-tmpdir: 1.0.2 + dev: false + /p-cancelable@1.1.0: resolution: {integrity: sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==} engines: {node: '>=6'} @@ -11096,6 +11141,11 @@ packages: semver: 7.6.0 dev: false + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + dev: false + /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -12079,6 +12129,12 @@ packages: convert-source-map: 2.0.0 dev: true + /validate-npm-package-name@3.0.0: + resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} + dependencies: + builtins: 1.0.3 + dev: false + /validator@13.11.0: resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} engines: {node: '>= 0.10'} @@ -12570,6 +12626,7 @@ time: /@rushstack/terminal@0.8.1: '2024-02-20T21:48:45.317Z' /@types/heft-jest@1.0.6: '2023-11-07T07:22:05.861Z' /@types/node@20.11.16: '2024-02-01T17:35:24.929Z' + /@types/npm-package-arg@6.1.0: '2018-11-01T17:41:31.538Z' /@types/react-dom@18.2.19: '2024-02-07T17:06:58.412Z' /@types/react@18.2.59: '2024-02-26T19:07:45.111Z' /@types/semver@7.5.7: '2024-02-11T14:35:16.597Z' @@ -12583,6 +12640,7 @@ time: /jest-diff@29.7.0: '2023-09-12T06:43:43.883Z' /lunr-languages@1.14.0: '2023-10-09T20:33:09.608Z' /lunr@2.3.9: '2020-08-19T20:30:07.948Z' + /npm-package-arg@6.1.0: '2018-04-10T17:45:45.725Z' /prism-react-renderer@2.3.1: '2023-12-18T14:23:38.265Z' /prismjs@1.29.0: '2022-08-23T10:42:14.395Z' /react-dom@18.2.0: '2022-06-14T19:46:48.370Z'