@andrian.yablonskyy/thub-coordinator 1.0.5 → 1.0.6

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.
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@andrian.yablonskyy/thub-coordinator",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "TestHub Coordinator — job queue, resource registry, scheduler, heartbeat monitor, log/artifact store and web dashboard",
5
5
  "main": "src/server.js",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
6
9
  "bin": {
7
10
  "thub-admin": "./bin/thub-admin.js",
8
11
  "thub-coordinator": "./src/server.js"
@@ -11,7 +14,7 @@
11
14
  "start": "node src/server.js",
12
15
  "dev": "node --watch src/server.js",
13
16
  "test": "node --test test/*.test.js",
14
- "postinstall": "node scripts/install-default-config.js && node scripts/install-systemd-unit.js",
17
+ "postinstall": "node scripts/ensure-native-deps.js && node scripts/install-default-config.js && node scripts/install-systemd-unit.js",
15
18
  "lint": "eslint .",
16
19
  "lint:fix": "eslint . --fix"
17
20
  },
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @file scripts/ensure-native-deps.js
3
+ * @description npm postinstall: makes sure better-sqlite3's native addon loads under the Node.js
4
+ * running this install, rebuilding it in place if not
5
+ *
6
+ * @author Andrian Yablonskyy
7
+ * @copyright Copyright (c) 2026 Andrian Yablonskyy. All rights reserved.
8
+ *
9
+ * This file is part of TestHub and is proprietary and confidential.
10
+ * Unauthorized copying, modification, distribution, or use of this file,
11
+ * via any medium, is strictly prohibited without prior written permission
12
+ * from AdSystem.PRO.
13
+ */
14
+
15
+ 'use strict';
16
+
17
+ const path = require('node:path'),
18
+ { execFileSync, spawnSync } = require('node:child_process'),
19
+ { isGlobalInstall } = require('./install-target'),
20
+
21
+ PKG_DIR = path.join(__dirname, '..');
22
+
23
+ // Checked in a child process: once a mismatched addon has been dlopen'ed,
24
+ // this process keeps the old image mapped, so an in-process retry after
25
+ // the rebuild would still see the stale one.
26
+ function loads(){
27
+ const res = spawnSync(process.execPath, ['-e', 'new (require(\'better-sqlite3\'))(\':memory:\').close()'], {
28
+ cwd: PKG_DIR,
29
+ encoding: 'utf8'
30
+ });
31
+ if (res.status === 0){
32
+ return true;
33
+ }
34
+ if (/ERR_DLOPEN_FAILED|NODE_MODULE_VERSION/.test(res.stderr)){
35
+ return false;
36
+ }
37
+ throw new Error(res.stderr.trim().split('\n').find((l) => /Error/.test(l)) || `exit ${res.status}`);
38
+ }
39
+
40
+ // Runs better-sqlite3's own install script (prebuild-install, falling back
41
+ // to node-gyp) in its own directory. Not `npm rebuild`: under `npm i -g`
42
+ // this script inherits npm_config_global=true, which points a nested
43
+ // `npm rebuild` at the global prefix instead of this package, where it
44
+ // "succeeds" without touching the addon at all.
45
+ function rebuild(){
46
+ const dir = path.dirname(require.resolve('better-sqlite3/package.json', { paths: [PKG_DIR] })),
47
+ npmCli = process.env.npm_execpath,
48
+ [cmd, args] = npmCli ? [process.execPath, [npmCli]] : ['npm', []];
49
+ execFileSync(cmd, [...args, 'run-script', 'install'], {
50
+ cwd: dir,
51
+ stdio: 'inherit',
52
+ env: { ...process.env, npm_config_global: 'false' }
53
+ });
54
+ }
55
+
56
+ // Re-running `npm i -g` for an already-installed version after a Node
57
+ // major upgrade leaves unchanged dependencies alone — including
58
+ // better-sqlite3's addon, still built for the old Node ABI — so the server
59
+ // then dies with NODE_MODULE_VERSION mismatch at startup. Rebuild it here
60
+ // with the same node/npm that's running this install. Best-effort: never
61
+ // fails the install itself.
62
+ function main(){
63
+ if (!isGlobalInstall()){
64
+ return;
65
+ }
66
+ try {
67
+ if (loads()){
68
+ return;
69
+ }
70
+ console.log(`thub-coordinator: rebuilding better-sqlite3 for Node.js ${process.version} (ABI ${process.versions.modules})`);
71
+ rebuild();
72
+ if (!loads()){
73
+ throw new Error('still fails to load after rebuild');
74
+ }
75
+ console.log('thub-coordinator: better-sqlite3 rebuilt');
76
+ }
77
+ catch (err){
78
+ console.warn(
79
+ `thub-coordinator: better-sqlite3 doesn't load under ${process.execPath} (${process.version}): ${err.message}\n` +
80
+ `Rebuild it manually: cd ${PKG_DIR} && sudo env "PATH=$PATH" npm rebuild better-sqlite3`
81
+ );
82
+ }
83
+ }
84
+
85
+ main();
package/src/db/index.js CHANGED
@@ -18,7 +18,7 @@ const fs = require('node:fs'),
18
18
  Database = require('better-sqlite3');
19
19
 
20
20
  function openDb(dbPath){
21
- const db = new Database(dbPath);
21
+ const db = openDatabase(dbPath);
22
22
  db.pragma('journal_mode = WAL');
23
23
  db.pragma('synchronous = NORMAL');
24
24
  db.pragma('foreign_keys = ON');
@@ -26,6 +26,32 @@ function openDb(dbPath){
26
26
  return db;
27
27
  }
28
28
 
29
+ // better-sqlite3 is a native addon built for one Node.js ABI at install
30
+ // time; after a Node major upgrade it must be rebuilt. Without this, the
31
+ // only hint is a raw NODE_MODULE_VERSION mismatch from deep in bindings.js.
32
+ // The rebuild command keeps the caller's PATH under sudo, since sudo's own
33
+ // secure_path often finds a different (older) node than the user's shell
34
+ // does — rebuilding with that one just reproduces the same mismatch.
35
+ function openDatabase(dbPath){
36
+ try {
37
+ return new Database(dbPath);
38
+ }
39
+ catch (err){
40
+ if (err.code === 'ERR_DLOPEN_FAILED'){
41
+ const builtFor = err.message.match(/NODE_MODULE_VERSION (\d+)/)?.[1],
42
+ pkgDir = path.join(__dirname, '..', '..');
43
+ throw new Error(
44
+ `better-sqlite3 was built for a different Node.js ABI (${builtFor || 'unknown'}) than this one: ` +
45
+ `${process.version}, ABI ${process.versions.modules}, at ${process.execPath}.\n` +
46
+ `Rebuild it with this same node: cd ${pkgDir} && sudo env "PATH=$PATH" npm rebuild better-sqlite3\n` +
47
+ 'If `sudo node -v` and `node -v` differ, sudo was using another Node.js for earlier installs.',
48
+ { cause: err }
49
+ );
50
+ }
51
+ throw err;
52
+ }
53
+ }
54
+
29
55
  function migrate(db){
30
56
  db.exec(`
31
57
  CREATE TABLE IF NOT EXISTS schema_migrations (