@adhdev/ghostty-vt-node 1.0.46 → 1.0.77

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 (28) hide show
  1. package/package.json +1 -1
  2. package/prebuilt/darwin-arm64-node115/ghostty_vt_node.node +0 -0
  3. package/prebuilt/darwin-arm64-node115/libghostty-vt.0.1.0.dylib +0 -0
  4. package/prebuilt/darwin-arm64-node115/libghostty-vt.dylib +0 -0
  5. package/prebuilt/darwin-arm64-node127/ghostty_vt_node.node +0 -0
  6. package/prebuilt/darwin-arm64-node127/libghostty-vt.0.1.0.dylib +0 -0
  7. package/prebuilt/darwin-arm64-node127/libghostty-vt.dylib +0 -0
  8. package/prebuilt/darwin-arm64-node137/libghostty-vt.0.1.0.dylib +0 -0
  9. package/prebuilt/darwin-arm64-node137/libghostty-vt.dylib +0 -0
  10. package/prebuilt/darwin-arm64-node141/ghostty_vt_node.node +0 -0
  11. package/prebuilt/darwin-arm64-node141/libghostty-vt.0.1.0.dylib +0 -0
  12. package/prebuilt/darwin-arm64-node141/libghostty-vt.dylib +0 -0
  13. package/prebuilt/linux-x64-node115/ghostty_vt_node.node +0 -0
  14. package/prebuilt/linux-x64-node115/libghostty-vt.so +0 -0
  15. package/prebuilt/linux-x64-node115/libghostty-vt.so.0.1.0 +0 -0
  16. package/prebuilt/linux-x64-node127/ghostty_vt_node.node +0 -0
  17. package/prebuilt/linux-x64-node127/libghostty-vt.so +0 -0
  18. package/prebuilt/linux-x64-node127/libghostty-vt.so.0.1.0 +0 -0
  19. package/prebuilt/linux-x64-node137/libghostty-vt.so +0 -0
  20. package/prebuilt/linux-x64-node137/libghostty-vt.so.0.1.0 +0 -0
  21. package/prebuilt/linux-x64-node141/ghostty_vt_node.node +0 -0
  22. package/prebuilt/{linux-x64-node137/libghostty-vt.so.0 → linux-x64-node141/libghostty-vt.so} +0 -0
  23. package/prebuilt/linux-x64-node141/libghostty-vt.so.0.1.0 +0 -0
  24. package/prebuilt/win32-x64-node127/ghostty-vt.dll +0 -0
  25. package/prebuilt/win32-x64-node127/ghostty_vt_node.node +0 -0
  26. package/scripts/build-or-use-prebuilt.mjs +95 -12
  27. package/prebuilt/win32-x64-node115/ghostty-vt.dll +0 -0
  28. package/prebuilt/win32-x64-node115/ghostty_vt_node.node +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/ghostty-vt-node",
3
- "version": "1.0.46",
3
+ "version": "1.0.77",
4
4
  "description": "Native Node binding for libghostty-vt",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -27,24 +27,105 @@ function latestSourceMtime() {
27
27
  return sourceInputs.reduce((latest, filePath) => Math.max(latest, safeMtime(filePath)), 0);
28
28
  }
29
29
 
30
+ const PREBUILT_ROOT = path.join(packageDir, 'prebuilt');
31
+ const ADDON_FILE = 'ghostty_vt_node.node';
32
+ // The addon plus its co-located runtime libs (.dylib/.so/.dll) — the addon
33
+ // resolves libghostty-vt next to itself, so it must travel with it.
34
+ const RUNTIME_FILE_PATTERN = /\.(node|dylib|so|so\.\d.*|dll)$/;
35
+
36
+ // The addon is a pure Node-API binding, so every same `platform-arch`
37
+ // prebuilt is loadable regardless of the `nodeNNN` ABI in its directory name
38
+ // (see index.js, which falls back the same way at runtime). Exact ABI first,
39
+ // then the other same-platform-arch directories, newest ABI first. Accepting
40
+ // only the exact triplet sent e.g. a Node 22 (ABI 127) fresh clone — where
41
+ // only `*-node137` is committed — into a cmake/zig source compile.
42
+ function samePlatformArchTriplets() {
43
+ const prefix = `${process.platform}-${process.arch}-node`;
44
+ let names = [];
45
+ try {
46
+ names = fs.readdirSync(PREBUILT_ROOT, { withFileTypes: true })
47
+ .filter((entry) => entry.isDirectory() && entry.name.startsWith(prefix) && entry.name !== triplet)
48
+ .map((entry) => entry.name);
49
+ } catch {
50
+ return [];
51
+ }
52
+ const abi = (name) => Number.parseInt(name.slice(prefix.length), 10) || 0;
53
+ return names.sort((a, b) => abi(b) - abi(a));
54
+ }
55
+
30
56
  function candidatePrebuiltPaths() {
31
57
  const candidates = [];
32
58
  const explicitDir = process.env.ADHDEV_GHOSTTY_VT_PREBUILT_DIR?.trim();
33
59
  if (explicitDir) {
34
- candidates.push(path.join(explicitDir, triplet, 'ghostty_vt_node.node'));
35
- candidates.push(path.join(explicitDir, 'ghostty_vt_node.node'));
60
+ candidates.push({ file: path.join(explicitDir, triplet, ADDON_FILE), explicit: true });
61
+ candidates.push({ file: path.join(explicitDir, ADDON_FILE), explicit: true });
62
+ }
63
+ for (const name of [triplet, ...samePlatformArchTriplets()]) {
64
+ candidates.push({ file: path.join(PREBUILT_ROOT, name, ADDON_FILE), explicit: false });
36
65
  }
37
- candidates.push(path.join(packageDir, 'prebuilt', triplet, 'ghostty_vt_node.node'));
38
66
  return candidates;
39
67
  }
