dep-up-surgeon 2.2.0 → 2.2.2
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 +107 -22
- package/dist/cli/doctor.d.ts +52 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +616 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/doctorCommand.d.ts +2 -0
- package/dist/cli/doctorCommand.d.ts.map +1 -0
- package/dist/cli/doctorCommand.js +71 -0
- package/dist/cli/doctorCommand.js.map +1 -0
- package/dist/cli/doctorRenderer.d.ts +21 -0
- package/dist/cli/doctorRenderer.d.ts.map +1 -0
- package/dist/cli/doctorRenderer.js +79 -0
- package/dist/cli/doctorRenderer.js.map +1 -0
- package/dist/cli/git.d.ts +11 -0
- package/dist/cli/git.d.ts.map +1 -1
- package/dist/cli/git.js +45 -9
- package/dist/cli/git.js.map +1 -1
- package/dist/cli/gitFlow.d.ts.map +1 -1
- package/dist/cli/gitFlow.js +7 -0
- package/dist/cli/gitFlow.js.map +1 -1
- package/dist/cli/lockfileFix.d.ts +91 -0
- package/dist/cli/lockfileFix.d.ts.map +1 -0
- package/dist/cli/lockfileFix.js +509 -0
- package/dist/cli/lockfileFix.js.map +1 -0
- package/dist/cli/report.d.ts +4 -0
- package/dist/cli/report.d.ts.map +1 -1
- package/dist/cli/report.js +5 -0
- package/dist/cli/report.js.map +1 -1
- package/dist/cli/summary.d.ts.map +1 -1
- package/dist/cli/summary.js +221 -2
- package/dist/cli/summary.js.map +1 -1
- package/dist/cli.js +80 -1
- package/dist/cli.js.map +1 -1
- package/dist/core/peerResolver.d.ts +82 -0
- package/dist/core/peerResolver.d.ts.map +1 -0
- package/dist/core/peerResolver.js +213 -0
- package/dist/core/peerResolver.js.map +1 -0
- package/dist/core/upgrader.d.ts +30 -6
- package/dist/core/upgrader.d.ts.map +1 -1
- package/dist/core/upgrader.js +195 -14
- package/dist/core/upgrader.js.map +1 -1
- package/dist/core/workspaces.d.ts +22 -0
- package/dist/core/workspaces.d.ts.map +1 -1
- package/dist/core/workspaces.js +52 -0
- package/dist/core/workspaces.js.map +1 -1
- package/dist/types.d.ts +92 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/concurrency.d.ts +55 -0
- package/dist/utils/concurrency.d.ts.map +1 -1
- package/dist/utils/concurrency.js +46 -1
- package/dist/utils/concurrency.js.map +1 -1
- package/dist/utils/npm.d.ts +14 -0
- package/dist/utils/npm.d.ts.map +1 -1
- package/dist/utils/npm.js +48 -0
- package/dist/utils/npm.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -19,27 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
**Website:** [https://dep-up-surgeon.netlify.app/](https://dep-up-surgeon.netlify.app/)
|
|
21
21
|
|
|
22
|
-
**Quick links:** [Website](https://dep-up-surgeon.netlify.app/) · [npm package](https://www.npmjs.com/package/dep-up-surgeon) · [GitHub repository](https://github.com/alexlibe95/dep-up-surgeon) · [Issues](https://github.com/alexlibe95/dep-up-surgeon/issues) · [Pull requests](https://github.com/alexlibe95/dep-up-surgeon/pulls) · [Socket (supply chain & maintenance)](https://socket.dev/npm/package/dep-up-surgeon) · [deps.dev (Open Source Insights)](https://deps.dev/npm/dep-up-surgeon) · [Libraries.io](https://libraries.io/npm/dep-up-surgeon) · [npms score](https://npms.io/search?q=dep-up-surgeon) · [Bundlephobia](https://bundlephobia.com/package/dep-up-surgeon) · [OpenSSF Scorecard (repo)](https://scorecard.dev/viewer/?uri=github.com/alexlibe95/dep-up-surgeon)
|
|
23
|
-
|
|
24
22
|
Production-oriented CLI that upgrades **npm** dependencies with **`npm install` + validation** after each change, and **rolls back** on failure. It is **framework-agnostic**: grouping and conflict handling come from **registry metadata** and **parsed npm output**, not hardcoded stacks (React, Angular, etc.).
|
|
25
23
|
|
|
26
|
-
### Package listings and security tools
|
|
27
|
-
|
|
28
|
-
| Where | What you get |
|
|
29
|
-
|--------|----------------|
|
|
30
|
-
| **[Website](https://dep-up-surgeon.netlify.app/)** | Project landing page: overview, live demo/session log, feature highlights, install commands, and flag reference. |
|
|
31
|
-
| **[npm](https://www.npmjs.com/package/dep-up-surgeon)** | Current version, **readme**, **dependencies**, dist tags, publish time, tarball **integrity** (`sha512`), download counts, maintainers, and npm’s own **Security** / advisory context for the ecosystem. |
|
|
32
|
-
| **[GitHub](https://github.com/alexlibe95/dep-up-surgeon)** | **Stars**, **forks**, **issues**, **pull requests**, **commits**, **contributors**, source tree, and (if enabled) **Dependabot** / **Security** advisories for the repo. |
|
|
33
|
-
| **[Socket](https://socket.dev/npm/package/dep-up-surgeon)** | Supply-chain style view: **maintenance**, **license**, **dependencies**, and related signals npm users often open in dedicated security UIs. |
|
|
34
|
-
| **[deps.dev](https://deps.dev/npm/dep-up-surgeon)** | Google **Open Source Insights**: dependency graph, versions, licenses, and cross-ecosystem metadata. |
|
|
35
|
-
| **[Libraries.io](https://libraries.io/npm/dep-up-surgeon)** | Release history, **reverse dependencies** (who depends on this package), and ecosystem metadata. |
|
|
36
|
-
| **[npms](https://npms.io/search?q=dep-up-surgeon)** | Search **quality score** (maintenance, popularity, dependencies) used by many npm search front-ends. |
|
|
37
|
-
| **[OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/alexlibe95/dep-up-surgeon)** | Automated **security health** checks for the GitHub repository (when the project is indexed). |
|
|
38
|
-
|
|
39
|
-
**Note:** Badges above pull live data from **npm**, **GitHub**, and **Libraries.io**; numbers change as the package and repo evolve. For **your** app’s risk after installing any tool, always run **`npm audit`** (and your own policy) in the project directory.
|
|
40
|
-
|
|
41
|
-
With **`--link-groups auto`** (default) it **clusters** upgrades using a **dependency graph** built from the npm registry (see below) plus optional **`.dep-up-surgeonrc`** `linkedGroups`. Use **`--link-groups none`** for strict one-package-at-a-time behavior.
|
|
42
|
-
|
|
43
24
|
## Install
|
|
44
25
|
|
|
45
26
|
```bash
|
|
@@ -81,7 +62,8 @@ dep-up-surgeon [options]
|
|
|
81
62
|
| `--workspaces-only` | Like `--workspaces` but **skips** the root `package.json`. Only workspace members are traversed. |
|
|
82
63
|
| `--workspace <names>` | Comma-separated workspace member **names** (the `name` field from each child `package.json`) to traverse. Pass `root` to also include the root. Example: `--workspace "@org/core,@org/web,root"`. Unknown names produce a friendly error listing the known members. |
|
|
83
64
|
| `--install-mode <mode>` | Workspace install strategy. **`root`** (default) always runs `<mgr> install` from the workspace root after every mutation — the safest option, supported by every package manager. **`filtered`** rewrites per-child installs to their workspace-scoped form: **npm 7+** uses `npm install --workspace <name>`, **pnpm** uses `pnpm install --filter <name>`, **yarn berry (v2+) with `@yarnpkg/plugin-workspace-tools`** uses `yarn workspaces focus <name>`, and **yarn classic / berry without the plugin** falls back to a full root install with a one-time warning explaining the upgrade path. The capability is auto-detected at startup (yarn version + plugin probe) and reported as `project.yarnMajorVersion` + `project.yarnSupportsFocus` in `--json`. Only meaningful with `--workspaces` / `--workspaces-only` / `--workspace <names>`. |
|
|
84
|
-
| `--concurrency <n>` | Maximum number of workspace targets to traverse in parallel (1–16; default `1`). Higher values overlap registry **scan + plan** phases across targets while a shared mutex keeps **install + validation strictly serialized** — the workspace lockfile is shared, so concurrent installs would corrupt it. The default in-process registry cache also deduplicates `pacote.manifest` / `pacote.packument` calls across targets, so even at concurrency `1` you get a speedup when the same dep appears in many workspaces. **Requires `--json`** so per-target log lines don't interleave; non-JSON mode silently downgrades to `1` with a warning. |
|
|
65
|
+
| `--concurrency <n>` | Maximum number of workspace targets to traverse in parallel (1–16; default `1`). Higher values overlap registry **scan + plan** phases across targets while a shared mutex keeps **install + validation strictly serialized** — the workspace lockfile is shared, so concurrent installs would corrupt it. The default in-process registry cache also deduplicates `pacote.manifest` / `pacote.packument` calls across targets, so even at concurrency `1` you get a speedup when the same dep appears in many workspaces. **Requires `--json`** so per-target log lines don't interleave; non-JSON mode silently downgrades to `1` with a warning. In an **isolated-lockfile** monorepo (pnpm `shared-workspace-lockfile=false`, or every workspace member shipping its own lockfile) installs + validation are ALSO run in parallel — see **Parallel installs** below. |
|
|
66
|
+
| `--no-parallel-installs` | Force installs + validation to stay serialized even when an isolated-lockfile monorepo is detected. Useful when debugging a flaky install step (parallel installs mask the ordering) or when a per-workspace postinstall script touches shared state outside its workspace. |
|
|
85
67
|
| `--retry-failed` | Read `.dep-up-surgeon.last-run.json` from the previous run and only re-attempt entries that failed for **non-terminal** reasons (`install`, `validation-conflicts`, `versions`, `unknown`). Successful upgrades + terminal failures (`peer`, `validation-script`) from the last run are added to the ignore list automatically. See **Persisted last-run report** below. |
|
|
86
68
|
| `--no-persist-report` | Do **not** write `.dep-up-surgeon.last-run.json` after the run. By default the structured report is written next to the workspace root for `--retry-failed` and CI consumers. |
|
|
87
69
|
| `--summary <format>` | Write a human-friendly summary of the run as `md` (default) or `html`. Destination is `$GITHUB_STEP_SUMMARY` if set (appended), otherwise `--summary-file <path>`, otherwise `./dep-up-surgeon-summary.<ext>`. |
|
|
@@ -97,8 +79,10 @@ dep-up-surgeon [options]
|
|
|
97
79
|
| `--security-only` | Run `npm audit` (or `pnpm`/`yarn` equivalent) first, then upgrade **only** the packages with open advisories. Every successful bump carries the advisory severity + ID into its commit subject (`[security:high]`) and into the summary's **Security fixes** table. Pairs well with `--git-commit-mode per-success` to produce one PR per CVE. See **Security-first mode** below. |
|
|
98
80
|
| `--min-severity <level>` | Minimum advisory severity to consider under `--security-only`: `low` (default), `moderate`, `high`, or `critical`. Lower-severity advisories are filtered out before the upgrade plan is built. |
|
|
99
81
|
| `--blast-radius` / `--no-blast-radius` | Scan project source files to list which files actually `import`/`require` each upgraded package, and surface the list in `--json` + `--summary`. **Default ON** when `--summary` is active. See **Blast radius** below. |
|
|
82
|
+
| `--resolve-peers` / `--no-resolve-peers` | When a linked-group bump (e.g. `react` + `react-dom` + `@types/react`) fails with a peer-dependency conflict, compute the intersection of peer ranges across the registry packument and retry with a satisfiable version tuple (members may land below `latest`). **Default ON**. See **Peer-range intersection resolver** below. |
|
|
100
83
|
| `--apply-overrides` | After the main upgrade loop, fix **transitive** CVEs that no direct bump could reach by writing a package-manager override (`overrides` for npm, `pnpm.overrides` for pnpm, `resolutions` for yarn) pinning each vulnerable transitive to its audit-recommended safe version. Runs install + validator after each pin and rolls back automatically when the validator fails. Requires `--security-only`. See **Transitive overrides** below. |
|
|
101
84
|
| `--override-force` | Used with `--apply-overrides`. Overwrite an **existing** override entry whose value conflicts with the audit-recommended version. By default we refuse to clobber user-managed pins and record `conflict` in the report. |
|
|
85
|
+
| `--fix-lockfile` | After the main upgrade loop, run the package manager's native dedupe command (`npm dedupe` / `pnpm dedupe` / `yarn dedupe`) to collapse redundant transitive copies **without touching `package.json`**, and flag transitives more than a minor or a full major behind registry `latest`. Lockfile is backed up before dedupe and restored if dedupe OR the post-dedupe validator fails. Yarn classic (v1) has no dedupe subcommand — recorded as `skipped: "unsupported"`. See **Lockfile fix** below. |
|
|
102
86
|
| `--open-pr` | After `--git-commit --git-branch` pushes the branch, open a GitHub PR with the `--summary` markdown as the body (falls back to a deterministic minimal body). Uses the `gh` CLI (must be installed + authenticated); never fatal — a missing binary, auth failure, or push rejection is recorded as `pullRequest.error` in the JSON report without aborting the run. See **Auto-opening a PR** below. |
|
|
103
87
|
| `--open-pr-title <title>` | Override the PR title. Default: derived from the upgrade counts, e.g. `deps: [breaking+security] bump 3 packages`. |
|
|
104
88
|
| `--open-pr-draft` | Open the PR as a draft. Recommended with `--force` or on Fridays so merge-queue bots don't auto-land it. |
|
|
@@ -150,6 +134,8 @@ Pass `--summary md` (or `--summary html`) to render a human-friendly report alon
|
|
|
150
134
|
|
|
151
135
|
The summary contains: counts (upgraded / failed / skipped), detected project info, target list, an **Upgraded** table (`Package | Workspace | From | To | Notes`), a **Failed** table (`Package | Workspace | Reason | Attempted | Detail`), pre-flight status when it aborted, and the ignored list. HTML output escapes all dynamic content. Designed to be ~40 lines of code on the producer side and easy to embed in PR comments / dashboards.
|
|
152
136
|
|
|
137
|
+
The **HTML output** is self-contained and styled: an inline `<style>` block scoped to `.dep-up-surgeon-report` gives severity chips (critical / high / moderate / low), breaking / peer-resolved / fallback / forced badges on the **Upgraded** table, clickable advisory IDs linking to `github.com/advisories/...`, and responsive tables. No external CSS or web-font requests — the file opens cleanly in any browser, and when the style tag is stripped (GitHub step-summary sanitizer) the tables still render as plain HTML. Release notes and **Blast radius** per-package blocks are folded inside `<details>` elements so the summary stays scannable.
|
|
138
|
+
|
|
153
139
|
### CI / bot mode (`--ci`)
|
|
154
140
|
|
|
155
141
|
`--ci` is a convenience flag for unattended runs (GitHub Actions, GitLab CI, Renovate-style bots). It:
|
|
@@ -300,6 +286,34 @@ Whenever a changelog excerpt is fetched, `dep-up-surgeon` scans it for breaking-
|
|
|
300
286
|
- **`--json`** → `upgraded[].changelog.breaking = { hasBreaking, matchedLines[], reasons[] }` (only present when the scan matched).
|
|
301
287
|
- **Never fatal, never noisy**: absence of a changelog means no scan, which means no flag. The scan caps matches at 10 per package and dedupes identical lines so verbose changelogs don't drown out the signal.
|
|
302
288
|
|
|
289
|
+
### Peer-range intersection resolver
|
|
290
|
+
|
|
291
|
+
Linked-group bumps (e.g. `react` + `react-dom` + `@types/react`, or the Jest / Testing-Library / Vitest families) frequently fail because one member's **latest** demands a peer version another member can't yet satisfy. Without help, the whole batch rolls back and the user has to figure out the right tuple by hand.
|
|
292
|
+
|
|
293
|
+
`--resolve-peers` (default ON) turns this into an automated constraint-satisfaction problem:
|
|
294
|
+
|
|
295
|
+
1. The first batch attempt runs exactly like today — every linked member goes to its registry `latest`.
|
|
296
|
+
2. If the install fails with a **peer** conflict, the resolver fetches each linked package's full registry packument (cached — one call per package per run) and reads every published version's `peerDependencies` block.
|
|
297
|
+
3. Each member gets a candidate domain: every version between `currentRange`'s `minVersion` and the originally-requested target, sorted newest-first, minus deprecated / pre-release versions.
|
|
298
|
+
4. A **newest-first backtracking search** enumerates version tuples (variable = one package, domain = its candidate versions). For each partial assignment, every peer constraint that has become knowable is checked; peers on packages inside the linked group are checked against the chosen version, peers on packages OUTSIDE the group are checked against that package's range in the current `package.json` (via `semver.minVersion`).
|
|
299
|
+
5. The **first** complete tuple to satisfy every constraint is also the least-downgrade one. The engine rewrites the batch's target versions and retries the install + validator. On success, every affected row is tagged with `resolvedPeer = { originalTarget, reason, tuplesExplored }`.
|
|
300
|
+
6. If the resolver can't find a satisfiable tuple, or the retried install still fails, the batch falls back to the pre-resolver behavior (rollback + `kind: 'peer'` failure row).
|
|
301
|
+
|
|
302
|
+
Guard rails that keep it safe:
|
|
303
|
+
|
|
304
|
+
- **Bounded search** — capped at 400 tuples explored per batch. Past that the resolver gives up silently instead of hanging the run on pathological inputs.
|
|
305
|
+
- **Optional peers** (`peerDependenciesMeta[name].optional === true`) are ignored. An unsatisfied optional peer isn't a hard conflict.
|
|
306
|
+
- **Deprecated versions** never appear in the domain. We'd rather fail to find a solution than auto-suggest a known-bad version.
|
|
307
|
+
- **`--force` bypasses the resolver** — the user has explicitly opted into barreling through peer conflicts.
|
|
308
|
+
- **`--no-resolve-peers`** keeps the old behavior when you WANT peer failures to surface so a human resolves them instead of the tool silently nudging versions off latest.
|
|
309
|
+
|
|
310
|
+
Where it shows up:
|
|
311
|
+
|
|
312
|
+
- **Console output**: `upgraded: react-dom → 18.3.1 (group react-pair) [peer-resolved from 19.0.0]`.
|
|
313
|
+
- **`--summary md|html`**: a dedicated **Peer-range resolutions** table (package / group / requested / installed / tuples explored) above the upgraded table, plus a `peer-resolved from <v>` badge in the upgraded row's Notes column.
|
|
314
|
+
- **Commit subjects**: `[peer-resolved]` tag sits between `[breaking]` and `[security:<sev>]` (stable order). The body gets a `Peer-range resolutions (kept linked group satisfiable):` footer listing each pinned member.
|
|
315
|
+
- **`--json`**: `upgraded[].resolvedPeer = { originalTarget, reason, tuplesExplored }` plus `upgraded[].requestedLatest` still reflects the pre-resolver target so downstream tools can diff them.
|
|
316
|
+
|
|
303
317
|
### Transitive overrides (`--apply-overrides`)
|
|
304
318
|
|
|
305
319
|
`--security-only` by itself can only fix vulnerabilities reachable from a direct dependency. For CVEs that live in transitives (very common — `lodash@4.17.20` buried six levels deep under a toolchain package), pair `--security-only` with `--apply-overrides` and the tool will write a package-manager override to pin the vulnerable transitive to its safe version.
|
|
@@ -322,6 +336,77 @@ npx dep-up-surgeon --workspaces \
|
|
|
322
336
|
--open-pr --open-pr-draft
|
|
323
337
|
```
|
|
324
338
|
|
|
339
|
+
### Lockfile fix (`--fix-lockfile`)
|
|
340
|
+
|
|
341
|
+
`--fix-lockfile` improves the **lockfile's** dependency graph without touching `package.json`. It's the counterpart to `--apply-overrides`: where overrides fix vulnerable transitives, `--fix-lockfile` collapses redundant ones and surfaces the stale-but-not-vulnerable tail that a direct upgrade loop can never reach.
|
|
342
|
+
|
|
343
|
+
What it does:
|
|
344
|
+
|
|
345
|
+
- **Dedupe**: runs the package manager's native dedupe command — `npm dedupe --no-audit` / `pnpm dedupe` / `yarn dedupe` (berry only). These commands collapse multiple copies of the same package when semver ranges allow it.
|
|
346
|
+
- **Stale-transitive scan**: for the top 250 packages in the lockfile (by installed-copy count), cross-references registry `latest` and flags any package whose highest installed version is more than one minor OR a full major behind. Trivial drift (a patch or a single minor) is filtered out — only the drifted-by-6-months cases show up.
|
|
347
|
+
- **Backup + rollback**: lockfile is snapshotted before dedupe. If dedupe exits non-zero OR the post-dedupe validator fails, the snapshot is restored and the manager is re-run to reconcile `node_modules`. The worst case is the same tree you started with.
|
|
348
|
+
|
|
349
|
+
Guard rails:
|
|
350
|
+
|
|
351
|
+
- **Yarn classic (v1)** has no `dedupe` subcommand — recorded as `skipped: "unsupported"` and the rest of the run continues normally.
|
|
352
|
+
- **No lockfile on disk** → `skipped: "no-lockfile"`. Nothing to dedupe when the install has never been run.
|
|
353
|
+
- Runs **after** `--apply-overrides` so the final tree includes security pins before dedupe.
|
|
354
|
+
|
|
355
|
+
Where it shows up:
|
|
356
|
+
|
|
357
|
+
- **Console**: one-line summary — `--fix-lockfile: npm dedupe ... succeeded (12 packages deduped/updated, 3 stale transitives flagged)`.
|
|
358
|
+
- **`--summary md` / `--summary html`**: a dedicated **Lockfile fix** section with a `merged`/`updated` diff table, a collapsible **Stale transitives** details block, and the last lines of the dedupe/validator output on failure.
|
|
359
|
+
- **`--json`**: `lockfileFix: { status, manager, lockfile, command, dedupeChanges[], stale[], ... }` with the full structured diff.
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
# Post-security-sweep cleanup: dedupe the tree and surface stale transitives in the PR body.
|
|
363
|
+
npx dep-up-surgeon --security-only --apply-overrides --fix-lockfile --summary md
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Doctor subcommand (`dep-up-surgeon doctor`)
|
|
367
|
+
|
|
368
|
+
`doctor` is a **read-only** diagnostic that answers one question: "is this project in good shape for an upgrade pass right now?". Run it before trusting an upgrade loop (or as a CI pre-check); it never mutates anything. Output is a **traffic-light report** — green/yellow/red per check with a remediation hint on anything non-green.
|
|
369
|
+
|
|
370
|
+
What it checks, in order (stable IDs for `--json` consumers):
|
|
371
|
+
|
|
372
|
+
1. **`node-version`** — current Node satisfies `engines.node` (if set). Red when a mismatched Node would tear down peer-dep resolution in ways that look like CVE-driven failures later.
|
|
373
|
+
2. **`manager`** — a single package manager was resolved cleanly. Yellow when multiple lockfiles coexist or the tool had to fall back to the `npm` default without any signal.
|
|
374
|
+
3. **`lockfile`** — the lockfile is parseable. Yellow on npm v1 shape (upgrades to v2 recommended); red on unreadable / corrupt files.
|
|
375
|
+
4. **`workspace-coherence`** — declared workspace members resolve on disk with their own `package.json`.
|
|
376
|
+
5. **`policy`** — `.dep-up-surgeon.policy.{yaml,json}` (when present) parses without warnings.
|
|
377
|
+
6. **`preflight-validator`** — your `<mgr> test` / `<mgr> run build` (or `--validate <cmd>`) passes right now, before any upgrade. Red here means the project is broken **before** the upgrade loop — fix that first or every failure downstream will look like a regression.
|
|
378
|
+
7. **`peer-deps`** — existing peer / missing dep warnings (via `npm ls --all`, `pnpm install --frozen-lockfile --offline`, or `yarn check`). Catches "already broken before you touched it" cases.
|
|
379
|
+
8. **`audit`** — `<mgr> audit` dry-run with severity breakdown. Red on any high/critical advisory, yellow on low/moderate.
|
|
380
|
+
9. **`stale-transitives`** — up to 100 transitives scanned against registry `latest`; yellow when any are more than a minor or a full major behind. Informational (never red); run `--fix-lockfile` to clean up the easy ones.
|
|
381
|
+
|
|
382
|
+
Exit codes:
|
|
383
|
+
|
|
384
|
+
- `0` — all checks green (or yellow-only without `--strict`)
|
|
385
|
+
- `1` — any yellow under `--strict`
|
|
386
|
+
- `2` — any red
|
|
387
|
+
|
|
388
|
+
Options are focused (no entanglement with the 70+ upgrade-flow flags):
|
|
389
|
+
|
|
390
|
+
| Option | Description |
|
|
391
|
+
|--------|-------------|
|
|
392
|
+
| `--json` | Emit the full `DoctorReport` as JSON on stdout instead of the human format. |
|
|
393
|
+
| `--strict` | Treat yellow checks as failures (exit 1 instead of 0). Use for CI gates. |
|
|
394
|
+
| `--no-validate` | Skip the pre-flight validator check. |
|
|
395
|
+
| `--validate <cmd>` | Override the validator command used by the pre-flight check. |
|
|
396
|
+
| `--skip-audit` | Skip the audit dry-run. Use for air-gapped CI / offline dev. |
|
|
397
|
+
| `--skip-peer-scan` | Skip the peer-dep scan (slow on huge trees). |
|
|
398
|
+
| `--skip-stale-scan` | Skip the registry-backed stale-transitive scan. |
|
|
399
|
+
| `--package-manager <mgr>` | Override detected manager: `auto`, `npm`, `pnpm`, `yarn`. |
|
|
400
|
+
| `--cwd <path>` | Run against a different directory. |
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
# Quick CI pre-check
|
|
404
|
+
npx dep-up-surgeon doctor --strict --json
|
|
405
|
+
|
|
406
|
+
# Local "should I trust the upgrade loop?" check
|
|
407
|
+
npx dep-up-surgeon doctor
|
|
408
|
+
```
|
|
409
|
+
|
|
325
410
|
### Auto-opening a PR (`--open-pr`)
|
|
326
411
|
|
|
327
412
|
When you've already paid the cost of running `--git-commit --git-branch`, `--open-pr` closes the loop by pushing the branch and opening a GitHub pull request via the [GitHub CLI (`gh`)](https://cli.github.com/). Uses your existing `gh auth`; the tool handles nothing sensitive.
|
|
@@ -357,7 +442,7 @@ npx dep-up-surgeon --workspaces --summary md \
|
|
|
357
442
|
- **yarn berry without the plugin** → falls back to a full root install with a one-time warning telling you the exact `yarn plugin import` command to fix it
|
|
358
443
|
|
|
359
444
|
The yarn capability is auto-probed at startup (`yarn --version` + `yarn workspaces focus --help`) and surfaced as `project.yarnMajorVersion` and `project.yarnSupportsFocus` in `--json`. The mode actually used is recorded as `installMode` in the report, and the exact filtered command appears under `failed[].install.command` when an upgrade rolls back.
|
|
360
|
-
- **Parallel target traversal (`--concurrency <n>`).** With more than one target, pass `--concurrency 4` (or up to `16`) to run target **scans + plans** concurrently. Registry IO (`pacote.manifest` / `pacote.packument`) is the slow part of each engine pass and is fully parallel-safe — overlapping it across targets gives a real wall-clock speedup on monorepos with many workspaces. **
|
|
445
|
+
- **Parallel target traversal (`--concurrency <n>`).** With more than one target, pass `--concurrency 4` (or up to `16`) to run target **scans + plans** concurrently. Registry IO (`pacote.manifest` / `pacote.packument`) is the slow part of each engine pass and is fully parallel-safe — overlapping it across targets gives a real wall-clock speedup on monorepos with many workspaces. In a **shared-lockfile** monorepo (the common case) installs and validations stay serialized under a keyed async mutex because they all touch the same root lockfile and `node_modules`; running them in parallel would corrupt the lockfile. In an **isolated-lockfile** monorepo (pnpm `shared-workspace-lockfile=false`, or every workspace member shipping its own lockfile) the installs and validations ALSO run in parallel — the keyed mutex keys off each target's install directory, so same-dir operations still serialize while different-dir operations unlock. The detection is automatic and surfaced as `project.isolatedLockfiles` + `parallelInstalls: true` in `--json`; pass `--no-parallel-installs` to force the old serialized behavior. An in-process registry cache (always on) also deduplicates fetches so the same dependency name in many workspaces only hits the network once. The effective concurrency is reported as `concurrency` in `--json` output. Parallelism requires `--json`; non-JSON mode silently downgrades to `1` to keep per-target log lines legible.
|
|
361
446
|
|
|
362
447
|
The detected manager + members are surfaced under `project` in `--json` output:
|
|
363
448
|
|
|
@@ -521,7 +606,7 @@ The compiled entry is `dist/cli.js` (see `"bin"` in `package.json`).
|
|
|
521
606
|
|
|
522
607
|
- GitLab / Bitbucket auto-PR providers (today `--open-pr` is GitHub-only via `gh`)
|
|
523
608
|
- Nested / parent-scoped override rules beyond the flat `name → version` form (`overrides: { "foo": { ">=2 <3": "3.0.0" } }` in npm, deep pnpm selectors)
|
|
524
|
-
-
|
|
609
|
+
- Resolver sharpening: SAT-backed peer-range intersection for very large linked graphs (10+ members) where the current 400-tuple backtracking budget gives up; peer-range intersection across **non-linked** packages too (today the resolver only intervenes on linked-group failures)
|
|
525
610
|
- Renovate-style scheduling helpers (cron / day-of-week filters, grouping rules)
|
|
526
611
|
- True parallel installs in monorepos that don't share a root lockfile (e.g. nohoist setups), going beyond today's parallel scan + serial install model
|
|
527
612
|
- AI-assisted failure explanation: feed `install.lastLines` + `validation.lastLines` to an LLM and attach a one-sentence "why this broke" note to failed records
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { PackageManager } from '../core/workspaces.js';
|
|
2
|
+
export type DoctorStatus = 'green' | 'yellow' | 'red';
|
|
3
|
+
export interface DoctorCheck {
|
|
4
|
+
/** Stable id — used as the JSON key so consumers can branch on it reliably. */
|
|
5
|
+
id: string;
|
|
6
|
+
/** Short label shown in the human-readable output. */
|
|
7
|
+
label: string;
|
|
8
|
+
status: DoctorStatus;
|
|
9
|
+
/** One-line human summary of the outcome. */
|
|
10
|
+
message: string;
|
|
11
|
+
/** Optional remediation hint shown under the message for non-green entries. */
|
|
12
|
+
hint?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Free-form structured payload included in `--json` output. Kept check-specific so the human
|
|
15
|
+
* renderer can stay dumb and downstream consumers can parse what they care about.
|
|
16
|
+
*/
|
|
17
|
+
data?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
export interface DoctorReport {
|
|
20
|
+
cwd: string;
|
|
21
|
+
toolVersion: string;
|
|
22
|
+
/** Per-check results in stable order. */
|
|
23
|
+
checks: DoctorCheck[];
|
|
24
|
+
/** Aggregate — worst status across all checks. */
|
|
25
|
+
overall: DoctorStatus;
|
|
26
|
+
/** Count of checks per status. */
|
|
27
|
+
counts: Record<DoctorStatus, number>;
|
|
28
|
+
}
|
|
29
|
+
export interface RunDoctorOptions {
|
|
30
|
+
cwd: string;
|
|
31
|
+
toolVersion: string;
|
|
32
|
+
/** When true, skip the pre-flight validator even if a command is configured. */
|
|
33
|
+
skipValidator?: boolean;
|
|
34
|
+
/** User-provided validator command (overrides the default `<mgr> test` fallback). */
|
|
35
|
+
validatorCommand?: string;
|
|
36
|
+
/** When true, skip `<mgr> audit` (air-gapped CI, offline dev). */
|
|
37
|
+
skipAudit?: boolean;
|
|
38
|
+
/** When true, skip `<mgr> ls --all` (slow on huge trees). */
|
|
39
|
+
skipPeerScan?: boolean;
|
|
40
|
+
/** When true, skip the registry-backed stale-transitive scan. */
|
|
41
|
+
skipStaleScan?: boolean;
|
|
42
|
+
/** Override package manager (mirrors the top-level `--package-manager` flag). */
|
|
43
|
+
manager?: PackageManager | 'auto';
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Public entry point. Runs every enabled check in sequence (checks are cheap enough that
|
|
47
|
+
* serial keeps the log predictable; we don't gain anything by racing them). Never throws —
|
|
48
|
+
* a check that explodes returns `red` with the error as the `message` so downstream renderers
|
|
49
|
+
* still see a complete report.
|
|
50
|
+
*/
|
|
51
|
+
export declare function runDoctor(opts: RunDoctorOptions): Promise<DoctorReport>;
|
|
52
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,uBAAuB,CAAC;AAUzE,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,EAAE,EAAE,MAAM,CAAC;IACX,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;IACrB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,kDAAkD;IAClD,OAAO,EAAE,YAAY,CAAC;IACtB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kEAAkE;IAClE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iEAAiE;IACjE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iFAAiF;IACjF,OAAO,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAkC7E"}
|