@ricsam/isolate-test-utils 0.0.1 → 0.1.1

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/src/index.ts ADDED
@@ -0,0 +1,209 @@
1
+ import type ivm from "isolated-vm";
2
+
3
+ // ============================================================================
4
+ // Types
5
+ // ============================================================================
6
+
7
+ export interface TestContext {
8
+ isolate: ivm.Isolate;
9
+ context: ivm.Context;
10
+ dispose(): void;
11
+ }
12
+
13
+ export interface TestResult<T> {
14
+ result: T;
15
+ logs: Array<{ level: string; args: unknown[] }>;
16
+ }
17
+
18
+ // ============================================================================
19
+ // Context Creation
20
+ // ============================================================================
21
+
22
+ /**
23
+ * Create a basic test context for isolated-vm tests.
24
+ * This creates a bare context without any APIs set up.
25
+ */
26
+ export async function createTestContext(): Promise<TestContext> {
27
+ const ivm = await import("isolated-vm");
28
+ const isolate = new ivm.default.Isolate();
29
+ const context = await isolate.createContext();
30
+
31
+ return {
32
+ isolate,
33
+ context,
34
+ dispose() {
35
+ context.release();
36
+ isolate.dispose();
37
+ },
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Create a test context with core APIs set up (Blob, File, URL, streams, etc.)
43
+ */
44
+ export async function createCoreTestContext(): Promise<TestContext> {
45
+ const ivm = await import("isolated-vm");
46
+ const { setupCore } = await import("@ricsam/isolate-core");
47
+
48
+ const isolate = new ivm.default.Isolate();
49
+ const context = await isolate.createContext();
50
+ const coreHandle = await setupCore(context);
51
+
52
+ return {
53
+ isolate,
54
+ context,
55
+ dispose() {
56
+ coreHandle.dispose();
57
+ context.release();
58
+ isolate.dispose();
59
+ },
60
+ };
61
+ }
62
+
63
+ // ============================================================================
64
+ // Code Evaluation Helpers
65
+ // ============================================================================
66
+
67
+ /**
68
+ * Synchronously evaluate code and return typed result.
69
+ * Use this for simple expressions that don't involve promises.
70
+ *
71
+ * @example
72
+ * const result = evalCode<number>(ctx.context, "1 + 1");
73
+ * // result === 2
74
+ */
75
+ export function evalCode<T = unknown>(context: ivm.Context, code: string): T {
76
+ return context.evalSync(code) as T;
77
+ }
78
+
79
+ /**
80
+ * Asynchronously evaluate code that may return promises.
81
+ * Automatically wraps code to handle promise resolution.
82
+ *
83
+ * @example
84
+ * const result = await evalCodeAsync<string>(ctx.context, `
85
+ * (async () => {
86
+ * return "hello";
87
+ * })()
88
+ * `);
89
+ */
90
+ export async function evalCodeAsync<T = unknown>(
91
+ context: ivm.Context,
92
+ code: string
93
+ ): Promise<T> {
94
+ return (await context.eval(code, { promise: true })) as T;
95
+ }
96
+
97
+ /**
98
+ * Evaluate code and return the result as JSON (for complex objects).
99
+ * Useful when you need to extract structured data from the isolate.
100
+ *
101
+ * @example
102
+ * const data = evalCodeJson<{ name: string }>(ctx.context, `
103
+ * JSON.stringify({ name: "test" })
104
+ * `);
105
+ */
106
+ export function evalCodeJson<T = unknown>(context: ivm.Context, code: string): T {
107
+ const jsonString = context.evalSync(code) as string;
108
+ return JSON.parse(jsonString) as T;
109
+ }
110
+
111
+ /**
112
+ * Evaluate async code and return the result as JSON (for complex objects).
113
+ *
114
+ * @example
115
+ * const data = await evalCodeJsonAsync<{ status: number }>(ctx.context, `
116
+ * (async () => {
117
+ * const response = await fetch("...");
118
+ * return JSON.stringify({ status: response.status });
119
+ * })()
120
+ * `);
121
+ */
122
+ export async function evalCodeJsonAsync<T = unknown>(
123
+ context: ivm.Context,
124
+ code: string
125
+ ): Promise<T> {
126
+ const jsonString = (await context.eval(code, { promise: true })) as string;
127
+ return JSON.parse(jsonString) as T;
128
+ }
129
+
130
+ /**
131
+ * Inject values into the isolate's global scope before running code.
132
+ *
133
+ * @example
134
+ * await injectGlobals(ctx.context, {
135
+ * testInput: "hello",
136
+ * testConfig: { debug: true }
137
+ * });
138
+ * const result = evalCode<string>(ctx.context, "testInput");
139
+ */
140
+ export async function injectGlobals(
141
+ context: ivm.Context,
142
+ values: Record<string, unknown>
143
+ ): Promise<void> {
144
+ const global = context.global;
145
+
146
+ for (const [key, value] of Object.entries(values)) {
147
+ if (typeof value === "function") {
148
+ const ivm = await import("isolated-vm");
149
+ global.setSync(key, new ivm.default.Callback(value as (...args: unknown[]) => unknown));
150
+ } else if (typeof value === "object" && value !== null) {
151
+ // For objects, serialize as JSON and inject
152
+ context.evalSync(`globalThis.${key} = ${JSON.stringify(value)}`);
153
+ } else {
154
+ // For primitives, set directly
155
+ global.setSync(key, value);
156
+ }
157
+ }
158
+ }
159
+
160
+ // ============================================================================
161
+ // Exports from other modules
162
+ // ============================================================================
163
+
164
+ export { MockFileSystem } from "./mock-fs.ts";
165
+ export { createFsTestContext } from "./fs-context.ts";
166
+ export type { FsTestContext } from "./fs-context.ts";
167
+ export { createRuntimeTestContext } from "./runtime-context.ts";
168
+ export type { RuntimeTestContext } from "./runtime-context.ts";
169
+ export { startIntegrationServer } from "./server.ts";
170
+ export type { IntegrationServer } from "./server.ts";
171
+ export { runTestCode } from "./native-input-test.ts";
172
+ export type { TestRunner, TestRuntime } from "./native-input-test.ts";
173
+ export { createFetchTestContext } from "./fetch-context.ts";
174
+ export type { FetchTestContext } from "./fetch-context.ts";
175
+
176
+ // Re-export useful types
177
+ export type { FileSystemHandler } from "@ricsam/isolate-fs";
178
+
179
+ // ============================================================================
180
+ // Type Checking Utilities
181
+ // ============================================================================
182
+
183
+ export {
184
+ typecheckIsolateCode,
185
+ formatTypecheckErrors,
186
+ type TypecheckResult,
187
+ type TypecheckError,
188
+ type TypecheckOptions,
189
+ type LibraryTypes,
190
+ type LibraryTypeFile,
191
+ } from "./typecheck.ts";
192
+
193
+ // ============================================================================
194
+ // Type Definitions
195
+ // ============================================================================
196
+
197
+ export {
198
+ CORE_TYPES,
199
+ CONSOLE_TYPES,
200
+ CRYPTO_TYPES,
201
+ ENCODING_TYPES,
202
+ FETCH_TYPES,
203
+ FS_TYPES,
204
+ PATH_TYPES,
205
+ TEST_ENV_TYPES,
206
+ TIMERS_TYPES,
207
+ TYPE_DEFINITIONS,
208
+ type TypeDefinitionKey,
209
+ } from "./isolate-types.ts";