@visulima/vis 1.0.0-alpha.11 → 1.0.0-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/LICENSE.md +559 -186
  3. package/README.md +18 -0
  4. package/dist/bin.js +1 -9
  5. package/dist/config/index.d.ts +477 -556
  6. package/dist/config/index.js +1 -2
  7. package/dist/generate/index.js +1 -3
  8. package/dist/packem_chunks/applyDefaults.js +2 -336
  9. package/dist/packem_chunks/bin.js +234 -9552
  10. package/dist/packem_chunks/doctor-probe.js +2 -112
  11. package/dist/packem_chunks/fix.js +11 -234
  12. package/dist/packem_chunks/handler.js +1 -99
  13. package/dist/packem_chunks/handler10.js +2 -53
  14. package/dist/packem_chunks/handler11.js +1 -32
  15. package/dist/packem_chunks/handler12.js +5 -100
  16. package/dist/packem_chunks/handler13.js +1 -25
  17. package/dist/packem_chunks/handler14.js +18 -916
  18. package/dist/packem_chunks/handler15.js +15 -201
  19. package/dist/packem_chunks/handler16.js +1 -124
  20. package/dist/packem_chunks/handler17.js +1 -13
  21. package/dist/packem_chunks/handler18.js +1 -106
  22. package/dist/packem_chunks/handler19.js +1 -19
  23. package/dist/packem_chunks/handler2.js +2 -75
  24. package/dist/packem_chunks/handler20.js +5 -29
  25. package/dist/packem_chunks/handler21.js +1 -222
  26. package/dist/packem_chunks/handler22.js +1 -237
  27. package/dist/packem_chunks/handler23.js +5 -101
  28. package/dist/packem_chunks/handler24.js +1 -110
  29. package/dist/packem_chunks/handler25.js +3 -402
  30. package/dist/packem_chunks/handler26.js +1 -13
  31. package/dist/packem_chunks/handler27.js +1 -63
  32. package/dist/packem_chunks/handler28.js +7 -34
  33. package/dist/packem_chunks/handler29.js +21 -456
  34. package/dist/packem_chunks/handler3.js +4 -95
  35. package/dist/packem_chunks/handler30.js +3 -170
  36. package/dist/packem_chunks/handler31.js +1 -530
  37. package/dist/packem_chunks/handler32.js +2 -214
  38. package/dist/packem_chunks/handler33.js +25 -119
  39. package/dist/packem_chunks/handler34.js +2 -630
  40. package/dist/packem_chunks/handler35.js +3 -283
  41. package/dist/packem_chunks/handler36.js +22 -542
  42. package/dist/packem_chunks/handler37.js +410 -744
  43. package/dist/packem_chunks/handler38.js +22 -989
  44. package/dist/packem_chunks/handler39.js +22 -574
  45. package/dist/packem_chunks/handler4.js +2 -90
  46. package/dist/packem_chunks/handler40.js +22 -1685
  47. package/dist/packem_chunks/handler41.js +6 -1088
  48. package/dist/packem_chunks/handler42.js +5 -797
  49. package/dist/packem_chunks/handler43.js +10 -2658
  50. package/dist/packem_chunks/handler44.js +51 -3784
  51. package/dist/packem_chunks/handler45.js +25 -2574
  52. package/dist/packem_chunks/handler46.js +3 -3769
  53. package/dist/packem_chunks/handler47.js +21 -1485
  54. package/dist/packem_chunks/handler48.js +42 -0
  55. package/dist/packem_chunks/handler5.js +8 -174
  56. package/dist/packem_chunks/handler6.js +1 -95
  57. package/dist/packem_chunks/handler7.js +1 -115
  58. package/dist/packem_chunks/handler8.js +1 -12
  59. package/dist/packem_chunks/handler9.js +1 -29
  60. package/dist/packem_chunks/heal-accept.js +10 -522
  61. package/dist/packem_chunks/heal.js +14 -673
  62. package/dist/packem_chunks/index.js +7 -873
  63. package/dist/packem_chunks/loader.js +1 -23
  64. package/dist/packem_chunks/tar.js +3 -0
  65. package/dist/packem_shared/ai-analysis-hm8d2W7z.js +67 -0
  66. package/dist/packem_shared/ai-cache-DoiF80AR.js +1 -0
  67. package/dist/packem_shared/ai-fix-nn4zOE95.js +43 -0
  68. package/dist/packem_shared/cache-directory-CwHlJhgx.js +1 -0
  69. package/dist/packem_shared/dependency-scan-COr5n63B.js +2 -0
  70. package/dist/packem_shared/docker-D6OGr5_S.js +2 -0
  71. package/dist/packem_shared/failure-log-iUVLf6ts.js +2 -0
  72. package/dist/packem_shared/flakiness-D9wf0t56.js +1 -0
  73. package/dist/packem_shared/giget-CcEy_Elm.js +2 -0
  74. package/dist/packem_shared/index-DH-5hsrC.js +1 -0
  75. package/dist/packem_shared/otel-DxDUPJJH.js +6 -0
  76. package/dist/packem_shared/otelPlugin-CQq6poq8.js +1 -0
  77. package/dist/packem_shared/registry-CkubDdiY.js +2 -0
  78. package/dist/packem_shared/run-summary-utils-BfBvjzhY.js +1 -0
  79. package/dist/packem_shared/runtime-check-BXZ43CBW.js +1 -0
  80. package/dist/packem_shared/selectors-BylODRiM.js +3 -0
  81. package/dist/packem_shared/symbols-CQmER5MT.js +1 -0
  82. package/dist/packem_shared/toolchain-BgBOUHII.js +5 -0
  83. package/dist/packem_shared/typosquats-CcZl99B1.js +1 -0
  84. package/dist/packem_shared/use-measured-height-DjYgUOKk.js +1 -0
  85. package/dist/packem_shared/utils-DrNg0XTR.js +1 -0
  86. package/dist/packem_shared/verify-Baj5mFJ7.js +1 -0
  87. package/dist/packem_shared/vis-update-app-D1jl0UZZ.js +1 -0
  88. package/dist/packem_shared/xxh3-DrAUNq4n.js +1 -0
  89. package/index.js +556 -727
  90. package/package.json +19 -29
  91. package/schemas/project.schema.json +739 -297
  92. package/schemas/vis-config.schema.json +3365 -384
  93. package/templates/buildkite-ci/template.yml +20 -20
  94. package/dist/packem_shared/VisUpdateApp-D-Yz_wvg.js +0 -1316
  95. package/dist/packem_shared/_commonjsHelpers-BqLXS_qQ.js +0 -5
  96. package/dist/packem_shared/ai-analysis-CHeB1joD.js +0 -367
  97. package/dist/packem_shared/ai-cache-Be_jexe4.js +0 -142
  98. package/dist/packem_shared/ai-fix-B9iQVcD2.js +0 -379
  99. package/dist/packem_shared/cache-directory-2qvs4goY.js +0 -98
  100. package/dist/packem_shared/catalog-BJTtyi-O.js +0 -1371
  101. package/dist/packem_shared/dependency-scan-A0KSklpG.js +0 -188
  102. package/dist/packem_shared/docker-2iZzc280.js +0 -181
  103. package/dist/packem_shared/failure-log-Cz3Z4SKL.js +0 -100
  104. package/dist/packem_shared/flakiness-goTxXuCX.js +0 -180
  105. package/dist/packem_shared/otel-DCvqCTz_.js +0 -158
  106. package/dist/packem_shared/otelPlugin-DFaLDvJf.js +0 -3
  107. package/dist/packem_shared/registry-CbqXI0rc.js +0 -272
  108. package/dist/packem_shared/run-summary-utils-PVMl4aIh.js +0 -130
  109. package/dist/packem_shared/runtime-check-Cobi3p6l.js +0 -127
  110. package/dist/packem_shared/selectors-SM69TfqC.js +0 -194
  111. package/dist/packem_shared/symbols-Ta7g2nU-.js +0 -14
  112. package/dist/packem_shared/toolchain-BdZd9eBi.js +0 -975
  113. package/dist/packem_shared/typosquats-C-bCh3PX.js +0 -1210
  114. package/dist/packem_shared/use-measured-height-CNP0vT4M.js +0 -20
  115. package/dist/packem_shared/utils-CthVdBPS.js +0 -40
  116. package/dist/packem_shared/xxh3-Ck8mXNg1.js +0 -239
