How Nodeterm Enforces Process Boundaries with Automated Tests
Nodeterm enforces its three-layer process architecture through dedicated no-electron test suites (src/core/no-electron.test.ts and src/server/no-electron.test.ts) that statically inspect every source file for forbidden electron imports and fail the CI pipeline on any violation.
Nodeterm is an open-source terminal and command-runner built on Electron, and its architecture splits code into three strict layers: main, core, and server. To ensure these boundaries are never accidentally crossed, the repository ships automated tests that act as a compile-time guard against illegal imports—something TypeScript alone cannot enforce because import statements remain valid JavaScript. This article explains how those tests work, why they matter, and how you can use them while contributing.
The Three-Process Architecture Behind Nodeterm
Nodeterm’s codebase is deliberately separated so that each process can run in its intended environment without hidden dependencies. According to the repository layout, the layers are:
| Layer | Responsibility | Directory |
|---|---|---|
| Main | Node/Electron main process – owns windows, IPC, dialogs, and the concrete CorePlatform implementation. |
src/main/ |
| Core | Pure‑logic service layer – contains PTY handling, workspace stores, Git integration, remote‑SSH support, and all agent logic. It must never import Electron APIs. | src/core/ |
| Server | Browser‑only “Server Edition” – runs a plain HTTP + WebSocket server, the same core services, and a bridge shim for the renderer. It also must stay Electron‑free. | src/server/ |
Because the core and server layers must both run in non‑Electron environments (like a plain browser), the forbidden import rules are strict. A single stray import { app } from 'electron' inside src/core/ would break the Server Edition bundle at runtime.
How the Core-Side Boundary Test Works
The test src/core/no-electron.test.ts is the main guard for the core layer. It imports every file under src/core/ and checks that none of them contain:
- An import of the
electronpackage itself. - Any module that lives under
src/main/(the Electron‑dependent area).
If an illegal import is found, the test fails immediately, blocking the merge. The mechanism is simple: it parses each file’s AST (Abstract Syntax Tree) and looks for ImportDeclaration nodes whose source value starts with electron or ../main/.
Here is the conceptual pseudo‑code that mirrors what the real test does:
// Pseudo‑code – the real test uses a custom loader and AST inspection.
import { getAllFiles } from 'some-glob-helper';
import { parse } from '@babel/parser';
const coreFiles = getAllFiles('src/core/**/*.ts');
for (const file of coreFiles) {
const ast = parse(file.content, { sourceType: 'module' });
// Look for import declarations that reference forbidden packages.
const forbidden = ast.program.body.some(node =>
node.type === 'ImportDeclaration' &&
(node.source.value === 'electron' ||
node.source.value.startsWith('../main/'))
);
if (forbidden) {
throw new Error(`Core file ${file.path} must not import Electron.`);
}
}
The test is run automatically by the CI pipeline via npm test. Because it fails fast, any accidental cross‑layer import is caught before code is merged.
Server-Side Boundary Test Architecture
The server layer follows the exact same pattern with src/server/no-electron.test.ts. This test validates that the server code does not import Electron‑specific modules, which keeps the Server Edition fully platform‑agnostic and ensures that any Electron‑only APIs are reachable only through the main process bridge.
The test is structured identically to the core one, scanning all src/server/**/*.ts files for forbidden imports. This layer exists so the Server Edition can run in a plain browser environment without crashing when Electron isn’t present.
Why Boundary Enforcement Matters for Nodeterm
The no-electron tests are more than a style preference; they solve real production problems:
- Server-only builds – The Server Edition shares the same core code but runs inside a plain browser. If core code imported Electron APIs, the bundled server build would crash at runtime. The tests prevent this scenario from ever reaching production.
- Cross-platform consistency – The same core code runs on macOS, Linux, Windows, and the browser. Enforcing the boundary ensures feature parity without hidden platform‑specific bugs.
- Future-proofing – As new layers (e.g., a mobile companion) are added, the test pattern can be replicated to keep each layer cleanly separated.
Running the Boundary Tests Locally
While contributing to Nodeterm, you can run specific test suites from your terminal:
# Run only the core boundary test
npm run test -- src/core/no-electron.test.ts
# Run only the server boundary test
npm run test -- src/server/no-electron.test.ts
Best Practices When Adding New Modules
When adding new code to the core layer, you must ensure it only depends on core‑internal modules:
// Correct: core‑only import
import { WorkspaceStore } from './workspace-store';
// ❌ Incorrect: electron import (will make the test fail)
import { ipcRenderer } from 'electron';
If the second import is added, the CI test src/core/no-electron.test.ts will flag the file, and the developer will be forced to move that logic to src/main/ or expose it through the established bridge (via src/preload/).
The same rule applies when implementing a new Server‑only feature:
// ✅ Allowed
import { CorePlatform } from '../core/platform';
// ❌ Disallowed – pulls in Electron
import { BrowserWindow } from 'electron';
Attempting the disallowed import will cause src/server/no-electron.test.ts to fail.
Key Files Summary
| File | Role | Link |
|---|---|---|
src/core/no-electron.test.ts |
Guarantees the core layer stays free of Electron imports. | https://github.com/eneskirca/nodeterm/blob/main/src/core/no-electron.test.ts |
src/server/no-electron.test.ts |
Guarantees the server layer stays free of Electron imports. | https://github.com/eneskirca/nodeterm/blob/main/src/server/no-electron.test.ts |
src/main/ |
Main Electron process – contains the bridge (src/preload/) and platform‑specific implementations. |
https://github.com/eneskirca/nodeterm/tree/main/src/main |
src/core/ |
Platform‑agnostic core services – PTY manager, workspace store, agents, etc. | https://github.com/eneskirca/nodeterm/tree/main/src/core |
src/server/ |
Server Edition – HTTP server, WS bridge, and UI shim for the browser. | https://github.com/eneskirca/nodeterm/tree/main/src/server |
Summary
- Nodeterm has three separate layers:
src/main/(Electron‑only),src/core/(platform‑agnostic), andsrc/server/(browser‑only). - Two dedicated test files (
src/core/no-electron.test.tsandsrc/server/no-electron.test.ts) enforce these boundaries by scanning every source file for forbiddenelectronorsrc/main/imports. - The tests run in CI via
npm testand fail fast, preventing cross‑layer dependencies from being merged. - These guards are essential because TypeScript alone cannot catch these invalid imports, and the Server Edition must run without Electron APIs.
Frequently Asked Questions
How does nodeterm's test system prevent Electron imports in the core layer?
Nodeterm uses an AST‑based scanner in src/core/no-electron.test.ts that reads every .ts file under src/core/, parses its import statements, and fails the test if an import references the electron package or any module under src/main/. This runs in CI and blocks merges.
Can TypeScript compiler settings enforce these same boundaries?
No, TypeScript’s import analysis only checks if a module resolves; it cannot block legitimate but undesirable imports. Nodeterm’s tests are a custom static analysis layer that second‑guesses TypeScript’s resolution, catching the bad imports that are otherwise valid JavaScript.
Which test files should I run when adding a new core or server module?
Run npm run test -- src/core/no-electron.test.ts when you modify code under src/core/, and run npm run test -- src/server/no-electron.test.ts when you change code under src/server/. Both are invoked automatically by the full npm test CI job.
What happens if the test fails after I add a new import?
If the test fails, you must remove the forbidden import and refactor the code so the Electron‑specific API resides in src/main/ or is exposed through the preload bridge. The failed CI job prevents the change from being merged until the boundary is restored.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →