node-version-use 2.4.0 → 2.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +61 -84
  2. package/assets/bin/node +0 -0
  3. package/assets/installBinaries.cjs +95 -18
  4. package/assets/postinstall.cjs +16 -1
  5. package/dist/cjs/assets/installBinaries.cjs +95 -18
  6. package/dist/cjs/assets/installBinaries.cjs.map +1 -1
  7. package/dist/cjs/assets/postinstall.cjs +16 -1
  8. package/dist/cjs/assets/postinstall.cjs.map +1 -1
  9. package/dist/cjs/cli.js.map +1 -1
  10. package/dist/cjs/commands/default.js +30 -4
  11. package/dist/cjs/commands/default.js.map +1 -1
  12. package/dist/cjs/commands/index.js.map +1 -1
  13. package/dist/cjs/commands/install.js.map +1 -1
  14. package/dist/cjs/commands/list.js.map +1 -1
  15. package/dist/cjs/commands/local.js.map +1 -1
  16. package/dist/cjs/commands/setup.d.cts +2 -3
  17. package/dist/cjs/commands/setup.d.ts +2 -3
  18. package/dist/cjs/commands/setup.js +9 -84
  19. package/dist/cjs/commands/setup.js.map +1 -1
  20. package/dist/cjs/commands/teardown.js.map +1 -1
  21. package/dist/cjs/commands/uninstall.js.map +1 -1
  22. package/dist/cjs/commands/which.js +15 -0
  23. package/dist/cjs/commands/which.js.map +1 -1
  24. package/dist/cjs/compat.d.cts +1 -0
  25. package/dist/cjs/compat.d.ts +1 -0
  26. package/dist/cjs/compat.js +17 -3
  27. package/dist/cjs/compat.js.map +1 -1
  28. package/dist/cjs/constants.js.map +1 -1
  29. package/dist/cjs/index.js.map +1 -1
  30. package/dist/cjs/lib/findInstalledVersions.js.map +1 -1
  31. package/dist/cjs/lib/loadNodeVersionInstall.js +1 -1
  32. package/dist/cjs/lib/loadNodeVersionInstall.js.map +1 -1
  33. package/dist/cjs/lib/resolveSystemBinary.d.cts +10 -0
  34. package/dist/cjs/lib/resolveSystemBinary.d.ts +10 -0
  35. package/dist/cjs/lib/resolveSystemBinary.js +96 -0
  36. package/dist/cjs/lib/resolveSystemBinary.js.map +1 -0
  37. package/dist/cjs/worker.js +53 -56
  38. package/dist/cjs/worker.js.map +1 -1
  39. package/dist/esm/assets/installBinaries.cjs +78 -18
  40. package/dist/esm/assets/installBinaries.cjs.map +1 -1
  41. package/dist/esm/assets/postinstall.cjs +16 -1
  42. package/dist/esm/assets/postinstall.cjs.map +1 -1
  43. package/dist/esm/cli.js.map +1 -1
  44. package/dist/esm/commands/default.js +30 -4
  45. package/dist/esm/commands/default.js.map +1 -1
  46. package/dist/esm/commands/index.js.map +1 -1
  47. package/dist/esm/commands/install.js.map +1 -1
  48. package/dist/esm/commands/list.js.map +1 -1
  49. package/dist/esm/commands/local.js.map +1 -1
  50. package/dist/esm/commands/setup.d.ts +2 -3
  51. package/dist/esm/commands/setup.js +9 -84
  52. package/dist/esm/commands/setup.js.map +1 -1
  53. package/dist/esm/commands/teardown.js.map +1 -1
  54. package/dist/esm/commands/uninstall.js.map +1 -1
  55. package/dist/esm/commands/which.js +15 -0
  56. package/dist/esm/commands/which.js.map +1 -1
  57. package/dist/esm/compat.d.ts +1 -0
  58. package/dist/esm/compat.js +16 -5
  59. package/dist/esm/compat.js.map +1 -1
  60. package/dist/esm/constants.js.map +1 -1
  61. package/dist/esm/index.js.map +1 -1
  62. package/dist/esm/lib/findInstalledVersions.js.map +1 -1
  63. package/dist/esm/lib/loadNodeVersionInstall.js +1 -1
  64. package/dist/esm/lib/loadNodeVersionInstall.js.map +1 -1
  65. package/dist/esm/lib/resolveSystemBinary.d.ts +10 -0
  66. package/dist/esm/lib/resolveSystemBinary.js +78 -0
  67. package/dist/esm/lib/resolveSystemBinary.js.map +1 -0
  68. package/dist/esm/types.js.map +1 -1
  69. package/dist/esm/worker.js +58 -11
  70. package/dist/esm/worker.js.map +1 -1
  71. package/package.json +3 -3
