Skip to content

Commit 0217d0f

Browse files
committed
feat: update quickstart examples to use ESM and NodeRuntime API
All quickstart examples now use ESM syntax (import/export) instead of CJS (require/module.exports) and the NodeRuntime API instead of the kernel API. Adds a new multi-file ESM example showing VFS imports.
1 parent d9dfce4 commit 0217d0f

11 files changed

Lines changed: 168 additions & 99 deletions

File tree

docs-internal/todo.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ docs-internal/specs/cli-tool-e2e.md
169169

170170
- [x] Define the minimal driver surface needed for Rivet integration. *(done — `RuntimeDriver` interface in `packages/kernel/src/types.ts`)*
171171

172+
- [ ] Support long-running processes (e.g. dev servers) without `await new Promise(() => {})` — sandbox should keep exec alive while active handles (listeners, timers) exist, matching Node's event loop semantics.
173+
172174
- [ ] Add a codemode example.
173175
- Provide a focused example that demonstrates secure-exec usage in a realistic tool flow.
174176
- Files: `examples/`

docs/quickstart.mdx

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ icon: "rocket"
4646
Use `runtime.run()` to execute JavaScript and get back exported values. Use `runtime.exec()` for scripts that produce console output.
4747

4848
<CodeGroup>
49-
```ts Run & Get Exports
49+
```ts Simple
5050
import {
5151
NodeRuntime,
5252
createNodeDriver,
@@ -59,15 +59,15 @@ icon: "rocket"
5959
});
6060

6161
const result = await runtime.run<{ message: string }>(
62-
`module.exports = { message: "hello from secure-exec" };`
62+
`export const message = "hello from secure-exec";`
6363
);
6464

6565
console.log(result.exports?.message); // "hello from secure-exec"
6666

6767
runtime.dispose();
6868
```
6969

70-
```ts Execute & Capture Output
70+
```ts Logging
7171
import {
7272
NodeRuntime,
7373
createNodeDriver,
@@ -105,24 +105,24 @@ icon: "rocket"
105105
const runtime = new NodeRuntime({
106106
systemDriver: createNodeDriver({
107107
filesystem,
108-
permissions: { fs: allowAllFs },
108+
permissions: { ...allowAllFs },
109109
}),
110110
runtimeDriverFactory: createNodeRuntimeDriverFactory(),
111111
});
112112

113113
await runtime.exec(`
114-
const fs = require("node:fs");
114+
import fs from "node:fs";
115115
fs.mkdirSync("/workspace", { recursive: true });
116116
fs.writeFileSync("/workspace/hello.txt", "hello from the sandbox");
117-
`);
117+
`, { filePath: "/entry.mjs" });
118118

119119
const bytes = await filesystem.readFile("/workspace/hello.txt");
120120
console.log(new TextDecoder().decode(bytes)); // "hello from the sandbox"
121121

122122
runtime.dispose();
123123
```
124124

125-
```ts Network Access
125+
```ts Fetch
126126
import {
127127
NodeRuntime,
128128
createNodeDriver,
@@ -132,7 +132,7 @@ icon: "rocket"
132132

133133
const runtime = new NodeRuntime({
134134
systemDriver: createNodeDriver({
135-
permissions: { network: allowAllNetwork },
135+
permissions: { ...allowAllNetwork },
136136
}),
137137
runtimeDriverFactory: createNodeRuntimeDriverFactory(),
138138
onStdio: (event) => {
@@ -148,24 +148,48 @@ icon: "rocket"
148148
runtime.dispose();
149149
```
150150

151-
```ts ESM Modules
151+
```ts Multi-File
152152
import {
153153
NodeRuntime,
154154
createNodeDriver,
155155
createNodeRuntimeDriverFactory,
156+
createInMemoryFileSystem,
157+
allowAllFs,
156158
} from "secure-exec";
157159

160+
const filesystem = createInMemoryFileSystem();
161+
162+
// Write module files to the virtual filesystem
163+
await filesystem.writeFile(
164+
"/app/math.mjs",
165+
`export function add(a, b) { return a + b; }`
166+
);
167+
await filesystem.writeFile(
168+
"/app/greet.mjs",
169+
`export function greet(name) { return "hello, " + name; }`
170+
);
171+
158172
const runtime = new NodeRuntime({
159-
systemDriver: createNodeDriver(),
173+
systemDriver: createNodeDriver({
174+
filesystem,
175+
permissions: { ...allowAllFs },
176+
}),
160177
runtimeDriverFactory: createNodeRuntimeDriverFactory(),
161178
});
162179