@@ -1,1371 +0,0 @@
1
- import { createRequire as __cjs_createRequire } from "node:module";
2
-
3
- const __cjs_require = __cjs_createRequire(import.meta.url);
4
-
5
- const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
6
-
7
- const __cjs_getBuiltinModule = (module) => {
8
- // Check if we're in Node.js and version supports getBuiltinModule
9
- if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
10
- const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
11
- // Node.js 20.16.0+ and 22.3.0+
12
- if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
13
- return __cjs_getProcess.getBuiltinModule(module);
14
- }
15
- }
16
- // Fallback to createRequire
17
- return __cjs_require(module);
18
- };
19
-
20
- const {
21
- createInterface
22
- } = __cjs_getBuiltinModule("node:readline");
23
- import { findCacheDirSync } from '@visulima/find-cache-dir';
24
- import { isAccessibleSync, readFileSync, writeFileSync, walkSync, removeSync, readJsonSync, ensureDirSync, writeJsonSync } from '@visulima/fs';
25
- import { join, dirname } from '@visulima/path';
26
- import { Text, renderToString, Box, Table } from '@visulima/tui';
27
- import React from 'react';
28
- import { parse, coerce, rcompare } from 'semver';
29
- import { D as DEFAULT_LOW_SCORE_THRESHOLD, o as resolveWorkspacePatterns, q as readPnpmWorkspacePatterns, f as fetchSocketReports, t as findAcceptedRisk } from '../packem_chunks/bin.js';
30
-
31
- const PREFIX_REGEX = /^([\^~]|>=|<=|[><=])/;
32
- const YAML_ENTRY_REGEX = /^(?:'([^']+)'|"([^"]+)"|([^:\s]+)):\s*(?:'([^']+)'|"([^"]+)"|(\S+))/;
33
- const CATALOG_SECTION_REGEX = /^catalog:/m;
34
- const CATALOGS_SECTION_REGEX = /^catalogs:/m;
35
- const SCOPE_REGISTRY_REGEX = /^(@[^:]+):registry$/;
36
- const AUTH_TOKEN_REGEX = /^\/\/(.+)\/:_authToken$/;
37
- const CONSECUTIVE_WILDCARDS_REGEX = /\*+/g;
38
- const GLOB_SPECIAL_CHARS_REGEX = /[.+^${}()|[\]\\]/g;
39
- const REGEX_SPECIAL_CHARS_REGEX = /[.*+?^${}()|[\]\\]/g;
40
- const QUOTES_TRIM_REGEX = /^['"]|['"]$/g;
41
- const REGISTRY_PROTOCOL_REGEX = /^https?:\/\//;
42
- const TRAILING_SLASH_REGEX = /\/$/;
43
- const DEFAULT_DEP_TYPES = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
44
- const VALID_DEP_TYPES = /* @__PURE__ */ new Set([...DEFAULT_DEP_TYPES, "overrides", "pnpm.overrides", "resolutions"]);
45
- const getBackupDir = (workspaceRoot) => {
46
- const cacheDir = findCacheDirSync("vis", { create: true, cwd: workspaceRoot });
47
- if (!cacheDir) {
48
- throw new Error("Cannot resolve cache directory. Ensure node_modules exists in your workspace. Run your package manager's install command first.");
49
- }
50
- return join(cacheDir, "backup");
51
- };
52
- const parseVersion = (input) => {
53
- const stripped = input.replace(/^[\^~]|^>=|^<=|^[><]/, "");
54
- const parsed = parse(stripped);
55
- if (parsed) {
56
- return {
57
- major: parsed.major,
58
- minor: parsed.minor,
59
- patch: parsed.patch,
60
- prerelease: Array.isArray(parsed.prerelease) ? parsed.prerelease.join(".") : String(parsed.prerelease ?? "")
61
- };
62
- }
63
- const coerced = coerce(input);
64
- if (coerced) {
65
- return {
66
- major: coerced.major,
67
- minor: coerced.minor,
68
- patch: coerced.patch,
69
- prerelease: ""
70
- };
71
- }
72
- return void 0;
73
- };
74
- const extractPrefix = (range) => {
75
- const match = PREFIX_REGEX.exec(range);
76
- return match?.[1] ?? "";
77
- };
78
- const versionToString = (v) => {
79
- const base = `${String(v.major)}.${String(v.minor)}.${String(v.patch)}`;
80
- return v.prerelease ? `${base}-${v.prerelease}` : base;
81
- };
82
- const getUpdateType = (current, target) => {
83
- if (current.major !== target.major) {
84
- return "major";
85
- }
86
- if (current.minor !== target.minor) {
87
- return "minor";
88
- }
89
- if (current.patch !== target.patch) {
90
- return "patch";
91
- }
92
- if (current.prerelease !== target.prerelease) {
93
- return "patch";
94
- }
95
- return "none";
96
- };
97
- const compareVersions = (a, b) => {
98
- if (a.major !== b.major) {
99
- return a.major - b.major;
100
- }
101
- if (a.minor !== b.minor) {
102
- return a.minor - b.minor;
103
- }
104
- if (a.patch !== b.patch) {
105
- return a.patch - b.patch;
106
- }
107
- if (!a.prerelease && b.prerelease) {
108
- return 1;
109
- }
110
- if (a.prerelease && !b.prerelease) {
111
- return -1;
112
- }
113
- if (a.prerelease && b.prerelease) {
114
- return a.prerelease < b.prerelease ? -1 : a.prerelease > b.prerelease ? 1 : 0;
115
- }
116
- return 0;
117
- };
118
- const isNewer = (current, target) => compareVersions(target, current) > 0;
119
- const matchesPattern = (name, pattern) => {
120
- const collapsed = pattern.replaceAll(CONSECUTIVE_WILDCARDS_REGEX, "*");
121
- const escaped = collapsed.replaceAll(GLOB_SPECIAL_CHARS_REGEX, String.raw`\$&`);
122
- const regex = new RegExp(`^${escaped.replaceAll("*", ".*").replaceAll("?", ".")}$`);
123
- return regex.test(name);
124
- };
125
- const matchesFilters = (name, include, exclude) => {
126
- if (exclude.some((p) => matchesPattern(name, p))) {
127
- return false;
128
- }
129
- if (include.length > 0) {
130
- return include.some((p) => matchesPattern(name, p));
131
- }
132
- return true;
133
- };
134
- const parseYamlEntry = (trimmed) => {
135
- const match = YAML_ENTRY_REGEX.exec(trimmed);
136
- if (!match) {
137
- return void 0;
138
- }
139
- const key = match[1] ?? match[2] ?? match[3];
140
- const value = match[4] ?? match[5] ?? match[6];
141
- if (!key || !value) {
142
- return void 0;
143
- }
144
- return [key, value];
145
- };
146
- const setCatalogEntry = (catalogs, catalogName, key, value) => {
147
- if (!catalogs.has(catalogName)) {
148
- catalogs.set(catalogName, /* @__PURE__ */ new Map());
149
- }
150
- const catalog = catalogs.get(catalogName);
151
- if (catalog) {
152
- catalog.set(key, value);
153
- }
154
- };
155
- const parseCatalogSection = (catalogs, trimmed, indent) => {
156
- if (indent < 2) {
157
- return;
158
- }
159
- const entry = parseYamlEntry(trimmed);
160
- if (entry) {
161
- setCatalogEntry(catalogs, "default", entry[0], entry[1]);
162
- }
163
- };
164
- const parseCatalogsSection = (catalogs, trimmed, indent, currentCatalogName) => {
165
- if (indent === 2 && trimmed.endsWith(":")) {
166
- return trimmed.slice(0, -1).trim().replaceAll(QUOTES_TRIM_REGEX, "");
167
- }
168
- if (indent >= 4 && currentCatalogName) {
169
- const entry = parseYamlEntry(trimmed);
170
- if (entry) {
171
- setCatalogEntry(catalogs, currentCatalogName, entry[0], entry[1]);
172
- }
173
- }
174
- return currentCatalogName;
175
- };
176
- const detectTopLevelSection = (trimmed) => {
177
- if (trimmed === "catalog:" || trimmed.startsWith("catalog:")) {
178
- return "catalog";
179
- }
180
- if (trimmed === "catalogs:" || trimmed.startsWith("catalogs:")) {
181
- return "catalogs";
182
- }
183
- return "none";
184
- };
185
- const parseCatalogsFromYaml = (content) => {
186
- const catalogs = /* @__PURE__ */ new Map();
187
- let section = "none";
188
- let currentCatalogName = "";
189
- for (const line of content.split("\n")) {
190
- const trimmed = line.trimStart();
191
- const indent = line.length - trimmed.length;
192
- if (indent === 0 && trimmed.length > 0 && !trimmed.startsWith("#")) {
193
- section = detectTopLevelSection(trimmed);
194
- if (section === "catalogs") {
195
- currentCatalogName = "";
196
- }
197
- continue;
198
- }
199
- if (trimmed.length === 0 || trimmed.startsWith("#")) {
200
- continue;
201
- }
202
- if (section === "catalog") {
203
- parseCatalogSection(catalogs, trimmed, indent);
204
- }
205
- if (section === "catalogs") {
206
- currentCatalogName = parseCatalogsSection(catalogs, trimmed, indent, currentCatalogName);
207
- }
208
- }
209
- return catalogs;
210
- };
211
- const hasPnpmCatalogs = (workspaceRoot) => {
212
- const filePath = join(workspaceRoot, "pnpm-workspace.yaml");
213
- if (!isAccessibleSync(filePath)) {
214
- return false;
215
- }
216
- const content = readFileSync(filePath);
217
- return CATALOG_SECTION_REGEX.test(content) || CATALOGS_SECTION_REGEX.test(content);
218
- };
219
- const readPnpmCatalogs = (workspaceRoot) => {
220
- const filePath = join(workspaceRoot, "pnpm-workspace.yaml");
221
- if (!isAccessibleSync(filePath)) {
222
- return /* @__PURE__ */ new Map();
223
- }
224
- const content = readFileSync(filePath);
225
- return parseCatalogsFromYaml(content);
226
- };
227
- const readPackageJsonSafe = (filePath) => {
228
- if (!isAccessibleSync(filePath)) {
229
- return void 0;
230
- }
231
- try {
232
- return readJsonSync(filePath);
233
- } catch {
234
- return void 0;
235
- }
236
- };
237
- const hasBunCatalogs = (workspaceRoot) => {
238
- const pkg = readPackageJsonSafe(join(workspaceRoot, "package.json"));
239
- return !!(pkg?.workspaces?.catalog || pkg?.workspaces?.catalogs);
240
- };
241
- const parseBunCatalogs = (pkg) => {
242
- const catalogs = /* @__PURE__ */ new Map();
243
- if (pkg.workspaces?.catalog && typeof pkg.workspaces.catalog === "object") {
244
- catalogs.set("default", new Map(Object.entries(pkg.workspaces.catalog)));
245
- }
246
- if (pkg.workspaces?.catalogs && typeof pkg.workspaces.catalogs === "object") {
247
- for (const [name, deps] of Object.entries(pkg.workspaces.catalogs)) {
248
- if (typeof deps === "object" && deps !== void 0) {
249
- catalogs.set(name, new Map(Object.entries(deps)));
250
- }
251
- }
252
- }
253
- return catalogs;
254
- };
255
- const readBunCatalogs = (workspaceRoot) => {
256
- const pkg = readPackageJsonSafe(join(workspaceRoot, "package.json"));
257
- if (!pkg) {
258
- return /* @__PURE__ */ new Map();
259
- }
260
- return parseBunCatalogs(pkg);
261
- };
262
- const parseCompositeCatalogName = (name) => {
263
- const colonIndex = name.lastIndexOf(":");
264
- if (colonIndex === -1) {
265
- return void 0;
266
- }
267
- const depType = name.slice(colonIndex + 1);
268
- if (!VALID_DEP_TYPES.has(depType)) {
269
- return void 0;
270
- }
271
- return { depType, relativePath: name.slice(0, colonIndex) };
272
- };
273
- const getDepTypesToInclude = (options) => {
274
- if (options?.dev) {
275
- return ["devDependencies"];
276
- }
277
- if (options?.prod) {
278
- return ["dependencies"];
279
- }
280
- if (options?.depFields && options.depFields.length > 0) {
281
- return options.depFields;
282
- }
283
- return DEFAULT_DEP_TYPES;
284
- };
285
- const collectInternalPackageNames = (workspaceRoot, rootName, workspaceDirectories) => {
286
- const names = /* @__PURE__ */ new Set();
287
- if (rootName) {
288
- names.add(rootName);
289
- }
290
- for (const directory of workspaceDirectories) {
291
- const pkgPath = join(workspaceRoot, directory, "package.json");
292
- if (isAccessibleSync(pkgPath)) {
293
- try {
294
- const pkg = readJsonSync(pkgPath);
295
- if (pkg.name) {
296
- names.add(pkg.name);
297
- }
298
- } catch {
299
- }
300
- }
301
- }
302
- return names;
303
- };
304
- const getNestedField = (object, path) => {
305
- const parts = path.split(".");
306
- let current = object;
307
- for (const part of parts) {
308
- if (current && typeof current === "object") {
309
- current = current[part];
310
- } else {
311
- return void 0;
312
- }
313
- }
314
- return typeof current === "object" && current !== null ? current : void 0;
315
- };
316
- const setNestedField = (object, path, key, value) => {
317
- const parts = path.split(".");
318
- let current = object;
319
- for (const part of parts.slice(0, -1)) {
320
- if (!current[part] || typeof current[part] !== "object") {
321
- current[part] = {};
322
- }
323
- current = current[part];
324
- }
325
- const lastPart = parts.at(-1);
326
- if (!current[lastPart] || typeof current[lastPart] !== "object") {
327
- current[lastPart] = {};
328
- }
329
- current[lastPart][key] = value;
330
- };
331
- const filterExternalDeps = (deps, internalNames) => {
332
- const filtered = /* @__PURE__ */ new Map();
333
- for (const [name, range] of Object.entries(deps)) {
334
- if (internalNames.has(name)) {
335
- continue;
336
- }
337
- if (range.startsWith("workspace:") || range.startsWith("file:") || range.startsWith("link:") || range.startsWith("catalog:") || range.startsWith("$")) {
338
- continue;
339
- }
340
- filtered.set(name, range);
341
- }
342
- return filtered;
343
- };
344
- const scanDirectoryDeps = (workspaceRoot, directory, rootPkgPath, depTypes, internalNames, catalogs) => {
345
- const pkgPath = directory === "." ? rootPkgPath : join(workspaceRoot, directory, "package.json");
346
- if (!isAccessibleSync(pkgPath)) {
347
- return;
348
- }
349
- let pkg;
350
- try {
351
- pkg = readJsonSync(pkgPath);
352
- } catch {
353
- return;
354
- }
355
- for (const depType of depTypes) {
356
- const deps = depType.includes(".") ? getNestedField(pkg, depType) : pkg[depType];
357
- if (!deps || typeof deps !== "object") {
358
- continue;
359
- }
360
- const filtered = filterExternalDeps(deps, internalNames);
361
- if (filtered.size > 0) {
362
- catalogs.set(`${directory}:${depType}`, filtered);
363
- }
364
- }
365
- };
366
- const readPackageJsonDeps = (workspaceRoot, options) => {
367
- const catalogs = /* @__PURE__ */ new Map();
368
- const rootPkgPath = join(workspaceRoot, "package.json");
369
- if (!isAccessibleSync(rootPkgPath)) {
370
- return catalogs;
371
- }
372
- const rootPkg = readJsonSync(rootPkgPath);
373
- let workspaceDirectories = [];
374
- const workspacesField = rootPkg.workspaces;
375
- if (workspacesField) {
376
- const patterns = Array.isArray(workspacesField) ? workspacesField : workspacesField.packages;
377
- if (patterns) {
378
- workspaceDirectories = resolveWorkspacePatterns(workspaceRoot, patterns);
379
- }
380
- }
381
- if (workspaceDirectories.length === 0) {
382
- const pnpmPatterns = readPnpmWorkspacePatterns(workspaceRoot);
383
- if (pnpmPatterns) {
384
- workspaceDirectories = resolveWorkspacePatterns(workspaceRoot, pnpmPatterns);
385
- }
386
- }
387
- const internalNames = collectInternalPackageNames(workspaceRoot, rootPkg.name, workspaceDirectories);
388
- const depTypes = getDepTypesToInclude(options);
389
- const directoriesToScan = [".", ...workspaceDirectories];
390
- for (const directory of directoriesToScan) {
391
- scanDirectoryDeps(workspaceRoot, directory, rootPkgPath, depTypes, internalNames, catalogs);
392
- }
393
- return catalogs;
394
- };
395
- const hasPackageJsonDeps = (workspaceRoot) => {
396
- const pkgPath = join(workspaceRoot, "package.json");
397
- if (!isAccessibleSync(pkgPath)) {
398
- return false;
399
- }
400
- try {
401
- const pkg = readJsonSync(pkgPath);
402
- return !!(pkg.dependencies || pkg.devDependencies || pkg.peerDependencies || pkg.optionalDependencies || pkg.overrides || pkg.resolutions || getNestedField(pkg, "pnpm.overrides"));
403
- } catch {
404
- return false;
405
- }
406
- };
407
- const applyPackageJsonUpdates = (workspaceRoot, updates) => {
408
- const byFile = /* @__PURE__ */ new Map();
409
- for (const update of updates) {
410
- const parsed = parseCompositeCatalogName(update.catalogName);
411
- if (!parsed) {
412
- continue;
413
- }
414
- const filePath = parsed.relativePath === "." ? join(workspaceRoot, "package.json") : join(workspaceRoot, parsed.relativePath, "package.json");
415
- if (!byFile.has(filePath)) {
416
- byFile.set(filePath, []);
417
- }
418
- const fileList = byFile.get(filePath);
419
- if (fileList) {
420
- fileList.push({ depType: parsed.depType, newRange: update.newRange, packageName: update.packageName });
421
- }
422
- }
423
- for (const [filePath, fileUpdates] of byFile) {
424
- const pkg = readJsonSync(filePath);
425
- for (const { depType, newRange, packageName } of fileUpdates) {
426
- if (depType.includes(".")) {
427
- setNestedField(pkg, depType, packageName, newRange);
428
- } else if (pkg[depType]) {
429
- pkg[depType][packageName] = newRange;
430
- }
431
- }
432
- writeJsonSync(filePath, pkg, { detectIndent: true, overwrite: true });
433
- }
434
- };
435
- const hasCatalogs = (workspaceRoot, packageManager) => {
436
- if (packageManager === "bun") {
437
- return hasBunCatalogs(workspaceRoot) || hasPackageJsonDeps(workspaceRoot);
438
- }
439
- if (packageManager === "npm" || packageManager === "yarn") {
440
- return hasPackageJsonDeps(workspaceRoot);
441
- }
442
- return hasPnpmCatalogs(workspaceRoot) || hasPackageJsonDeps(workspaceRoot);
443
- };
444
- const readCatalogs = (workspaceRoot, packageManager, options) => {
445
- let catalogs;
446
- if (packageManager === "bun") {
447
- catalogs = readBunCatalogs(workspaceRoot);
448
- } else if (packageManager === "npm" || packageManager === "yarn") {
449
- catalogs = /* @__PURE__ */ new Map();
450
- } else {
451
- catalogs = readPnpmCatalogs(workspaceRoot);
452
- }
453
- const pkgDeps = readPackageJsonDeps(workspaceRoot, options);
454
- for (const [key, deps] of pkgDeps) {
455
- if (!catalogs.has(key)) {
456
- catalogs.set(key, deps);
457
- }
458
- }
459
- return catalogs;
460
- };
461
- const parseNpmrc = (content) => {
462
- const registries = /* @__PURE__ */ new Map();
463
- const authTokens = /* @__PURE__ */ new Map();
464
- let defaultRegistry = "https://registry.npmjs.org";
465
- for (const line of content.split("\n")) {
466
- const trimmed = line.trim();
467
- if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith(";")) {
468
- continue;
469
- }
470
- const eqIndex = trimmed.indexOf("=");
471
- if (eqIndex === -1) {
472
- continue;
473
- }
474
- const key = trimmed.slice(0, eqIndex).trim();
475
- const value = trimmed.slice(eqIndex + 1).trim();
476
- const scopeMatch = SCOPE_REGISTRY_REGEX.exec(key);
477
- if (scopeMatch?.[1]) {
478
- registries.set(scopeMatch[1], value);
479
- continue;
480
- }
481
- if (key === "registry") {
482
- defaultRegistry = value;
483
- continue;
484
- }
485
- const authMatch = AUTH_TOKEN_REGEX.exec(key);
486
- if (authMatch?.[1]) {
487
- authTokens.set(authMatch[1], value);
488
- }
489
- }
490
- return { authTokens, defaultRegistry, registries };
491
- };
492
- const mergeNpmrcConfigs = (base, override) => {
493
- const merged = {
494
- authTokens: new Map([...base.authTokens, ...override.authTokens]),
495
- defaultRegistry: override.defaultRegistry === "https://registry.npmjs.org" ? base.defaultRegistry : override.defaultRegistry,
496
- registries: new Map([...base.registries, ...override.registries])
497
- };
498
- return merged;
499
- };
500
- const loadNpmrc = (workspaceRoot) => {
501
- const empty = { authTokens: /* @__PURE__ */ new Map(), defaultRegistry: "https://registry.npmjs.org", registries: /* @__PURE__ */ new Map() };
502
- const homeDirectory = process.env.HOME ?? process.env.USERPROFILE ?? "";
503
- const userNpmrc = join(homeDirectory, ".npmrc");
504
- let config = homeDirectory && isAccessibleSync(userNpmrc) ? parseNpmrc(readFileSync(userNpmrc)) : empty;
505
- const projectNpmrc = join(workspaceRoot, ".npmrc");
506
- if (isAccessibleSync(projectNpmrc)) {
507
- config = mergeNpmrcConfigs(config, parseNpmrc(readFileSync(projectNpmrc)));
508
- }
509
- return config;
510
- };
511
- const getRegistryForPackage = (packageName, config) => {
512
- let registryUrl = config.defaultRegistry;
513
- if (packageName.startsWith("@")) {
514
- const scope = packageName.split("/")[0];
515
- if (scope && config.registries.has(scope)) {
516
- const scopeRegistry = config.registries.get(scope);
517
- if (scopeRegistry) {
518
- registryUrl = scopeRegistry;
519
- }
520
- }
521
- }
522
- const registryHost = registryUrl.replace(REGISTRY_PROTOCOL_REGEX, "").replace(TRAILING_SLASH_REGEX, "");
523
- const token = config.authTokens.get(registryHost);
524
- return { token, url: registryUrl };
525
- };
526
- const DEFAULT_FETCH_TIMEOUT = 15e3;
527
- const fetchPackageVersions = async (packageName, registryConfig, timeoutMs = DEFAULT_FETCH_TIMEOUT, fetchPublishTimes = false) => {
528
- const baseUrl = (registryConfig?.url ?? "https://registry.npmjs.org").replace(TRAILING_SLASH_REGEX, "");
529
- const url = `${baseUrl}/${packageName}`;
530
- const headers = fetchPublishTimes ? { Accept: "application/json" } : { Accept: "application/vnd.npm.install-v1+json" };
531
- if (registryConfig?.authToken) {
532
- headers["Authorization"] = `Bearer ${registryConfig.authToken}`;
533
- }
534
- const controller = new AbortController();
535
- const timeout = setTimeout(() => {
536
- controller.abort();
537
- }, timeoutMs);
538
- try {
539
- const response = await fetch(url, { headers, signal: controller.signal });
540
- if (!response.ok) {
541
- throw new Error(`Failed to fetch ${packageName}: ${String(response.status)} ${response.statusText}`);
542
- }
543
- const data = await response.json();
544
- const result = {
545
- latest: data["dist-tags"]?.["latest"] ?? "",
546
- versions: Object.keys(data.versions ?? {})
547
- };
548
- if (fetchPublishTimes && data.time) {
549
- result.publishTimes = new Map(Object.entries(data.time));
550
- }
551
- return result;
552
- } finally {
553
- clearTimeout(timeout);
554
- }
555
- };
556
- const mapOsvSeverity = (vuln) => {
557
- const databaseSeverity = vuln.database_specific?.severity?.toUpperCase();
558
- if (databaseSeverity === "CRITICAL" || databaseSeverity === "HIGH" || databaseSeverity === "MODERATE" || databaseSeverity === "LOW") {
559
- return databaseSeverity;
560
- }
561
- const cvss = vuln.severity?.find((s) => s.type === "CVSS_V3")?.score;
562
- if (cvss) {
563
- const score = Number.parseFloat(cvss);
564
- if (score >= 9) {
565
- return "CRITICAL";
566
- }
567
- if (score >= 7) {
568
- return "HIGH";
569
- }
570
- if (score >= 4) {
571
- return "MODERATE";
572
- }
573
- return "LOW";
574
- }
575
- return "UNKNOWN";
576
- };
577
- const mapOsvCvss = (vuln) => {
578
- const cvss = vuln.severity?.find((s) => s.type === "CVSS_V3")?.score;
579
- return cvss ? Number.parseFloat(cvss) : void 0;
580
- };
581
- const mapOsvFixedVersions = (vuln) => {
582
- const fixed = [];
583
- for (const affected of vuln.affected ?? []) {
584
- for (const range of affected.ranges ?? []) {
585
- for (const event of range.events ?? []) {
586
- if (event.fixed) {
587
- fixed.push(event.fixed);
588
- }
589
- }
590
- }
591
- }
592
- return fixed;
593
- };
594
- const mapOsvVuln = (vuln) => {
595
- return {
596
- aliases: vuln.aliases?.length ? vuln.aliases : void 0,
597
- cvssScore: mapOsvCvss(vuln),
598
- fixedVersions: mapOsvFixedVersions(vuln),
599
- id: vuln.id,
600
- severity: mapOsvSeverity(vuln),
601
- summary: vuln.summary ?? ""
602
- };
603
- };
604
- const fetchVulnerabilities = async (packages, timeoutMs = 1e4) => {
605
- if (packages.length === 0) {
606
- return /* @__PURE__ */ new Map();
607
- }
608
- const queries = packages.map((pkg) => {
609
- return {
610
- package: { ecosystem: "npm", name: pkg.name },
611
- version: pkg.version
612
- };
613
- });
614
- const controller = new AbortController();
615
- const timeout = setTimeout(() => {
616
- controller.abort();
617
- }, timeoutMs);
618
- try {
619
- const response = await fetch("https://api.osv.dev/v1/querybatch", {
620
- body: JSON.stringify({ queries }),
621
- headers: { "Content-Type": "application/json" },
622
- method: "POST",
623
- signal: controller.signal
624
- });
625
- if (!response.ok) {
626
- return /* @__PURE__ */ new Map();
627
- }
628
- const data = await response.json();
629
- const result = /* @__PURE__ */ new Map();
630
- for (const [index, packageInfo] of packages.entries()) {
631
- const vulns = data.results[index]?.vulns;
632
- if (vulns && vulns.length > 0) {
633
- result.set(
634
- packageInfo.name,
635
- vulns.map((vuln) => mapOsvVuln(vuln))
636
- );
637
- }
638
- }
639
- return result;
640
- } catch {
641
- return /* @__PURE__ */ new Map();
642
- } finally {
643
- clearTimeout(timeout);
644
- }
645
- };
646
- const packageModeRegexCache = /* @__PURE__ */ new Map();
647
- const resolvePackageTarget = (packageName, globalTarget, packageMode) => {
648
- if (!packageMode) {
649
- return globalTarget;
650
- }
651
- const exact = packageMode[packageName];
652
- if (exact !== void 0) {
653
- return exact;
654
- }
655
- for (const [pattern, target] of Object.entries(packageMode)) {
656
- if (pattern === packageName) {
657
- continue;
658
- }
659
- if (pattern.startsWith("/") && pattern.endsWith("/")) {
660
- let regex = packageModeRegexCache.get(pattern);
661
- if (!regex) {
662
- regex = new RegExp(pattern.slice(1, -1));
663
- packageModeRegexCache.set(pattern, regex);
664
- }
665
- if (regex.test(packageName)) {
666
- return target;
667
- }
668
- } else if (matchesPattern(packageName, pattern)) {
669
- return target;
670
- }
671
- }
672
- return globalTarget;
673
- };
674
- const isTooNew = (version, publishTimes, minAgeMs) => {
675
- const publishDate = publishTimes?.get(version);
676
- if (!publishDate) {
677
- return false;
678
- }
679
- return Date.now() - new Date(publishDate).getTime() < minAgeMs;
680
- };
681
- const resolveMinAgeMs = (maturity) => {
682
- if (!maturity?.minimumReleaseAge) {
683
- return 0;
684
- }
685
- const isExcluded = maturity.packageName && maturity.minimumReleaseAgeExclude?.some((p) => matchesPattern(maturity.packageName, p));
686
- return isExcluded ? 0 : maturity.minimumReleaseAge * 60 * 1e3;
687
- };
688
- const filterCandidates = (versions, current, includePrerelease, minAgeMs, publishTimes, constraint) => {
689
- const best = versions.map((v) => {
690
- return { parsed: parseVersion(v), raw: v };
691
- }).filter((v) => {
692
- if (!v.parsed) {
693
- return false;
694
- }
695
- if (!includePrerelease && v.parsed.prerelease !== "") {
696
- return false;
697
- }
698
- if (!isNewer(current, v.parsed)) {
699
- return false;
700
- }
701
- if (minAgeMs && isTooNew(v.raw, publishTimes, minAgeMs)) {
702
- return false;
703
- }
704
- return constraint ? constraint(v.parsed) : true;
705
- }).toSorted((a, b) => rcompare(a.raw, b.raw));
706
- return best[0]?.raw;
707
- };
708
- const findTargetVersion = (versions, latest, currentRange, target, includePrerelease, maturity) => {
709
- const current = parseVersion(currentRange);
710
- if (!current) {
711
- return void 0;
712
- }
713
- const minAgeMs = resolveMinAgeMs(maturity);
714
- if (target === "latest") {
715
- const latestParsed = parseVersion(latest);
716
- if (!latestParsed) {
717
- return void 0;
718
- }
719
- if (!includePrerelease && latestParsed.prerelease !== "") {
720
- return void 0;
721
- }
722
- if (!isNewer(current, latestParsed)) {
723
- return void 0;
724
- }
725
- if (!minAgeMs || !isTooNew(latest, maturity?.publishTimes, minAgeMs)) {
726
- return latest;
727
- }
728
- return filterCandidates(versions, current, includePrerelease, minAgeMs, maturity?.publishTimes);
729
- }
730
- const constraint = target === "patch" ? (p) => p.major === current.major && p.minor === current.minor : (p) => p.major === current.major;
731
- return filterCandidates(versions, current, includePrerelease, minAgeMs, maturity?.publishTimes, constraint);
732
- };
733
- const collectEntries = (catalogs, options) => {
734
- const entries = [];
735
- const ignoredSet = /* @__PURE__ */ new Set();
736
- for (const [catalogName, deps] of catalogs) {
737
- for (const [packageName, range] of deps) {
738
- if (range.startsWith("workspace:") || range.startsWith("file:") || range.startsWith("link:") || range === "*") {
739
- continue;
740
- }
741
- if (!options.includeLocked) {
742
- const prefix = extractPrefix(range);
743
- if (!prefix) {
744
- continue;
745
- }
746
- }
747
- if (options.ignore.some((p) => matchesPattern(packageName, p))) {
748
- ignoredSet.add(packageName);
749
- continue;
750
- }
751
- if (matchesFilters(packageName, options.include, options.exclude)) {
752
- entries.push({ catalogName, packageName, range });
753
- }
754
- }
755
- }
756
- return { entries, ignored: [...ignoredSet] };
757
- };
758
- const fetchVersionsBatched = async (uniquePackages, npmrcConfig, onProgress, fetchPublishTimes = false) => {
759
- const versionCache = /* @__PURE__ */ new Map();
760
- const failed = [];
761
- const concurrency = 8;
762
- let completed = 0;
763
- for (let index = 0; index < uniquePackages.length; index += concurrency) {
764
- const batch = uniquePackages.slice(index, index + concurrency);
765
- const results = await Promise.allSettled(
766
- batch.map(async (name) => {
767
- const registry = npmrcConfig ? getRegistryForPackage(name, npmrcConfig) : void 0;
768
- const info = await fetchPackageVersions(
769
- name,
770
- registry ? { authToken: registry.token, url: registry.url } : void 0,
771
- DEFAULT_FETCH_TIMEOUT,
772
- fetchPublishTimes
773
- );
774
- versionCache.set(name, info);
775
- return name;
776
- })
777
- );
778
- for (const [batchIndex, batchResult] of results.entries()) {
779
- completed += 1;
780
- if (batchResult.status === "rejected") {
781
- const batchPackage = batch[batchIndex];
782
- if (batchPackage) {
783
- failed.push(batchPackage);
784
- }
785
- }
786
- }
787
- if (onProgress) {
788
- onProgress(completed, uniquePackages.length);
789
- }
790
- }
791
- return { failed, versionCache };
792
- };
793
- const buildOutdatedEntries = (entries, versionCache, options) => {
794
- const outdated = [];
795
- for (const entry of entries) {
796
- const info = versionCache.get(entry.packageName);
797
- if (!info) {
798
- continue;
799
- }
800
- const effectiveTarget = resolvePackageTarget(entry.packageName, options.target, options.packageMode);
801
- const targetVersion = findTargetVersion(info.versions, info.latest, entry.range, effectiveTarget, options.includePrerelease, {
802
- minimumReleaseAge: options.minimumReleaseAge,
803
- minimumReleaseAgeExclude: options.minimumReleaseAgeExclude,
804
- packageName: entry.packageName,
805
- publishTimes: info.publishTimes
806
- });
807
- if (!targetVersion) {
808
- continue;
809
- }
810
- const current = parseVersion(entry.range);
811
- const target = parseVersion(targetVersion);
812
- if (!current || !target) {
813
- continue;
814
- }
815
- const updateType = getUpdateType(current, target);
816
- if (updateType === "none") {
817
- continue;
818
- }
819
- const prefix = extractPrefix(entry.range);
820
- outdated.push({
821
- catalogName: entry.catalogName,
822
- currentRange: entry.range,
823
- newRange: `${prefix}${targetVersion}`,
824
- packageName: entry.packageName,
825
- targetVersion,
826
- updateType
827
- });
828
- }
829
- return outdated;
830
- };
831
- const formatVersionString = (parsed) => parsed ? versionToString(parsed) : "";
832
- const enrichWithSecurity = async (outdated, entries, socketOptions, acceptedRisks) => {
833
- const packagesToScan = [
834
- ...new Map(
835
- entries.map((entry) => {
836
- const parsed = parseVersion(entry.range);
837
- return [entry.packageName, { name: entry.packageName, version: formatVersionString(parsed) }];
838
- })
839
- ).values()
840
- ].filter((p) => p.version);
841
- const socketPromise = socketOptions ? fetchSocketReports(packagesToScan, socketOptions) : void 0;
842
- const [vulnMap, socketReports] = await Promise.all([fetchVulnerabilities(packagesToScan), socketPromise]);
843
- for (const entry of outdated) {
844
- const vulns = vulnMap.get(entry.packageName);
845
- if (vulns && vulns.length > 0) {
846
- entry.vulnerabilities = vulns;
847
- }
848
- const parsed = parseVersion(entry.currentRange);
849
- const version = formatVersionString(parsed);
850
- if (socketReports) {
851
- const report = socketReports.get(`${entry.packageName}@${version}`);
852
- if (report) {
853
- entry.socketReport = {
854
- alerts: report.alerts,
855
- license: report.license,
856
- score: report.score
857
- };
858
- }
859
- }
860
- if (acceptedRisks) {
861
- const risk = findAcceptedRisk(entry.packageName, version, acceptedRisks);
862
- if (risk) {
863
- entry.acceptedRisk = risk;
864
- }
865
- }
866
- }
867
- };
868
- const OUTDATED_CACHE_TTL_MS = 6e4;
869
- const computeCacheHash = (catalogs, options, socketEnabled, acceptedRiskKeys) => {
870
- const parts = [];
871
- for (const [name, deps] of catalogs) {
872
- for (const [pkg, range] of deps) {
873
- parts.push(`${name}:${pkg}=${range}`);
874
- }
875
- }
876
- parts.push(`target=${options.target},pre=${String(options.includePrerelease)},sec=${String(options.security ?? false)}`);
877
- parts.push(`in=${options.include.join(",")},ex=${options.exclude.join(",")},ig=${options.ignore.join(",")}`);
878
- parts.push(`locked=${String(options.includeLocked)}`);
879
- parts.push(`mra=${String(options.minimumReleaseAge ?? 0)}`);
880
- if (options.packageMode) {
881
- const modeEntries = Object.entries(options.packageMode).map(([k, v]) => `${k}=${v}`).toSorted().join(",");
882
- parts.push(`pkgMode=${modeEntries}`);
883
- }
884
- parts.push(`socket=${String(socketEnabled ?? false)}`);
885
- if (acceptedRiskKeys && acceptedRiskKeys.length > 0) {
886
- parts.push(`risks=${acceptedRiskKeys.toSorted().join(",")}`);
887
- }
888
- let hash = 5381;
889
- const string_ = parts.join("|");
890
- for (let i = 0; i < string_.length; i++) {
891
- hash = (hash << 5) + hash + string_.charCodeAt(i) | 0;
892
- }
893
- return String(hash);
894
- };
895
- const getOutdatedCachePath = (workspaceRoot) => {
896
- const cacheDir = findCacheDirSync("vis", { create: true, cwd: workspaceRoot });
897
- return cacheDir ? join(cacheDir, "outdated-cache.json") : void 0;
898
- };
899
- const readOutdatedCache = (workspaceRoot, hash) => {
900
- const cachePath = getOutdatedCachePath(workspaceRoot);
901
- if (!cachePath || !isAccessibleSync(cachePath)) {
902
- return void 0;
903
- }
904
- try {
905
- const cache = readJsonSync(cachePath);
906
- if (cache.hash === hash && Date.now() - cache.timestamp < OUTDATED_CACHE_TTL_MS) {
907
- return cache.result;
908
- }
909
- } catch {
910
- }
911
- return void 0;
912
- };
913
- const writeOutdatedCache = (workspaceRoot, hash, result) => {
914
- const cachePath = getOutdatedCachePath(workspaceRoot);
915
- if (!cachePath) {
916
- return;
917
- }
918
- try {
919
- ensureDirSync(dirname(cachePath));
920
- writeJsonSync(cachePath, { hash, result, timestamp: Date.now() });
921
- } catch {
922
- }
923
- };
924
- const checkOutdated = async (catalogs, options, npmrcConfig, onProgress, workspaceRoot, socketOptions, acceptedRisks) => {
925
- const hash = computeCacheHash(catalogs, options, Boolean(socketOptions), acceptedRisks ? Object.keys(acceptedRisks) : void 0);
926
- if (workspaceRoot) {
927
- const cached = readOutdatedCache(workspaceRoot, hash);
928
- if (cached) {
929
- return cached;
930
- }
931
- }
932
- const { entries, ignored } = collectEntries(catalogs, options);
933
- const uniquePackages = [...new Set(entries.map((entry) => entry.packageName))];
934
- const needPublishTimes = Boolean(options.minimumReleaseAge && options.minimumReleaseAge > 0);
935
- const { failed, versionCache } = await fetchVersionsBatched(uniquePackages, npmrcConfig, onProgress, needPublishTimes);
936
- const outdated = buildOutdatedEntries(entries, versionCache, options);
937
- let filteredByTarget = [];
938
- if (options.target !== "latest") {
939
- const outdatedNames = new Set(outdated.map((e) => e.packageName));
940
- const latestOptions = { ...options, packageMode: void 0, target: "latest" };
941
- const allLatest = buildOutdatedEntries(entries, versionCache, latestOptions);
942
- filteredByTarget = allLatest.filter((e) => !outdatedNames.has(e.packageName));
943
- }
944
- if ((options.security || socketOptions) && outdated.length > 0) {
945
- await enrichWithSecurity(outdated, entries, socketOptions, acceptedRisks);
946
- }
947
- const result = { checkedCount: uniquePackages.length, failed, filteredByTarget, ignored, outdated };
948
- if (workspaceRoot) {
949
- writeOutdatedCache(workspaceRoot, hash, result);
950
- }
951
- return result;
952
- };
953
- const getCatalogFilePath = (workspaceRoot, packageManager) => {
954
- if (packageManager === "bun") {
955
- return join(workspaceRoot, "package.json");
956
- }
957
- return join(workspaceRoot, "pnpm-workspace.yaml");
958
- };
959
- const createPackageJsonBackup = (workspaceRoot, updates) => {
960
- const backupDirectory = getBackupDir(workspaceRoot);
961
- const filesToBackup = /* @__PURE__ */ new Set();
962
- for (const update of updates) {
963
- const parsed = parseCompositeCatalogName(update.catalogName);
964
- if (parsed) {
965
- filesToBackup.add(parsed.relativePath === "." ? "package.json" : join(parsed.relativePath, "package.json"));
966
- }
967
- }
968
- if (filesToBackup.size === 0) {
969
- return void 0;
970
- }
971
- ensureDirSync(backupDirectory);
972
- for (const relativePath of filesToBackup) {
973
- const sourcePath = join(workspaceRoot, relativePath);
974
- if (isAccessibleSync(sourcePath)) {
975
- const destinationPath = join(backupDirectory, relativePath);
976
- const destinationDirectory = dirname(destinationPath);
977
- ensureDirSync(destinationDirectory);
978
- writeFileSync(destinationPath, readFileSync(sourcePath));
979
- }
980
- }
981
- return backupDirectory;
982
- };
983
- const createBackup = (workspaceRoot, packageManager, updates) => {
984
- if ((packageManager === "npm" || packageManager === "yarn") && updates) {
985
- return createPackageJsonBackup(workspaceRoot, updates);
986
- }
987
- let catalogBackupPath;
988
- const filePath = getCatalogFilePath(workspaceRoot, packageManager);
989
- if (isAccessibleSync(filePath)) {
990
- catalogBackupPath = `${filePath}.bak`;
991
- writeFileSync(catalogBackupPath, readFileSync(filePath));
992
- }
993
- if (updates) {
994
- const pkgJsonUpdates = updates.filter((u) => parseCompositeCatalogName(u.catalogName));
995
- if (pkgJsonUpdates.length > 0) {
996
- createPackageJsonBackup(workspaceRoot, pkgJsonUpdates);
997
- }
998
- }
999
- return catalogBackupPath;
1000
- };
1001
- const restorePackageJsonBackup = (workspaceRoot) => {
1002
- const backupDirectory = getBackupDir(workspaceRoot);
1003
- if (!isAccessibleSync(backupDirectory)) {
1004
- return false;
1005
- }
1006
- for (const entry of walkSync(backupDirectory, { includeDirs: false })) {
1007
- const relativePath = entry.path.slice(backupDirectory.length + 1);
1008
- const destinationPath = join(workspaceRoot, relativePath);
1009
- writeFileSync(destinationPath, readFileSync(entry.path));
1010
- }
1011
- removeSync(backupDirectory);
1012
- return true;
1013
- };
1014
- const restoreFromBackup = (workspaceRoot, packageManager) => {
1015
- if (packageManager === "npm" || packageManager === "yarn") {
1016
- return restorePackageJsonBackup(workspaceRoot);
1017
- }
1018
- const filePath = getCatalogFilePath(workspaceRoot, packageManager);
1019
- const backupPath = `${filePath}.bak`;
1020
- if (!isAccessibleSync(backupPath)) {
1021
- return false;
1022
- }
1023
- const content = readFileSync(backupPath);
1024
- writeFileSync(filePath, content);
1025
- return true;
1026
- };
1027
- const hasBackup = (workspaceRoot, packageManager) => {
1028
- if (packageManager === "npm" || packageManager === "yarn") {
1029
- try {
1030
- const backupDir = getBackupDir(workspaceRoot);
1031
- return isAccessibleSync(backupDir);
1032
- } catch {
1033
- return false;
1034
- }
1035
- }
1036
- const filePath = getCatalogFilePath(workspaceRoot, packageManager);
1037
- return isAccessibleSync(`${filePath}.bak`);
1038
- };
1039
- const formatOutdatedJson = (result) => JSON.stringify(result, void 0, 2);
1040
- const formatOutdatedMinimal = (outdated) => outdated.map((entry) => `${entry.packageName} ${entry.currentRange} → ${entry.newRange}`).join("\n");
1041
- const toFilterArray = (value) => {
1042
- if (!value) {
1043
- return [];
1044
- }
1045
- return Array.isArray(value) ? value : [value];
1046
- };
1047
- const groupByCatalog = (entries) => {
1048
- const grouped = /* @__PURE__ */ new Map();
1049
- for (const entry of entries) {
1050
- if (!grouped.has(entry.catalogName)) {
1051
- grouped.set(entry.catalogName, []);
1052
- }
1053
- const group = grouped.get(entry.catalogName);
1054
- if (group) {
1055
- group.push(entry);
1056
- }
1057
- }
1058
- return grouped;
1059
- };
1060
- const formatCatalogDisplayName = (catalogName) => {
1061
- const parsed = parseCompositeCatalogName(catalogName);
1062
- if (parsed) {
1063
- const location = parsed.relativePath === "." ? "root" : parsed.relativePath;
1064
- return `${location} (${parsed.depType})`;
1065
- }
1066
- return `Catalog: ${catalogName}`;
1067
- };
1068
- const formatOutdatedTable = (outdated, logger) => {
1069
- const byCatalog = groupByCatalog(outdated);
1070
- const columns = process.stdout.columns || 80;
1071
- const hasSocketData = outdated.some((entry) => entry.socketReport);
1072
- for (const [catalogName, entries] of byCatalog) {
1073
- const tableData = entries.flatMap((entry) => {
1074
- const hasSec = entry.vulnerabilities && entry.vulnerabilities.length > 0;
1075
- const hasSocketAlerts = entry.socketReport && entry.socketReport.alerts.length > 0;
1076
- const prefix = hasSec || hasSocketAlerts ? "[SEC] " : "";
1077
- const displayName2 = `${prefix}${entry.packageName}`;
1078
- const scoreString = entry.socketReport ? `${String(Math.round(entry.socketReport.score.overall * 100))}%` : "";
1079
- const row = {
1080
- current: entry.currentRange,
1081
- package: displayName2,
1082
- target: entry.newRange,
1083
- type: entry.updateType
1084
- };
1085
- if (hasSocketData) {
1086
- row.score = scoreString;
1087
- }
1088
- const rows = [row];
1089
- if (entry.vulnerabilities) {
1090
- for (const vuln of entry.vulnerabilities) {
1091
- const vulnRow = { current: vuln.summary, package: ` ${vuln.severity} ${vuln.id}`, target: "", type: "" };
1092
- if (hasSocketData) {
1093
- vulnRow.score = "";
1094
- }
1095
- rows.push(vulnRow);
1096
- }
1097
- }
1098
- if (entry.socketReport) {
1099
- for (const alert of entry.socketReport.alerts) {
1100
- const alertRow = {
1101
- current: alert.category,
1102
- package: ` [${alert.severity.toUpperCase()}] ${alert.type}`,
1103
- target: "",
1104
- type: ""
1105
- };
1106
- if (hasSocketData) {
1107
- alertRow.score = "";
1108
- }
1109
- rows.push(alertRow);
1110
- }
1111
- }
1112
- return rows;
1113
- });
1114
- const displayName = formatCatalogDisplayName(catalogName);
1115
- const output = renderToString(React.createElement(Table, { data: tableData }), { columns });
1116
- logger.info(`${displayName}
1117
- ${output}
1118
- `);
1119
- }
1120
- };
1121
- const formatSummary = (outdated) => {
1122
- let majors = 0;
1123
- let minors = 0;
1124
- let patches = 0;
1125
- let securityCount = 0;
1126
- let socketAlertCount = 0;
1127
- let lowScoreCount = 0;
1128
- for (const entry of outdated) {
1129
- if (entry.updateType === "major") {
1130
- majors++;
1131
- } else if (entry.updateType === "minor") {
1132
- minors++;
1133
- } else {
1134
- patches++;
1135
- }
1136
- if (entry.vulnerabilities && entry.vulnerabilities.length > 0) {
1137
- securityCount++;
1138
- }
1139
- if (entry.socketReport?.alerts.length) {
1140
- socketAlertCount++;
1141
- }
1142
- if (entry.socketReport && entry.socketReport.score.overall < DEFAULT_LOW_SCORE_THRESHOLD) {
1143
- lowScoreCount++;
1144
- }
1145
- }
1146
- const parts = [];
1147
- if (majors) {
1148
- parts.push(`${String(majors)} major`);
1149
- }
1150
- if (minors) {
1151
- parts.push(`${String(minors)} minor`);
1152
- }
1153
- if (patches) {
1154
- parts.push(`${String(patches)} patch`);
1155
- }
1156
- if (securityCount) {
1157
- parts.push(`${String(securityCount)} with vulnerabilities`);
1158
- }
1159
- if (socketAlertCount) {
1160
- parts.push(`${String(socketAlertCount)} with Socket.dev alerts`);
1161
- }
1162
- const summary = `Found ${String(outdated.length)} outdated (${parts.join(", ")})`;
1163
- const columns = process.stdout.columns || 80;
1164
- const children = [React.createElement(Text, { bold: true }, "─ Summary"), React.createElement(Text, null, ` ${summary}`)];
1165
- if (lowScoreCount > 0) {
1166
- children.push(
1167
- React.createElement(
1168
- Text,
1169
- { color: "yellow" },
1170
- ` ${String(lowScoreCount)} package${lowScoreCount === 1 ? "" : "s"} with low Socket.dev score (<${String(DEFAULT_LOW_SCORE_THRESHOLD * 100)}%)`
1171
- )
1172
- );
1173
- }
1174
- return renderToString(React.createElement(Box, { flexDirection: "column", paddingX: 1 }, ...children), { columns });
1175
- };
1176
- const buildLineMatchRegex = (packageName, range) => {
1177
- const escapedName = packageName.replaceAll(REGEX_SPECIAL_CHARS_REGEX, String.raw`\$&`);
1178
- const escapedRange = range.replaceAll(REGEX_SPECIAL_CHARS_REGEX, String.raw`\$&`);
1179
- return new RegExp(String.raw`^(?:'${escapedName}'|"${escapedName}"|${escapedName}):\s*['"]?${escapedRange}['"]?`);
1180
- };
1181
- const lineMatchesPackage = (trimmed, packageName, range) => {
1182
- const regex = buildLineMatchRegex(packageName, range);
1183
- return regex.test(trimmed);
1184
- };
1185
- const buildUpdateMap = (updates) => {
1186
- const updateMap = /* @__PURE__ */ new Map();
1187
- for (const update of updates) {
1188
- if (!updateMap.has(update.catalogName)) {
1189
- updateMap.set(update.catalogName, /* @__PURE__ */ new Map());
1190
- }
1191
- const catalogMap = updateMap.get(update.catalogName);
1192
- if (catalogMap) {
1193
- catalogMap.set(update.packageName, {
1194
- newRange: update.newRange,
1195
- oldRange: update.currentRange
1196
- });
1197
- }
1198
- }
1199
- return updateMap;
1200
- };
1201
- const applyLineUpdate = (line, trimmed, catalogUpdates) => {
1202
- if (!catalogUpdates) {
1203
- return line;
1204
- }
1205
- for (const [pkgName, { newRange, oldRange }] of catalogUpdates) {
1206
- if (lineMatchesPackage(trimmed, pkgName, oldRange)) {
1207
- return line.replace(oldRange, newRange);
1208
- }
1209
- }
1210
- return line;
1211
- };
1212
- const processYamlLineForUpdate = (line, section, currentCatalogName, updateMap) => {
1213
- const trimmed = line.trimStart();
1214
- const indent = line.length - trimmed.length;
1215
- if (trimmed.length === 0 || trimmed.startsWith("#")) {
1216
- return line;
1217
- }
1218
- if (section === "catalog" && indent >= 2) {
1219
- return applyLineUpdate(line, trimmed, updateMap.get("default"));
1220
- }
1221
- if (section === "catalogs" && indent >= 4 && currentCatalogName) {
1222
- return applyLineUpdate(line, trimmed, updateMap.get(currentCatalogName));
1223
- }
1224
- return line;
1225
- };
1226
- const applyPnpmCatalogUpdates = (workspaceRoot, updates) => {
1227
- const filePath = join(workspaceRoot, "pnpm-workspace.yaml");
1228
- const content = readFileSync(filePath);
1229
- const lines = content.split("\n");
1230
- const updateMap = buildUpdateMap(updates);
1231
- let section = "none";
1232
- let currentCatalogName = "";
1233
- const result = [];
1234
- for (const line of lines) {
1235
- const trimmed = line.trimStart();
1236
- const indent = line.length - trimmed.length;
1237
- if (indent === 0 && trimmed.length > 0 && !trimmed.startsWith("#")) {
1238
- section = detectTopLevelSection(trimmed);
1239
- if (section === "catalogs") {
1240
- currentCatalogName = "";
1241
- }
1242
- }
1243
- if (section === "catalogs" && indent === 2 && trimmed.endsWith(":")) {
1244
- currentCatalogName = trimmed.slice(0, -1).trim().replaceAll(QUOTES_TRIM_REGEX, "");
1245
- }
1246
- result.push(processYamlLineForUpdate(line, section, currentCatalogName, updateMap));
1247
- }
1248
- writeFileSync(filePath, result.join("\n"));
1249
- };
1250
- const applyBunCatalogUpdates = (workspaceRoot, updates) => {
1251
- const filePath = join(workspaceRoot, "package.json");
1252
- const pkg = readJsonSync(filePath);
1253
- for (const update of updates) {
1254
- if (update.catalogName === "default") {
1255
- if (pkg.workspaces?.catalog) {
1256
- pkg.workspaces.catalog[update.packageName] = update.newRange;
1257
- }
1258
- } else {
1259
- const catalogEntry = pkg.workspaces?.catalogs?.[update.catalogName];
1260
- if (catalogEntry) {
1261
- catalogEntry[update.packageName] = update.newRange;
1262
- }
1263
- }
1264
- }
1265
- writeJsonSync(filePath, pkg, { detectIndent: true, overwrite: true });
1266
- };
1267
- const applyCatalogUpdates = (workspaceRoot, updates, packageManager, backup = true) => {
1268
- let backupPath;
1269
- if (backup) {
1270
- backupPath = createBackup(workspaceRoot, packageManager, updates);
1271
- }
1272
- const catalogUpdates = [];
1273
- const pkgJsonUpdates = [];
1274
- for (const update of updates) {
1275
- if (parseCompositeCatalogName(update.catalogName)) {
1276
- pkgJsonUpdates.push(update);
1277
- } else {
1278
- catalogUpdates.push(update);
1279
- }
1280
- }
1281
- if (catalogUpdates.length > 0) {
1282
- if (packageManager === "npm" || packageManager === "yarn") {
1283
- applyPackageJsonUpdates(workspaceRoot, catalogUpdates);
1284
- } else if (packageManager === "bun") {
1285
- applyBunCatalogUpdates(workspaceRoot, catalogUpdates);
1286
- } else {
1287
- applyPnpmCatalogUpdates(workspaceRoot, catalogUpdates);
1288
- }
1289
- }
1290
- if (pkgJsonUpdates.length > 0) {
1291
- applyPackageJsonUpdates(workspaceRoot, pkgJsonUpdates);
1292
- }
1293
- return backupPath;
1294
- };
1295
- const promptPackageSelection = async (outdated) => {
1296
- const rl = createInterface({ input: process.stdin, output: process.stdout });
1297
- const ask = (question) => new Promise((resolve) => {
1298
- rl.question(question, (answer2) => {
1299
- resolve(answer2.trim());
1300
- });
1301
- });
1302
- process.stdout.write("\nOutdated catalog dependencies:\n");
1303
- for (const [index, element] of outdated.entries()) {
1304
- if (!element) {
1305
- continue;
1306
- }
1307
- process.stdout.write(` ${String(index + 1)}. ${element.packageName}: ${element.currentRange} → ${element.newRange} (${element.updateType})
1308
- `);
1309
- }
1310
- process.stdout.write("\n");
1311
- const answer = await ask("Apply updates? [a]ll / [n]one / [s]elect: ");
1312
- if (answer.toLowerCase() === "a" || answer.toLowerCase() === "all") {
1313
- rl.close();
1314
- return outdated;
1315
- }
1316
- if (answer.toLowerCase() === "n" || answer.toLowerCase() === "none") {
1317
- rl.close();
1318
- return [];
1319
- }
1320
- if (answer.toLowerCase() === "s" || answer.toLowerCase() === "select") {
1321
- const selection = await ask("Enter numbers to apply (comma-separated): ");
1322
- rl.close();
1323
- const indices = selection.split(",").map((s) => Number.parseInt(s.trim(), 10) - 1).filter((i) => i >= 0 && i < outdated.length);
1324
- return indices.map((i) => outdated[i]).filter((entry) => entry !== void 0);
1325
- }
1326
- rl.close();
1327
- return [];
1328
- };
1329
- const GITHUB_REPO_REGEX = /github\.com[/:]([\w.-]+)\/([\w.-]+?)(?:\.git|\/|$)/;
1330
- const fetchChangelogInfo = async (packages, timeoutMs = 1e4) => {
1331
- const results = [];
1332
- const controller = new AbortController();
1333
- const timeout = setTimeout(() => {
1334
- controller.abort();
1335
- }, timeoutMs);
1336
- try {
1337
- const fetches = packages.map(async (entry) => {
1338
- const npmUrl = `https://www.npmjs.com/package/${entry.packageName}`;
1339
- try {
1340
- const response = await fetch(`https://registry.npmjs.org/${entry.packageName}`, {
1341
- headers: { Accept: "application/json" },
1342
- signal: controller.signal
1343
- });
1344
- if (!response.ok) {
1345
- return { npmUrl, packageName: entry.packageName };
1346
- }
1347
- const data = await response.json();
1348
- const repoUrl = data.repository?.url;
1349
- if (!repoUrl) {
1350
- return { npmUrl, packageName: entry.packageName };
1351
- }
1352
- const match = GITHUB_REPO_REGEX.exec(repoUrl);
1353
- if (!match) {
1354
- return { npmUrl, packageName: entry.packageName, repoUrl };
1355
- }
1356
- const owner = match[1];
1357
- const repo = match[2];
1358
- const releaseUrl = `https://github.com/${owner}/${repo}/releases/tag/v${entry.targetVersion}`;
1359
- return { npmUrl, packageName: entry.packageName, releaseUrl, repoUrl: `https://github.com/${owner}/${repo}` };
1360
- } catch {
1361
- return { npmUrl, packageName: entry.packageName };
1362
- }
1363
- });
1364
- results.push(...await Promise.all(fetches));
1365
- } finally {
1366
- clearTimeout(timeout);
1367
- }
1368
- return results;
1369
- };
1370
-
1371
- export { fetchVulnerabilities as a, formatSummary as b, checkOutdated as c, formatOutdatedMinimal as d, extractPrefix as e, fetchPackageVersions as f, getUpdateType as g, formatOutdatedTable as h, hasBackup as i, restoreFromBackup as j, hasCatalogs as k, loadNpmrc as l, fetchChangelogInfo as m, promptPackageSelection as n, applyCatalogUpdates as o, parseVersion as p, formatOutdatedJson as q, readCatalogs as r, toFilterArray as t };