@ricsam/quickjs-runtime 0.2.19 → 0.2.21
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 +272 -30
- package/dist/cjs/create-runtime.cjs +329 -0
- package/dist/cjs/create-runtime.cjs.map +10 -0
- package/dist/cjs/index.cjs +4 -2
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/create-runtime.mjs +324 -0
- package/dist/mjs/create-runtime.mjs.map +10 -0
- package/dist/mjs/index.mjs +6 -2
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/create-runtime.d.ts +201 -0
- package/dist/types/index.d.ts +4 -3
- package/package.json +10 -8
package/README.md
CHANGED
|
@@ -1,49 +1,291 @@
|
|
|
1
1
|
# @ricsam/quickjs-runtime
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The recommended way to create QuickJS sandboxed runtimes with web-standard APIs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @ricsam/quickjs-runtime quickjs-emscripten
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { createRuntime } from "@ricsam/quickjs-runtime";
|
|
15
|
+
|
|
16
|
+
const runtime = await createRuntime({
|
|
17
|
+
console: {
|
|
18
|
+
onEntry: (entry) => {
|
|
19
|
+
if (entry.type === "output") {
|
|
20
|
+
console.log(`[sandbox:${entry.level}]`, ...entry.args);
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
fetch: async (request) => fetch(request),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await runtime.eval(`
|
|
28
|
+
const response = await fetch("https://api.example.com/data");
|
|
29
|
+
const data = await response.json();
|
|
30
|
+
console.log("Fetched:", data);
|
|
31
|
+
`);
|
|
32
|
+
|
|
33
|
+
await runtime.dispose();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
|
|
38
|
+
### createRuntime(options?)
|
|
39
|
+
|
|
40
|
+
Creates a fully configured QuickJS runtime with all WHATWG APIs.
|
|
4
41
|
|
|
5
42
|
```typescript
|
|
6
|
-
|
|
43
|
+
const runtime = await createRuntime({
|
|
44
|
+
// Memory limit in bytes
|
|
45
|
+
memoryLimit: 1024 * 1024 * 10, // 10MB
|
|
7
46
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
onFetch: async (req) => fetch(req),
|
|
47
|
+
// Console output handler
|
|
48
|
+
console: {
|
|
49
|
+
onEntry: (entry) => { /* handle console output */ },
|
|
12
50
|
},
|
|
13
|
-
|
|
51
|
+
|
|
52
|
+
// Fetch handler for outbound requests
|
|
53
|
+
fetch: async (request) => fetch(request),
|
|
54
|
+
|
|
55
|
+
// File system access
|
|
14
56
|
fs: {
|
|
15
57
|
getDirectory: async (path) => createNodeDirectoryHandle(`./sandbox${path}`),
|
|
16
58
|
},
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
59
|
+
|
|
60
|
+
// ES module loader
|
|
61
|
+
moduleLoader: async (moduleName) => {
|
|
62
|
+
if (moduleName === "@/utils") {
|
|
63
|
+
return `export const add = (a, b) => a + b;`;
|
|
64
|
+
}
|
|
65
|
+
throw new Error(`Unknown module: ${moduleName}`);
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// Custom host functions
|
|
69
|
+
customFunctions: {
|
|
70
|
+
hashPassword: {
|
|
71
|
+
fn: async (password) => Bun.password.hash(password),
|
|
72
|
+
async: true,
|
|
73
|
+
},
|
|
74
|
+
getConfig: {
|
|
75
|
+
fn: () => ({ environment: "production" }),
|
|
76
|
+
async: false,
|
|
21
77
|
},
|
|
22
78
|
},
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
79
|
+
|
|
80
|
+
// Enable test environment (describe, it, expect)
|
|
81
|
+
testEnvironment: {
|
|
82
|
+
onEvent: (event) => { /* handle test events */ },
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
// Playwright browser automation
|
|
86
|
+
playwright: {
|
|
87
|
+
page: playwrightPage,
|
|
88
|
+
baseUrl: "https://example.com",
|
|
89
|
+
},
|
|
27
90
|
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### RuntimeHandle
|
|
94
|
+
|
|
95
|
+
The returned handle provides:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
interface RuntimeHandle {
|
|
99
|
+
// Unique runtime identifier
|
|
100
|
+
readonly id: string;
|
|
101
|
+
|
|
102
|
+
// Execute code as ES module (supports top-level await)
|
|
103
|
+
eval(code: string, filename?: string): Promise<void>;
|
|
28
104
|
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
handle.fetch; // FetchHandle (if enabled)
|
|
32
|
-
handle.fs; // FsHandle (if enabled)
|
|
33
|
-
handle.console; // ConsoleHandle (if enabled)
|
|
34
|
-
handle.crypto; // CryptoHandle (if enabled)
|
|
35
|
-
handle.encoding; // EncodingHandle (if enabled)
|
|
105
|
+
// Dispose all resources
|
|
106
|
+
dispose(): Promise<void>;
|
|
36
107
|
|
|
37
|
-
|
|
108
|
+
// Sub-handles for specific features
|
|
109
|
+
readonly fetch: RuntimeFetchHandle;
|
|
110
|
+
readonly timers: RuntimeTimersHandle;
|
|
111
|
+
readonly console: RuntimeConsoleHandle;
|
|
112
|
+
readonly testEnvironment: RuntimeTestEnvironmentHandle;
|
|
113
|
+
readonly playwright: RuntimePlaywrightHandle;
|
|
114
|
+
}
|
|
38
115
|
```
|
|
39
116
|
|
|
40
|
-
|
|
117
|
+
## Examples
|
|
118
|
+
|
|
119
|
+
### HTTP Server
|
|
41
120
|
|
|
42
121
|
```typescript
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
console: true, // Enable console with no handlers (silent)
|
|
46
|
-
crypto: true, // Enable Web Crypto API
|
|
47
|
-
encoding: true, // Enable atob/btoa
|
|
122
|
+
const runtime = await createRuntime({
|
|
123
|
+
console: { onEntry: (e) => e.type === "output" && console.log(...e.args) },
|
|
48
124
|
});
|
|
49
|
-
|
|
125
|
+
|
|
126
|
+
await runtime.eval(`
|
|
127
|
+
serve({
|
|
128
|
+
fetch(request) {
|
|
129
|
+
const url = new URL(request.url);
|
|
130
|
+
return Response.json({ path: url.pathname });
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
`);
|
|
134
|
+
|
|
135
|
+
// Dispatch requests to the sandboxed server
|
|
136
|
+
const response = await runtime.fetch.dispatchRequest(
|
|
137
|
+
new Request("http://localhost/api/users")
|
|
138
|
+
);
|
|
139
|
+
console.log(await response.json()); // { path: "/api/users" }
|
|
140
|
+
|
|
141
|
+
await runtime.dispose();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Running Tests
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
const runtime = await createRuntime({
|
|
148
|
+
testEnvironment: {
|
|
149
|
+
onEvent: (event) => {
|
|
150
|
+
if (event.type === "testEnd") {
|
|
151
|
+
const icon = event.test.status === "pass" ? "✓" : "✗";
|
|
152
|
+
console.log(`${icon} ${event.test.fullName}`);
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
await runtime.eval(`
|
|
159
|
+
describe("Math", () => {
|
|
160
|
+
it("adds numbers", () => {
|
|
161
|
+
expect(1 + 1).toBe(2);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
`);
|
|
165
|
+
|
|
166
|
+
const results = await runtime.testEnvironment.runTests();
|
|
167
|
+
console.log(`${results.passed}/${results.total} passed`);
|
|
168
|
+
|
|
169
|
+
await runtime.dispose();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Browser Automation with Playwright
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { chromium } from "playwright";
|
|
176
|
+
|
|
177
|
+
const browser = await chromium.launch();
|
|
178
|
+
const page = await browser.newPage();
|
|
179
|
+
|
|
180
|
+
const runtime = await createRuntime({
|
|
181
|
+
testEnvironment: true,
|
|
182
|
+
playwright: {
|
|
183
|
+
page,
|
|
184
|
+
baseUrl: "https://example.com",
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
await runtime.eval(`
|
|
189
|
+
describe("Homepage", () => {
|
|
190
|
+
it("displays welcome message", async () => {
|
|
191
|
+
await page.goto("/");
|
|
192
|
+
await expect(page.getByRole("heading")).toContainText("Welcome");
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
`);
|
|
196
|
+
|
|
197
|
+
await runtime.testEnvironment.runTests();
|
|
198
|
+
await runtime.dispose();
|
|
199
|
+
await browser.close();
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Custom Functions
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
const runtime = await createRuntime({
|
|
206
|
+
customFunctions: {
|
|
207
|
+
// Async function
|
|
208
|
+
hashPassword: {
|
|
209
|
+
fn: async (password) => Bun.password.hash(password),
|
|
210
|
+
async: true,
|
|
211
|
+
},
|
|
212
|
+
// Sync function
|
|
213
|
+
generateId: {
|
|
214
|
+
fn: () => crypto.randomUUID(),
|
|
215
|
+
async: false,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
await runtime.eval(`
|
|
221
|
+
const hash = await hashPassword("secret123");
|
|
222
|
+
const id = generateId();
|
|
223
|
+
console.log({ hash, id });
|
|
224
|
+
`);
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### ES Modules
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
const runtime = await createRuntime({
|
|
231
|
+
moduleLoader: async (moduleName) => {
|
|
232
|
+
const modules = {
|
|
233
|
+
"@/utils": `export const double = (n) => n * 2;`,
|
|
234
|
+
"@/config": `export default { apiUrl: "https://api.example.com" };`,
|
|
235
|
+
};
|
|
236
|
+
if (moduleName in modules) {
|
|
237
|
+
return modules[moduleName];
|
|
238
|
+
}
|
|
239
|
+
throw new Error(`Module not found: ${moduleName}`);
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
await runtime.eval(`
|
|
244
|
+
import { double } from "@/utils";
|
|
245
|
+
import config from "@/config";
|
|
246
|
+
|
|
247
|
+
console.log(double(21)); // 42
|
|
248
|
+
console.log(config.apiUrl);
|
|
249
|
+
`);
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Included APIs
|
|
253
|
+
|
|
254
|
+
When you use `createRuntime()`, the following globals are automatically available in the sandbox:
|
|
255
|
+
|
|
256
|
+
- **Console**: `console.log`, `console.warn`, `console.error`, etc.
|
|
257
|
+
- **Fetch**: `fetch`, `Request`, `Response`, `Headers`, `FormData`, `AbortController`
|
|
258
|
+
- **Server**: `serve()` with WebSocket support
|
|
259
|
+
- **Crypto**: `crypto.getRandomValues()`, `crypto.randomUUID()`, `crypto.subtle`
|
|
260
|
+
- **Encoding**: `atob()`, `btoa()`
|
|
261
|
+
- **Timers**: `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`
|
|
262
|
+
- **Streams**: `ReadableStream`, `WritableStream`, `TransformStream`
|
|
263
|
+
- **Blob/File**: `Blob`, `File`
|
|
264
|
+
- **Path**: `path.join()`, `path.resolve()`, etc.
|
|
265
|
+
|
|
266
|
+
Optional (when configured):
|
|
267
|
+
- **File System**: `fs.getDirectory()` (requires `fs` option)
|
|
268
|
+
- **Test Environment**: `describe`, `it`, `expect` (requires `testEnvironment` option)
|
|
269
|
+
- **Playwright**: `page` object (requires `playwright` option)
|
|
270
|
+
|
|
271
|
+
## Security
|
|
272
|
+
|
|
273
|
+
- **No automatic network access** - `fetch` option must be explicitly provided
|
|
274
|
+
- **File system isolation** - `fs.getDirectory` controls all path access
|
|
275
|
+
- **Memory limits** - Use `memoryLimit` option to prevent resource exhaustion
|
|
276
|
+
- **No access to host** - Code runs in isolated QuickJS VM
|
|
277
|
+
|
|
278
|
+
## Advanced: Low-level API
|
|
279
|
+
|
|
280
|
+
For advanced use cases requiring direct context manipulation, you can use the low-level `setupRuntime()` function or individual package setup functions. See the individual package READMEs for details:
|
|
281
|
+
|
|
282
|
+
- [@ricsam/quickjs-core](../core)
|
|
283
|
+
- [@ricsam/quickjs-console](../console)
|
|
284
|
+
- [@ricsam/quickjs-fetch](../fetch)
|
|
285
|
+
- [@ricsam/quickjs-fs](../fs)
|
|
286
|
+
- [@ricsam/quickjs-crypto](../crypto)
|
|
287
|
+
- [@ricsam/quickjs-encoding](../encoding)
|
|
288
|
+
- [@ricsam/quickjs-timers](../timers)
|
|
289
|
+
- [@ricsam/quickjs-path](../path)
|
|
290
|
+
- [@ricsam/quickjs-test-environment](../test-environment)
|
|
291
|
+
- [@ricsam/quickjs-playwright](../playwright)
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// @bun @bun-cjs
|
|
2
|
+
(function(exports, require, module, __filename, __dirname) {var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
7
|
+
var __toCommonJS = (from) => {
|
|
8
|
+
var entry = __moduleCache.get(from), desc;
|
|
9
|
+
if (entry)
|
|
10
|
+
return entry;
|
|
11
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
13
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
14
|
+
get: () => from[key],
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
}));
|
|
17
|
+
__moduleCache.set(from, entry);
|
|
18
|
+
return entry;
|
|
19
|
+
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// packages/runtime/src/create-runtime.ts
|
|
31
|
+
var exports_create_runtime = {};
|
|
32
|
+
__export(exports_create_runtime, {
|
|
33
|
+
createRuntime: () => createRuntime
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(exports_create_runtime);
|
|
36
|
+
var import_quickjs_emscripten = require("quickjs-emscripten");
|
|
37
|
+
var import_quickjs_core = require("@ricsam/quickjs-core");
|
|
38
|
+
var import_quickjs_fetch = require("@ricsam/quickjs-fetch");
|
|
39
|
+
var import_quickjs_fs = require("@ricsam/quickjs-fs");
|
|
40
|
+
var import_quickjs_console = require("@ricsam/quickjs-console");
|
|
41
|
+
var import_quickjs_encoding = require("@ricsam/quickjs-encoding");
|
|
42
|
+
var import_quickjs_timers = require("@ricsam/quickjs-timers");
|
|
43
|
+
var import_quickjs_crypto = require("@ricsam/quickjs-crypto");
|
|
44
|
+
var import_quickjs_test_environment = require("@ricsam/quickjs-test-environment");
|
|
45
|
+
var import_quickjs_playwright = require("@ricsam/quickjs-playwright");
|
|
46
|
+
async function createRuntime(options) {
|
|
47
|
+
const opts = options ?? {};
|
|
48
|
+
const id = crypto.randomUUID();
|
|
49
|
+
const needsAsync = opts.moduleLoader !== undefined;
|
|
50
|
+
let context;
|
|
51
|
+
let runtime;
|
|
52
|
+
let isAsyncContext = false;
|
|
53
|
+
if (needsAsync) {
|
|
54
|
+
const asyncContext = await import_quickjs_emscripten.newAsyncContext();
|
|
55
|
+
context = asyncContext;
|
|
56
|
+
runtime = asyncContext.runtime;
|
|
57
|
+
isAsyncContext = true;
|
|
58
|
+
if (opts.moduleLoader) {
|
|
59
|
+
const moduleLoader = opts.moduleLoader;
|
|
60
|
+
runtime.setModuleLoader(async (moduleName) => {
|
|
61
|
+
try {
|
|
62
|
+
const code = await moduleLoader(moduleName);
|
|
63
|
+
return code;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return { error };
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
const QuickJS = await import_quickjs_emscripten.getQuickJS();
|
|
71
|
+
runtime = QuickJS.newRuntime();
|
|
72
|
+
context = runtime.newContext();
|
|
73
|
+
}
|
|
74
|
+
if (opts.memoryLimit) {
|
|
75
|
+
runtime.setMemoryLimit(opts.memoryLimit);
|
|
76
|
+
}
|
|
77
|
+
const stateMap = import_quickjs_core.createStateMap();
|
|
78
|
+
const coreHandle = import_quickjs_core.setupCore(context, { stateMap });
|
|
79
|
+
const consoleHandle = import_quickjs_console.setupConsole(context, {
|
|
80
|
+
...opts.console,
|
|
81
|
+
stateMap,
|
|
82
|
+
coreHandle
|
|
83
|
+
});
|
|
84
|
+
const encodingHandle = import_quickjs_encoding.setupEncoding(context, { stateMap, coreHandle });
|
|
85
|
+
const timersHandle = import_quickjs_timers.setupTimers(context, { stateMap, coreHandle });
|
|
86
|
+
const cryptoHandle = import_quickjs_crypto.setupCrypto(context, { stateMap, coreHandle });
|
|
87
|
+
let fetchHandle;
|
|
88
|
+
if (opts.fetch) {
|
|
89
|
+
const fetchCallback = opts.fetch;
|
|
90
|
+
fetchHandle = import_quickjs_fetch.setupFetch(context, {
|
|
91
|
+
stateMap,
|
|
92
|
+
coreHandle,
|
|
93
|
+
onFetch: async (request) => {
|
|
94
|
+
return Promise.resolve(fetchCallback(request));
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
fetchHandle = import_quickjs_fetch.setupFetch(context, { stateMap, coreHandle });
|
|
99
|
+
}
|
|
100
|
+
let fsHandle;
|
|
101
|
+
if (opts.fs) {
|
|
102
|
+
fsHandle = import_quickjs_fs.setupFs(context, {
|
|
103
|
+
...opts.fs,
|
|
104
|
+
stateMap,
|
|
105
|
+
coreHandle
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (opts.customFunctions) {
|
|
109
|
+
for (const [name, def] of Object.entries(opts.customFunctions)) {
|
|
110
|
+
if (def.async) {
|
|
111
|
+
const fn = import_quickjs_core.defineAsyncFunction(context, name, async (...args) => {
|
|
112
|
+
return def.fn(...args);
|
|
113
|
+
});
|
|
114
|
+
context.setProp(context.global, name, fn);
|
|
115
|
+
fn.dispose();
|
|
116
|
+
} else {
|
|
117
|
+
const fn = import_quickjs_core.defineFunction(context, name, (...args) => {
|
|
118
|
+
return def.fn(...args);
|
|
119
|
+
});
|
|
120
|
+
context.setProp(context.global, name, fn);
|
|
121
|
+
fn.dispose();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
let testEnvHandle;
|
|
126
|
+
if (opts.testEnvironment) {
|
|
127
|
+
const testEnvOpts = typeof opts.testEnvironment === "object" ? opts.testEnvironment : {};
|
|
128
|
+
testEnvHandle = import_quickjs_test_environment.setupTestEnvironment(context, {
|
|
129
|
+
stateMap,
|
|
130
|
+
coreHandle,
|
|
131
|
+
onEvent: testEnvOpts.onEvent
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
let playwrightHandle;
|
|
135
|
+
if (opts.playwright) {
|
|
136
|
+
playwrightHandle = import_quickjs_playwright.setupPlaywright(context, {
|
|
137
|
+
...opts.playwright,
|
|
138
|
+
stateMap,
|
|
139
|
+
coreHandle,
|
|
140
|
+
consoleCallbacks: opts.playwright.console ? opts.console : undefined
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
const runtimeFetchHandle = {
|
|
144
|
+
async dispatchRequest(request, _options) {
|
|
145
|
+
if (!fetchHandle) {
|
|
146
|
+
throw new Error("Fetch not configured");
|
|
147
|
+
}
|
|
148
|
+
return fetchHandle.dispatchRequest(request);
|
|
149
|
+
},
|
|
150
|
+
hasServeHandler() {
|
|
151
|
+
return fetchHandle?.hasServeHandler() ?? false;
|
|
152
|
+
},
|
|
153
|
+
hasActiveConnections() {
|
|
154
|
+
return fetchHandle?.hasActiveConnections() ?? false;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
const runtimeTimersHandle = {
|
|
158
|
+
clearAll() {
|
|
159
|
+
timersHandle.clearAll();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const runtimeConsoleHandle = {
|
|
163
|
+
reset() {
|
|
164
|
+
consoleHandle.reset();
|
|
165
|
+
},
|
|
166
|
+
getTimers() {
|
|
167
|
+
return consoleHandle.getTimers();
|
|
168
|
+
},
|
|
169
|
+
getCounters() {
|
|
170
|
+
return consoleHandle.getCounters();
|
|
171
|
+
},
|
|
172
|
+
getGroupDepth() {
|
|
173
|
+
return consoleHandle.getGroupDepth();
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const runtimeTestEnvironmentHandle = {
|
|
177
|
+
async runTests(_timeout) {
|
|
178
|
+
if (!testEnvHandle) {
|
|
179
|
+
throw new Error("Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
180
|
+
}
|
|
181
|
+
return testEnvHandle.run();
|
|
182
|
+
},
|
|
183
|
+
hasTests() {
|
|
184
|
+
return testEnvHandle?.hasTests() ?? false;
|
|
185
|
+
},
|
|
186
|
+
getTestCount() {
|
|
187
|
+
return testEnvHandle?.getTestCount() ?? 0;
|
|
188
|
+
},
|
|
189
|
+
reset() {
|
|
190
|
+
testEnvHandle?.reset();
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
const runtimePlaywrightHandle = {
|
|
194
|
+
getCollectedData() {
|
|
195
|
+
if (!playwrightHandle) {
|
|
196
|
+
return { browserConsoleLogs: [], networkRequests: [], networkResponses: [] };
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
browserConsoleLogs: playwrightHandle.getBrowserConsoleLogs(),
|
|
200
|
+
networkRequests: playwrightHandle.getNetworkRequests(),
|
|
201
|
+
networkResponses: playwrightHandle.getNetworkResponses()
|
|
202
|
+
};
|
|
203
|
+
},
|
|
204
|
+
clearCollectedData() {
|
|
205
|
+
playwrightHandle?.clearCollected();
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
return {
|
|
209
|
+
id,
|
|
210
|
+
fetch: runtimeFetchHandle,
|
|
211
|
+
timers: runtimeTimersHandle,
|
|
212
|
+
console: runtimeConsoleHandle,
|
|
213
|
+
testEnvironment: runtimeTestEnvironmentHandle,
|
|
214
|
+
playwright: runtimePlaywrightHandle,
|
|
215
|
+
async eval(code, filename) {
|
|
216
|
+
if (isAsyncContext) {
|
|
217
|
+
const asyncContext = context;
|
|
218
|
+
const result = await asyncContext.evalCodeAsync(code, filename ?? "<eval>", {
|
|
219
|
+
type: "module"
|
|
220
|
+
});
|
|
221
|
+
if (result.error) {
|
|
222
|
+
const err = context.dump(result.error);
|
|
223
|
+
result.error.dispose();
|
|
224
|
+
throw new Error(`Eval failed: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
225
|
+
}
|
|
226
|
+
const promiseState = context.getPromiseState(result.value);
|
|
227
|
+
if (promiseState.type === "pending") {
|
|
228
|
+
const resolved = await context.resolvePromise(result.value);
|
|
229
|
+
result.value.dispose();
|
|
230
|
+
if (resolved.error) {
|
|
231
|
+
const err = context.dump(resolved.error);
|
|
232
|
+
resolved.error.dispose();
|
|
233
|
+
throw new Error(`Promise rejected: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
234
|
+
}
|
|
235
|
+
resolved.value.dispose();
|
|
236
|
+
} else {
|
|
237
|
+
result.value.dispose();
|
|
238
|
+
}
|
|
239
|
+
runtime.executePendingJobs();
|
|
240
|
+
} else {
|
|
241
|
+
const result = context.evalCode(code, filename ?? "<eval>", {
|
|
242
|
+
type: "module"
|
|
243
|
+
});
|
|
244
|
+
if (result.error) {
|
|
245
|
+
const err = context.dump(result.error);
|
|
246
|
+
result.error.dispose();
|
|
247
|
+
throw new Error(`Eval failed: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
248
|
+
}
|
|
249
|
+
const promiseState = context.getPromiseState(result.value);
|
|
250
|
+
if (promiseState.type === "pending") {
|
|
251
|
+
const resolved = await context.resolvePromise(result.value);
|
|
252
|
+
result.value.dispose();
|
|
253
|
+
runtime.executePendingJobs();
|
|
254
|
+
if (resolved.error) {
|
|
255
|
+
const err = context.dump(resolved.error);
|
|
256
|
+
resolved.error.dispose();
|
|
257
|
+
throw new Error(`Promise rejected: ${typeof err === "string" ? err : JSON.stringify(err)}`);
|
|
258
|
+
}
|
|
259
|
+
resolved.value.dispose();
|
|
260
|
+
} else {
|
|
261
|
+
result.value.dispose();
|
|
262
|
+
}
|
|
263
|
+
runtime.executePendingJobs();
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
async dispose() {
|
|
267
|
+
for (let i = 0;i < 1000; i++) {
|
|
268
|
+
if (!runtime.hasPendingJob())
|
|
269
|
+
break;
|
|
270
|
+
const result = runtime.executePendingJobs();
|
|
271
|
+
if (result.error)
|
|
272
|
+
result.error.dispose();
|
|
273
|
+
}
|
|
274
|
+
playwrightHandle?.dispose();
|
|
275
|
+
testEnvHandle?.dispose();
|
|
276
|
+
cryptoHandle.dispose();
|
|
277
|
+
timersHandle.dispose();
|
|
278
|
+
encodingHandle.dispose();
|
|
279
|
+
consoleHandle.dispose();
|
|
280
|
+
fsHandle?.dispose();
|
|
281
|
+
fetchHandle?.dispose();
|
|
282
|
+
for (let i = 0;i < 1000; i++) {
|
|
283
|
+
if (!runtime.hasPendingJob())
|
|
284
|
+
break;
|
|
285
|
+
const result = runtime.executePendingJobs();
|
|
286
|
+
if (result.error)
|
|
287
|
+
result.error.dispose();
|
|
288
|
+
}
|
|
289
|
+
import_quickjs_core.cleanupUnmarshaledHandles(context);
|
|
290
|
+
import_quickjs_core.clearAllInstanceState();
|
|
291
|
+
try {
|
|
292
|
+
const clearGlobals = context.evalCode(`
|
|
293
|
+
(function() {
|
|
294
|
+
const keysToDelete = Object.keys(globalThis).filter(k =>
|
|
295
|
+
k !== 'globalThis' && k !== 'undefined' && k !== 'NaN' && k !== 'Infinity'
|
|
296
|
+
);
|
|
297
|
+
for (const key of keysToDelete) {
|
|
298
|
+
try { globalThis[key] = undefined; } catch (e) {}
|
|
299
|
+
}
|
|
300
|
+
for (const key of keysToDelete) {
|
|
301
|
+
try { delete globalThis[key]; } catch (e) {}
|
|
302
|
+
}
|
|
303
|
+
return keysToDelete.length;
|
|
304
|
+
})()
|
|
305
|
+
`);
|
|
306
|
+
if (clearGlobals.error) {
|
|
307
|
+
clearGlobals.error.dispose();
|
|
308
|
+
} else {
|
|
309
|
+
clearGlobals.value.dispose();
|
|
310
|
+
}
|
|
311
|
+
} catch {}
|
|
312
|
+
for (let i = 0;i < 100; i++) {
|
|
313
|
+
if (!runtime.hasPendingJob())
|
|
314
|
+
break;
|
|
315
|
+
const result = runtime.executePendingJobs();
|
|
316
|
+
if (result.error)
|
|
317
|
+
result.error.dispose();
|
|
318
|
+
}
|
|
319
|
+
coreHandle.dispose();
|
|
320
|
+
context.dispose();
|
|
321
|
+
if (!isAsyncContext) {
|
|
322
|
+
runtime.dispose();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
//# debugId=0041C068E93313DA64756E2164756E21
|