@ricsam/quickjs-runtime 0.2.23 → 0.2.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -13
- package/dist/cjs/create-runtime.cjs +118 -47
- package/dist/cjs/create-runtime.cjs.map +3 -3
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/create-runtime.mjs +123 -49
- package/dist/mjs/create-runtime.mjs.map +3 -3
- package/dist/mjs/index.mjs.map +2 -2
- package/dist/mjs/package.json +1 -1
- package/dist/types/create-runtime.d.ts +43 -15
- package/dist/types/index.d.ts +1 -1
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -69,11 +69,11 @@ const runtime = await createRuntime({
|
|
|
69
69
|
customFunctions: {
|
|
70
70
|
hashPassword: {
|
|
71
71
|
fn: async (password) => Bun.password.hash(password),
|
|
72
|
-
|
|
72
|
+
type: 'async',
|
|
73
73
|
},
|
|
74
74
|
getConfig: {
|
|
75
75
|
fn: () => ({ environment: "production" }),
|
|
76
|
-
|
|
76
|
+
type: 'sync',
|
|
77
77
|
},
|
|
78
78
|
},
|
|
79
79
|
|
|
@@ -100,7 +100,7 @@ interface RuntimeHandle {
|
|
|
100
100
|
readonly id: string;
|
|
101
101
|
|
|
102
102
|
// Execute code as ES module (supports top-level await)
|
|
103
|
-
eval(code: string,
|
|
103
|
+
eval(code: string, filenameOrOptions?: string | EvalOptions): Promise<void>;
|
|
104
104
|
|
|
105
105
|
// Dispose all resources
|
|
106
106
|
dispose(): Promise<void>;
|
|
@@ -112,6 +112,38 @@ interface RuntimeHandle {
|
|
|
112
112
|
readonly testEnvironment: RuntimeTestEnvironmentHandle;
|
|
113
113
|
readonly playwright: RuntimePlaywrightHandle;
|
|
114
114
|
}
|
|
115
|
+
|
|
116
|
+
interface EvalOptions {
|
|
117
|
+
// Filename for the evaluated code (for stack traces)
|
|
118
|
+
filename?: string;
|
|
119
|
+
// Maximum execution time in milliseconds
|
|
120
|
+
maxExecutionMs?: number;
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Execution Timeout
|
|
125
|
+
|
|
126
|
+
Use `maxExecutionMs` to prevent infinite loops and long-running code:
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const runtime = await createRuntime();
|
|
130
|
+
|
|
131
|
+
// Set a 5 second timeout
|
|
132
|
+
await runtime.eval(`
|
|
133
|
+
// Code that completes quickly
|
|
134
|
+
const result = compute();
|
|
135
|
+
`, { maxExecutionMs: 5000 });
|
|
136
|
+
|
|
137
|
+
// Infinite loops will be interrupted
|
|
138
|
+
try {
|
|
139
|
+
await runtime.eval(`
|
|
140
|
+
while (true) { /* infinite loop */ }
|
|
141
|
+
`, { maxExecutionMs: 100 });
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.log("Execution timed out");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await runtime.dispose();
|
|
115
147
|
```
|
|
116
148
|
|
|
117
149
|
## Examples
|
|
@@ -201,29 +233,94 @@ await browser.close();
|
|
|
201
233
|
|
|
202
234
|
### Custom Functions
|
|
203
235
|
|
|
236
|
+
Custom functions expose host capabilities to the sandbox. Arguments and return values are automatically marshalled between host and QuickJS.
|
|
237
|
+
|
|
238
|
+
**Function types:**
|
|
239
|
+
- `type: 'sync'` - Synchronous function
|
|
240
|
+
- `type: 'async'` - Returns a Promise
|
|
241
|
+
- `type: 'asyncIterator'` - Returns an async iterable (for streaming)
|
|
242
|
+
|
|
204
243
|
```typescript
|
|
205
244
|
const runtime = await createRuntime({
|
|
206
245
|
customFunctions: {
|
|
246
|
+
// Sync function
|
|
247
|
+
generateId: {
|
|
248
|
+
fn: () => crypto.randomUUID(),
|
|
249
|
+
type: 'sync',
|
|
250
|
+
},
|
|
207
251
|
// Async function
|
|
208
252
|
hashPassword: {
|
|
209
253
|
fn: async (password) => Bun.password.hash(password),
|
|
210
|
-
|
|
254
|
+
type: 'async',
|
|
211
255
|
},
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
fn:
|
|
215
|
-
|
|
256
|
+
// Async iterator for streaming
|
|
257
|
+
streamData: {
|
|
258
|
+
fn: async function* (count) {
|
|
259
|
+
for (let i = 0; i < count; i++) {
|
|
260
|
+
await new Promise(r => setTimeout(r, 100));
|
|
261
|
+
yield { index: i, timestamp: Date.now() };
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
type: 'asyncIterator',
|
|
216
265
|
},
|
|
217
266
|
},
|
|
218
267
|
});
|
|
219
268
|
|
|
220
269
|
await runtime.eval(`
|
|
221
|
-
const hash = await hashPassword("secret123");
|
|
222
270
|
const id = generateId();
|
|
223
|
-
|
|
271
|
+
const hash = await hashPassword("secret123");
|
|
272
|
+
|
|
273
|
+
for await (const item of streamData(3)) {
|
|
274
|
+
console.log(item); // { index: 0, timestamp: ... }, etc.
|
|
275
|
+
}
|
|
224
276
|
`);
|
|
225
277
|
```
|
|
226
278
|
|
|
279
|
+
**Supported return types** (auto-marshalled):
|
|
280
|
+
|
|
281
|
+
| Type | Notes |
|
|
282
|
+
|------|-------|
|
|
283
|
+
| `string`, `number`, `boolean`, `null`, `undefined`, `bigint` | Primitives |
|
|
284
|
+
| `{ key: value }` | Plain objects (nested supported, max depth 10) |
|
|
285
|
+
| `[1, 2, 3]` | Arrays |
|
|
286
|
+
| `Date` | Becomes QuickJS Date |
|
|
287
|
+
| `Uint8Array`, `ArrayBuffer` | Binary data |
|
|
288
|
+
| `Promise` | Becomes QuickJS Promise |
|
|
289
|
+
| Functions | Become callable from QuickJS (see below) |
|
|
290
|
+
|
|
291
|
+
**Returning callable functions:**
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
customFunctions: {
|
|
295
|
+
// Return a factory function
|
|
296
|
+
createMultiplier: {
|
|
297
|
+
fn: (factor) => (x) => x * factor,
|
|
298
|
+
type: 'sync',
|
|
299
|
+
},
|
|
300
|
+
// Return an object with methods
|
|
301
|
+
getDatabase: {
|
|
302
|
+
fn: async () => {
|
|
303
|
+
const db = await connectToDb();
|
|
304
|
+
return {
|
|
305
|
+
query: async (sql) => db.query(sql),
|
|
306
|
+
close: () => db.close(),
|
|
307
|
+
};
|
|
308
|
+
},
|
|
309
|
+
type: 'async',
|
|
310
|
+
},
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
```javascript
|
|
315
|
+
// In sandbox:
|
|
316
|
+
const double = createMultiplier(2);
|
|
317
|
+
console.log(double(5)); // 10
|
|
318
|
+
|
|
319
|
+
const db = await getDatabase();
|
|
320
|
+
const users = await db.query("SELECT * FROM users");
|
|
321
|
+
db.close();
|
|
322
|
+
```
|
|
323
|
+
|
|
227
324
|
### ES Modules
|
|
228
325
|
|
|
229
326
|
```typescript
|
|
@@ -264,15 +361,16 @@ When you use `createRuntime()`, the following globals are automatically availabl
|
|
|
264
361
|
- **Path**: `path.join()`, `path.resolve()`, etc.
|
|
265
362
|
|
|
266
363
|
Optional (when configured):
|
|
267
|
-
- **File System**: `
|
|
364
|
+
- **File System**: `getDirectory()` (requires `fs` option)
|
|
268
365
|
- **Test Environment**: `describe`, `it`, `expect` (requires `testEnvironment` option)
|
|
269
366
|
- **Playwright**: `page` object (requires `playwright` option)
|
|
270
367
|
|
|
271
368
|
## Security
|
|
272
369
|
|
|
273
370
|
- **No automatic network access** - `fetch` option must be explicitly provided
|
|
274
|
-
- **File system isolation** - `
|
|
275
|
-
- **Memory limits** - Use `memoryLimitMB` option to prevent
|
|
371
|
+
- **File system isolation** - `getDirectory` controls all path access
|
|
372
|
+
- **Memory limits** - Use `memoryLimitMB` option to prevent memory exhaustion
|
|
373
|
+
- **Execution timeouts** - Use `maxExecutionMs` in `eval()` to prevent infinite loops
|
|
276
374
|
- **No access to host** - Code runs in isolated QuickJS VM
|
|
277
375
|
|
|
278
376
|
## Advanced: Low-level API
|
|
@@ -107,12 +107,18 @@ async function createRuntime(options) {
|
|
|
107
107
|
}
|
|
108
108
|
if (opts.customFunctions) {
|
|
109
109
|
for (const [name, def] of Object.entries(opts.customFunctions)) {
|
|
110
|
-
if (def.async) {
|
|
110
|
+
if (def.type === "async") {
|
|
111
111
|
const fn = import_quickjs_core.defineAsyncFunction(context, name, async (...args) => {
|
|
112
112
|
return def.fn(...args);
|
|
113
113
|
});
|
|
114
114
|
context.setProp(context.global, name, fn);
|
|
115
115
|
fn.dispose();
|
|
116
|
+
} else if (def.type === "asyncIterator") {
|
|
117
|
+
const fn = import_quickjs_core.defineAsyncIteratorFunction(context, name, (...args) => {
|
|
118
|
+
return def.fn(...args);
|
|
119
|
+
});
|
|
120
|
+
context.setProp(context.global, name, fn);
|
|
121
|
+
fn.dispose();
|
|
116
122
|
} else {
|
|
117
123
|
const fn = import_quickjs_core.defineFunction(context, name, (...args) => {
|
|
118
124
|
return def.fn(...args);
|
|
@@ -245,55 +251,119 @@ async function createRuntime(options) {
|
|
|
245
251
|
console: runtimeConsoleHandle,
|
|
246
252
|
testEnvironment: runtimeTestEnvironmentHandle,
|
|
247
253
|
playwright: runtimePlaywrightHandle,
|
|
248
|
-
async eval(code,
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
result.
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
throw new Error(`Promise rejected: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
254
|
+
async eval(code, filenameOrOptions) {
|
|
255
|
+
const evalOpts = typeof filenameOrOptions === "string" ? { filename: filenameOrOptions } : filenameOrOptions ?? {};
|
|
256
|
+
const filename = evalOpts.filename ?? "<eval>";
|
|
257
|
+
const maxExecutionMs = evalOpts.maxExecutionMs;
|
|
258
|
+
if (maxExecutionMs) {
|
|
259
|
+
const deadline = Date.now() + maxExecutionMs;
|
|
260
|
+
runtime.setInterruptHandler(import_quickjs_emscripten.shouldInterruptAfterDeadline(deadline));
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
if (isAsyncContext) {
|
|
264
|
+
const asyncContext = context;
|
|
265
|
+
const result = await asyncContext.evalCodeAsync(code, filename, {
|
|
266
|
+
type: "module"
|
|
267
|
+
});
|
|
268
|
+
if (result.error) {
|
|
269
|
+
const err = context.dump(result.error);
|
|
270
|
+
result.error.dispose();
|
|
271
|
+
throw new Error(`Eval failed: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
267
272
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
273
|
+
const promiseState = context.getPromiseState(result.value);
|
|
274
|
+
if (promiseState.type === "pending") {
|
|
275
|
+
runtime.executePendingJobs();
|
|
276
|
+
const resolved = await Promise.race([
|
|
277
|
+
context.resolvePromise(result.value),
|
|
278
|
+
(async () => {
|
|
279
|
+
for (let i = 0;i < 1e4; i++) {
|
|
280
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
281
|
+
runtime.executePendingJobs();
|
|
282
|
+
const state = context.getPromiseState(result.value);
|
|
283
|
+
if (state.type !== "pending") {
|
|
284
|
+
return { pollingResolved: true, state };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
throw new Error("Promise resolution timeout");
|
|
288
|
+
})()
|
|
289
|
+
]);
|
|
290
|
+
result.value.dispose();
|
|
291
|
+
runtime.executePendingJobs();
|
|
292
|
+
if ("pollingResolved" in resolved) {
|
|
293
|
+
if (resolved.state.type === "rejected") {
|
|
294
|
+
const err = context.dump(resolved.state.error);
|
|
295
|
+
resolved.state.error.dispose();
|
|
296
|
+
throw new Error(`Promise rejected: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
297
|
+
}
|
|
298
|
+
if (resolved.state.value) {
|
|
299
|
+
resolved.state.value.dispose();
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
if (resolved.error) {
|
|
303
|
+
const err = context.dump(resolved.error);
|
|
304
|
+
resolved.error.dispose();
|
|
305
|
+
throw new Error(`Promise rejected: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
306
|
+
}
|
|
307
|
+
resolved.value.dispose();
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
result.value.dispose();
|
|
291
311
|
}
|
|
292
|
-
|
|
312
|
+
runtime.executePendingJobs();
|
|
293
313
|
} else {
|
|
294
|
-
result.
|
|
314
|
+
const result = context.evalCode(code, filename, {
|
|
315
|
+
type: "module"
|
|
316
|
+
});
|
|
317
|
+
if (result.error) {
|
|
318
|
+
const err = context.dump(result.error);
|
|
319
|
+
result.error.dispose();
|
|
320
|
+
throw new Error(`Eval failed: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
321
|
+
}
|
|
322
|
+
const promiseState = context.getPromiseState(result.value);
|
|
323
|
+
if (promiseState.type === "pending") {
|
|
324
|
+
runtime.executePendingJobs();
|
|
325
|
+
const resolved = await Promise.race([
|
|
326
|
+
context.resolvePromise(result.value),
|
|
327
|
+
(async () => {
|
|
328
|
+
for (let i = 0;i < 1e4; i++) {
|
|
329
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
330
|
+
runtime.executePendingJobs();
|
|
331
|
+
const state = context.getPromiseState(result.value);
|
|
332
|
+
if (state.type !== "pending") {
|
|
333
|
+
return { pollingResolved: true, state };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
throw new Error("Promise resolution timeout");
|
|
337
|
+
})()
|
|
338
|
+
]);
|
|
339
|
+
result.value.dispose();
|
|
340
|
+
runtime.executePendingJobs();
|
|
341
|
+
if ("pollingResolved" in resolved) {
|
|
342
|
+
if (resolved.state.type === "rejected") {
|
|
343
|
+
const err = context.dump(resolved.state.error);
|
|
344
|
+
resolved.state.error.dispose();
|
|
345
|
+
throw new Error(`Promise rejected: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
346
|
+
}
|
|
347
|
+
if (resolved.state.value) {
|
|
348
|
+
resolved.state.value.dispose();
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
if (resolved.error) {
|
|
352
|
+
const err = context.dump(resolved.error);
|
|
353
|
+
resolved.error.dispose();
|
|
354
|
+
throw new Error(`Promise rejected: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
355
|
+
}
|
|
356
|
+
resolved.value.dispose();
|
|
357
|
+
}
|
|
358
|
+
} else {
|
|
359
|
+
result.value.dispose();
|
|
360
|
+
}
|
|
361
|
+
runtime.executePendingJobs();
|
|
362
|
+
}
|
|
363
|
+
} finally {
|
|
364
|
+
if (maxExecutionMs) {
|
|
365
|
+
runtime.removeInterruptHandler();
|
|
295
366
|
}
|
|
296
|
-
runtime.executePendingJobs();
|
|
297
367
|
}
|
|
298
368
|
},
|
|
299
369
|
async dispose() {
|
|
@@ -319,6 +389,7 @@ async function createRuntime(options) {
|
|
|
319
389
|
if (result.error)
|
|
320
390
|
result.error.dispose();
|
|
321
391
|
}
|
|
392
|
+
import_quickjs_core.cleanupAsyncIterators(context);
|
|
322
393
|
import_quickjs_core.cleanupUnmarshaledHandles(context);
|
|
323
394
|
import_quickjs_core.clearAllInstanceState();
|
|
324
395
|
try {
|
|
@@ -359,4 +430,4 @@ async function createRuntime(options) {
|
|
|
359
430
|
}
|
|
360
431
|
})
|
|
361
432
|
|
|
362
|
-
//# debugId=
|
|
433
|
+
//# debugId=66184374D0F003ED64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/create-runtime.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import {\n getQuickJS,\n newAsyncContext,\n type QuickJSAsyncContext,\n type QuickJSAsyncRuntime,\n type QuickJSContext,\n type QuickJSRuntime,\n} from \"quickjs-emscripten\";\nimport {\n setupCore,\n createStateMap,\n cleanupUnmarshaledHandles,\n clearAllInstanceState,\n defineFunction,\n defineAsyncFunction,\n type StateMap,\n type CoreHandle,\n} from \"@ricsam/quickjs-core\";\nimport {\n setupFetch,\n type SetupFetchOptions,\n type FetchHandle,\n type UpgradeRequest,\n type WebSocketCommand,\n} from \"@ricsam/quickjs-fetch\";\nimport {\n setupFs,\n type SetupFsOptions,\n type FsHandle,\n} from \"@ricsam/quickjs-fs\";\nimport {\n setupConsole,\n type ConsoleHandle,\n type ConsoleEntry,\n type ConsoleCallbacks,\n} from \"@ricsam/quickjs-console\";\nimport {\n setupEncoding,\n type EncodingHandle,\n} from \"@ricsam/quickjs-encoding\";\nimport {\n setupTimers,\n type TimersHandle,\n} from \"@ricsam/quickjs-timers\";\nimport {\n setupCrypto,\n type CryptoHandle,\n} from \"@ricsam/quickjs-crypto\";\nimport {\n setupTestEnvironment,\n type TestEnvironmentHandle,\n type RunResults,\n type TestEvent,\n} from \"@ricsam/quickjs-test-environment\";\nimport {\n setupPlaywright,\n type PlaywrightHandle,\n type PlaywrightOptions,\n type PlaywrightEvent,\n type BrowserConsoleLogEntry,\n type NetworkRequestInfo,\n type NetworkResponseInfo,\n} from \"@ricsam/quickjs-playwright\";\n\n// Re-export types\nexport type { ConsoleEntry, ConsoleCallbacks, TestEvent, PlaywrightEvent };\n\n/**\n * Module loader callback type.\n * Called when the runtime imports a module dynamically.\n * Returns the JavaScript source code for the module.\n */\nexport type ModuleLoaderCallback = (\n moduleName: string\n) => string | Promise<string>;\n\n/**\n * A custom function that can be called from within the runtime.\n */\nexport type CustomFunction = (...args: unknown[]) => unknown | Promise<unknown>;\n\n/**\n * Custom function definition with metadata.\n * Requires explicit `async` property to be clear about function behavior.\n */\nexport interface CustomFunctionDefinition {\n /** The function implementation */\n fn: CustomFunction;\n /** Whether the function is async (returns a Promise) */\n async: boolean;\n}\n\n/**\n * Custom functions to register in the runtime.\n * Each function must be defined with explicit async property.\n *\n * @example\n * ```typescript\n * customFunctions: {\n * // Async function\n * hashPassword: {\n * fn: async (password) => bcrypt.hash(password, 10),\n * async: true,\n * },\n * // Sync function\n * getConfig: {\n * fn: () => ({ environment: \"production\" }),\n * async: false,\n * },\n * }\n * ```\n */\nexport type CustomFunctions = Record<string, CustomFunctionDefinition>;\n\n/**\n * Fetch callback type.\n */\nexport type FetchCallback = (request: Request) => Response | Promise<Response>;\n\n/**\n * Test environment options.\n */\nexport interface TestEnvironmentOptions {\n /** Event callback for test events */\n onEvent?: (event: TestEvent) => void;\n /** Default test timeout in milliseconds */\n testTimeout?: number;\n}\n\n/**\n * Options for creating a runtime.\n */\nexport interface RuntimeOptions {\n /** Memory limit in megabytes (optional) */\n memoryLimitMB?: number;\n /** Console callback handlers */\n console?: ConsoleCallbacks;\n /** Fetch callback handler */\n fetch?: FetchCallback;\n /** File system options */\n fs?: SetupFsOptions;\n /** Module loader callback for resolving dynamic imports */\n moduleLoader?: ModuleLoaderCallback;\n /** Custom functions callable from within the runtime */\n customFunctions?: CustomFunctions;\n /** Current working directory for path operations. Defaults to \"/\" */\n cwd?: string;\n /** Test environment options. Set to true for defaults, or provide options. */\n testEnvironment?: boolean | TestEnvironmentOptions;\n /** Playwright options for browser automation */\n playwright?: PlaywrightOptions;\n}\n\n/**\n * Runtime fetch handle - provides access to fetch/serve operations.\n */\nexport interface RuntimeFetchHandle {\n /** Dispatch HTTP request to serve() handler */\n dispatchRequest(request: Request): Promise<Response>;\n\n /**\n * Check if the last request resulted in an upgrade request\n * Must be called immediately after dispatchRequest()\n */\n getUpgradeRequest(): UpgradeRequest | null;\n\n /**\n * Dispatch WebSocket open event\n * @param connectionId The connectionId from getUpgradeRequest()\n */\n dispatchWebSocketOpen(connectionId: string): void;\n\n /**\n * Dispatch WebSocket message event\n */\n dispatchWebSocketMessage(connectionId: string, message: string | ArrayBuffer): void;\n\n /**\n * Dispatch WebSocket close event\n */\n dispatchWebSocketClose(connectionId: string, code: number, reason: string): void;\n\n /**\n * Dispatch WebSocket error event\n */\n dispatchWebSocketError(connectionId: string, error: Error): void;\n\n /**\n * Register a callback for outgoing WebSocket messages/commands\n * Called when QuickJS code calls ws.send() or ws.close()\n */\n onWebSocketCommand(callback: (command: WebSocketCommand) => void): () => void;\n\n /** Check if serve() has been called */\n hasServeHandler(): boolean;\n\n /** Check if there are active WebSocket connections */\n hasActiveConnections(): boolean;\n}\n\n/**\n * Runtime timers handle - provides access to timer operations.\n */\nexport interface RuntimeTimersHandle {\n /** Clear all pending timers */\n clearAll(): void;\n}\n\n/**\n * Runtime console handle - provides access to console state.\n */\nexport interface RuntimeConsoleHandle {\n /** Reset all console state (timers, counters, group depth) */\n reset(): void;\n /** Get console.time() timers */\n getTimers(): Map<string, number>;\n /** Get console.count() counters */\n getCounters(): Map<string, number>;\n /** Get current console.group() nesting depth */\n getGroupDepth(): number;\n}\n\n/**\n * Runtime test environment handle - provides access to test operations.\n */\nexport interface RuntimeTestEnvironmentHandle {\n /** Run all registered tests */\n runTests(timeout?: number): Promise<RunResults>;\n /** Check if any tests have been registered */\n hasTests(): boolean;\n /** Get the number of registered tests */\n getTestCount(): number;\n /** Reset test state (clear registered tests) */\n reset(): void;\n}\n\n/**\n * Collected playwright data.\n */\nexport interface CollectedData {\n browserConsoleLogs: BrowserConsoleLogEntry[];\n networkRequests: NetworkRequestInfo[];\n networkResponses: NetworkResponseInfo[];\n}\n\n/**\n * Runtime playwright handle - provides access to playwright data.\n */\nexport interface RuntimePlaywrightHandle {\n /** Get collected browser logs and network data */\n getCollectedData(): CollectedData;\n /** Clear all collected data */\n clearCollectedData(): void;\n}\n\n/**\n * Runtime handle - the main interface for interacting with the runtime.\n */\nexport interface RuntimeHandle {\n /** Unique runtime identifier */\n readonly id: string;\n /** Execute code as ES module (supports top-level await) */\n eval(code: string, filename?: string): Promise<void>;\n /** Dispose all resources */\n dispose(): Promise<void>;\n\n /** Fetch handle - access to fetch/serve operations */\n readonly fetch: RuntimeFetchHandle;\n /** Timers handle - access to timer operations */\n readonly timers: RuntimeTimersHandle;\n /** Console handle - access to console state */\n readonly console: RuntimeConsoleHandle;\n /** Test environment handle - access to test operations */\n readonly testEnvironment: RuntimeTestEnvironmentHandle;\n /** Playwright handle - access to browser automation data */\n readonly playwright: RuntimePlaywrightHandle;\n}\n\n/**\n * Create a fully configured QuickJS runtime\n *\n * Sets up all WHATWG APIs: fetch, fs, console, crypto, encoding, timers.\n * Supports ES modules with custom module loader and custom host functions.\n *\n * @example\n * ```typescript\n * const runtime = await createRuntime({\n * console: { onEntry: (e) => console.log(\"[sandbox]\", e) },\n * fetch: async (request) => fetch(request),\n * moduleLoader: async (name) => {\n * if (name === \"@/utils\") {\n * return `export const add = (a, b) => a + b;`;\n * }\n * throw new Error(`Unknown module: ${name}`);\n * },\n * customFunctions: {\n * hashPassword: {\n * fn: async (pw) => Bun.password.hash(pw),\n * async: true,\n * },\n * },\n * });\n *\n * await runtime.eval(`\n * import { add } from \"@/utils\";\n * const hash = await hashPassword(\"secret\");\n * console.log(add(1, 2), hash);\n * `);\n *\n * await runtime.dispose();\n * ```\n */\nexport async function createRuntime(\n options?: RuntimeOptions\n): Promise<RuntimeHandle> {\n const opts = options ?? {};\n\n // Generate unique ID\n const id = crypto.randomUUID();\n\n // Determine if we need async context (for async module loader)\n const needsAsync = opts.moduleLoader !== undefined;\n\n let context: QuickJSContext | QuickJSAsyncContext;\n let runtime: QuickJSRuntime | QuickJSAsyncRuntime;\n let isAsyncContext = false;\n\n if (needsAsync) {\n // Create async context for module loading support\n const asyncContext = await newAsyncContext();\n context = asyncContext;\n runtime = asyncContext.runtime;\n isAsyncContext = true;\n\n // Set up module loader\n if (opts.moduleLoader) {\n const moduleLoader = opts.moduleLoader;\n (runtime as QuickJSAsyncRuntime).setModuleLoader(async (moduleName: string) => {\n try {\n const code = await moduleLoader(moduleName);\n return code;\n } catch (error) {\n // Return error in SuccessOrFail format\n return { error: error as Error };\n }\n });\n }\n } else {\n // Create sync context\n const QuickJS = await getQuickJS();\n runtime = QuickJS.newRuntime();\n context = runtime.newContext();\n }\n\n // Set memory limit if specified\n if (opts.memoryLimitMB) {\n runtime.setMemoryLimit(opts.memoryLimitMB * 1024 * 1024);\n }\n\n // Create shared state\n const stateMap = createStateMap();\n\n // Setup core APIs\n const coreHandle = setupCore(context, { stateMap });\n\n // Setup console\n const consoleHandle = setupConsole(context, {\n ...opts.console,\n stateMap,\n coreHandle,\n });\n\n // Setup encoding (btoa/atob)\n const encodingHandle = setupEncoding(context, { stateMap, coreHandle });\n\n // Setup timers\n const timersHandle = setupTimers(context, { stateMap, coreHandle });\n\n // Setup crypto\n const cryptoHandle = setupCrypto(context, { stateMap, coreHandle });\n\n // Setup fetch if callback provided\n let fetchHandle: FetchHandle | undefined;\n if (opts.fetch) {\n const fetchCallback = opts.fetch;\n fetchHandle = setupFetch(context, {\n stateMap,\n coreHandle,\n onFetch: async (request: Request) => {\n return Promise.resolve(fetchCallback(request));\n },\n });\n } else {\n // Still setup fetch for serve() support, but without fetch() function\n fetchHandle = setupFetch(context, { stateMap, coreHandle });\n }\n\n // Setup file system if provided\n let fsHandle: FsHandle | undefined;\n if (opts.fs) {\n fsHandle = setupFs(context, {\n ...opts.fs,\n stateMap,\n coreHandle,\n });\n }\n\n // Setup custom functions\n if (opts.customFunctions) {\n for (const [name, def] of Object.entries(opts.customFunctions)) {\n if (def.async) {\n const fn = defineAsyncFunction(context, name, async (...args: unknown[]) => {\n return def.fn(...args);\n });\n context.setProp(context.global, name, fn);\n fn.dispose();\n } else {\n const fn = defineFunction(context, name, (...args: unknown[]) => {\n return def.fn(...args);\n });\n context.setProp(context.global, name, fn);\n fn.dispose();\n }\n }\n }\n\n // Setup test environment if enabled\n let testEnvHandle: TestEnvironmentHandle | undefined;\n if (opts.testEnvironment) {\n const testEnvOpts = typeof opts.testEnvironment === \"object\" ? opts.testEnvironment : {};\n testEnvHandle = setupTestEnvironment(context, {\n stateMap,\n coreHandle,\n onEvent: testEnvOpts.onEvent,\n });\n }\n\n // Setup playwright if provided\n let playwrightHandle: PlaywrightHandle | undefined;\n if (opts.playwright) {\n playwrightHandle = setupPlaywright(context, {\n ...opts.playwright,\n stateMap,\n coreHandle,\n consoleCallbacks: opts.playwright.console ? opts.console : undefined,\n });\n }\n\n // Create fetch handle wrapper\n const runtimeFetchHandle: RuntimeFetchHandle = {\n async dispatchRequest(request: Request): Promise<Response> {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n return fetchHandle.dispatchRequest(request);\n },\n getUpgradeRequest() {\n return fetchHandle?.getUpgradeRequest() ?? null;\n },\n dispatchWebSocketOpen(connectionId: string) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketOpen(connectionId);\n },\n dispatchWebSocketMessage(connectionId: string, message: string | ArrayBuffer) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketMessage(connectionId, message);\n },\n dispatchWebSocketClose(connectionId: string, code: number, reason: string) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketClose(connectionId, code, reason);\n },\n dispatchWebSocketError(connectionId: string, error: Error) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketError(connectionId, error);\n },\n onWebSocketCommand(callback: (command: WebSocketCommand) => void) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n return fetchHandle.onWebSocketCommand(callback);\n },\n hasServeHandler() {\n return fetchHandle?.hasServeHandler() ?? false;\n },\n hasActiveConnections() {\n return fetchHandle?.hasActiveConnections() ?? false;\n },\n };\n\n // Create timers handle wrapper\n const runtimeTimersHandle: RuntimeTimersHandle = {\n clearAll() {\n timersHandle.clearAll();\n },\n };\n\n // Create console handle wrapper\n const runtimeConsoleHandle: RuntimeConsoleHandle = {\n reset() {\n consoleHandle.reset();\n },\n getTimers() {\n return consoleHandle.getTimers();\n },\n getCounters() {\n return consoleHandle.getCounters();\n },\n getGroupDepth() {\n return consoleHandle.getGroupDepth();\n },\n };\n\n // Create test environment handle wrapper\n const runtimeTestEnvironmentHandle: RuntimeTestEnvironmentHandle = {\n async runTests(_timeout?: number): Promise<RunResults> {\n if (!testEnvHandle) {\n throw new Error(\"Test environment not enabled. Set testEnvironment: true in createRuntime options.\");\n }\n return testEnvHandle.run();\n },\n hasTests() {\n return testEnvHandle?.hasTests() ?? false;\n },\n getTestCount() {\n return testEnvHandle?.getTestCount() ?? 0;\n },\n reset() {\n testEnvHandle?.reset();\n },\n };\n\n // Create playwright handle wrapper\n const runtimePlaywrightHandle: RuntimePlaywrightHandle = {\n getCollectedData() {\n if (!playwrightHandle) {\n return { browserConsoleLogs: [], networkRequests: [], networkResponses: [] };\n }\n return {\n browserConsoleLogs: playwrightHandle.getBrowserConsoleLogs(),\n networkRequests: playwrightHandle.getNetworkRequests(),\n networkResponses: playwrightHandle.getNetworkResponses(),\n };\n },\n clearCollectedData() {\n playwrightHandle?.clearCollected();\n },\n };\n\n return {\n id,\n fetch: runtimeFetchHandle,\n timers: runtimeTimersHandle,\n console: runtimeConsoleHandle,\n testEnvironment: runtimeTestEnvironmentHandle,\n playwright: runtimePlaywrightHandle,\n\n async eval(code: string, filename?: string): Promise<void> {\n if (isAsyncContext) {\n // Use evalCodeAsync for async context (supports top-level await and async module loading)\n const asyncContext = context as QuickJSAsyncContext;\n const result = await asyncContext.evalCodeAsync(code, filename ?? \"<eval>\", {\n type: \"module\",\n });\n\n if (result.error) {\n const err = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Eval failed: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n\n // Handle module exports (could be a promise for top-level await)\n const promiseState = context.getPromiseState(result.value);\n if (promiseState.type === \"pending\") {\n const resolved = await context.resolvePromise(result.value);\n result.value.dispose();\n\n if (resolved.error) {\n const err = context.dump(resolved.error);\n resolved.error.dispose();\n throw new Error(`Promise rejected: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n resolved.value.dispose();\n } else {\n result.value.dispose();\n }\n\n // Execute pending jobs\n runtime.executePendingJobs();\n } else {\n // Use evalCode for sync context\n const result = context.evalCode(code, filename ?? \"<eval>\", {\n type: \"module\",\n });\n\n if (result.error) {\n const err = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Eval failed: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n\n // Handle async module result\n const promiseState = context.getPromiseState(result.value);\n if (promiseState.type === \"pending\") {\n const resolved = await context.resolvePromise(result.value);\n result.value.dispose();\n\n // Execute pending jobs\n runtime.executePendingJobs();\n\n if (resolved.error) {\n const err = context.dump(resolved.error);\n resolved.error.dispose();\n throw new Error(`Promise rejected: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n resolved.value.dispose();\n } else {\n result.value.dispose();\n }\n\n // Execute any remaining pending jobs\n runtime.executePendingJobs();\n }\n },\n\n async dispose(): Promise<void> {\n // Drain pending jobs before disposal\n for (let i = 0; i < 1000; i++) {\n if (!runtime.hasPendingJob()) break;\n const result = runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n // Dispose handles in reverse order\n playwrightHandle?.dispose();\n testEnvHandle?.dispose();\n cryptoHandle.dispose();\n timersHandle.dispose();\n encodingHandle.dispose();\n consoleHandle.dispose();\n fsHandle?.dispose();\n fetchHandle?.dispose();\n\n // Drain pending jobs again\n for (let i = 0; i < 1000; i++) {\n if (!runtime.hasPendingJob()) break;\n const result = runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n // Clean up handles\n cleanupUnmarshaledHandles(context);\n clearAllInstanceState();\n\n // Clear globals to help GC\n try {\n const clearGlobals = context.evalCode(`\n (function() {\n const keysToDelete = Object.keys(globalThis).filter(k =>\n k !== 'globalThis' && k !== 'undefined' && k !== 'NaN' && k !== 'Infinity'\n );\n for (const key of keysToDelete) {\n try { globalThis[key] = undefined; } catch (e) {}\n }\n for (const key of keysToDelete) {\n try { delete globalThis[key]; } catch (e) {}\n }\n return keysToDelete.length;\n })()\n `);\n if (clearGlobals.error) {\n clearGlobals.error.dispose();\n } else {\n clearGlobals.value.dispose();\n }\n } catch {\n // Ignore errors during cleanup\n }\n\n // Final drain\n for (let i = 0; i < 100; i++) {\n if (!runtime.hasPendingJob()) break;\n const result = runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n coreHandle.dispose();\n context.dispose();\n\n // Only dispose runtime if it was created separately (not via newAsyncContext)\n if (!isAsyncContext) {\n runtime.dispose();\n }\n },\n };\n}\n"
|
|
5
|
+
"import {\n getQuickJS,\n newAsyncContext,\n shouldInterruptAfterDeadline,\n type QuickJSAsyncContext,\n type QuickJSAsyncRuntime,\n type QuickJSContext,\n type QuickJSRuntime,\n} from \"quickjs-emscripten\";\nimport {\n setupCore,\n createStateMap,\n cleanupUnmarshaledHandles,\n clearAllInstanceState,\n defineFunction,\n defineAsyncFunction,\n defineAsyncIteratorFunction,\n cleanupAsyncIterators,\n type StateMap,\n type CoreHandle,\n} from \"@ricsam/quickjs-core\";\nimport {\n setupFetch,\n type SetupFetchOptions,\n type FetchHandle,\n type UpgradeRequest,\n type WebSocketCommand,\n} from \"@ricsam/quickjs-fetch\";\nimport {\n setupFs,\n type SetupFsOptions,\n type FsHandle,\n} from \"@ricsam/quickjs-fs\";\nimport {\n setupConsole,\n type ConsoleHandle,\n type ConsoleEntry,\n type ConsoleCallbacks,\n} from \"@ricsam/quickjs-console\";\nimport {\n setupEncoding,\n type EncodingHandle,\n} from \"@ricsam/quickjs-encoding\";\nimport {\n setupTimers,\n type TimersHandle,\n} from \"@ricsam/quickjs-timers\";\nimport {\n setupCrypto,\n type CryptoHandle,\n} from \"@ricsam/quickjs-crypto\";\nimport {\n setupTestEnvironment,\n type TestEnvironmentHandle,\n type RunResults,\n type TestEvent,\n} from \"@ricsam/quickjs-test-environment\";\nimport {\n setupPlaywright,\n type PlaywrightHandle,\n type PlaywrightOptions,\n type PlaywrightEvent,\n type BrowserConsoleLogEntry,\n type NetworkRequestInfo,\n type NetworkResponseInfo,\n} from \"@ricsam/quickjs-playwright\";\n\n// Re-export types\nexport type { ConsoleEntry, ConsoleCallbacks, TestEvent, PlaywrightEvent };\n\n/**\n * Module loader callback type.\n * Called when the runtime imports a module dynamically.\n * Returns the JavaScript source code for the module.\n */\nexport type ModuleLoaderCallback = (\n moduleName: string\n) => string | Promise<string>;\n\n/**\n * A sync custom function that can be called from within the runtime.\n */\nexport type SyncCustomFunction = (...args: unknown[]) => unknown;\n\n/**\n * An async custom function that can be called from within the runtime.\n */\nexport type AsyncCustomFunction = (...args: unknown[]) => Promise<unknown>;\n\n/**\n * An async iterator custom function that can be called from within the runtime.\n */\nexport type AsyncIteratorCustomFunction = (...args: unknown[]) => AsyncGenerator<unknown, unknown, unknown>;\n\n/**\n * Custom function definition with discriminated union based on type.\n * BREAKING CHANGE: Replaced `async: boolean` with `type: 'sync' | 'async' | 'asyncIterator'`\n */\nexport type CustomFunctionDefinition =\n | { fn: SyncCustomFunction; type: 'sync' }\n | { fn: AsyncCustomFunction; type: 'async' }\n | { fn: AsyncIteratorCustomFunction; type: 'asyncIterator' };\n\n/**\n * Custom functions to register in the runtime.\n * Each function must be defined with explicit type property.\n *\n * @example\n * ```typescript\n * customFunctions: {\n * // Async function\n * hashPassword: {\n * fn: async (password) => bcrypt.hash(password, 10),\n * type: 'async',\n * },\n * // Sync function\n * getConfig: {\n * fn: () => ({ environment: \"production\" }),\n * type: 'sync',\n * },\n * // Async iterator function\n * streamData: {\n * fn: async function* (count) {\n * for (let i = 0; i < count; i++) yield i;\n * },\n * type: 'asyncIterator',\n * },\n * }\n * ```\n */\nexport type CustomFunctions = Record<string, CustomFunctionDefinition>;\n\n/**\n * Fetch callback type.\n */\nexport type FetchCallback = (request: Request) => Response | Promise<Response>;\n\n/**\n * Test environment options.\n */\nexport interface TestEnvironmentOptions {\n /** Event callback for test events */\n onEvent?: (event: TestEvent) => void;\n /** Default test timeout in milliseconds */\n testTimeout?: number;\n}\n\n/**\n * Options for creating a runtime.\n */\nexport interface RuntimeOptions {\n /** Memory limit in megabytes (optional) */\n memoryLimitMB?: number;\n /** Console callback handlers */\n console?: ConsoleCallbacks;\n /** Fetch callback handler */\n fetch?: FetchCallback;\n /** File system options */\n fs?: SetupFsOptions;\n /** Module loader callback for resolving dynamic imports */\n moduleLoader?: ModuleLoaderCallback;\n /** Custom functions callable from within the runtime */\n customFunctions?: CustomFunctions;\n /** Current working directory for path operations. Defaults to \"/\" */\n cwd?: string;\n /** Test environment options. Set to true for defaults, or provide options. */\n testEnvironment?: boolean | TestEnvironmentOptions;\n /** Playwright options for browser automation */\n playwright?: PlaywrightOptions;\n}\n\n/**\n * Runtime fetch handle - provides access to fetch/serve operations.\n */\nexport interface RuntimeFetchHandle {\n /** Dispatch HTTP request to serve() handler */\n dispatchRequest(request: Request): Promise<Response>;\n\n /**\n * Check if the last request resulted in an upgrade request\n * Must be called immediately after dispatchRequest()\n */\n getUpgradeRequest(): UpgradeRequest | null;\n\n /**\n * Dispatch WebSocket open event\n * @param connectionId The connectionId from getUpgradeRequest()\n */\n dispatchWebSocketOpen(connectionId: string): void;\n\n /**\n * Dispatch WebSocket message event\n */\n dispatchWebSocketMessage(connectionId: string, message: string | ArrayBuffer): void;\n\n /**\n * Dispatch WebSocket close event\n */\n dispatchWebSocketClose(connectionId: string, code: number, reason: string): void;\n\n /**\n * Dispatch WebSocket error event\n */\n dispatchWebSocketError(connectionId: string, error: Error): void;\n\n /**\n * Register a callback for outgoing WebSocket messages/commands\n * Called when QuickJS code calls ws.send() or ws.close()\n */\n onWebSocketCommand(callback: (command: WebSocketCommand) => void): () => void;\n\n /** Check if serve() has been called */\n hasServeHandler(): boolean;\n\n /** Check if there are active WebSocket connections */\n hasActiveConnections(): boolean;\n}\n\n/**\n * Runtime timers handle - provides access to timer operations.\n */\nexport interface RuntimeTimersHandle {\n /** Clear all pending timers */\n clearAll(): void;\n}\n\n/**\n * Runtime console handle - provides access to console state.\n */\nexport interface RuntimeConsoleHandle {\n /** Reset all console state (timers, counters, group depth) */\n reset(): void;\n /** Get console.time() timers */\n getTimers(): Map<string, number>;\n /** Get console.count() counters */\n getCounters(): Map<string, number>;\n /** Get current console.group() nesting depth */\n getGroupDepth(): number;\n}\n\n/**\n * Runtime test environment handle - provides access to test operations.\n */\nexport interface RuntimeTestEnvironmentHandle {\n /** Run all registered tests */\n runTests(timeout?: number): Promise<RunResults>;\n /** Check if any tests have been registered */\n hasTests(): boolean;\n /** Get the number of registered tests */\n getTestCount(): number;\n /** Reset test state (clear registered tests) */\n reset(): void;\n}\n\n/**\n * Collected playwright data.\n */\nexport interface CollectedData {\n browserConsoleLogs: BrowserConsoleLogEntry[];\n networkRequests: NetworkRequestInfo[];\n networkResponses: NetworkResponseInfo[];\n}\n\n/**\n * Runtime playwright handle - provides access to playwright data.\n */\nexport interface RuntimePlaywrightHandle {\n /** Get collected browser logs and network data */\n getCollectedData(): CollectedData;\n /** Clear all collected data */\n clearCollectedData(): void;\n}\n\n/**\n * Options for the eval method.\n */\nexport interface EvalOptions {\n /** Filename for the evaluated code (for stack traces) */\n filename?: string;\n /** Maximum execution time in milliseconds (optional) */\n maxExecutionMs?: number;\n}\n\n/**\n * Runtime handle - the main interface for interacting with the runtime.\n */\nexport interface RuntimeHandle {\n /** Unique runtime identifier */\n readonly id: string;\n /** Execute code as ES module (supports top-level await) */\n eval(code: string, filenameOrOptions?: string | EvalOptions): Promise<void>;\n /** Dispose all resources */\n dispose(): Promise<void>;\n\n /** Fetch handle - access to fetch/serve operations */\n readonly fetch: RuntimeFetchHandle;\n /** Timers handle - access to timer operations */\n readonly timers: RuntimeTimersHandle;\n /** Console handle - access to console state */\n readonly console: RuntimeConsoleHandle;\n /** Test environment handle - access to test operations */\n readonly testEnvironment: RuntimeTestEnvironmentHandle;\n /** Playwright handle - access to browser automation data */\n readonly playwright: RuntimePlaywrightHandle;\n}\n\n/**\n * Create a fully configured QuickJS runtime\n *\n * Sets up all WHATWG APIs: fetch, fs, console, crypto, encoding, timers.\n * Supports ES modules with custom module loader and custom host functions.\n *\n * @example\n * ```typescript\n * const runtime = await createRuntime({\n * console: { onEntry: (e) => console.log(\"[sandbox]\", e) },\n * fetch: async (request) => fetch(request),\n * moduleLoader: async (name) => {\n * if (name === \"@/utils\") {\n * return `export const add = (a, b) => a + b;`;\n * }\n * throw new Error(`Unknown module: ${name}`);\n * },\n * customFunctions: {\n * hashPassword: {\n * fn: async (pw) => Bun.password.hash(pw),\n * type: 'async',\n * },\n * },\n * });\n *\n * await runtime.eval(`\n * import { add } from \"@/utils\";\n * const hash = await hashPassword(\"secret\");\n * console.log(add(1, 2), hash);\n * `);\n *\n * await runtime.dispose();\n * ```\n */\nexport async function createRuntime(\n options?: RuntimeOptions\n): Promise<RuntimeHandle> {\n const opts = options ?? {};\n\n // Generate unique ID\n const id = crypto.randomUUID();\n\n // Determine if we need async context (for async module loader)\n const needsAsync = opts.moduleLoader !== undefined;\n\n let context: QuickJSContext | QuickJSAsyncContext;\n let runtime: QuickJSRuntime | QuickJSAsyncRuntime;\n let isAsyncContext = false;\n\n if (needsAsync) {\n // Create async context for module loading support\n const asyncContext = await newAsyncContext();\n context = asyncContext;\n runtime = asyncContext.runtime;\n isAsyncContext = true;\n\n // Set up module loader\n if (opts.moduleLoader) {\n const moduleLoader = opts.moduleLoader;\n (runtime as QuickJSAsyncRuntime).setModuleLoader(async (moduleName: string) => {\n try {\n const code = await moduleLoader(moduleName);\n return code;\n } catch (error) {\n // Return error in SuccessOrFail format\n return { error: error as Error };\n }\n });\n }\n } else {\n // Create sync context\n const QuickJS = await getQuickJS();\n runtime = QuickJS.newRuntime();\n context = runtime.newContext();\n }\n\n // Set memory limit if specified\n if (opts.memoryLimitMB) {\n runtime.setMemoryLimit(opts.memoryLimitMB * 1024 * 1024);\n }\n\n // Create shared state\n const stateMap = createStateMap();\n\n // Setup core APIs\n const coreHandle = setupCore(context, { stateMap });\n\n // Setup console\n const consoleHandle = setupConsole(context, {\n ...opts.console,\n stateMap,\n coreHandle,\n });\n\n // Setup encoding (btoa/atob)\n const encodingHandle = setupEncoding(context, { stateMap, coreHandle });\n\n // Setup timers\n const timersHandle = setupTimers(context, { stateMap, coreHandle });\n\n // Setup crypto\n const cryptoHandle = setupCrypto(context, { stateMap, coreHandle });\n\n // Setup fetch if callback provided\n let fetchHandle: FetchHandle | undefined;\n if (opts.fetch) {\n const fetchCallback = opts.fetch;\n fetchHandle = setupFetch(context, {\n stateMap,\n coreHandle,\n onFetch: async (request: Request) => {\n return Promise.resolve(fetchCallback(request));\n },\n });\n } else {\n // Still setup fetch for serve() support, but without fetch() function\n fetchHandle = setupFetch(context, { stateMap, coreHandle });\n }\n\n // Setup file system if provided\n let fsHandle: FsHandle | undefined;\n if (opts.fs) {\n fsHandle = setupFs(context, {\n ...opts.fs,\n stateMap,\n coreHandle,\n });\n }\n\n // Setup custom functions\n if (opts.customFunctions) {\n for (const [name, def] of Object.entries(opts.customFunctions)) {\n if (def.type === 'async') {\n const fn = defineAsyncFunction(context, name, async (...args: unknown[]) => {\n return def.fn(...args);\n });\n context.setProp(context.global, name, fn);\n fn.dispose();\n } else if (def.type === 'asyncIterator') {\n const fn = defineAsyncIteratorFunction(context, name, (...args: unknown[]) => {\n return def.fn(...args);\n });\n context.setProp(context.global, name, fn);\n fn.dispose();\n } else {\n // type === 'sync'\n const fn = defineFunction(context, name, (...args: unknown[]) => {\n return def.fn(...args);\n });\n context.setProp(context.global, name, fn);\n fn.dispose();\n }\n }\n }\n\n // Setup test environment if enabled\n let testEnvHandle: TestEnvironmentHandle | undefined;\n if (opts.testEnvironment) {\n const testEnvOpts = typeof opts.testEnvironment === \"object\" ? opts.testEnvironment : {};\n testEnvHandle = setupTestEnvironment(context, {\n stateMap,\n coreHandle,\n onEvent: testEnvOpts.onEvent,\n });\n }\n\n // Setup playwright if provided\n let playwrightHandle: PlaywrightHandle | undefined;\n if (opts.playwright) {\n playwrightHandle = setupPlaywright(context, {\n ...opts.playwright,\n stateMap,\n coreHandle,\n consoleCallbacks: opts.playwright.console ? opts.console : undefined,\n });\n }\n\n // Create fetch handle wrapper\n const runtimeFetchHandle: RuntimeFetchHandle = {\n async dispatchRequest(request: Request): Promise<Response> {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n return fetchHandle.dispatchRequest(request);\n },\n getUpgradeRequest() {\n return fetchHandle?.getUpgradeRequest() ?? null;\n },\n dispatchWebSocketOpen(connectionId: string) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketOpen(connectionId);\n },\n dispatchWebSocketMessage(connectionId: string, message: string | ArrayBuffer) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketMessage(connectionId, message);\n },\n dispatchWebSocketClose(connectionId: string, code: number, reason: string) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketClose(connectionId, code, reason);\n },\n dispatchWebSocketError(connectionId: string, error: Error) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketError(connectionId, error);\n },\n onWebSocketCommand(callback: (command: WebSocketCommand) => void) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n return fetchHandle.onWebSocketCommand(callback);\n },\n hasServeHandler() {\n return fetchHandle?.hasServeHandler() ?? false;\n },\n hasActiveConnections() {\n return fetchHandle?.hasActiveConnections() ?? false;\n },\n };\n\n // Create timers handle wrapper\n const runtimeTimersHandle: RuntimeTimersHandle = {\n clearAll() {\n timersHandle.clearAll();\n },\n };\n\n // Create console handle wrapper\n const runtimeConsoleHandle: RuntimeConsoleHandle = {\n reset() {\n consoleHandle.reset();\n },\n getTimers() {\n return consoleHandle.getTimers();\n },\n getCounters() {\n return consoleHandle.getCounters();\n },\n getGroupDepth() {\n return consoleHandle.getGroupDepth();\n },\n };\n\n // Create test environment handle wrapper\n const runtimeTestEnvironmentHandle: RuntimeTestEnvironmentHandle = {\n async runTests(_timeout?: number): Promise<RunResults> {\n if (!testEnvHandle) {\n throw new Error(\"Test environment not enabled. Set testEnvironment: true in createRuntime options.\");\n }\n return testEnvHandle.run();\n },\n hasTests() {\n return testEnvHandle?.hasTests() ?? false;\n },\n getTestCount() {\n return testEnvHandle?.getTestCount() ?? 0;\n },\n reset() {\n testEnvHandle?.reset();\n },\n };\n\n // Create playwright handle wrapper\n const runtimePlaywrightHandle: RuntimePlaywrightHandle = {\n getCollectedData() {\n if (!playwrightHandle) {\n return { browserConsoleLogs: [], networkRequests: [], networkResponses: [] };\n }\n return {\n browserConsoleLogs: playwrightHandle.getBrowserConsoleLogs(),\n networkRequests: playwrightHandle.getNetworkRequests(),\n networkResponses: playwrightHandle.getNetworkResponses(),\n };\n },\n clearCollectedData() {\n playwrightHandle?.clearCollected();\n },\n };\n\n return {\n id,\n fetch: runtimeFetchHandle,\n timers: runtimeTimersHandle,\n console: runtimeConsoleHandle,\n testEnvironment: runtimeTestEnvironmentHandle,\n playwright: runtimePlaywrightHandle,\n\n async eval(code: string, filenameOrOptions?: string | EvalOptions): Promise<void> {\n // Parse options\n const evalOpts: EvalOptions = typeof filenameOrOptions === \"string\"\n ? { filename: filenameOrOptions }\n : filenameOrOptions ?? {};\n const filename = evalOpts.filename ?? \"<eval>\";\n const maxExecutionMs = evalOpts.maxExecutionMs;\n\n // Set up interrupt handler if maxExecutionMs is configured\n if (maxExecutionMs) {\n const deadline = Date.now() + maxExecutionMs;\n runtime.setInterruptHandler(shouldInterruptAfterDeadline(deadline));\n }\n\n try {\n if (isAsyncContext) {\n // Use evalCodeAsync for async context (supports top-level await and async module loading)\n const asyncContext = context as QuickJSAsyncContext;\n const result = await asyncContext.evalCodeAsync(code, filename, {\n type: \"module\",\n });\n\n if (result.error) {\n const err = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Eval failed: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n\n // Handle module exports (could be a promise for top-level await)\n const promiseState = context.getPromiseState(result.value);\n if (promiseState.type === \"pending\") {\n // Execute pending jobs first to allow JS async functions to schedule\n runtime.executePendingJobs();\n\n // Race between resolvePromise and active job execution loop\n const resolved = await Promise.race([\n context.resolvePromise(result.value),\n (async () => {\n for (let i = 0; i < 10000; i++) {\n await new Promise((r) => setTimeout(r, 0));\n runtime.executePendingJobs();\n const state = context.getPromiseState(result.value);\n if (state.type !== \"pending\") {\n return { pollingResolved: true as const, state };\n }\n }\n throw new Error(\"Promise resolution timeout\");\n })(),\n ]);\n\n result.value.dispose();\n runtime.executePendingJobs();\n\n if (\"pollingResolved\" in resolved) {\n if (resolved.state.type === \"rejected\") {\n const err = context.dump(resolved.state.error!);\n resolved.state.error!.dispose();\n throw new Error(`Promise rejected: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n if (resolved.state.value) {\n resolved.state.value.dispose();\n }\n } else {\n if (resolved.error) {\n const err = context.dump(resolved.error);\n resolved.error.dispose();\n throw new Error(`Promise rejected: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n resolved.value.dispose();\n }\n } else {\n result.value.dispose();\n }\n\n // Execute pending jobs\n runtime.executePendingJobs();\n } else {\n // Use evalCode for sync context\n const result = context.evalCode(code, filename, {\n type: \"module\",\n });\n\n if (result.error) {\n const err = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Eval failed: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n\n // Handle async module result\n const promiseState = context.getPromiseState(result.value);\n if (promiseState.type === \"pending\") {\n // Execute pending jobs first to allow JS async functions to schedule\n runtime.executePendingJobs();\n\n // Race between resolvePromise and active job execution loop\n const resolved = await Promise.race([\n context.resolvePromise(result.value),\n (async () => {\n for (let i = 0; i < 10000; i++) {\n await new Promise((r) => setTimeout(r, 0));\n runtime.executePendingJobs();\n const state = context.getPromiseState(result.value);\n if (state.type !== \"pending\") {\n return { pollingResolved: true as const, state };\n }\n }\n throw new Error(\"Promise resolution timeout\");\n })(),\n ]);\n\n result.value.dispose();\n runtime.executePendingJobs();\n\n if (\"pollingResolved\" in resolved) {\n if (resolved.state.type === \"rejected\") {\n const err = context.dump(resolved.state.error!);\n resolved.state.error!.dispose();\n throw new Error(`Promise rejected: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n if (resolved.state.value) {\n resolved.state.value.dispose();\n }\n } else {\n if (resolved.error) {\n const err = context.dump(resolved.error);\n resolved.error.dispose();\n throw new Error(`Promise rejected: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n resolved.value.dispose();\n }\n } else {\n result.value.dispose();\n }\n\n // Execute any remaining pending jobs\n runtime.executePendingJobs();\n }\n } finally {\n // Remove interrupt handler after eval\n if (maxExecutionMs) {\n runtime.removeInterruptHandler();\n }\n }\n },\n\n async dispose(): Promise<void> {\n // Drain pending jobs before disposal\n for (let i = 0; i < 1000; i++) {\n if (!runtime.hasPendingJob()) break;\n const result = runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n // Dispose handles in reverse order\n playwrightHandle?.dispose();\n testEnvHandle?.dispose();\n cryptoHandle.dispose();\n timersHandle.dispose();\n encodingHandle.dispose();\n consoleHandle.dispose();\n fsHandle?.dispose();\n fetchHandle?.dispose();\n\n // Drain pending jobs again\n for (let i = 0; i < 1000; i++) {\n if (!runtime.hasPendingJob()) break;\n const result = runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n // Clean up handles\n cleanupAsyncIterators(context);\n cleanupUnmarshaledHandles(context);\n clearAllInstanceState();\n\n // Clear globals to help GC\n try {\n const clearGlobals = context.evalCode(`\n (function() {\n const keysToDelete = Object.keys(globalThis).filter(k =>\n k !== 'globalThis' && k !== 'undefined' && k !== 'NaN' && k !== 'Infinity'\n );\n for (const key of keysToDelete) {\n try { globalThis[key] = undefined; } catch (e) {}\n }\n for (const key of keysToDelete) {\n try { delete globalThis[key]; } catch (e) {}\n }\n return keysToDelete.length;\n })()\n `);\n if (clearGlobals.error) {\n clearGlobals.error.dispose();\n } else {\n clearGlobals.value.dispose();\n }\n } catch {\n // Ignore errors during cleanup\n }\n\n // Final drain\n for (let i = 0; i < 100; i++) {\n if (!runtime.hasPendingJob()) break;\n const result = runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n coreHandle.dispose();\n context.dispose();\n\n // Only dispose runtime if it was created separately (not via newAsyncContext)\n if (!isAsyncContext) {\n runtime.dispose();\n }\n },\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "66184374D0F003ED64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"// Re-export everything from sub-packages\nexport * from \"@ricsam/quickjs-core\";\nexport * from \"@ricsam/quickjs-console\";\nexport * from \"@ricsam/quickjs-crypto\";\nexport * from \"@ricsam/quickjs-encoding\";\nexport * from \"@ricsam/quickjs-fetch\";\nexport * from \"@ricsam/quickjs-fs\";\nexport * from \"@ricsam/quickjs-timers\";\n\n// Export high-level createRuntime API\nexport {\n createRuntime,\n type RuntimeOptions,\n type RuntimeHandle,\n type RuntimeFetchHandle,\n type RuntimeTimersHandle,\n type RuntimeConsoleHandle,\n type ModuleLoaderCallback,\n type
|
|
5
|
+
"// Re-export everything from sub-packages\nexport * from \"@ricsam/quickjs-core\";\nexport * from \"@ricsam/quickjs-console\";\nexport * from \"@ricsam/quickjs-crypto\";\nexport * from \"@ricsam/quickjs-encoding\";\nexport * from \"@ricsam/quickjs-fetch\";\nexport * from \"@ricsam/quickjs-fs\";\nexport * from \"@ricsam/quickjs-timers\";\n\n// Export high-level createRuntime API\nexport {\n createRuntime,\n type RuntimeOptions,\n type RuntimeHandle,\n type RuntimeFetchHandle,\n type RuntimeTimersHandle,\n type RuntimeConsoleHandle,\n type ModuleLoaderCallback,\n type SyncCustomFunction,\n type AsyncCustomFunction,\n type AsyncIteratorCustomFunction,\n type CustomFunctionDefinition,\n type CustomFunctions,\n type FetchCallback,\n type EvalOptions,\n} from \"./create-runtime.cjs\";\n\nimport type { QuickJSContext } from \"quickjs-emscripten\";\nimport {\n setupCore,\n createStateMap,\n cleanupUnmarshaledHandles,\n clearAllInstanceState,\n type StateMap,\n type CoreHandle,\n} from \"@ricsam/quickjs-core\";\nimport {\n setupFetch,\n type SetupFetchOptions,\n type FetchHandle,\n} from \"@ricsam/quickjs-fetch\";\nimport {\n setupFs,\n type SetupFsOptions,\n type FsHandle,\n} from \"@ricsam/quickjs-fs\";\nimport {\n setupConsole,\n type SetupConsoleOptions,\n type ConsoleHandle,\n} from \"@ricsam/quickjs-console\";\nimport {\n setupEncoding,\n type SetupEncodingOptions,\n type EncodingHandle,\n} from \"@ricsam/quickjs-encoding\";\nimport {\n setupTimers,\n type SetupTimersOptions,\n type TimersHandle,\n} from \"@ricsam/quickjs-timers\";\nimport {\n setupCrypto,\n type SetupCryptoOptions,\n type CryptoHandle,\n} from \"@ricsam/quickjs-crypto\";\n\n/**\n * Options for setting up the runtime\n */\nexport interface SetupRuntimeOptions {\n /**\n * Fetch API options\n * Pass `true` to enable with defaults\n * Pass `false` or omit to disable\n */\n fetch?: SetupFetchOptions | boolean;\n\n /**\n * File system options\n * Pass options object to enable\n * Pass `false` or omit to disable\n */\n fs?: SetupFsOptions | false;\n\n /**\n * Console options\n * Pass `true` to enable with defaults\n * Pass handlers object to customize\n * Pass `false` or omit to disable\n */\n console?: SetupConsoleOptions | boolean;\n\n /**\n * Encoding options (atob/btoa)\n * Pass `true` to enable with defaults\n * Pass `false` or omit to disable\n */\n encoding?: SetupEncodingOptions | boolean;\n\n /**\n * Timer options (setTimeout, setInterval, clearTimeout, clearInterval)\n * Pass `true` to enable with defaults\n * Pass `false` or omit to disable\n */\n timers?: SetupTimersOptions | boolean;\n\n /**\n * Crypto API options (crypto.subtle, crypto.getRandomValues, crypto.randomUUID)\n * Pass `true` to enable with defaults\n * Pass `false` or omit to disable\n */\n crypto?: SetupCryptoOptions | boolean;\n}\n\n/**\n * Handle returned from setupRuntime (low-level API)\n */\nexport interface SetupRuntimeHandle {\n /** Core handle (always present) */\n core: CoreHandle;\n\n /** Fetch handle (if enabled) */\n fetch?: FetchHandle;\n\n /** File system handle (if enabled) */\n fs?: FsHandle;\n\n /** Console handle (if enabled) */\n console?: ConsoleHandle;\n\n /** Encoding handle (if enabled) */\n encoding?: EncodingHandle;\n\n /** Timers handle (if enabled) */\n timers?: TimersHandle;\n\n /** Crypto handle (if enabled) */\n crypto?: CryptoHandle;\n\n /** Shared state map */\n readonly stateMap: StateMap;\n\n /** Get the underlying QuickJS context */\n readonly context: QuickJSContext;\n\n /**\n * Drain all pending jobs with timeout.\n * Call BEFORE dispose() to prevent GC assertion failures.\n * @param maxIterations - Maximum iterations (default: 1000)\n * @returns true if all jobs completed, false if timed out\n */\n drainPendingJobs(maxIterations?: number): boolean;\n\n /** Dispose all handles and cleanup */\n dispose(): void;\n}\n\n/**\n * Setup multiple APIs in a QuickJS context\n *\n * @example\n * const handle = setupRuntime(context, {\n * fetch: {\n * onFetch: async (req) => fetch(req),\n * },\n * fs: {\n * getDirectory: async (path) => createNodeDirectoryHandle(`/sandbox${path}`),\n * },\n * });\n *\n * // Use the context...\n *\n * handle.dispose();\n */\nexport function setupRuntime(\n context: QuickJSContext,\n options: SetupRuntimeOptions = {}\n): SetupRuntimeHandle {\n const stateMap = createStateMap();\n\n // Always setup core\n const core = setupCore(context, { stateMap });\n\n let fetch: FetchHandle | undefined;\n let fs: FsHandle | undefined;\n let consoleHandle: ConsoleHandle | undefined;\n let encoding: EncodingHandle | undefined;\n let timers: TimersHandle | undefined;\n let cryptoHandle: CryptoHandle | undefined;\n\n // Setup fetch if enabled\n if (options.fetch) {\n const fetchOptions: SetupFetchOptions =\n options.fetch === true\n ? { stateMap, coreHandle: core }\n : { ...options.fetch, stateMap, coreHandle: core };\n\n fetch = setupFetch(context, fetchOptions);\n }\n\n // Setup fs if provided\n if (options.fs) {\n fs = setupFs(context, {\n ...options.fs,\n stateMap,\n coreHandle: core,\n });\n }\n\n // Setup console if enabled\n if (options.console) {\n const consoleOptions: SetupConsoleOptions =\n options.console === true\n ? { stateMap, coreHandle: core }\n : { ...options.console, stateMap, coreHandle: core };\n\n consoleHandle = setupConsole(context, consoleOptions);\n }\n\n // Setup encoding if enabled\n if (options.encoding) {\n const encodingOptions: SetupEncodingOptions =\n options.encoding === true\n ? { stateMap, coreHandle: core }\n : { ...options.encoding, stateMap, coreHandle: core };\n\n encoding = setupEncoding(context, encodingOptions);\n }\n\n // Setup timers if enabled\n if (options.timers) {\n const timersOptions: SetupTimersOptions =\n options.timers === true\n ? { stateMap, coreHandle: core }\n : { ...options.timers, stateMap, coreHandle: core };\n\n timers = setupTimers(context, timersOptions);\n }\n\n // Setup crypto if enabled\n if (options.crypto) {\n const cryptoOptions: SetupCryptoOptions =\n options.crypto === true\n ? { stateMap, coreHandle: core }\n : { ...options.crypto, stateMap, coreHandle: core };\n\n cryptoHandle = setupCrypto(context, cryptoOptions);\n }\n\n return {\n core,\n fetch,\n fs,\n console: consoleHandle,\n encoding,\n timers,\n crypto: cryptoHandle,\n stateMap,\n get context() {\n return context;\n },\n drainPendingJobs(maxIterations = 1000): boolean {\n for (let i = 0; i < maxIterations; i++) {\n if (!context.runtime.hasPendingJob()) {\n return true;\n }\n const result = context.runtime.executePendingJobs();\n if (result.error) {\n result.error.dispose();\n }\n }\n return !context.runtime.hasPendingJob();\n },\n dispose() {\n // Drain pending jobs before disposal\n for (let i = 0; i < 1000; i++) {\n if (!context.runtime.hasPendingJob()) break;\n const result = context.runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n // Dispose sub-handles in reverse order\n cryptoHandle?.dispose();\n timers?.dispose();\n encoding?.dispose();\n consoleHandle?.dispose();\n fs?.dispose();\n fetch?.dispose();\n\n // Drain pending jobs again after sub-handle disposal\n // (fetch?.dispose() calls evalCode which can create new jobs)\n for (let i = 0; i < 1000; i++) {\n if (!context.runtime.hasPendingJob()) break;\n const result = context.runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n // Clean up tracked handles\n cleanupUnmarshaledHandles(context);\n clearAllInstanceState();\n\n // Clear globals to help GC break circular references\n try {\n const clearGlobals = context.evalCode(`\n (function() {\n const keysToDelete = Object.keys(globalThis).filter(k =>\n k !== 'globalThis' && k !== 'undefined' && k !== 'NaN' && k !== 'Infinity'\n );\n for (const key of keysToDelete) {\n try { globalThis[key] = undefined; } catch (e) {}\n }\n for (const key of keysToDelete) {\n try { delete globalThis[key]; } catch (e) {}\n }\n return keysToDelete.length;\n })()\n `);\n if (clearGlobals.error) {\n clearGlobals.error.dispose();\n } else {\n clearGlobals.value.dispose();\n }\n } catch (e) {\n // Ignore errors during cleanup\n }\n\n // Final drain\n for (let i = 0; i < 100; i++) {\n if (!context.runtime.hasPendingJob()) break;\n const result = context.runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n core.dispose();\n },\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA;AACA;AACA;AACA;AACA;AACA;AACA;
|
|
7
|
+
"mappings": "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",
|
|
8
8
|
"debugId": "D5C8A62287B56EA664756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/package.json
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// packages/runtime/src/create-runtime.ts
|
|
3
3
|
import {
|
|
4
4
|
getQuickJS,
|
|
5
|
-
newAsyncContext
|
|
5
|
+
newAsyncContext,
|
|
6
|
+
shouldInterruptAfterDeadline
|
|
6
7
|
} from "quickjs-emscripten";
|
|
7
8
|
import {
|
|
8
9
|
setupCore,
|
|
@@ -10,7 +11,9 @@ import {
|
|
|
10
11
|
cleanupUnmarshaledHandles,
|
|
11
12
|
clearAllInstanceState,
|
|
12
13
|
defineFunction,
|
|
13
|
-
defineAsyncFunction
|
|
14
|
+
defineAsyncFunction,
|
|
15
|
+
defineAsyncIteratorFunction,
|
|
16
|
+
cleanupAsyncIterators
|
|
14
17
|
} from "@ricsam/quickjs-core";
|
|
15
18
|
import {
|
|
16
19
|
setupFetch
|
|
@@ -100,12 +103,18 @@ async function createRuntime(options) {
|
|
|
100
103
|
}
|
|
101
104
|
if (opts.customFunctions) {
|
|
102
105
|
for (const [name, def] of Object.entries(opts.customFunctions)) {
|
|
103
|
-
if (def.async) {
|
|
106
|
+
if (def.type === "async") {
|
|
104
107
|
const fn = defineAsyncFunction(context, name, async (...args) => {
|
|
105
108
|
return def.fn(...args);
|
|
106
109
|
});
|
|
107
110
|
context.setProp(context.global, name, fn);
|
|
108
111
|
fn.dispose();
|
|
112
|
+
} else if (def.type === "asyncIterator") {
|
|
113
|
+
const fn = defineAsyncIteratorFunction(context, name, (...args) => {
|
|
114
|
+
return def.fn(...args);
|
|
115
|
+
});
|
|
116
|
+
context.setProp(context.global, name, fn);
|
|
117
|
+
fn.dispose();
|
|
109
118
|
} else {
|
|
110
119
|
const fn = defineFunction(context, name, (...args) => {
|
|
111
120
|
return def.fn(...args);
|
|
@@ -238,55 +247,119 @@ async function createRuntime(options) {
|
|
|
238
247
|
console: runtimeConsoleHandle,
|
|
239
248
|
testEnvironment: runtimeTestEnvironmentHandle,
|
|
240
249
|
playwright: runtimePlaywrightHandle,
|
|
241
|
-
async eval(code,
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
result.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
throw new Error(`Promise rejected: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
250
|
+
async eval(code, filenameOrOptions) {
|
|
251
|
+
const evalOpts = typeof filenameOrOptions === "string" ? { filename: filenameOrOptions } : filenameOrOptions ?? {};
|
|
252
|
+
const filename = evalOpts.filename ?? "<eval>";
|
|
253
|
+
const maxExecutionMs = evalOpts.maxExecutionMs;
|
|
254
|
+
if (maxExecutionMs) {
|
|
255
|
+
const deadline = Date.now() + maxExecutionMs;
|
|
256
|
+
runtime.setInterruptHandler(shouldInterruptAfterDeadline(deadline));
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
if (isAsyncContext) {
|
|
260
|
+
const asyncContext = context;
|
|
261
|
+
const result = await asyncContext.evalCodeAsync(code, filename, {
|
|
262
|
+
type: "module"
|
|
263
|
+
});
|
|
264
|
+
if (result.error) {
|
|
265
|
+
const err = context.dump(result.error);
|
|
266
|
+
result.error.dispose();
|
|
267
|
+
throw new Error(`Eval failed: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
260
268
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
269
|
+
const promiseState = context.getPromiseState(result.value);
|
|
270
|
+
if (promiseState.type === "pending") {
|
|
271
|
+
runtime.executePendingJobs();
|
|
272
|
+
const resolved = await Promise.race([
|
|
273
|
+
context.resolvePromise(result.value),
|
|
274
|
+
(async () => {
|
|
275
|
+
for (let i = 0;i < 1e4; i++) {
|
|
276
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
277
|
+
runtime.executePendingJobs();
|
|
278
|
+
const state = context.getPromiseState(result.value);
|
|
279
|
+
if (state.type !== "pending") {
|
|
280
|
+
return { pollingResolved: true, state };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
throw new Error("Promise resolution timeout");
|
|
284
|
+
})()
|
|
285
|
+
]);
|
|
286
|
+
result.value.dispose();
|
|
287
|
+
runtime.executePendingJobs();
|
|
288
|
+
if ("pollingResolved" in resolved) {
|
|
289
|
+
if (resolved.state.type === "rejected") {
|
|
290
|
+
const err = context.dump(resolved.state.error);
|
|
291
|
+
resolved.state.error.dispose();
|
|
292
|
+
throw new Error(`Promise rejected: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
293
|
+
}
|
|
294
|
+
if (resolved.state.value) {
|
|
295
|
+
resolved.state.value.dispose();
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
if (resolved.error) {
|
|
299
|
+
const err = context.dump(resolved.error);
|
|
300
|
+
resolved.error.dispose();
|
|
301
|
+
throw new Error(`Promise rejected: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
302
|
+
}
|
|
303
|
+
resolved.value.dispose();
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
result.value.dispose();
|
|
284
307
|
}
|
|
285
|
-
|
|
308
|
+
runtime.executePendingJobs();
|
|
286
309
|
} else {
|
|
287
|
-
result.
|
|
310
|
+
const result = context.evalCode(code, filename, {
|
|
311
|
+
type: "module"
|
|
312
|
+
});
|
|
313
|
+
if (result.error) {
|
|
314
|
+
const err = context.dump(result.error);
|
|
315
|
+
result.error.dispose();
|
|
316
|
+
throw new Error(`Eval failed: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
317
|
+
}
|
|
318
|
+
const promiseState = context.getPromiseState(result.value);
|
|
319
|
+
if (promiseState.type === "pending") {
|
|
320
|
+
runtime.executePendingJobs();
|
|
321
|
+
const resolved = await Promise.race([
|
|
322
|
+
context.resolvePromise(result.value),
|
|
323
|
+
(async () => {
|
|
324
|
+
for (let i = 0;i < 1e4; i++) {
|
|
325
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
326
|
+
runtime.executePendingJobs();
|
|
327
|
+
const state = context.getPromiseState(result.value);
|
|
328
|
+
if (state.type !== "pending") {
|
|
329
|
+
return { pollingResolved: true, state };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
throw new Error("Promise resolution timeout");
|
|
333
|
+
})()
|
|
334
|
+
]);
|
|
335
|
+
result.value.dispose();
|
|
336
|
+
runtime.executePendingJobs();
|
|
337
|
+
if ("pollingResolved" in resolved) {
|
|
338
|
+
if (resolved.state.type === "rejected") {
|
|
339
|
+
const err = context.dump(resolved.state.error);
|
|
340
|
+
resolved.state.error.dispose();
|
|
341
|
+
throw new Error(`Promise rejected: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
342
|
+
}
|
|
343
|
+
if (resolved.state.value) {
|
|
344
|
+
resolved.state.value.dispose();
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
if (resolved.error) {
|
|
348
|
+
const err = context.dump(resolved.error);
|
|
349
|
+
resolved.error.dispose();
|
|
350
|
+
throw new Error(`Promise rejected: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
351
|
+
}
|
|
352
|
+
resolved.value.dispose();
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
result.value.dispose();
|
|
356
|
+
}
|
|
357
|
+
runtime.executePendingJobs();
|
|
358
|
+
}
|
|
359
|
+
} finally {
|
|
360
|
+
if (maxExecutionMs) {
|
|
361
|
+
runtime.removeInterruptHandler();
|
|
288
362
|
}
|
|
289
|
-
runtime.executePendingJobs();
|
|
290
363
|
}
|
|
291
364
|
},
|
|
292
365
|
async dispose() {
|
|
@@ -312,6 +385,7 @@ async function createRuntime(options) {
|
|
|
312
385
|
if (result.error)
|
|
313
386
|
result.error.dispose();
|
|
314
387
|
}
|
|
388
|
+
cleanupAsyncIterators(context);
|
|
315
389
|
cleanupUnmarshaledHandles(context);
|
|
316
390
|
clearAllInstanceState();
|
|
317
391
|
try {
|
|
@@ -354,4 +428,4 @@ export {
|
|
|
354
428
|
createRuntime
|
|
355
429
|
};
|
|
356
430
|
|
|
357
|
-
//# debugId=
|
|
431
|
+
//# debugId=0011FDA20D60BFF864756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/create-runtime.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import {\n getQuickJS,\n newAsyncContext,\n type QuickJSAsyncContext,\n type QuickJSAsyncRuntime,\n type QuickJSContext,\n type QuickJSRuntime,\n} from \"quickjs-emscripten\";\nimport {\n setupCore,\n createStateMap,\n cleanupUnmarshaledHandles,\n clearAllInstanceState,\n defineFunction,\n defineAsyncFunction,\n type StateMap,\n type CoreHandle,\n} from \"@ricsam/quickjs-core\";\nimport {\n setupFetch,\n type SetupFetchOptions,\n type FetchHandle,\n type UpgradeRequest,\n type WebSocketCommand,\n} from \"@ricsam/quickjs-fetch\";\nimport {\n setupFs,\n type SetupFsOptions,\n type FsHandle,\n} from \"@ricsam/quickjs-fs\";\nimport {\n setupConsole,\n type ConsoleHandle,\n type ConsoleEntry,\n type ConsoleCallbacks,\n} from \"@ricsam/quickjs-console\";\nimport {\n setupEncoding,\n type EncodingHandle,\n} from \"@ricsam/quickjs-encoding\";\nimport {\n setupTimers,\n type TimersHandle,\n} from \"@ricsam/quickjs-timers\";\nimport {\n setupCrypto,\n type CryptoHandle,\n} from \"@ricsam/quickjs-crypto\";\nimport {\n setupTestEnvironment,\n type TestEnvironmentHandle,\n type RunResults,\n type TestEvent,\n} from \"@ricsam/quickjs-test-environment\";\nimport {\n setupPlaywright,\n type PlaywrightHandle,\n type PlaywrightOptions,\n type PlaywrightEvent,\n type BrowserConsoleLogEntry,\n type NetworkRequestInfo,\n type NetworkResponseInfo,\n} from \"@ricsam/quickjs-playwright\";\n\n// Re-export types\nexport type { ConsoleEntry, ConsoleCallbacks, TestEvent, PlaywrightEvent };\n\n/**\n * Module loader callback type.\n * Called when the runtime imports a module dynamically.\n * Returns the JavaScript source code for the module.\n */\nexport type ModuleLoaderCallback = (\n moduleName: string\n) => string | Promise<string>;\n\n/**\n * A custom function that can be called from within the runtime.\n */\nexport type CustomFunction = (...args: unknown[]) => unknown | Promise<unknown>;\n\n/**\n * Custom function definition with metadata.\n * Requires explicit `async` property to be clear about function behavior.\n */\nexport interface CustomFunctionDefinition {\n /** The function implementation */\n fn: CustomFunction;\n /** Whether the function is async (returns a Promise) */\n async: boolean;\n}\n\n/**\n * Custom functions to register in the runtime.\n * Each function must be defined with explicit async property.\n *\n * @example\n * ```typescript\n * customFunctions: {\n * // Async function\n * hashPassword: {\n * fn: async (password) => bcrypt.hash(password, 10),\n * async: true,\n * },\n * // Sync function\n * getConfig: {\n * fn: () => ({ environment: \"production\" }),\n * async: false,\n * },\n * }\n * ```\n */\nexport type CustomFunctions = Record<string, CustomFunctionDefinition>;\n\n/**\n * Fetch callback type.\n */\nexport type FetchCallback = (request: Request) => Response | Promise<Response>;\n\n/**\n * Test environment options.\n */\nexport interface TestEnvironmentOptions {\n /** Event callback for test events */\n onEvent?: (event: TestEvent) => void;\n /** Default test timeout in milliseconds */\n testTimeout?: number;\n}\n\n/**\n * Options for creating a runtime.\n */\nexport interface RuntimeOptions {\n /** Memory limit in megabytes (optional) */\n memoryLimitMB?: number;\n /** Console callback handlers */\n console?: ConsoleCallbacks;\n /** Fetch callback handler */\n fetch?: FetchCallback;\n /** File system options */\n fs?: SetupFsOptions;\n /** Module loader callback for resolving dynamic imports */\n moduleLoader?: ModuleLoaderCallback;\n /** Custom functions callable from within the runtime */\n customFunctions?: CustomFunctions;\n /** Current working directory for path operations. Defaults to \"/\" */\n cwd?: string;\n /** Test environment options. Set to true for defaults, or provide options. */\n testEnvironment?: boolean | TestEnvironmentOptions;\n /** Playwright options for browser automation */\n playwright?: PlaywrightOptions;\n}\n\n/**\n * Runtime fetch handle - provides access to fetch/serve operations.\n */\nexport interface RuntimeFetchHandle {\n /** Dispatch HTTP request to serve() handler */\n dispatchRequest(request: Request): Promise<Response>;\n\n /**\n * Check if the last request resulted in an upgrade request\n * Must be called immediately after dispatchRequest()\n */\n getUpgradeRequest(): UpgradeRequest | null;\n\n /**\n * Dispatch WebSocket open event\n * @param connectionId The connectionId from getUpgradeRequest()\n */\n dispatchWebSocketOpen(connectionId: string): void;\n\n /**\n * Dispatch WebSocket message event\n */\n dispatchWebSocketMessage(connectionId: string, message: string | ArrayBuffer): void;\n\n /**\n * Dispatch WebSocket close event\n */\n dispatchWebSocketClose(connectionId: string, code: number, reason: string): void;\n\n /**\n * Dispatch WebSocket error event\n */\n dispatchWebSocketError(connectionId: string, error: Error): void;\n\n /**\n * Register a callback for outgoing WebSocket messages/commands\n * Called when QuickJS code calls ws.send() or ws.close()\n */\n onWebSocketCommand(callback: (command: WebSocketCommand) => void): () => void;\n\n /** Check if serve() has been called */\n hasServeHandler(): boolean;\n\n /** Check if there are active WebSocket connections */\n hasActiveConnections(): boolean;\n}\n\n/**\n * Runtime timers handle - provides access to timer operations.\n */\nexport interface RuntimeTimersHandle {\n /** Clear all pending timers */\n clearAll(): void;\n}\n\n/**\n * Runtime console handle - provides access to console state.\n */\nexport interface RuntimeConsoleHandle {\n /** Reset all console state (timers, counters, group depth) */\n reset(): void;\n /** Get console.time() timers */\n getTimers(): Map<string, number>;\n /** Get console.count() counters */\n getCounters(): Map<string, number>;\n /** Get current console.group() nesting depth */\n getGroupDepth(): number;\n}\n\n/**\n * Runtime test environment handle - provides access to test operations.\n */\nexport interface RuntimeTestEnvironmentHandle {\n /** Run all registered tests */\n runTests(timeout?: number): Promise<RunResults>;\n /** Check if any tests have been registered */\n hasTests(): boolean;\n /** Get the number of registered tests */\n getTestCount(): number;\n /** Reset test state (clear registered tests) */\n reset(): void;\n}\n\n/**\n * Collected playwright data.\n */\nexport interface CollectedData {\n browserConsoleLogs: BrowserConsoleLogEntry[];\n networkRequests: NetworkRequestInfo[];\n networkResponses: NetworkResponseInfo[];\n}\n\n/**\n * Runtime playwright handle - provides access to playwright data.\n */\nexport interface RuntimePlaywrightHandle {\n /** Get collected browser logs and network data */\n getCollectedData(): CollectedData;\n /** Clear all collected data */\n clearCollectedData(): void;\n}\n\n/**\n * Runtime handle - the main interface for interacting with the runtime.\n */\nexport interface RuntimeHandle {\n /** Unique runtime identifier */\n readonly id: string;\n /** Execute code as ES module (supports top-level await) */\n eval(code: string, filename?: string): Promise<void>;\n /** Dispose all resources */\n dispose(): Promise<void>;\n\n /** Fetch handle - access to fetch/serve operations */\n readonly fetch: RuntimeFetchHandle;\n /** Timers handle - access to timer operations */\n readonly timers: RuntimeTimersHandle;\n /** Console handle - access to console state */\n readonly console: RuntimeConsoleHandle;\n /** Test environment handle - access to test operations */\n readonly testEnvironment: RuntimeTestEnvironmentHandle;\n /** Playwright handle - access to browser automation data */\n readonly playwright: RuntimePlaywrightHandle;\n}\n\n/**\n * Create a fully configured QuickJS runtime\n *\n * Sets up all WHATWG APIs: fetch, fs, console, crypto, encoding, timers.\n * Supports ES modules with custom module loader and custom host functions.\n *\n * @example\n * ```typescript\n * const runtime = await createRuntime({\n * console: { onEntry: (e) => console.log(\"[sandbox]\", e) },\n * fetch: async (request) => fetch(request),\n * moduleLoader: async (name) => {\n * if (name === \"@/utils\") {\n * return `export const add = (a, b) => a + b;`;\n * }\n * throw new Error(`Unknown module: ${name}`);\n * },\n * customFunctions: {\n * hashPassword: {\n * fn: async (pw) => Bun.password.hash(pw),\n * async: true,\n * },\n * },\n * });\n *\n * await runtime.eval(`\n * import { add } from \"@/utils\";\n * const hash = await hashPassword(\"secret\");\n * console.log(add(1, 2), hash);\n * `);\n *\n * await runtime.dispose();\n * ```\n */\nexport async function createRuntime(\n options?: RuntimeOptions\n): Promise<RuntimeHandle> {\n const opts = options ?? {};\n\n // Generate unique ID\n const id = crypto.randomUUID();\n\n // Determine if we need async context (for async module loader)\n const needsAsync = opts.moduleLoader !== undefined;\n\n let context: QuickJSContext | QuickJSAsyncContext;\n let runtime: QuickJSRuntime | QuickJSAsyncRuntime;\n let isAsyncContext = false;\n\n if (needsAsync) {\n // Create async context for module loading support\n const asyncContext = await newAsyncContext();\n context = asyncContext;\n runtime = asyncContext.runtime;\n isAsyncContext = true;\n\n // Set up module loader\n if (opts.moduleLoader) {\n const moduleLoader = opts.moduleLoader;\n (runtime as QuickJSAsyncRuntime).setModuleLoader(async (moduleName: string) => {\n try {\n const code = await moduleLoader(moduleName);\n return code;\n } catch (error) {\n // Return error in SuccessOrFail format\n return { error: error as Error };\n }\n });\n }\n } else {\n // Create sync context\n const QuickJS = await getQuickJS();\n runtime = QuickJS.newRuntime();\n context = runtime.newContext();\n }\n\n // Set memory limit if specified\n if (opts.memoryLimitMB) {\n runtime.setMemoryLimit(opts.memoryLimitMB * 1024 * 1024);\n }\n\n // Create shared state\n const stateMap = createStateMap();\n\n // Setup core APIs\n const coreHandle = setupCore(context, { stateMap });\n\n // Setup console\n const consoleHandle = setupConsole(context, {\n ...opts.console,\n stateMap,\n coreHandle,\n });\n\n // Setup encoding (btoa/atob)\n const encodingHandle = setupEncoding(context, { stateMap, coreHandle });\n\n // Setup timers\n const timersHandle = setupTimers(context, { stateMap, coreHandle });\n\n // Setup crypto\n const cryptoHandle = setupCrypto(context, { stateMap, coreHandle });\n\n // Setup fetch if callback provided\n let fetchHandle: FetchHandle | undefined;\n if (opts.fetch) {\n const fetchCallback = opts.fetch;\n fetchHandle = setupFetch(context, {\n stateMap,\n coreHandle,\n onFetch: async (request: Request) => {\n return Promise.resolve(fetchCallback(request));\n },\n });\n } else {\n // Still setup fetch for serve() support, but without fetch() function\n fetchHandle = setupFetch(context, { stateMap, coreHandle });\n }\n\n // Setup file system if provided\n let fsHandle: FsHandle | undefined;\n if (opts.fs) {\n fsHandle = setupFs(context, {\n ...opts.fs,\n stateMap,\n coreHandle,\n });\n }\n\n // Setup custom functions\n if (opts.customFunctions) {\n for (const [name, def] of Object.entries(opts.customFunctions)) {\n if (def.async) {\n const fn = defineAsyncFunction(context, name, async (...args: unknown[]) => {\n return def.fn(...args);\n });\n context.setProp(context.global, name, fn);\n fn.dispose();\n } else {\n const fn = defineFunction(context, name, (...args: unknown[]) => {\n return def.fn(...args);\n });\n context.setProp(context.global, name, fn);\n fn.dispose();\n }\n }\n }\n\n // Setup test environment if enabled\n let testEnvHandle: TestEnvironmentHandle | undefined;\n if (opts.testEnvironment) {\n const testEnvOpts = typeof opts.testEnvironment === \"object\" ? opts.testEnvironment : {};\n testEnvHandle = setupTestEnvironment(context, {\n stateMap,\n coreHandle,\n onEvent: testEnvOpts.onEvent,\n });\n }\n\n // Setup playwright if provided\n let playwrightHandle: PlaywrightHandle | undefined;\n if (opts.playwright) {\n playwrightHandle = setupPlaywright(context, {\n ...opts.playwright,\n stateMap,\n coreHandle,\n consoleCallbacks: opts.playwright.console ? opts.console : undefined,\n });\n }\n\n // Create fetch handle wrapper\n const runtimeFetchHandle: RuntimeFetchHandle = {\n async dispatchRequest(request: Request): Promise<Response> {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n return fetchHandle.dispatchRequest(request);\n },\n getUpgradeRequest() {\n return fetchHandle?.getUpgradeRequest() ?? null;\n },\n dispatchWebSocketOpen(connectionId: string) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketOpen(connectionId);\n },\n dispatchWebSocketMessage(connectionId: string, message: string | ArrayBuffer) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketMessage(connectionId, message);\n },\n dispatchWebSocketClose(connectionId: string, code: number, reason: string) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketClose(connectionId, code, reason);\n },\n dispatchWebSocketError(connectionId: string, error: Error) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketError(connectionId, error);\n },\n onWebSocketCommand(callback: (command: WebSocketCommand) => void) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n return fetchHandle.onWebSocketCommand(callback);\n },\n hasServeHandler() {\n return fetchHandle?.hasServeHandler() ?? false;\n },\n hasActiveConnections() {\n return fetchHandle?.hasActiveConnections() ?? false;\n },\n };\n\n // Create timers handle wrapper\n const runtimeTimersHandle: RuntimeTimersHandle = {\n clearAll() {\n timersHandle.clearAll();\n },\n };\n\n // Create console handle wrapper\n const runtimeConsoleHandle: RuntimeConsoleHandle = {\n reset() {\n consoleHandle.reset();\n },\n getTimers() {\n return consoleHandle.getTimers();\n },\n getCounters() {\n return consoleHandle.getCounters();\n },\n getGroupDepth() {\n return consoleHandle.getGroupDepth();\n },\n };\n\n // Create test environment handle wrapper\n const runtimeTestEnvironmentHandle: RuntimeTestEnvironmentHandle = {\n async runTests(_timeout?: number): Promise<RunResults> {\n if (!testEnvHandle) {\n throw new Error(\"Test environment not enabled. Set testEnvironment: true in createRuntime options.\");\n }\n return testEnvHandle.run();\n },\n hasTests() {\n return testEnvHandle?.hasTests() ?? false;\n },\n getTestCount() {\n return testEnvHandle?.getTestCount() ?? 0;\n },\n reset() {\n testEnvHandle?.reset();\n },\n };\n\n // Create playwright handle wrapper\n const runtimePlaywrightHandle: RuntimePlaywrightHandle = {\n getCollectedData() {\n if (!playwrightHandle) {\n return { browserConsoleLogs: [], networkRequests: [], networkResponses: [] };\n }\n return {\n browserConsoleLogs: playwrightHandle.getBrowserConsoleLogs(),\n networkRequests: playwrightHandle.getNetworkRequests(),\n networkResponses: playwrightHandle.getNetworkResponses(),\n };\n },\n clearCollectedData() {\n playwrightHandle?.clearCollected();\n },\n };\n\n return {\n id,\n fetch: runtimeFetchHandle,\n timers: runtimeTimersHandle,\n console: runtimeConsoleHandle,\n testEnvironment: runtimeTestEnvironmentHandle,\n playwright: runtimePlaywrightHandle,\n\n async eval(code: string, filename?: string): Promise<void> {\n if (isAsyncContext) {\n // Use evalCodeAsync for async context (supports top-level await and async module loading)\n const asyncContext = context as QuickJSAsyncContext;\n const result = await asyncContext.evalCodeAsync(code, filename ?? \"<eval>\", {\n type: \"module\",\n });\n\n if (result.error) {\n const err = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Eval failed: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n\n // Handle module exports (could be a promise for top-level await)\n const promiseState = context.getPromiseState(result.value);\n if (promiseState.type === \"pending\") {\n const resolved = await context.resolvePromise(result.value);\n result.value.dispose();\n\n if (resolved.error) {\n const err = context.dump(resolved.error);\n resolved.error.dispose();\n throw new Error(`Promise rejected: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n resolved.value.dispose();\n } else {\n result.value.dispose();\n }\n\n // Execute pending jobs\n runtime.executePendingJobs();\n } else {\n // Use evalCode for sync context\n const result = context.evalCode(code, filename ?? \"<eval>\", {\n type: \"module\",\n });\n\n if (result.error) {\n const err = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Eval failed: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n\n // Handle async module result\n const promiseState = context.getPromiseState(result.value);\n if (promiseState.type === \"pending\") {\n const resolved = await context.resolvePromise(result.value);\n result.value.dispose();\n\n // Execute pending jobs\n runtime.executePendingJobs();\n\n if (resolved.error) {\n const err = context.dump(resolved.error);\n resolved.error.dispose();\n throw new Error(`Promise rejected: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n resolved.value.dispose();\n } else {\n result.value.dispose();\n }\n\n // Execute any remaining pending jobs\n runtime.executePendingJobs();\n }\n },\n\n async dispose(): Promise<void> {\n // Drain pending jobs before disposal\n for (let i = 0; i < 1000; i++) {\n if (!runtime.hasPendingJob()) break;\n const result = runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n // Dispose handles in reverse order\n playwrightHandle?.dispose();\n testEnvHandle?.dispose();\n cryptoHandle.dispose();\n timersHandle.dispose();\n encodingHandle.dispose();\n consoleHandle.dispose();\n fsHandle?.dispose();\n fetchHandle?.dispose();\n\n // Drain pending jobs again\n for (let i = 0; i < 1000; i++) {\n if (!runtime.hasPendingJob()) break;\n const result = runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n // Clean up handles\n cleanupUnmarshaledHandles(context);\n clearAllInstanceState();\n\n // Clear globals to help GC\n try {\n const clearGlobals = context.evalCode(`\n (function() {\n const keysToDelete = Object.keys(globalThis).filter(k =>\n k !== 'globalThis' && k !== 'undefined' && k !== 'NaN' && k !== 'Infinity'\n );\n for (const key of keysToDelete) {\n try { globalThis[key] = undefined; } catch (e) {}\n }\n for (const key of keysToDelete) {\n try { delete globalThis[key]; } catch (e) {}\n }\n return keysToDelete.length;\n })()\n `);\n if (clearGlobals.error) {\n clearGlobals.error.dispose();\n } else {\n clearGlobals.value.dispose();\n }\n } catch {\n // Ignore errors during cleanup\n }\n\n // Final drain\n for (let i = 0; i < 100; i++) {\n if (!runtime.hasPendingJob()) break;\n const result = runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n coreHandle.dispose();\n context.dispose();\n\n // Only dispose runtime if it was created separately (not via newAsyncContext)\n if (!isAsyncContext) {\n runtime.dispose();\n }\n },\n };\n}\n"
|
|
5
|
+
"import {\n getQuickJS,\n newAsyncContext,\n shouldInterruptAfterDeadline,\n type QuickJSAsyncContext,\n type QuickJSAsyncRuntime,\n type QuickJSContext,\n type QuickJSRuntime,\n} from \"quickjs-emscripten\";\nimport {\n setupCore,\n createStateMap,\n cleanupUnmarshaledHandles,\n clearAllInstanceState,\n defineFunction,\n defineAsyncFunction,\n defineAsyncIteratorFunction,\n cleanupAsyncIterators,\n type StateMap,\n type CoreHandle,\n} from \"@ricsam/quickjs-core\";\nimport {\n setupFetch,\n type SetupFetchOptions,\n type FetchHandle,\n type UpgradeRequest,\n type WebSocketCommand,\n} from \"@ricsam/quickjs-fetch\";\nimport {\n setupFs,\n type SetupFsOptions,\n type FsHandle,\n} from \"@ricsam/quickjs-fs\";\nimport {\n setupConsole,\n type ConsoleHandle,\n type ConsoleEntry,\n type ConsoleCallbacks,\n} from \"@ricsam/quickjs-console\";\nimport {\n setupEncoding,\n type EncodingHandle,\n} from \"@ricsam/quickjs-encoding\";\nimport {\n setupTimers,\n type TimersHandle,\n} from \"@ricsam/quickjs-timers\";\nimport {\n setupCrypto,\n type CryptoHandle,\n} from \"@ricsam/quickjs-crypto\";\nimport {\n setupTestEnvironment,\n type TestEnvironmentHandle,\n type RunResults,\n type TestEvent,\n} from \"@ricsam/quickjs-test-environment\";\nimport {\n setupPlaywright,\n type PlaywrightHandle,\n type PlaywrightOptions,\n type PlaywrightEvent,\n type BrowserConsoleLogEntry,\n type NetworkRequestInfo,\n type NetworkResponseInfo,\n} from \"@ricsam/quickjs-playwright\";\n\n// Re-export types\nexport type { ConsoleEntry, ConsoleCallbacks, TestEvent, PlaywrightEvent };\n\n/**\n * Module loader callback type.\n * Called when the runtime imports a module dynamically.\n * Returns the JavaScript source code for the module.\n */\nexport type ModuleLoaderCallback = (\n moduleName: string\n) => string | Promise<string>;\n\n/**\n * A sync custom function that can be called from within the runtime.\n */\nexport type SyncCustomFunction = (...args: unknown[]) => unknown;\n\n/**\n * An async custom function that can be called from within the runtime.\n */\nexport type AsyncCustomFunction = (...args: unknown[]) => Promise<unknown>;\n\n/**\n * An async iterator custom function that can be called from within the runtime.\n */\nexport type AsyncIteratorCustomFunction = (...args: unknown[]) => AsyncGenerator<unknown, unknown, unknown>;\n\n/**\n * Custom function definition with discriminated union based on type.\n * BREAKING CHANGE: Replaced `async: boolean` with `type: 'sync' | 'async' | 'asyncIterator'`\n */\nexport type CustomFunctionDefinition =\n | { fn: SyncCustomFunction; type: 'sync' }\n | { fn: AsyncCustomFunction; type: 'async' }\n | { fn: AsyncIteratorCustomFunction; type: 'asyncIterator' };\n\n/**\n * Custom functions to register in the runtime.\n * Each function must be defined with explicit type property.\n *\n * @example\n * ```typescript\n * customFunctions: {\n * // Async function\n * hashPassword: {\n * fn: async (password) => bcrypt.hash(password, 10),\n * type: 'async',\n * },\n * // Sync function\n * getConfig: {\n * fn: () => ({ environment: \"production\" }),\n * type: 'sync',\n * },\n * // Async iterator function\n * streamData: {\n * fn: async function* (count) {\n * for (let i = 0; i < count; i++) yield i;\n * },\n * type: 'asyncIterator',\n * },\n * }\n * ```\n */\nexport type CustomFunctions = Record<string, CustomFunctionDefinition>;\n\n/**\n * Fetch callback type.\n */\nexport type FetchCallback = (request: Request) => Response | Promise<Response>;\n\n/**\n * Test environment options.\n */\nexport interface TestEnvironmentOptions {\n /** Event callback for test events */\n onEvent?: (event: TestEvent) => void;\n /** Default test timeout in milliseconds */\n testTimeout?: number;\n}\n\n/**\n * Options for creating a runtime.\n */\nexport interface RuntimeOptions {\n /** Memory limit in megabytes (optional) */\n memoryLimitMB?: number;\n /** Console callback handlers */\n console?: ConsoleCallbacks;\n /** Fetch callback handler */\n fetch?: FetchCallback;\n /** File system options */\n fs?: SetupFsOptions;\n /** Module loader callback for resolving dynamic imports */\n moduleLoader?: ModuleLoaderCallback;\n /** Custom functions callable from within the runtime */\n customFunctions?: CustomFunctions;\n /** Current working directory for path operations. Defaults to \"/\" */\n cwd?: string;\n /** Test environment options. Set to true for defaults, or provide options. */\n testEnvironment?: boolean | TestEnvironmentOptions;\n /** Playwright options for browser automation */\n playwright?: PlaywrightOptions;\n}\n\n/**\n * Runtime fetch handle - provides access to fetch/serve operations.\n */\nexport interface RuntimeFetchHandle {\n /** Dispatch HTTP request to serve() handler */\n dispatchRequest(request: Request): Promise<Response>;\n\n /**\n * Check if the last request resulted in an upgrade request\n * Must be called immediately after dispatchRequest()\n */\n getUpgradeRequest(): UpgradeRequest | null;\n\n /**\n * Dispatch WebSocket open event\n * @param connectionId The connectionId from getUpgradeRequest()\n */\n dispatchWebSocketOpen(connectionId: string): void;\n\n /**\n * Dispatch WebSocket message event\n */\n dispatchWebSocketMessage(connectionId: string, message: string | ArrayBuffer): void;\n\n /**\n * Dispatch WebSocket close event\n */\n dispatchWebSocketClose(connectionId: string, code: number, reason: string): void;\n\n /**\n * Dispatch WebSocket error event\n */\n dispatchWebSocketError(connectionId: string, error: Error): void;\n\n /**\n * Register a callback for outgoing WebSocket messages/commands\n * Called when QuickJS code calls ws.send() or ws.close()\n */\n onWebSocketCommand(callback: (command: WebSocketCommand) => void): () => void;\n\n /** Check if serve() has been called */\n hasServeHandler(): boolean;\n\n /** Check if there are active WebSocket connections */\n hasActiveConnections(): boolean;\n}\n\n/**\n * Runtime timers handle - provides access to timer operations.\n */\nexport interface RuntimeTimersHandle {\n /** Clear all pending timers */\n clearAll(): void;\n}\n\n/**\n * Runtime console handle - provides access to console state.\n */\nexport interface RuntimeConsoleHandle {\n /** Reset all console state (timers, counters, group depth) */\n reset(): void;\n /** Get console.time() timers */\n getTimers(): Map<string, number>;\n /** Get console.count() counters */\n getCounters(): Map<string, number>;\n /** Get current console.group() nesting depth */\n getGroupDepth(): number;\n}\n\n/**\n * Runtime test environment handle - provides access to test operations.\n */\nexport interface RuntimeTestEnvironmentHandle {\n /** Run all registered tests */\n runTests(timeout?: number): Promise<RunResults>;\n /** Check if any tests have been registered */\n hasTests(): boolean;\n /** Get the number of registered tests */\n getTestCount(): number;\n /** Reset test state (clear registered tests) */\n reset(): void;\n}\n\n/**\n * Collected playwright data.\n */\nexport interface CollectedData {\n browserConsoleLogs: BrowserConsoleLogEntry[];\n networkRequests: NetworkRequestInfo[];\n networkResponses: NetworkResponseInfo[];\n}\n\n/**\n * Runtime playwright handle - provides access to playwright data.\n */\nexport interface RuntimePlaywrightHandle {\n /** Get collected browser logs and network data */\n getCollectedData(): CollectedData;\n /** Clear all collected data */\n clearCollectedData(): void;\n}\n\n/**\n * Options for the eval method.\n */\nexport interface EvalOptions {\n /** Filename for the evaluated code (for stack traces) */\n filename?: string;\n /** Maximum execution time in milliseconds (optional) */\n maxExecutionMs?: number;\n}\n\n/**\n * Runtime handle - the main interface for interacting with the runtime.\n */\nexport interface RuntimeHandle {\n /** Unique runtime identifier */\n readonly id: string;\n /** Execute code as ES module (supports top-level await) */\n eval(code: string, filenameOrOptions?: string | EvalOptions): Promise<void>;\n /** Dispose all resources */\n dispose(): Promise<void>;\n\n /** Fetch handle - access to fetch/serve operations */\n readonly fetch: RuntimeFetchHandle;\n /** Timers handle - access to timer operations */\n readonly timers: RuntimeTimersHandle;\n /** Console handle - access to console state */\n readonly console: RuntimeConsoleHandle;\n /** Test environment handle - access to test operations */\n readonly testEnvironment: RuntimeTestEnvironmentHandle;\n /** Playwright handle - access to browser automation data */\n readonly playwright: RuntimePlaywrightHandle;\n}\n\n/**\n * Create a fully configured QuickJS runtime\n *\n * Sets up all WHATWG APIs: fetch, fs, console, crypto, encoding, timers.\n * Supports ES modules with custom module loader and custom host functions.\n *\n * @example\n * ```typescript\n * const runtime = await createRuntime({\n * console: { onEntry: (e) => console.log(\"[sandbox]\", e) },\n * fetch: async (request) => fetch(request),\n * moduleLoader: async (name) => {\n * if (name === \"@/utils\") {\n * return `export const add = (a, b) => a + b;`;\n * }\n * throw new Error(`Unknown module: ${name}`);\n * },\n * customFunctions: {\n * hashPassword: {\n * fn: async (pw) => Bun.password.hash(pw),\n * type: 'async',\n * },\n * },\n * });\n *\n * await runtime.eval(`\n * import { add } from \"@/utils\";\n * const hash = await hashPassword(\"secret\");\n * console.log(add(1, 2), hash);\n * `);\n *\n * await runtime.dispose();\n * ```\n */\nexport async function createRuntime(\n options?: RuntimeOptions\n): Promise<RuntimeHandle> {\n const opts = options ?? {};\n\n // Generate unique ID\n const id = crypto.randomUUID();\n\n // Determine if we need async context (for async module loader)\n const needsAsync = opts.moduleLoader !== undefined;\n\n let context: QuickJSContext | QuickJSAsyncContext;\n let runtime: QuickJSRuntime | QuickJSAsyncRuntime;\n let isAsyncContext = false;\n\n if (needsAsync) {\n // Create async context for module loading support\n const asyncContext = await newAsyncContext();\n context = asyncContext;\n runtime = asyncContext.runtime;\n isAsyncContext = true;\n\n // Set up module loader\n if (opts.moduleLoader) {\n const moduleLoader = opts.moduleLoader;\n (runtime as QuickJSAsyncRuntime).setModuleLoader(async (moduleName: string) => {\n try {\n const code = await moduleLoader(moduleName);\n return code;\n } catch (error) {\n // Return error in SuccessOrFail format\n return { error: error as Error };\n }\n });\n }\n } else {\n // Create sync context\n const QuickJS = await getQuickJS();\n runtime = QuickJS.newRuntime();\n context = runtime.newContext();\n }\n\n // Set memory limit if specified\n if (opts.memoryLimitMB) {\n runtime.setMemoryLimit(opts.memoryLimitMB * 1024 * 1024);\n }\n\n // Create shared state\n const stateMap = createStateMap();\n\n // Setup core APIs\n const coreHandle = setupCore(context, { stateMap });\n\n // Setup console\n const consoleHandle = setupConsole(context, {\n ...opts.console,\n stateMap,\n coreHandle,\n });\n\n // Setup encoding (btoa/atob)\n const encodingHandle = setupEncoding(context, { stateMap, coreHandle });\n\n // Setup timers\n const timersHandle = setupTimers(context, { stateMap, coreHandle });\n\n // Setup crypto\n const cryptoHandle = setupCrypto(context, { stateMap, coreHandle });\n\n // Setup fetch if callback provided\n let fetchHandle: FetchHandle | undefined;\n if (opts.fetch) {\n const fetchCallback = opts.fetch;\n fetchHandle = setupFetch(context, {\n stateMap,\n coreHandle,\n onFetch: async (request: Request) => {\n return Promise.resolve(fetchCallback(request));\n },\n });\n } else {\n // Still setup fetch for serve() support, but without fetch() function\n fetchHandle = setupFetch(context, { stateMap, coreHandle });\n }\n\n // Setup file system if provided\n let fsHandle: FsHandle | undefined;\n if (opts.fs) {\n fsHandle = setupFs(context, {\n ...opts.fs,\n stateMap,\n coreHandle,\n });\n }\n\n // Setup custom functions\n if (opts.customFunctions) {\n for (const [name, def] of Object.entries(opts.customFunctions)) {\n if (def.type === 'async') {\n const fn = defineAsyncFunction(context, name, async (...args: unknown[]) => {\n return def.fn(...args);\n });\n context.setProp(context.global, name, fn);\n fn.dispose();\n } else if (def.type === 'asyncIterator') {\n const fn = defineAsyncIteratorFunction(context, name, (...args: unknown[]) => {\n return def.fn(...args);\n });\n context.setProp(context.global, name, fn);\n fn.dispose();\n } else {\n // type === 'sync'\n const fn = defineFunction(context, name, (...args: unknown[]) => {\n return def.fn(...args);\n });\n context.setProp(context.global, name, fn);\n fn.dispose();\n }\n }\n }\n\n // Setup test environment if enabled\n let testEnvHandle: TestEnvironmentHandle | undefined;\n if (opts.testEnvironment) {\n const testEnvOpts = typeof opts.testEnvironment === \"object\" ? opts.testEnvironment : {};\n testEnvHandle = setupTestEnvironment(context, {\n stateMap,\n coreHandle,\n onEvent: testEnvOpts.onEvent,\n });\n }\n\n // Setup playwright if provided\n let playwrightHandle: PlaywrightHandle | undefined;\n if (opts.playwright) {\n playwrightHandle = setupPlaywright(context, {\n ...opts.playwright,\n stateMap,\n coreHandle,\n consoleCallbacks: opts.playwright.console ? opts.console : undefined,\n });\n }\n\n // Create fetch handle wrapper\n const runtimeFetchHandle: RuntimeFetchHandle = {\n async dispatchRequest(request: Request): Promise<Response> {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n return fetchHandle.dispatchRequest(request);\n },\n getUpgradeRequest() {\n return fetchHandle?.getUpgradeRequest() ?? null;\n },\n dispatchWebSocketOpen(connectionId: string) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketOpen(connectionId);\n },\n dispatchWebSocketMessage(connectionId: string, message: string | ArrayBuffer) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketMessage(connectionId, message);\n },\n dispatchWebSocketClose(connectionId: string, code: number, reason: string) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketClose(connectionId, code, reason);\n },\n dispatchWebSocketError(connectionId: string, error: Error) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n fetchHandle.dispatchWebSocketError(connectionId, error);\n },\n onWebSocketCommand(callback: (command: WebSocketCommand) => void) {\n if (!fetchHandle) {\n throw new Error(\"Fetch not configured\");\n }\n return fetchHandle.onWebSocketCommand(callback);\n },\n hasServeHandler() {\n return fetchHandle?.hasServeHandler() ?? false;\n },\n hasActiveConnections() {\n return fetchHandle?.hasActiveConnections() ?? false;\n },\n };\n\n // Create timers handle wrapper\n const runtimeTimersHandle: RuntimeTimersHandle = {\n clearAll() {\n timersHandle.clearAll();\n },\n };\n\n // Create console handle wrapper\n const runtimeConsoleHandle: RuntimeConsoleHandle = {\n reset() {\n consoleHandle.reset();\n },\n getTimers() {\n return consoleHandle.getTimers();\n },\n getCounters() {\n return consoleHandle.getCounters();\n },\n getGroupDepth() {\n return consoleHandle.getGroupDepth();\n },\n };\n\n // Create test environment handle wrapper\n const runtimeTestEnvironmentHandle: RuntimeTestEnvironmentHandle = {\n async runTests(_timeout?: number): Promise<RunResults> {\n if (!testEnvHandle) {\n throw new Error(\"Test environment not enabled. Set testEnvironment: true in createRuntime options.\");\n }\n return testEnvHandle.run();\n },\n hasTests() {\n return testEnvHandle?.hasTests() ?? false;\n },\n getTestCount() {\n return testEnvHandle?.getTestCount() ?? 0;\n },\n reset() {\n testEnvHandle?.reset();\n },\n };\n\n // Create playwright handle wrapper\n const runtimePlaywrightHandle: RuntimePlaywrightHandle = {\n getCollectedData() {\n if (!playwrightHandle) {\n return { browserConsoleLogs: [], networkRequests: [], networkResponses: [] };\n }\n return {\n browserConsoleLogs: playwrightHandle.getBrowserConsoleLogs(),\n networkRequests: playwrightHandle.getNetworkRequests(),\n networkResponses: playwrightHandle.getNetworkResponses(),\n };\n },\n clearCollectedData() {\n playwrightHandle?.clearCollected();\n },\n };\n\n return {\n id,\n fetch: runtimeFetchHandle,\n timers: runtimeTimersHandle,\n console: runtimeConsoleHandle,\n testEnvironment: runtimeTestEnvironmentHandle,\n playwright: runtimePlaywrightHandle,\n\n async eval(code: string, filenameOrOptions?: string | EvalOptions): Promise<void> {\n // Parse options\n const evalOpts: EvalOptions = typeof filenameOrOptions === \"string\"\n ? { filename: filenameOrOptions }\n : filenameOrOptions ?? {};\n const filename = evalOpts.filename ?? \"<eval>\";\n const maxExecutionMs = evalOpts.maxExecutionMs;\n\n // Set up interrupt handler if maxExecutionMs is configured\n if (maxExecutionMs) {\n const deadline = Date.now() + maxExecutionMs;\n runtime.setInterruptHandler(shouldInterruptAfterDeadline(deadline));\n }\n\n try {\n if (isAsyncContext) {\n // Use evalCodeAsync for async context (supports top-level await and async module loading)\n const asyncContext = context as QuickJSAsyncContext;\n const result = await asyncContext.evalCodeAsync(code, filename, {\n type: \"module\",\n });\n\n if (result.error) {\n const err = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Eval failed: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n\n // Handle module exports (could be a promise for top-level await)\n const promiseState = context.getPromiseState(result.value);\n if (promiseState.type === \"pending\") {\n // Execute pending jobs first to allow JS async functions to schedule\n runtime.executePendingJobs();\n\n // Race between resolvePromise and active job execution loop\n const resolved = await Promise.race([\n context.resolvePromise(result.value),\n (async () => {\n for (let i = 0; i < 10000; i++) {\n await new Promise((r) => setTimeout(r, 0));\n runtime.executePendingJobs();\n const state = context.getPromiseState(result.value);\n if (state.type !== \"pending\") {\n return { pollingResolved: true as const, state };\n }\n }\n throw new Error(\"Promise resolution timeout\");\n })(),\n ]);\n\n result.value.dispose();\n runtime.executePendingJobs();\n\n if (\"pollingResolved\" in resolved) {\n if (resolved.state.type === \"rejected\") {\n const err = context.dump(resolved.state.error!);\n resolved.state.error!.dispose();\n throw new Error(`Promise rejected: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n if (resolved.state.value) {\n resolved.state.value.dispose();\n }\n } else {\n if (resolved.error) {\n const err = context.dump(resolved.error);\n resolved.error.dispose();\n throw new Error(`Promise rejected: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n resolved.value.dispose();\n }\n } else {\n result.value.dispose();\n }\n\n // Execute pending jobs\n runtime.executePendingJobs();\n } else {\n // Use evalCode for sync context\n const result = context.evalCode(code, filename, {\n type: \"module\",\n });\n\n if (result.error) {\n const err = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Eval failed: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n\n // Handle async module result\n const promiseState = context.getPromiseState(result.value);\n if (promiseState.type === \"pending\") {\n // Execute pending jobs first to allow JS async functions to schedule\n runtime.executePendingJobs();\n\n // Race between resolvePromise and active job execution loop\n const resolved = await Promise.race([\n context.resolvePromise(result.value),\n (async () => {\n for (let i = 0; i < 10000; i++) {\n await new Promise((r) => setTimeout(r, 0));\n runtime.executePendingJobs();\n const state = context.getPromiseState(result.value);\n if (state.type !== \"pending\") {\n return { pollingResolved: true as const, state };\n }\n }\n throw new Error(\"Promise resolution timeout\");\n })(),\n ]);\n\n result.value.dispose();\n runtime.executePendingJobs();\n\n if (\"pollingResolved\" in resolved) {\n if (resolved.state.type === \"rejected\") {\n const err = context.dump(resolved.state.error!);\n resolved.state.error!.dispose();\n throw new Error(`Promise rejected: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n if (resolved.state.value) {\n resolved.state.value.dispose();\n }\n } else {\n if (resolved.error) {\n const err = context.dump(resolved.error);\n resolved.error.dispose();\n throw new Error(`Promise rejected: ${typeof err === \"string\" ? err : JSON.stringify(err)}`);\n }\n resolved.value.dispose();\n }\n } else {\n result.value.dispose();\n }\n\n // Execute any remaining pending jobs\n runtime.executePendingJobs();\n }\n } finally {\n // Remove interrupt handler after eval\n if (maxExecutionMs) {\n runtime.removeInterruptHandler();\n }\n }\n },\n\n async dispose(): Promise<void> {\n // Drain pending jobs before disposal\n for (let i = 0; i < 1000; i++) {\n if (!runtime.hasPendingJob()) break;\n const result = runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n // Dispose handles in reverse order\n playwrightHandle?.dispose();\n testEnvHandle?.dispose();\n cryptoHandle.dispose();\n timersHandle.dispose();\n encodingHandle.dispose();\n consoleHandle.dispose();\n fsHandle?.dispose();\n fetchHandle?.dispose();\n\n // Drain pending jobs again\n for (let i = 0; i < 1000; i++) {\n if (!runtime.hasPendingJob()) break;\n const result = runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n // Clean up handles\n cleanupAsyncIterators(context);\n cleanupUnmarshaledHandles(context);\n clearAllInstanceState();\n\n // Clear globals to help GC\n try {\n const clearGlobals = context.evalCode(`\n (function() {\n const keysToDelete = Object.keys(globalThis).filter(k =>\n k !== 'globalThis' && k !== 'undefined' && k !== 'NaN' && k !== 'Infinity'\n );\n for (const key of keysToDelete) {\n try { globalThis[key] = undefined; } catch (e) {}\n }\n for (const key of keysToDelete) {\n try { delete globalThis[key]; } catch (e) {}\n }\n return keysToDelete.length;\n })()\n `);\n if (clearGlobals.error) {\n clearGlobals.error.dispose();\n } else {\n clearGlobals.value.dispose();\n }\n } catch {\n // Ignore errors during cleanup\n }\n\n // Final drain\n for (let i = 0; i < 100; i++) {\n if (!runtime.hasPendingJob()) break;\n const result = runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n coreHandle.dispose();\n context.dispose();\n\n // Only dispose runtime if it was created separately (not via newAsyncContext)\n if (!isAsyncContext) {\n runtime.dispose();\n }\n },\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;AAAA;AAAA;AAAA;AAAA;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "0011FDA20D60BFF864756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/index.mjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"// Re-export everything from sub-packages\nexport * from \"@ricsam/quickjs-core\";\nexport * from \"@ricsam/quickjs-console\";\nexport * from \"@ricsam/quickjs-crypto\";\nexport * from \"@ricsam/quickjs-encoding\";\nexport * from \"@ricsam/quickjs-fetch\";\nexport * from \"@ricsam/quickjs-fs\";\nexport * from \"@ricsam/quickjs-timers\";\n\n// Export high-level createRuntime API\nexport {\n createRuntime,\n type RuntimeOptions,\n type RuntimeHandle,\n type RuntimeFetchHandle,\n type RuntimeTimersHandle,\n type RuntimeConsoleHandle,\n type ModuleLoaderCallback,\n type
|
|
5
|
+
"// Re-export everything from sub-packages\nexport * from \"@ricsam/quickjs-core\";\nexport * from \"@ricsam/quickjs-console\";\nexport * from \"@ricsam/quickjs-crypto\";\nexport * from \"@ricsam/quickjs-encoding\";\nexport * from \"@ricsam/quickjs-fetch\";\nexport * from \"@ricsam/quickjs-fs\";\nexport * from \"@ricsam/quickjs-timers\";\n\n// Export high-level createRuntime API\nexport {\n createRuntime,\n type RuntimeOptions,\n type RuntimeHandle,\n type RuntimeFetchHandle,\n type RuntimeTimersHandle,\n type RuntimeConsoleHandle,\n type ModuleLoaderCallback,\n type SyncCustomFunction,\n type AsyncCustomFunction,\n type AsyncIteratorCustomFunction,\n type CustomFunctionDefinition,\n type CustomFunctions,\n type FetchCallback,\n type EvalOptions,\n} from \"./create-runtime.mjs\";\n\nimport type { QuickJSContext } from \"quickjs-emscripten\";\nimport {\n setupCore,\n createStateMap,\n cleanupUnmarshaledHandles,\n clearAllInstanceState,\n type StateMap,\n type CoreHandle,\n} from \"@ricsam/quickjs-core\";\nimport {\n setupFetch,\n type SetupFetchOptions,\n type FetchHandle,\n} from \"@ricsam/quickjs-fetch\";\nimport {\n setupFs,\n type SetupFsOptions,\n type FsHandle,\n} from \"@ricsam/quickjs-fs\";\nimport {\n setupConsole,\n type SetupConsoleOptions,\n type ConsoleHandle,\n} from \"@ricsam/quickjs-console\";\nimport {\n setupEncoding,\n type SetupEncodingOptions,\n type EncodingHandle,\n} from \"@ricsam/quickjs-encoding\";\nimport {\n setupTimers,\n type SetupTimersOptions,\n type TimersHandle,\n} from \"@ricsam/quickjs-timers\";\nimport {\n setupCrypto,\n type SetupCryptoOptions,\n type CryptoHandle,\n} from \"@ricsam/quickjs-crypto\";\n\n/**\n * Options for setting up the runtime\n */\nexport interface SetupRuntimeOptions {\n /**\n * Fetch API options\n * Pass `true` to enable with defaults\n * Pass `false` or omit to disable\n */\n fetch?: SetupFetchOptions | boolean;\n\n /**\n * File system options\n * Pass options object to enable\n * Pass `false` or omit to disable\n */\n fs?: SetupFsOptions | false;\n\n /**\n * Console options\n * Pass `true` to enable with defaults\n * Pass handlers object to customize\n * Pass `false` or omit to disable\n */\n console?: SetupConsoleOptions | boolean;\n\n /**\n * Encoding options (atob/btoa)\n * Pass `true` to enable with defaults\n * Pass `false` or omit to disable\n */\n encoding?: SetupEncodingOptions | boolean;\n\n /**\n * Timer options (setTimeout, setInterval, clearTimeout, clearInterval)\n * Pass `true` to enable with defaults\n * Pass `false` or omit to disable\n */\n timers?: SetupTimersOptions | boolean;\n\n /**\n * Crypto API options (crypto.subtle, crypto.getRandomValues, crypto.randomUUID)\n * Pass `true` to enable with defaults\n * Pass `false` or omit to disable\n */\n crypto?: SetupCryptoOptions | boolean;\n}\n\n/**\n * Handle returned from setupRuntime (low-level API)\n */\nexport interface SetupRuntimeHandle {\n /** Core handle (always present) */\n core: CoreHandle;\n\n /** Fetch handle (if enabled) */\n fetch?: FetchHandle;\n\n /** File system handle (if enabled) */\n fs?: FsHandle;\n\n /** Console handle (if enabled) */\n console?: ConsoleHandle;\n\n /** Encoding handle (if enabled) */\n encoding?: EncodingHandle;\n\n /** Timers handle (if enabled) */\n timers?: TimersHandle;\n\n /** Crypto handle (if enabled) */\n crypto?: CryptoHandle;\n\n /** Shared state map */\n readonly stateMap: StateMap;\n\n /** Get the underlying QuickJS context */\n readonly context: QuickJSContext;\n\n /**\n * Drain all pending jobs with timeout.\n * Call BEFORE dispose() to prevent GC assertion failures.\n * @param maxIterations - Maximum iterations (default: 1000)\n * @returns true if all jobs completed, false if timed out\n */\n drainPendingJobs(maxIterations?: number): boolean;\n\n /** Dispose all handles and cleanup */\n dispose(): void;\n}\n\n/**\n * Setup multiple APIs in a QuickJS context\n *\n * @example\n * const handle = setupRuntime(context, {\n * fetch: {\n * onFetch: async (req) => fetch(req),\n * },\n * fs: {\n * getDirectory: async (path) => createNodeDirectoryHandle(`/sandbox${path}`),\n * },\n * });\n *\n * // Use the context...\n *\n * handle.dispose();\n */\nexport function setupRuntime(\n context: QuickJSContext,\n options: SetupRuntimeOptions = {}\n): SetupRuntimeHandle {\n const stateMap = createStateMap();\n\n // Always setup core\n const core = setupCore(context, { stateMap });\n\n let fetch: FetchHandle | undefined;\n let fs: FsHandle | undefined;\n let consoleHandle: ConsoleHandle | undefined;\n let encoding: EncodingHandle | undefined;\n let timers: TimersHandle | undefined;\n let cryptoHandle: CryptoHandle | undefined;\n\n // Setup fetch if enabled\n if (options.fetch) {\n const fetchOptions: SetupFetchOptions =\n options.fetch === true\n ? { stateMap, coreHandle: core }\n : { ...options.fetch, stateMap, coreHandle: core };\n\n fetch = setupFetch(context, fetchOptions);\n }\n\n // Setup fs if provided\n if (options.fs) {\n fs = setupFs(context, {\n ...options.fs,\n stateMap,\n coreHandle: core,\n });\n }\n\n // Setup console if enabled\n if (options.console) {\n const consoleOptions: SetupConsoleOptions =\n options.console === true\n ? { stateMap, coreHandle: core }\n : { ...options.console, stateMap, coreHandle: core };\n\n consoleHandle = setupConsole(context, consoleOptions);\n }\n\n // Setup encoding if enabled\n if (options.encoding) {\n const encodingOptions: SetupEncodingOptions =\n options.encoding === true\n ? { stateMap, coreHandle: core }\n : { ...options.encoding, stateMap, coreHandle: core };\n\n encoding = setupEncoding(context, encodingOptions);\n }\n\n // Setup timers if enabled\n if (options.timers) {\n const timersOptions: SetupTimersOptions =\n options.timers === true\n ? { stateMap, coreHandle: core }\n : { ...options.timers, stateMap, coreHandle: core };\n\n timers = setupTimers(context, timersOptions);\n }\n\n // Setup crypto if enabled\n if (options.crypto) {\n const cryptoOptions: SetupCryptoOptions =\n options.crypto === true\n ? { stateMap, coreHandle: core }\n : { ...options.crypto, stateMap, coreHandle: core };\n\n cryptoHandle = setupCrypto(context, cryptoOptions);\n }\n\n return {\n core,\n fetch,\n fs,\n console: consoleHandle,\n encoding,\n timers,\n crypto: cryptoHandle,\n stateMap,\n get context() {\n return context;\n },\n drainPendingJobs(maxIterations = 1000): boolean {\n for (let i = 0; i < maxIterations; i++) {\n if (!context.runtime.hasPendingJob()) {\n return true;\n }\n const result = context.runtime.executePendingJobs();\n if (result.error) {\n result.error.dispose();\n }\n }\n return !context.runtime.hasPendingJob();\n },\n dispose() {\n // Drain pending jobs before disposal\n for (let i = 0; i < 1000; i++) {\n if (!context.runtime.hasPendingJob()) break;\n const result = context.runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n // Dispose sub-handles in reverse order\n cryptoHandle?.dispose();\n timers?.dispose();\n encoding?.dispose();\n consoleHandle?.dispose();\n fs?.dispose();\n fetch?.dispose();\n\n // Drain pending jobs again after sub-handle disposal\n // (fetch?.dispose() calls evalCode which can create new jobs)\n for (let i = 0; i < 1000; i++) {\n if (!context.runtime.hasPendingJob()) break;\n const result = context.runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n // Clean up tracked handles\n cleanupUnmarshaledHandles(context);\n clearAllInstanceState();\n\n // Clear globals to help GC break circular references\n try {\n const clearGlobals = context.evalCode(`\n (function() {\n const keysToDelete = Object.keys(globalThis).filter(k =>\n k !== 'globalThis' && k !== 'undefined' && k !== 'NaN' && k !== 'Infinity'\n );\n for (const key of keysToDelete) {\n try { globalThis[key] = undefined; } catch (e) {}\n }\n for (const key of keysToDelete) {\n try { delete globalThis[key]; } catch (e) {}\n }\n return keysToDelete.length;\n })()\n `);\n if (clearGlobals.error) {\n clearGlobals.error.dispose();\n } else {\n clearGlobals.value.dispose();\n }\n } catch (e) {\n // Ignore errors during cleanup\n }\n\n // Final drain\n for (let i = 0; i < 100; i++) {\n if (!context.runtime.hasPendingJob()) break;\n const result = context.runtime.executePendingJobs();\n if (result.error) result.error.dispose();\n }\n\n core.dispose();\n },\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAAA;AAAA;
|
|
7
|
+
"mappings": "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",
|
|
8
8
|
"debugId": "B678E9E9303FA62664756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/package.json
CHANGED
|
@@ -11,22 +11,34 @@ export type { ConsoleEntry, ConsoleCallbacks, TestEvent, PlaywrightEvent };
|
|
|
11
11
|
*/
|
|
12
12
|
export type ModuleLoaderCallback = (moduleName: string) => string | Promise<string>;
|
|
13
13
|
/**
|
|
14
|
-
* A custom function that can be called from within the runtime.
|
|
14
|
+
* A sync custom function that can be called from within the runtime.
|
|
15
15
|
*/
|
|
16
|
-
export type
|
|
16
|
+
export type SyncCustomFunction = (...args: unknown[]) => unknown;
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
* Requires explicit `async` property to be clear about function behavior.
|
|
18
|
+
* An async custom function that can be called from within the runtime.
|
|
20
19
|
*/
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
export type AsyncCustomFunction = (...args: unknown[]) => Promise<unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* An async iterator custom function that can be called from within the runtime.
|
|
23
|
+
*/
|
|
24
|
+
export type AsyncIteratorCustomFunction = (...args: unknown[]) => AsyncGenerator<unknown, unknown, unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* Custom function definition with discriminated union based on type.
|
|
27
|
+
* BREAKING CHANGE: Replaced `async: boolean` with `type: 'sync' | 'async' | 'asyncIterator'`
|
|
28
|
+
*/
|
|
29
|
+
export type CustomFunctionDefinition = {
|
|
30
|
+
fn: SyncCustomFunction;
|
|
31
|
+
type: 'sync';
|
|
32
|
+
} | {
|
|
33
|
+
fn: AsyncCustomFunction;
|
|
34
|
+
type: 'async';
|
|
35
|
+
} | {
|
|
36
|
+
fn: AsyncIteratorCustomFunction;
|
|
37
|
+
type: 'asyncIterator';
|
|
38
|
+
};
|
|
27
39
|
/**
|
|
28
40
|
* Custom functions to register in the runtime.
|
|
29
|
-
* Each function must be defined with explicit
|
|
41
|
+
* Each function must be defined with explicit type property.
|
|
30
42
|
*
|
|
31
43
|
* @example
|
|
32
44
|
* ```typescript
|
|
@@ -34,12 +46,19 @@ export interface CustomFunctionDefinition {
|
|
|
34
46
|
* // Async function
|
|
35
47
|
* hashPassword: {
|
|
36
48
|
* fn: async (password) => bcrypt.hash(password, 10),
|
|
37
|
-
*
|
|
49
|
+
* type: 'async',
|
|
38
50
|
* },
|
|
39
51
|
* // Sync function
|
|
40
52
|
* getConfig: {
|
|
41
53
|
* fn: () => ({ environment: "production" }),
|
|
42
|
-
*
|
|
54
|
+
* type: 'sync',
|
|
55
|
+
* },
|
|
56
|
+
* // Async iterator function
|
|
57
|
+
* streamData: {
|
|
58
|
+
* fn: async function* (count) {
|
|
59
|
+
* for (let i = 0; i < count; i++) yield i;
|
|
60
|
+
* },
|
|
61
|
+
* type: 'asyncIterator',
|
|
43
62
|
* },
|
|
44
63
|
* }
|
|
45
64
|
* ```
|
|
@@ -169,6 +188,15 @@ export interface RuntimePlaywrightHandle {
|
|
|
169
188
|
/** Clear all collected data */
|
|
170
189
|
clearCollectedData(): void;
|
|
171
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Options for the eval method.
|
|
193
|
+
*/
|
|
194
|
+
export interface EvalOptions {
|
|
195
|
+
/** Filename for the evaluated code (for stack traces) */
|
|
196
|
+
filename?: string;
|
|
197
|
+
/** Maximum execution time in milliseconds (optional) */
|
|
198
|
+
maxExecutionMs?: number;
|
|
199
|
+
}
|
|
172
200
|
/**
|
|
173
201
|
* Runtime handle - the main interface for interacting with the runtime.
|
|
174
202
|
*/
|
|
@@ -176,7 +204,7 @@ export interface RuntimeHandle {
|
|
|
176
204
|
/** Unique runtime identifier */
|
|
177
205
|
readonly id: string;
|
|
178
206
|
/** Execute code as ES module (supports top-level await) */
|
|
179
|
-
eval(code: string,
|
|
207
|
+
eval(code: string, filenameOrOptions?: string | EvalOptions): Promise<void>;
|
|
180
208
|
/** Dispose all resources */
|
|
181
209
|
dispose(): Promise<void>;
|
|
182
210
|
/** Fetch handle - access to fetch/serve operations */
|
|
@@ -210,7 +238,7 @@ export interface RuntimeHandle {
|
|
|
210
238
|
* customFunctions: {
|
|
211
239
|
* hashPassword: {
|
|
212
240
|
* fn: async (pw) => Bun.password.hash(pw),
|
|
213
|
-
*
|
|
241
|
+
* type: 'async',
|
|
214
242
|
* },
|
|
215
243
|
* },
|
|
216
244
|
* });
|
package/dist/types/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export * from "@ricsam/quickjs-encoding";
|
|
|
5
5
|
export * from "@ricsam/quickjs-fetch";
|
|
6
6
|
export * from "@ricsam/quickjs-fs";
|
|
7
7
|
export * from "@ricsam/quickjs-timers";
|
|
8
|
-
export { createRuntime, type RuntimeOptions, type RuntimeHandle, type RuntimeFetchHandle, type RuntimeTimersHandle, type RuntimeConsoleHandle, type ModuleLoaderCallback, type
|
|
8
|
+
export { createRuntime, type RuntimeOptions, type RuntimeHandle, type RuntimeFetchHandle, type RuntimeTimersHandle, type RuntimeConsoleHandle, type ModuleLoaderCallback, type SyncCustomFunction, type AsyncCustomFunction, type AsyncIteratorCustomFunction, type CustomFunctionDefinition, type CustomFunctions, type FetchCallback, type EvalOptions, } from "./create-runtime.ts";
|
|
9
9
|
import type { QuickJSContext } from "quickjs-emscripten";
|
|
10
10
|
import { type StateMap, type CoreHandle } from "@ricsam/quickjs-core";
|
|
11
11
|
import { type SetupFetchOptions, type FetchHandle } from "@ricsam/quickjs-fetch";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ricsam/quickjs-runtime",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.25",
|
|
4
4
|
"main": "./dist/cjs/index.cjs",
|
|
5
5
|
"types": "./dist/types/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
"typecheck": "tsc --noEmit"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@ricsam/quickjs-core": "^0.2.
|
|
20
|
-
"@ricsam/quickjs-console": "^0.2.
|
|
21
|
-
"@ricsam/quickjs-crypto": "^0.2.
|
|
22
|
-
"@ricsam/quickjs-encoding": "^0.2.
|
|
23
|
-
"@ricsam/quickjs-fetch": "^0.2.
|
|
24
|
-
"@ricsam/quickjs-fs": "^0.2.
|
|
25
|
-
"@ricsam/quickjs-timers": "^0.2.
|
|
26
|
-
"@ricsam/quickjs-test-environment": "^0.2.
|
|
27
|
-
"@ricsam/quickjs-playwright": "^0.2.
|
|
19
|
+
"@ricsam/quickjs-core": "^0.2.17",
|
|
20
|
+
"@ricsam/quickjs-console": "^0.2.18",
|
|
21
|
+
"@ricsam/quickjs-crypto": "^0.2.17",
|
|
22
|
+
"@ricsam/quickjs-encoding": "^0.2.17",
|
|
23
|
+
"@ricsam/quickjs-fetch": "^0.2.20",
|
|
24
|
+
"@ricsam/quickjs-fs": "^0.2.19",
|
|
25
|
+
"@ricsam/quickjs-timers": "^0.2.17",
|
|
26
|
+
"@ricsam/quickjs-test-environment": "^0.2.18",
|
|
27
|
+
"@ricsam/quickjs-playwright": "^0.2.20",
|
|
28
28
|
"quickjs-emscripten": "^0.31.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|