@pnpm/store.commands 1000.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015-2016 Rico Sta. Cruz and other contributors
4
+ Copyright (c) 2016-2026 Zoltan Kochan and other contributors
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { catFile, catIndex, findHash } from './inspecting/index.js';
2
+ export { store } from './store/index.js';
package/lib/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { catFile, catIndex, findHash } from './inspecting/index.js';
2
+ export { store } from './store/index.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,8 @@
1
+ import type { Config } from '@pnpm/config.reader';
2
+ export declare const skipPackageManagerCheck = true;
3
+ export declare const commandNames: string[];
4
+ export declare const rcOptionsTypes: typeof cliOptionsTypes;
5
+ export declare function cliOptionsTypes(): Record<string, unknown>;
6
+ export declare function help(): string;
7
+ export type CatFileCommandOptions = Pick<Config, 'storeDir' | 'pnpmHomeDir'>;
8
+ export declare function handler(opts: CatFileCommandOptions, params: string[]): Promise<string>;
@@ -0,0 +1,46 @@
1
+ import path from 'node:path';
2
+ import util from 'node:util';
3
+ import { PnpmError } from '@pnpm/error';
4
+ import gfs from '@pnpm/fs.graceful-fs';
5
+ import { getStorePath } from '@pnpm/store.path';
6
+ import { renderHelp } from 'render-help';
7
+ const INTEGRITY_REGEX = /^[^-]+-([a-z0-9+/=]+)$/i;
8
+ export const skipPackageManagerCheck = true;
9
+ export const commandNames = ['cat-file'];
10
+ export const rcOptionsTypes = cliOptionsTypes;
11
+ export function cliOptionsTypes() {
12
+ return {};
13
+ }
14
+ export function help() {
15
+ return renderHelp({
16
+ description: 'Prints the contents of a file based on the hash value stored in the index file.',
17
+ descriptionLists: [],
18
+ usages: ['pnpm cat-file <hash>'],
19
+ });
20
+ }
21
+ export async function handler(opts, params) {
22
+ if (!params || params.length === 0) {
23
+ throw new PnpmError('MISSING_HASH', 'Missing file hash', {
24
+ hint: help(),
25
+ });
26
+ }
27
+ const [, integrityHash] = params[0].match(INTEGRITY_REGEX);
28
+ const toHex = Buffer.from(integrityHash, 'base64').toString('hex');
29
+ const storeDir = await getStorePath({
30
+ pkgRoot: process.cwd(),
31
+ storePath: opts.storeDir,
32
+ pnpmHomeDir: opts.pnpmHomeDir,
33
+ });
34
+ const cafsDir = path.join(storeDir, 'files');
35
+ const filePath = path.resolve(cafsDir, toHex.slice(0, 2), toHex.slice(2));
36
+ try {
37
+ return gfs.readFileSync(filePath, 'utf8');
38
+ }
39
+ catch (err) {
40
+ if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
41
+ throw new PnpmError('INVALID_HASH', 'Corresponding hash file not found');
42
+ }
43
+ throw err;
44
+ }
45
+ }
46
+ //# sourceMappingURL=catFile.js.map
@@ -0,0 +1,8 @@
1
+ import type { Config } from '@pnpm/config.reader';
2
+ export declare const skipPackageManagerCheck = true;
3
+ export declare const commandNames: string[];
4
+ export declare const rcOptionsTypes: typeof cliOptionsTypes;
5
+ export declare function cliOptionsTypes(): Record<string, unknown>;
6
+ export declare function help(): string;
7
+ export type CatIndexCommandOptions = Pick<Config, 'rawConfig' | 'pnpmHomeDir' | 'storeDir' | 'lockfileDir' | 'dir' | 'registries' | 'cacheDir' | 'sslConfigs'>;
8
+ export declare function handler(opts: CatIndexCommandOptions, params: string[]): Promise<string>;
@@ -0,0 +1,69 @@
1
+ import util from 'node:util';
2
+ import { PnpmError } from '@pnpm/error';
3
+ import { createResolver } from '@pnpm/installing.client';
4
+ import { sortDeepKeys } from '@pnpm/object.key-sorting';
5
+ import { parseWantedDependency } from '@pnpm/resolving.parse-wanted-dependency';
6
+ import { StoreIndex, storeIndexKey } from '@pnpm/store.index';
7
+ import { getStorePath } from '@pnpm/store.path';
8
+ import { lexCompare } from '@pnpm/util.lex-comparator';
9
+ import { renderHelp } from 'render-help';
10
+ export const skipPackageManagerCheck = true;
11
+ export const commandNames = ['cat-index'];
12
+ export const rcOptionsTypes = cliOptionsTypes;
13
+ export function cliOptionsTypes() {
14
+ return {};
15
+ }
16
+ export function help() {
17
+ return renderHelp({
18
+ description: 'Prints the index file of a specific package from the store.',
19
+ descriptionLists: [],
20
+ usages: ['pnpm cat-index <pkg name>@<pkg version>'],
21
+ });
22
+ }
23
+ export async function handler(opts, params) {
24
+ if (!params || params.length === 0) {
25
+ throw new PnpmError('MISSING_PACKAGE_NAME', 'Specify a package', {
26
+ hint: help(),
27
+ });
28
+ }
29
+ const wantedDependency = params[0];
30
+ const { alias, bareSpecifier } = parseWantedDependency(wantedDependency) || {};
31
+ if (!alias) {
32
+ throw new PnpmError('INVALID_SELECTOR', `Cannot parse the "${wantedDependency}" selector`);
33
+ }
34
+ const storeDir = await getStorePath({
35
+ pkgRoot: process.cwd(),
36
+ storePath: opts.storeDir,
37
+ pnpmHomeDir: opts.pnpmHomeDir,
38
+ });
39
+ const { resolve } = createResolver({
40
+ ...opts,
41
+ authConfig: opts.rawConfig,
42
+ });
43
+ const pkgSnapshot = await resolve({ alias, bareSpecifier }, {
44
+ lockfileDir: opts.lockfileDir ?? opts.dir,
45
+ preferredVersions: {},
46
+ projectDir: opts.dir,
47
+ });
48
+ const filesIndexFile = storeIndexKey(pkgSnapshot.resolution.integrity.toString(), `${alias}@${bareSpecifier}`);
49
+ const storeIndex = new StoreIndex(storeDir);
50
+ try {
51
+ const pkgFilesIndex = storeIndex.get(filesIndexFile);
52
+ if (!pkgFilesIndex) {
53
+ throw new PnpmError('INVALID_PACKAGE', 'No corresponding index file found. You can use pnpm list to see if the package is installed.');
54
+ }
55
+ return JSON.stringify(sortDeepKeys(pkgFilesIndex), replacer, 2);
56
+ }
57
+ finally {
58
+ storeIndex.close();
59
+ }
60
+ }
61
+ function replacer(key, value) {
62
+ if (util.types.isMap(value)) {
63
+ const entries = Array.from(value.entries());
64
+ entries.sort(([key1], [key2]) => lexCompare(key1, key2));
65
+ return Object.fromEntries(entries);
66
+ }
67
+ return value;
68
+ }
69
+ //# sourceMappingURL=catIndex.js.map
@@ -0,0 +1,15 @@
1
+ import type { Config } from '@pnpm/config.reader';
2
+ export declare const PACKAGE_INFO_CLR: import("chalk").ChalkInstance;
3
+ export declare const INDEX_PATH_CLR: import("chalk").ChalkInstance;
4
+ export declare const skipPackageManagerCheck = true;
5
+ export declare const commandNames: string[];
6
+ export declare const rcOptionsTypes: typeof cliOptionsTypes;
7
+ export declare function cliOptionsTypes(): Record<string, unknown>;
8
+ export declare function help(): string;
9
+ export type FindHashCommandOptions = Pick<Config, 'storeDir' | 'pnpmHomeDir'>;
10
+ export interface FindHashResult {
11
+ name: string;
12
+ version: string;
13
+ indexKey: string;
14
+ }
15
+ export declare function handler(opts: FindHashCommandOptions, params: string[]): Promise<string>;
@@ -0,0 +1,85 @@
1
+ import { PnpmError } from '@pnpm/error';
2
+ import { StoreIndex } from '@pnpm/store.index';
3
+ import { getStorePath } from '@pnpm/store.path';
4
+ import chalk from 'chalk';
5
+ import { renderHelp } from 'render-help';
6
+ export const PACKAGE_INFO_CLR = chalk.greenBright;
7
+ export const INDEX_PATH_CLR = chalk.hex('#078487');
8
+ export const skipPackageManagerCheck = true;
9
+ export const commandNames = ['find-hash'];
10
+ export const rcOptionsTypes = cliOptionsTypes;
11
+ export function cliOptionsTypes() {
12
+ return {};
13
+ }
14
+ export function help() {
15
+ return renderHelp({
16
+ description: 'Experimental! Lists the packages that include the file with the specified hash.',
17
+ descriptionLists: [],
18
+ usages: ['pnpm find-hash <hash>'],
19
+ });
20
+ }
21
+ export async function handler(opts, params) {
22
+ if (!params || params.length === 0) {
23
+ throw new PnpmError('MISSING_HASH', '`pnpm find-hash` requires the hash');
24
+ }
25
+ // Convert the input hash to hex format for comparison
26
+ // Input can be either:
27
+ // - A hex string (used directly)
28
+ // - A base64 integrity string like "sha512-..." (converted to hex)
29
+ let hash = params[0];
30
+ if (hash.includes('-')) {
31
+ // Looks like an integrity string (algo-base64), extract and convert the base64 part
32
+ const base64Part = hash.split('-').slice(1).join('-');
33
+ hash = Buffer.from(base64Part, 'base64').toString('hex');
34
+ }
35
+ // Stored digests are lowercase hex, so normalize the input to lowercase
36
+ hash = hash.toLowerCase();
37
+ const storeDir = await getStorePath({
38
+ pkgRoot: process.cwd(),
39
+ storePath: opts.storeDir,
40
+ pnpmHomeDir: opts.pnpmHomeDir,
41
+ });
42
+ const result = [];
43
+ const storeIndex = new StoreIndex(storeDir);
44
+ try {
45
+ for (const [indexKey, data] of storeIndex.entries()) {
46
+ const pkgFilesIndex = data;
47
+ if (!pkgFilesIndex)
48
+ continue;
49
+ if (pkgFilesIndex.files) {
50
+ for (const file of pkgFilesIndex.files.values()) {
51
+ if (file?.digest === hash) {
52
+ result.push({ name: pkgFilesIndex.manifest?.name ?? 'unknown', version: pkgFilesIndex.manifest?.version ?? 'unknown', indexKey });
53
+ // a package is only found once.
54
+ continue;
55
+ }
56
+ }
57
+ }
58
+ if (pkgFilesIndex.sideEffects) {
59
+ for (const { added } of pkgFilesIndex.sideEffects.values()) {
60
+ if (!added)
61
+ continue;
62
+ for (const file of added.values()) {
63
+ if (file?.digest === hash) {
64
+ result.push({ name: pkgFilesIndex.manifest?.name ?? 'unknown', version: pkgFilesIndex.manifest?.version ?? 'unknown', indexKey });
65
+ // a package is only found once.
66
+ continue;
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+ finally {
74
+ storeIndex.close();
75
+ }
76
+ if (!result.length) {
77
+ throw new PnpmError('INVALID_FILE_HASH', 'No package or index file matching this hash was found.');
78
+ }
79
+ let acc = '';
80
+ for (const { name, version, indexKey } of result) {
81
+ acc += `${PACKAGE_INFO_CLR(name)}@${PACKAGE_INFO_CLR(version)} ${INDEX_PATH_CLR(indexKey)}\n`;
82
+ }
83
+ return acc;
84
+ }
85
+ //# sourceMappingURL=findHash.js.map
@@ -0,0 +1,4 @@
1
+ import * as catFile from './catFile.js';
2
+ import * as catIndex from './catIndex.js';
3
+ import * as findHash from './findHash.js';
4
+ export { catFile, catIndex, findHash };
@@ -0,0 +1,5 @@
1
+ import * as catFile from './catFile.js';
2
+ import * as catIndex from './catIndex.js';
3
+ import * as findHash from './findHash.js';
4
+ export { catFile, catIndex, findHash };
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,6 @@
1
+ export declare function cleanExpiredDlxCache({ cacheDir, dlxCacheMaxAge, now }: {
2
+ cacheDir: string;
3
+ dlxCacheMaxAge: number;
4
+ now: Date;
5
+ }): Promise<void>;
6
+ export declare function cleanOrphans(dlxCacheDir: string): Promise<void>;
@@ -0,0 +1,97 @@
1
+ import { promises as fs, readdirSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import util from 'node:util';
4
+ export async function cleanExpiredDlxCache({ cacheDir, dlxCacheMaxAge, now, }) {
5
+ if (dlxCacheMaxAge === Infinity)
6
+ return;
7
+ const dlxCacheDir = path.join(cacheDir, 'dlx');
8
+ const dlxCacheNames = readOptDir(dlxCacheDir);
9
+ if (!dlxCacheNames)
10
+ return;
11
+ await Promise.all(dlxCacheNames.map(async (dlxCacheName) => {
12
+ const dlxCachePath = path.join(dlxCacheDir, dlxCacheName);
13
+ const dlxCacheLink = path.join(dlxCachePath, 'pkg');
14
+ let shouldClean;
15
+ if (dlxCacheMaxAge <= 0) {
16
+ shouldClean = true;
17
+ }
18
+ else {
19
+ const dlxCacheLinkStats = await getStats(dlxCacheLink);
20
+ shouldClean = dlxCacheLinkStats !== 'ENOENT' && isOutdated(dlxCacheLinkStats, dlxCacheMaxAge, now);
21
+ }
22
+ if (shouldClean) {
23
+ // delete the symlink, the symlink's target, and orphans (if any)
24
+ await fs.rm(dlxCachePath, { recursive: true, force: true });
25
+ }
26
+ }));
27
+ await cleanOrphans(dlxCacheDir);
28
+ }
29
+ export async function cleanOrphans(dlxCacheDir) {
30
+ const dlxCacheNames = readOptDir(dlxCacheDir);
31
+ if (!dlxCacheNames)
32
+ return;
33
+ await Promise.all(dlxCacheNames.map(async dlxCacheName => {
34
+ const dlxCachePath = path.join(dlxCacheDir, dlxCacheName);
35
+ const dlxCacheLink = path.join(dlxCachePath, 'pkg');
36
+ const dlxCacheLinkStats = await getStats(dlxCacheLink);
37
+ if (dlxCacheLinkStats === 'ENOENT') {
38
+ return fs.rm(dlxCachePath, { recursive: true, force: true });
39
+ }
40
+ const dlxCacheLinkTarget = await getRealPath(dlxCacheLink);
41
+ const children = await fs.readdir(dlxCachePath);
42
+ await Promise.all(children.map(async name => {
43
+ if (name === 'pkg')
44
+ return;
45
+ const fullPath = path.join(dlxCachePath, name);
46
+ if (fullPath === dlxCacheLinkTarget)
47
+ return;
48
+ await fs.rm(fullPath, { recursive: true, force: true });
49
+ }));
50
+ }));
51
+ }
52
+ function isOutdated(stats, dlxCacheMaxAge, now) {
53
+ return stats.mtime.getTime() + dlxCacheMaxAge * 60_000 < now.getTime();
54
+ }
55
+ async function getStats(path) {
56
+ try {
57
+ return await fs.lstat(path);
58
+ }
59
+ catch (err) {
60
+ if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
61
+ return 'ENOENT';
62
+ }
63
+ throw err;
64
+ }
65
+ }
66
+ function readOptDir(dirPath) {
67
+ try {
68
+ const dirEntries = [];
69
+ for (const entry of readdirSync(dirPath, {
70
+ encoding: 'utf-8',
71
+ withFileTypes: true,
72
+ })) {
73
+ if (entry.isDirectory()) {
74
+ dirEntries.push(entry.name);
75
+ }
76
+ }
77
+ return dirEntries;
78
+ }
79
+ catch (err) {
80
+ if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
81
+ return null;
82
+ }
83
+ throw err;
84
+ }
85
+ }
86
+ async function getRealPath(linkPath) {
87
+ try {
88
+ return await fs.realpath(linkPath);
89
+ }
90
+ catch (err) {
91
+ if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
92
+ return null;
93
+ }
94
+ throw err;
95
+ }
96
+ }
97
+ //# sourceMappingURL=cleanExpiredDlxCache.js.map
@@ -0,0 +1,2 @@
1
+ import * as store from './store.js';
2
+ export { store };
@@ -0,0 +1,3 @@
1
+ import * as store from './store.js';
2
+ export { store };
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,12 @@
1
+ import { type Config } from '@pnpm/config.reader';
2
+ import { type LogBase } from '@pnpm/logger';
3
+ import { type CreateStoreControllerOptions } from '@pnpm/store.connection-manager';
4
+ export declare const skipPackageManagerCheck = true;
5
+ export declare const rcOptionsTypes: typeof cliOptionsTypes;
6
+ export declare function cliOptionsTypes(): Record<string, unknown>;
7
+ export declare const commandNames: string[];
8
+ export declare function help(): string;
9
+ export type StoreCommandOptions = Pick<Config, 'dir' | 'lockfileDir' | 'registries' | 'tag' | 'storeDir' | 'force' | 'dlxCacheMaxAge'> & Partial<Pick<Config, 'globalPkgDir'>> & CreateStoreControllerOptions & {
10
+ reporter?: (logObj: LogBase) => void;
11
+ };
12
+ export declare function handler(opts: StoreCommandOptions, params: string[]): Promise<string | undefined>;
@@ -0,0 +1,121 @@
1
+ import { docsUrl } from '@pnpm/cli.utils';
2
+ import { types as allTypes } from '@pnpm/config.reader';
3
+ import { PnpmError } from '@pnpm/error';
4
+ import { logger } from '@pnpm/logger';
5
+ import { createStoreController } from '@pnpm/store.connection-manager';
6
+ import { getStorePath } from '@pnpm/store.path';
7
+ import { pick } from 'ramda';
8
+ import { renderHelp } from 'render-help';
9
+ import { storeAdd } from './storeAdd.js';
10
+ import { storePrune } from './storePrune.js';
11
+ import { storeStatus } from './storeStatus/index.js';
12
+ export const skipPackageManagerCheck = true;
13
+ export const rcOptionsTypes = cliOptionsTypes;
14
+ export function cliOptionsTypes() {
15
+ return pick([
16
+ 'registry',
17
+ 'store-dir',
18
+ 'force',
19
+ ], allTypes);
20
+ }
21
+ export const commandNames = ['store'];
22
+ export function help() {
23
+ return renderHelp({
24
+ description: 'Reads and performs actions on pnpm store that is on the current filesystem.',
25
+ descriptionLists: [
26
+ {
27
+ title: 'Commands',
28
+ list: [
29
+ {
30
+ description: '\
31
+ Checks for modified packages in the store. \
32
+ Returns exit code 0 if the content of the package is the same as it was at the time of unpacking',
33
+ name: 'status',
34
+ },
35
+ {
36
+ description: 'Adds new packages to the store. Example: pnpm store add express@4 typescript@2.1.0',
37
+ name: 'add <pkg>...',
38
+ },
39
+ {
40
+ description: '\
41
+ Removes unreferenced (extraneous, orphan) packages from the store. \
42
+ Pruning the store is not harmful, but might slow down future installations. \
43
+ Visit the documentation for more information on unreferenced packages and why they occur',
44
+ name: 'prune',
45
+ },
46
+ {
47
+ description: 'If there are alien directories in the store, this command removes them. \
48
+ Alien directories are directories/files that were not created by the package manager.',
49
+ name: 'prune --force',
50
+ },
51
+ {
52
+ description: 'Returns the path to the active store directory.',
53
+ name: 'path',
54
+ },
55
+ ],
56
+ },
57
+ ],
58
+ url: docsUrl('store'),
59
+ usages: ['pnpm store <command>'],
60
+ });
61
+ }
62
+ class StoreStatusError extends PnpmError {
63
+ modified;
64
+ constructor(modified) {
65
+ super('MODIFIED_DEPENDENCY', '');
66
+ this.modified = modified;
67
+ }
68
+ }
69
+ export async function handler(opts, params) {
70
+ let store;
71
+ switch (params[0]) {
72
+ case 'status':
73
+ return statusCmd(opts);
74
+ case 'path':
75
+ return getStorePath({
76
+ pkgRoot: opts.workspaceDir ?? opts.dir,
77
+ storePath: opts.storeDir,
78
+ pnpmHomeDir: opts.pnpmHomeDir,
79
+ });
80
+ case 'prune': {
81
+ store = await createStoreController(opts);
82
+ const storePruneOptions = Object.assign(opts, {
83
+ storeController: store.ctrl,
84
+ storeDir: store.dir,
85
+ removeAlienFiles: opts.force,
86
+ cacheDir: opts.cacheDir,
87
+ dlxCacheMaxAge: opts.dlxCacheMaxAge,
88
+ });
89
+ return storePrune(storePruneOptions);
90
+ }
91
+ case 'add':
92
+ store = await createStoreController(opts);
93
+ return storeAdd(params.slice(1), {
94
+ prefix: opts.dir,
95
+ reporter: opts.reporter,
96
+ storeController: store.ctrl,
97
+ tag: opts.tag,
98
+ });
99
+ default:
100
+ return help();
101
+ }
102
+ }
103
+ async function statusCmd(opts) {
104
+ const modifiedPkgs = await storeStatus(Object.assign(opts, {
105
+ lockfileDir: opts.lockfileDir ?? opts.workspaceDir ?? opts.dir,
106
+ storeDir: await getStorePath({
107
+ pkgRoot: opts.workspaceDir ?? opts.dir,
108
+ storePath: opts.storeDir,
109
+ pnpmHomeDir: opts.pnpmHomeDir,
110
+ }),
111
+ }));
112
+ if (!modifiedPkgs || (modifiedPkgs.length === 0)) {
113
+ logger.info({
114
+ message: 'Packages in the store are untouched',
115
+ prefix: opts.dir,
116
+ });
117
+ return;
118
+ }
119
+ throw new StoreStatusError(modifiedPkgs);
120
+ }
121
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1,10 @@
1
+ import type { StoreController } from '@pnpm/store.controller-types';
2
+ import type { SupportedArchitectures } from '@pnpm/types';
3
+ import type { ReporterFunction } from './types.js';
4
+ export declare function storeAdd(fuzzyDeps: string[], opts: {
5
+ prefix?: string;
6
+ reporter?: ReporterFunction;
7
+ storeController: StoreController;
8
+ tag?: string;
9
+ supportedArchitectures?: SupportedArchitectures;
10
+ }): Promise<void>;
@@ -0,0 +1,36 @@
1
+ import { PnpmError } from '@pnpm/error';
2
+ import { globalInfo, logger, streamParser } from '@pnpm/logger';
3
+ import { parseWantedDependency } from '@pnpm/resolving.parse-wanted-dependency';
4
+ export async function storeAdd(fuzzyDeps, opts) {
5
+ const reporter = opts?.reporter;
6
+ if ((reporter != null) && typeof reporter === 'function') {
7
+ streamParser.on('data', reporter);
8
+ }
9
+ const deps = fuzzyDeps.map((dep) => parseWantedDependency(dep));
10
+ let hasFailures = false;
11
+ const prefix = opts.prefix ?? process.cwd();
12
+ await Promise.all(deps.map(async (dep) => {
13
+ try {
14
+ const pkgResponse = await opts.storeController.requestPackage(dep, {
15
+ downloadPriority: 1,
16
+ lockfileDir: prefix,
17
+ preferredVersions: {},
18
+ projectDir: prefix,
19
+ supportedArchitectures: opts.supportedArchitectures,
20
+ });
21
+ await pkgResponse.fetching();
22
+ globalInfo(`+ ${pkgResponse.body.id}`);
23
+ }
24
+ catch (e) { // eslint-disable-line
25
+ hasFailures = true;
26
+ logger('store').error(e);
27
+ }
28
+ }));
29
+ if ((reporter != null) && typeof reporter === 'function') {
30
+ streamParser.removeListener('data', reporter);
31
+ }
32
+ if (hasFailures) {
33
+ throw new PnpmError('STORE_ADD_FAILURE', 'Some packages have not been added correctly');
34
+ }
35
+ }
36
+ //# sourceMappingURL=storeAdd.js.map
@@ -0,0 +1,10 @@
1
+ import type { StoreController } from '@pnpm/store.controller-types';
2
+ import type { ReporterFunction } from './types.js';
3
+ export declare function storePrune(opts: {
4
+ reporter?: ReporterFunction;
5
+ storeController: StoreController;
6
+ removeAlienFiles?: boolean;
7
+ cacheDir: string;
8
+ dlxCacheMaxAge: number;
9
+ globalPkgDir?: string;
10
+ }): Promise<void>;
@@ -0,0 +1,23 @@
1
+ import { cleanOrphanedInstallDirs } from '@pnpm/global.packages';
2
+ import { streamParser } from '@pnpm/logger';
3
+ import { cleanExpiredDlxCache } from './cleanExpiredDlxCache.js';
4
+ export async function storePrune(opts) {
5
+ const reporter = opts?.reporter;
6
+ if ((reporter != null) && typeof reporter === 'function') {
7
+ streamParser.on('data', reporter);
8
+ }
9
+ await opts.storeController.prune(opts.removeAlienFiles);
10
+ await opts.storeController.close();
11
+ await cleanExpiredDlxCache({
12
+ cacheDir: opts.cacheDir,
13
+ dlxCacheMaxAge: opts.dlxCacheMaxAge,
14
+ now: new Date(),
15
+ });
16
+ if (opts.globalPkgDir) {
17
+ cleanOrphanedInstallDirs(opts.globalPkgDir);
18
+ }
19
+ if ((reporter != null) && typeof reporter === 'function') {
20
+ streamParser.removeListener('data', reporter);
21
+ }
22
+ }
23
+ //# sourceMappingURL=storePrune.js.map
@@ -0,0 +1,23 @@
1
+ import type { Registries } from '@pnpm/types';
2
+ import type { ReporterFunction } from '../types.js';
3
+ export interface StrictStoreStatusOptions {
4
+ autoInstallPeers: boolean;
5
+ excludeLinksFromLockfile: boolean;
6
+ lockfileDir: string;
7
+ dir: string;
8
+ storeDir: string;
9
+ force: boolean;
10
+ nodeLinker: 'isolated' | 'hoisted' | 'pnp';
11
+ useLockfile: boolean;
12
+ registries: Registries;
13
+ shamefullyHoist: boolean;
14
+ reporter: ReporterFunction;
15
+ production: boolean;
16
+ development: boolean;
17
+ optional: boolean;
18
+ binsDir: string;
19
+ virtualStoreDirMaxLength: number;
20
+ peersSuffixMaxLength: number;
21
+ }
22
+ export type StoreStatusOptions = Partial<StrictStoreStatusOptions> & Pick<StrictStoreStatusOptions, 'storeDir' | 'virtualStoreDirMaxLength'>;
23
+ export declare function extendStoreStatusOptions(opts: StoreStatusOptions): Promise<StrictStoreStatusOptions>;
@@ -0,0 +1,31 @@
1
+ import path from 'node:path';
2
+ import { DEFAULT_REGISTRIES, normalizeRegistries } from '@pnpm/config.normalize-registries';
3
+ const defaults = async (opts) => {
4
+ const dir = opts.dir ?? process.cwd();
5
+ const lockfileDir = opts.lockfileDir ?? dir;
6
+ return {
7
+ binsDir: path.join(dir, 'node_modules', '.bin'),
8
+ dir,
9
+ force: false,
10
+ lockfileDir,
11
+ nodeLinker: 'isolated',
12
+ registries: DEFAULT_REGISTRIES,
13
+ shamefullyHoist: false,
14
+ storeDir: opts.storeDir,
15
+ useLockfile: true,
16
+ };
17
+ };
18
+ export async function extendStoreStatusOptions(opts) {
19
+ if (opts) {
20
+ for (const key in opts) {
21
+ if (opts[key] === undefined) {
22
+ delete opts[key];
23
+ }
24
+ }
25
+ }
26
+ const defaultOpts = await defaults(opts);
27
+ const extendedOpts = { ...defaultOpts, ...opts, storeDir: defaultOpts.storeDir };
28
+ extendedOpts.registries = normalizeRegistries(extendedOpts.registries);
29
+ return extendedOpts;
30
+ }
31
+ //# sourceMappingURL=extendStoreStatusOptions.js.map
@@ -0,0 +1,2 @@
1
+ import { type StoreStatusOptions } from './extendStoreStatusOptions.js';
2
+ export declare function storeStatus(maybeOpts: StoreStatusOptions): Promise<string[]>;
@@ -0,0 +1,66 @@
1
+ import path from 'node:path';
2
+ import { formatIntegrity } from '@pnpm/crypto.integrity';
3
+ import * as dp from '@pnpm/deps.path';
4
+ import { getContextForSingleImporter } from '@pnpm/installing.context';
5
+ import { nameVerFromPkgSnapshot, packageIdFromSnapshot, } from '@pnpm/lockfile.utils';
6
+ import { streamParser } from '@pnpm/logger';
7
+ import { gitHostedStoreIndexKey, storeIndexKey } from '@pnpm/store.index';
8
+ import { StoreIndex } from '@pnpm/store.index';
9
+ import dint from 'dint';
10
+ import pFilter from 'p-filter';
11
+ import { extendStoreStatusOptions, } from './extendStoreStatusOptions.js';
12
+ export async function storeStatus(maybeOpts) {
13
+ const reporter = maybeOpts?.reporter;
14
+ if ((reporter != null) && typeof reporter === 'function') {
15
+ streamParser.on('data', reporter);
16
+ }
17
+ const opts = await extendStoreStatusOptions(maybeOpts);
18
+ const { storeDir, skipped, virtualStoreDir, wantedLockfile, } = await getContextForSingleImporter({}, {
19
+ ...opts,
20
+ extraBinPaths: [], // ctx.extraBinPaths is not needed, so this is fine
21
+ });
22
+ if (!wantedLockfile)
23
+ return [];
24
+ const pkgs = Object.entries(wantedLockfile.packages ?? {})
25
+ .filter(([depPath]) => !skipped.has(depPath))
26
+ .map(([depPath, pkgSnapshot]) => {
27
+ const id = packageIdFromSnapshot(depPath, pkgSnapshot);
28
+ return {
29
+ depPath,
30
+ id,
31
+ integrity: pkgSnapshot.resolution.integrity,
32
+ pkgPath: depPath,
33
+ ...nameVerFromPkgSnapshot(depPath, pkgSnapshot),
34
+ };
35
+ });
36
+ const storeIndex = new StoreIndex(storeDir);
37
+ try {
38
+ const modified = await pFilter(pkgs, async ({ id, integrity, depPath, name }) => {
39
+ const pkgIndexFilePath = integrity
40
+ ? storeIndexKey(integrity, id)
41
+ : gitHostedStoreIndexKey(id, { built: true });
42
+ const pkgFilesIndex = storeIndex.get(pkgIndexFilePath);
43
+ if (!pkgFilesIndex) {
44
+ return false;
45
+ }
46
+ const { algo, files } = pkgFilesIndex;
47
+ // Transform files to dint format: { integrity: '<algo>-<base64>', size: number }
48
+ const dintFiles = {};
49
+ for (const [filePath, { digest, size }] of files) {
50
+ dintFiles[filePath] = {
51
+ integrity: formatIntegrity(algo, digest),
52
+ size,
53
+ };
54
+ }
55
+ return (await dint.check(path.join(virtualStoreDir, dp.depPathToFilename(depPath, maybeOpts.virtualStoreDirMaxLength), 'node_modules', name), dintFiles)) === false;
56
+ }, { concurrency: 8 });
57
+ if ((reporter != null) && typeof reporter === 'function') {
58
+ streamParser.removeListener('data', reporter);
59
+ }
60
+ return modified.map(({ pkgPath }) => pkgPath);
61
+ }
62
+ finally {
63
+ storeIndex.close();
64
+ }
65
+ }
66
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ import type { LogBase } from '@pnpm/logger';
2
+ export type ReporterFunction = (logObj: LogBase) => void;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
package/package.json ADDED
@@ -0,0 +1,91 @@
1
+ {
2
+ "name": "@pnpm/store.commands",
3
+ "version": "1000.0.0",
4
+ "description": "Commands for controlling and inspecting the store",
5
+ "keywords": [
6
+ "pnpm",
7
+ "pnpm11",
8
+ "scripts"
9
+ ],
10
+ "license": "MIT",
11
+ "funding": "https://opencollective.com/pnpm",
12
+ "repository": "https://github.com/pnpm/pnpm/tree/main/store/commands",
13
+ "homepage": "https://github.com/pnpm/pnpm/tree/main/store/commands#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/pnpm/pnpm/issues"
16
+ },
17
+ "type": "module",
18
+ "main": "lib/index.js",
19
+ "types": "lib/index.d.ts",
20
+ "exports": {
21
+ ".": "./lib/index.js"
22
+ },
23
+ "files": [
24
+ "lib",
25
+ "!*.map"
26
+ ],
27
+ "dependencies": {
28
+ "@pnpm/util.lex-comparator": "^3.0.2",
29
+ "archy": "^1.0.0",
30
+ "chalk": "^5.6.0",
31
+ "dint": "^5.1.0",
32
+ "p-filter": "^4.1.0",
33
+ "ramda": "npm:@pnpm/ramda@0.28.1",
34
+ "render-help": "^2.0.0",
35
+ "@pnpm/cli.utils": "1001.2.8",
36
+ "@pnpm/config.normalize-registries": "1000.1.4",
37
+ "@pnpm/config.reader": "1004.4.2",
38
+ "@pnpm/fs.graceful-fs": "1000.0.1",
39
+ "@pnpm/error": "1000.0.5",
40
+ "@pnpm/global.packages": "1000.0.0-0",
41
+ "@pnpm/deps.path": "1001.1.3",
42
+ "@pnpm/installing.client": "1001.1.4",
43
+ "@pnpm/crypto.integrity": "1100.0.0-0",
44
+ "@pnpm/installing.context": "1001.1.8",
45
+ "@pnpm/lockfile.types": "1002.0.2",
46
+ "@pnpm/resolving.parse-wanted-dependency": "1001.0.0",
47
+ "@pnpm/lockfile.utils": "1003.0.3",
48
+ "@pnpm/store.cafs": "1000.0.19",
49
+ "@pnpm/store.connection-manager": "1002.2.4",
50
+ "@pnpm/object.key-sorting": "1000.0.1",
51
+ "@pnpm/store.controller-types": "1004.1.0",
52
+ "@pnpm/store.index": "1000.0.0-0",
53
+ "@pnpm/store.path": "1000.0.5",
54
+ "@pnpm/types": "1000.9.0"
55
+ },
56
+ "peerDependencies": {
57
+ "@pnpm/logger": ">=1001.0.0 <1002.0.0"
58
+ },
59
+ "devDependencies": {
60
+ "@jest/globals": "30.0.5",
61
+ "@pnpm/registry-mock": "5.2.4",
62
+ "@types/archy": "0.0.36",
63
+ "@types/ramda": "0.29.12",
64
+ "@types/ssri": "^7.1.5",
65
+ "@zkochan/rimraf": "^4.0.0",
66
+ "execa": "npm:safe-execa@0.3.0",
67
+ "load-json-file": "^7.0.1",
68
+ "ssri": "13.0.0",
69
+ "tempy": "3.0.0",
70
+ "@pnpm/assert-store": "1000.0.3",
71
+ "@pnpm/constants": "1001.3.1",
72
+ "@pnpm/exec.commands": "1001.0.15",
73
+ "@pnpm/lockfile.fs": "1001.1.21",
74
+ "@pnpm/store.commands": "1000.0.0",
75
+ "@pnpm/prepare": "1000.0.4",
76
+ "@pnpm/store.controller": "1004.0.0",
77
+ "@pnpm/logger": "1001.0.1"
78
+ },
79
+ "engines": {
80
+ "node": ">=22.13"
81
+ },
82
+ "jest": {
83
+ "preset": "@pnpm/jest-config/with-registry"
84
+ },
85
+ "scripts": {
86
+ "lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
87
+ "_test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest",
88
+ "test": "pnpm run compile && pnpm run _test",
89
+ "compile": "tsgo --build && pnpm run lint --fix"
90
+ }
91
+ }