Skip to content

Commit 8b80765

Browse files
committed
feat: [US-009] - [Fix module.exports capture in NodeRuntime.run()]
1 parent 8d1b871 commit 8b80765

3 files changed

Lines changed: 73 additions & 1 deletion

File tree

packages/secure-exec/tests/runtime-driver/node/standalone-dist-smoke.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,67 @@ describe("standalone dist bootstrap", () => {
9999
expect(result.kernelCode).toBe(0);
100100
expect(result.kernelStdout).toContain("1");
101101
}, 30_000);
102+
103+
it("supports runtime.run exports outside vitest transforms", async () => {
104+
const stdout = await runStandaloneScript(`
105+
import {
106+
NodeRuntime,
107+
createNodeDriver,
108+
createNodeRuntimeDriverFactory,
109+
} from ${JSON.stringify(DIST_INDEX_URL)};
110+
111+
const runtime = new NodeRuntime({
112+
systemDriver: createNodeDriver(),
113+
runtimeDriverFactory: createNodeRuntimeDriverFactory(),
114+
});
115+
116+
const cjsObject = await runtime.run('module.exports = { message: "hello" };');
117+
const cjsScalar = await runtime.run("module.exports = 42;");
118+
const cjsNested = await runtime.run("module.exports = { a: 1, b: [2, 3] };");
119+
const esm = await runtime.run("export const answer = 42;", "/entry.mjs");
120+
121+
const result = JSON.stringify({
122+
cjsObject,
123+
cjsScalar,
124+
cjsNested,
125+
esm,
126+
});
127+
128+
await runtime.terminate();
129+
await new Promise((resolve, reject) => {
130+
process.stdout.write(result, (error) => {
131+
if (error) {
132+
reject(error);
133+
return;
134+
}
135+
resolve();
136+
});
137+
});
138+
process.exit(0);
139+
`);
140+
141+
const result = JSON.parse(stdout) as {
142+
cjsObject: { code: number; exports: { message: string } };
143+
cjsScalar: { code: number; exports: number };
144+
cjsNested: { code: number; exports: { a: number; b: number[] } };
145+
esm: { code: number; exports: { answer: number } };
146+
};
147+
148+
expect(result.cjsObject).toEqual({
149+
code: 0,
150+
exports: { message: "hello" },
151+
});
152+
expect(result.cjsScalar).toEqual({
153+
code: 0,
154+
exports: 42,
155+
});
156+
expect(result.cjsNested).toEqual({
157+
code: 0,
158+
exports: { a: 1, b: [2, 3] },
159+
});
160+
expect(result.esm).toEqual({
161+
code: 0,
162+
exports: { answer: 42 },
163+
});
164+
}, 30_000);
102165
});

scripts/ralph/prd.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@
172172
"Typecheck passes"
173173
],
174174
"priority": 9,
175-
"passes": false,
175+
"passes": true,
176176
"notes": "runtime.run() completes with exit code 0 but result.exports is always undefined. The export extraction in runtimeDriver.run() isn't capturing module.exports. May be related to US-008 bridge bootstrap issue \u2014 if require is undefined, module.exports assignment can't work either."
177177
},
178178
{

scripts/ralph/progress.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- `bridge-initial-globals.ts` intentionally removes `SharedArrayBuffer`; when vendored conformance files hard-require it, classify the expectation as a `security-constraint` instead of treating the failure as a crypto implementation bug
88
- When a crypto/node-conformance expectation reason looks stale, temporarily remove just that entry and rerun the exact vendored file filter; the runner prints the real sandbox stderr, which is often a missing fixture or error-shape mismatch rather than the older bridge-gap guess
99
- Host-side ESM packages in `packages/nodejs/src/` cannot use bare `require(...)`; standalone `dist/` execution runs them as real ESM, so bridge helpers must use static imports or `createRequire()`
10+
- Standalone `dist/` smoke tests that spawn `node --input-type=module -e ...` should flush `process.stdout.write(...)` via a callback and then `process.exit(0)`; otherwise the verification script can print the right JSON but linger long enough for Vitest to hit its timeout
1011
- Conformance tests live in packages/secure-exec/tests/node-conformance/ — vendored Node.js v22.14.0 test/parallel/
1112
- Runner is packages/secure-exec/tests/node-conformance/runner.test.ts — run with: pnpm vitest run packages/secure-exec/tests/node-conformance/runner.test.ts
1213
- Expectations are in packages/secure-exec/tests/node-conformance/expectations.json — each entry has expected (pass/fail/skip), reason, category
@@ -104,3 +105,11 @@
104105
- `packages/nodejs/src/bridge-handlers.ts` runs on the host as ESM, so any leftover `require("@secure-exec/core")` calls will only fail once the published-style `dist/` path is used.
105106
- When a spawned verification script only needs to report pass/fail state, explicitly flushing stdout and exiting keeps the smoke test focused on bootstrap behavior instead of shared-runtime process lifetime.
106107
---
108+
## [2026-03-25 04:31 PDT] - US-009
109+
- Verified the underlying `runtime.run()` export capture issue is already fixed on the current branch and added standalone `dist/` regression coverage for CommonJS object/scalar/nested `module.exports` plus ESM named exports.
110+
- Files changed: `packages/secure-exec/tests/runtime-driver/node/standalone-dist-smoke.test.ts`, `scripts/ralph/prd.json`, `scripts/ralph/progress.txt`
111+
- **Learnings for future iterations:**
112+
- `US-008`'s standalone bootstrap fix also restored `runtime.run()` export capture from `packages/secure-exec/dist/index.js`; the missing piece was regression coverage, not another runtime patch.
113+
- A standalone smoke that validates multiple `run()` calls should flush stdout through the write callback and exit explicitly after `runtime.terminate()` to keep Vitest from timing out on a lingering host process.
114+
- This test area is best exercised against built `dist/` imports, because source-level Vitest coverage already passes and does not prove publish-style behavior.
115+
---

0 commit comments

Comments
 (0)