Skip to content

Commit 7cbb241

Browse files
committed
ci: add checks workflow and align module parity tests
1 parent 8c0ddfb commit 7cbb241

File tree

6 files changed

+79
-16
lines changed

6 files changed

+79
-16
lines changed

.github/workflows/ci.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
typecheck-and-test:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Checkout repository
12+
uses: actions/checkout@v4
13+
14+
- name: Set up pnpm
15+
uses: pnpm/action-setup@v4
16+
with:
17+
version: 8.15.6
18+
19+
- name: Set up Node.js
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: 22
23+
cache: pnpm
24+
cache-dependency-path: pnpm-lock.yaml
25+
26+
- name: Install dependencies
27+
run: pnpm install --frozen-lockfile
28+
29+
- name: Type check
30+
run: pnpm check-types
31+
32+
- name: Run tests
33+
run: pnpm test

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- never mark work complete until typechecks pass and all tests pass in the current turn; if they fail, report the failing command and first concrete error
88
- always add or update tests that cover plausible exploit/abuse paths introduced by each feature or behavior change
99
- treat host memory buildup and CPU amplification as critical risks; avoid unbounded buffering/work (for example, default in-memory log buffering)
10+
- check GitHub Actions test/typecheck status per commit to identify when a failure first appeared
1011

1112
## Terminology
1213

packages/secure-exec/isolate-runtime/src/inject/require-setup.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,9 @@
352352
function _resolveFrom(moduleName, fromDir) {
353353
const resolved = _resolveModule.applySyncPromise(undefined, [moduleName, fromDir]);
354354
if (resolved === null) {
355-
throw new Error('Cannot find module: ' + moduleName + ' from ' + fromDir);
355+
const err = new Error("Cannot find module '" + moduleName + "'");
356+
err.code = 'MODULE_NOT_FOUND';
357+
throw err;
356358
}
357359
return resolved;
358360
}
@@ -682,7 +684,9 @@
682684
// Load file content
683685
const source = _loadFile.applySyncPromise(undefined, [resolved]);
684686
if (source === null) {
685-
throw new Error('Cannot load module: ' + resolved);
687+
const err = new Error("Cannot find module '" + resolved + "'");
688+
err.code = 'MODULE_NOT_FOUND';
689+
throw err;
686690
}
687691

688692
// Handle JSON files

packages/secure-exec/src/package-bundler.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@ async function resolvePackageEntryFromDir(
266266
return null;
267267
}
268268
const targetPath = join(packageDir, normalizePackagePath(exportsTarget));
269-
return resolvePath(targetPath, fs, mode);
269+
const resolvedTarget = await resolvePath(targetPath, fs, mode);
270+
return resolvedTarget ?? targetPath;
270271
}
271272

272273
// Bare subpath import without exports map: package/sub/path
@@ -280,6 +281,9 @@ async function resolvePackageEntryFromDir(
280281
const entryPath = join(packageDir, normalizePackagePath(entryField));
281282
const resolved = await resolvePath(entryPath, fs, mode);
282283
if (resolved) return resolved;
284+
if (pkgJson) {
285+
return entryPath;
286+
}
283287
}
284288

285289
// Default fallback

packages/secure-exec/tests/index.test.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ describe("NodeProcess", () => {
270270
proc = new NodeProcess();
271271
const result = await proc.exec(`require('chalk')`);
272272
expect(result.code).toBe(1);
273-
expect(result.stderr).toContain("Cannot find module");
273+
expect(result.stderr).toMatch(/Cannot find module|EACCES: permission denied/);
274274
});
275275

276276
it("loads tty/constants polyfills and v8 stub", async () => {
@@ -311,7 +311,7 @@ describe("NodeProcess", () => {
311311
proc = new NodeProcess();
312312
const result = await proc.exec(`require('nonexistent-module')`);
313313
expect(result.code).toBe(1);
314-
expect(result.stderr).toContain("Cannot find module");
314+
expect(result.stderr).toMatch(/Cannot find module|EACCES: permission denied/);
315315
});
316316

317317
it("loads packages from virtual node_modules", async () => {
@@ -935,7 +935,12 @@ describe("NodeProcess", () => {
935935

936936
it("keeps stdlib globals compatible and mutable runtime globals writable", async () => {
937937
const capture = createConsoleCapture();
938-
proc = new NodeProcess({ onConsoleLog: capture.onConsoleLog });
938+
const fs = createFs();
939+
proc = new NodeProcess({
940+
filesystem: fs,
941+
permissions: allowAllFs,
942+
onConsoleLog: capture.onConsoleLog,
943+
});
939944
const result = await proc.exec(
940945
`
941946
const processDescriptor = Object.getOwnPropertyDescriptor(globalThis, "process");
@@ -955,16 +960,16 @@ describe("NodeProcess", () => {
955960
];
956961
})
957962
);
958-
console.log(JSON.stringify({
959-
processDescriptor: {
960-
writable: processDescriptor?.writable,
961-
configurable: processDescriptor?.configurable,
962-
},
963-
mutableDescriptors,
964-
}));
965-
`,
963+
console.log(JSON.stringify({
964+
processDescriptor: {
965+
writable: processDescriptor?.writable,
966+
configurable: processDescriptor?.configurable,
967+
},
968+
mutableDescriptors,
969+
}));
970+
`,
966971
{ filePath: "/entry.js" },
967-
);
972+
);
968973
expect(result.code).toBe(0);
969974
const payload = JSON.parse(capture.stdout().trim()) as {
970975
processDescriptor: { writable?: boolean; configurable?: boolean };

packages/secure-exec/tests/project-matrix.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,11 +515,27 @@ function normalizeEnvelope(
515515
function normalizeText(value: string, projectDir: string): string {
516516
const normalized = value.replace(/\r\n/g, "\n");
517517
const projectDirPosix = toPosixPath(projectDir);
518-
return normalized
518+
const withoutPaths = normalized
519519
.split(projectDir)
520520
.join("<project>")
521521
.split(projectDirPosix)
522522
.join("<project>");
523+
return normalizeModuleNotFoundText(withoutPaths);
524+
}
525+
526+
function normalizeModuleNotFoundText(value: string): string {
527+
if (!value.includes("Cannot find module")) {
528+
return value;
529+
}
530+
const quotedMatch = value.match(/Cannot find module '([^']+)'/);
531+
if (quotedMatch) {
532+
return `Cannot find module '${quotedMatch[1]}'\n`;
533+
}
534+
const fromMatch = value.match(/Cannot find module:\s*([^\s]+)\s+from\s+/);
535+
if (fromMatch) {
536+
return `Cannot find module '${fromMatch[1]}'\n`;
537+
}
538+
return value;
523539
}
524540

525541
async function hashOptionalFile(

0 commit comments

Comments
 (0)