163-
const result = await runtime.run<{ answer: number }>(
164-
`export const answer = 42;`,
165-
"/entry.mjs" // .mjs extension triggers ESM mode
180+
const result = await runtime.run<{ sum: number; greeting: string }>(
181+
`
182+
import { add } from "./math.mjs";
183+
import { greet } from "./greet.mjs";
184+
185+
export const sum = add(1, 2);
186+
export const greeting = greet("secure-exec");
187+
`,
188+
"/app/entry.mjs"
166189
);
167190

168-
console.log(result.exports?.answer); // 42
191+
console.log(result.exports?.sum); // 3
192+
console.log(result.exports?.greeting); // "hello, secure-exec"
169193

170194
runtime.dispose();
171195
```

examples/quickstart/scripts/verify-docs.mjs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ const docsPath = path.join(repoRoot, "docs/quickstart.mdx");
88

99
const expectedFiles = new Map([
1010
["Simple", "src/simple.ts"],
11-
["TypeScript", "src/typescript.ts"],
1211
["Logging", "src/logging.ts"],
1312
["Filesystem", "src/filesystem.ts"],
1413
["Fetch", "src/fetch.ts"],
15-
["HTTP Server (Hono)", "src/http-server-hono.ts"],
16-
["Run Command", "src/run-command.ts"],
14+
["Multi-File", "src/multi-file.ts"],
1715
]);
1816

1917
function normalizeTitle(title) {

examples/quickstart/src/fetch.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
import {
2-
createKernel,
3-
createInMemoryFileSystem,
4-
createNodeRuntime,
2+
NodeRuntime,
3+
createNodeDriver,
4+
createNodeRuntimeDriverFactory,
5+
allowAllNetwork,
56
} from "secure-exec";
67

7-
const kernel = createKernel({
8-
filesystem: createInMemoryFileSystem(),
9-
permissions: {
10-
network: () => ({ allow: true }),
8+
const runtime = new NodeRuntime({
9+
systemDriver: createNodeDriver({
10+
permissions: { ...allowAllNetwork },
11+
}),
12+
runtimeDriverFactory: createNodeRuntimeDriverFactory(),
13+
onStdio: (event) => {
14+
process.stdout.write(event.message);
1115
},
1216
});
13-
await kernel.mount(createNodeRuntime());
1417

15-
const result = await kernel.exec(`node -e "
16-
(async () => {
17-
const response = await fetch('https://example.com');
18-
console.log(response.status);
19-
})();
20-
"`);
18+
await runtime.exec(`
19+
const response = await fetch("https://example.com");
20+
console.log(response.status); // 200
21+
`);
2122

22-
console.log(result.stdout); // "200\n"
23-
24-
await kernel.dispose();
23+
runtime.dispose();
Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11
import {
2-
createKernel,
2+
NodeRuntime,
3+
createNodeDriver,
4+
createNodeRuntimeDriverFactory,
35
createInMemoryFileSystem,
4-
createNodeRuntime,
6+
allowAllFs,
57
} from "secure-exec";
68

79
const filesystem = createInMemoryFileSystem();
8-
const kernel = createKernel({
9-
filesystem,
10-
permissions: {
11-
fs: () => ({ allow: true }),
12-
},
10+
11+
const runtime = new NodeRuntime({
12+
systemDriver: createNodeDriver({
13+
filesystem,
14+
permissions: { ...allowAllFs },
15+
}),
16+
runtimeDriverFactory: createNodeRuntimeDriverFactory(),
1317
});
14-
await kernel.mount(createNodeRuntime());
1518

16-
await kernel.exec(`node -e "
17-
const fs = require('node:fs');
18-
fs.mkdirSync('/workspace', { recursive: true });
19-
fs.writeFileSync('/workspace/hello.txt', 'hello from the sandbox');
20-
"`);
19+
await runtime.exec(`
20+
import fs from "node:fs";
21+
fs.mkdirSync("/workspace", { recursive: true });
22+
fs.writeFileSync("/workspace/hello.txt", "hello from the sandbox");
23+
`, { filePath: "/entry.mjs" });
2124

2225
const bytes = await filesystem.readFile("/workspace/hello.txt");
2326
console.log(new TextDecoder().decode(bytes)); // "hello from the sandbox"
2427

25-
await kernel.dispose();
28+
runtime.dispose();

examples/quickstart/src/http-server-hono.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,15 @@ const runtime = new NodeRuntime({
1818

1919
// Start a Hono server inside the sandbox
2020
const execPromise = runtime.exec(`
21-
(async () => {
22-
const { Hono } = require("hono");
23-
const { serve } = require("@hono/node-server");
21+
import { Hono } from "hono";
22+
import { serve } from "@hono/node-server";
2423
25-
const app = new Hono();
26-
app.get("/", (c) => c.text("hello from hono"));
24+
const app = new Hono();
25+
app.get("/", (c) => c.text("hello from hono"));
2726
28-
serve({ fetch: app.fetch, port: ${port}, hostname: "127.0.0.1" });
29-
await new Promise(() => {});
30-
})();
31-
`);
27+
serve({ fetch: app.fetch, port: ${port}, hostname: "127.0.0.1" });
28+
await new Promise(() => {});
29+
`, { filePath: "/app/server.mjs" });
3230

3331
// Wait for the server to be ready, then fetch from the host
3432
const url = "http://127.0.0.1:" + port + "/";

examples/quickstart/src/logging.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import {
2-
createKernel,
3-
createInMemoryFileSystem,
4-
createNodeRuntime,
2+
NodeRuntime,
3+
createNodeDriver,
4+
createNodeRuntimeDriverFactory,
55
} from "secure-exec";
66

7-
const kernel = createKernel({
8-
filesystem: createInMemoryFileSystem(),
7+
const runtime = new NodeRuntime({
8+
systemDriver: createNodeDriver(),
9+
runtimeDriverFactory: createNodeRuntimeDriverFactory(),
10+
onStdio: (event) => {
11+
process.stdout.write(event.message);
12+
},
913
});
10-
await kernel.mount(createNodeRuntime());
1114

12-
const result = await kernel.exec(
13-
"node -e \"console.log('hello from secure-exec')\""
14-
);
15+
const result = await runtime.exec(`
16+
console.log("hello from secure-exec");
17+
`);
1518

16-
console.log(result.stdout); // "hello from secure-exec\n"
17-
console.log(result.stderr); // ""
19+
console.log("exit code:", result.code); // 0
1820

19-
await kernel.dispose();
21+
runtime.dispose();
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {
2+
NodeRuntime,
3+
createNodeDriver,
4+
createNodeRuntimeDriverFactory,
5+
createInMemoryFileSystem,
6+
allowAllFs,
7+
} from "secure-exec";
8+
9+
const filesystem = createInMemoryFileSystem();
10+
11+
// Write module files to the virtual filesystem
12+
await filesystem.writeFile(
13+
"/app/math.mjs",
14+
`export function add(a, b) { return a + b; }`
15+
);
16+
await filesystem.writeFile(
17+
"/app/greet.mjs",
18+
`export function greet(name) { return "hello, " + name; }`
19+
);
20+
21+
const runtime = new NodeRuntime({
22+
systemDriver: createNodeDriver({
23+
filesystem,
24+
permissions: { ...allowAllFs },
25+
}),
26+
runtimeDriverFactory: createNodeRuntimeDriverFactory(),
27+
});
28+
29+
const result = await runtime.run<{ sum: number; greeting: string }>(
30+
`
31+
import { add } from "./math.mjs";
32+
import { greet } from "./greet.mjs";
33+
34+
export const sum = add(1, 2);
35+
export const greeting = greet("secure-exec");
36+
`,
37+
"/app/entry.mjs"
38+
);
39+
40+
console.log(result.exports?.sum); // 3
41+
console.log(result.exports?.greeting); // "hello, secure-exec"
42+
43+
runtime.dispose();
Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
import {
2-
createKernel,
3-
createInMemoryFileSystem,
4-
createNodeRuntime,
2+
NodeRuntime,
3+
createNodeDriver,
4+
createNodeRuntimeDriverFactory,
5+
allowAllChildProcess,
56
} from "secure-exec";
67

7-
const kernel = createKernel({
8-
filesystem: createInMemoryFileSystem(),
9-
permissions: {
10-
childProcess: () => ({ allow: true }),
8+
const runtime = new NodeRuntime({
9+
systemDriver: createNodeDriver({
10+
permissions: { ...allowAllChildProcess },
11+
}),
12+
runtimeDriverFactory: createNodeRuntimeDriverFactory(),
13+
onStdio: (event) => {
14+
process.stdout.write(event.message);
1115
},
1216
});
13-
await kernel.mount(createNodeRuntime());
1417

15-
const result = await kernel.exec(`node -e "
16-
const { execSync } = require('node:child_process');
17-
console.log(execSync('node --version', { encoding: 'utf8' }).trim());
18-
"`);
18+
await runtime.exec(`
19+
import { execSync } from "node:child_process";
20+
console.log(execSync("node --version", { encoding: "utf8" }).trim());
21+
`, { filePath: "/entry.mjs" });
1922

20-
console.log(result.stdout); // e.g. "v22.x.x\n"
21-
22-
await kernel.dispose();
23+
runtime.dispose();

examples/quickstart/src/simple.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import {
2-
createKernel,
3-
createInMemoryFileSystem,
4-
createNodeRuntime,
2+
NodeRuntime,
3+
createNodeDriver,
4+
createNodeRuntimeDriverFactory,
55
} from "secure-exec";
66

7-
const kernel = createKernel({
8-
filesystem: createInMemoryFileSystem(),
7+
const runtime = new NodeRuntime({
8+
systemDriver: createNodeDriver(),
9+
runtimeDriverFactory: createNodeRuntimeDriverFactory(),
910
});
10-
await kernel.mount(createNodeRuntime());
1111

12-
const result = await kernel.exec(
13-
"node -e \"console.log('hello from secure-exec')\""
12+
const result = await runtime.run<{ message: string }>(
13+
`export const message = "hello from secure-exec";`
1414
);
1515

16-
console.log(result.stdout); // "hello from secure-exec\n"
16+
console.log(result.exports?.message); // "hello from secure-exec"
1717

18-
await kernel.dispose();
18+
runtime.dispose();

0 commit comments

Comments
 (0)