@ricsam/quickjs-runtime 0.2.20 → 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.
@@ -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\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\n */\nexport interface RuntimeHandle {\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): RuntimeHandle {\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"
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 CustomFunction,\n type CustomFunctionDefinition,\n type CustomFunctions,\n type FetchCallback,\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": "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",
8
- "debugId": "3A1DE579F5C2FCF564756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "B678E9E9303FA62664756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/quickjs-runtime",
3
- "version": "0.2.20",
3
+ "version": "0.2.21",
4
4
  "type": "module"
5
5
  }
@@ -0,0 +1,201 @@
1
+ import { type SetupFsOptions } from "@ricsam/quickjs-fs";
2
+ import { type ConsoleEntry, type ConsoleCallbacks } from "@ricsam/quickjs-console";
3
+ import { type RunResults, type TestEvent } from "@ricsam/quickjs-test-environment";
4
+ import { type PlaywrightOptions, type PlaywrightEvent, type BrowserConsoleLogEntry, type NetworkRequestInfo, type NetworkResponseInfo } from "@ricsam/quickjs-playwright";
5
+ export type { ConsoleEntry, ConsoleCallbacks, TestEvent, PlaywrightEvent };
6
+ /**
7
+ * Module loader callback type.
8
+ * Called when the runtime imports a module dynamically.
9
+ * Returns the JavaScript source code for the module.
10
+ */
11
+ export type ModuleLoaderCallback = (moduleName: string) => string | Promise<string>;
12
+ /**
13
+ * A custom function that can be called from within the runtime.
14
+ */
15
+ export type CustomFunction = (...args: unknown[]) => unknown | Promise<unknown>;
16
+ /**
17
+ * Custom function definition with metadata.
18
+ * Requires explicit `async` property to be clear about function behavior.
19
+ */
20
+ export interface CustomFunctionDefinition {
21
+ /** The function implementation */
22
+ fn: CustomFunction;
23
+ /** Whether the function is async (returns a Promise) */
24
+ async: boolean;
25
+ }
26
+ /**
27
+ * Custom functions to register in the runtime.
28
+ * Each function must be defined with explicit async property.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * customFunctions: {
33
+ * // Async function
34
+ * hashPassword: {
35
+ * fn: async (password) => bcrypt.hash(password, 10),
36
+ * async: true,
37
+ * },
38
+ * // Sync function
39
+ * getConfig: {
40
+ * fn: () => ({ environment: "production" }),
41
+ * async: false,
42
+ * },
43
+ * }
44
+ * ```
45
+ */
46
+ export type CustomFunctions = Record<string, CustomFunctionDefinition>;
47
+ /**
48
+ * Fetch callback type.
49
+ */
50
+ export type FetchCallback = (request: Request) => Response | Promise<Response>;
51
+ /**
52
+ * Test environment options.
53
+ */
54
+ export interface TestEnvironmentOptions {
55
+ /** Event callback for test events */
56
+ onEvent?: (event: TestEvent) => void;
57
+ /** Default test timeout in milliseconds */
58
+ testTimeout?: number;
59
+ }
60
+ /**
61
+ * Options for creating a runtime.
62
+ */
63
+ export interface RuntimeOptions {
64
+ /** Memory limit in bytes (optional) */
65
+ memoryLimit?: number;
66
+ /** Console callback handlers */
67
+ console?: ConsoleCallbacks;
68
+ /** Fetch callback handler */
69
+ fetch?: FetchCallback;
70
+ /** File system options */
71
+ fs?: SetupFsOptions;
72
+ /** Module loader callback for resolving dynamic imports */
73
+ moduleLoader?: ModuleLoaderCallback;
74
+ /** Custom functions callable from within the runtime */
75
+ customFunctions?: CustomFunctions;
76
+ /** Current working directory for path operations. Defaults to "/" */
77
+ cwd?: string;
78
+ /** Test environment options. Set to true for defaults, or provide options. */
79
+ testEnvironment?: boolean | TestEnvironmentOptions;
80
+ /** Playwright options for browser automation */
81
+ playwright?: PlaywrightOptions;
82
+ }
83
+ /**
84
+ * Runtime fetch handle - provides access to fetch/serve operations.
85
+ */
86
+ export interface RuntimeFetchHandle {
87
+ /** Dispatch HTTP request to serve() handler */
88
+ dispatchRequest(request: Request, options?: {
89
+ timeout?: number;
90
+ }): Promise<Response>;
91
+ /** Check if serve() has been called */
92
+ hasServeHandler(): boolean;
93
+ /** Check if there are active WebSocket connections */
94
+ hasActiveConnections(): boolean;
95
+ }
96
+ /**
97
+ * Runtime timers handle - provides access to timer operations.
98
+ */
99
+ export interface RuntimeTimersHandle {
100
+ /** Clear all pending timers */
101
+ clearAll(): void;
102
+ }
103
+ /**
104
+ * Runtime console handle - provides access to console state.
105
+ */
106
+ export interface RuntimeConsoleHandle {
107
+ /** Reset all console state (timers, counters, group depth) */
108
+ reset(): void;
109
+ /** Get console.time() timers */
110
+ getTimers(): Map<string, number>;
111
+ /** Get console.count() counters */
112
+ getCounters(): Map<string, number>;
113
+ /** Get current console.group() nesting depth */
114
+ getGroupDepth(): number;
115
+ }
116
+ /**
117
+ * Runtime test environment handle - provides access to test operations.
118
+ */
119
+ export interface RuntimeTestEnvironmentHandle {
120
+ /** Run all registered tests */
121
+ runTests(timeout?: number): Promise<RunResults>;
122
+ /** Check if any tests have been registered */
123
+ hasTests(): boolean;
124
+ /** Get the number of registered tests */
125
+ getTestCount(): number;
126
+ /** Reset test state (clear registered tests) */
127
+ reset(): void;
128
+ }
129
+ /**
130
+ * Collected playwright data.
131
+ */
132
+ export interface CollectedData {
133
+ browserConsoleLogs: BrowserConsoleLogEntry[];
134
+ networkRequests: NetworkRequestInfo[];
135
+ networkResponses: NetworkResponseInfo[];
136
+ }
137
+ /**
138
+ * Runtime playwright handle - provides access to playwright data.
139
+ */
140
+ export interface RuntimePlaywrightHandle {
141
+ /** Get collected browser logs and network data */
142
+ getCollectedData(): CollectedData;
143
+ /** Clear all collected data */
144
+ clearCollectedData(): void;
145
+ }
146
+ /**
147
+ * Runtime handle - the main interface for interacting with the runtime.
148
+ */
149
+ export interface RuntimeHandle {
150
+ /** Unique runtime identifier */
151
+ readonly id: string;
152
+ /** Execute code as ES module (supports top-level await) */
153
+ eval(code: string, filename?: string): Promise<void>;
154
+ /** Dispose all resources */
155
+ dispose(): Promise<void>;
156
+ /** Fetch handle - access to fetch/serve operations */
157
+ readonly fetch: RuntimeFetchHandle;
158
+ /** Timers handle - access to timer operations */
159
+ readonly timers: RuntimeTimersHandle;
160
+ /** Console handle - access to console state */
161
+ readonly console: RuntimeConsoleHandle;
162
+ /** Test environment handle - access to test operations */
163
+ readonly testEnvironment: RuntimeTestEnvironmentHandle;
164
+ /** Playwright handle - access to browser automation data */
165
+ readonly playwright: RuntimePlaywrightHandle;
166
+ }
167
+ /**
168
+ * Create a fully configured QuickJS runtime
169
+ *
170
+ * Sets up all WHATWG APIs: fetch, fs, console, crypto, encoding, timers.
171
+ * Supports ES modules with custom module loader and custom host functions.
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * const runtime = await createRuntime({
176
+ * console: { onEntry: (e) => console.log("[sandbox]", e) },
177
+ * fetch: async (request) => fetch(request),
178
+ * moduleLoader: async (name) => {
179
+ * if (name === "@/utils") {
180
+ * return `export const add = (a, b) => a + b;`;
181
+ * }
182
+ * throw new Error(`Unknown module: ${name}`);
183
+ * },
184
+ * customFunctions: {
185
+ * hashPassword: {
186
+ * fn: async (pw) => Bun.password.hash(pw),
187
+ * async: true,
188
+ * },
189
+ * },
190
+ * });
191
+ *
192
+ * await runtime.eval(`
193
+ * import { add } from "@/utils";
194
+ * const hash = await hashPassword("secret");
195
+ * console.log(add(1, 2), hash);
196
+ * `);
197
+ *
198
+ * await runtime.dispose();
199
+ * ```
200
+ */
201
+ export declare function createRuntime(options?: RuntimeOptions): Promise<RuntimeHandle>;
@@ -5,6 +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 CustomFunction, type CustomFunctionDefinition, type CustomFunctions, type FetchCallback, } from "./create-runtime.ts";
8
9
  import type { QuickJSContext } from "quickjs-emscripten";
