@grafema/util 0.3.14 → 0.3.16

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.
@@ -29,12 +29,15 @@ export declare function findInGrafemaBin(binaryName: string): string | null;
29
29
  */
30
30
  export declare function downloadBinary(binaryName: string, tag?: string, onProgress?: (msg: string) => void): Promise<string>;
31
31
  /**
32
- * Ensure a binary exists, downloading it if missing and eligible.
32
+ * Ensure a binary exists and is current, downloading it if missing or stale.
33
33
  *
34
34
  * This is the main entry point for lazy downloading. Call it before
35
35
  * spawning an analyzer binary. Returns the path if found or downloaded,
36
36
  * null if the binary can't be obtained.
37
37
  *
38
+ * Staleness check: if a binary exists in ~/.grafema/bin/ but its .version
39
+ * file doesn't match the current Grafema release, it is re-downloaded.
40
+ *
38
41
  * @param binaryName - e.g. "grafema-analyzer"
39
42
  * @param existingPath - path from findBinary() (null = not found locally)
40
43
  * @param onProgress - optional callback for download progress
@@ -1 +1 @@
1
- {"version":3,"file":"lazyDownload.d.ts","sourceRoot":"","sources":["../../src/utils/lazyDownload.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAwDH;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAGzC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIlE;AA6CD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GACjC,OAAO,CAAC,MAAM,CAAC,CAgDjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GACjC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmBxB"}
1
+ {"version":3,"file":"lazyDownload.d.ts","sourceRoot":"","sources":["../../src/utils/lazyDownload.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAyDH;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAGzC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIlE;AA6CD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GACjC,OAAO,CAAC,MAAM,CAAC,CAmDjB;AAuCD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GACjC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA8BxB"}
@@ -7,10 +7,11 @@
7
7
  * Asset naming convention: {binary-name}-{platform}
8
8
  * Example: grafema-analyzer-darwin-arm64
9
9
  */
10
- import { existsSync, mkdirSync, chmodSync, renameSync, unlinkSync, createWriteStream } from 'fs';
10
+ import { existsSync, mkdirSync, chmodSync, renameSync, unlinkSync, createWriteStream, readFileSync, writeFileSync } from 'fs';
11
11
  import { join } from 'path';
12
12
  import { get as httpsGet } from 'https';
13
13
  import { getPlatformDir } from './findRfdbBinary.js';
14
+ import { GRAFEMA_VERSION } from '../version.js';
14
15
  const GITHUB_REPO = 'Disentinel/grafema';
15
16
  /** All analyzer binaries that can be lazy-downloaded. */
