@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/CHANGELOG.md +13 -0
- package/package.json +52 -7
- package/src/fetch-context.ts +33 -0
- package/src/fs-context.ts +65 -0
- package/src/index.test.ts +473 -0
- package/src/index.ts +209 -0
- package/src/isolate-types.ts +1778 -0
- package/src/mock-fs.ts +246 -0
- package/src/native-input-test.ts +412 -0
- package/src/runtime-context.ts +120 -0
- package/src/server.ts +150 -0
- package/src/typecheck.ts +291 -0
- package/tsconfig.json +8 -0
- package/README.md +0 -45
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";
|