@crbroughton/recul 0.1.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -0
  3. package/dist/bin/recul.d.ts +3 -0
  4. package/dist/bin/recul.d.ts.map +1 -0
  5. package/dist/bin/recul.js +96 -0
  6. package/dist/bin/recul.js.map +1 -0
  7. package/dist/src/adapters/npm.d.ts +10 -0
  8. package/dist/src/adapters/npm.d.ts.map +1 -0
  9. package/dist/src/adapters/npm.js +26 -0
  10. package/dist/src/adapters/npm.js.map +1 -0
  11. package/dist/src/adapters/pnpm.d.ts +34 -0
  12. package/dist/src/adapters/pnpm.d.ts.map +1 -0
  13. package/dist/src/adapters/pnpm.js +97 -0
  14. package/dist/src/adapters/pnpm.js.map +1 -0
  15. package/dist/src/audit.d.ts +19 -0
  16. package/dist/src/audit.d.ts.map +1 -0
  17. package/dist/src/audit.js +65 -0
  18. package/dist/src/audit.js.map +1 -0
  19. package/dist/src/config.d.ts +23 -0
  20. package/dist/src/config.d.ts.map +1 -0
  21. package/dist/src/config.js +67 -0
  22. package/dist/src/config.js.map +1 -0
  23. package/dist/src/index.d.ts +6 -0
  24. package/dist/src/index.d.ts.map +1 -0
  25. package/dist/src/index.js +5 -0
  26. package/dist/src/index.js.map +1 -0
  27. package/dist/src/init.d.ts +2 -0
  28. package/dist/src/init.d.ts.map +1 -0
  29. package/dist/src/init.js +113 -0
  30. package/dist/src/init.js.map +1 -0
  31. package/dist/src/lockfile.d.ts +24 -0
  32. package/dist/src/lockfile.d.ts.map +1 -0
  33. package/dist/src/lockfile.js +54 -0
  34. package/dist/src/lockfile.js.map +1 -0
  35. package/dist/src/output.d.ts +13 -0
  36. package/dist/src/output.d.ts.map +1 -0
  37. package/dist/src/output.js +198 -0
  38. package/dist/src/output.js.map +1 -0
  39. package/dist/src/resolve.d.ts +25 -0
  40. package/dist/src/resolve.d.ts.map +1 -0
  41. package/dist/src/resolve.js +55 -0
  42. package/dist/src/resolve.js.map +1 -0
  43. package/dist/src/semver.d.ts +29 -0
  44. package/dist/src/semver.d.ts.map +1 -0
  45. package/dist/src/semver.js +39 -0
  46. package/dist/src/semver.js.map +1 -0
  47. package/dist/src/types.d.ts +70 -0
  48. package/dist/src/types.d.ts.map +1 -0
  49. package/dist/src/types.js +2 -0
  50. package/dist/src/types.js.map +1 -0
  51. package/package.json +66 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Craig R Broughton
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # recul
2
+
3
+ Stay N versions behind the latest published release of your npm dependencies to avoid supply chain attacks.
4
+
5
+ recul is not a replacement for typical auditing via `npm audit` or third party security tools; it is a complementary layer that reduces the attack surface without requiring active effort on every release cycle.
6
+
7
+ ## How it works
8
+
9
+ Given a lag of `N`, the target version is `versions[latest_index - N]`. Only stable releases are counted; pre-release versions (configurable, defaults to `-alpha`, `-beta`, `-rc`, `-next`, `-canary`) are excluded. If a package has fewer releases than the lag value, recul pins to the oldest available stable version.
10
+
11
+ Packages already older than the lag target are left alone by default. The invariant is "never be too new", not "be exactly N behind".
12
+
13
+ ## Requirements
14
+
15
+ - Node.js 18 or later
16
+
17
+ ## Installation
18
+
19
+ ```sh
20
+ npm i -D recul
21
+ # or
22
+ pnpm add -D recul
23
+ ```
24
+
25
+ ## Quick start
26
+
27
+ ```sh
28
+ # Create a config file in the current directory
29
+ recul init
30
+
31
+ # Audit your dependencies
32
+ recul
33
+ ```
34
+
35
+ ## Configuration
36
+
37
+ Commit a `recul.config.jsonc` to standardise settings across the team.
38
+
39
+ ```jsonc
40
+ {
41
+ // How many versions to stay behind the latest published release.
42
+ // Counted in releases, not semver increments.
43
+ //
44
+ // 1 → days to weeks (fast-moving projects, minimal buffer)
45
+ // 2 → weeks (balanced default, recommended)
46
+ // 3 → weeks to months (cautious teams, slower release cadences)
47
+ // 5+ → months (regulated environments, high-security contexts)
48
+ "lag": 2,
49
+
50
+ // Package manager: "npm" | "pnpm"
51
+ "packageManager": "pnpm",
52
+
53
+ // Path to the package.json to audit, relative to this config file.
54
+ "packageFile": "package.json",
55
+
56
+ // How to handle packages already older than the lag target.
57
+ // "ignore" → treat as ok, no output (default)
58
+ // "report" → surface them with a safe upgrade-to-target command
59
+ "behindBehavior": "ignore",
60
+
61
+ // Version prefix used in generated install commands.
62
+ // "exact" → 1.3.4 (recommended; audits are reliable)
63
+ // "caret" → ^1.3.4 (allows minor/patch drift)
64
+ // "tilde" → ~1.3.4 (allows patch drift only)
65
+ //
66
+ // Per-package map also supported:
67
+ // { "default": "exact", "react": "tilde" }
68
+ "rangeSpecifier": "exact",
69
+
70
+ // Packages to skip entirely.
71
+ "ignore": [],
72
+
73
+ // Minimum days a version must have been published before it is eligible
74
+ // as a lag target. Combines with "lag" for defence-in-depth.
75
+ // Omit or set to 0 to disable.
76
+ "minimumReleaseAge": 3,
77
+
78
+ // Version strings containing any of these substrings are treated as
79
+ // pre-releases and excluded from the candidate list.
80
+ "preReleaseFilter": ["-alpha", "-beta", "-rc", "-next", "-canary"]
81
+ }
82
+ ```
83
+
84
+ A config file is required; run `recul init` if you do not have one.
85
+
86
+ ## Output
87
+
88
+ ```
89
+ recul staying 2 versions behind latest
90
+
91
+ settings
92
+ lag 2 ; stay 2 versions behind latest
93
+ pm pnpm ; the chosen package manager
94
+ behind ignore ; ignore packages behind target
95
+ range exact ; pin exact versions
96
+ minAge 3 ; skip versions published within the last 3 days
97
+
98
+ package declared → target latest status
99
+ ────────────────────────────────────────────────────────────────────────────────
100
+ express ^4.19.2 4.17.3 4.19.2 ↓ will pin back
101
+ react ^18.3.1 18.1.0 18.3.1 ↓ will pin back
102
+ typescript 5.4.5 5.4.5 5.4.5 ✓ ok
103
+
104
+ to pin back:
105
+ pnpm add express@4.17.3 react@18.1.0
106
+ ```
107
+
108
+ ### Status values
109
+
110
+ | Status | Meaning |
111
+ |--------|---------|
112
+ | `✓ ok` | At or behind the lag target |
113
+ | `↓ will pin back` | Ahead of the lag target; install command shown |
114
+ | `↑ safe to upgrade` | Behind the target (only shown when `behindBehavior: report`) |
115
+ | `✗ unresolved` | Registry fetch failed or no stable versions found |
116
+
117
+ A `⚠ declared <specifier>` warning is appended when a package is declared with a different range prefix than `rangeSpecifier`; this means the audited version may differ from what is actually installed.
118
+
119
+ ## pnpm catalog support
120
+
121
+ When using pnpm workspaces with a `catalogs` block in `pnpm-workspace.yaml`, recul reads catalog entries to resolve `catalog:` and `catalog:<name>` references in `package.json`.
122
+
123
+ Violations in catalog-managed packages are reported with the catalog entry to update rather than an install command. Pass `--fix` to apply the updates directly to `pnpm-workspace.yaml`.
124
+
125
+ ## Lockfile support
126
+
127
+ When a lockfile is present, recul reads the installed version from it and uses that for comparison rather than the declared range. This gives accurate results for packages declared with `^` or `~`.
128
+
129
+ | Package manager | Lockfile |
130
+ |----------------|----------|
131
+ | npm | `package-lock.json` (v3) |
132
+ | pnpm | `pnpm-lock.yaml` (v6+) |
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=recul.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recul.d.ts","sourceRoot":"","sources":["../../bin/recul.ts"],"names":[],"mappings":""}
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'node:fs';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { defineCommand, runMain } from 'citty';
5
+ import { defu } from 'defu';
6
+ import { destr } from 'destr';
7
+ import { auditDeps } from '../src/audit.js';
8
+ import { DEFAULTS, loadConfigFile, resolveConfigDir } from '../src/config.js';
9
+ import { runInit } from '../src/init.js';
10
+ import { detectPackageManager, loadLockfile, loadPnpmCatalog, npmAdapter, pnpmAdapter, resolveCatalogRefs, updatePnpmCatalog } from '../src/lockfile.js';
11
+ import { printResults } from '../src/output.js';
12
+ const initCommand = defineCommand({
13
+ meta: { name: 'init', description: 'Create recul.config.jsonc with recommended settings' },
14
+ run() {
15
+ runInit(process.cwd());
16
+ },
17
+ });
18
+ const main = defineCommand({
19
+ meta: { name: 'recul', description: 'Stay N versions behind latest' },
20
+ args: {
21
+ file: { type: 'string', alias: 'f', description: 'Path to package.json (default: package.json)' },
22
+ fix: { type: 'boolean', description: 'Apply catalog fixes directly to pnpm-workspace.yaml' },
23
+ },
24
+ subCommands: { init: initCommand },
25
+ async run({ args }) {
26
+ const configDir = resolveConfigDir({ ...(args.file !== undefined ? { file: args.file } : {}), cwd: process.cwd() });
27
+ const fileConfig = loadConfigFile(configDir);
28
+ if (fileConfig === null) {
29
+ console.error('no config file found or config could not be parsed.\n');
30
+ console.error('run "recul init" to create recul.config.jsonc with recommended settings.');
31
+ process.exit(1);
32
+ }
33
+ const detectedPm = detectPackageManager(configDir);
34
+ const config = defu({ file: args.file }, { lag: fileConfig.lag, file: fileConfig.packageFile, pm: fileConfig.packageManager, behindBehavior: fileConfig.behindBehavior, rangeSpecifier: fileConfig.rangeSpecifier, ignore: fileConfig.ignore, minimumReleaseAge: fileConfig.minimumReleaseAge, preReleaseFilter: fileConfig.preReleaseFilter }, { pm: detectedPm ?? undefined }, DEFAULTS);
35
+ const { lag, file, pm, behindBehavior, rangeSpecifier, ignore, preReleaseFilter } = config;
36
+ const minimumReleaseAge = config.minimumReleaseAge !== null ? config.minimumReleaseAge : undefined;
37
+ const pkgPath = resolve(process.cwd(), file);
38
+ let raw;
39
+ try {
40
+ raw = destr(readFileSync(pkgPath, 'utf8'));
41
+ }
42
+ catch (err) {
43
+ const message = err instanceof Error ? err.message : String(err);
44
+ console.error(`error: could not read ${pkgPath}: ${message}`);
45
+ process.exit(1);
46
+ }
47
+ if (raw === null || typeof raw !== 'object') {
48
+ console.error(`error: ${pkgPath} is not a JSON object`);
49
+ process.exit(1);
50
+ }
51
+ const pkgDir = dirname(pkgPath);
52
+ const base = raw;
53
+ const catalogs = loadPnpmCatalog(pkgDir);
54
+ const pkgJson = catalogs !== null
55
+ ? {
56
+ ...(base.dependencies !== undefined ? { dependencies: resolveCatalogRefs(base.dependencies, catalogs) } : {}),
57
+ ...(base.devDependencies !== undefined ? { devDependencies: resolveCatalogRefs(base.devDependencies, catalogs) } : {}),
58
+ }
59
+ : base;
60
+ const catalogPackages = catalogs !== null
61
+ ? new Set(Object.values(catalogs).flatMap(Object.keys))
62
+ : undefined;
63
+ const installedMap = loadLockfile({ dir: pkgDir, adapters: [npmAdapter, pnpmAdapter] });
64
+ const results = await auditDeps({
65
+ pkgJson,
66
+ lag,
67
+ ...(minimumReleaseAge !== undefined ? { minimumReleaseAge } : {}),
68
+ preReleaseFilter,
69
+ rangeSpecifier,
70
+ ignore,
71
+ ...(installedMap !== null ? { installed: installedMap } : {}),
72
+ ...(catalogPackages !== undefined ? { catalogPackages } : {}),
73
+ });
74
+ let fixed;
75
+ if (args.fix && catalogPackages !== undefined) {
76
+ const updates = {};
77
+ for (const r of results) {
78
+ if (!r.fromCatalog)
79
+ continue;
80
+ if (r.status === 'pin' && r.target !== null)
81
+ updates[r.name] = r.target;
82
+ else if (r.status === 'behind' && behindBehavior === 'report' && r.target !== null)
83
+ updates[r.name] = r.target;
84
+ else if (r.specifierMismatch && r.status !== 'pin')
85
+ updates[r.name] = r.current;
86
+ }
87
+ if (Object.keys(updates).length > 0) {
88
+ updatePnpmCatalog(pkgDir, updates);
89
+ fixed = Object.keys(updates);
90
+ }
91
+ }
92
+ printResults({ results, lag, pm, behindBehavior, rangeSpecifier, ...(minimumReleaseAge !== undefined ? { minimumReleaseAge } : {}), ...(catalogPackages !== undefined ? { workspaceFile: 'pnpm-workspace.yaml' } : {}), ...(fixed !== undefined ? { fixed } : {}) });
93
+ },
94
+ });
95
+ runMain(main);
96
+ //# sourceMappingURL=recul.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recul.js","sourceRoot":"","sources":["../../bin/recul.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { LockfileAdapter } from '../lockfile.js';
2
+ import type { InstalledVersionMap } from '../types.js';
3
+ interface NpmLockPackageEntry {
4
+ version?: string;
5
+ }
6
+ export declare function parsePackagesBlock(packages: Record<string, NpmLockPackageEntry>): InstalledVersionMap;
7
+ export declare function parseNpmLock(content: string): InstalledVersionMap;
8
+ export declare const npmAdapter: LockfileAdapter;
9
+ export {};
10
+ //# sourceMappingURL=npm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"npm.d.ts","sourceRoot":"","sources":["../../../src/adapters/npm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAGtD,UAAU,mBAAmB;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAMD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,mBAAmB,CAarG;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAKjE;AAED,eAAO,MAAM,UAAU,EAAE,eAGxB,CAAA"}
@@ -0,0 +1,26 @@
1
+ import { destr } from 'destr';
2
+ export function parsePackagesBlock(packages) {
3
+ const result = {};
4
+ for (const [key, entry] of Object.entries(packages)) {
5
+ if (!key.startsWith('node_modules/'))
6
+ continue;
7
+ const name = key.slice('node_modules/'.length);
8
+ // Skip nested installs like "foo/node_modules/bar"
9
+ if (name.includes('node_modules'))
10
+ continue;
11
+ if (typeof entry.version === 'string')
12
+ result[name] = entry.version;
13
+ }
14
+ return result;
15
+ }
16
+ export function parseNpmLock(content) {
17
+ const raw = destr(content);
18
+ if (raw === null || typeof raw !== 'object')
19
+ return {};
20
+ return raw.packages ? parsePackagesBlock(raw.packages) : {};
21
+ }
22
+ export const npmAdapter = {
23
+ filename: 'package-lock.json',
24
+ parse: parseNpmLock,
25
+ };
26
+ //# sourceMappingURL=npm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"npm.js","sourceRoot":"","sources":["../../../src/adapters/npm.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAU7B,MAAM,UAAU,kBAAkB,CAAC,QAA6C;IAC9E,MAAM,MAAM,GAAwB,EAAE,CAAA;IACtC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC;YAClC,SAAQ;QACV,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAA;QAC9C,mDAAmD;QACnD,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC;YAC/B,SAAQ;QACV,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;YACnC,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAA;IAChC,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,GAAG,GAAG,KAAK,CAAiB,OAAO,CAAC,CAAA;IAC1C,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;QACzC,OAAO,EAAE,CAAA;IACX,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AAC7D,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAoB;IACzC,QAAQ,EAAE,mBAAmB;IAC7B,KAAK,EAAE,YAAY;CACpB,CAAA"}
@@ -0,0 +1,34 @@
1
+ import type { LockfileAdapter } from '../lockfile.js';
2
+ import type { InstalledVersionMap } from '../types.js';
3
+ /**
4
+ * Parse a pnpm-lock.yaml (v6–v9) into an installed version map.
5
+ *
6
+ * Reads only the root importer (`.`) from the `importers` block.
7
+ * Peer-dependency suffixes like `1.2.3(@types/node@22.0.0)` are stripped
8
+ * to leave only the bare version number.
9
+ */
10
+ export declare function parsePnpmLock(content: string): InstalledVersionMap;
11
+ export declare const pnpmAdapter: LockfileAdapter;
12
+ /**
13
+ * Read all catalogs from pnpm-workspace.yaml and return a
14
+ * catalogName → (packageName → specifier) map.
15
+ *
16
+ * `catalog:` references use the `"default"` key.
17
+ * Named references like `catalog:testing` use the catalog name as key.
18
+ *
19
+ * Returns null if the file is absent or has no catalogs section.
20
+ */
21
+ export declare function loadPnpmCatalog(dir: string): Record<string, Record<string, string>> | null;
22
+ /**
23
+ * Substitute `catalog:` and `catalog:<name>` references in a deps map with
24
+ * the real specifier from the workspace catalogs.
25
+ * Entries not found in the catalog are left as-is.
26
+ */
27
+ export declare function resolveCatalogRefs(deps: Record<string, string>, catalogs: Record<string, Record<string, string>>): Record<string, string>;
28
+ /**
29
+ * Update catalog entries in pnpm-workspace.yaml in place.
30
+ * `updates` is a map of package name → new specifier (e.g. `"1.2.3"`).
31
+ * Only lines within the file that match a catalog entry for the given name are rewritten.
32
+ */
33
+ export declare function updatePnpmCatalog(dir: string, updates: Record<string, string>): void;
34
+ //# sourceMappingURL=pnpm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pnpm.d.ts","sourceRoot":"","sources":["../../../src/adapters/pnpm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAiBtD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAkBlE;AAED,eAAO,MAAM,WAAW,EAAE,eAGzB,CAAA;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,CAmB1F;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC/C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAexB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CASpF"}
@@ -0,0 +1,97 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { parseYAML } from 'confbox';
4
+ /**
5
+ * Parse a pnpm-lock.yaml (v6–v9) into an installed version map.
6
+ *
7
+ * Reads only the root importer (`.`) from the `importers` block.
8
+ * Peer-dependency suffixes like `1.2.3(@types/node@22.0.0)` are stripped
9
+ * to leave only the bare version number.
10
+ */
11
+ export function parsePnpmLock(content) {
12
+ const raw = parseYAML(content);
13
+ const result = {};
14
+ const root = raw?.importers?.['.'];
15
+ if (!root)
16
+ return result;
17
+ for (const block of [root.dependencies, root.devDependencies]) {
18
+ if (!block)
19
+ continue;
20
+ for (const [name, entry] of Object.entries(block)) {
21
+ if (entry.version !== undefined) {
22
+ result[name] = entry.version.split('(')[0].trim();
23
+ }
24
+ }
25
+ }
26
+ return result;
27
+ }
28
+ export const pnpmAdapter = {
29
+ filename: 'pnpm-lock.yaml',
30
+ parse: parsePnpmLock,
31
+ };
32
+ /**
33
+ * Read all catalogs from pnpm-workspace.yaml and return a
34
+ * catalogName → (packageName → specifier) map.
35
+ *
36
+ * `catalog:` references use the `"default"` key.
37
+ * Named references like `catalog:testing` use the catalog name as key.
38
+ *
39
+ * Returns null if the file is absent or has no catalogs section.
40
+ */
41
+ export function loadPnpmCatalog(dir) {
42
+ const path = resolve(dir, 'pnpm-workspace.yaml');
43
+ if (!existsSync(path))
44
+ return null;
45
+ try {
46
+ const raw = parseYAML(readFileSync(path, 'utf8'));
47
+ if (!raw?.catalogs)
48
+ return null;
49
+ const result = {};
50
+ for (const [catalogName, section] of Object.entries(raw.catalogs)) {
51
+ result[catalogName] = { ...section };
52
+ }
53
+ return Object.keys(result).length > 0 ? result : null;
54
+ }
55
+ catch (err) {
56
+ const message = err instanceof Error ? err.message : String(err);
57
+ console.error(`error: could not parse ${path}: ${message}`);
58
+ return null;
59
+ }
60
+ }
61
+ /**
62
+ * Substitute `catalog:` and `catalog:<name>` references in a deps map with
63
+ * the real specifier from the workspace catalogs.
64
+ * Entries not found in the catalog are left as-is.
65
+ */
66
+ export function resolveCatalogRefs(deps, catalogs) {
67
+ const result = {};
68
+ for (const [name, ver] of Object.entries(deps)) {
69
+ if (ver === 'catalog:') {
70
+ result[name] = catalogs.default?.[name] ?? ver;
71
+ }
72
+ else if (ver.startsWith('catalog:')) {
73
+ const catalogName = ver.slice('catalog:'.length);
74
+ result[name] = catalogs[catalogName]?.[name] ?? ver;
75
+ }
76
+ else {
77
+ result[name] = ver;
78
+ }
79
+ }
80
+ return result;
81
+ }
82
+ /**
83
+ * Update catalog entries in pnpm-workspace.yaml in place.
84
+ * `updates` is a map of package name → new specifier (e.g. `"1.2.3"`).
85
+ * Only lines within the file that match a catalog entry for the given name are rewritten.
86
+ */
87
+ export function updatePnpmCatalog(dir, updates) {
88
+ const path = resolve(dir, 'pnpm-workspace.yaml');
89
+ let content = readFileSync(path, 'utf8');
90
+ for (const [name, version] of Object.entries(updates)) {
91
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
92
+ const pattern = new RegExp(`^(\\s+(?:'${escaped}'|"${escaped}"|${escaped}):\\s*)(.+)$`, 'gm');
93
+ content = content.replace(pattern, `$1${version}`);
94
+ }
95
+ writeFileSync(path, content, 'utf8');
96
+ }
97
+ //# sourceMappingURL=pnpm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pnpm.js","sourceRoot":"","sources":["../../../src/adapters/pnpm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { AuditResult, InstalledVersionMap, PackageJson, RangeSpecifierConfig } from './types.js';
2
+ export interface AuditDepsOptions {
3
+ pkgJson: PackageJson;
4
+ lag: number;
5
+ minimumReleaseAge?: number;
6
+ preReleaseFilter?: string[];
7
+ rangeSpecifier: RangeSpecifierConfig;
8
+ ignore?: string[];
9
+ installed?: InstalledVersionMap;
10
+ catalogPackages?: ReadonlySet<string>;
11
+ }
12
+ /**
13
+ * Audit all dependencies in a package.json object.
14
+ * - `ignore`: skip these package names entirely.
15
+ * - `installed`: lockfile-resolved version map; used for comparison when present.
16
+ * - `rangeSpecifier`: global string or per-package record.
17
+ */
18
+ export declare function auditDeps({ pkgJson, lag, minimumReleaseAge, preReleaseFilter, rangeSpecifier, ignore, installed, catalogPackages }: AuditDepsOptions): Promise<AuditResult[]>;
19
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAe,mBAAmB,EAAE,WAAW,EAAkB,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAyDlI,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,WAAW,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,cAAc,EAAE,oBAAoB,CAAA;IACpC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,CAAC,EAAE,mBAAmB,CAAA;IAC/B,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CACtC;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,gBAAqB,EAAE,cAAc,EAAE,MAAW,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CA0B7L"}
@@ -0,0 +1,65 @@
1
+ import { resolveRangeSpecifier } from './config.js';
2
+ import { resolvePackage } from './resolve.js';
3
+ import { bareVersion, detectSpecifier, semverCompareForSpecifier } from './semver.js';
4
+ async function auditOne({ name, rawVersion, lag, minimumReleaseAge, preReleaseFilter, rangeSpecifier, installedVersion, fromCatalog }) {
5
+ const declared = rawVersion;
6
+ const isCatalogRef = rawVersion.startsWith('catalog:');
7
+ // catalog: entries have no usable version in package.json — the lockfile is required
8
+ if (isCatalogRef && installedVersion === null) {
9
+ return { name, declared, current: '', installed: null, target: null, latest: null, status: 'unresolved', rangeSpecifier, declaredSpecifier: 'other', specifierMismatch: false, fromCatalog, error: 'catalog reference; pnpm lockfile required' };
10
+ }
11
+ const current = isCatalogRef ? installedVersion : bareVersion(rawVersion);
12
+ const installed = installedVersion;
13
+ const declaredSpecifier = isCatalogRef ? 'other' : detectSpecifier(rawVersion);
14
+ const specifierMismatch = declaredSpecifier !== 'other' && declaredSpecifier !== rangeSpecifier;
15
+ let resolved;
16
+ try {
17
+ resolved = await resolvePackage({ name, lag, preReleaseFilter, ...(minimumReleaseAge !== undefined ? { minimumReleaseAge } : {}) });
18
+ }
19
+ catch (err) {
20
+ const message = err instanceof Error ? err.message : String(err);
21
+ return { name, declared, current, installed, target: null, latest: null, status: 'unresolved', rangeSpecifier, declaredSpecifier, specifierMismatch, fromCatalog, error: message };
22
+ }
23
+ const { target, latest } = resolved;
24
+ if (target === null) {
25
+ return { name, declared, current, installed, target: null, latest, status: 'unresolved', rangeSpecifier, declaredSpecifier, specifierMismatch, fromCatalog, error: 'no stable versions found' };
26
+ }
27
+ const compareVersion = installed ?? current;
28
+ const cmp = semverCompareForSpecifier({ versionA: compareVersion, versionB: target, specifier: rangeSpecifier });
29
+ let status;
30
+ if (cmp > 0)
31
+ status = 'pin';
32
+ else if (cmp === 0)
33
+ status = 'ok';
34
+ else
35
+ status = 'behind';
36
+ return { name, declared, current, installed, target, latest, status, rangeSpecifier, declaredSpecifier, specifierMismatch, fromCatalog };
37
+ }
38
+ /**
39
+ * Audit all dependencies in a package.json object.
40
+ * - `ignore`: skip these package names entirely.
41
+ * - `installed`: lockfile-resolved version map; used for comparison when present.
42
+ * - `rangeSpecifier`: global string or per-package record.
43
+ */
44
+ export async function auditDeps({ pkgJson, lag, minimumReleaseAge, preReleaseFilter = [], rangeSpecifier, ignore = [], installed, catalogPackages }) {
45
+ const deps = {
46
+ ...(pkgJson.dependencies ?? {}),
47
+ ...(pkgJson.devDependencies ?? {}),
48
+ };
49
+ let names = Object.keys(deps);
50
+ if (ignore.length > 0) {
51
+ const ignoreSet = new Set(ignore);
52
+ names = names.filter(n => !ignoreSet.has(n));
53
+ }
54
+ return Promise.all(names.map(n => auditOne({
55
+ name: n,
56
+ rawVersion: deps[n] ?? '',
57
+ lag,
58
+ ...(minimumReleaseAge !== undefined ? { minimumReleaseAge } : {}),
59
+ preReleaseFilter,
60
+ rangeSpecifier: resolveRangeSpecifier({ config: rangeSpecifier, name: n }),
61
+ installedVersion: installed?.[n] ?? null,
62
+ fromCatalog: catalogPackages?.has(n) ?? false,
63
+ })));
64
+ }
65
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/audit.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAarF,KAAK,UAAU,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAmB;IACpJ,MAAM,QAAQ,GAAG,UAAU,CAAA;IAC3B,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,CAAA;IAEtD,qFAAqF;IACrF,IAAI,YAAY,IAAI,gBAAgB,KAAK,IAAI,EAAE,CAAC;QAC9C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,2CAA2C,EAAE,CAAA;IAClP,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,gBAAiB,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAA;IAC1E,MAAM,SAAS,GAAG,gBAAgB,CAAA;IAClC,MAAM,iBAAiB,GAAG,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,CAAA;IAC9E,MAAM,iBAAiB,GAAG,iBAAiB,KAAK,OAAO,IAAI,iBAAiB,KAAK,cAAc,CAAA;IAE/F,IAAI,QAAQ,CAAA;IACZ,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,cAAc,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,gBAAgB,EAAE,GAAG,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IACrI,CAAC;IACD,OAAO,GAAG,EAAE,CAAC;QACX,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAChE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;IACpL,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAA;IAEnC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,WAAW,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAA;IACjM,CAAC;IAED,MAAM,cAAc,GAAG,SAAS,IAAI,OAAO,CAAA;IAC3C,MAAM,GAAG,GAAG,yBAAyB,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAA;IAChH,IAAI,MAAmB,CAAA;IACvB,IAAI,GAAG,GAAG,CAAC;QACT,MAAM,GAAG,KAAK,CAAA;SACX,IAAI,GAAG,KAAK,CAAC;QAChB,MAAM,GAAG,IAAI,CAAA;;QACV,MAAM,GAAG,QAAQ,CAAA;IAEtB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAA;AAC1I,CAAC;AAaD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,gBAAgB,GAAG,EAAE,EAAE,cAAc,EAAE,MAAM,GAAG,EAAE,EAAE,SAAS,EAAE,eAAe,EAAoB;IACnK,MAAM,IAAI,GAA2B;QACnC,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;QAC/B,GAAG,CAAC,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC;KACnC,CAAA;IAED,IAAI,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC7B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAA;QACjC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9C,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAChB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CACZ,QAAQ,CAAC;QACP,IAAI,EAAE,CAAC;QACP,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE;QACzB,GAAG;QACH,GAAG,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,gBAAgB;QAChB,cAAc,EAAE,qBAAqB,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC1E,gBAAgB,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI;QACxC,WAAW,EAAE,eAAe,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK;KAC9C,CAAC,CACH,CACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { Config, ConfigFile, RangeSpecifier, RangeSpecifierConfig } from './types.js';
2
+ /**
3
+ * Load the config file from `dir`.
4
+ * Returns the parsed config, or null if no config file was found.
5
+ */
6
+ export declare function loadConfigFile(dir: string): ConfigFile | null;
7
+ /** Resolve the directory to search for the config file, based on --file if provided. */
8
+ export declare function resolveConfigDir({ file, cwd }: {
9
+ file?: string;
10
+ cwd: string;
11
+ }): string;
12
+ export declare const DEFAULTS: Config;
13
+ /**
14
+ * Resolve the effective RangeSpecifier for a specific package.
15
+ * When config is a string it applies to all packages.
16
+ * When config is a record, looks up by name, then "default", then falls back to "exact".
17
+ */
18
+ export declare function resolveRangeSpecifier({ config, name }: {
19
+ config: RangeSpecifierConfig;
20
+ name: string;
21
+ }): RangeSpecifier;
22
+ export declare function rangePrefix(specifier: RangeSpecifier): string;
23
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAa1F;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAiB7D;AAED,wFAAwF;AACxF,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEtF;AAED,eAAO,MAAM,QAAQ,EAAE,MAQtB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;IAAE,MAAM,EAAE,oBAAoB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CAUtH;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,CAM7D"}