16
17
  const DOWNLOADABLE_BINARIES = [
@@ -159,38 +160,92 @@ export async function downloadBinary(binaryName, tag, onProgress) {
159
160
  // Atomic rename + make executable
160
161
  renameSync(tmpPath, targetPath);
161
162
  chmodSync(targetPath, 0o755);
163
+ // Record which version was downloaded so we can detect stale binaries
164
+ writeFileSync(`${targetPath}.version`, releaseTag, 'utf-8');
162
165
  log(` Installed to ${targetPath}`);
163
166
  return targetPath;
164
167
  }
165
168
  /**
166
- * Ensure a binary exists, downloading it if missing and eligible.
169
+ * Extract semver from a binaries tag: "binaries-v0.3.14" "0.3.14"
170
+ */
171
+ function versionFromTag(tag) {
172
+ return tag.replace(/^binaries-v/, '');
173
+ }
174
+ /**
175
+ * Compare two semver strings. Returns true if a >= b.
176
+ * Simple numeric comparison of major.minor.patch.
177
+ */
178
+ function semverGte(a, b) {
179
+ const pa = a.split('-')[0].split('.').map(Number);
180
+ const pb = b.split('-')[0].split('.').map(Number);
181
+ for (let i = 0; i < 3; i++) {
182
+ if ((pa[i] || 0) > (pb[i] || 0))
183
+ return true;
184
+ if ((pa[i] || 0) < (pb[i] || 0))
185
+ return false;
186
+ }
187
+ return true; // equal
188
+ }
189
+ /**
190
+ * Check if a cached binary in ~/.grafema/bin/ is from a release >= current version.
191
+ * The .version file stores the release tag used for download (e.g. "binaries-v0.3.14").
192
+ */
193
+ function isBinaryCurrentVersion(binaryPath) {
194
+ const versionFile = `${binaryPath}.version`;
195
+ if (!existsSync(versionFile))
196
+ return false;
197
+ try {
198
+ const storedTag = readFileSync(versionFile, 'utf-8').trim();
199
+ const storedVersion = versionFromTag(storedTag);
200
+ return semverGte(storedVersion, GRAFEMA_VERSION);
201
+ }
202
+ catch {
203
+ return false;
204
+ }
205
+ }
206
+ /**
207
+ * Ensure a binary exists and is current, downloading it if missing or stale.
167
208
  *
168
209
  * This is the main entry point for lazy downloading. Call it before
169
210
  * spawning an analyzer binary. Returns the path if found or downloaded,
170
211
  * null if the binary can't be obtained.
171
212
  *
213
+ * Staleness check: if a binary exists in ~/.grafema/bin/ but its .version
214
+ * file doesn't match the current Grafema release, it is re-downloaded.
215
+ *
172
216
  * @param binaryName - e.g. "grafema-analyzer"
173
217
  * @param existingPath - path from findBinary() (null = not found locally)
174
218
  * @param onProgress - optional callback for download progress
175
219
  */
176
220
  export async function ensureBinary(binaryName, existingPath, onProgress) {
177
- // Already found locally
221
+ // Already found locally (e.g. in platform npm package or PATH)
178
222
  if (existingPath)
179
223
  return existingPath;
180
224
  // Check ~/.grafema/bin/ first (may have been downloaded previously)
181
225
  const cached = findInGrafemaBin(binaryName);
182
- if (cached)
226
+ if (cached && isBinaryCurrentVersion(cached))
183
227
  return cached;
184
228
  // Not downloadable (e.g. rfdb-server — must be in npm package)
185
- if (!isDownloadable(binaryName))
229
+ if (!isDownloadable(binaryName)) {
230
+ // If cached exists but is stale and not downloadable, return it anyway
231
+ if (cached)
232
+ return cached;
186
233
  return null;
187
- // Download
234
+ }
235
+ // Download (missing or stale) — auto-detect latest binaries-v* release tag
236
+ const log = onProgress || (() => { });
237
+ if (cached) {
238
+ log(`Updating ${binaryName} (stale binary detected)...`);
239
+ }
188
240
  try {
189
241
  return await downloadBinary(binaryName, undefined, onProgress);
190
242
  }
191
243
  catch (err) {
192
244
  const msg = err instanceof Error ? err.message : String(err);
193
245
  (onProgress || console.error)(`Failed to download ${binaryName}: ${msg}`);
246
+ // Fall back to stale binary if download fails
247
+ if (cached)
248
+ return cached;
194
249
  return null;
195
250
  }
196
251
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lazyDownload.js","sourceRoot":"","sources":["../../src/utils/lazyDownload.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lazyDownload.js","sourceRoot":"","sources":["../../src/utils/lazyDownload.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC9H,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,GAAG,IAAI,QAAQ,EAAE,MAAM,OAAO,CAAC;AAExC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,GAAG,oBAAoB,CAAC;AAEzC,yDAAyD;AACzD,MAAM,qBAAqB,GAAG;IAC5B,sBAAsB;IACtB,QAAQ;IACR,kBAAkB;IAClB,iBAAiB;IACjB,UAAU;IACV,kBAAkB;IAClB,iBAAiB;IACjB,OAAO;IACP,uBAAuB;IACvB,sBAAsB;IACtB,OAAO;IACP,uBAAuB;IACvB,cAAc;IACd,aAAa;IACb,SAAS;IACT,yBAAyB;IACzB,gBAAgB;IAChB,eAAe;IACf,qBAAqB;IACrB,mBAAmB;IACnB,SAAS;IACT,yBAAyB;IACzB,gBAAgB;IAChB,KAAK;IACL,qBAAqB;IACrB,YAAY;IACZ,WAAW;IACX,QAAQ;IACR,sBAAsB;IACtB,aAAa;IACb,QAAQ;IACR,wBAAwB;IACxB,eAAe;IACf,cAAc;IACd,QAAQ;IACR,uBAAuB;IACvB,aAAa;IACb,uBAAuB;IACvB,qBAAqB;IACrB,uBAAuB;IACvB,eAAe;IACf,cAAc;CACf,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;IAC/D,OAAO,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,OAAO,qBAAqB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IACjD,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAClC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,GAAW;IAC1C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;YAC9E,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;gBACrD,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC;gBACtC,IAAI,CAAC,QAAQ;oBAAE,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAC;gBACrE,uBAAuB,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBACxD,OAAO;YACT,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,CAAC;QACf,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,oBAAoB;IACjC,MAAM,GAAG,GAAG,gCAAgC,WAAW,uBAAuB,CAAC;IAC/E,MAAM,GAAG,GAAG,MAAM,uBAAuB,CAAC,GAAG,CAAC,CAAC;IAE/C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC;IAC/B,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAErD,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,CAAC,UAAU,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAClF,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAgC,CAAC;IACjE,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC;IAC3E,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,UAAU,CAAC,QAAQ,CAAC;AAC7B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,UAAkB,EAClB,GAAY,EACZ,UAAkC;IAElC,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAClC,MAAM,SAAS,GAAG,GAAG,UAAU,IAAI,QAAQ,EAAE,CAAC;IAC9C,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAElC,gCAAgC;IAChC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,GAAG,UAAU,cAAc,CAAC;IAE5C,cAAc;IACd,MAAM,UAAU,GAAG,GAAG,IAAI,MAAM,oBAAoB,EAAE,CAAC;IACvD,MAAM,WAAW,GAAG,sBAAsB,WAAW,sBAAsB,UAAU,IAAI,SAAS,EAAE,CAAC;IAErG,MAAM,GAAG,GAAG,UAAU,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACrC,GAAG,CAAC,eAAe,UAAU,QAAQ,QAAQ,KAAK,CAAC,CAAC;IAEpD,wBAAwB;IACxB,MAAM,GAAG,GAAG,MAAM,uBAAuB,CAAC,WAAW,CAAC,CAAC;IAEvD,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,sBAAsB,SAAS,UAAU,GAAG,CAAC,UAAU,IAAI;YAC3D,sBAAsB,UAAU,cAAc,SAAS,GAAG,CAC3D,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC;IACpG,GAAG,CAAC,KAAK,SAAS,KAAK,MAAM,GAAG,CAAC,CAAC;IAElC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,IAAI,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACxC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,kCAAkC;IAClC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAChC,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAE7B,sEAAsE;IACtE,aAAa,CAAC,GAAG,UAAU,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IAE5D,GAAG,CAAC,kBAAkB,UAAU,EAAE,CAAC,CAAC;IACpC,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,GAAW;IACjC,OAAO,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAAC,CAAS,EAAE,CAAS;IACrC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClD,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7C,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IAChD,CAAC;IACD,OAAO,IAAI,CAAC,CAAC,QAAQ;AACvB,CAAC;AAED;;;GAGG;AACH,SAAS,sBAAsB,CAAC,UAAkB;IAChD,MAAM,WAAW,GAAG,GAAG,UAAU,UAAU,CAAC;IAC5C,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5D,MAAM,aAAa,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO,SAAS,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,UAAkB,EAClB,YAA2B,EAC3B,UAAkC;IAElC,+DAA+D;IAC/D,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IAEtC,oEAAoE;IACpE,MAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,MAAM,IAAI,sBAAsB,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAE5D,+DAA+D;IAC/D,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;QAChC,uEAAuE;QACvE,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,2EAA2E;IAC3E,MAAM,GAAG,GAAG,UAAU,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACrC,IAAI,MAAM,EAAE,CAAC;QACX,GAAG,CAAC,YAAY,UAAU,6BAA6B,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,cAAc,CAAC,UAAU,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,CAAC,UAAU,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,sBAAsB,UAAU,KAAK,GAAG,EAAE,CAAC,CAAC;QAC1E,8CAA8C;QAC9C,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grafema/util",
3
- "version": "0.3.14",
3
+ "version": "0.3.16",
4
4
  "description": "Query, config, and guarantee utilities for Grafema",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -35,8 +35,8 @@
35
35
  "ajv": "^8.17.1",
36
36
  "minimatch": "^10.1.1",
37
37
  "yaml": "^2.8.2",
38
- "@grafema/types": "0.3.14",
39
- "@grafema/rfdb-client": "0.3.14"
38
+ "@grafema/rfdb-client": "0.3.16",
39
+ "@grafema/types": "0.3.16"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^25.0.8",
@@ -8,11 +8,12 @@
8
8
  * Example: grafema-analyzer-darwin-arm64
9
9
  */
10
10
 
11
- import { existsSync, mkdirSync, chmodSync, renameSync, unlinkSync, createWriteStream } from 'fs';
11
+ import { existsSync, mkdirSync, chmodSync, renameSync, unlinkSync, createWriteStream, readFileSync, writeFileSync } from 'fs';
12
12
  import { join } from 'path';
13
13
  import { get as httpsGet } from 'https';
14
14
  import type { IncomingMessage } from 'http';
15
15
  import { getPlatformDir } from './findRfdbBinary.js';
16
+ import { GRAFEMA_VERSION } from '../version.js';
16
17
 
17
18
  const GITHUB_REPO = 'Disentinel/grafema';
18
19
 
@@ -187,17 +188,60 @@ export async function downloadBinary(
187
188
  renameSync(tmpPath, targetPath);
188
189
  chmodSync(targetPath, 0o755);
189
190
 
191
+ // Record which version was downloaded so we can detect stale binaries
192
+ writeFileSync(`${targetPath}.version`, releaseTag, 'utf-8');
193
+
190
194
  log(` Installed to ${targetPath}`);
191
195
  return targetPath;
192
196
  }
193
197
 
194
198
  /**
195
- * Ensure a binary exists, downloading it if missing and eligible.
199
+ * Extract semver from a binaries tag: "binaries-v0.3.14" "0.3.14"
200
+ */
201
+ function versionFromTag(tag: string): string {
202
+ return tag.replace(/^binaries-v/, '');
203
+ }
204
+
205
+ /**
206
+ * Compare two semver strings. Returns true if a >= b.
207
+ * Simple numeric comparison of major.minor.patch.
208
+ */
209
+ function semverGte(a: string, b: string): boolean {
210
+ const pa = a.split('-')[0].split('.').map(Number);
211
+ const pb = b.split('-')[0].split('.').map(Number);
212
+ for (let i = 0; i < 3; i++) {
213
+ if ((pa[i] || 0) > (pb[i] || 0)) return true;
214
+ if ((pa[i] || 0) < (pb[i] || 0)) return false;
215
+ }
216
+ return true; // equal
217
+ }
218
+
219
+ /**
220
+ * Check if a cached binary in ~/.grafema/bin/ is from a release >= current version.
221
+ * The .version file stores the release tag used for download (e.g. "binaries-v0.3.14").
222
+ */
223
+ function isBinaryCurrentVersion(binaryPath: string): boolean {
224
+ const versionFile = `${binaryPath}.version`;
225
+ if (!existsSync(versionFile)) return false;
226
+ try {
227
+ const storedTag = readFileSync(versionFile, 'utf-8').trim();
228
+ const storedVersion = versionFromTag(storedTag);
229
+ return semverGte(storedVersion, GRAFEMA_VERSION);
230
+ } catch {
231
+ return false;
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Ensure a binary exists and is current, downloading it if missing or stale.
196
237
  *
197
238
  * This is the main entry point for lazy downloading. Call it before
198
239
  * spawning an analyzer binary. Returns the path if found or downloaded,
199
240
  * null if the binary can't be obtained.
200
241
  *
242
+ * Staleness check: if a binary exists in ~/.grafema/bin/ but its .version
243
+ * file doesn't match the current Grafema release, it is re-downloaded.
244
+ *
201
245
  * @param binaryName - e.g. "grafema-analyzer"
202
246
  * @param existingPath - path from findBinary() (null = not found locally)
203
247
  * @param onProgress - optional callback for download progress
@@ -207,22 +251,33 @@ export async function ensureBinary(
207
251
  existingPath: string | null,
208
252
  onProgress?: (msg: string) => void,
209
253
  ): Promise<string | null> {
210
- // Already found locally
254
+ // Already found locally (e.g. in platform npm package or PATH)
211
255
  if (existingPath) return existingPath;
212
256
 
213
257
  // Check ~/.grafema/bin/ first (may have been downloaded previously)
214
258
  const cached = findInGrafemaBin(binaryName);
215
- if (cached) return cached;
259
+ if (cached && isBinaryCurrentVersion(cached)) return cached;
216
260
 
217
261
  // Not downloadable (e.g. rfdb-server — must be in npm package)
218
- if (!isDownloadable(binaryName)) return null;
262
+ if (!isDownloadable(binaryName)) {
263
+ // If cached exists but is stale and not downloadable, return it anyway
264
+ if (cached) return cached;
265
+ return null;
266
+ }
267
+
268
+ // Download (missing or stale) — auto-detect latest binaries-v* release tag
269
+ const log = onProgress || (() => {});
270
+ if (cached) {
271
+ log(`Updating ${binaryName} (stale binary detected)...`);
272
+ }
219
273
 
220
- // Download
221
274
  try {
222
275
  return await downloadBinary(binaryName, undefined, onProgress);
223
276
  } catch (err) {
224
277
  const msg = err instanceof Error ? err.message : String(err);
225
278
  (onProgress || console.error)(`Failed to download ${binaryName}: ${msg}`);
279
+ // Fall back to stale binary if download fails
280
+ if (cached) return cached;
226
281
  return null;
227
282
  }
228
283
  }