inup 1.6.6 → 1.6.8
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/README.md +25 -2
- package/dist/{interactive-ui.js → app/interactive-ui.js} +34 -21
- package/dist/{core → app}/upgrade-runner.js +18 -22
- package/dist/cli.js +19 -18
- package/dist/{services → features/audit}/background-audit.js +2 -2
- package/dist/features/{headless/vulnerability-audit.js → audit/headless-audit.js} +8 -11
- package/dist/features/audit/index.js +36 -0
- package/dist/{ui/presenters/vulnerability.js → features/audit/presenter.js} +1 -3
- package/dist/{ui/controllers → features/audit}/vulnerability-audit-controller.js +14 -8
- package/dist/{services → features/audit}/vulnerability-checker.js +9 -10
- package/dist/features/changelog/clients/github-client.js +41 -21
- package/dist/features/changelog/clients/npm-registry-client.js +16 -2
- package/dist/features/changelog/index.js +1 -1
- package/dist/features/changelog/services/package-metadata-service.js +6 -2
- package/dist/features/changelog/services/release-notes-service.js +9 -12
- package/dist/features/changelog/types.js +3 -0
- package/dist/features/debug/index.js +3 -4
- package/dist/features/debug/{services/perf-logger.js → perf-logger.js} +15 -15
- package/dist/features/debug/types.js +3 -0
- package/dist/features/headless/headless-runner.js +32 -19
- package/dist/features/headless/index.js +2 -5
- package/dist/features/headless/report.js +2 -0
- package/dist/features/interactive/controllers/index.js +6 -0
- package/dist/{ui → features/interactive}/controllers/package-info-modal-controller.js +3 -3
- package/dist/features/interactive/index.js +21 -0
- package/dist/{ui → features/interactive}/input-handler.js +6 -6
- package/dist/{ui → features/interactive}/modal/index.js +1 -1
- package/dist/{ui → features/interactive}/modal/layout.js +5 -5
- package/dist/{ui/renderer/package-list.js → features/interactive/modal/package-info-sections/index.js} +3 -3
- package/dist/{ui → features/interactive}/modal/package-info-sections/release-notes.js +1 -1
- package/dist/{ui → features/interactive}/modal/package-info-sections/sections.js +55 -24
- package/dist/{ui → features/interactive}/modal/package-info-sections/text.js +6 -6
- package/dist/{ui → features/interactive}/modal/package-info.js +33 -18
- package/dist/features/interactive/modal/types.js +3 -0
- package/dist/{ui → features/interactive}/presenters/health.js +2 -2
- package/dist/{ui → features/interactive}/renderer/confirmation.js +6 -3
- package/dist/{ui → features/interactive}/renderer/help-modal.js +3 -3
- package/dist/{ui → features/interactive}/renderer/index.js +3 -4
- package/dist/{types.js → features/interactive/renderer/package-list/index.js} +3 -4
- package/dist/{ui → features/interactive}/renderer/package-list/interface.js +15 -16
- package/dist/{ui → features/interactive}/renderer/package-list/rows.js +31 -25
- package/dist/features/{debug → interactive}/renderer/performance-modal.js +3 -3
- package/dist/{ui/utils/version.js → features/interactive/renderer/version-format.js} +9 -13
- package/dist/{ui → features/interactive}/session/action-dispatcher.js +2 -2
- package/dist/{ui → features/interactive}/session/index.js +5 -4
- package/dist/{ui → features/interactive}/session/interactive-session.js +44 -23
- package/dist/{ui → features/interactive}/session/selection-state-builder.js +25 -9
- package/dist/{ui → features/interactive}/state/filter-manager.js +3 -3
- package/dist/{ui → features/interactive}/state/index.js +7 -7
- package/dist/{ui → features/interactive}/state/state-manager.js +10 -8
- package/dist/{ui → features/interactive}/state/theme-manager.js +7 -5
- package/dist/{ui → features/interactive}/themes-colors.js +3 -1
- package/dist/features/upgrade/index.js +8 -0
- package/dist/{core → features/upgrade}/package-detector.js +96 -37
- package/dist/{core → features/upgrade}/upgrader.js +84 -35
- package/dist/index.js +2 -2
- package/dist/{config → shared/config}/package-meta.js +1 -1
- package/dist/{config → shared/config}/project-config.js +10 -8
- package/dist/{utils/config.js → shared/config/user-config.js} +11 -11
- package/dist/{utils → shared}/debug-logger.js +9 -9
- package/dist/{utils → shared}/exec.js +4 -4
- package/dist/{ui/modal/package-info-sections.js → shared/fs/index.js} +4 -3
- package/dist/{utils/filesystem → shared/fs}/io.js +3 -4
- package/dist/shared/fs/paths.js +19 -0
- package/dist/{utils/filesystem → shared/fs}/scan.js +23 -20
- package/dist/{utils → shared}/git.js +3 -3
- package/dist/{services → shared}/http/etag-store.js +56 -4
- package/dist/{services → shared}/http/resizable-semaphore.js +3 -1
- package/dist/{utils → shared}/local-env.js +8 -8
- package/dist/{services/package-manager-detector.js → shared/package-manager.js} +26 -25
- package/dist/shared/pnpm-catalogs.js +138 -0
- package/dist/{services → shared/registry}/npm-registry.js +54 -33
- package/dist/shared/registry/registry-config.js +76 -0
- package/dist/{services → shared/registry}/version-checker.js +25 -26
- package/dist/{ui/utils → shared/terminal}/cursor.js +1 -1
- package/dist/{ui/utils → shared/terminal}/index.js +5 -7
- package/dist/{ui/utils → shared/terminal}/terminal-input.js +5 -0
- package/dist/shared/terminal/text.js +48 -0
- package/dist/shared/types/index.js +20 -0
- package/dist/{utils/version.js → shared/versions.js} +45 -30
- package/package.json +21 -8
- package/dist/core/index.js +0 -23
- package/dist/features/changelog/types/changelog.types.js +0 -3
- package/dist/features/debug/types/debug.types.js +0 -3
- package/dist/services/index.js +0 -25
- package/dist/ui/controllers/index.js +0 -8
- package/dist/ui/index.js +0 -27
- package/dist/ui/presenters/index.js +0 -11
- package/dist/ui/utils/text.js +0 -76
- package/dist/utils/filesystem/paths.js +0 -19
- package/dist/utils/filesystem.js +0 -20
- package/dist/utils/index.js +0 -34
- /package/dist/{ui/modal → features/audit}/types.js +0 -0
- /package/dist/features/debug/{services/performance-tracker.js → performance-tracker.js} +0 -0
- /package/dist/{ui → features/interactive}/keymap.js +0 -0
- /package/dist/{ui → features/interactive}/modal/theme-selector.js +0 -0
- /package/dist/{ui → features/interactive}/state/modal-manager.js +0 -0
- /package/dist/{ui → features/interactive}/state/navigation-manager.js +0 -0
- /package/dist/{ui → features/interactive}/themes.js +0 -0
- /package/dist/{config → shared/config}/constants.js +0 -0
- /package/dist/{config → shared/config}/index.js +0 -0
- /package/dist/{utils → shared}/engines.js +0 -0
- /package/dist/{services → shared}/http/adaptive-controller.js +0 -0
- /package/dist/{services → shared}/http/inflight.js +0 -0
- /package/dist/{services → shared}/http/retry.js +0 -0
- /package/dist/{utils → shared}/manifest.js +0 -0
- /package/dist/{utils → shared/terminal}/color.js +0 -0
- /package/dist/{types → shared/types}/domain.js +0 -0
- /package/dist/{types → shared/types}/streaming.js +0 -0
- /package/dist/{types → shared/types}/ui.js +0 -0
|
@@ -4,8 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.PackageManagerDetector = void 0;
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
10
|
const PACKAGE_MANAGERS = {
|
|
11
11
|
npm: {
|
|
@@ -45,18 +45,19 @@ const PACKAGE_MANAGERS = {
|
|
|
45
45
|
color: chalk_1.default.magenta,
|
|
46
46
|
},
|
|
47
47
|
};
|
|
48
|
+
// biome-ignore lint/complexity/noStaticOnlyClass: intentional namespace-style API used throughout the codebase
|
|
48
49
|
class PackageManagerDetector {
|
|
49
50
|
/**
|
|
50
51
|
* Detect package manager from packageManager field or lock files
|
|
51
52
|
*/
|
|
52
53
|
static detect(cwd = process.cwd()) {
|
|
53
54
|
// 1. Check packageManager field in package.json
|
|
54
|
-
const fromPackageJson =
|
|
55
|
+
const fromPackageJson = PackageManagerDetector.detectFromPackageJson(cwd);
|
|
55
56
|
if (fromPackageJson) {
|
|
56
57
|
return fromPackageJson;
|
|
57
58
|
}
|
|
58
59
|
// 2. Check for lock files
|
|
59
|
-
const fromLockFile =
|
|
60
|
+
const fromLockFile = PackageManagerDetector.detectFromLockFiles(cwd);
|
|
60
61
|
if (fromLockFile) {
|
|
61
62
|
return fromLockFile;
|
|
62
63
|
}
|
|
@@ -68,12 +69,12 @@ class PackageManagerDetector {
|
|
|
68
69
|
* Detect from package.json packageManager field
|
|
69
70
|
*/
|
|
70
71
|
static detectFromPackageJson(cwd) {
|
|
71
|
-
const packageJsonPath = (0,
|
|
72
|
-
if (!(0,
|
|
72
|
+
const packageJsonPath = (0, node_path_1.join)(cwd, 'package.json');
|
|
73
|
+
if (!(0, node_fs_1.existsSync)(packageJsonPath)) {
|
|
73
74
|
return null;
|
|
74
75
|
}
|
|
75
76
|
try {
|
|
76
|
-
const content = (0,
|
|
77
|
+
const content = (0, node_fs_1.readFileSync)(packageJsonPath, 'utf-8');
|
|
77
78
|
const packageJson = JSON.parse(content);
|
|
78
79
|
if (packageJson.packageManager) {
|
|
79
80
|
// Parse format: "pnpm@10.28.1" or "npm@9.0.0+sha512.abc..."
|
|
@@ -84,7 +85,7 @@ class PackageManagerDetector {
|
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
|
-
catch
|
|
88
|
+
catch {
|
|
88
89
|
// Invalid package.json, continue to lock file detection
|
|
89
90
|
}
|
|
90
91
|
return null;
|
|
@@ -94,19 +95,19 @@ class PackageManagerDetector {
|
|
|
94
95
|
*/
|
|
95
96
|
static detectFromLockFiles(cwd) {
|
|
96
97
|
const lockFileChecks = [
|
|
97
|
-
{ pm: PACKAGE_MANAGERS.pnpm, path: (0,
|
|
98
|
-
{ pm: PACKAGE_MANAGERS.bun, path: (0,
|
|
98
|
+
{ pm: PACKAGE_MANAGERS.pnpm, path: (0, node_path_1.join)(cwd, 'pnpm-lock.yaml') },
|
|
99
|
+
{ pm: PACKAGE_MANAGERS.bun, path: (0, node_path_1.join)(cwd, 'bun.lockb') },
|
|
99
100
|
// Bun >= 1.2 writes a text `bun.lock` instead of the binary `bun.lockb`.
|
|
100
|
-
{ pm: PACKAGE_MANAGERS.bun, path: (0,
|
|
101
|
-
{ pm: PACKAGE_MANAGERS.yarn, path: (0,
|
|
102
|
-
{ pm: PACKAGE_MANAGERS.npm, path: (0,
|
|
101
|
+
{ pm: PACKAGE_MANAGERS.bun, path: (0, node_path_1.join)(cwd, 'bun.lock') },
|
|
102
|
+
{ pm: PACKAGE_MANAGERS.yarn, path: (0, node_path_1.join)(cwd, 'yarn.lock') },
|
|
103
|
+
{ pm: PACKAGE_MANAGERS.npm, path: (0, node_path_1.join)(cwd, 'package-lock.json') },
|
|
103
104
|
];
|
|
104
105
|
const existingLocks = lockFileChecks
|
|
105
|
-
.filter(({ path }) => (0,
|
|
106
|
+
.filter(({ path }) => (0, node_fs_1.existsSync)(path))
|
|
106
107
|
.map(({ pm, path }) => ({
|
|
107
108
|
pm,
|
|
108
109
|
path,
|
|
109
|
-
mtime: (0,
|
|
110
|
+
mtime: (0, node_fs_1.statSync)(path).mtime.getTime(),
|
|
110
111
|
}));
|
|
111
112
|
if (existingLocks.length === 0) {
|
|
112
113
|
return null;
|
|
@@ -131,20 +132,20 @@ class PackageManagerDetector {
|
|
|
131
132
|
static findWorkspaceRoot(cwd = process.cwd(), packageManager) {
|
|
132
133
|
const pmInfo = PACKAGE_MANAGERS[packageManager];
|
|
133
134
|
let currentDir = cwd;
|
|
134
|
-
while (currentDir !== (0,
|
|
135
|
+
while (currentDir !== (0, node_path_1.join)(currentDir, '..')) {
|
|
135
136
|
// Check for package manager-specific workspace file
|
|
136
137
|
if (pmInfo.workspaceFile) {
|
|
137
|
-
const workspaceFilePath = (0,
|
|
138
|
-
if ((0,
|
|
138
|
+
const workspaceFilePath = (0, node_path_1.join)(currentDir, pmInfo.workspaceFile);
|
|
139
|
+
if ((0, node_fs_1.existsSync)(workspaceFilePath)) {
|
|
139
140
|
return currentDir;
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
else {
|
|
143
144
|
// Check for package.json with workspaces field
|
|
144
|
-
const packageJsonPath = (0,
|
|
145
|
-
if ((0,
|
|
145
|
+
const packageJsonPath = (0, node_path_1.join)(currentDir, 'package.json');
|
|
146
|
+
if ((0, node_fs_1.existsSync)(packageJsonPath)) {
|
|
146
147
|
try {
|
|
147
|
-
const content = (0,
|
|
148
|
+
const content = (0, node_fs_1.readFileSync)(packageJsonPath, 'utf-8');
|
|
148
149
|
const packageJson = JSON.parse(content);
|
|
149
150
|
// Check if workspaces field exists and is non-empty
|
|
150
151
|
if (packageJson.workspaces) {
|
|
@@ -159,12 +160,12 @@ class PackageManagerDetector {
|
|
|
159
160
|
}
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
|
-
catch
|
|
163
|
+
catch {
|
|
163
164
|
// Invalid package.json, continue searching
|
|
164
165
|
}
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
|
-
currentDir = (0,
|
|
168
|
+
currentDir = (0, node_path_1.join)(currentDir, '..');
|
|
168
169
|
}
|
|
169
170
|
return null;
|
|
170
171
|
}
|
|
@@ -172,8 +173,8 @@ class PackageManagerDetector {
|
|
|
172
173
|
* Check if directory is in a workspace
|
|
173
174
|
*/
|
|
174
175
|
static isInWorkspace(cwd, packageManager) {
|
|
175
|
-
return
|
|
176
|
+
return PackageManagerDetector.findWorkspaceRoot(cwd, packageManager) !== null;
|
|
176
177
|
}
|
|
177
178
|
}
|
|
178
179
|
exports.PackageManagerDetector = PackageManagerDetector;
|
|
179
|
-
//# sourceMappingURL=package-manager
|
|
180
|
+
//# sourceMappingURL=package-manager.js.map
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PnpmCatalogs = exports.DEFAULT_CATALOG = exports.PNPM_WORKSPACE_FILE = void 0;
|
|
4
|
+
exports.isCatalogReference = isCatalogReference;
|
|
5
|
+
exports.writeCatalogUpdates = writeCatalogUpdates;
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const yaml_1 = require("yaml");
|
|
9
|
+
const debug_logger_1 = require("./debug-logger");
|
|
10
|
+
exports.PNPM_WORKSPACE_FILE = 'pnpm-workspace.yaml';
|
|
11
|
+
/** Name pnpm gives the top-level `catalog:` map (also addressable as `catalogs.default`). */
|
|
12
|
+
exports.DEFAULT_CATALOG = 'default';
|
|
13
|
+
/** `catalog:` (default) or `catalog:<name>` — the pnpm catalog protocol. */
|
|
14
|
+
function isCatalogReference(version) {
|
|
15
|
+
return version.startsWith('catalog:');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The catalog definitions of the nearest `pnpm-workspace.yaml`.
|
|
19
|
+
*
|
|
20
|
+
* Catalogs (pnpm ≥ 9.5) hoist shared version ranges out of the member
|
|
21
|
+
* package.json files: a dependency declared as `"react": "catalog:"` gets its
|
|
22
|
+
* actual range from this file's `catalog:` map (or `catalogs.<name>` for
|
|
23
|
+
* `"catalog:<name>"` references). Upgrading such a dependency therefore means
|
|
24
|
+
* editing pnpm-workspace.yaml, not the referencing package.json.
|
|
25
|
+
*/
|
|
26
|
+
class PnpmCatalogs {
|
|
27
|
+
path;
|
|
28
|
+
catalogs;
|
|
29
|
+
constructor(
|
|
30
|
+
/** Absolute path of the pnpm-workspace.yaml the catalogs were read from. */
|
|
31
|
+
path,
|
|
32
|
+
/** catalog name → (package name → range). */
|
|
33
|
+
catalogs) {
|
|
34
|
+
this.path = path;
|
|
35
|
+
this.catalogs = catalogs;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load catalogs from the nearest pnpm-workspace.yaml at or above `startDir`.
|
|
39
|
+
* Returns null when there is no workspace file, it is unreadable, or it
|
|
40
|
+
* defines no catalogs — a broken file must never break a scan.
|
|
41
|
+
*/
|
|
42
|
+
static load(startDir) {
|
|
43
|
+
const path = findPnpmWorkspaceFile(startDir);
|
|
44
|
+
if (!path)
|
|
45
|
+
return null;
|
|
46
|
+
try {
|
|
47
|
+
const raw = (0, yaml_1.parse)((0, node_fs_1.readFileSync)(path, 'utf8'));
|
|
48
|
+
if (!raw || typeof raw !== 'object')
|
|
49
|
+
return null;
|
|
50
|
+
const catalogs = new Map();
|
|
51
|
+
const addCatalog = (name, value) => {
|
|
52
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
53
|
+
return;
|
|
54
|
+
const entries = new Map();
|
|
55
|
+
for (const [pkg, range] of Object.entries(value)) {
|
|
56
|
+
if (typeof range === 'string')
|
|
57
|
+
entries.set(pkg, range);
|
|
58
|
+
}
|
|
59
|
+
if (entries.size > 0)
|
|
60
|
+
catalogs.set(name, entries);
|
|
61
|
+
};
|
|
62
|
+
addCatalog(exports.DEFAULT_CATALOG, raw.catalog);
|
|
63
|
+
if (raw.catalogs && typeof raw.catalogs === 'object' && !Array.isArray(raw.catalogs)) {
|
|
64
|
+
for (const [name, value] of Object.entries(raw.catalogs)) {
|
|
65
|
+
addCatalog(name, value);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (catalogs.size === 0)
|
|
69
|
+
return null;
|
|
70
|
+
return new PnpmCatalogs(path, catalogs);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
debug_logger_1.debugLog.warn('PnpmCatalogs', `failed to parse ${path}: ${error}`);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Resolve a `catalog:` / `catalog:<name>` version specifier for `packageName`
|
|
79
|
+
* to the range declared in the workspace file, or null when the reference
|
|
80
|
+
* points at a catalog (or entry) that does not exist.
|
|
81
|
+
*/
|
|
82
|
+
resolve(packageName, versionSpecifier) {
|
|
83
|
+
if (!isCatalogReference(versionSpecifier))
|
|
84
|
+
return null;
|
|
85
|
+
const name = versionSpecifier.slice('catalog:'.length).trim() || exports.DEFAULT_CATALOG;
|
|
86
|
+
const range = this.catalogs.get(name)?.get(packageName);
|
|
87
|
+
return range === undefined ? null : { catalog: name, range };
|
|
88
|
+
}
|
|
89
|
+
/** Every entry declared in the given catalog (empty for unknown catalogs). */
|
|
90
|
+
entriesOf(catalogName) {
|
|
91
|
+
const entries = this.catalogs.get(catalogName);
|
|
92
|
+
if (!entries)
|
|
93
|
+
return [];
|
|
94
|
+
return Array.from(entries, ([name, range]) => ({ name, range }));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.PnpmCatalogs = PnpmCatalogs;
|
|
98
|
+
/**
|
|
99
|
+
* Write new ranges for catalog entries back into pnpm-workspace.yaml.
|
|
100
|
+
*
|
|
101
|
+
* Edits go through the yaml Document API so comments, key order, and the
|
|
102
|
+
* formatting of untouched nodes survive the round-trip. Entries that no longer
|
|
103
|
+
* exist in the file (edited since the scan) are skipped, never invented.
|
|
104
|
+
*/
|
|
105
|
+
function writeCatalogUpdates(workspaceFilePath, updates) {
|
|
106
|
+
const raw = (0, node_fs_1.readFileSync)(workspaceFilePath, 'utf8');
|
|
107
|
+
const doc = (0, yaml_1.parseDocument)(raw);
|
|
108
|
+
for (const update of updates) {
|
|
109
|
+
// The default catalog may live under `catalog:` or `catalogs.default:`.
|
|
110
|
+
const candidatePaths = update.catalog === exports.DEFAULT_CATALOG
|
|
111
|
+
? [
|
|
112
|
+
['catalog', update.name],
|
|
113
|
+
['catalogs', exports.DEFAULT_CATALOG, update.name],
|
|
114
|
+
]
|
|
115
|
+
: [['catalogs', update.catalog, update.name]];
|
|
116
|
+
const keyPath = candidatePaths.find((path) => doc.hasIn(path));
|
|
117
|
+
if (!keyPath) {
|
|
118
|
+
debug_logger_1.debugLog.warn('PnpmCatalogs', `catalog entry ${update.catalog}:${update.name} no longer exists in ${workspaceFilePath} — skipping`);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
doc.setIn(keyPath, update.range);
|
|
122
|
+
}
|
|
123
|
+
(0, node_fs_1.writeFileSync)(workspaceFilePath, doc.toString());
|
|
124
|
+
}
|
|
125
|
+
/** Nearest pnpm-workspace.yaml at or above `startDir`, or null. */
|
|
126
|
+
function findPnpmWorkspaceFile(startDir) {
|
|
127
|
+
let dir = (0, node_path_1.resolve)(startDir);
|
|
128
|
+
for (;;) {
|
|
129
|
+
const candidate = (0, node_path_1.join)(dir, exports.PNPM_WORKSPACE_FILE);
|
|
130
|
+
if ((0, node_fs_1.existsSync)(candidate))
|
|
131
|
+
return candidate;
|
|
132
|
+
const parent = (0, node_path_1.dirname)(dir);
|
|
133
|
+
if (parent === dir)
|
|
134
|
+
return null;
|
|
135
|
+
dir = parent;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=pnpm-catalogs.js.map
|
|
@@ -2,22 +2,25 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.fetchPackageVersions = fetchPackageVersions;
|
|
4
4
|
exports.clearPackageCache = clearPackageCache;
|
|
5
|
-
const undici_1 = require("undici");
|
|
6
|
-
const node_zlib_1 = require("node:zlib");
|
|
7
5
|
const node_util_1 = require("node:util");
|
|
6
|
+
const node_zlib_1 = require("node:zlib");
|
|
7
|
+
const undici_1 = require("undici");
|
|
8
8
|
const gunzipAsync = (0, node_util_1.promisify)(node_zlib_1.gunzip);
|
|
9
9
|
const inflateAsync = (0, node_util_1.promisify)(node_zlib_1.inflate);
|
|
10
10
|
const brotliDecompressAsync = (0, node_util_1.promisify)(node_zlib_1.brotliDecompress);
|
|
11
11
|
const config_1 = require("../config");
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const inflight_1 = require("
|
|
15
|
-
const resizable_semaphore_1 = require("
|
|
16
|
-
const
|
|
17
|
-
const
|
|
12
|
+
const adaptive_controller_1 = require("../http/adaptive-controller");
|
|
13
|
+
const etag_store_1 = require("../http/etag-store");
|
|
14
|
+
const inflight_1 = require("../http/inflight");
|
|
15
|
+
const resizable_semaphore_1 = require("../http/resizable-semaphore");
|
|
16
|
+
const retry_1 = require("../http/retry");
|
|
17
|
+
const versions_1 = require("../versions");
|
|
18
|
+
const registry_config_1 = require("./registry-config");
|
|
18
19
|
const inFlightLookups = new inflight_1.InflightMap();
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
// One pool per registry origin: scoped packages may resolve to different
|
|
21
|
+
// registries (`@scope:registry` in .npmrc), and each origin keeps its own
|
|
22
|
+
// keep-alive connections. Most runs still touch a single origin.
|
|
23
|
+
//
|
|
21
24
|
// Connection count is kept == the adaptive controller's ceiling (POOL_CONNECTIONS)
|
|
22
25
|
// so the controller is never silently throttled below its chosen limit. Idle
|
|
23
26
|
// keep-alive connections are cheap.
|
|
@@ -28,16 +31,24 @@ const registryPathPrefix = new URL(config_1.NPM_REGISTRY_URL).pathname.replace(/
|
|
|
28
31
|
// controller. With a headers timeout, a stall surfaces as a transient error the
|
|
29
32
|
// controller can react to (and retry handles). `bodyTimeout` stays 0 — large
|
|
30
33
|
// packuments legitimately stream slowly.
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
const poolByOrigin = new Map();
|
|
35
|
+
function poolFor(origin) {
|
|
36
|
+
let pool = poolByOrigin.get(origin);
|
|
37
|
+
if (!pool) {
|
|
38
|
+
pool = new undici_1.Pool(origin, {
|
|
39
|
+
connections: config_1.POOL_CONNECTIONS,
|
|
40
|
+
pipelining: 1,
|
|
41
|
+
keepAliveTimeout: 30_000,
|
|
42
|
+
keepAliveMaxTimeout: 600_000,
|
|
43
|
+
headersTimeout: 30_000,
|
|
44
|
+
bodyTimeout: 0,
|
|
45
|
+
connectTimeout: 15_000,
|
|
46
|
+
allowH2: false,
|
|
47
|
+
});
|
|
48
|
+
poolByOrigin.set(origin, pool);
|
|
49
|
+
}
|
|
50
|
+
return pool;
|
|
51
|
+
}
|
|
41
52
|
const MAX_REGISTRY_ATTEMPTS = 3;
|
|
42
53
|
const RETRY_BACKOFF_MS = [500, 1500, 3000];
|
|
43
54
|
// Fixed concurrency used when adaptive is disabled (INUP_ADAPTIVE=0, the A/B
|
|
@@ -48,27 +59,32 @@ async function getFreshPackageData(packageName, currentVersion, onAttempt) {
|
|
|
48
59
|
const cacheKey = `${packageName}@${currentVersion ?? ''}`;
|
|
49
60
|
return inFlightLookups.dedupe(cacheKey, () => fetchPackageFromRegistry(packageName, onAttempt));
|
|
50
61
|
}
|
|
51
|
-
const encodeRegistryPath = (packageName) => {
|
|
62
|
+
const encodeRegistryPath = (packageName, pathPrefix) => {
|
|
52
63
|
const encodedName = packageName.startsWith('@')
|
|
53
64
|
? `@${encodeURIComponent(packageName.slice(1).split('/')[0])}/${encodeURIComponent(packageName.slice(packageName.indexOf('/') + 1))}`
|
|
54
65
|
: encodeURIComponent(packageName);
|
|
55
|
-
return `${
|
|
66
|
+
return `${pathPrefix}/${encodedName}`;
|
|
56
67
|
};
|
|
57
|
-
async function attemptRegistryFetch(path) {
|
|
68
|
+
async function attemptRegistryFetch(target, path) {
|
|
58
69
|
const startedAt = Date.now();
|
|
59
70
|
// Conditional request: if we have a stored ETag for this packument, ask the
|
|
60
71
|
// registry to validate it. Unchanged → 304 (no body) and we reuse stored data.
|
|
61
72
|
// This still hits the registry every run, so data is never served stale.
|
|
62
|
-
|
|
73
|
+
// Keys are origin-qualified so two registries can never collide on a path.
|
|
74
|
+
const cacheKey = `${target.origin}${path}`;
|
|
75
|
+
const cached = (0, etag_store_1.readEtag)(cacheKey);
|
|
63
76
|
try {
|
|
64
77
|
const requestHeaders = {
|
|
65
78
|
accept: 'application/vnd.npm.install-v1+json',
|
|
66
79
|
'accept-encoding': 'gzip, deflate, br',
|
|
67
80
|
};
|
|
81
|
+
if (target.authHeader) {
|
|
82
|
+
requestHeaders.authorization = target.authHeader;
|
|
83
|
+
}
|
|
68
84
|
if (cached) {
|
|
69
85
|
requestHeaders['if-none-match'] = cached.etag;
|
|
70
86
|
}
|
|
71
|
-
const { statusCode, headers, body } = await
|
|
87
|
+
const { statusCode, headers, body } = await poolFor(target.origin).request({
|
|
72
88
|
path,
|
|
73
89
|
method: 'GET',
|
|
74
90
|
headers: requestHeaders,
|
|
@@ -112,12 +128,12 @@ async function attemptRegistryFetch(path) {
|
|
|
112
128
|
else {
|
|
113
129
|
decoded = raw;
|
|
114
130
|
}
|
|
115
|
-
const data = (0,
|
|
131
|
+
const data = (0, versions_1.parseVersions)(decoded.toString('utf8'));
|
|
116
132
|
// Persist the ETag for next run's conditional request.
|
|
117
|
-
const etagHeader = headers
|
|
133
|
+
const etagHeader = headers.etag;
|
|
118
134
|
const etag = Array.isArray(etagHeader) ? etagHeader[0] : etagHeader;
|
|
119
135
|
if (etag) {
|
|
120
|
-
(0, etag_store_1.writeEtag)(
|
|
136
|
+
(0, etag_store_1.writeEtag)(cacheKey, etag.toString(), data);
|
|
121
137
|
}
|
|
122
138
|
return {
|
|
123
139
|
kind: 'success',
|
|
@@ -134,10 +150,10 @@ async function attemptRegistryFetch(path) {
|
|
|
134
150
|
return { kind: 'transient' };
|
|
135
151
|
}
|
|
136
152
|
}
|
|
137
|
-
async function fetchFromRegistryWithRetries(path, onAttempt) {
|
|
153
|
+
async function fetchFromRegistryWithRetries(target, path, onAttempt) {
|
|
138
154
|
let lastOutcome = { kind: 'transient' };
|
|
139
155
|
for (let attempt = 0; attempt < MAX_REGISTRY_ATTEMPTS; attempt++) {
|
|
140
|
-
const outcome = await attemptRegistryFetch(path);
|
|
156
|
+
const outcome = await attemptRegistryFetch(target, path);
|
|
141
157
|
onAttempt?.(outcome);
|
|
142
158
|
if (outcome.kind === 'success' || outcome.kind === 'not-found') {
|
|
143
159
|
return outcome;
|
|
@@ -154,8 +170,11 @@ async function fetchFromRegistryWithRetries(path, onAttempt) {
|
|
|
154
170
|
return lastOutcome;
|
|
155
171
|
}
|
|
156
172
|
async function fetchPackageFromRegistry(packageName, onAttempt) {
|
|
157
|
-
|
|
158
|
-
|
|
173
|
+
// Scoped packages may live on a different registry (with credentials) than
|
|
174
|
+
// unscoped ones — resolved from the npm config chain, memoized per scope.
|
|
175
|
+
const target = (0, registry_config_1.registryTargetFor)(packageName);
|
|
176
|
+
const path = encodeRegistryPath(packageName, target.pathPrefix);
|
|
177
|
+
const outcome = await fetchFromRegistryWithRetries(target, path, onAttempt);
|
|
159
178
|
if (outcome.kind === 'success') {
|
|
160
179
|
return outcome.data;
|
|
161
180
|
}
|
|
@@ -213,8 +232,10 @@ async function fetchPackageVersions(packageNames, options = {}) {
|
|
|
213
232
|
const pendingEmissions = new Map();
|
|
214
233
|
let nextEmitIndex = 0;
|
|
215
234
|
const flushPending = () => {
|
|
216
|
-
while (
|
|
235
|
+
while (true) {
|
|
217
236
|
const ready = pendingEmissions.get(nextEmitIndex);
|
|
237
|
+
if (!ready)
|
|
238
|
+
break;
|
|
218
239
|
pendingEmissions.delete(nextEmitIndex);
|
|
219
240
|
options.onBatchReady?.(ready);
|
|
220
241
|
nextEmitIndex++;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.scopeOfPackage = scopeOfPackage;
|
|
7
|
+
exports.registryTargetFor = registryTargetFor;
|
|
8
|
+
exports.clearRegistryTargetCache = clearRegistryTargetCache;
|
|
9
|
+
const registry_auth_token_1 = __importDefault(require("registry-auth-token"));
|
|
10
|
+
const registry_url_1 = __importDefault(require("registry-auth-token/registry-url"));
|
|
11
|
+
const config_1 = require("../config");
|
|
12
|
+
// The submodule's .d.ts declares the second parameter as `{ npmrc?: … }`, but the
|
|
13
|
+
// implementation consumes the plain npmrc key/value record directly.
|
|
14
|
+
const getRegistryUrl = registry_url_1.default;
|
|
15
|
+
/** Scope of a package name (`@scope/pkg` → `@scope`), or undefined when unscoped. */
|
|
16
|
+
function scopeOfPackage(packageName) {
|
|
17
|
+
if (!packageName.startsWith('@'))
|
|
18
|
+
return undefined;
|
|
19
|
+
const slash = packageName.indexOf('/');
|
|
20
|
+
return slash > 0 ? packageName.slice(0, slash) : undefined;
|
|
21
|
+
}
|
|
22
|
+
// Config files are re-read on every resolution inside registry-auth-token, so
|
|
23
|
+
// memoize per scope: one resolution per distinct scope per process.
|
|
24
|
+
const targetByScope = new Map();
|
|
25
|
+
function registryTargetFor(packageName, npmrc) {
|
|
26
|
+
const scope = scopeOfPackage(packageName) ?? '';
|
|
27
|
+
if (!npmrc) {
|
|
28
|
+
const cached = targetByScope.get(scope);
|
|
29
|
+
if (cached)
|
|
30
|
+
return cached;
|
|
31
|
+
}
|
|
32
|
+
const target = resolveTarget(scope || undefined, npmrc);
|
|
33
|
+
if (!npmrc) {
|
|
34
|
+
targetByScope.set(scope, target);
|
|
35
|
+
}
|
|
36
|
+
return target;
|
|
37
|
+
}
|
|
38
|
+
function resolveTarget(scope, npmrc) {
|
|
39
|
+
let registryHref = config_1.NPM_REGISTRY_URL;
|
|
40
|
+
try {
|
|
41
|
+
registryHref = getRegistryUrl(scope, npmrc);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Unreadable npm config must never break a run — fall back to the public registry.
|
|
45
|
+
}
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = new URL(registryHref);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
parsed = new URL(config_1.NPM_REGISTRY_URL);
|
|
52
|
+
}
|
|
53
|
+
let authHeader;
|
|
54
|
+
try {
|
|
55
|
+
const auth = (0, registry_auth_token_1.default)(parsed.href, { recursive: true, ...(npmrc ? { npmrc } : {}) });
|
|
56
|
+
// registry-auth-token stringifies an unset ${ENV_VAR} reference into the
|
|
57
|
+
// literal "undefined". Sending `Bearer undefined` guarantees a 401 where an
|
|
58
|
+
// anonymous request might succeed, so treat it as no credentials.
|
|
59
|
+
if (auth?.token && auth.token !== 'undefined') {
|
|
60
|
+
authHeader = `${auth.type} ${auth.token}`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// No credentials (or unreadable config) → anonymous requests, matching npm.
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
origin: parsed.origin,
|
|
68
|
+
pathPrefix: parsed.pathname.replace(/\/$/, ''),
|
|
69
|
+
authHeader,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** Test helper: forget memoized resolutions so npmrc overrides take effect. */
|
|
73
|
+
function clearRegistryTargetCache() {
|
|
74
|
+
targetByScope.clear();
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=registry-config.js.map
|
|
@@ -35,21 +35,32 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.checkForUpdate = checkForUpdate;
|
|
37
37
|
exports.checkForUpdateAsync = checkForUpdateAsync;
|
|
38
|
-
const child_process_1 = require("child_process");
|
|
39
38
|
const semver = __importStar(require("semver"));
|
|
40
39
|
const config_1 = require("../config");
|
|
40
|
+
/** Cap for the self-update check; it must never hold up an interactive session. */
|
|
41
|
+
const UPDATE_CHECK_TIMEOUT_MS = 5000;
|
|
41
42
|
/**
|
|
42
|
-
* Check if the current package version is outdated compared to npm registry
|
|
43
|
+
* Check if the current package version is outdated compared to npm registry.
|
|
44
|
+
*
|
|
45
|
+
* One small registry request (`{registry}/{name}/latest`) instead of spawning
|
|
46
|
+
* the npm CLI — `npm view` paid a few hundred ms of process startup for the
|
|
47
|
+
* same answer.
|
|
43
48
|
*/
|
|
44
49
|
async function checkForUpdate(packageName, currentVersion) {
|
|
45
50
|
try {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
51
|
+
const response = await fetch(`${config_1.NPM_REGISTRY_URL}/${encodeURIComponent(packageName)}/latest`, {
|
|
52
|
+
method: 'GET',
|
|
53
|
+
headers: { accept: 'application/json' },
|
|
54
|
+
signal: AbortSignal.timeout(UPDATE_CHECK_TIMEOUT_MS),
|
|
51
55
|
});
|
|
52
|
-
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const manifest = (await response.json());
|
|
60
|
+
const latestVersion = typeof manifest.version === 'string' ? manifest.version.trim() : '';
|
|
61
|
+
if (!semver.valid(latestVersion)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
53
64
|
// Compare versions
|
|
54
65
|
const isOutdated = semver.lt(currentVersion, latestVersion);
|
|
55
66
|
// Determine update command based on how the tool was likely invoked
|
|
@@ -63,30 +74,18 @@ async function checkForUpdate(packageName, currentVersion) {
|
|
|
63
74
|
updateCommand,
|
|
64
75
|
};
|
|
65
76
|
}
|
|
66
|
-
catch
|
|
77
|
+
catch {
|
|
67
78
|
// Silently fail - don't interrupt the user experience
|
|
68
79
|
return null;
|
|
69
80
|
}
|
|
70
81
|
}
|
|
71
82
|
/**
|
|
72
|
-
* Check for updates in the background without blocking
|
|
73
|
-
*
|
|
83
|
+
* Check for updates in the background without blocking.
|
|
84
|
+
*
|
|
85
|
+
* `checkForUpdate` self-caps at UPDATE_CHECK_TIMEOUT_MS and never rejects
|
|
86
|
+
* (every failure resolves to null), so no extra timeout race is needed here.
|
|
74
87
|
*/
|
|
75
88
|
function checkForUpdateAsync(packageName, currentVersion) {
|
|
76
|
-
return
|
|
77
|
-
// Set a timeout to prevent hanging
|
|
78
|
-
const timeout = setTimeout(() => {
|
|
79
|
-
resolve(null);
|
|
80
|
-
}, 5000);
|
|
81
|
-
checkForUpdate(packageName, currentVersion)
|
|
82
|
-
.then((result) => {
|
|
83
|
-
clearTimeout(timeout);
|
|
84
|
-
resolve(result);
|
|
85
|
-
})
|
|
86
|
-
.catch(() => {
|
|
87
|
-
clearTimeout(timeout);
|
|
88
|
-
resolve(null);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
89
|
+
return checkForUpdate(packageName, currentVersion);
|
|
91
90
|
}
|
|
92
91
|
//# sourceMappingURL=version-checker.js.map
|
|
@@ -82,7 +82,7 @@ exports.ConsoleUtils = {
|
|
|
82
82
|
clearProgress() {
|
|
83
83
|
if (!process.stderr.isTTY)
|
|
84
84
|
return;
|
|
85
|
-
process.stderr.write(
|
|
85
|
+
process.stderr.write(`\r${' '.repeat(exports.ConsoleUtils.LINE_WIDTH)}\r`);
|
|
86
86
|
},
|
|
87
87
|
};
|
|
88
88
|
//# sourceMappingURL=cursor.js.map
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.wrapPlainText = exports.truncatePlainText = exports.stripAnsi = exports.getVisualLength = exports.TerminalInput = exports.RAW_SHOW_CURSOR = exports.RAW_EXIT_ALT_SCREEN = exports.
|
|
4
|
-
var
|
|
5
|
-
Object.defineProperty(exports, "
|
|
6
|
-
Object.defineProperty(exports, "
|
|
7
|
-
Object.defineProperty(exports, "truncateMiddle", { enumerable: true, get: function () { return version_1.truncateMiddle; } });
|
|
8
|
-
Object.defineProperty(exports, "formatVersionDiff", { enumerable: true, get: function () { return version_1.formatVersionDiff; } });
|
|
3
|
+
exports.wrapPlainText = exports.truncatePlainText = exports.stripAnsi = exports.getVisualLength = exports.TerminalInput = exports.RAW_SHOW_CURSOR = exports.RAW_EXIT_ALT_SCREEN = exports.CursorUtils = exports.ConsoleUtils = exports.shouldDisableColor = exports.applyColorSetting = void 0;
|
|
4
|
+
var color_1 = require("./color");
|
|
5
|
+
Object.defineProperty(exports, "applyColorSetting", { enumerable: true, get: function () { return color_1.applyColorSetting; } });
|
|
6
|
+
Object.defineProperty(exports, "shouldDisableColor", { enumerable: true, get: function () { return color_1.shouldDisableColor; } });
|
|
9
7
|
var cursor_1 = require("./cursor");
|
|
10
|
-
Object.defineProperty(exports, "CursorUtils", { enumerable: true, get: function () { return cursor_1.CursorUtils; } });
|
|
11
8
|
Object.defineProperty(exports, "ConsoleUtils", { enumerable: true, get: function () { return cursor_1.ConsoleUtils; } });
|
|
9
|
+
Object.defineProperty(exports, "CursorUtils", { enumerable: true, get: function () { return cursor_1.CursorUtils; } });
|
|
12
10
|
Object.defineProperty(exports, "RAW_EXIT_ALT_SCREEN", { enumerable: true, get: function () { return cursor_1.RAW_EXIT_ALT_SCREEN; } });
|
|
13
11
|
Object.defineProperty(exports, "RAW_SHOW_CURSOR", { enumerable: true, get: function () { return cursor_1.RAW_SHOW_CURSOR; } });
|
|
14
12
|
var terminal_input_1 = require("./terminal-input");
|
|
@@ -85,7 +85,12 @@ exports.TerminalInput = {
|
|
|
85
85
|
promptForImmediateConfirmation(prompt, defaultValue = true) {
|
|
86
86
|
return new Promise((resolve) => {
|
|
87
87
|
process.stdout.write(prompt);
|
|
88
|
+
// Replaced synchronously below before any keypress can call finish(),
|
|
89
|
+
// so the placeholder body is unreachable. (The `v8 ignore next` form
|
|
90
|
+
// is not honored by coverage-v8 here; the block form is.)
|
|
91
|
+
/* v8 ignore start */
|
|
88
92
|
let cleanup = () => { };
|
|
93
|
+
/* v8 ignore stop */
|
|
89
94
|
const finish = (value) => {
|
|
90
95
|
cleanup();
|
|
91
96
|
process.stdout.write('\n');
|