@pnpm/global.packages 1100.0.12 → 1100.0.13

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @pnpm/global.packages
2
2
 
3
+ ## 1100.0.13
4
+
5
+ ### Patch Changes
6
+
7
+ - Republished every package: the tarballs published by the v11.13.1 through v11.16.0 releases were missing most of their compiled files due to a packing bug [#13164](https://github.com/pnpm/pnpm/issues/13164).
8
+
9
+ - Updated dependencies:
10
+ - @pnpm/bins.resolver@1100.0.11
11
+ - @pnpm/crypto.hash@1100.0.2
12
+ - @pnpm/pkg-manifest.reader@1100.0.12
13
+ - @pnpm/types@1101.6.0
14
+
3
15
  ## 1100.0.12
4
16
 
5
17
  ### Patch Changes
@@ -0,0 +1,4 @@
1
+ export declare function createGlobalCacheKey(opts: {
2
+ aliases: string[];
3
+ registries: Record<string, string>;
4
+ }): string;
@@ -0,0 +1,9 @@
1
+ import { createHexHash } from '@pnpm/crypto.hash';
2
+ import { lexCompare } from '@pnpm/util.lex-comparator';
3
+ export function createGlobalCacheKey(opts) {
4
+ const sortedAliases = [...opts.aliases].sort(lexCompare);
5
+ const sortedRegistries = Object.entries(opts.registries).sort(([k1], [k2]) => lexCompare(k1, k2));
6
+ const hashStr = JSON.stringify([sortedAliases, sortedRegistries]);
7
+ return createHexHash(hashStr);
8
+ }
9
+ //# sourceMappingURL=cacheKey.js.map
@@ -0,0 +1,3 @@
1
+ export declare function getHashLink(globalDir: string, hash: string): string;
2
+ export declare function resolveInstallDir(globalDir: string, hash: string): string | null;
3
+ export declare function createInstallDir(globalDir: string): string;
@@ -0,0 +1,44 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import util from 'node:util';
5
+ export function getHashLink(globalDir, hash) {
6
+ return path.join(globalDir, hash);
7
+ }
8
+ export function resolveInstallDir(globalDir, hash) {
9
+ const linkPath = getHashLink(globalDir, hash);
10
+ try {
11
+ const stats = fs.lstatSync(linkPath);
12
+ if (!stats.isSymbolicLink())
13
+ return null;
14
+ return fs.realpathSync(linkPath);
15
+ }
16
+ catch (err) {
17
+ if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
18
+ return null;
19
+ }
20
+ throw err;
21
+ }
22
+ }
23
+ export function createInstallDir(globalDir) {
24
+ // Ensure the parent exists, then create the per-group dir *exclusively*
25
+ // (no `recursive`, which would silently reuse an existing entry or follow
26
+ // a pre-existing symlink). The name adds random bytes on top of pid+time
27
+ // so it isn't predictable and can't collide within the same millisecond.
28
+ fs.mkdirSync(globalDir, { recursive: true });
29
+ for (let i = 0; i < 10; i++) {
30
+ const name = `${process.pid.toString(16)}-${Date.now().toString(16)}-${crypto.randomBytes(8).toString('hex')}`;
31
+ const dir = path.join(globalDir, name);
32
+ try {
33
+ fs.mkdirSync(dir);
34
+ return dir;
35
+ }
36
+ catch (err) {
37
+ if (util.types.isNativeError(err) && 'code' in err && err.code === 'EEXIST')
38
+ continue;
39
+ throw err;
40
+ }
41
+ }
42
+ throw new Error('Could not create a unique global install directory');
43
+ }
44
+ //# sourceMappingURL=globalPackageDir.js.map
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { createGlobalCacheKey } from './cacheKey.js';
2
+ export { createInstallDir, getHashLink, resolveInstallDir, } from './globalPackageDir.js';
3
+ export { cleanOrphanedInstallDirs, findGlobalPackage, getGlobalPackageDetails, getInstalledBinNames, type GlobalPackageInfo, type InstalledGlobalPackage, isValidGlobalDependencyAlias, scanGlobalPackages, } from './scanGlobalPackages.js';
@@ -0,0 +1,17 @@
1
+ import type { PackageManifest } from '@pnpm/types';
2
+ export declare function isValidGlobalDependencyAlias(alias: string): boolean;
3
+ export interface GlobalPackageInfo {
4
+ hash: string;
5
+ installDir: string;
6
+ dependencies: Record<string, string>;
7
+ }
8
+ export interface InstalledGlobalPackage {
9
+ alias: string;
10
+ version: string;
11
+ manifest: PackageManifest;
12
+ }
13
+ export declare function scanGlobalPackages(globalDir: string): GlobalPackageInfo[];
14
+ export declare function findGlobalPackage(globalDir: string, alias: string): GlobalPackageInfo | null;
15
+ export declare function getGlobalPackageDetails(info: GlobalPackageInfo): Promise<InstalledGlobalPackage[]>;
16
+ export declare function cleanOrphanedInstallDirs(globalDir: string): void;
17
+ export declare function getInstalledBinNames(info: GlobalPackageInfo): Promise<string[]>;
@@ -0,0 +1,158 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import util from 'node:util';
4
+ import { getBinsFromPackageManifest } from '@pnpm/bins.resolver';
5
+ import { readPackageJsonFromDirRawSync, safeReadPackageJsonFromDir } from '@pnpm/pkg-manifest.reader';
6
+ const RESERVED_ALIASES = new Set(['node_modules', 'favicon.ico']);
7
+ const isUrlFriendly = (segment) => encodeURIComponent(segment) === segment;
8
+ // A dependency alias from a global group's package.json becomes a directory
9
+ // name under `node_modules` at every join site (list, conflict-check,
10
+ // remove, update). A tampered manifest could use an alias like `../x` or an
11
+ // absolute path to escape the install dir, so only valid npm package names
12
+ // are trusted. This applies the same `validForOldPackages` rules that
13
+ // `validate-npm-package-name` (and `@pnpm/fs.symlink-dependency`'s
14
+ // `safeJoinModulesDir`) enforce — implemented inline to avoid adding a
15
+ // dependency to this low-level package.
16
+ export function isValidGlobalDependencyAlias(alias) {
17
+ if (alias.length === 0)
18
+ return false;
19
+ if (/^[._-]/.test(alias))
20
+ return false;
21
+ if (alias.trim() !== alias)
22
+ return false;
23
+ if (RESERVED_ALIASES.has(alias.toLowerCase()))
24
+ return false;
25
+ if (isUrlFriendly(alias))
26
+ return true;
27
+ const scoped = /^@([^/]+)\/([^/]+)$/.exec(alias);
28
+ if (scoped) {
29
+ const [, scope, name] = scoped;
30
+ return !name.startsWith('.') && isUrlFriendly(scope) && isUrlFriendly(name);
31
+ }
32
+ return false;
33
+ }
34
+ function pickValidDependencies(dependencies) {
35
+ const result = {};
36
+ for (const [alias, spec] of Object.entries(dependencies)) {
37
+ if (isValidGlobalDependencyAlias(alias)) {
38
+ result[alias] = spec;
39
+ }
40
+ }
41
+ return result;
42
+ }
43
+ export function scanGlobalPackages(globalDir) {
44
+ let entries;
45
+ try {
46
+ entries = fs.readdirSync(globalDir, { withFileTypes: true });
47
+ }
48
+ catch (err) {
49
+ if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
50
+ return [];
51
+ }
52
+ throw err;
53
+ }
54
+ const result = [];
55
+ for (const entry of entries) {
56
+ // Hash entries are symlinks pointing to install dirs
57
+ if (!entry.isSymbolicLink())
58
+ continue;
59
+ const linkPath = path.join(globalDir, entry.name);
60
+ let installDir;
61
+ try {
62
+ installDir = fs.realpathSync(linkPath);
63
+ }
64
+ catch {
65
+ continue;
66
+ }
67
+ let pkgJson;
68
+ try {
69
+ pkgJson = readPackageJsonFromDirRawSync(installDir);
70
+ }
71
+ catch {
72
+ continue;
73
+ }
74
+ if (!pkgJson.dependencies)
75
+ continue;
76
+ const dependencies = pickValidDependencies(pkgJson.dependencies);
77
+ if (Object.keys(dependencies).length === 0)
78
+ continue;
79
+ result.push({
80
+ hash: entry.name,
81
+ installDir,
82
+ dependencies,
83
+ });
84
+ }
85
+ return result;
86
+ }
87
+ export function findGlobalPackage(globalDir, alias) {
88
+ const packages = scanGlobalPackages(globalDir);
89
+ return packages.find((pkg) => alias in pkg.dependencies) ?? null;
90
+ }
91
+ export async function getGlobalPackageDetails(info) {
92
+ const aliases = Object.keys(info.dependencies);
93
+ const installedPackages = await Promise.all(aliases.map(async (alias) => {
94
+ const manifest = await safeReadPackageJsonFromDir(path.join(info.installDir, 'node_modules', alias));
95
+ if (!manifest)
96
+ return null;
97
+ return { alias, version: manifest.version, manifest };
98
+ }));
99
+ return installedPackages.filter((pkg) => pkg !== null);
100
+ }
101
+ export function cleanOrphanedInstallDirs(globalDir) {
102
+ globalDir = path.resolve(globalDir);
103
+ let entries;
104
+ try {
105
+ entries = fs.readdirSync(globalDir, { withFileTypes: true });
106
+ }
107
+ catch {
108
+ return;
109
+ }
110
+ // Collect real paths of all symlink targets
111
+ const referenced = new Set();
112
+ for (const entry of entries) {
113
+ if (!entry.isSymbolicLink())
114
+ continue;
115
+ try {
116
+ referenced.add(fs.realpathSync(path.join(globalDir, entry.name)));
117
+ }
118
+ catch { }
119
+ }
120
+ // Remove directories that no symlink points to.
121
+ // Skip recently-created dirs to avoid racing with a concurrent install
122
+ // that hasn't created its hash symlink yet.
123
+ const now = Date.now();
124
+ const SAFETY_WINDOW_MS = 5 * 60 * 1000;
125
+ for (const entry of entries) {
126
+ if (!entry.isDirectory())
127
+ continue;
128
+ const dirPath = path.join(globalDir, entry.name);
129
+ if (referenced.has(dirPath))
130
+ continue;
131
+ try {
132
+ const stat = fs.statSync(dirPath);
133
+ if (now - Math.max(stat.birthtimeMs, stat.ctimeMs) < SAFETY_WINDOW_MS)
134
+ continue;
135
+ }
136
+ catch {
137
+ continue;
138
+ }
139
+ fs.rmSync(dirPath, { recursive: true, force: true });
140
+ }
141
+ }
142
+ export async function getInstalledBinNames(info) {
143
+ const bins = new Set();
144
+ const aliases = Object.keys(info.dependencies);
145
+ const modulesDir = path.join(info.installDir, 'node_modules');
146
+ await Promise.all(aliases.map(async (alias) => {
147
+ const depDir = path.join(modulesDir, alias);
148
+ const manifest = await safeReadPackageJsonFromDir(depDir);
149
+ if (!manifest)
150
+ return;
151
+ const binsOfPkg = await getBinsFromPackageManifest(manifest, depDir);
152
+ for (const bin of binsOfPkg) {
153
+ bins.add(bin.name);
154
+ }
155
+ }));
156
+ return [...bins];
157
+ }
158
+ //# sourceMappingURL=scanGlobalPackages.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/global.packages",
3
- "version": "1100.0.12",
3
+ "version": "1100.0.13",
4
4
  "description": "Utilities for managing isolated global packages",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -28,14 +28,14 @@
28
28
  "!*.map"
29
29
  ],
30
30
  "dependencies": {
31
- "@pnpm/bins.resolver": "1100.0.10",
32
- "@pnpm/crypto.hash": "1100.0.1",
33
- "@pnpm/pkg-manifest.reader": "1100.0.11",
34
- "@pnpm/types": "1101.5.0",
31
+ "@pnpm/bins.resolver": "1100.0.11",
32
+ "@pnpm/crypto.hash": "1100.0.2",
33
+ "@pnpm/pkg-manifest.reader": "1100.0.12",
34
+ "@pnpm/types": "1101.6.0",
35
35
  "@pnpm/util.lex-comparator": "^4.0.1"
36
36
  },
37
37
  "devDependencies": {
38
- "@pnpm/global.packages": "1100.0.12"
38
+ "@pnpm/global.packages": "1100.0.13"
39
39
  },
40
40
  "engines": {
41
41
  "node": ">=22.13"