package/README.md CHANGED
@@ -1,140 +1,117 @@
1
1
  ## node-version-use
2
2
 
3
- Cross-platform solution for using multiple versions of Node.js. Useful for compatibility testing and transparent version switching.
3
+ Cross-platform solution for using multiple versions of Node.js. Transparent version switching via command interception.
4
4
 
5
5
  ### Installation
6
6
 
7
7
  ```bash
8
8
  npm install -g node-version-use
9
+ export PATH="$HOME/.nvu/bin:$PATH" # Add to shell profile
9
10
  ```
10
11
 
11
- On install, nvu automatically downloads platform-specific binaries to `~/.nvu/bin/`. To enable transparent Node version switching, add to your shell profile:
12
+ ### Quick Start
12
13
 
13
14
  ```bash
14
- # For bash (~/.bashrc) or zsh (~/.zshrc):
15
- export PATH="$HOME/.nvu/bin:$PATH"
16
-
17
- # For fish (~/.config/fish/config.fish):
18
- set -gx PATH $HOME/.nvu/bin $PATH
15
+ nvu default 20 # Set global default
16
+ nvu local 18 # Set project version (.nvmrc)
17
+ node --version # Uses v20 (or v18 in project)
19
18
  ```
20
19
 
21
- ### Quick Start
20
+ ### Commands
22
21
 
23
- ```bash
24
- # Set a global default Node version
25
- nvu default 20
22
+ ```
23
+ nvu default 20 # Global default
24
+ nvu default system # Use system Node
25
+ nvu local 18 # Project version (.nvmrc)
26
+ nvu install 22 # Install Node
27
+ nvu uninstall 22 # Uninstall Node
28
+ nvu list # List installed
29
+ nvu 22 npm run test # Run with specific version
30
+ ```
26
31
 
27
- # Or set a project-specific version (creates .nvmrc)
28
- nvu local 18
32
+ ### How It Works
29
33
 
30
- # Now 'node' and 'npm' use the configured version automatically
31
- node --version # v20.x.x (or v18.x.x in project directory)
34
+ ```
35
+ ~/.nvu/bin/ # Go binary shims (node, npm, npx, nvu, ...)
36
+
37
+ ~/.nvu/default # Contains "22", "20", or "system"
38
+
39
+ ~/.nvu/installed/v22/ # Real Node.js installation
40
+ └── bin/node # Actual Node binary
32
41
  ```
33
42
 
34
- ### Commands
35
-
36
- #### Version Management
43
+ **Key design decisions:**
37
44
 
38
- ```bash
39
- # Set global default version
40
- nvu default 20
45
+ 1. **Strict routing** - Each command routes to exactly one version (the default)
46
+ 2. **npm compatibility** - Uses `npm_config_prefix` so npm behaves normally
47
+ 3. **System escape hatch** - `nvu system npm ...` bypasses version routing
48
+ 4. **Version-specific packages** - Global npm packages live in the version's directory
41
49
 
42
- # Set local version for current directory (creates .nvmrc)
43
- nvu local 18
50
+ ### Frequently Asked Questions
44
51
 
45
- # Install a Node version
46
- nvu install 22
52
+ #### How do I reinstall nvu if it's missing?
47
53
 
48
- # Uninstall a Node version
49
- nvu uninstall 22
54
+ ```bash
55
+ nvu system npm install -g node-version-use
56
+ ```
50
57
 
51
- # List installed versions
52
- nvu list
58
+ This bypasses version routing entirely.
53
59
 
54
- # Show which version would be used
55
- nvu which
56
- ```
60
+ #### Are global npm packages shared across versions?
57
61
 
58
- #### Binary Management
62
+ No. Each Node version has its own `lib/node_modules/`. Install separately:
59
63
 
60
64
  ```bash
