From eae612cb38a017de7ef87478ece9988dda78b6bb Mon Sep 17 00:00:00 2001 From: Pandipipas <62224708+Pandipipas@users.noreply.github.com> Date: Mon, 2 Mar 2026 22:53:22 +0100 Subject: [PATCH] refactor: run NodeCG from lib/scoreko-dev for Node 24 migration --- README.md | 4 +- docs/architecture.md | 4 +- docs/troubleshooting.md | 14 +-- package-lock.json | 28 +++--- package.json | 8 +- scripts/doctor.mjs | 32 +++++-- scripts/rebuild-nodecg-native.mjs | 8 +- src/main/main.ts | 2 +- src/main/nodecg/process-manager.ts | 144 ++++++++++++----------------- src/tests/process-manager.test.ts | 22 ++--- 10 files changed, 126 insertions(+), 140 deletions(-) diff --git a/README.md b/README.md index 4e90afe..796d7f3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # scoreko-electron -Desktop app (Electron + TypeScript) to run and package a NodeCG installation with the `scoreko-dev` bundle. +Desktop app (Electron + TypeScript) to run and package `scoreko-dev` (Electron host + NodeCG started inside `lib/scoreko-dev`). ## Requirements @@ -38,7 +38,7 @@ Desktop app (Electron + TypeScript) to run and package a NodeCG installation wit ### Native modules -- `npm run rebuild:native`: rebuilds NodeCG native modules. +- `npm run rebuild:native`: rebuilds scoreko-dev native modules (including NodeCG dependency). - `npm run rebuild:better-sqlite3`: rebuilds only `better-sqlite3` for Electron. ## Quick setup diff --git a/docs/architecture.md b/docs/architecture.md index 94c2afc..819fe29 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -4,14 +4,14 @@ 1. `src/main/main.ts` loads `appConfig` from `config/runtime-config.ts`. 2. Creates windows (`windows/window-factory.ts`). -3. Starts NodeCG with `nodecg/process-manager.ts`. +3. Starts NodeCG with `nodecg/process-manager.ts` by running `npx nodecg start` from `lib/scoreko-dev`. 4. Waits for HTTP readiness and shows loading -> main dashboard. 5. On shutdown, runs a single graceful-stop flow to avoid orphan processes. ## Main modules - `config/runtime-config.ts`: read/validate env vars. -- `nodecg/process-manager.ts`: start, readiness, and stop for NodeCG; install/permission/port validation. +- `nodecg/process-manager.ts`: start, readiness, and stop for NodeCG; install/permission/port validation in `lib/scoreko-dev`. - `windows/window-factory.ts`: window creation and navigation policy. - `windows/navigation-security.ts`: internal navigation allowlist and safe external schemes. - `errors/error-presenter.ts`: fatal error presentation. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 310df27..851e50b 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,14 +1,14 @@ # Troubleshooting -## `NodeCG folder does not exist` +## `Scoreko app folder does not exist` -- Verify `lib/nodecg` exists. -- Make sure the project contains a full NodeCG installation. +- Verify `lib/scoreko-dev` exists. +- Make sure the project contains the scoreko-dev bundle root with its own `package.json`. -## `No read/write permissions on NodeCG` +## `No read/write permissions on scoreko app folder` -- Adjust permissions on `lib/nodecg` for the user running Electron. -- On Linux/macOS: `chmod -R u+rw lib/nodecg` (according to your local policy). +- Adjust permissions on `lib/scoreko-dev` for the user running Electron. +- On Linux/macOS: `chmod -R u+rw lib/scoreko-dev` (according to your local policy). ## `Port is already in use` @@ -19,7 +19,7 @@ - Check NodeCG logs in standard output. - Increase `NODECG_STARTUP_TIMEOUT_MS` if the environment is slow. -- Verify NodeCG dependencies (`cd lib/nodecg && npm install`). +- Verify scoreko-dev dependencies (`cd lib/scoreko-dev && npm install`). ## macOS build fails because of icon diff --git a/package-lock.json b/package-lock.json index 4ebf4b9..79621f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,11 @@ "license": "MIT", "devDependencies": { "@electron/rebuild": "^3.7.1", - "@types/node": "^22.10.5", + "@types/node": "^24.3.0", "@typescript-eslint/eslint-plugin": "^8.22.0", "@typescript-eslint/parser": "^8.22.0", "concurrently": "^9.1.2", - "electron": "39.5.1", + "electron": "40.6.1", "electron-builder": "^25.1.8", "eslint": "^9.19.0", "prettier": "^3.4.2", @@ -23,7 +23,7 @@ "wait-on": "^8.0.1" }, "engines": { - "node": ">=22" + "node": ">=24.14.0" } }, "node_modules/@develar/schema-utils": { @@ -975,13 +975,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", - "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", + "version": "24.11.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.11.0.tgz", + "integrity": "sha512-fPxQqz4VTgPI/IQ+lj9r0h+fDR66bzoeMGHp8ASee+32OSGIkeASsoZuJixsQoVef1QJbeubcPBxKk22QVoWdw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/plist": { @@ -2913,15 +2913,15 @@ } }, "node_modules/electron": { - "version": "39.5.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-39.5.1.tgz", - "integrity": "sha512-6s/sBQar+bbW59XSqohZj04MPic+kdVUAWjLbfQB/uLOeNw9jWX5FHaTxpHK29Xp3mKOHef7wErsjwMyCuWltg==", + "version": "40.6.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-40.6.1.tgz", + "integrity": "sha512-u9YfoixttdauciHV9Ut9Zf3YipJoU093kR1GSYTTXTAXqhiXI0G1A0NnL/f0O2m2UULCXaXMf2W71PloR6V9pQ==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", - "@types/node": "^22.7.7", + "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { @@ -6286,9 +6286,9 @@ } }, "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 26fe43e..fec546f 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "dev:electron": "wait-on dist/main/main.js && electron .", "pack": "npm run build && electron-builder --dir", "rebuild:native": "node scripts/rebuild-nodecg-native.mjs", - "rebuild:better-sqlite3": "electron-rebuild --version 40.6.1 --module-dir lib/nodecg/workspaces/database-adapter-sqlite-legacy --only better-sqlite3 -f", + "rebuild:better-sqlite3": "electron-rebuild --version 40.6.1 --module-dir lib/scoreko-dev/workspaces/database-adapter-sqlite-legacy --only better-sqlite3 -f", "test": "npm run build && node --test dist/tests/**/*.test.js", "doctor": "node scripts/doctor.mjs", "lint": "eslint . --ext .ts,.js,.mjs", @@ -45,8 +45,8 @@ ], "extraResources": [ { - "from": "lib/nodecg", - "to": "lib/nodecg" + "from": "lib/scoreko-dev", + "to": "lib/scoreko-dev" }, { "from": "static", @@ -90,7 +90,7 @@ }, "devDependencies": { "@electron/rebuild": "^3.7.1", - "@types/node": "^22.10.5", + "@types/node": "^24.3.0", "concurrently": "^9.1.2", "electron": "40.6.1", "electron-builder": "^25.1.8", diff --git a/scripts/doctor.mjs b/scripts/doctor.mjs index f44a330..6e52cb1 100644 --- a/scripts/doctor.mjs +++ b/scripts/doctor.mjs @@ -4,7 +4,7 @@ import net from "node:net"; import path from "node:path"; const cwd = process.cwd(); -const nodecgRootPath = path.resolve(cwd, "lib", "nodecg"); +const scorekoRootPath = path.resolve(cwd, "lib", "scoreko-dev"); const checks = []; @@ -36,19 +36,31 @@ function parseIntInRange(name, fallback, min, max) { } function checkNodecgInstall() { - const indexPath = path.join(nodecgRootPath, "index.js"); - const bundleName = (process.env.NODECG_BUNDLE_NAME ?? "scoreko-dev").trim(); - const bundlePath = path.join(nodecgRootPath, "bundles", bundleName); + const packageJsonPath = path.join(scorekoRootPath, "package.json"); + const nodecgCliPath = path.join( + scorekoRootPath, + "node_modules", + ".bin", + process.platform === "win32" ? "nodecg.cmd" : "nodecg", + ); + const bundleAssetPaths = ["dashboard", "graphics", "extension", "extensions"].map((folder) => + path.join(scorekoRootPath, folder), + ); - addCheck(fs.existsSync(nodecgRootPath), "NodeCG root", nodecgRootPath); - addCheck(fs.existsSync(indexPath), "NodeCG index.js", indexPath); - addCheck(fs.existsSync(bundlePath), `Bundle '${bundleName}'`, bundlePath); + addCheck(fs.existsSync(scorekoRootPath), "Scoreko root", scorekoRootPath); + addCheck(fs.existsSync(packageJsonPath), "scoreko-dev package.json", packageJsonPath); + addCheck(fs.existsSync(nodecgCliPath), "NodeCG CLI", nodecgCliPath); + addCheck( + bundleAssetPaths.some((candidatePath) => fs.existsSync(candidatePath)), + "scoreko-dev bundle assets", + `Expected one of: ${bundleAssetPaths.join(", ")}`, + ); try { - fs.accessSync(nodecgRootPath, fs.constants.R_OK | fs.constants.W_OK); - addCheck(true, "lib/nodecg permissions", "Read/write OK"); + fs.accessSync(scorekoRootPath, fs.constants.R_OK | fs.constants.W_OK); + addCheck(true, "lib/scoreko-dev permissions", "Read/write OK"); } catch { - addCheck(false, "lib/nodecg permissions", "No read/write permissions in lib/nodecg"); + addCheck(false, "lib/scoreko-dev permissions", "No read/write permissions in lib/scoreko-dev"); } } diff --git a/scripts/rebuild-nodecg-native.mjs b/scripts/rebuild-nodecg-native.mjs index 423600a..5b4a6a7 100644 --- a/scripts/rebuild-nodecg-native.mjs +++ b/scripts/rebuild-nodecg-native.mjs @@ -3,13 +3,13 @@ import path from "node:path"; import { spawn } from "node:child_process"; const root = process.cwd(); -const nodecgDir = path.join(root, "lib", "nodecg"); -const sqliteLegacyDir = path.join(nodecgDir, "workspaces", "database-adapter-sqlite-legacy"); +const scorekoDir = path.join(root, "lib", "scoreko-dev"); +const sqliteLegacyDir = path.join(scorekoDir, "workspaces", "database-adapter-sqlite-legacy"); -const moduleDirs = [nodecgDir, sqliteLegacyDir].filter((dir) => existsSync(path.join(dir, "package.json"))); +const moduleDirs = [scorekoDir, sqliteLegacyDir].filter((dir) => existsSync(path.join(dir, "package.json"))); if (moduleDirs.length === 0) { - console.error("No NodeCG package folders found. Expected lib/nodecg and/or workspaces."); + console.error("No package folders found. Expected lib/scoreko-dev and/or workspaces."); process.exit(1); } diff --git a/src/main/main.ts b/src/main/main.ts index 37c775f..e6e8b5a 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -15,7 +15,7 @@ app.setPath("userData", path.join(app.getPath("appData"), appConfig.userDataDire const isDev = !app.isPackaged; const rootPath = isDev ? path.resolve(__dirname, "../..") : process.resourcesPath; -const nodecgRootPath = path.resolve(rootPath, "lib", "nodecg"); +const nodecgRootPath = path.resolve(rootPath, "lib", "scoreko-dev"); const mainDashboardUrl = `http://localhost:${appConfig.nodecgPort}/bundles/${appConfig.bundleName}/${appConfig.mainDashboardRoute}`; const loadingDashboardUrl = `http://localhost:${appConfig.nodecgPort}/bundles/${appConfig.bundleName}/${appConfig.loadingDashboardRoute}`; const nodecgBaseUrl = `http://127.0.0.1:${appConfig.nodecgPort}`; diff --git a/src/main/nodecg/process-manager.ts b/src/main/nodecg/process-manager.ts index 94bc3ed..538f93c 100644 --- a/src/main/nodecg/process-manager.ts +++ b/src/main/nodecg/process-manager.ts @@ -20,7 +20,6 @@ type NodecgProcessManagerDeps = { pathExists: (candidatePath: string) => boolean; fetchUrl: typeof fetch; platform: NodeJS.Platform; - execPath: string; env: NodeJS.ProcessEnv; killProcess: (pid: number, signal: NodeJS.Signals) => void; setTimer: (handler: () => void, timeoutMs: number) => unknown; @@ -53,7 +52,6 @@ export function createNodecgProcessManager({ let lastStderrLine: string | null = null; const startNodecgProcess = async (): Promise => { - // Fail fast with actionable errors before spawning child processes. validateNodecgInstall( nodecgRootPath, appConfig.bundleName, @@ -69,18 +67,17 @@ export function createNodecgProcessManager({ ); } - const indexPath = path.join(nodecgRootPath, "index.js"); - const child = resolvedDeps.spawnProcess(resolvedDeps.execPath, [indexPath], { + const command = resolvedDeps.platform === "win32" ? "npx.cmd" : "npx"; + const child = resolvedDeps.spawnProcess(command, ["nodecg", "start"], { cwd: nodecgRootPath, env: { ...resolvedDeps.env, NODE_ENV: isDev ? "development" : "production", NODECG_PORT: appConfig.nodecgPort, - ELECTRON_RUN_AS_NODE: "1", }, stdio: ["ignore", "pipe", "pipe"], detached: resolvedDeps.platform !== "win32", - shell: resolvedDeps.platform === "win32", + shell: false, }); child.stdout?.on("data", (chunk) => { @@ -108,7 +105,6 @@ export function createNodecgProcessManager({ }; const waitForNodecgReady = async (startTime: number): Promise => { - // Poll the local NodeCG URL until it answers or we hit the configured timeout. while (Date.now() - startTime < appConfig.startupTimeoutMs) { if (!nodecgProcess) { const exitDetails = lastExit @@ -121,7 +117,7 @@ export function createNodecgProcessManager({ exitDetails, stderrDetails, `NodeCG path: ${nodecgRootPath}`, - "Check that lib/nodecg dependencies are installed and the bundle exists.", + "Check that lib/scoreko-dev dependencies are installed and the bundle exists.", ].join("\n"), ); } @@ -142,7 +138,6 @@ export function createNodecgProcessManager({ }; const stopNodecgProcessGracefully = (): Promise => { - // Reuse the same stop promise to avoid sending multiple kill signals during app shutdown. if (stopNodecgPromise) { return stopNodecgPromise; } @@ -204,7 +199,6 @@ function resolveDeps(deps?: Partial): NodecgProcessMan pathExists: deps?.pathExists ?? fs.existsSync, fetchUrl: deps?.fetchUrl ?? fetch, platform: deps?.platform ?? process.platform, - execPath: deps?.execPath ?? process.execPath, env: deps?.env ?? process.env, killProcess: deps?.killProcess ?? process.kill, setTimer: deps?.setTimer ?? setTimeout, @@ -221,39 +215,46 @@ function validateNodecgInstall( pathExists: (candidatePath: string) => boolean, hasReadWriteAccessToPath: (candidatePath: string) => boolean, ): void { - const indexPath = path.join(nodecgRootPath, "index.js"); - const nodecgBootstrapPath = path.join(nodecgRootPath, "node_modules", "nodecg", "dist", "server", "bootstrap.js"); - const bundlePath = path.join(nodecgRootPath, "bundles", bundleName); + const packageJsonPath = path.join(nodecgRootPath, "package.json"); + const nodecgCliPath = path.join( + nodecgRootPath, + "node_modules", + ".bin", + process.platform === "win32" ? "nodecg.cmd" : "nodecg", + ); + const bundleAssetDirs = ["dashboard", "graphics", "extension", "extensions"].map((dir) => + path.join(nodecgRootPath, dir), + ); if (!pathExists(nodecgRootPath)) { - throw new Error(`NodeCG folder does not exist: ${nodecgRootPath}`); + throw new Error(`Scoreko app folder does not exist: ${nodecgRootPath}`); } if (!hasReadWriteAccessToPath(nodecgRootPath)) { - throw new Error(`No read/write permissions on NodeCG: ${nodecgRootPath}`); + throw new Error(`No read/write permissions on scoreko app folder: ${nodecgRootPath}`); } - if (!pathExists(indexPath)) { - throw new Error(`${indexPath} was not found. Copy a full NodeCG installation into lib/nodecg.`); + if (!pathExists(packageJsonPath)) { + throw new Error(`${packageJsonPath} was not found. Expected a NodeCG bundle app at lib/scoreko-dev.`); } - if (!pathExists(nodecgBootstrapPath)) { + if (!pathExists(nodecgCliPath)) { throw new Error( [ - "NodeCG is present but internal dependencies are missing.", - `Not found: ${nodecgBootstrapPath}`, - "Solution: enter lib/nodecg and install dependencies:", + "NodeCG dependency is missing in lib/scoreko-dev.", + `Not found: ${nodecgCliPath}`, + "Solution: enter lib/scoreko-dev and install dependencies:", " npm install", ].join("\n"), ); } - if (!pathExists(bundlePath)) { + if (!bundleAssetDirs.some((candidatePath) => pathExists(candidatePath))) { throw new Error( [ - `Bundle '${bundleName}' was not found.`, - `Expected path: ${bundlePath}`, - "Copy/clone your bundle inside lib/nodecg/bundles before running Electron.", + `Bundle '${bundleName}' appears incomplete.`, + `Expected one of: ${bundleAssetDirs.join(", ")}`, + "Ensure extensions/dashboard/graphics assets exist inside lib/scoreko-dev.", ].join("\n"), ); } @@ -270,76 +271,49 @@ function hasReadWriteAccess(candidatePath: string): boolean { function probePortAvailable(port: number): Promise { return new Promise((resolve) => { - // A successful TCP connection means some process is already listening on the port. - const socket = net.createConnection({ host: "127.0.0.1", port }); - let resolved = false; + const server = net.createServer(); - const complete = (isAvailable: boolean): void => { - if (resolved) { - return; - } - - resolved = true; - socket.destroy(); - resolve(isAvailable); - }; - - socket.setTimeout(1000); - - socket.once("connect", () => { - complete(false); + server.once("error", () => { + resolve(false); }); - socket.once("timeout", () => { - complete(true); + server.once("listening", () => { + server.close(() => resolve(true)); }); - socket.once("error", (error: NodeJS.ErrnoException) => { - if (error.code === "ECONNREFUSED" || error.code === "EHOSTUNREACH") { - complete(true); - return; - } - - complete(false); - }); + server.listen(port, "127.0.0.1"); }); } -function killNodecgProcessTree( - pid: number, - signal: NodeJS.Signals, - log: (...args: unknown[]) => void, - deps: Pick, -): boolean { - if (deps.platform === "win32") { - const force = signal === "SIGKILL" ? "/F" : ""; - const killer = deps.spawnProcess("taskkill", ["/pid", String(pid), "/T", ...(force ? [force] : [])], { - stdio: "ignore", - shell: true, - }); - - killer.on("error", (error) => { - log(`taskkill error for pid=${pid}`, error); - }); - - return true; - } - - try { - deps.killProcess(-pid, signal); - return true; - } catch { - try { - deps.killProcess(pid, signal); - return true; - } catch { - return false; - } - } -} - function sleep(ms: number, setTimer: (handler: () => void, timeoutMs: number) => unknown): Promise { return new Promise((resolve) => { setTimer(resolve, ms); }); } + +function killNodecgProcessTree( + pid: number, + signal: NodeJS.Signals, + log: (...args: unknown[]) => void, + deps: Pick, +): void { + if (deps.platform === "win32") { + try { + deps.killProcess(pid, signal); + } catch (error) { + log(`Error sending ${signal} to pid=${pid}`, error); + } + + return; + } + + try { + deps.killProcess(-pid, signal); + } catch { + try { + deps.killProcess(pid, signal); + } catch (error) { + log(`Error sending ${signal} to pid=${pid}`, error); + } + } +} diff --git a/src/tests/process-manager.test.ts b/src/tests/process-manager.test.ts index bb9de81..c40d226 100644 --- a/src/tests/process-manager.test.ts +++ b/src/tests/process-manager.test.ts @@ -35,7 +35,7 @@ function getBaseConfig(): AppRuntimeConfig { test("startNodeCG validates NodeCG installation before starting", async () => { const manager = createNodecgProcessManager({ isDev: true, - nodecgRootPath: "/fake/nodecg", + nodecgRootPath: "/fake/scoreko-dev", nodecgBaseUrl: "http://127.0.0.1:9090", appConfig: getBaseConfig(), log: () => undefined, @@ -49,13 +49,13 @@ test("startNodeCG validates NodeCG installation before starting", async () => { await assert.rejects(async () => { await manager.startNodecgProcess(); - }, /NodeCG folder does not exist/); + }, /Scoreko app folder does not exist/); }); test("startNodeCG fails when there are no read/write permissions", async () => { const manager = createNodecgProcessManager({ isDev: true, - nodecgRootPath: "/fake/nodecg", + nodecgRootPath: "/fake/scoreko-dev", nodecgBaseUrl: "http://127.0.0.1:9090", appConfig: getBaseConfig(), log: () => undefined, @@ -67,14 +67,14 @@ test("startNodeCG fails when there are no read/write permissions", async () => { await assert.rejects(async () => { await manager.startNodecgProcess(); - }, /No read\/write permissions on NodeCG/); + }, /No read\/write permissions on scoreko app folder/); }); test("waitForNodeCGReady resolves when endpoint returns 404", async () => { const child = new MockChildProcess(4321); const manager = createNodecgProcessManager({ isDev: true, - nodecgRootPath: "/fake/nodecg", + nodecgRootPath: "/fake/scoreko-dev", nodecgBaseUrl: "http://127.0.0.1:9090", appConfig: getBaseConfig(), log: () => undefined, @@ -107,7 +107,7 @@ test("stopNodeCG sends SIGTERM and then SIGKILL if the process does not exit", a const manager = createNodecgProcessManager({ isDev: true, - nodecgRootPath: "/fake/nodecg", + nodecgRootPath: "/fake/scoreko-dev", nodecgBaseUrl: "http://127.0.0.1:9090", appConfig: getBaseConfig(), log: () => undefined, @@ -153,7 +153,7 @@ test("stopNodeCG reuses the same promise when invoked in parallel", async () => const manager = createNodecgProcessManager({ isDev: true, - nodecgRootPath: "/fake/nodecg", + nodecgRootPath: "/fake/scoreko-dev", nodecgBaseUrl: "http://127.0.0.1:9090", appConfig: getBaseConfig(), log: () => undefined, @@ -186,7 +186,7 @@ test("stopNodeCG normalizes negative timeout to zero", async () => { const manager = createNodecgProcessManager({ isDev: true, - nodecgRootPath: "/fake/nodecg", + nodecgRootPath: "/fake/scoreko-dev", nodecgBaseUrl: "http://127.0.0.1:9090", appConfig: { ...getBaseConfig(), @@ -222,7 +222,7 @@ test("stopNodeCG normalizes negative timeout to zero", async () => { test("startNodeCG fails if the port is already in use", async () => { const manager = createNodecgProcessManager({ isDev: true, - nodecgRootPath: "/fake/nodecg", + nodecgRootPath: "/fake/scoreko-dev", nodecgBaseUrl: "http://127.0.0.1:9090", appConfig: getBaseConfig(), log: () => undefined, @@ -242,7 +242,7 @@ test("waitForNodeCGReady exposes diagnostics when NodeCG exits before readiness" const child = new MockChildProcess(4242); const manager = createNodecgProcessManager({ isDev: true, - nodecgRootPath: "/fake/nodecg", + nodecgRootPath: "/fake/scoreko-dev", nodecgBaseUrl: "http://127.0.0.1:9090", appConfig: getBaseConfig(), log: () => undefined, @@ -275,7 +275,7 @@ test("waitForNodeCGReady exposes diagnostics when NodeCG exits before readiness" assert.ok(error instanceof Error); assert.match(error.message, /NodeCG exited before becoming ready/); assert.match(error.message, /Last recorded exit/); - assert.match(error.message, /NodeCG path: \/fake\/nodecg/); + assert.match(error.message, /NodeCG path: \/fake\/scoreko-dev/); return true; }, );