@ricsam/isolate-runtime 0.1.3 → 0.1.7
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 +387 -26
- package/dist/cjs/index.cjs +443 -28
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/internal.cjs +97 -0
- package/dist/cjs/internal.cjs.map +10 -0
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/index.mjs +433 -29
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/internal.mjs +53 -0
- package/dist/mjs/internal.mjs.map +10 -0
- package/dist/mjs/package.json +1 -1
- package/dist/types/index.d.ts +158 -30
- package/dist/types/internal.d.ts +39 -0
- package/package.json +12 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// @bun @bun-cjs
|
|
2
|
+
(function(exports, require, module, __filename, __dirname) {var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
9
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
|
+
for (let key of __getOwnPropNames(mod))
|
|
12
|
+
if (!__hasOwnProp.call(to, key))
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: () => mod[key],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
20
|
+
var __toCommonJS = (from) => {
|
|
21
|
+
var entry = __moduleCache.get(from), desc;
|
|
22
|
+
if (entry)
|
|
23
|
+
return entry;
|
|
24
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
25
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
26
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
27
|
+
get: () => from[key],
|
|
28
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
29
|
+
}));
|
|
30
|
+
__moduleCache.set(from, entry);
|
|
31
|
+
return entry;
|
|
32
|
+
};
|
|
33
|
+
var __export = (target, all) => {
|
|
34
|
+
for (var name in all)
|
|
35
|
+
__defProp(target, name, {
|
|
36
|
+
get: all[name],
|
|
37
|
+
enumerable: true,
|
|
38
|
+
configurable: true,
|
|
39
|
+
set: (newValue) => all[name] = () => newValue
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// packages/runtime/src/internal.ts
|
|
44
|
+
var exports_internal = {};
|
|
45
|
+
__export(exports_internal, {
|
|
46
|
+
createInternalRuntime: () => createInternalRuntime
|
|
47
|
+
});
|
|
48
|
+
module.exports = __toCommonJS(exports_internal);
|
|
49
|
+
var import_isolated_vm = __toESM(require("isolated-vm"));
|
|
50
|
+
var import_isolate_core = require("@ricsam/isolate-core");
|
|
51
|
+
var import_isolate_console = require("@ricsam/isolate-console");
|
|
52
|
+
var import_isolate_encoding = require("@ricsam/isolate-encoding");
|
|
53
|
+
var import_isolate_timers = require("@ricsam/isolate-timers");
|
|
54
|
+
var import_isolate_path = require("@ricsam/isolate-path");
|
|
55
|
+
var import_isolate_crypto = require("@ricsam/isolate-crypto");
|
|
56
|
+
var import_isolate_fetch = require("@ricsam/isolate-fetch");
|
|
57
|
+
var import_isolate_fs = require("@ricsam/isolate-fs");
|
|
58
|
+
async function createInternalRuntime(options) {
|
|
59
|
+
const opts = options ?? {};
|
|
60
|
+
const isolate = new import_isolated_vm.default.Isolate({
|
|
61
|
+
memoryLimit: opts.memoryLimitMB
|
|
62
|
+
});
|
|
63
|
+
const context = await isolate.createContext();
|
|
64
|
+
const handles = {};
|
|
65
|
+
handles.core = await import_isolate_core.setupCore(context);
|
|
66
|
+
handles.console = await import_isolate_console.setupConsole(context, opts.console);
|
|
67
|
+
handles.encoding = await import_isolate_encoding.setupEncoding(context);
|
|
68
|
+
handles.timers = await import_isolate_timers.setupTimers(context);
|
|
69
|
+
handles.path = await import_isolate_path.setupPath(context, { cwd: opts.cwd });
|
|
70
|
+
handles.crypto = await import_isolate_crypto.setupCrypto(context);
|
|
71
|
+
handles.fetch = await import_isolate_fetch.setupFetch(context, opts.fetch);
|
|
72
|
+
if (opts.fs) {
|
|
73
|
+
handles.fs = await import_isolate_fs.setupFs(context, opts.fs);
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
isolate,
|
|
77
|
+
context,
|
|
78
|
+
fetch: handles.fetch,
|
|
79
|
+
timers: handles.timers,
|
|
80
|
+
console: handles.console,
|
|
81
|
+
dispose() {
|
|
82
|
+
handles.fs?.dispose();
|
|
83
|
+
handles.fetch?.dispose();
|
|
84
|
+
handles.crypto?.dispose();
|
|
85
|
+
handles.path?.dispose();
|
|
86
|
+
handles.timers?.dispose();
|
|
87
|
+
handles.encoding?.dispose();
|
|
88
|
+
handles.console?.dispose();
|
|
89
|
+
handles.core?.dispose();
|
|
90
|
+
context.release();
|
|
91
|
+
isolate.dispose();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
//# debugId=D12930C28AC6F96064756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/internal.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Internal APIs for @ricsam/isolate-runtime\n *\n * These are not part of the public API and may change without notice.\n * Only used by @ricsam/isolate-daemon internally.\n */\n\nimport ivm from \"isolated-vm\";\nimport { setupCore } from \"@ricsam/isolate-core\";\nimport { setupConsole } from \"@ricsam/isolate-console\";\nimport { setupEncoding } from \"@ricsam/isolate-encoding\";\nimport { setupTimers } from \"@ricsam/isolate-timers\";\nimport { setupPath } from \"@ricsam/isolate-path\";\nimport { setupCrypto } from \"@ricsam/isolate-crypto\";\nimport { setupFetch } from \"@ricsam/isolate-fetch\";\nimport { setupFs } from \"@ricsam/isolate-fs\";\n\nimport type { ConsoleOptions, ConsoleHandle } from \"@ricsam/isolate-console\";\nimport type { FetchOptions, FetchHandle } from \"@ricsam/isolate-fetch\";\nimport type { FsOptions, FsHandle } from \"@ricsam/isolate-fs\";\nimport type { CoreHandle } from \"@ricsam/isolate-core\";\nimport type { EncodingHandle } from \"@ricsam/isolate-encoding\";\nimport type { TimersHandle } from \"@ricsam/isolate-timers\";\nimport type { PathHandle } from \"@ricsam/isolate-path\";\nimport type { CryptoHandle } from \"@ricsam/isolate-crypto\";\n\n/**\n * @internal Options for creating a legacy runtime.\n */\nexport interface InternalRuntimeOptions {\n memoryLimitMB?: number;\n console?: ConsoleOptions;\n fetch?: FetchOptions;\n fs?: FsOptions;\n /** Current working directory for path.resolve(). Defaults to \"/\" */\n cwd?: string;\n}\n\n/**\n * @internal Runtime handle with direct isolate/context access.\n * Used by isolate-daemon for low-level operations.\n */\nexport interface InternalRuntimeHandle {\n readonly isolate: ivm.Isolate;\n readonly context: ivm.Context;\n readonly fetch: FetchHandle;\n readonly timers: TimersHandle;\n readonly console: ConsoleHandle;\n dispose(): void;\n}\n\n/**\n * @internal Create a runtime with direct access to isolate and context.\n * This is for internal use by @ricsam/isolate-daemon only.\n */\nexport async function createInternalRuntime(\n options?: InternalRuntimeOptions\n): Promise<InternalRuntimeHandle> {\n const opts = options ?? {};\n\n const isolate = new ivm.Isolate({\n memoryLimit: opts.memoryLimitMB,\n });\n\n const context = await isolate.createContext();\n\n // Setup all APIs\n const handles: {\n core?: CoreHandle;\n console?: ConsoleHandle;\n encoding?: EncodingHandle;\n timers?: TimersHandle;\n path?: PathHandle;\n crypto?: CryptoHandle;\n fetch?: FetchHandle;\n fs?: FsHandle;\n } = {};\n\n handles.core = await setupCore(context);\n handles.console = await setupConsole(context, opts.console);\n handles.encoding = await setupEncoding(context);\n handles.timers = await setupTimers(context);\n handles.path = await setupPath(context, { cwd: opts.cwd });\n handles.crypto = await setupCrypto(context);\n handles.fetch = await setupFetch(context, opts.fetch);\n\n if (opts.fs) {\n handles.fs = await setupFs(context, opts.fs);\n }\n\n return {\n isolate,\n context,\n fetch: handles.fetch,\n timers: handles.timers!,\n console: handles.console!,\n dispose() {\n handles.fs?.dispose();\n handles.fetch?.dispose();\n handles.crypto?.dispose();\n handles.path?.dispose();\n handles.timers?.dispose();\n handles.encoding?.dispose();\n handles.console?.dispose();\n handles.core?.dispose();\n context.release();\n isolate.dispose();\n },\n };\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOgB,IAAhB;AAC0B,IAA1B;AAC6B,IAA7B;AAC8B,IAA9B;AAC4B,IAA5B;AAC0B,IAA1B;AAC4B,IAA5B;AAC2B,IAA3B;AACwB,IAAxB;AAwCA,eAAsB,qBAAqB,CACzC,SACgC;AAAA,EAChC,MAAM,OAAO,WAAW,CAAC;AAAA,EAEzB,MAAM,UAAU,IAAI,2BAAI,QAAQ;AAAA,IAC9B,aAAa,KAAK;AAAA,EACpB,CAAC;AAAA,EAED,MAAM,UAAU,MAAM,QAAQ,cAAc;AAAA,EAG5C,MAAM,UASF,CAAC;AAAA,EAEL,QAAQ,OAAO,MAAM,8BAAU,OAAO;AAAA,EACtC,QAAQ,UAAU,MAAM,oCAAa,SAAS,KAAK,OAAO;AAAA,EAC1D,QAAQ,WAAW,MAAM,sCAAc,OAAO;AAAA,EAC9C,QAAQ,SAAS,MAAM,kCAAY,OAAO;AAAA,EAC1C,QAAQ,OAAO,MAAM,8BAAU,SAAS,EAAE,KAAK,KAAK,IAAI,CAAC;AAAA,EACzD,QAAQ,SAAS,MAAM,kCAAY,OAAO;AAAA,EAC1C,QAAQ,QAAQ,MAAM,gCAAW,SAAS,KAAK,KAAK;AAAA,EAEpD,IAAI,KAAK,IAAI;AAAA,IACX,QAAQ,KAAK,MAAM,0BAAQ,SAAS,KAAK,EAAE;AAAA,EAC7C;AAAA,EAEA,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ;AAAA,IACjB,OAAO,GAAG;AAAA,MACR,QAAQ,IAAI,QAAQ;AAAA,MACpB,QAAQ,OAAO,QAAQ;AAAA,MACvB,QAAQ,QAAQ,QAAQ;AAAA,MACxB,QAAQ,MAAM,QAAQ;AAAA,MACtB,QAAQ,QAAQ,QAAQ;AAAA,MACxB,QAAQ,UAAU,QAAQ;AAAA,MAC1B,QAAQ,SAAS,QAAQ;AAAA,MACzB,QAAQ,MAAM,QAAQ;AAAA,MACtB,QAAQ,QAAQ;AAAA,MAChB,QAAQ,QAAQ;AAAA;AAAA,EAEpB;AAAA;",
|
|
8
|
+
"debugId": "D12930C28AC6F96064756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/dist/cjs/package.json
CHANGED
package/dist/mjs/index.mjs
CHANGED
|
@@ -9,54 +9,454 @@ import { setupPath } from "@ricsam/isolate-path";
|
|
|
9
9
|
import { setupCrypto } from "@ricsam/isolate-crypto";
|
|
10
10
|
import { setupFetch } from "@ricsam/isolate-fetch";
|
|
11
11
|
import { setupFs } from "@ricsam/isolate-fs";
|
|
12
|
+
import {
|
|
13
|
+
setupTestEnvironment,
|
|
14
|
+
runTests as runTestsInContext,
|
|
15
|
+
hasTests as hasTestsInContext,
|
|
16
|
+
getTestCount as getTestCountInContext
|
|
17
|
+
} from "@ricsam/isolate-test-environment";
|
|
18
|
+
import { setupPlaywright } from "@ricsam/isolate-playwright";
|
|
12
19
|
import { setupCore as setupCore2 } from "@ricsam/isolate-core";
|
|
13
|
-
import { setupConsole as setupConsole2 } from "@ricsam/isolate-console";
|
|
20
|
+
import { setupConsole as setupConsole2, simpleConsoleHandler } from "@ricsam/isolate-console";
|
|
14
21
|
import { setupCrypto as setupCrypto2 } from "@ricsam/isolate-crypto";
|
|
15
22
|
import { setupEncoding as setupEncoding2 } from "@ricsam/isolate-encoding";
|
|
16
23
|
import { setupFetch as setupFetch2 } from "@ricsam/isolate-fetch";
|
|
17
24
|
import { setupFs as setupFs2, createNodeFileSystemHandler } from "@ricsam/isolate-fs";
|
|
18
25
|
import { setupPath as setupPath2 } from "@ricsam/isolate-path";
|
|
19
26
|
import { setupTimers as setupTimers2 } from "@ricsam/isolate-timers";
|
|
27
|
+
import { setupTestEnvironment as setupTestEnvironment2, runTests, hasTests, getTestCount } from "@ricsam/isolate-test-environment";
|
|
28
|
+
import { setupPlaywright as setupPlaywright2, createPlaywrightHandler } from "@ricsam/isolate-playwright";
|
|
29
|
+
|
|
30
|
+
export * from "./internal.mjs";
|
|
31
|
+
var iteratorSessions = new Map;
|
|
32
|
+
var nextIteratorId = 1;
|
|
33
|
+
async function setupCustomFunctions(context, customFunctions) {
|
|
34
|
+
const global = context.global;
|
|
35
|
+
const invokeCallbackRef = new ivm.Reference(async (name, argsJson) => {
|
|
36
|
+
const def = customFunctions[name];
|
|
37
|
+
if (!def) {
|
|
38
|
+
return JSON.stringify({
|
|
39
|
+
ok: false,
|
|
40
|
+
error: { message: `Custom function '${name}' not found`, name: "Error" }
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
const args = JSON.parse(argsJson);
|
|
44
|
+
try {
|
|
45
|
+
const result = def.type === "async" ? await def.fn(...args) : def.fn(...args);
|
|
46
|
+
return JSON.stringify({ ok: true, value: result });
|
|
47
|
+
} catch (error) {
|
|
48
|
+
const err = error;
|
|
49
|
+
return JSON.stringify({
|
|
50
|
+
ok: false,
|
|
51
|
+
error: { message: err.message, name: err.name }
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
global.setSync("__customFn_invoke", invokeCallbackRef);
|
|
56
|
+
const iterStartRef = new ivm.Reference(async (name, argsJson) => {
|
|
57
|
+
const def = customFunctions[name];
|
|
58
|
+
if (!def || def.type !== "asyncIterator") {
|
|
59
|
+
return JSON.stringify({
|
|
60
|
+
ok: false,
|
|
61
|
+
error: { message: `Async iterator function '${name}' not found`, name: "Error" }
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const args = JSON.parse(argsJson);
|
|
66
|
+
const fn = def.fn;
|
|
67
|
+
const iterator = fn(...args);
|
|
68
|
+
const iteratorId = nextIteratorId++;
|
|
69
|
+
iteratorSessions.set(iteratorId, { iterator });
|
|
70
|
+
return JSON.stringify({ ok: true, iteratorId });
|
|
71
|
+
} catch (error) {
|
|
72
|
+
const err = error;
|
|
73
|
+
return JSON.stringify({
|
|
74
|
+
ok: false,
|
|
75
|
+
error: { message: err.message, name: err.name }
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
global.setSync("__iter_start", iterStartRef);
|
|
80
|
+
const iterNextRef = new ivm.Reference(async (iteratorId) => {
|
|
81
|
+
const session = iteratorSessions.get(iteratorId);
|
|
82
|
+
if (!session) {
|
|
83
|
+
return JSON.stringify({
|
|
84
|
+
ok: false,
|
|
85
|
+
error: { message: `Iterator session ${iteratorId} not found`, name: "Error" }
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const result = await session.iterator.next();
|
|
90
|
+
if (result.done) {
|
|
91
|
+
iteratorSessions.delete(iteratorId);
|
|
92
|
+
}
|
|
93
|
+
return JSON.stringify({ ok: true, done: result.done, value: result.value });
|
|
94
|
+
} catch (error) {
|
|
95
|
+
const err = error;
|
|
96
|
+
iteratorSessions.delete(iteratorId);
|
|
97
|
+
return JSON.stringify({
|
|
98
|
+
ok: false,
|
|
99
|
+
error: { message: err.message, name: err.name }
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
global.setSync("__iter_next", iterNextRef);
|
|
104
|
+
const iterReturnRef = new ivm.Reference(async (iteratorId, valueJson) => {
|
|
105
|
+
const session = iteratorSessions.get(iteratorId);
|
|
106
|
+
if (!session) {
|
|
107
|
+
return JSON.stringify({ ok: true, done: true, value: undefined });
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const value = valueJson ? JSON.parse(valueJson) : undefined;
|
|
111
|
+
const result = await session.iterator.return?.(value);
|
|
112
|
+
iteratorSessions.delete(iteratorId);
|
|
113
|
+
return JSON.stringify({ ok: true, done: true, value: result?.value });
|
|
114
|
+
} catch (error) {
|
|
115
|
+
const err = error;
|
|
116
|
+
iteratorSessions.delete(iteratorId);
|
|
117
|
+
return JSON.stringify({
|
|
118
|
+
ok: false,
|
|
119
|
+
error: { message: err.message, name: err.name }
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
global.setSync("__iter_return", iterReturnRef);
|
|
124
|
+
const iterThrowRef = new ivm.Reference(async (iteratorId, errorJson) => {
|
|
125
|
+
const session = iteratorSessions.get(iteratorId);
|
|
126
|
+
if (!session) {
|
|
127
|
+
return JSON.stringify({
|
|
128
|
+
ok: false,
|
|
129
|
+
error: { message: `Iterator session ${iteratorId} not found`, name: "Error" }
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const errorData = JSON.parse(errorJson);
|
|
134
|
+
const error = Object.assign(new Error(errorData.message), { name: errorData.name });
|
|
135
|
+
const result = await session.iterator.throw?.(error);
|
|
136
|
+
iteratorSessions.delete(iteratorId);
|
|
137
|
+
return JSON.stringify({ ok: true, done: result?.done ?? true, value: result?.value });
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const err = error;
|
|
140
|
+
iteratorSessions.delete(iteratorId);
|
|
141
|
+
return JSON.stringify({
|
|
142
|
+
ok: false,
|
|
143
|
+
error: { message: err.message, name: err.name }
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
global.setSync("__iter_throw", iterThrowRef);
|
|
148
|
+
for (const name of Object.keys(customFunctions)) {
|
|
149
|
+
const def = customFunctions[name];
|
|
150
|
+
if (def.type === "async") {
|
|
151
|
+
context.evalSync(`
|
|
152
|
+
globalThis.${name} = async function(...args) {
|
|
153
|
+
const resultJson = __customFn_invoke.applySyncPromise(
|
|
154
|
+
undefined,
|
|
155
|
+
["${name}", JSON.stringify(args)]
|
|
156
|
+
);
|
|
157
|
+
const result = JSON.parse(resultJson);
|
|
158
|
+
if (result.ok) {
|
|
159
|
+
return result.value;
|
|
160
|
+
} else {
|
|
161
|
+
const error = new Error(result.error.message);
|
|
162
|
+
error.name = result.error.name;
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
`);
|
|
167
|
+
} else if (def.type === "sync") {
|
|
168
|
+
context.evalSync(`
|
|
169
|
+
globalThis.${name} = function(...args) {
|
|
170
|
+
const resultJson = __customFn_invoke.applySyncPromise(
|
|
171
|
+
undefined,
|
|
172
|
+
["${name}", JSON.stringify(args)]
|
|
173
|
+
);
|
|
174
|
+
const result = JSON.parse(resultJson);
|
|
175
|
+
if (result.ok) {
|
|
176
|
+
return result.value;
|
|
177
|
+
} else {
|
|
178
|
+
const error = new Error(result.error.message);
|
|
179
|
+
error.name = result.error.name;
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
`);
|
|
184
|
+
} else if (def.type === "asyncIterator") {
|
|
185
|
+
context.evalSync(`
|
|
186
|
+
globalThis.${name} = function(...args) {
|
|
187
|
+
const startResult = JSON.parse(__iter_start.applySyncPromise(undefined, ["${name}", JSON.stringify(args)]));
|
|
188
|
+
if (!startResult.ok) {
|
|
189
|
+
throw Object.assign(new Error(startResult.error.message), { name: startResult.error.name });
|
|
190
|
+
}
|
|
191
|
+
const iteratorId = startResult.iteratorId;
|
|
192
|
+
return {
|
|
193
|
+
[Symbol.asyncIterator]() { return this; },
|
|
194
|
+
async next() {
|
|
195
|
+
const result = JSON.parse(__iter_next.applySyncPromise(undefined, [iteratorId]));
|
|
196
|
+
if (!result.ok) {
|
|
197
|
+
throw Object.assign(new Error(result.error.message), { name: result.error.name });
|
|
198
|
+
}
|
|
199
|
+
return { done: result.done, value: result.value };
|
|
200
|
+
},
|
|
201
|
+
async return(v) {
|
|
202
|
+
const result = JSON.parse(__iter_return.applySyncPromise(undefined, [iteratorId, JSON.stringify(v)]));
|
|
203
|
+
return { done: true, value: result.value };
|
|
204
|
+
},
|
|
205
|
+
async throw(e) {
|
|
206
|
+
const result = JSON.parse(__iter_throw.applySyncPromise(undefined, [iteratorId, JSON.stringify({ message: e.message, name: e.name })]));
|
|
207
|
+
if (!result.ok) {
|
|
208
|
+
throw Object.assign(new Error(result.error.message), { name: result.error.name });
|
|
209
|
+
}
|
|
210
|
+
return { done: result.done, value: result.value };
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return invokeCallbackRef;
|
|
218
|
+
}
|
|
219
|
+
function createModuleResolver(state) {
|
|
220
|
+
return async (specifier, _referrer) => {
|
|
221
|
+
const cached = state.moduleCache.get(specifier);
|
|
222
|
+
if (cached)
|
|
223
|
+
return cached;
|
|
224
|
+
if (!state.moduleLoader) {
|
|
225
|
+
throw new Error(`No module loader registered. Cannot import: ${specifier}`);
|
|
226
|
+
}
|
|
227
|
+
const code = await state.moduleLoader(specifier);
|
|
228
|
+
const mod = await state.isolate.compileModule(code, {
|
|
229
|
+
filename: specifier
|
|
230
|
+
});
|
|
231
|
+
state.moduleCache.set(specifier, mod);
|
|
232
|
+
const resolver = createModuleResolver(state);
|
|
233
|
+
await mod.instantiate(state.context, resolver);
|
|
234
|
+
return mod;
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function convertFetchCallback(callback) {
|
|
238
|
+
if (!callback) {
|
|
239
|
+
return {};
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
onFetch: async (request) => {
|
|
243
|
+
return Promise.resolve(callback(request));
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
20
247
|
async function createRuntime(options) {
|
|
21
248
|
const opts = options ?? {};
|
|
249
|
+
const id = crypto.randomUUID();
|
|
22
250
|
const isolate = new ivm.Isolate({
|
|
23
|
-
memoryLimit: opts.
|
|
251
|
+
memoryLimit: opts.memoryLimitMB
|
|
24
252
|
});
|
|
25
253
|
const context = await isolate.createContext();
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
254
|
+
const state = {
|
|
255
|
+
isolate,
|
|
256
|
+
context,
|
|
257
|
+
handles: {},
|
|
258
|
+
moduleCache: new Map,
|
|
259
|
+
moduleLoader: opts.moduleLoader,
|
|
260
|
+
customFunctions: opts.customFunctions
|
|
261
|
+
};
|
|
262
|
+
state.handles.core = await setupCore(context);
|
|
263
|
+
state.handles.console = await setupConsole(context, opts.console);
|
|
264
|
+
state.handles.encoding = await setupEncoding(context);
|
|
265
|
+
state.handles.timers = await setupTimers(context);
|
|
266
|
+
state.handles.path = await setupPath(context, { cwd: opts.cwd });
|
|
267
|
+
state.handles.crypto = await setupCrypto(context);
|
|
268
|
+
state.handles.fetch = await setupFetch(context, convertFetchCallback(opts.fetch));
|
|
34
269
|
if (opts.fs) {
|
|
35
|
-
handles.fs = await setupFs(context, opts.fs);
|
|
270
|
+
state.handles.fs = await setupFs(context, opts.fs);
|
|
271
|
+
}
|
|
272
|
+
if (opts.customFunctions) {
|
|
273
|
+
state.customFnInvokeRef = await setupCustomFunctions(context, opts.customFunctions);
|
|
36
274
|
}
|
|
275
|
+
if (opts.testEnvironment) {
|
|
276
|
+
const testEnvOptions = typeof opts.testEnvironment === "object" ? opts.testEnvironment : undefined;
|
|
277
|
+
state.handles.testEnvironment = await setupTestEnvironment(context, testEnvOptions);
|
|
278
|
+
}
|
|
279
|
+
if (opts.playwright) {
|
|
280
|
+
let eventCallback = opts.playwright.onEvent;
|
|
281
|
+
if (opts.playwright.console && opts.console?.onEntry) {
|
|
282
|
+
const originalCallback = eventCallback;
|
|
283
|
+
const consoleHandler = opts.console.onEntry;
|
|
284
|
+
eventCallback = (event) => {
|
|
285
|
+
if (originalCallback) {
|
|
286
|
+
originalCallback(event);
|
|
287
|
+
}
|
|
288
|
+
if (event.type === "browserConsoleLog") {
|
|
289
|
+
consoleHandler({
|
|
290
|
+
type: "browserOutput",
|
|
291
|
+
level: event.level,
|
|
292
|
+
args: event.args,
|
|
293
|
+
timestamp: event.timestamp
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
state.handles.playwright = await setupPlaywright(context, {
|
|
299
|
+
page: opts.playwright.page,
|
|
300
|
+
timeout: opts.playwright.timeout,
|
|
301
|
+
baseUrl: opts.playwright.baseUrl,
|
|
302
|
+
console: opts.playwright.console && !opts.console?.onEntry,
|
|
303
|
+
onEvent: eventCallback
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
const fetchHandle = {
|
|
307
|
+
async dispatchRequest(request, options2) {
|
|
308
|
+
if (!state.handles.fetch) {
|
|
309
|
+
throw new Error("Fetch handle not available");
|
|
310
|
+
}
|
|
311
|
+
return state.handles.fetch.dispatchRequest(request, options2);
|
|
312
|
+
},
|
|
313
|
+
getUpgradeRequest() {
|
|
314
|
+
if (!state.handles.fetch) {
|
|
315
|
+
throw new Error("Fetch handle not available");
|
|
316
|
+
}
|
|
317
|
+
return state.handles.fetch.getUpgradeRequest();
|
|
318
|
+
},
|
|
319
|
+
dispatchWebSocketOpen(connectionId) {
|
|
320
|
+
if (!state.handles.fetch) {
|
|
321
|
+
throw new Error("Fetch handle not available");
|
|
322
|
+
}
|
|
323
|
+
state.handles.fetch.dispatchWebSocketOpen(connectionId);
|
|
324
|
+
},
|
|
325
|
+
dispatchWebSocketMessage(connectionId, message) {
|
|
326
|
+
if (!state.handles.fetch) {
|
|
327
|
+
throw new Error("Fetch handle not available");
|
|
328
|
+
}
|
|
329
|
+
state.handles.fetch.dispatchWebSocketMessage(connectionId, message);
|
|
330
|
+
},
|
|
331
|
+
dispatchWebSocketClose(connectionId, code, reason) {
|
|
332
|
+
if (!state.handles.fetch) {
|
|
333
|
+
throw new Error("Fetch handle not available");
|
|
334
|
+
}
|
|
335
|
+
state.handles.fetch.dispatchWebSocketClose(connectionId, code, reason);
|
|
336
|
+
},
|
|
337
|
+
dispatchWebSocketError(connectionId, error) {
|
|
338
|
+
if (!state.handles.fetch) {
|
|
339
|
+
throw new Error("Fetch handle not available");
|
|
340
|
+
}
|
|
341
|
+
state.handles.fetch.dispatchWebSocketError(connectionId, error);
|
|
342
|
+
},
|
|
343
|
+
onWebSocketCommand(callback) {
|
|
344
|
+
if (!state.handles.fetch) {
|
|
345
|
+
throw new Error("Fetch handle not available");
|
|
346
|
+
}
|
|
347
|
+
return state.handles.fetch.onWebSocketCommand(callback);
|
|
348
|
+
},
|
|
349
|
+
hasServeHandler() {
|
|
350
|
+
if (!state.handles.fetch) {
|
|
351
|
+
throw new Error("Fetch handle not available");
|
|
352
|
+
}
|
|
353
|
+
return state.handles.fetch.hasServeHandler();
|
|
354
|
+
},
|
|
355
|
+
hasActiveConnections() {
|
|
356
|
+
if (!state.handles.fetch) {
|
|
357
|
+
throw new Error("Fetch handle not available");
|
|
358
|
+
}
|
|
359
|
+
return state.handles.fetch.hasActiveConnections();
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
const timersHandle = {
|
|
363
|
+
clearAll() {
|
|
364
|
+
state.handles.timers?.clearAll();
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
const consoleHandle = {
|
|
368
|
+
reset() {
|
|
369
|
+
state.handles.console?.reset();
|
|
370
|
+
},
|
|
371
|
+
getTimers() {
|
|
372
|
+
return state.handles.console?.getTimers() ?? new Map;
|
|
373
|
+
},
|
|
374
|
+
getCounters() {
|
|
375
|
+
return state.handles.console?.getCounters() ?? new Map;
|
|
376
|
+
},
|
|
377
|
+
getGroupDepth() {
|
|
378
|
+
return state.handles.console?.getGroupDepth() ?? 0;
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
const testEnvironmentHandle = {
|
|
382
|
+
async runTests(_timeout) {
|
|
383
|
+
if (!state.handles.testEnvironment) {
|
|
384
|
+
throw new Error("Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
385
|
+
}
|
|
386
|
+
return runTestsInContext(state.context);
|
|
387
|
+
},
|
|
388
|
+
hasTests() {
|
|
389
|
+
if (!state.handles.testEnvironment) {
|
|
390
|
+
throw new Error("Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
391
|
+
}
|
|
392
|
+
return hasTestsInContext(state.context);
|
|
393
|
+
},
|
|
394
|
+
getTestCount() {
|
|
395
|
+
if (!state.handles.testEnvironment) {
|
|
396
|
+
throw new Error("Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
397
|
+
}
|
|
398
|
+
return getTestCountInContext(state.context);
|
|
399
|
+
},
|
|
400
|
+
reset() {
|
|
401
|
+
state.handles.testEnvironment?.dispose();
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
const playwrightHandle = {
|
|
405
|
+
getCollectedData() {
|
|
406
|
+
if (!state.handles.playwright) {
|
|
407
|
+
throw new Error("Playwright not configured. Provide playwright.page in createRuntime options.");
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
browserConsoleLogs: state.handles.playwright.getBrowserConsoleLogs(),
|
|
411
|
+
networkRequests: state.handles.playwright.getNetworkRequests(),
|
|
412
|
+
networkResponses: state.handles.playwright.getNetworkResponses()
|
|
413
|
+
};
|
|
414
|
+
},
|
|
415
|
+
clearCollectedData() {
|
|
416
|
+
state.handles.playwright?.clearCollected();
|
|
417
|
+
}
|
|
418
|
+
};
|
|
37
419
|
return {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
420
|
+
id,
|
|
421
|
+
fetch: fetchHandle,
|
|
422
|
+
timers: timersHandle,
|
|
423
|
+
console: consoleHandle,
|
|
424
|
+
testEnvironment: testEnvironmentHandle,
|
|
425
|
+
playwright: playwrightHandle,
|
|
426
|
+
async eval(code, filenameOrOptions) {
|
|
427
|
+
const options2 = typeof filenameOrOptions === "string" ? { filename: filenameOrOptions } : filenameOrOptions;
|
|
428
|
+
const mod = await state.isolate.compileModule(code, {
|
|
429
|
+
filename: options2?.filename ?? "<eval>"
|
|
430
|
+
});
|
|
431
|
+
const resolver = createModuleResolver(state);
|
|
432
|
+
await mod.instantiate(state.context, resolver);
|
|
433
|
+
await mod.evaluate(options2?.maxExecutionMs ? { timeout: options2.maxExecutionMs } : undefined);
|
|
434
|
+
},
|
|
435
|
+
async dispose() {
|
|
436
|
+
if (state.customFnInvokeRef) {
|
|
437
|
+
state.customFnInvokeRef.release();
|
|
438
|
+
}
|
|
439
|
+
state.handles.playwright?.dispose();
|
|
440
|
+
state.handles.testEnvironment?.dispose();
|
|
441
|
+
state.handles.fs?.dispose();
|
|
442
|
+
state.handles.fetch?.dispose();
|
|
443
|
+
state.handles.crypto?.dispose();
|
|
444
|
+
state.handles.path?.dispose();
|
|
445
|
+
state.handles.timers?.dispose();
|
|
446
|
+
state.handles.encoding?.dispose();
|
|
447
|
+
state.handles.console?.dispose();
|
|
448
|
+
state.handles.core?.dispose();
|
|
449
|
+
state.moduleCache.clear();
|
|
450
|
+
state.context.release();
|
|
451
|
+
state.isolate.dispose();
|
|
55
452
|
}
|
|
56
453
|
};
|
|
57
454
|
}
|
|
58
455
|
export {
|
|
456
|
+
simpleConsoleHandler,
|
|
59
457
|
setupTimers2 as setupTimers,
|
|
458
|
+
setupTestEnvironment2 as setupTestEnvironment,
|
|
459
|
+
setupPlaywright2 as setupPlaywright,
|
|
60
460
|
setupPath2 as setupPath,
|
|
61
461
|
setupFs2 as setupFs,
|
|
62
462
|
setupFetch2 as setupFetch,
|
|
@@ -64,8 +464,12 @@ export {
|
|
|
64
464
|
setupCrypto2 as setupCrypto,
|
|
65
465
|
setupCore2 as setupCore,
|
|
66
466
|
setupConsole2 as setupConsole,
|
|
467
|
+
runTests,
|
|
468
|
+
hasTests,
|
|
469
|
+
getTestCount,
|
|
67
470
|
createRuntime,
|
|
471
|
+
createPlaywrightHandler,
|
|
68
472
|
createNodeFileSystemHandler
|
|
69
473
|
};
|
|
70
474
|
|
|
71
|
-
//# debugId=
|
|
475
|
+
//# debugId=727908D2ABC724D264756E2164756E21
|