40
68
 
41
- function installPrebuiltIfPresent(minMtime = 0) {
69
+ function git(args) {
70
+ const result = spawnSync('git', ['-C', packageDir, ...args], { encoding: 'utf8', windowsHide: true });
71
+ if (result.error || result.status === null) return null;
72
+ return { status: result.status, stdout: result.stdout || '' };
73
+ }
74
+
75
+ // Staleness of a candidate prebuilt relative to the sources it was built from.
76
+ //
77
+ // The mtime guard only means something for files this machine PRODUCED (a
78
+ // local compile, or a prebuilt emitted from one): their mtimes order
79
+ // genuinely. A git-tracked prebuilt's mtime is just checkout order — in a
80
+ // fresh clone a valid prebuilt often looks "older" than addon.cc and was
81
+ // skipped, and the build then died for lack of cmake. For a tracked prebuilt
82
+ // the question that matters is whether the SOURCES were edited locally since
83
+ // the committed state; only then is the committed binary out of date.
84
+ let sourcesLocallyModifiedMemo;
85
+ function sourcesLocallyModified() {
86
+ if (sourcesLocallyModifiedMemo !== undefined) return sourcesLocallyModifiedMemo;
87
+ const rels = sourceInputs.map((filePath) => path.relative(packageDir, filePath));
88
+ const status = git(['status', '--porcelain', '--', ...rels]);
89
+ sourcesLocallyModifiedMemo = !!status && status.status === 0 && status.stdout.trim().length > 0;
90
+ return sourcesLocallyModifiedMemo;
91
+ }
92
+
93
+ function isGitTracked(filePath) {
94
+ const result = git(['ls-files', '--error-unmatch', '--', path.relative(packageDir, filePath)]);
95
+ return !!result && result.status === 0;
96
+ }
97
+
98
+ function isUsablePrebuilt(candidate, sourceMtime) {
99
+ // An explicit override dir is the operator's choice — trust it.
100
+ if (candidate.explicit) return true;
101
+ if (isGitTracked(candidate.file)) return !sourcesLocallyModified();
102
+ // Not tracked: either a locally emitted prebuilt (mtime is meaningful) or an
103
+ // npm-installed package with no git at all, where the shipped sources and
104
+ // prebuilts are pristine together. Without git metadata there is nothing
105
+ // local to be stale against, so only a working tree applies the guard.
106
+ const insideWorkTree = git(['rev-parse', '--is-inside-work-tree']);
107
+ if (!insideWorkTree || insideWorkTree.status !== 0) return true;
108
+ return safeMtime(candidate.file) >= sourceMtime;
109
+ }
110
+
111
+ function installPrebuilt(candidateFile) {
112
+ fs.mkdirSync(outputDir, { recursive: true });
113
+ const sourceDir = path.dirname(candidateFile);
114
+ for (const entry of fs.readdirSync(sourceDir)) {
115
+ if (RUNTIME_FILE_PATTERN.test(entry)) fs.copyFileSync(path.join(sourceDir, entry), path.join(outputDir, entry));
116
+ }
117
+ if (!fs.existsSync(outputFile)) fs.copyFileSync(candidateFile, outputFile);
118
+ }
119
+
120
+ function installPrebuiltIfPresent(sourceMtime) {
42
121
  for (const candidate of candidatePrebuiltPaths()) {
43
- if (!fs.existsSync(candidate)) continue;
44
- if (safeMtime(candidate) < minMtime) continue;
45
- fs.mkdirSync(outputDir, { recursive: true });
46
- fs.copyFileSync(candidate, outputFile);
47
- console.log(`[ghostty-vt-node] using prebuilt native binding from ${candidate}`);
122
+ if (!fs.existsSync(candidate.file)) continue;
123
+ if (!isUsablePrebuilt(candidate, sourceMtime)) {
124
+ console.log(`[ghostty-vt-node] skipping prebuilt ${candidate.file}: sources were modified locally after it was built`);
125
+ continue;
126
+ }
127
+ installPrebuilt(candidate.file);
128
+ console.log(`[ghostty-vt-node] using prebuilt native binding from ${candidate.file}`);
48
129
  return true;
49
130
  }
50
131
  return false;
@@ -53,17 +134,19 @@ function installPrebuiltIfPresent(minMtime = 0) {
53
134
  const sourceMtime = latestSourceMtime();
54
135
  const outputMtime = safeMtime(outputFile);
55
136
 
137
+ // A local build output is something this machine compiled, so its mtime
138
+ // against the sources is meaningful: keep it while it is not older.
56
139
  if (outputMtime >= sourceMtime && outputMtime > 0) {
57
140
  console.log(`[ghostty-vt-node] keeping existing local build at ${outputFile}`);
58
141
  process.exit(0);
59
142
  }
60
143
 
61
- if (!installPrebuiltIfPresent(sourceMtime) && process.env.ADHDEV_SKIP_GHOSTTY_VT_BUILD === '1') {
62
- console.log(`[ghostty-vt-node] skipping native build for ${triplet} (ADHDEV_SKIP_GHOSTTY_VT_BUILD=1)`);
144
+ if (installPrebuiltIfPresent(sourceMtime)) {
63
145
  process.exit(0);
64
146
  }
65
147
 
66
- if (installPrebuiltIfPresent(sourceMtime)) {
148
+ if (process.env.ADHDEV_SKIP_GHOSTTY_VT_BUILD === '1') {
149
+ console.log(`[ghostty-vt-node] skipping native build for ${triplet} (ADHDEV_SKIP_GHOSTTY_VT_BUILD=1)`);
67
150
  process.exit(0);
68
151
  }
69
152