@ricsam/quickjs-test-utils 1.0.20 → 1.0.22
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 +8 -6
- package/dist/cjs/integration-server.cjs +2 -2
- package/dist/cjs/integration-server.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/runtime-context.cjs.map +1 -1
- package/dist/mjs/integration-server.mjs +2 -2
- package/dist/mjs/integration-server.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/runtime-context.mjs.map +1 -1
- package/dist/types/integration-server.d.ts +3 -3
- package/dist/types/runtime-context.d.ts +2 -2
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Testing utilities including type checking for QuickJS user code.
|
|
4
4
|
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
5
7
|
```bash
|
|
6
8
|
bun add @ricsam/quickjs-test-utils
|
|
7
9
|
```
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## Type Checking QuickJS Code
|
|
10
12
|
|
|
11
13
|
Validate TypeScript/JavaScript code that will run inside QuickJS before execution using `ts-morph`:
|
|
12
14
|
|
|
@@ -41,14 +43,14 @@ if (!result.success) {
|
|
|
41
43
|
}
|
|
42
44
|
```
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
## Options
|
|
45
47
|
|
|
46
48
|
| Option | Description |
|
|
47
49
|
|--------|-------------|
|
|
48
50
|
| `include` | Which package types to include: `"core"`, `"fetch"`, `"fs"` (default: all) |
|
|
49
51
|
| `compilerOptions` | Additional TypeScript compiler options |
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
## Using with Tests
|
|
52
54
|
|
|
53
55
|
```typescript
|
|
54
56
|
import { describe, expect, test } from "bun:test";
|
|
@@ -64,7 +66,7 @@ describe("QuickJS code validation", () => {
|
|
|
64
66
|
});
|
|
65
67
|
```
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
## Type Definition Strings
|
|
68
70
|
|
|
69
71
|
The type definitions are also exported as strings for custom use cases:
|
|
70
72
|
|
|
@@ -81,7 +83,7 @@ import {
|
|
|
81
83
|
project.createSourceFile("quickjs-globals.d.ts", FETCH_TYPES);
|
|
82
84
|
```
|
|
83
85
|
|
|
84
|
-
|
|
86
|
+
## Type Definition Files
|
|
85
87
|
|
|
86
88
|
Each package also exports `.d.ts` files for use with `tsconfig.json`:
|
|
87
89
|
|
|
@@ -98,4 +100,4 @@ Each package also exports `.d.ts` files for use with `tsconfig.json`:
|
|
|
98
100
|
{ "path": "./node_modules/@ricsam/quickjs-crypto/src/quickjs.d.ts" }
|
|
99
101
|
]
|
|
100
102
|
}
|
|
101
|
-
```
|
|
103
|
+
```
|
|
@@ -47,7 +47,7 @@ async function startIntegrationServer(quickJSCode, options) {
|
|
|
47
47
|
coreHandle,
|
|
48
48
|
onFetch: options?.onFetch
|
|
49
49
|
});
|
|
50
|
-
const consoleHandle = options?.
|
|
50
|
+
const consoleHandle = options?.console ? import_quickjs_console.setupConsole(context, { ...options.console, coreHandle }) : undefined;
|
|
51
51
|
const fsHandle = options?.fs ? import_quickjs_fs.setupFs(context, { coreHandle, getDirectory: options.fs.getDirectory }) : undefined;
|
|
52
52
|
const wsConnections = new Map;
|
|
53
53
|
fetchHandle.onWebSocketCommand((cmd) => {
|
|
@@ -141,4 +141,4 @@ async function startIntegrationServer(quickJSCode, options) {
|
|
|
141
141
|
}
|
|
142
142
|
})
|
|
143
143
|
|
|
144
|
-
//# debugId=
|
|
144
|
+
//# debugId=2D7D31091F2D0F5564756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/integration-server.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { getQuickJS, type QuickJSContext, type QuickJSRuntime } from \"quickjs-emscripten\";\nimport { setupFetch, type FetchHandle, type WebSocketCommand, type SetupFetchOptions } from \"@ricsam/quickjs-fetch\";\nimport { setupCore, type CoreHandle } from \"@ricsam/quickjs-core\";\nimport { setupFs, type FsHandle, type HostDirectoryHandle } from \"@ricsam/quickjs-fs\";\nimport type { Server, ServerWebSocket } from \"bun\";\nimport { setupConsole, type
|
|
5
|
+
"import { getQuickJS, type QuickJSContext, type QuickJSRuntime } from \"quickjs-emscripten\";\nimport { setupFetch, type FetchHandle, type WebSocketCommand, type SetupFetchOptions } from \"@ricsam/quickjs-fetch\";\nimport { setupCore, type CoreHandle } from \"@ricsam/quickjs-core\";\nimport { setupFs, type FsHandle, type HostDirectoryHandle } from \"@ricsam/quickjs-fs\";\nimport type { Server, ServerWebSocket } from \"bun\";\nimport { setupConsole, type ConsoleCallbacks, type ConsoleHandle } from \"@ricsam/quickjs-console\";\n\ninterface WsData {\n connectionId: string;\n}\n\nexport interface IntegrationTestServer {\n baseURL: string;\n wsURL: string;\n port: number;\n fetchHandle: FetchHandle;\n context: QuickJSContext;\n runtime: QuickJSRuntime;\n coreHandle: CoreHandle;\n consoleHandle?: ConsoleHandle;\n fsHandle?: FsHandle;\n stop(): Promise<void>;\n}\n\nexport interface StartIntegrationServerOptions {\n /** Handler for outbound fetch() calls from QuickJS */\n onFetch?: SetupFetchOptions[\"onFetch\"];\n /** Console callbacks for QuickJS */\n console?: ConsoleCallbacks;\n /** File system configuration for QuickJS */\n fs?: {\n /** Returns a directory handle for the given path */\n getDirectory: (path: string) => Promise<HostDirectoryHandle>;\n };\n}\n\n/**\n * Start a real HTTP server that dispatches requests to a QuickJS serve() handler.\n *\n * @param quickJSCode - JavaScript code to run in QuickJS, should call serve()\n * @param options - Optional configuration\n * @returns Server info including baseURL, wsURL, and stop function\n *\n * @example\n * const server = await startIntegrationServer(`\n * serve({\n * fetch(request) {\n * return new Response(\"Hello!\");\n * }\n * });\n * `);\n *\n * const response = await fetch(\\`\\${server.baseURL}/test\\`);\n * expect(await response.text()).toBe(\"Hello!\");\n *\n * await server.stop();\n */\nexport async function startIntegrationServer(\n quickJSCode: string,\n options?: StartIntegrationServerOptions\n): Promise<IntegrationTestServer> {\n const QuickJS = await getQuickJS();\n const runtime = QuickJS.newRuntime();\n const context = runtime.newContext();\n\n // Setup core and fetch APIs\n const coreHandle = setupCore(context);\n const fetchHandle = setupFetch(context, {\n coreHandle,\n onFetch: options?.onFetch,\n });\n\n // Setup console if callbacks provided\n const consoleHandle = options?.console\n ? setupConsole(context, { ...options.console, coreHandle })\n : undefined;\n\n // Setup fs if configured\n const fsHandle = options?.fs\n ? setupFs(context, { coreHandle, getDirectory: options.fs.getDirectory })\n : undefined;\n\n // Track WebSocket connections\n const wsConnections = new Map<string, ServerWebSocket<WsData>>();\n\n // Handle outgoing WebSocket commands from QuickJS\n fetchHandle.onWebSocketCommand((cmd: WebSocketCommand) => {\n const ws = wsConnections.get(cmd.connectionId);\n if (!ws) return;\n\n if (cmd.type === \"message\") {\n ws.send(cmd.data);\n } else if (cmd.type === \"close\") {\n ws.close(cmd.code, cmd.reason);\n }\n });\n\n // Run the user's QuickJS code\n const result = context.evalCode(quickJSCode);\n if (result.error) {\n const error = context.dump(result.error);\n result.error.dispose();\n fetchHandle.dispose();\n coreHandle.dispose();\n context.dispose();\n runtime.dispose();\n throw new Error(`Failed to evaluate QuickJS code: ${JSON.stringify(error)}`);\n }\n result.value.dispose();\n\n // Start Bun server with port 0 for dynamic assignment\n const server: Server<WsData> = Bun.serve<WsData>({\n port: 0,\n async fetch(req, server) {\n try {\n // Dispatch to QuickJS handler\n const response = await fetchHandle.dispatchRequest(req);\n\n // Check if QuickJS requested a WebSocket upgrade\n const upgrade = fetchHandle.getUpgradeRequest();\n if (upgrade?.requested) {\n // The connectionId is generated by QuickJS in server.upgrade()\n // User data is stored in QuickJS registry - we just pass the connectionId\n const success = server.upgrade(req, {\n data: { connectionId: upgrade.connectionId },\n });\n if (success) {\n return undefined;\n }\n return new Response(\"WebSocket upgrade failed\", { status: 500 });\n }\n\n return response;\n } catch (error) {\n console.error(\"Integration test request failed:\", error);\n return new Response(\"Internal Server Error\", { status: 500 });\n }\n },\n websocket: {\n open(ws) {\n const { connectionId } = ws.data;\n wsConnections.set(connectionId, ws);\n // User data is looked up from QuickJS registry using connectionId\n fetchHandle.dispatchWebSocketOpen(connectionId);\n },\n message(ws, message) {\n const msg = typeof message === \"string\" ? message : message.buffer;\n fetchHandle.dispatchWebSocketMessage(ws.data.connectionId, msg as string | ArrayBuffer);\n },\n close(ws, code, reason) {\n fetchHandle.dispatchWebSocketClose(ws.data.connectionId, code, reason);\n wsConnections.delete(ws.data.connectionId);\n },\n },\n });\n\n const port = server.port;\n if (port === undefined) {\n throw new Error(\"Server failed to get port\");\n }\n const baseURL = `http://localhost:${port}`;\n const wsURL = `ws://localhost:${port}`;\n\n return {\n baseURL,\n wsURL,\n port,\n fetchHandle,\n context,\n runtime,\n coreHandle,\n consoleHandle,\n fsHandle,\n async stop() {\n // Close all WebSocket connections\n for (const ws of wsConnections.values()) {\n ws.close(1000, \"Server stopping\");\n }\n wsConnections.clear();\n\n // Stop the server\n server.stop(true);\n\n // Dispose QuickJS resources\n fetchHandle.dispose();\n fsHandle?.dispose();\n consoleHandle?.dispose();\n coreHandle.dispose();\n context.dispose();\n runtime.dispose();\n },\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAqE,IAArE;AAC4F,IAA5F;AAC2C,IAA3C;AACiE,IAAjE;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "2D7D31091F2D0F5564756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/runtime-context.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { getQuickJS, type QuickJSContext, type QuickJSRuntime } from \"quickjs-emscripten\";\nimport { clearAllInstanceState } from \"@ricsam/quickjs-core\";\nimport {\n setupRuntime,\n type
|
|
5
|
+
"import { getQuickJS, type QuickJSContext, type QuickJSRuntime } from \"quickjs-emscripten\";\nimport { clearAllInstanceState } from \"@ricsam/quickjs-core\";\nimport {\n setupRuntime,\n type SetupRuntimeHandle,\n type SetupRuntimeOptions,\n} from \"@ricsam/quickjs-runtime\";\n\nexport interface RuntimeTestContext {\n runtime: QuickJSRuntime;\n context: QuickJSContext;\n runtimeHandle: SetupRuntimeHandle;\n}\n\nexport async function createRuntimeTestContext(\n options?: SetupRuntimeOptions\n): Promise<RuntimeTestContext> {\n const QuickJS = await getQuickJS();\n const runtime = QuickJS.newRuntime();\n const context = runtime.newContext();\n clearAllInstanceState();\n\n // setupRuntime internally creates the coreHandle\n const runtimeHandle = setupRuntime(context, options);\n\n return { runtime, context, runtimeHandle };\n}\n\nexport function disposeRuntimeTestContext(ctx: RuntimeTestContext): void {\n ctx.runtimeHandle.dispose();\n ctx.context.dispose();\n ctx.runtime.dispose();\n}\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAqE,IAArE;AACsC,IAAtC;AAKO,IAJP;AAYA,eAAsB,wBAAwB,CAC5C,SAC6B;AAAA,EAC7B,MAAM,UAAU,MAAM,qCAAW;AAAA,EACjC,MAAM,UAAU,QAAQ,WAAW;AAAA,EACnC,MAAM,UAAU,QAAQ,WAAW;AAAA,EACnC,0CAAsB;AAAA,EAGtB,MAAM,gBAAgB,oCAAa,SAAS,OAAO;AAAA,EAEnD,OAAO,EAAE,SAAS,SAAS,cAAc;AAAA;AAGpC,SAAS,yBAAyB,CAAC,KAA+B;AAAA,EACvE,IAAI,cAAc,QAAQ;AAAA,EAC1B,IAAI,QAAQ,QAAQ;AAAA,EACpB,IAAI,QAAQ,QAAQ;AAAA;",
|
|
8
8
|
"debugId": "D69AAF9C4A61412164756E2164756E21",
|
|
@@ -14,7 +14,7 @@ async function startIntegrationServer(quickJSCode, options) {
|
|
|
14
14
|
coreHandle,
|
|
15
15
|
onFetch: options?.onFetch
|
|
16
16
|
});
|
|
17
|
-
const consoleHandle = options?.
|
|
17
|
+
const consoleHandle = options?.console ? setupConsole(context, { ...options.console, coreHandle }) : undefined;
|
|
18
18
|
const fsHandle = options?.fs ? setupFs(context, { coreHandle, getDirectory: options.fs.getDirectory }) : undefined;
|
|
19
19
|
const wsConnections = new Map;
|
|
20
20
|
fetchHandle.onWebSocketCommand((cmd) => {
|
|
@@ -110,4 +110,4 @@ export {
|
|
|
110
110
|
startIntegrationServer
|
|
111
111
|
};
|
|
112
112
|
|
|
113
|
-
//# debugId=
|
|
113
|
+
//# debugId=A82C3E3FCA6F745D64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/integration-server.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { getQuickJS, type QuickJSContext, type QuickJSRuntime } from \"quickjs-emscripten\";\nimport { setupFetch, type FetchHandle, type WebSocketCommand, type SetupFetchOptions } from \"@ricsam/quickjs-fetch\";\nimport { setupCore, type CoreHandle } from \"@ricsam/quickjs-core\";\nimport { setupFs, type FsHandle, type HostDirectoryHandle } from \"@ricsam/quickjs-fs\";\nimport type { Server, ServerWebSocket } from \"bun\";\nimport { setupConsole, type
|
|
5
|
+
"import { getQuickJS, type QuickJSContext, type QuickJSRuntime } from \"quickjs-emscripten\";\nimport { setupFetch, type FetchHandle, type WebSocketCommand, type SetupFetchOptions } from \"@ricsam/quickjs-fetch\";\nimport { setupCore, type CoreHandle } from \"@ricsam/quickjs-core\";\nimport { setupFs, type FsHandle, type HostDirectoryHandle } from \"@ricsam/quickjs-fs\";\nimport type { Server, ServerWebSocket } from \"bun\";\nimport { setupConsole, type ConsoleCallbacks, type ConsoleHandle } from \"@ricsam/quickjs-console\";\n\ninterface WsData {\n connectionId: string;\n}\n\nexport interface IntegrationTestServer {\n baseURL: string;\n wsURL: string;\n port: number;\n fetchHandle: FetchHandle;\n context: QuickJSContext;\n runtime: QuickJSRuntime;\n coreHandle: CoreHandle;\n consoleHandle?: ConsoleHandle;\n fsHandle?: FsHandle;\n stop(): Promise<void>;\n}\n\nexport interface StartIntegrationServerOptions {\n /** Handler for outbound fetch() calls from QuickJS */\n onFetch?: SetupFetchOptions[\"onFetch\"];\n /** Console callbacks for QuickJS */\n console?: ConsoleCallbacks;\n /** File system configuration for QuickJS */\n fs?: {\n /** Returns a directory handle for the given path */\n getDirectory: (path: string) => Promise<HostDirectoryHandle>;\n };\n}\n\n/**\n * Start a real HTTP server that dispatches requests to a QuickJS serve() handler.\n *\n * @param quickJSCode - JavaScript code to run in QuickJS, should call serve()\n * @param options - Optional configuration\n * @returns Server info including baseURL, wsURL, and stop function\n *\n * @example\n * const server = await startIntegrationServer(`\n * serve({\n * fetch(request) {\n * return new Response(\"Hello!\");\n * }\n * });\n * `);\n *\n * const response = await fetch(\\`\\${server.baseURL}/test\\`);\n * expect(await response.text()).toBe(\"Hello!\");\n *\n * await server.stop();\n */\nexport async function startIntegrationServer(\n quickJSCode: string,\n options?: StartIntegrationServerOptions\n): Promise<IntegrationTestServer> {\n const QuickJS = await getQuickJS();\n const runtime = QuickJS.newRuntime();\n const context = runtime.newContext();\n\n // Setup core and fetch APIs\n const coreHandle = setupCore(context);\n const fetchHandle = setupFetch(context, {\n coreHandle,\n onFetch: options?.onFetch,\n });\n\n // Setup console if callbacks provided\n const consoleHandle = options?.console\n ? setupConsole(context, { ...options.console, coreHandle })\n : undefined;\n\n // Setup fs if configured\n const fsHandle = options?.fs\n ? setupFs(context, { coreHandle, getDirectory: options.fs.getDirectory })\n : undefined;\n\n // Track WebSocket connections\n const wsConnections = new Map<string, ServerWebSocket<WsData>>();\n\n // Handle outgoing WebSocket commands from QuickJS\n fetchHandle.onWebSocketCommand((cmd: WebSocketCommand) => {\n const ws = wsConnections.get(cmd.connectionId);\n if (!ws) return;\n\n if (cmd.type === \"message\") {\n ws.send(cmd.data);\n } else if (cmd.type === \"close\") {\n ws.close(cmd.code, cmd.reason);\n }\n });\n\n // Run the user's QuickJS code\n const result = context.evalCode(quickJSCode);\n if (result.error) {\n const error = context.dump(result.error);\n result.error.dispose();\n fetchHandle.dispose();\n coreHandle.dispose();\n context.dispose();\n runtime.dispose();\n throw new Error(`Failed to evaluate QuickJS code: ${JSON.stringify(error)}`);\n }\n result.value.dispose();\n\n // Start Bun server with port 0 for dynamic assignment\n const server: Server<WsData> = Bun.serve<WsData>({\n port: 0,\n async fetch(req, server) {\n try {\n // Dispatch to QuickJS handler\n const response = await fetchHandle.dispatchRequest(req);\n\n // Check if QuickJS requested a WebSocket upgrade\n const upgrade = fetchHandle.getUpgradeRequest();\n if (upgrade?.requested) {\n // The connectionId is generated by QuickJS in server.upgrade()\n // User data is stored in QuickJS registry - we just pass the connectionId\n const success = server.upgrade(req, {\n data: { connectionId: upgrade.connectionId },\n });\n if (success) {\n return undefined;\n }\n return new Response(\"WebSocket upgrade failed\", { status: 500 });\n }\n\n return response;\n } catch (error) {\n console.error(\"Integration test request failed:\", error);\n return new Response(\"Internal Server Error\", { status: 500 });\n }\n },\n websocket: {\n open(ws) {\n const { connectionId } = ws.data;\n wsConnections.set(connectionId, ws);\n // User data is looked up from QuickJS registry using connectionId\n fetchHandle.dispatchWebSocketOpen(connectionId);\n },\n message(ws, message) {\n const msg = typeof message === \"string\" ? message : message.buffer;\n fetchHandle.dispatchWebSocketMessage(ws.data.connectionId, msg as string | ArrayBuffer);\n },\n close(ws, code, reason) {\n fetchHandle.dispatchWebSocketClose(ws.data.connectionId, code, reason);\n wsConnections.delete(ws.data.connectionId);\n },\n },\n });\n\n const port = server.port;\n if (port === undefined) {\n throw new Error(\"Server failed to get port\");\n }\n const baseURL = `http://localhost:${port}`;\n const wsURL = `ws://localhost:${port}`;\n\n return {\n baseURL,\n wsURL,\n port,\n fetchHandle,\n context,\n runtime,\n coreHandle,\n consoleHandle,\n fsHandle,\n async stop() {\n // Close all WebSocket connections\n for (const ws of wsConnections.values()) {\n ws.close(1000, \"Server stopping\");\n }\n wsConnections.clear();\n\n // Stop the server\n server.stop(true);\n\n // Dispose QuickJS resources\n fetchHandle.dispose();\n fsHandle?.dispose();\n consoleHandle?.dispose();\n coreHandle.dispose();\n context.dispose();\n runtime.dispose();\n },\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;AAAA;AACA;AACA;AACA;AAEA;AAoDA,eAAsB,sBAAsB,CAC1C,aACA,SACgC;AAAA,EAChC,MAAM,UAAU,MAAM,WAAW;AAAA,EACjC,MAAM,UAAU,QAAQ,WAAW;AAAA,EACnC,MAAM,UAAU,QAAQ,WAAW;AAAA,EAGnC,MAAM,aAAa,UAAU,OAAO;AAAA,EACpC,MAAM,cAAc,WAAW,SAAS;AAAA,IACtC;AAAA,IACA,SAAS,SAAS;AAAA,EACpB,CAAC;AAAA,EAGD,MAAM,gBAAgB,SAAS,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "A82C3E3FCA6F745D64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/runtime-context.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { getQuickJS, type QuickJSContext, type QuickJSRuntime } from \"quickjs-emscripten\";\nimport { clearAllInstanceState } from \"@ricsam/quickjs-core\";\nimport {\n setupRuntime,\n type
|
|
5
|
+
"import { getQuickJS, type QuickJSContext, type QuickJSRuntime } from \"quickjs-emscripten\";\nimport { clearAllInstanceState } from \"@ricsam/quickjs-core\";\nimport {\n setupRuntime,\n type SetupRuntimeHandle,\n type SetupRuntimeOptions,\n} from \"@ricsam/quickjs-runtime\";\n\nexport interface RuntimeTestContext {\n runtime: QuickJSRuntime;\n context: QuickJSContext;\n runtimeHandle: SetupRuntimeHandle;\n}\n\nexport async function createRuntimeTestContext(\n options?: SetupRuntimeOptions\n): Promise<RuntimeTestContext> {\n const QuickJS = await getQuickJS();\n const runtime = QuickJS.newRuntime();\n const context = runtime.newContext();\n clearAllInstanceState();\n\n // setupRuntime internally creates the coreHandle\n const runtimeHandle = setupRuntime(context, options);\n\n return { runtime, context, runtimeHandle };\n}\n\nexport function disposeRuntimeTestContext(ctx: RuntimeTestContext): void {\n ctx.runtimeHandle.dispose();\n ctx.context.dispose();\n ctx.runtime.dispose();\n}\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": ";;AAAA;AACA;AACA;AAAA;AAAA;AAYA,eAAsB,wBAAwB,CAC5C,SAC6B;AAAA,EAC7B,MAAM,UAAU,MAAM,WAAW;AAAA,EACjC,MAAM,UAAU,QAAQ,WAAW;AAAA,EACnC,MAAM,UAAU,QAAQ,WAAW;AAAA,EACnC,sBAAsB;AAAA,EAGtB,MAAM,gBAAgB,aAAa,SAAS,OAAO;AAAA,EAEnD,OAAO,EAAE,SAAS,SAAS,cAAc;AAAA;AAGpC,SAAS,yBAAyB,CAAC,KAA+B;AAAA,EACvE,IAAI,cAAc,QAAQ;AAAA,EAC1B,IAAI,QAAQ,QAAQ;AAAA,EACpB,IAAI,QAAQ,QAAQ;AAAA;",
|
|
8
8
|
"debugId": "8B023BD3945C89FC64756E2164756E21",
|
|
@@ -2,7 +2,7 @@ import { type QuickJSContext, type QuickJSRuntime } from "quickjs-emscripten";
|
|
|
2
2
|
import { type FetchHandle, type SetupFetchOptions } from "@ricsam/quickjs-fetch";
|
|
3
3
|
import { type CoreHandle } from "@ricsam/quickjs-core";
|
|
4
4
|
import { type FsHandle, type HostDirectoryHandle } from "@ricsam/quickjs-fs";
|
|
5
|
-
import { type
|
|
5
|
+
import { type ConsoleCallbacks, type ConsoleHandle } from "@ricsam/quickjs-console";
|
|
6
6
|
export interface IntegrationTestServer {
|
|
7
7
|
baseURL: string;
|
|
8
8
|
wsURL: string;
|
|
@@ -18,8 +18,8 @@ export interface IntegrationTestServer {
|
|
|
18
18
|
export interface StartIntegrationServerOptions {
|
|
19
19
|
/** Handler for outbound fetch() calls from QuickJS */
|
|
20
20
|
onFetch?: SetupFetchOptions["onFetch"];
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/** Console callbacks for QuickJS */
|
|
22
|
+
console?: ConsoleCallbacks;
|
|
23
23
|
/** File system configuration for QuickJS */
|
|
24
24
|
fs?: {
|
|
25
25
|
/** Returns a directory handle for the given path */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type QuickJSContext, type QuickJSRuntime } from "quickjs-emscripten";
|
|
2
|
-
import { type
|
|
2
|
+
import { type SetupRuntimeHandle, type SetupRuntimeOptions } from "@ricsam/quickjs-runtime";
|
|
3
3
|
export interface RuntimeTestContext {
|
|
4
4
|
runtime: QuickJSRuntime;
|
|
5
5
|
context: QuickJSContext;
|
|
6
|
-
runtimeHandle:
|
|
6
|
+
runtimeHandle: SetupRuntimeHandle;
|
|
7
7
|
}
|
|
8
8
|
export declare function createRuntimeTestContext(options?: SetupRuntimeOptions): Promise<RuntimeTestContext>;
|
|
9
9
|
export declare function disposeRuntimeTestContext(ctx: RuntimeTestContext): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ricsam/quickjs-test-utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"main": "./dist/cjs/index.cjs",
|
|
5
5
|
"types": "./dist/types/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"quickjs-emscripten": "^0.31.0",
|
|
23
|
-
"@ricsam/quickjs-console": "^0.2.
|
|
24
|
-
"@ricsam/quickjs-core": "^0.2.
|
|
25
|
-
"@ricsam/quickjs-fetch": "^0.2.
|
|
26
|
-
"@ricsam/quickjs-fs": "^0.2.
|
|
27
|
-
"@ricsam/quickjs-runtime": "^0.2.
|
|
23
|
+
"@ricsam/quickjs-console": "^0.2.17",
|
|
24
|
+
"@ricsam/quickjs-core": "^0.2.16",
|
|
25
|
+
"@ricsam/quickjs-fetch": "^0.2.19",
|
|
26
|
+
"@ricsam/quickjs-fs": "^0.2.17",
|
|
27
|
+
"@ricsam/quickjs-runtime": "^0.2.21"
|
|
28
28
|
},
|
|
29
29
|
"peerDependenciesMeta": {
|
|
30
30
|
"@ricsam/quickjs-console": {
|