@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,975 +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
- execFileSync
22
- } = __cjs_getBuiltinModule("node:child_process");
23
- const {
24
- writeFileSync,
25
- renameSync,
26
- unlinkSync
27
- } = __cjs_getBuiltinModule("node:fs");
28
- import { isAccessibleSync, readFileSync, readJsonSync } from '@visulima/fs';
29
- import { join, delimiter, sep } from '@visulima/path';
30
-
31
- const SUPPORTED_MANAGERS = ["proto", "mise", "fnm", "volta", "asdf", "nvm", "corepack"];
32
- const MANAGER_CAPABILITIES = {
33
- // npm / pnpm / yarn are available via community asdf plugins; users
34
- // who haven't installed those plugins won't have asdf reach for
35
- // them in resolveManagerFor (the manager just won't have a config
36
- // file referencing them), so listing them here is safe and matches
37
- // the "asdf is catch-all" docstring above.
38
- asdf: ["bun", "deno", "go", "node", "npm", "pnpm", "python", "ruby", "rust", "yarn"],
39
- corepack: ["npm", "pnpm", "yarn"],
40
- fnm: ["node"],
41
- mise: ["bun", "deno", "go", "node", "npm", "pnpm", "python", "ruby", "rust", "yarn"],
42
- nvm: ["node"],
43
- proto: ["bun", "deno", "go", "node", "npm", "pnpm", "python", "ruby", "rust", "yarn"],
44
- volta: ["node", "npm", "pnpm", "yarn"]
45
- };
46
- const MANAGER_ORDER = ["proto", "mise", "fnm", "volta", "asdf", "nvm", "corepack"];
47
- const MANAGER_CONFIG_FILES = {
48
- asdf: [".tool-versions"],
49
- // Corepack's config is the `packageManager` field in package.json; we
50
- // detect that separately in `corepackConfigFiles`.
51
- corepack: [],
52
- fnm: [".nvmrc", ".node-version"],
53
- mise: [".mise.toml", ".config/mise.toml", "mise.toml"],
54
- nvm: [".nvmrc"],
55
- proto: [".prototools"],
56
- volta: []
57
- };
58
- const onPathCache = /* @__PURE__ */ new Map();
59
- const isOnPath = (binary) => {
60
- const cached = onPathCache.get(binary);
61
- if (cached !== void 0 || onPathCache.has(binary)) {
62
- return cached;
63
- }
64
- const pathEnv = process.env["PATH"];
65
- if (!pathEnv) {
66
- onPathCache.set(binary, void 0);
67
- return void 0;
68
- }
69
- const isWindows = process.platform === "win32";
70
- const exts = isWindows ? ["", ...(process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD").split(";")] : [""];
71
- for (const rawDir of pathEnv.split(delimiter)) {
72
- const dir = rawDir.replaceAll(/^["']|["']$/g, "").trim();
73
- if (dir === "") {
74
- continue;
75
- }
76
- for (const ext of exts) {
77
- const candidate = `${dir}${sep}${binary}${ext}`;
78
- if (isAccessibleSync(candidate)) {
79
- onPathCache.set(binary, candidate);
80
- return candidate;
81
- }
82
- }
83
- }
84
- onPathCache.set(binary, void 0);
85
- return void 0;
86
- };
87
- const queryManagerVersion = (binary, args = ["--version"]) => {
88
- try {
89
- const output = execFileSync(binary, args, {
90
- encoding: "utf8",
91
- stdio: ["ignore", "pipe", "ignore"],
92
- timeout: 2e3
93
- });
94
- const match = /\d+\.\d+(\.\d+)?/.exec(output);
95
- return match ? match[0] : output.trim() || void 0;
96
- } catch {
97
- return void 0;
98
- }
99
- };
100
- const pkgFieldConfigFiles = (workspaceRoot, field) => {
101
- const pkgPath = join(workspaceRoot, "package.json");
102
- if (!isAccessibleSync(pkgPath)) {
103
- return [];
104
- }
105
- try {
106
- const pkg = readJsonSync(pkgPath);
107
- const value = pkg[field];
108
- if (field === "volta" && typeof value === "object" && value !== null && Object.keys(value).length > 0) {
109
- return ["package.json"];
110
- }
111
- if (field === "packageManager" && typeof value === "string" && value.length > 0) {
112
- return ["package.json"];
113
- }
114
- } catch {
115
- }
116
- return [];
117
- };
118
- const configFilesFor = (name, workspaceRoot) => {
119
- if (name === "volta") {
120
- return pkgFieldConfigFiles(workspaceRoot, "volta");
121
- }
122
- if (name === "corepack") {
123
- return pkgFieldConfigFiles(workspaceRoot, "packageManager");
124
- }
125
- return MANAGER_CONFIG_FILES[name].filter((file) => isAccessibleSync(join(workspaceRoot, file)));
126
- };
127
- const detectionCache = /* @__PURE__ */ new Map();
128
- const clearToolchainCache = () => {
129
- detectionCache.clear();
130
- onPathCache.clear();
131
- };
132
- const findInstalledManagers = (workspaceRoot, options) => {
133
- {
134
- const cached = detectionCache.get(workspaceRoot);
135
- if (cached) {
136
- return cached;
137
- }
138
- }
139
- const results = [];
140
- const pnpmOrYarnOnPath = Boolean(isOnPath("pnpm")) || Boolean(isOnPath("yarn"));
141
- for (const name of MANAGER_ORDER) {
142
- const binary = name === "nvm" ? void 0 : isOnPath(name);
143
- const nvmInstalled = name === "nvm" && Boolean(process.env["NVM_DIR"]);
144
- const configFiles = configFilesFor(name, workspaceRoot);
145
- const installed = Boolean(binary) || nvmInstalled;
146
- if (name === "corepack" && !installed) {
147
- const hasPackageManagerPin = configFiles.length > 0;
148
- if (!hasPackageManagerPin || pnpmOrYarnOnPath) {
149
- continue;
150
- }
151
- }
152
- if (!installed && configFiles.length === 0) {
153
- continue;
154
- }
155
- results.push({
156
- binPath: binary,
157
- configFiles,
158
- installed,
159
- name,
160
- version: binary ? queryManagerVersion(binary) : void 0
161
- });
162
- }
163
- const frozen = Object.freeze(results);
164
- detectionCache.set(workspaceRoot, frozen);
165
- return frozen;
166
- };
167
- const pickPrimaryManager = (workspaceRoot, config, detected) => {
168
- const found = findInstalledManagers(workspaceRoot);
169
- if (config?.preferredManager && config.preferredManager !== "none") {
170
- return found.find((d) => d.name === config.preferredManager) ?? { configFiles: [], installed: false, name: config.preferredManager };
171
- }
172
- return found.find((d) => d.installed && d.configFiles.length > 0) ?? found.find((d) => d.installed) ?? found.find((d) => d.configFiles.length > 0) ?? { configFiles: [], installed: false, name: "none" };
173
- };
174
- const readVersionFile = (workspaceRoot, names) => {
175
- for (const name of names) {
176
- const path = join(workspaceRoot, name);
177
- if (!isAccessibleSync(path)) {
178
- continue;
179
- }
180
- try {
181
- const content = readFileSync(path).trim();
182
- if (content !== "") {
183
- return { name, value: content.replace(/^v/, "") };
184
- }
185
- } catch {
186
- }
187
- }
188
- return void 0;
189
- };
190
- const PROTO_LINE_RE = /^([a-z][\w-]*)\s*=\s*"?([^"\n#]+?)"?\s*(?:#.*)?$/i;
191
- const parsePrototools = (workspaceRoot) => {
192
- const path = join(workspaceRoot, ".prototools");
193
- if (!isAccessibleSync(path)) {
194
- return [];
195
- }
196
- const content = readFileSync(path);
197
- const specs = [];
198
- let inSection = false;
199
- for (const rawLine of content.split(/\r?\n/)) {
200
- const line = rawLine.trim();
201
- if (line === "" || line.startsWith("#")) {
202
- continue;
203
- }
204
- if (line.startsWith("[")) {
205
- inSection = true;
206
- continue;
207
- }
208
- if (inSection) {
209
- continue;
210
- }
211
- const match = PROTO_LINE_RE.exec(line);
212
- if (!match) {
213
- continue;
214
- }
215
- const [, rawTool, version] = match;
216
- const normalizedTool = normalizeToolName(rawTool);
217
- if (normalizedTool) {
218
- specs.push({ source: ".prototools", tool: normalizedTool, version: version.trim() });
219
- }
220
- }
221
- return specs;
222
- };
223
- const MISE_TOOL_HEADER_RE = /^\[tools\]\s*$/i;
224
- const MISE_LINE_RE = /^([a-z][\w-]*)\s*=\s*"?([^"\n#]+?)"?\s*(?:#.*)?$/i;
225
- const parseMiseToml = (workspaceRoot) => {
226
- for (const candidate of MANAGER_CONFIG_FILES.mise) {
227
- const path = join(workspaceRoot, candidate);
228
- if (!isAccessibleSync(path)) {
229
- continue;
230
- }
231
- const specs = [];
232
- const content = readFileSync(path);
233
- let inToolsSection = false;
234
- for (const rawLine of content.split(/\r?\n/)) {
235
- const line = rawLine.trim();
236
- if (line === "" || line.startsWith("#")) {
237
- continue;
238
- }
239
- if (line.startsWith("[")) {
240
- inToolsSection = MISE_TOOL_HEADER_RE.test(line);
241
- continue;
242
- }
243
- if (!inToolsSection) {
244
- continue;
245
- }
246
- const match = MISE_LINE_RE.exec(line);
247
- if (!match) {
248
- continue;
249
- }
250
- const [, rawTool, version] = match;
251
- const normalizedTool = normalizeToolName(rawTool);
252
- if (normalizedTool) {
253
- specs.push({ source: ".mise.toml", tool: normalizedTool, version: version.trim() });
254
- }
255
- }
256
- if (specs.length > 0) {
257
- return specs;
258
- }
259
- }
260
- return [];
261
- };
262
- const parseToolVersions = (workspaceRoot) => {
263
- const path = join(workspaceRoot, ".tool-versions");
264
- if (!isAccessibleSync(path)) {
265
- return [];
266
- }
267
- const content = readFileSync(path);
268
- const specs = [];
269
- for (const rawLine of content.split(/\r?\n/)) {
270
- const line = rawLine.trim();
271
- if (line === "" || line.startsWith("#")) {
272
- continue;
273
- }
274
- const parts = line.split(/\s+/);
275
- if (parts.length < 2) {
276
- continue;
277
- }
278
- const [rawTool, ...versions] = parts;
279
- const normalizedTool = normalizeToolName(rawTool);
280
- if (normalizedTool && versions[0]) {
281
- specs.push({ source: ".tool-versions", tool: normalizedTool, version: versions[0] });
282
- }
283
- }
284
- return specs;
285
- };
286
- const normalizeToolName = (raw) => {
287
- const lower = raw.toLowerCase();
288
- switch (lower) {
289
- case "bun": {
290
- return "bun";
291
- }
292
- case "deno": {
293
- return "deno";
294
- }
295
- case "go":
296
- case "golang": {
297
- return "go";
298
- }
299
- case "node":
300
- case "nodejs": {
301
- return "node";
302
- }
303
- case "npm": {
304
- return "npm";
305
- }
306
- case "pnpm": {
307
- return "pnpm";
308
- }
309
- case "python":
310
- case "python3": {
311
- return "python";
312
- }
313
- case "ruby": {
314
- return "ruby";
315
- }
316
- case "rust":
317
- case "rustc": {
318
- return "rust";
319
- }
320
- case "yarn": {
321
- return "yarn";
322
- }
323
- default: {
324
- return void 0;
325
- }
326
- }
327
- };
328
- const parsePackageManagerField = (field) => {
329
- const [nameAndVersion] = field.split("+", 1);
330
- const match = /^(pnpm|yarn|npm|bun)@(.+)$/.exec(nameAndVersion ?? "");
331
- if (!match) {
332
- return void 0;
333
- }
334
- const normalizedTool = normalizeToolName(match[1]);
335
- if (!normalizedTool) {
336
- return void 0;
337
- }
338
- return { source: "packageManager", tool: normalizedTool, version: match[2] };
339
- };
340
- const parseExpectedTools = (workspaceRoot, config) => {
341
- const merged = /* @__PURE__ */ new Map();
342
- const add = (spec) => {
343
- merged.set(spec.tool, spec);
344
- };
345
- const pkgPath = join(workspaceRoot, "package.json");
346
- let rootPkg = {};
347
- try {
348
- if (isAccessibleSync(pkgPath)) {
349
- rootPkg = readJsonSync(pkgPath);
350
- }
351
- } catch {
352
- }
353
- if (rootPkg.engines) {
354
- for (const [rawTool, version] of Object.entries(rootPkg.engines)) {
355
- const normalizedTool = normalizeToolName(rawTool);
356
- if (normalizedTool && typeof version === "string") {
357
- add({ source: "engines", tool: normalizedTool, version });
358
- }
359
- }
360
- }
361
- if (rootPkg.packageManager) {
362
- const pmSpec = parsePackageManagerField(rootPkg.packageManager);
363
- if (pmSpec) {
364
- add(pmSpec);
365
- }
366
- }
367
- if (rootPkg.volta) {
368
- for (const [rawTool, version] of Object.entries(rootPkg.volta)) {
369
- const normalizedTool = normalizeToolName(rawTool);
370
- if (normalizedTool && typeof version === "string") {
371
- add({ source: "volta", tool: normalizedTool, version });
372
- }
373
- }
374
- }
375
- const nodeFile = readVersionFile(workspaceRoot, [".nvmrc", ".node-version"]);
376
- if (nodeFile) {
377
- add({
378
- source: nodeFile.name === ".nvmrc" ? ".nvmrc" : ".node-version",
379
- tool: "node",
380
- version: nodeFile.value
381
- });
382
- }
383
- for (const spec of parseToolVersions(workspaceRoot)) {
384
- add(spec);
385
- }
386
- for (const spec of parseMiseToml(workspaceRoot)) {
387
- add(spec);
388
- }
389
- for (const spec of parsePrototools(workspaceRoot)) {
390
- add(spec);
391
- }
392
- if (config?.tools) {
393
- for (const [rawTool, version] of Object.entries(config.tools)) {
394
- const normalizedTool = normalizeToolName(rawTool);
395
- if (normalizedTool && typeof version === "string") {
396
- add({ source: "vis.config.ts", tool: normalizedTool, version });
397
- }
398
- }
399
- }
400
- return [...merged.values()];
401
- };
402
- const TOOL_VERSION_QUERY = {
403
- bun: { args: ["--version"], binaries: ["bun"] },
404
- deno: { args: ["--version"], binaries: ["deno"] },
405
- go: { args: ["version"], binaries: ["go"] },
406
- node: { args: ["--version"], binaries: ["node"] },
407
- npm: { args: ["--version"], binaries: ["npm"] },
408
- pnpm: { args: ["--version"], binaries: ["pnpm"] },
409
- python: { args: ["--version"], binaries: ["python", "python3"] },
410
- ruby: { args: ["--version"], binaries: ["ruby"] },
411
- rust: { args: ["--version"], binaries: ["rustc"] },
412
- yarn: { args: ["--version"], binaries: ["yarn"] }
413
- };
414
- const queryToolVersion = (tool) => {
415
- const lookup = TOOL_VERSION_QUERY[tool];
416
- for (const candidate of lookup.binaries) {
417
- const binary = isOnPath(candidate);
418
- if (binary) {
419
- return queryManagerVersion(binary, lookup.args);
420
- }
421
- }
422
- if (tool === "node") {
423
- return process.versions.node;
424
- }
425
- return void 0;
426
- };
427
- const satisfies = (actual, range) => {
428
- const normalized = range.trim();
429
- if (normalized === "" || normalized === "*" || normalized === "latest") {
430
- return true;
431
- }
432
- if (/^\d[\d.]*$/.test(normalized)) {
433
- return actual === normalized || actual.startsWith(`${normalized}.`);
434
- }
435
- const parse = (version) => version.split(/[.\-+]/).map((part) => Number.parseInt(part, 10) || 0);
436
- const compare = (a, b) => {
437
- const aParts = parse(a);
438
- const bParts = parse(b);
439
- const len = Math.max(aParts.length, bParts.length);
440
- for (let index = 0; index < len; index++) {
441
- const ai = aParts[index] ?? 0;
442
- const bi = bParts[index] ?? 0;
443
- if (ai !== bi) {
444
- return ai - bi;
445
- }
446
- }
447
- return 0;
448
- };
449
- const matchesAll = (clauses) => {
450
- for (const clause of clauses) {
451
- if (clause.startsWith(">=")) {
452
- if (compare(actual, clause.slice(2).trim()) < 0) {
453
- return false;
454
- }
455
- } else if (clause.startsWith("<=")) {
456
- if (compare(actual, clause.slice(2).trim()) > 0) {
457
- return false;
458
- }
459
- } else if (clause.startsWith(">")) {
460
- if (compare(actual, clause.slice(1).trim()) <= 0) {
461
- return false;
462
- }
463
- } else if (clause.startsWith("<")) {
464
- if (compare(actual, clause.slice(1).trim()) >= 0) {
465
- return false;
466
- }
467
- } else if (clause.startsWith("^") || clause.startsWith("~")) {
468
- const target = clause.slice(1).trim();
469
- const [targetMajor, targetMinor] = parse(target);
470
- const [actualMajor, actualMinor] = parse(actual);
471
- if (actualMajor !== targetMajor) {
472
- return false;
473
- }
474
- if (clause.startsWith("~") && actualMinor !== targetMinor) {
475
- return false;
476
- }
477
- if (compare(actual, target) < 0) {
478
- return false;
479
- }
480
- }
481
- }
482
- return true;
483
- };
484
- const alternatives = normalized.split("||").map((alternative) => alternative.trim().split(/\s+/).filter(Boolean)).filter((clauses) => clauses.length > 0);
485
- if (alternatives.length === 0) {
486
- return true;
487
- }
488
- return alternatives.some((clauses) => matchesAll(clauses));
489
- };
490
- const preferenceFor = (source, tool) => {
491
- switch (source) {
492
- case ".mise.toml": {
493
- return ["mise"];
494
- }
495
- case ".node-version":
496
- case ".nvmrc": {
497
- return ["fnm", "nvm", "volta", "proto", "mise", "asdf"];
498
- }
499
- case ".prototools": {
500
- return ["proto"];
501
- }
502
- case ".tool-versions": {
503
- return ["asdf", "mise"];
504
- }
505
- case "packageManager": {
506
- if (tool === "pnpm" || tool === "yarn") {
507
- return ["self-activate", "volta", "proto", "mise", "corepack"];
508
- }
509
- if (tool === "npm") {
510
- return ["volta", "proto", "mise", "asdf", "corepack"];
511
- }
512
- if (tool === "bun") {
513
- return ["proto", "mise", "asdf"];
514
- }
515
- return ["volta", "proto", "mise"];
516
- }
517
- case "volta": {
518
- return ["volta"];
519
- }
520
- // Catch-all pins (`engines`, `vis.config.ts`, anything else) —
521
- // walk every capable manager.
522
- default: {
523
- return ["proto", "mise", "fnm", "volta", "asdf", "nvm", "corepack"];
524
- }
525
- }
526
- };
527
- const resolveManagerFor = (spec, detected, config) => {
528
- if (config?.preferredManager && config.preferredManager !== "none" && canHandle(config.preferredManager, spec.tool)) {
529
- const override = detected.find((d) => d.name === config.preferredManager);
530
- return override ? { installed: override.installed, name: override.name } : {
531
- installed: false,
532
- name: config.preferredManager,
533
- note: `${config.preferredManager} is the preferred manager but isn't on PATH`
534
- };
535
- }
536
- const preference = preferenceFor(spec.source, spec.tool);
537
- for (const name of preference) {
538
- if (name === "self-activate") {
539
- if ((spec.tool === "pnpm" || spec.tool === "yarn") && isOnPath(spec.tool)) {
540
- return {
541
- installed: true,
542
- name: "self-activate",
543
- note: `${spec.tool} will activate ${spec.version} from the packageManager field on next invocation`
544
- };
545
- }
546
- continue;
547
- }
548
- if (!canHandle(name, spec.tool)) {
549
- continue;
550
- }
551
- const match = detected.find((d) => d.name === name);
552
- if (match?.installed) {
553
- return { installed: true, name };
554
- }
555
- }
556
- for (const name of preference) {
557
- if (name === "self-activate" || !canHandle(name, spec.tool)) {
558
- continue;
559
- }
560
- return { installed: false, name, note: `${name} can install ${spec.tool} — run \`vis toolchain install\` after adding it to PATH` };
561
- }
562
- return { installed: false, name: "none", note: "No manager knows how to install this tool" };
563
- };
564
- const canHandle = (name, tool) => {
565
- if (name === "none") {
566
- return false;
567
- }
568
- if (name === "self-activate") {
569
- return tool === "pnpm" || tool === "yarn";
570
- }
571
- return MANAGER_CAPABILITIES[name].includes(tool);
572
- };
573
- const getToolchainStatus = (workspaceRoot, config) => {
574
- const detected = findInstalledManagers(workspaceRoot);
575
- const expectedTools = parseExpectedTools(workspaceRoot, config);
576
- const tools = expectedTools.map((expected) => {
577
- const actual = queryToolVersion(expected.tool);
578
- const matches = actual !== void 0 && satisfies(actual, expected.version);
579
- const manager = resolveManagerFor(expected, detected, config);
580
- return { actual, expected, manager, matches };
581
- });
582
- return { detected, tools };
583
- };
584
- const buildInstallInvocation = (manager, spec) => {
585
- switch (manager) {
586
- case "asdf": {
587
- if (!spec) {
588
- return void 0;
589
- }
590
- return { args: ["install", spec.tool, spec.version], bin: "asdf" };
591
- }
592
- case "corepack": {
593
- if (!spec) {
594
- return { args: ["prepare", "--activate"], bin: "corepack", hint: "reads the packageManager field in package.json" };
595
- }
596
- return {
597
- args: ["prepare", `${spec.tool}@${spec.version}`, "--activate"],
598
- bin: "corepack"
599
- };
600
- }
601
- case "fnm": {
602
- if (spec?.tool !== "node") {
603
- return void 0;
604
- }
605
- return { args: ["install", spec.version], bin: "fnm" };
606
- }
607
- case "mise": {
608
- if (!spec) {
609
- return void 0;
610
- }
611
- return { args: ["install", `${spec.tool}@${spec.version}`], bin: "mise" };
612
- }
613
- case "none": {
614
- return void 0;
615
- }
616
- case "nvm": {
617
- return {
618
- args: [],
619
- bin: "nvm",
620
- hint: "nvm is a shell function — run `nvm install` / `nvm use` from your shell"
621
- };
622
- }
623
- case "proto": {
624
- if (!spec) {
625
- return void 0;
626
- }
627
- return { args: ["install", spec.tool, spec.version], bin: "proto" };
628
- }
629
- case "self-activate": {
630
- return {
631
- args: [],
632
- bin: spec?.tool ?? "pnpm",
633
- hint: `${spec?.tool ?? "pnpm"} will self-activate on next invocation — no install needed`
634
- };
635
- }
636
- case "volta": {
637
- if (!spec) {
638
- return { args: ["install", "node@lts"], bin: "volta", hint: "volta pins per-tool; specify <tool>@<version>" };
639
- }
640
- return { args: ["install", `${spec.tool}@${spec.version}`], bin: "volta" };
641
- }
642
- default: {
643
- const exhaustive = manager;
644
- throw new Error(`Unknown manager: ${exhaustive}`);
645
- }
646
- }
647
- };
648
- const buildUseInvocation = (manager, spec) => {
649
- switch (manager) {
650
- case "asdf": {
651
- return {
652
- args: ["local", spec.tool, spec.version],
653
- bin: "asdf",
654
- configChange: { file: ".tool-versions", hint: `Pins ${spec.tool} ${spec.version}` }
655
- };
656
- }
657
- case "corepack": {
658
- if (spec.tool !== "npm" && spec.tool !== "pnpm" && spec.tool !== "yarn") {
659
- return void 0;
660
- }
661
- return {
662
- args: ["use", `${spec.tool}@${spec.version}`],
663
- bin: "corepack",
664
- configChange: { file: "package.json", hint: `Writes packageManager: "${spec.tool}@${spec.version}"` }
665
- };
666
- }
667
- case "fnm": {
668
- if (spec.tool === "node") {
669
- return { args: ["use", spec.version], bin: "fnm" };
670
- }
671
- return void 0;
672
- }
673
- case "mise": {
674
- return {
675
- args: ["use", "--", `${spec.tool}@${spec.version}`],
676
- bin: "mise",
677
- configChange: { file: ".mise.toml", hint: `Pins ${spec.tool} ${spec.version}` }
678
- };
679
- }
680
- case "none": {
681
- return void 0;
682
- }
683
- case "nvm": {
684
- if (spec.tool === "node") {
685
- return {
686
- args: [],
687
- bin: "nvm",
688
- configChange: { file: ".nvmrc", hint: "Write version to .nvmrc manually (nvm doesn't persist)." }
689
- };
690
- }
691
- return void 0;
692
- }
693
- case "proto": {
694
- return {
695
- args: ["pin", spec.tool, spec.version],
696
- bin: "proto",
697
- configChange: { file: ".prototools", hint: `Pins ${spec.tool} ${spec.version}` }
698
- };
699
- }
700
- case "self-activate": {
701
- return {
702
- args: [],
703
- bin: spec.tool,
704
- configChange: {
705
- file: "package.json",
706
- hint: `Set packageManager: "${spec.tool}@${spec.version}" — ${spec.tool} will self-activate on next invocation`
707
- }
708
- };
709
- }
710
- case "volta": {
711
- return {
712
- args: ["pin", `${spec.tool}@${spec.version}`],
713
- bin: "volta",
714
- configChange: { file: "package.json", hint: `Writes volta.${spec.tool}` }
715
- };
716
- }
717
- default: {
718
- const exhaustive = manager;
719
- throw new Error(`Unknown manager: ${exhaustive}`);
720
- }
721
- }
722
- };
723
- const findOnPathByAlias = (tool) => {
724
- for (const alias of TOOL_VERSION_QUERY[tool].binaries) {
725
- const binary = isOnPath(alias);
726
- if (binary) {
727
- return binary;
728
- }
729
- }
730
- return void 0;
731
- };
732
- const resolveToolBinary = (manager, tool) => {
733
- const aliases = TOOL_VERSION_QUERY[tool].binaries;
734
- if (manager.installed && manager.binPath && // proto/mise/asdf expose `which`; volta has `volta which`; fnm
735
- // prints to stdout from `fnm which`. Try each alias in order so
736
- // `mise which rust` (which doesn't know "rust") falls back to
737
- // `mise which rustc`.
738
- (manager.name === "proto" || manager.name === "mise" || manager.name === "asdf" || manager.name === "volta" || manager.name === "fnm")) {
739
- for (const alias of aliases) {
740
- try {
741
- const output = execFileSync(manager.binPath, ["which", alias], {
742
- encoding: "utf8",
743
- stdio: ["ignore", "pipe", "ignore"],
744
- timeout: 2e3
745
- });
746
- const trimmed = output.trim();
747
- if (trimmed) {
748
- return trimmed;
749
- }
750
- } catch {
751
- }
752
- }
753
- }
754
- for (const alias of aliases) {
755
- const binary = isOnPath(alias);
756
- if (binary) {
757
- return binary;
758
- }
759
- }
760
- return void 0;
761
- };
762
- const atomicWrite = (path, body) => {
763
- const tmp = `${path}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
764
- writeFileSync(tmp, body);
765
- try {
766
- renameSync(tmp, path);
767
- } catch (error) {
768
- try {
769
- unlinkSync(tmp);
770
- } catch {
771
- }
772
- throw error;
773
- }
774
- };
775
- const insertPackageManagerKey = (pkg, value) => {
776
- if ("packageManager" in pkg) {
777
- pkg.packageManager = value;
778
- return pkg;
779
- }
780
- const ordered = {};
781
- let inserted = false;
782
- for (const [key, fieldValue] of Object.entries(pkg)) {
783
- if (!inserted && (key === "dependencies" || key === "devDependencies" || key === "peerDependencies" || key === "optionalDependencies")) {
784
- ordered.packageManager = value;
785
- inserted = true;
786
- }
787
- ordered[key] = fieldValue;
788
- }
789
- if (!inserted) {
790
- ordered.packageManager = value;
791
- }
792
- return ordered;
793
- };
794
- const writePackageManagerField = (workspaceRoot, spec) => {
795
- if (spec.tool !== "pnpm" && spec.tool !== "yarn" && spec.tool !== "npm" && spec.tool !== "bun") {
796
- return void 0;
797
- }
798
- const pkgPath = join(workspaceRoot, "package.json");
799
- if (!isAccessibleSync(pkgPath)) {
800
- throw new Error(`Cannot pin ${spec.tool}: ${pkgPath} does not exist.`);
801
- }
802
- const raw = readFileSync(pkgPath);
803
- const indentMatch = /\n([ \t]+)/.exec(raw);
804
- const indent = indentMatch?.[1] ?? " ";
805
- let pkg;
806
- try {
807
- pkg = JSON.parse(raw);
808
- } catch (error) {
809
- throw new Error(`${pkgPath} is not valid JSON — fix it before running \`vis toolchain use\`. Underlying error: ${error.message}`);
810
- }
811
- const value = `${spec.tool}@${spec.version}`;
812
- const updated = insertPackageManagerKey(pkg, value);
813
- const trailingNewline = raw.endsWith("\n") ? "\n" : "";
814
- atomicWrite(pkgPath, `${JSON.stringify(updated, void 0, indent)}${trailingNewline}`);
815
- return value;
816
- };
817
- const updateEnginesField = (workspaceRoot, spec) => {
818
- const pkgPath = join(workspaceRoot, "package.json");
819
- if (!isAccessibleSync(pkgPath)) {
820
- return void 0;
821
- }
822
- const raw = readFileSync(pkgPath);
823
- let pkg;
824
- try {
825
- pkg = JSON.parse(raw);
826
- } catch (error) {
827
- throw new Error(`${pkgPath} is not valid JSON — fix it before running \`vis toolchain use\`. Underlying error: ${error.message}`);
828
- }
829
- if (pkg.engines?.[spec.tool] === void 0) {
830
- return void 0;
831
- }
832
- if (pkg.engines[spec.tool] === spec.version) {
833
- return void 0;
834
- }
835
- pkg.engines[spec.tool] = spec.version;
836
- const indentMatch = /\n([ \t]+)/.exec(raw);
837
- const indent = indentMatch?.[1] ?? " ";
838
- const trailingNewline = raw.endsWith("\n") ? "\n" : "";
839
- atomicWrite(pkgPath, `${JSON.stringify(pkg, void 0, indent)}${trailingNewline}`);
840
- return spec.version;
841
- };
842
- const parseUseArgument = (raw) => {
843
- const match = /^([a-z][\w-]*)@(.+)$/i.exec(raw.trim());
844
- if (!match) {
845
- return void 0;
846
- }
847
- const normalizedTool = normalizeToolName(match[1]);
848
- if (!normalizedTool) {
849
- return void 0;
850
- }
851
- return { source: "vis.config.ts", tool: normalizedTool, version: match[2] };
852
- };
853
- const ensureToolchain = async (workspaceRoot, config, logger) => {
854
- const status = getToolchainStatus(workspaceRoot, config);
855
- const mismatches = status.tools.filter((t) => !t.matches);
856
- if (mismatches.length === 0) {
857
- return { attempted: [], failed: [], upToDate: true };
858
- }
859
- const hasManager = status.detected.some((d) => d.installed);
860
- const autoInstall = config?.autoInstall ?? hasManager;
861
- if (!autoInstall) {
862
- return { attempted: [], failed: [], upToDate: false };
863
- }
864
- const attempted = [];
865
- const failed = [];
866
- const byManager = /* @__PURE__ */ new Map();
867
- for (const tool of mismatches) {
868
- const bucket = byManager.get(tool.manager.name);
869
- if (bucket) {
870
- bucket.push(tool);
871
- } else {
872
- byManager.set(tool.manager.name, [tool]);
873
- }
874
- }
875
- for (const [managerName, tools] of byManager) {
876
- if (managerName === "self-activate") {
877
- for (const { expected } of tools) {
878
- logger.info(`toolchain: ${expected.tool} ${expected.version} will self-activate on next ${expected.tool} invocation`);
879
- attempted.push(expected);
880
- }
881
- continue;
882
- }
883
- if (managerName === "none") {
884
- for (const { expected } of tools) {
885
- failed.push({
886
- error: `no manager can install ${expected.tool} — install one of ${SUPPORTED_MANAGERS.join(", ")}`,
887
- spec: expected
888
- });
889
- }
890
- continue;
891
- }
892
- const manager = status.detected.find((d) => d.name === managerName);
893
- if (!manager?.installed) {
894
- for (const { expected } of tools) {
895
- failed.push({ error: `${managerName} is not on PATH`, spec: expected });
896
- }
897
- continue;
898
- }
899
- const pairs = tools.map((tool) => {
900
- return { invocation: buildInstallInvocation(managerName, tool.expected), tool };
901
- }).filter((pair) => pair.invocation !== void 0);
902
- for (const { invocation, tool } of pairs) {
903
- const { expected } = tool;
904
- if (invocation.bin === "nvm" && invocation.args.length === 0) {
905
- logger.warn(
906
- `toolchain: nvm requires a shell-side activation for ${expected.tool} ${expected.version}. Run \`nvm install\` / \`nvm use\` manually.`
907
- );
908
- failed.push({ error: "nvm requires shell-side activation", spec: expected });
909
- continue;
910
- }
911
- logger.info(`toolchain: $ ${invocation.bin} ${invocation.args.join(" ")}`);
912
- try {
913
- execFileSync(invocation.bin, invocation.args, {
914
- cwd: workspaceRoot,
915
- stdio: "inherit"
916
- });
917
- attempted.push(expected);
918
- if (managerName === "fnm") {
919
- activateFnmEnv(invocation.bin, logger);
920
- }
921
- } catch (error) {
922
- failed.push({ error: error.message, spec: expected });
923
- break;
924
- }
925
- }
926
- }
927
- clearToolchainCache();
928
- return { attempted, failed, upToDate: false };
929
- };
930
- const runToolchainPreflight = async (workspaceRoot, config, logger, skip = false) => {
931
- if (skip) {
932
- return;
933
- }
934
- const result = await ensureToolchain(workspaceRoot, config, logger);
935
- for (const failure of result.failed) {
936
- logger.warn(`toolchain: ${failure.spec.tool} ${failure.spec.version} — ${failure.error}`);
937
- }
938
- };
939
- const activateFnmEnv = (fnmBin, logger) => {
940
- const shell = process.platform === "win32" ? "powershell" : "bash";
941
- try {
942
- const output = execFileSync(fnmBin, ["env", "--shell", shell], {
943
- encoding: "utf8",
944
- stdio: ["ignore", "pipe", "ignore"],
945
- timeout: 2e3
946
- });
947
- for (const line of output.split(/\r?\n/)) {
948
- const trimmed = line.trim();
949
- if (trimmed === "") {
950
- continue;
951
- }
952
- const psMatch = /^\$env:([A-Z_]\w*)\s*=\s*(.+)$/i.exec(trimmed);
953
- if (psMatch) {
954
- const [, name, rawValue] = psMatch;
955
- process.env[name] = rawValue.replaceAll(/^["']|["']$/g, "");
956
- continue;
957
- }
958
- const cmdMatch = /^set\s+"?([A-Z_]\w*)=(.*?)"?$/i.exec(trimmed);
959
- if (cmdMatch) {
960
- const [, name, value] = cmdMatch;
961
- process.env[name] = value;
962
- continue;
963
- }
964
- const bashMatch = /^(?:export\s+)?([A-Z_]\w*)=(.+)$/i.exec(trimmed);
965
- if (bashMatch) {
966
- const [, name, rawValue] = bashMatch;
967
- process.env[name] = rawValue.replaceAll(/^["']|["']$/g, "");
968
- }
969
- }
970
- } catch (error) {
971
- logger.warn(`toolchain: could not activate fnm env (${error.message}). Subsequent tasks may use the previous Node version.`);
972
- }
973
- };
974
-
975
- export { SUPPORTED_MANAGERS as S, resolveManagerFor as a, resolveToolBinary as b, findOnPathByAlias as c, buildUseInvocation as d, buildInstallInvocation as e, findInstalledManagers as f, getToolchainStatus as g, pickPrimaryManager as h, parseUseArgument as p, runToolchainPreflight as r, updateEnginesField as u, writePackageManagerField as w };