61
- # Install/reinstall binaries
62
- nvu setup
63
-
64
- # Remove binaries
65
- nvu teardown
65
+ nvu 22 npm install -g some-package
66
+ nvu 20 npm install -g some-package
66
67
  ```
67
68
 
68
- #### Run Commands with Specific Versions
69
+ #### Can I use system Node?
69
70
 
70
71
  ```bash
71
- # Run with a specific version
72
- nvu 14.4.0 npm run test
73
-
74
- # Run with highest matching major version
75
- nvu 12 npm run test
72
+ nvu default system
73
+ ```
76
74
 
77
- # Run with LTS version
78
- nvu lts npm run test
75
+ Routes all commands to system binaries via PATH.
79
76
 
80
- # Run with multiple versions (comma-delimited)
81
- nvu 0.8,4,8,14 npm run test
77
+ #### Why not search all installed versions for binaries?
82
78
 
83
- # Run with version expression
84
- nvu >=0.8 node --version
79
+ Explicit is better than implicit. You know exactly which version runs. Use `nvu <version> <command>` for specific versions.
85
80
 
86
- # Use engines.node from package.json
87
- nvu engines node --version
88
- ```
81
+ ### How nvu Differs from Other Version Managers
89
82
 
90
- ### How It Works
83
+ | Feature | nvu | nvm | Volta |
84
+ |---------|-----|-----|-------|
85
+ | Command routing | Go binary shim | Shell function | npm shim |
86
+ | Default version | Global or per-project | Global | Per-project (package.json) |
87
+ | Global packages | Version-specific | Shared (via symlinks) | Pin to version |
88
+ | System Node | `nvu default system` | `nvm use system` | `volta off` |
89
+ | Recovery when broken | `nvu system npm ...` | Reinstall nvm | Reinstall volta |
91
90
 
92
- nvu uses lightweight binaries that intercept `node`, `npm`, and `npx` commands. When you run `node`, the binary:
91
+ **nvu** uses a single Go binary that intercepts commands. Simple, predictable routing.
93
92
 
94
- 1. Checks for `.nvurc` in current/parent directories
95
- 2. Checks for `.nvmrc` in current/parent directories
96
- 3. Falls back to `~/.nvu/default`
97
- 4. Executes the matching installed Node version
93
+ **nvm** is a shell function that changes `$NODE_HOME` environment variable.
98
94
 
99
- This works in all contexts: terminals, IDEs, CI, scripts - without shell integration.
100
-
101
- ### Version Resolution
102
-
103
- The binary resolves partial versions to installed versions:
104
- - `20` matches `v20.19.6`
105
- - `18.19` matches `v18.19.0`
106
- - `v22` matches `v22.0.0`
95
+ **Volta** pins packages to specific Node versions in package.json and uses npm shims.
107
96
 
108
97
  ### JavaScript API
109
98
 
110
99
  ```javascript
111
100
  const nvu = require('node-version-use');
112
-
113
- // Run with callback
114
- nvu('>=0.8', 'node', ['--version'], { versions: '12', stdio: 'inherit' }, (err, results) => {
115
- // results is an array per-version of form {version, error, result}
116
- });
117
-
118
- // Run with async/await
119
- const results = await nvu('engines', 'node', ['--version'], { stdio: 'inherit' });
101
+ const results = await nvu('>=0.8', 'node', ['--version'], { stdio: 'inherit' });
120
102
  ```
121
103
 
122
- ### Uninstalling
104
+ ### Uninstall
123
105
 
124
106
  ```bash
125
- # Remove binaries
126
- nvu teardown
127
-
128
- # Optionally remove all nvu data
129
- rm -rf ~/.nvu
107
+ nvu teardown # Remove ~/.nvu/bin
108
+ rm -rf ~/.nvu # Remove all data
130
109
  ```
131
110
 
132
- Then remove the PATH line from your shell profile.
133
-
134
111
  ### Compatibility
135
112
 
136
113
  - macOS (arm64, x64)
137
114
  - Linux (arm64, x64)
138
115
  - Windows (arm64, x64)
139
116
 
140
- Compatible with `.nvmrc` files from nvm, fnm, and other version managers.
117
+ Compatible with `.nvmrc` files from nvm, fnm, and other tools.
Binary file
@@ -37,21 +37,24 @@ function removeIfExistsSync(filePath) {
37
37
  }
