- Build: explicitly write tt-native package.json into the bundle in
CI / Build & Test (push) Successful in 3m47s

patchBundle so the runtime never gets empty content for that key
  (fixes "Unexpected end of JSON input" in Module._extensions..json
  when the host is started by the extension on Windows).
- State: treat empty or whitespace-only state.json (and legacy
  persist file) as missing and return default state instead of
  throwing in JSON.parse.
- State: add ensureStorageDir() and call it from message-router
  after setStoragePath so the storage directory exists on fresh
  install before any state is loaded or saved.
This commit is contained in:
Raven Scott
2026-03-06 18:51:53 -05:00
parent 59f0524739
commit 0e0822aa81
4 changed files with 28 additions and 1 deletions
+5
View File
@@ -65,6 +65,10 @@ function setStoragePath(baseDir) {
stateModule.setStoragePath(baseDir); stateModule.setStoragePath(baseDir);
} }
function ensureStorageDir() {
stateModule.ensureStorageDir();
}
// ── Restore persisted state ─────────────────────────────────────────────────── // ── Restore persisted state ───────────────────────────────────────────────────
/** /**
@@ -108,6 +112,7 @@ module.exports = {
setEventEmitter, setEventEmitter,
// Storage // Storage
setStoragePath, setStoragePath,
ensureStorageDir,
// Settings // Settings
getSettings: settingsModule.getSettings, getSettings: settingsModule.getSettings,
updateSettings: settingsModule.updateSettings, updateSettings: settingsModule.updateSettings,
+15 -1
View File
@@ -47,6 +47,16 @@ function getStatePath() {
return stateFilePath; return stateFilePath;
} }
/** Create the storage directory if it does not exist (e.g. fresh install on Windows). */
function ensureStorageDir() {
const dir = path.dirname(getStatePath());
try {
fs.mkdirSync(dir, { recursive: true });
} catch (e) {
if (process.stderr) process.stderr.write('[holesail-manager] could not create storage dir: ' + e.message + '\n');
}
}
function getLegacyPersistPath() { function getLegacyPersistPath() {
const dir = path.dirname(getStatePath()); const dir = path.dirname(getStatePath());
return path.join(dir, LEGACY_PERSIST_FILENAME); return path.join(dir, LEGACY_PERSIST_FILENAME);
@@ -82,6 +92,8 @@ function loadState() {
try { try {
const raw = fs.readFileSync(file, 'utf8'); const raw = fs.readFileSync(file, 'utf8');
const trimmed = raw && typeof raw === 'string' ? raw.trim() : '';
if (!trimmed) return buildDefaultState();
const data = JSON.parse(raw); const data = JSON.parse(raw);
if (!data || typeof data !== 'object') return buildDefaultState(); if (!data || typeof data !== 'object') return buildDefaultState();
const out = { const out = {
@@ -110,6 +122,8 @@ function loadState() {
const legacyFile = getLegacyPersistPath(); const legacyFile = getLegacyPersistPath();
try { try {
const raw = fs.readFileSync(legacyFile, 'utf8'); const raw = fs.readFileSync(legacyFile, 'utf8');
const trimmed = raw && typeof raw === 'string' ? raw.trim() : '';
if (!trimmed) return buildDefaultState();
const data = JSON.parse(raw); const data = JSON.parse(raw);
if (process.stderr) process.stderr.write('[holesail-manager] migrating from ' + legacyFile + ' to ' + file + '\n'); if (process.stderr) process.stderr.write('[holesail-manager] migrating from ' + legacyFile + ' to ' + file + '\n');
const out = { const out = {
@@ -134,4 +148,4 @@ function loadState() {
return buildDefaultState(); return buildDefaultState();
} }
module.exports = { SETTINGS_DEFAULTS, setStoragePath, loadState, saveStateSync, buildDefaultState }; module.exports = { SETTINGS_DEFAULTS, setStoragePath, loadState, saveStateSync, buildDefaultState, ensureStorageDir };
+1
View File
@@ -12,6 +12,7 @@ const { initStartup, getProxiesReadyPromise, getTunnelsRestoredPromise, setTunne
const holesailManager = require('../holesail-manager/index.js'); const holesailManager = require('../holesail-manager/index.js');
holesailManager.setStoragePath(STORAGE_PATH); holesailManager.setStoragePath(STORAGE_PATH);
holesailManager.ensureStorageDir();
const certificateAuthority = require('../managers/certificate-authority.js'); const certificateAuthority = require('../managers/certificate-authority.js');
const httpsProxy = require('../proxy/https-proxy.js'); const httpsProxy = require('../proxy/https-proxy.js');
+7
View File
@@ -180,6 +180,13 @@ function patchBundle(bundle, hosts) {
} }
} }
) )
// Ensure tt-native package.json has content in the bundle. On Windows the runtime
// can end up reading empty content for this key (path/bundle handling), causing
// "Unexpected end of JSON input" when the .json loader runs. Explicitly write it.
const pkgPath = path.join(NATIVE_HOST_DIR, 'node_modules', 'tt-native', 'package.json')
if (fs.existsSync(pkgPath)) {
bundle.write(ttNativePkgKey, fs.readFileSync(pkgPath))
}
console.log(' Patched tt-native binding (host-specific resolutions)') console.log(' Patched tt-native binding (host-specific resolutions)')
} }
} }