9
10
  import { type StateMap, type CoreHandle } from "@ricsam/quickjs-core";
10
11
  import { type SetupFetchOptions, type FetchHandle } from "@ricsam/quickjs-fetch";
@@ -56,9 +57,9 @@ export interface SetupRuntimeOptions {
56
57
  crypto?: SetupCryptoOptions | boolean;
57
58
  }
58
59
  /**
59
- * Handle returned from setupRuntime
60
+ * Handle returned from setupRuntime (low-level API)
60
61
  */
61
- export interface RuntimeHandle {
62
+ export interface SetupRuntimeHandle {
62
63
  /** Core handle (always present) */
63
64
  core: CoreHandle;
64
65
  /** Fetch handle (if enabled) */
@@ -104,4 +105,4 @@ export interface RuntimeHandle {
104
105
  *
105
106
  * handle.dispose();
106
107
  */
107
- export declare function setupRuntime(context: QuickJSContext, options?: SetupRuntimeOptions): RuntimeHandle;
108
+ export declare function setupRuntime(context: QuickJSContext, options?: SetupRuntimeOptions): SetupRuntimeHandle;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/quickjs-runtime",
3
- "version": "0.2.20",
3
+ "version": "0.2.21",
4
4
  "main": "./dist/cjs/index.cjs",
5
5
  "types": "./dist/types/index.d.ts",
6
6
  "exports": {
@@ -17,12 +17,14 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@ricsam/quickjs-core": "^0.2.16",
20
- "@ricsam/quickjs-console": "^0.2.16",
20
+ "@ricsam/quickjs-console": "^0.2.17",
21
21
  "@ricsam/quickjs-crypto": "^0.2.16",
22
22
  "@ricsam/quickjs-encoding": "^0.2.16",
23
- "@ricsam/quickjs-fetch": "^0.2.18",
23
+ "@ricsam/quickjs-fetch": "^0.2.19",
24
24
  "@ricsam/quickjs-fs": "^0.2.17",
25
25
  "@ricsam/quickjs-timers": "^0.2.16",
26
+ "@ricsam/quickjs-test-environment": "^0.2.17",
27
+ "@ricsam/quickjs-playwright": "^0.2.19",
26
28
  "quickjs-emscripten": "^0.31.0"
27
29
  },
28
30
  "peerDependencies": {