38
38
  }
39
39
  /**
40
- * On Windows, rename a file out of the way instead of deleting.
41
- * This works even if the file is currently running.
40
+ * Move a file out of the way (works even if running on Windows)
41
+ * First tries to unlink; if that fails (Windows locked), rename to .old-timestamp
42
42
  */ function moveOutOfWay(filePath) {
43
43
  if (!fs.existsSync(filePath)) return;
44
+ // First try to unlink (works on Unix, fails on Windows if running)
45
+ try {
46
+ fs.unlinkSync(filePath);
47
+ return;
48
+ } catch (_e) {
49
+ // Unlink failed (likely Windows locked file), try rename
50
+ }
51
+ // Rename to .old-timestamp as fallback
44
52
  var timestamp = Date.now();
45
53
  var oldPath = "".concat(filePath, ".old-").concat(timestamp);
46
54
  try {
47
55
  fs.renameSync(filePath, oldPath);
48
- } catch (_e) {
49
- // If rename fails, try delete as fallback (works on Unix)
50
- try {
51
- fs.unlinkSync(filePath);
52
- } catch (_e2) {
53
- // ignore - will fail on atomic rename instead
54
- }
56
+ } catch (_e2) {
57
+ // Both unlink and rename failed - will fail on atomic rename instead
55
58
  }
56
59
  }
57
60
  /**
@@ -114,6 +117,55 @@ function removeIfExistsSync(filePath) {
114
117
  var content = fs.readFileSync(src);
115
118
  fs.writeFileSync(dest, content);
116
119
  }
120
+ /**
121
+ * Sync all shims by copying the nvu binary to all other files in the bin directory
122
+ * All shims (node, npm, npx, corepack, eslint, etc.) are copies of the same binary
123
+ */ module.exports.syncAllShims = function syncAllShims(binDir) {
124
+ var isWindows = process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE);
125
+ var ext = isWindows ? '.exe' : '';
126
+ // Source: nvu binary
127
+ var nvuSource = path.join(binDir, "nvu".concat(ext));
128
+ if (!fs.existsSync(nvuSource)) return;
129
+ try {
130
+ var entries = fs.readdirSync(binDir);
131
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
132
+ try {
133
+ for(var _iterator = entries[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
134
+ var name = _step.value;
135
+ // Skip nvu itself and nvu.json
136
+ if (name === "nvu".concat(ext) || name === 'nvu.json') continue;
137
+ // On Windows, only process .exe files
138
+ if (isWindows && !name.endsWith('.exe')) continue;
139
+ var shimPath = path.join(binDir, name);
140
+ var stat = fs.statSync(shimPath);
141
+ if (!stat.isFile()) continue;
142
+ // Move existing file out of the way (Windows compatibility)
143
+ moveOutOfWay(shimPath);
144
+ // Copy nvu binary to shim
145
+ copyFileSync(nvuSource, shimPath);
146
+ // Make executable on Unix
147
+ if (!isWindows) {
148
+ fs.chmodSync(shimPath, 493);
149
+ }
150
+ }
151
+ } catch (err) {
152
+ _didIteratorError = true;
153
+ _iteratorError = err;
154
+ } finally{
155
+ try {
156
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
157
+ _iterator.return();
158
+ }
159
+ } finally{
160
+ if (_didIteratorError) {
161
+ throw _iteratorError;
162
+ }
163
+ }
164
+ }
165
+ } catch (_e) {
166
+ // Ignore errors - shim sync is best effort
167
+ }
168
+ };
117
169
  /**
118
170
  * Atomic rename with fallback to copy+delete for cross-device moves
119
171
  */ function atomicRename(src, dest, callback) {
@@ -296,38 +348,63 @@ function removeIfExistsSync(filePath) {
296
348
  }
297
349
  var extractedBinaryName = "".concat(archiveBaseName).concat(isWindows ? '.exe' : '');
298
350
  var binDir = path.join(storagePath, 'bin');
351
+ var nvuJsonPath = path.join(binDir, 'nvu.json');
299
352
  // check if we need to upgrade
300
353
  if (!options.force) {
301
354
  try {
302
- // already installed
303
- if (fs.statSync(binDir)) {
304
- if (fs.readFileSync(path.join(binDir, 'version.txt'), 'utf8') === BINARY_VERSION) {
305
- callback(null, false);
306
- return;
307
- }
355
+ // already installed - read nvu.json
356
+ var nvuJson = JSON.parse(fs.readFileSync(nvuJsonPath, 'utf8'));
357
+ if (nvuJson.binaryVersion === BINARY_VERSION) {
358
+ callback(null, false);
359
+ return;
308
360
  }
309
361
  } catch (_err) {}
310
362
  }
311
363
  // Create directories
312
364
  mkdirp.sync(storagePath);
313
365
  mkdirp.sync(binDir);
366
+ mkdirp.sync(path.join(storagePath, 'cache'));
314
367
  // Clean up old .old-* files from previous installs
315
368
  cleanupOldFiles(binDir);
316
369
  var downloadUrl = "https://github.com/".concat(GITHUB_REPO, "/releases/download/binary-v").concat(BINARY_VERSION, "/").concat(archiveBaseName).concat(isWindows ? '.zip' : '.tar.gz');
317
- var tempPath = path.join(tmpdir(), "nvu-binary-".concat(Date.now()).concat(isWindows ? '.zip' : '.tar.gz'));
370
+ var cachePath = path.join(storagePath, 'cache', "".concat(archiveBaseName).concat(isWindows ? '.zip' : '.tar.gz'));
371
+ // Check cache first
372
+ if (fs.existsSync(cachePath)) {
373
+ console.log('Using cached binary...');
374
+ // Use cached file
375
+ extractAndInstall(cachePath, binDir, extractedBinaryName, function(err) {
376
+ if (err) return callback(err);
377
+ // save binary version for upgrade checks
378
+ fs.writeFileSync(nvuJsonPath, JSON.stringify({
379
+ binaryVersion: BINARY_VERSION
380
+ }, null, 2), 'utf8');
381
+ console.log('Binary installed successfully!');
382
+ callback(null, true);
383
+ });
384
+ return;
385
+ }
386
+ // Download to temp file
318
387
  console.log("Downloading binary for ".concat(process.platform, "-").concat(process.arch, "..."));
388
+ var tempPath = path.join(tmpdir(), "nvu-binary-".concat(Date.now()).concat(isWindows ? '.zip' : '.tar.gz'));
319
389
  getFile(downloadUrl, tempPath, function(err) {
320
390
  if (err) {
321
391
  removeIfExistsSync(tempPath);
322
392
  callback(new Error("No prebuilt binary available for ".concat(process.platform, "-").concat(process.arch, ". Download: ").concat(downloadUrl, ". Error: ").concat(err.message)));
323
393
  return;
324
394
  }
325
- console.log('Extracting binary...');
395
+ // Copy to cache for future use
396
+ try {
397
+ copyFileSync(tempPath, cachePath);
398
+ } catch (_e) {
399
+ // Cache write failed, continue anyway
400
+ }
326
401
  extractAndInstall(tempPath, binDir, extractedBinaryName, function(err) {
327
402
  removeIfExistsSync(tempPath);
328
403
  if (err) return callback(err);
329
404
  // save binary version for upgrade checks
330
- fs.writeFileSync(path.join(binDir, 'version.txt'), BINARY_VERSION, 'utf8');
405
+ fs.writeFileSync(nvuJsonPath, JSON.stringify({
406
+ binaryVersion: BINARY_VERSION
407
+ }, null, 2), 'utf8');
331
408
  console.log('Binary installed successfully!');
332
409
  callback(null, true);
333
410
  });
@@ -10,7 +10,17 @@
10
10
  * 2. Extract to temp directory
11
11
  * 3. Atomic rename to final location
12
12
  */ var exit = require('exit-compat');
13
- var _require = require('./installBinaries.cjs'), installBinaries = _require.installBinaries, printInstructions = _require.printInstructions;
13
+ var path = require('path');
14
+ var os = require('os');
15
+ var _require = require('./installBinaries.cjs'), installBinaries = _require.installBinaries, printInstructions = _require.printInstructions, syncAllShims = _require.syncAllShims;
16
+ var hasHomedir = typeof os.homedir === 'function';
17
+ function homedir() {
18
+ if (hasHomedir) return os.homedir();
19
+ var home = require('homedir-polyfill');
20
+ return home();
21
+ }
22
+ // Allow NVU_HOME override for testing
23
+ var storagePath = process.env.NVU_HOME || path.join(homedir(), '.nvu');
14
24
  /**
15
25
  * Main installation function
16
26
  */ function main() {
@@ -22,8 +32,13 @@ var _require = require('./installBinaries.cjs'), installBinaries = _require.inst
22
32
  return;
23
33
  }
24
34
  if (installed) {
35
+ // Sync all shims to the new binary version
36
+ var binDir = path.join(storagePath, 'bin');
37
+ syncAllShims(binDir);
25
38
  printInstructions();
26
39
  console.log('postinstall: Binary installed successfully!');
40
+ } else {
41
+ console.log('postinstall: Binaries already up to date.');
27
42
  }
28
43
  exit(0);
29
44
  });
@@ -37,21 +37,24 @@ function removeIfExistsSync(filePath) {
37
37
  }
38
38
  }
39
39
  /**
40
- * On Windows, rename a file out of the way instead of deleting.
41
- * This works even if the file is currently running.
40
+ * Move a file out of the way (works even if running on Windows)
41
+ * First tries to unlink; if that fails (Windows locked), rename to .old-timestamp
42
42
  */ function moveOutOfWay(filePath) {
43
43
  if (!fs.existsSync(filePath)) return;
44
+ // First try to unlink (works on Unix, fails on Windows if running)
45
+ try {
46
+ fs.unlinkSync(filePath);
47
+ return;
48
+ } catch (_e) {
49
+ // Unlink failed (likely Windows locked file), try rename
50
+ }
51
+ // Rename to .old-timestamp as fallback
44
52
  var timestamp = Date.now();
45
53
  var oldPath = "".concat(filePath, ".old-").concat(timestamp);
46
54
  try {
47
55
  fs.renameSync(filePath, oldPath);
48
- } catch (_e) {
49
- // If rename fails, try delete as fallback (works on Unix)
50
- try {
51
- fs.unlinkSync(filePath);
52
- } catch (_e2) {
53
- // ignore - will fail on atomic rename instead
54
- }
56
+ } catch (_e2) {
57
+ // Both unlink and rename failed - will fail on atomic rename instead
55
58
  }
56
59
  }
57
60
  /**
@@ -114,6 +117,55 @@ function removeIfExistsSync(filePath) {
114
117
  var content = fs.readFileSync(src);
115
118
  fs.writeFileSync(dest, content);
116
119
  }
120
+ /**
121
+ * Sync all shims by copying the nvu binary to all other files in the bin directory
122
+ * All shims (node, npm, npx, corepack, eslint, etc.) are copies of the same binary
123
+ */ module.exports.syncAllShims = function syncAllShims(binDir) {
124
+ var isWindows = process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE);
125
+ var ext = isWindows ? '.exe' : '';
126
+ // Source: nvu binary
127
+ var nvuSource = path.join(binDir, "nvu".concat(ext));
128
+ if (!fs.existsSync(nvuSource)) return;
129
+ try {
130
+ var entries = fs.readdirSync(binDir);
131
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
132
+ try {
133
+ for(var _iterator = entries[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
134
+ var name = _step.value;
135
+ // Skip nvu itself and nvu.json
136
+ if (name === "nvu".concat(ext) || name === 'nvu.json') continue;
137
+ // On Windows, only process .exe files
138
+ if (isWindows && !name.endsWith('.exe')) continue;
139
+ var shimPath = path.join(binDir, name);
140
+ var stat = fs.statSync(shimPath);
141
+ if (!stat.isFile()) continue;
142
+ // Move existing file out of the way (Windows compatibility)
143
+ moveOutOfWay(shimPath);
144
+ // Copy nvu binary to shim
145
+ copyFileSync(nvuSource, shimPath);
146
+ // Make executable on Unix
147
+ if (!isWindows) {
148
+ fs.chmodSync(shimPath, 493);
149
+ }
150
+ }
151
+ } catch (err) {
152
+ _didIteratorError = true;
153
+ _iteratorError = err;
154
+ } finally{
155
+ try {
156
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
157
+ _iterator.return();
158
+ }
159
+ } finally{
160
+ if (_didIteratorError) {
161
+ throw _iteratorError;
162
+ }
163
+ }
164
+ }
165
+ } catch (_e) {
166
+ // Ignore errors - shim sync is best effort
167
+ }
168
+ };
117
169
  /**
118
170
  * Atomic rename with fallback to copy+delete for cross-device moves
119
171
  */ function atomicRename(src, dest, callback) {
@@ -296,38 +348,63 @@ function removeIfExistsSync(filePath) {
296
348
  }
297
349
  var extractedBinaryName = "".concat(archiveBaseName).concat(isWindows ? '.exe' : '');
298
350
  var binDir = path.join(storagePath, 'bin');
351
+ var nvuJsonPath = path.join(binDir, 'nvu.json');
299
352
  // check if we need to upgrade
300
353
  if (!options.force) {
301
354
  try {
302
- // already installed
303
- if (fs.statSync(binDir)) {
304
- if (fs.readFileSync(path.join(binDir, 'version.txt'), 'utf8') === BINARY_VERSION) {
305
- callback(null, false);
306
- return;
307
- }
355
+ // already installed - read nvu.json
356
+ var nvuJson = JSON.parse(fs.readFileSync(nvuJsonPath, 'utf8'));
357
+ if (nvuJson.binaryVersion === BINARY_VERSION) {
358
+ callback(null, false);
359
+ return;
308
360
  }
309
361
  } catch (_err) {}
310
362
  }
311
363
  // Create directories
312
364
  mkdirp.sync(storagePath);
313
365
  mkdirp.sync(binDir);
366
+ mkdirp.sync(path.join(storagePath, 'cache'));
314
367
  // Clean up old .old-* files from previous installs
315
368
  cleanupOldFiles(binDir);
316
369
  var downloadUrl = "https://github.com/".concat(GITHUB_REPO, "/releases/download/binary-v").concat(BINARY_VERSION, "/").concat(archiveBaseName).concat(isWindows ? '.zip' : '.tar.gz');
317
- var tempPath = path.join(tmpdir(), "nvu-binary-".concat(Date.now()).concat(isWindows ? '.zip' : '.tar.gz'));
370
+ var cachePath = path.join(storagePath, 'cache', "".concat(archiveBaseName).concat(isWindows ? '.zip' : '.tar.gz'));
371
+ // Check cache first
372
+ if (fs.existsSync(cachePath)) {
373
+ console.log('Using cached binary...');
374
+ // Use cached file
375
+ extractAndInstall(cachePath, binDir, extractedBinaryName, function(err) {
376
+ if (err) return callback(err);
377
+ // save binary version for upgrade checks
378
+ fs.writeFileSync(nvuJsonPath, JSON.stringify({
379
+ binaryVersion: BINARY_VERSION
380
+ }, null, 2), 'utf8');
381
+ console.log('Binary installed successfully!');
382
+ callback(null, true);
383
+ });
384
+ return;
385
+ }
386
+ // Download to temp file
318
387
  console.log("Downloading binary for ".concat(process.platform, "-").concat(process.arch, "..."));
388
+ var tempPath = path.join(tmpdir(), "nvu-binary-".concat(Date.now()).concat(isWindows ? '.zip' : '.tar.gz'));
319
389
  getFile(downloadUrl, tempPath, function(err) {
320
390
  if (err) {
321
391
  removeIfExistsSync(tempPath);
322
392
  callback(new Error("No prebuilt binary available for ".concat(process.platform, "-").concat(process.arch, ". Download: ").concat(downloadUrl, ". Error: ").concat(err.message)));
323
393
  return;
324
394
  }
325
- console.log('Extracting binary...');
395
+ // Copy to cache for future use
396
+ try {
397
+ copyFileSync(tempPath, cachePath);
398
+ } catch (_e) {
399
+ // Cache write failed, continue anyway
400
+ }
326
401
  extractAndInstall(tempPath, binDir, extractedBinaryName, function(err) {
327
402
  removeIfExistsSync(tempPath);
328
403
  if (err) return callback(err);
329
404
  // save binary version for upgrade checks
330
- fs.writeFileSync(path.join(binDir, 'version.txt'), BINARY_VERSION, 'utf8');
405
+ fs.writeFileSync(nvuJsonPath, JSON.stringify({
406
+ binaryVersion: BINARY_VERSION
407
+ }, null, 2), 'utf8');
331
408
  console.log('Binary installed successfully!');
332
409
  callback(null, true);
333
410
  });