@ricsam/isolate-runtime 0.1.1 → 0.1.2
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 +51 -0
- package/dist/cjs/index.cjs +115 -0
- package/dist/cjs/index.cjs.map +10 -0
- package/dist/cjs/package.json +5 -0
- package/dist/mjs/index.mjs +71 -0
- package/dist/mjs/index.mjs.map +10 -0
- package/dist/mjs/package.json +5 -0
- package/dist/types/index.d.ts +65 -0
- package/package.json +38 -13
- package/CHANGELOG.md +0 -15
- package/src/index.test.ts +0 -503
- package/src/index.ts +0 -164
- package/tsconfig.json +0 -8
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @ricsam/isolate-runtime
|
|
2
|
+
|
|
3
|
+
Umbrella package that combines all APIs.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { createRuntime } from "@ricsam/isolate-runtime";
|
|
7
|
+
|
|
8
|
+
const runtime = await createRuntime({
|
|
9
|
+
// Memory limit in MB
|
|
10
|
+
memoryLimit: 128,
|
|
11
|
+
// Console API
|
|
12
|
+
console: {
|
|
13
|
+
onLog: (level, ...args) => console.log(`[${level}]`, ...args),
|
|
14
|
+
},
|
|
15
|
+
// Fetch API
|
|
16
|
+
fetch: {
|
|
17
|
+
onFetch: async (req) => fetch(req),
|
|
18
|
+
},
|
|
19
|
+
// File System API (optional)
|
|
20
|
+
fs: {
|
|
21
|
+
getDirectory: async (path) => createNodeFileSystemHandler(`./data${path}`),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// The runtime includes:
|
|
26
|
+
// - runtime.isolate: The V8 isolate
|
|
27
|
+
// - runtime.context: The execution context
|
|
28
|
+
// - runtime.tick(): Process pending timers
|
|
29
|
+
// - runtime.dispose(): Clean up all resources
|
|
30
|
+
|
|
31
|
+
// Run code
|
|
32
|
+
await runtime.context.eval(`
|
|
33
|
+
console.log("Hello from sandbox!");
|
|
34
|
+
`, { promise: true });
|
|
35
|
+
|
|
36
|
+
// Process timers
|
|
37
|
+
await runtime.tick(100);
|
|
38
|
+
|
|
39
|
+
// Cleanup
|
|
40
|
+
runtime.dispose();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**What's Included:**
|
|
44
|
+
- Core (Blob, File, streams, URL, TextEncoder/Decoder)
|
|
45
|
+
- Console
|
|
46
|
+
- Encoding (atob/btoa)
|
|
47
|
+
- Timers (setTimeout, setInterval)
|
|
48
|
+
- Path utilities
|
|
49
|
+
- Crypto (randomUUID, getRandomValues, subtle)
|
|
50
|
+
- Fetch API
|
|
51
|
+
- File System (if handler provided)
|
|
@@ -0,0 +1,115 @@
|
|
|
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/index.ts
|
|
44
|
+
var exports_src = {};
|
|
45
|
+
__export(exports_src, {
|
|
46
|
+
setupTimers: () => import_isolate_timers2.setupTimers,
|
|
47
|
+
setupPath: () => import_isolate_path2.setupPath,
|
|
48
|
+
setupFs: () => import_isolate_fs2.setupFs,
|
|
49
|
+
setupFetch: () => import_isolate_fetch2.setupFetch,
|
|
50
|
+
setupEncoding: () => import_isolate_encoding2.setupEncoding,
|
|
51
|
+
setupCrypto: () => import_isolate_crypto2.setupCrypto,
|
|
52
|
+
setupCore: () => import_isolate_core2.setupCore,
|
|
53
|
+
setupConsole: () => import_isolate_console2.setupConsole,
|
|
54
|
+
createRuntime: () => createRuntime,
|
|
55
|
+
createNodeFileSystemHandler: () => import_isolate_fs2.createNodeFileSystemHandler
|
|
56
|
+
});
|
|
57
|
+
module.exports = __toCommonJS(exports_src);
|
|
58
|
+
var import_isolated_vm = __toESM(require("isolated-vm"));
|
|
59
|
+
var import_isolate_core = require("@ricsam/isolate-core");
|
|
60
|
+
var import_isolate_console = require("@ricsam/isolate-console");
|
|
61
|
+
var import_isolate_encoding = require("@ricsam/isolate-encoding");
|
|
62
|
+
var import_isolate_timers = require("@ricsam/isolate-timers");
|
|
63
|
+
var import_isolate_path = require("@ricsam/isolate-path");
|
|
64
|
+
var import_isolate_crypto = require("@ricsam/isolate-crypto");
|
|
65
|
+
var import_isolate_fetch = require("@ricsam/isolate-fetch");
|
|
66
|
+
var import_isolate_fs = require("@ricsam/isolate-fs");
|
|
67
|
+
var import_isolate_core2 = require("@ricsam/isolate-core");
|
|
68
|
+
var import_isolate_console2 = require("@ricsam/isolate-console");
|
|
69
|
+
var import_isolate_crypto2 = require("@ricsam/isolate-crypto");
|
|
70
|
+
var import_isolate_encoding2 = require("@ricsam/isolate-encoding");
|
|
71
|
+
var import_isolate_fetch2 = require("@ricsam/isolate-fetch");
|
|
72
|
+
var import_isolate_fs2 = require("@ricsam/isolate-fs");
|
|
73
|
+
var import_isolate_path2 = require("@ricsam/isolate-path");
|
|
74
|
+
var import_isolate_timers2 = require("@ricsam/isolate-timers");
|
|
75
|
+
async function createRuntime(options) {
|
|
76
|
+
const opts = options ?? {};
|
|
77
|
+
const isolate = new import_isolated_vm.default.Isolate({
|
|
78
|
+
memoryLimit: opts.memoryLimit
|
|
79
|
+
});
|
|
80
|
+
const context = await isolate.createContext();
|
|
81
|
+
const handles = {};
|
|
82
|
+
handles.core = await import_isolate_core.setupCore(context);
|
|
83
|
+
handles.console = await import_isolate_console.setupConsole(context, opts.console);
|
|
84
|
+
handles.encoding = await import_isolate_encoding.setupEncoding(context);
|
|
85
|
+
handles.timers = await import_isolate_timers.setupTimers(context);
|
|
86
|
+
handles.path = await import_isolate_path.setupPath(context);
|
|
87
|
+
handles.crypto = await import_isolate_crypto.setupCrypto(context);
|
|
88
|
+
handles.fetch = await import_isolate_fetch.setupFetch(context, opts.fetch);
|
|
89
|
+
if (opts.fs) {
|
|
90
|
+
handles.fs = await import_isolate_fs.setupFs(context, opts.fs);
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
isolate,
|
|
94
|
+
context,
|
|
95
|
+
fetch: handles.fetch,
|
|
96
|
+
async tick(ms) {
|
|
97
|
+
await handles.timers.tick(ms);
|
|
98
|
+
},
|
|
99
|
+
dispose() {
|
|
100
|
+
handles.fs?.dispose();
|
|
101
|
+
handles.fetch?.dispose();
|
|
102
|
+
handles.crypto?.dispose();
|
|
103
|
+
handles.path?.dispose();
|
|
104
|
+
handles.timers?.dispose();
|
|
105
|
+
handles.encoding?.dispose();
|
|
106
|
+
handles.console?.dispose();
|
|
107
|
+
handles.core?.dispose();
|
|
108
|
+
context.release();
|
|
109
|
+
isolate.dispose();
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
//# debugId=4A635399139F1BA664756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import 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\nexport interface RuntimeOptions {\n /** Isolate memory limit in MB */\n memoryLimit?: number;\n /** Console options */\n console?: ConsoleOptions;\n /** Fetch options */\n fetch?: FetchOptions;\n /** File system options (optional - fs only set up if provided) */\n fs?: FsOptions;\n}\n\nexport interface RuntimeHandle {\n /** The isolate instance */\n readonly isolate: ivm.Isolate;\n /** The context instance */\n readonly context: ivm.Context;\n /** The fetch handle for serve() and WebSocket dispatching */\n readonly fetch: FetchHandle;\n /** Process pending timers */\n tick(ms?: number): Promise<void>;\n /** Dispose all resources */\n dispose(): void;\n}\n\n/**\n * Create a fully configured isolated-vm runtime\n *\n * Sets up all WHATWG APIs: fetch, fs, console, crypto, encoding, timers\n *\n * @example\n * const runtime = await createRuntime({\n * console: {\n * onLog: (level, ...args) => console.log(`[${level}]`, ...args)\n * },\n * fetch: {\n * onFetch: async (request) => fetch(request)\n * }\n * });\n *\n * await runtime.context.eval(`\n * console.log(\"Hello from sandbox!\");\n * const response = await fetch(\"https://example.com\");\n * `);\n *\n * runtime.dispose();\n */\nexport async function createRuntime(\n options?: RuntimeOptions\n): Promise<RuntimeHandle> {\n const opts = options ?? {};\n\n // Create isolate with optional memory limit\n const isolate = new ivm.Isolate({\n memoryLimit: opts.memoryLimit,\n });\n const context = await isolate.createContext();\n\n // Store all handles for disposal\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 // Setup all APIs in order\n // Core must be first as it provides Blob, File, streams, URL, etc.\n handles.core = await setupCore(context);\n\n // Console\n handles.console = await setupConsole(context, opts.console);\n\n // Encoding (btoa/atob)\n handles.encoding = await setupEncoding(context);\n\n // Timers (setTimeout, setInterval)\n handles.timers = await setupTimers(context);\n\n // Path module\n handles.path = await setupPath(context);\n\n // Crypto (randomUUID, getRandomValues)\n handles.crypto = await setupCrypto(context);\n\n // Fetch API\n handles.fetch = await setupFetch(context, opts.fetch);\n\n // File system (only if handler provided)\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 async tick(ms?: number) {\n await handles.timers!.tick(ms);\n },\n dispose() {\n // Dispose all handles\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\n // Release context and dispose isolate\n context.release();\n isolate.dispose();\n },\n };\n}\n\n// Re-export all package types and functions\nexport { setupCore } from \"@ricsam/isolate-core\";\nexport type { CoreHandle, SetupCoreOptions } from \"@ricsam/isolate-core\";\n\nexport { setupConsole } from \"@ricsam/isolate-console\";\nexport type { ConsoleHandle, ConsoleOptions } from \"@ricsam/isolate-console\";\n\nexport { setupCrypto } from \"@ricsam/isolate-crypto\";\nexport type { CryptoHandle } from \"@ricsam/isolate-crypto\";\n\nexport { setupEncoding } from \"@ricsam/isolate-encoding\";\nexport type { EncodingHandle } from \"@ricsam/isolate-encoding\";\n\nexport { setupFetch } from \"@ricsam/isolate-fetch\";\nexport type { FetchHandle, FetchOptions, WebSocketCommand, UpgradeRequest } from \"@ricsam/isolate-fetch\";\n\nexport { setupFs, createNodeFileSystemHandler } from \"@ricsam/isolate-fs\";\nexport type { FsHandle, FsOptions, FileSystemHandler, NodeFileSystemHandlerOptions } from \"@ricsam/isolate-fs\";\n\nexport { setupPath } from \"@ricsam/isolate-path\";\nexport type { PathHandle } from \"@ricsam/isolate-path\";\n\nexport { setupTimers } from \"@ricsam/isolate-timers\";\nexport type { TimersHandle } from \"@ricsam/isolate-timers\";\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAgB,IAAhB;AAC0B,IAA1B;AAC6B,IAA7B;AAC8B,IAA9B;AAC4B,IAA5B;AAC0B,IAA1B;AAC4B,IAA5B;AAC2B,IAA3B;AACwB,IAAxB;AAqI0B,IAA1B;AAG6B,IAA7B;AAG4B,IAA5B;AAG8B,IAA9B;AAG2B,IAA3B;AAGqD,IAArD;AAG0B,IAA1B;AAG4B,IAA5B;AAjGA,eAAsB,aAAa,CACjC,SACwB;AAAA,EACxB,MAAM,OAAO,WAAW,CAAC;AAAA,EAGzB,MAAM,UAAU,IAAI,2BAAI,QAAQ;AAAA,IAC9B,aAAa,KAAK;AAAA,EACpB,CAAC;AAAA,EACD,MAAM,UAAU,MAAM,QAAQ,cAAc;AAAA,EAG5C,MAAM,UASF,CAAC;AAAA,EAIL,QAAQ,OAAO,MAAM,8BAAU,OAAO;AAAA,EAGtC,QAAQ,UAAU,MAAM,oCAAa,SAAS,KAAK,OAAO;AAAA,EAG1D,QAAQ,WAAW,MAAM,sCAAc,OAAO;AAAA,EAG9C,QAAQ,SAAS,MAAM,kCAAY,OAAO;AAAA,EAG1C,QAAQ,OAAO,MAAM,8BAAU,OAAO;AAAA,EAGtC,QAAQ,SAAS,MAAM,kCAAY,OAAO;AAAA,EAG1C,QAAQ,QAAQ,MAAM,gCAAW,SAAS,KAAK,KAAK;AAAA,EAGpD,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,SACT,KAAI,CAAC,IAAa;AAAA,MACtB,MAAM,QAAQ,OAAQ,KAAK,EAAE;AAAA;AAAA,IAE/B,OAAO,GAAG;AAAA,MAER,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,MAGtB,QAAQ,QAAQ;AAAA,MAChB,QAAQ,QAAQ;AAAA;AAAA,EAEpB;AAAA;",
|
|
8
|
+
"debugId": "4A635399139F1BA664756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/runtime/src/index.ts
|
|
3
|
+
import ivm from "isolated-vm";
|
|
4
|
+
import { setupCore } from "@ricsam/isolate-core";
|
|
5
|
+
import { setupConsole } from "@ricsam/isolate-console";
|
|
6
|
+
import { setupEncoding } from "@ricsam/isolate-encoding";
|
|
7
|
+
import { setupTimers } from "@ricsam/isolate-timers";
|
|
8
|
+
import { setupPath } from "@ricsam/isolate-path";
|
|
9
|
+
import { setupCrypto } from "@ricsam/isolate-crypto";
|
|
10
|
+
import { setupFetch } from "@ricsam/isolate-fetch";
|
|
11
|
+
import { setupFs } from "@ricsam/isolate-fs";
|
|
12
|
+
import { setupCore as setupCore2 } from "@ricsam/isolate-core";
|
|
13
|
+
import { setupConsole as setupConsole2 } from "@ricsam/isolate-console";
|
|
14
|
+
import { setupCrypto as setupCrypto2 } from "@ricsam/isolate-crypto";
|
|
15
|
+
import { setupEncoding as setupEncoding2 } from "@ricsam/isolate-encoding";
|
|
16
|
+
import { setupFetch as setupFetch2 } from "@ricsam/isolate-fetch";
|
|
17
|
+
import { setupFs as setupFs2, createNodeFileSystemHandler } from "@ricsam/isolate-fs";
|
|
18
|
+
import { setupPath as setupPath2 } from "@ricsam/isolate-path";
|
|
19
|
+
import { setupTimers as setupTimers2 } from "@ricsam/isolate-timers";
|
|
20
|
+
async function createRuntime(options) {
|
|
21
|
+
const opts = options ?? {};
|
|
22
|
+
const isolate = new ivm.Isolate({
|
|
23
|
+
memoryLimit: opts.memoryLimit
|
|
24
|
+
});
|
|
25
|
+
const context = await isolate.createContext();
|
|
26
|
+
const handles = {};
|
|
27
|
+
handles.core = await setupCore(context);
|
|
28
|
+
handles.console = await setupConsole(context, opts.console);
|
|
29
|
+
handles.encoding = await setupEncoding(context);
|
|
30
|
+
handles.timers = await setupTimers(context);
|
|
31
|
+
handles.path = await setupPath(context);
|
|
32
|
+
handles.crypto = await setupCrypto(context);
|
|
33
|
+
handles.fetch = await setupFetch(context, opts.fetch);
|
|
34
|
+
if (opts.fs) {
|
|
35
|
+
handles.fs = await setupFs(context, opts.fs);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
isolate,
|
|
39
|
+
context,
|
|
40
|
+
fetch: handles.fetch,
|
|
41
|
+
async tick(ms) {
|
|
42
|
+
await handles.timers.tick(ms);
|
|
43
|
+
},
|
|
44
|
+
dispose() {
|
|
45
|
+
handles.fs?.dispose();
|
|
46
|
+
handles.fetch?.dispose();
|
|
47
|
+
handles.crypto?.dispose();
|
|
48
|
+
handles.path?.dispose();
|
|
49
|
+
handles.timers?.dispose();
|
|
50
|
+
handles.encoding?.dispose();
|
|
51
|
+
handles.console?.dispose();
|
|
52
|
+
handles.core?.dispose();
|
|
53
|
+
context.release();
|
|
54
|
+
isolate.dispose();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
setupTimers2 as setupTimers,
|
|
60
|
+
setupPath2 as setupPath,
|
|
61
|
+
setupFs2 as setupFs,
|
|
62
|
+
setupFetch2 as setupFetch,
|
|
63
|
+
setupEncoding2 as setupEncoding,
|
|
64
|
+
setupCrypto2 as setupCrypto,
|
|
65
|
+
setupCore2 as setupCore,
|
|
66
|
+
setupConsole2 as setupConsole,
|
|
67
|
+
createRuntime,
|
|
68
|
+
createNodeFileSystemHandler
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
//# debugId=FDBFD94222E0044364756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import 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\nexport interface RuntimeOptions {\n /** Isolate memory limit in MB */\n memoryLimit?: number;\n /** Console options */\n console?: ConsoleOptions;\n /** Fetch options */\n fetch?: FetchOptions;\n /** File system options (optional - fs only set up if provided) */\n fs?: FsOptions;\n}\n\nexport interface RuntimeHandle {\n /** The isolate instance */\n readonly isolate: ivm.Isolate;\n /** The context instance */\n readonly context: ivm.Context;\n /** The fetch handle for serve() and WebSocket dispatching */\n readonly fetch: FetchHandle;\n /** Process pending timers */\n tick(ms?: number): Promise<void>;\n /** Dispose all resources */\n dispose(): void;\n}\n\n/**\n * Create a fully configured isolated-vm runtime\n *\n * Sets up all WHATWG APIs: fetch, fs, console, crypto, encoding, timers\n *\n * @example\n * const runtime = await createRuntime({\n * console: {\n * onLog: (level, ...args) => console.log(`[${level}]`, ...args)\n * },\n * fetch: {\n * onFetch: async (request) => fetch(request)\n * }\n * });\n *\n * await runtime.context.eval(`\n * console.log(\"Hello from sandbox!\");\n * const response = await fetch(\"https://example.com\");\n * `);\n *\n * runtime.dispose();\n */\nexport async function createRuntime(\n options?: RuntimeOptions\n): Promise<RuntimeHandle> {\n const opts = options ?? {};\n\n // Create isolate with optional memory limit\n const isolate = new ivm.Isolate({\n memoryLimit: opts.memoryLimit,\n });\n const context = await isolate.createContext();\n\n // Store all handles for disposal\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 // Setup all APIs in order\n // Core must be first as it provides Blob, File, streams, URL, etc.\n handles.core = await setupCore(context);\n\n // Console\n handles.console = await setupConsole(context, opts.console);\n\n // Encoding (btoa/atob)\n handles.encoding = await setupEncoding(context);\n\n // Timers (setTimeout, setInterval)\n handles.timers = await setupTimers(context);\n\n // Path module\n handles.path = await setupPath(context);\n\n // Crypto (randomUUID, getRandomValues)\n handles.crypto = await setupCrypto(context);\n\n // Fetch API\n handles.fetch = await setupFetch(context, opts.fetch);\n\n // File system (only if handler provided)\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 async tick(ms?: number) {\n await handles.timers!.tick(ms);\n },\n dispose() {\n // Dispose all handles\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\n // Release context and dispose isolate\n context.release();\n isolate.dispose();\n },\n };\n}\n\n// Re-export all package types and functions\nexport { setupCore } from \"@ricsam/isolate-core\";\nexport type { CoreHandle, SetupCoreOptions } from \"@ricsam/isolate-core\";\n\nexport { setupConsole } from \"@ricsam/isolate-console\";\nexport type { ConsoleHandle, ConsoleOptions } from \"@ricsam/isolate-console\";\n\nexport { setupCrypto } from \"@ricsam/isolate-crypto\";\nexport type { CryptoHandle } from \"@ricsam/isolate-crypto\";\n\nexport { setupEncoding } from \"@ricsam/isolate-encoding\";\nexport type { EncodingHandle } from \"@ricsam/isolate-encoding\";\n\nexport { setupFetch } from \"@ricsam/isolate-fetch\";\nexport type { FetchHandle, FetchOptions, WebSocketCommand, UpgradeRequest } from \"@ricsam/isolate-fetch\";\n\nexport { setupFs, createNodeFileSystemHandler } from \"@ricsam/isolate-fs\";\nexport type { FsHandle, FsOptions, FileSystemHandler, NodeFileSystemHandlerOptions } from \"@ricsam/isolate-fs\";\n\nexport { setupPath } from \"@ricsam/isolate-path\";\nexport type { PathHandle } from \"@ricsam/isolate-path\";\n\nexport { setupTimers } from \"@ricsam/isolate-timers\";\nexport type { TimersHandle } from \"@ricsam/isolate-timers\";\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAqIA,sBAAS;AAGT,yBAAS;AAGT,wBAAS;AAGT,0BAAS;AAGT,uBAAS;AAGT,oBAAS;AAGT,sBAAS;AAGT,wBAAS;AAjGT,eAAsB,aAAa,CACjC,SACwB;AAAA,EACxB,MAAM,OAAO,WAAW,CAAC;AAAA,EAGzB,MAAM,UAAU,IAAI,IAAI,QAAQ;AAAA,IAC9B,aAAa,KAAK;AAAA,EACpB,CAAC;AAAA,EACD,MAAM,UAAU,MAAM,QAAQ,cAAc;AAAA,EAG5C,MAAM,UASF,CAAC;AAAA,EAIL,QAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,EAGtC,QAAQ,UAAU,MAAM,aAAa,SAAS,KAAK,OAAO;AAAA,EAG1D,QAAQ,WAAW,MAAM,cAAc,OAAO;AAAA,EAG9C,QAAQ,SAAS,MAAM,YAAY,OAAO;AAAA,EAG1C,QAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,EAGtC,QAAQ,SAAS,MAAM,YAAY,OAAO;AAAA,EAG1C,QAAQ,QAAQ,MAAM,WAAW,SAAS,KAAK,KAAK;AAAA,EAGpD,IAAI,KAAK,IAAI;AAAA,IACX,QAAQ,KAAK,MAAM,QAAQ,SAAS,KAAK,EAAE;AAAA,EAC7C;AAAA,EAEA,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,OAAO,QAAQ;AAAA,SACT,KAAI,CAAC,IAAa;AAAA,MACtB,MAAM,QAAQ,OAAQ,KAAK,EAAE;AAAA;AAAA,IAE/B,OAAO,GAAG;AAAA,MAER,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,MAGtB,QAAQ,QAAQ;AAAA,MAChB,QAAQ,QAAQ;AAAA;AAAA,EAEpB;AAAA;",
|
|
8
|
+
"debugId": "FDBFD94222E0044364756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import ivm from "isolated-vm";
|
|
2
|
+
import type { ConsoleOptions } from "@ricsam/isolate-console";
|
|
3
|
+
import type { FetchOptions, FetchHandle } from "@ricsam/isolate-fetch";
|
|
4
|
+
import type { FsOptions } from "@ricsam/isolate-fs";
|
|
5
|
+
export interface RuntimeOptions {
|
|
6
|
+
/** Isolate memory limit in MB */
|
|
7
|
+
memoryLimit?: number;
|
|
8
|
+
/** Console options */
|
|
9
|
+
console?: ConsoleOptions;
|
|
10
|
+
/** Fetch options */
|
|
11
|
+
fetch?: FetchOptions;
|
|
12
|
+
/** File system options (optional - fs only set up if provided) */
|
|
13
|
+
fs?: FsOptions;
|
|
14
|
+
}
|
|
15
|
+
export interface RuntimeHandle {
|
|
16
|
+
/** The isolate instance */
|
|
17
|
+
readonly isolate: ivm.Isolate;
|
|
18
|
+
/** The context instance */
|
|
19
|
+
readonly context: ivm.Context;
|
|
20
|
+
/** The fetch handle for serve() and WebSocket dispatching */
|
|
21
|
+
readonly fetch: FetchHandle;
|
|
22
|
+
/** Process pending timers */
|
|
23
|
+
tick(ms?: number): Promise<void>;
|
|
24
|
+
/** Dispose all resources */
|
|
25
|
+
dispose(): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create a fully configured isolated-vm runtime
|
|
29
|
+
*
|
|
30
|
+
* Sets up all WHATWG APIs: fetch, fs, console, crypto, encoding, timers
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const runtime = await createRuntime({
|
|
34
|
+
* console: {
|
|
35
|
+
* onLog: (level, ...args) => console.log(`[${level}]`, ...args)
|
|
36
|
+
* },
|
|
37
|
+
* fetch: {
|
|
38
|
+
* onFetch: async (request) => fetch(request)
|
|
39
|
+
* }
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* await runtime.context.eval(`
|
|
43
|
+
* console.log("Hello from sandbox!");
|
|
44
|
+
* const response = await fetch("https://example.com");
|
|
45
|
+
* `);
|
|
46
|
+
*
|
|
47
|
+
* runtime.dispose();
|
|
48
|
+
*/
|
|
49
|
+
export declare function createRuntime(options?: RuntimeOptions): Promise<RuntimeHandle>;
|
|
50
|
+
export { setupCore } from "@ricsam/isolate-core";
|
|
51
|
+
export type { CoreHandle, SetupCoreOptions } from "@ricsam/isolate-core";
|
|
52
|
+
export { setupConsole } from "@ricsam/isolate-console";
|
|
53
|
+
export type { ConsoleHandle, ConsoleOptions } from "@ricsam/isolate-console";
|
|
54
|
+
export { setupCrypto } from "@ricsam/isolate-crypto";
|
|
55
|
+
export type { CryptoHandle } from "@ricsam/isolate-crypto";
|
|
56
|
+
export { setupEncoding } from "@ricsam/isolate-encoding";
|
|
57
|
+
export type { EncodingHandle } from "@ricsam/isolate-encoding";
|
|
58
|
+
export { setupFetch } from "@ricsam/isolate-fetch";
|
|
59
|
+
export type { FetchHandle, FetchOptions, WebSocketCommand, UpgradeRequest } from "@ricsam/isolate-fetch";
|
|
60
|
+
export { setupFs, createNodeFileSystemHandler } from "@ricsam/isolate-fs";
|
|
61
|
+
export type { FsHandle, FsOptions, FileSystemHandler, NodeFileSystemHandlerOptions } from "@ricsam/isolate-fs";
|
|
62
|
+
export { setupPath } from "@ricsam/isolate-path";
|
|
63
|
+
export type { PathHandle } from "@ricsam/isolate-path";
|
|
64
|
+
export { setupTimers } from "@ricsam/isolate-timers";
|
|
65
|
+
export type { TimersHandle } from "@ricsam/isolate-timers";
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ricsam/isolate-runtime",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"types": "./src/index.ts",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"main": "./dist/cjs/index.cjs",
|
|
5
|
+
"types": "./dist/types/index.d.ts",
|
|
7
6
|
"exports": {
|
|
8
7
|
".": {
|
|
9
|
-
"
|
|
10
|
-
"
|
|
8
|
+
"types": "./dist/types/index.d.ts",
|
|
9
|
+
"require": "./dist/cjs/index.cjs",
|
|
10
|
+
"import": "./dist/mjs/index.mjs"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
@@ -25,12 +25,37 @@
|
|
|
25
25
|
"@ricsam/isolate-timers": "*",
|
|
26
26
|
"isolated-vm": "^6"
|
|
27
27
|
},
|
|
28
|
-
"devDependencies": {
|
|
29
|
-
"@ricsam/isolate-test-utils": "*",
|
|
30
|
-
"@types/node": "^24",
|
|
31
|
-
"typescript": "^5"
|
|
32
|
-
},
|
|
33
28
|
"peerDependencies": {
|
|
34
29
|
"isolated-vm": "^6"
|
|
35
|
-
}
|
|
36
|
-
|
|
30
|
+
},
|
|
31
|
+
"author": "Richard Samuelsson",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/ricsam/isolate.git"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/ricsam/isolate/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/ricsam/isolate#readme",
|
|
41
|
+
"keywords": [
|
|
42
|
+
"isolated-vm",
|
|
43
|
+
"sandbox",
|
|
44
|
+
"javascript",
|
|
45
|
+
"runtime",
|
|
46
|
+
"fetch",
|
|
47
|
+
"filesystem",
|
|
48
|
+
"streams",
|
|
49
|
+
"v8",
|
|
50
|
+
"isolate"
|
|
51
|
+
],
|
|
52
|
+
"description": "Complete isolated-vm V8 sandbox runtime with fetch, fs, and core bindings",
|
|
53
|
+
"module": "./dist/mjs/index.mjs",
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"files": [
|
|
58
|
+
"dist",
|
|
59
|
+
"README.md"
|
|
60
|
+
]
|
|
61
|
+
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# @ricsam/isolate-runtime
|
|
2
|
-
|
|
3
|
-
## 0.1.1
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- initial release
|
|
8
|
-
- Updated dependencies
|
|
9
|
-
- @ricsam/isolate-console@0.1.1
|
|
10
|
-
- @ricsam/isolate-core@0.1.1
|
|
11
|
-
- @ricsam/isolate-crypto@0.1.1
|
|
12
|
-
- @ricsam/isolate-encoding@0.1.1
|
|
13
|
-
- @ricsam/isolate-fetch@0.1.1
|
|
14
|
-
- @ricsam/isolate-fs@0.1.1
|
|
15
|
-
- @ricsam/isolate-timers@0.1.1
|
package/src/index.test.ts
DELETED
|
@@ -1,503 +0,0 @@
|
|
|
1
|
-
import { test, describe } from "node:test";
|
|
2
|
-
import assert from "node:assert";
|
|
3
|
-
import { createRuntime, type RuntimeHandle } from "./index.ts";
|
|
4
|
-
|
|
5
|
-
describe("@ricsam/isolate-runtime", () => {
|
|
6
|
-
describe("createRuntime", () => {
|
|
7
|
-
test("creates runtime with default options", async () => {
|
|
8
|
-
const runtime = await createRuntime();
|
|
9
|
-
try {
|
|
10
|
-
assert(runtime.isolate, "isolate should be defined");
|
|
11
|
-
assert(runtime.context, "context should be defined");
|
|
12
|
-
assert(typeof runtime.tick === "function", "tick should be a function");
|
|
13
|
-
assert(
|
|
14
|
-
typeof runtime.dispose === "function",
|
|
15
|
-
"dispose should be a function"
|
|
16
|
-
);
|
|
17
|
-
} finally {
|
|
18
|
-
runtime.dispose();
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test("runtime has all globals defined", async () => {
|
|
23
|
-
const runtime = await createRuntime();
|
|
24
|
-
try {
|
|
25
|
-
const result = await runtime.context.eval(`
|
|
26
|
-
JSON.stringify({
|
|
27
|
-
hasFetch: typeof fetch === 'function',
|
|
28
|
-
hasConsole: typeof console === 'object',
|
|
29
|
-
hasCrypto: typeof crypto === 'object',
|
|
30
|
-
hasSetTimeout: typeof setTimeout === 'function',
|
|
31
|
-
hasSetInterval: typeof setInterval === 'function',
|
|
32
|
-
hasClearTimeout: typeof clearTimeout === 'function',
|
|
33
|
-
hasClearInterval: typeof clearInterval === 'function',
|
|
34
|
-
hasPath: typeof path === 'object',
|
|
35
|
-
hasTextEncoder: typeof TextEncoder === 'function',
|
|
36
|
-
hasTextDecoder: typeof TextDecoder === 'function',
|
|
37
|
-
hasBlob: typeof Blob === 'function',
|
|
38
|
-
hasFile: typeof File === 'function',
|
|
39
|
-
hasURL: typeof URL === 'function',
|
|
40
|
-
hasURLSearchParams: typeof URLSearchParams === 'function',
|
|
41
|
-
hasHeaders: typeof Headers === 'function',
|
|
42
|
-
hasRequest: typeof Request === 'function',
|
|
43
|
-
hasResponse: typeof Response === 'function',
|
|
44
|
-
hasFormData: typeof FormData === 'function',
|
|
45
|
-
hasAbortController: typeof AbortController === 'function',
|
|
46
|
-
hasAbortSignal: typeof AbortSignal === 'function',
|
|
47
|
-
hasReadableStream: typeof ReadableStream === 'function',
|
|
48
|
-
hasBtoa: typeof btoa === 'function',
|
|
49
|
-
hasAtob: typeof atob === 'function',
|
|
50
|
-
})
|
|
51
|
-
`);
|
|
52
|
-
const globals = JSON.parse(result as string);
|
|
53
|
-
|
|
54
|
-
assert.strictEqual(globals.hasFetch, true, "fetch should be defined");
|
|
55
|
-
assert.strictEqual(
|
|
56
|
-
globals.hasConsole,
|
|
57
|
-
true,
|
|
58
|
-
"console should be defined"
|
|
59
|
-
);
|
|
60
|
-
assert.strictEqual(globals.hasCrypto, true, "crypto should be defined");
|
|
61
|
-
assert.strictEqual(
|
|
62
|
-
globals.hasSetTimeout,
|
|
63
|
-
true,
|
|
64
|
-
"setTimeout should be defined"
|
|
65
|
-
);
|
|
66
|
-
assert.strictEqual(
|
|
67
|
-
globals.hasSetInterval,
|
|
68
|
-
true,
|
|
69
|
-
"setInterval should be defined"
|
|
70
|
-
);
|
|
71
|
-
assert.strictEqual(
|
|
72
|
-
globals.hasClearTimeout,
|
|
73
|
-
true,
|
|
74
|
-
"clearTimeout should be defined"
|
|
75
|
-
);
|
|
76
|
-
assert.strictEqual(
|
|
77
|
-
globals.hasClearInterval,
|
|
78
|
-
true,
|
|
79
|
-
"clearInterval should be defined"
|
|
80
|
-
);
|
|
81
|
-
assert.strictEqual(globals.hasPath, true, "path should be defined");
|
|
82
|
-
assert.strictEqual(
|
|
83
|
-
globals.hasTextEncoder,
|
|
84
|
-
true,
|
|
85
|
-
"TextEncoder should be defined"
|
|
86
|
-
);
|
|
87
|
-
assert.strictEqual(
|
|
88
|
-
globals.hasTextDecoder,
|
|
89
|
-
true,
|
|
90
|
-
"TextDecoder should be defined"
|
|
91
|
-
);
|
|
92
|
-
assert.strictEqual(globals.hasBlob, true, "Blob should be defined");
|
|
93
|
-
assert.strictEqual(globals.hasFile, true, "File should be defined");
|
|
94
|
-
assert.strictEqual(globals.hasURL, true, "URL should be defined");
|
|
95
|
-
assert.strictEqual(
|
|
96
|
-
globals.hasURLSearchParams,
|
|
97
|
-
true,
|
|
98
|
-
"URLSearchParams should be defined"
|
|
99
|
-
);
|
|
100
|
-
assert.strictEqual(
|
|
101
|
-
globals.hasHeaders,
|
|
102
|
-
true,
|
|
103
|
-
"Headers should be defined"
|
|
104
|
-
);
|
|
105
|
-
assert.strictEqual(
|
|
106
|
-
globals.hasRequest,
|
|
107
|
-
true,
|
|
108
|
-
"Request should be defined"
|
|
109
|
-
);
|
|
110
|
-
assert.strictEqual(
|
|
111
|
-
globals.hasResponse,
|
|
112
|
-
true,
|
|
113
|
-
"Response should be defined"
|
|
114
|
-
);
|
|
115
|
-
assert.strictEqual(
|
|
116
|
-
globals.hasFormData,
|
|
117
|
-
true,
|
|
118
|
-
"FormData should be defined"
|
|
119
|
-
);
|
|
120
|
-
assert.strictEqual(
|
|
121
|
-
globals.hasAbortController,
|
|
122
|
-
true,
|
|
123
|
-
"AbortController should be defined"
|
|
124
|
-
);
|
|
125
|
-
assert.strictEqual(
|
|
126
|
-
globals.hasAbortSignal,
|
|
127
|
-
true,
|
|
128
|
-
"AbortSignal should be defined"
|
|
129
|
-
);
|
|
130
|
-
assert.strictEqual(
|
|
131
|
-
globals.hasReadableStream,
|
|
132
|
-
true,
|
|
133
|
-
"ReadableStream should be defined"
|
|
134
|
-
);
|
|
135
|
-
assert.strictEqual(globals.hasBtoa, true, "btoa should be defined");
|
|
136
|
-
assert.strictEqual(globals.hasAtob, true, "atob should be defined");
|
|
137
|
-
} finally {
|
|
138
|
-
runtime.dispose();
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test("dispose cleans up resources", async () => {
|
|
143
|
-
const runtime = await createRuntime();
|
|
144
|
-
runtime.dispose();
|
|
145
|
-
|
|
146
|
-
// After dispose, the isolate should be disposed
|
|
147
|
-
// Attempting to use it should throw
|
|
148
|
-
assert.throws(
|
|
149
|
-
() => {
|
|
150
|
-
runtime.isolate.createContextSync();
|
|
151
|
-
},
|
|
152
|
-
/disposed/i,
|
|
153
|
-
"isolate should be disposed"
|
|
154
|
-
);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test("accepts memory limit option", async () => {
|
|
158
|
-
const runtime = await createRuntime({
|
|
159
|
-
memoryLimit: 128,
|
|
160
|
-
});
|
|
161
|
-
try {
|
|
162
|
-
assert(runtime.isolate, "isolate should be created with memory limit");
|
|
163
|
-
} finally {
|
|
164
|
-
runtime.dispose();
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
describe("console integration", () => {
|
|
170
|
-
test("console.log is captured", async () => {
|
|
171
|
-
const logs: Array<{ level: string; args: unknown[] }> = [];
|
|
172
|
-
|
|
173
|
-
const runtime = await createRuntime({
|
|
174
|
-
console: {
|
|
175
|
-
onLog: (level, ...args) => {
|
|
176
|
-
logs.push({ level, args });
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
try {
|
|
182
|
-
await runtime.context.eval(`
|
|
183
|
-
console.log("hello", "world");
|
|
184
|
-
console.warn("warning message");
|
|
185
|
-
console.error("error message");
|
|
186
|
-
`);
|
|
187
|
-
|
|
188
|
-
assert.strictEqual(logs.length, 3, "should have captured 3 logs");
|
|
189
|
-
assert.strictEqual(logs[0].level, "log");
|
|
190
|
-
assert.deepStrictEqual(logs[0].args, ["hello", "world"]);
|
|
191
|
-
assert.strictEqual(logs[1].level, "warn");
|
|
192
|
-
assert.deepStrictEqual(logs[1].args, ["warning message"]);
|
|
193
|
-
assert.strictEqual(logs[2].level, "error");
|
|
194
|
-
assert.deepStrictEqual(logs[2].args, ["error message"]);
|
|
195
|
-
} finally {
|
|
196
|
-
runtime.dispose();
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
describe("fetch integration", () => {
|
|
202
|
-
test("fetch calls onFetch handler", async () => {
|
|
203
|
-
let capturedRequest: Request | null = null;
|
|
204
|
-
|
|
205
|
-
const runtime = await createRuntime({
|
|
206
|
-
fetch: {
|
|
207
|
-
onFetch: async (request) => {
|
|
208
|
-
capturedRequest = request;
|
|
209
|
-
return new Response(JSON.stringify({ message: "mocked" }), {
|
|
210
|
-
status: 200,
|
|
211
|
-
headers: { "Content-Type": "application/json" },
|
|
212
|
-
});
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
try {
|
|
218
|
-
const result = await runtime.context.eval(
|
|
219
|
-
`
|
|
220
|
-
(async () => {
|
|
221
|
-
const response = await fetch("https://example.com/api", {
|
|
222
|
-
method: "POST",
|
|
223
|
-
headers: { "X-Custom": "header" },
|
|
224
|
-
body: "test body"
|
|
225
|
-
});
|
|
226
|
-
return JSON.stringify({
|
|
227
|
-
status: response.status,
|
|
228
|
-
body: await response.json()
|
|
229
|
-
});
|
|
230
|
-
})()
|
|
231
|
-
`,
|
|
232
|
-
{ promise: true }
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
const data = JSON.parse(result as string);
|
|
236
|
-
assert.strictEqual(data.status, 200);
|
|
237
|
-
assert.deepStrictEqual(data.body, { message: "mocked" });
|
|
238
|
-
|
|
239
|
-
// Verify the request was captured correctly
|
|
240
|
-
assert(capturedRequest, "request should be captured");
|
|
241
|
-
assert.strictEqual(capturedRequest!.url, "https://example.com/api");
|
|
242
|
-
assert.strictEqual(capturedRequest!.method, "POST");
|
|
243
|
-
assert.strictEqual(capturedRequest!.headers.get("X-Custom"), "header");
|
|
244
|
-
} finally {
|
|
245
|
-
runtime.dispose();
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
describe("timers integration", () => {
|
|
251
|
-
test("setTimeout works with tick()", async () => {
|
|
252
|
-
const runtime = await createRuntime();
|
|
253
|
-
|
|
254
|
-
try {
|
|
255
|
-
// Set up a timeout that modifies a global variable
|
|
256
|
-
await runtime.context.eval(`
|
|
257
|
-
globalThis.timerFired = false;
|
|
258
|
-
globalThis.timerValue = 0;
|
|
259
|
-
setTimeout(() => {
|
|
260
|
-
globalThis.timerFired = true;
|
|
261
|
-
globalThis.timerValue = 42;
|
|
262
|
-
}, 100);
|
|
263
|
-
`);
|
|
264
|
-
|
|
265
|
-
// Before tick, timer should not have fired
|
|
266
|
-
let result = await runtime.context.eval(`globalThis.timerFired`);
|
|
267
|
-
assert.strictEqual(result, false, "timer should not fire before tick");
|
|
268
|
-
|
|
269
|
-
// Tick forward 50ms - still not enough
|
|
270
|
-
await runtime.tick(50);
|
|
271
|
-
result = await runtime.context.eval(`globalThis.timerFired`);
|
|
272
|
-
assert.strictEqual(result, false, "timer should not fire at 50ms");
|
|
273
|
-
|
|
274
|
-
// Tick forward another 50ms (total 100ms) - now it should fire
|
|
275
|
-
await runtime.tick(50);
|
|
276
|
-
result = await runtime.context.eval(`globalThis.timerFired`);
|
|
277
|
-
assert.strictEqual(result, true, "timer should fire at 100ms");
|
|
278
|
-
|
|
279
|
-
result = await runtime.context.eval(`globalThis.timerValue`);
|
|
280
|
-
assert.strictEqual(result, 42, "timer should have set value");
|
|
281
|
-
} finally {
|
|
282
|
-
runtime.dispose();
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
test("setInterval works with tick()", async () => {
|
|
287
|
-
const runtime = await createRuntime();
|
|
288
|
-
|
|
289
|
-
try {
|
|
290
|
-
await runtime.context.eval(`
|
|
291
|
-
globalThis.intervalCount = 0;
|
|
292
|
-
setInterval(() => {
|
|
293
|
-
globalThis.intervalCount++;
|
|
294
|
-
}, 100);
|
|
295
|
-
`);
|
|
296
|
-
|
|
297
|
-
// Tick incrementally - interval fires at each 100ms boundary
|
|
298
|
-
await runtime.tick(100); // t=100ms, first fire
|
|
299
|
-
let count = await runtime.context.eval(`globalThis.intervalCount`);
|
|
300
|
-
assert.strictEqual(count, 1, "interval should fire once at 100ms");
|
|
301
|
-
|
|
302
|
-
await runtime.tick(100); // t=200ms, second fire
|
|
303
|
-
count = await runtime.context.eval(`globalThis.intervalCount`);
|
|
304
|
-
assert.strictEqual(count, 2, "interval should fire twice at 200ms");
|
|
305
|
-
} finally {
|
|
306
|
-
runtime.dispose();
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
describe("crypto integration", () => {
|
|
312
|
-
test("crypto.randomUUID generates valid UUIDs", async () => {
|
|
313
|
-
const runtime = await createRuntime();
|
|
314
|
-
|
|
315
|
-
try {
|
|
316
|
-
const uuid = (await runtime.context.eval(
|
|
317
|
-
`crypto.randomUUID()`
|
|
318
|
-
)) as string;
|
|
319
|
-
assert.match(
|
|
320
|
-
uuid,
|
|
321
|
-
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
|
|
322
|
-
"should generate valid UUID v4"
|
|
323
|
-
);
|
|
324
|
-
} finally {
|
|
325
|
-
runtime.dispose();
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
describe("path integration", () => {
|
|
331
|
-
test("path.join works correctly", async () => {
|
|
332
|
-
const runtime = await createRuntime();
|
|
333
|
-
|
|
334
|
-
try {
|
|
335
|
-
const result = await runtime.context.eval(`path.join('a', 'b', 'c')`);
|
|
336
|
-
assert.strictEqual(result, "a/b/c");
|
|
337
|
-
} finally {
|
|
338
|
-
runtime.dispose();
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
describe("encoding integration", () => {
|
|
344
|
-
test("btoa and atob work correctly", async () => {
|
|
345
|
-
const runtime = await createRuntime();
|
|
346
|
-
|
|
347
|
-
try {
|
|
348
|
-
const encoded = await runtime.context.eval(`btoa('hello')`);
|
|
349
|
-
assert.strictEqual(encoded, "aGVsbG8=");
|
|
350
|
-
|
|
351
|
-
const decoded = await runtime.context.eval(`atob('aGVsbG8=')`);
|
|
352
|
-
assert.strictEqual(decoded, "hello");
|
|
353
|
-
} finally {
|
|
354
|
-
runtime.dispose();
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
describe("GC disposal", () => {
|
|
360
|
-
test("resources are cleaned up on dispose", async () => {
|
|
361
|
-
const runtime = await createRuntime();
|
|
362
|
-
|
|
363
|
-
// Create some resources
|
|
364
|
-
await runtime.context.eval(`
|
|
365
|
-
const blob = new Blob(["test"]);
|
|
366
|
-
const url = new URL("https://example.com");
|
|
367
|
-
setTimeout(() => {}, 1000);
|
|
368
|
-
`);
|
|
369
|
-
|
|
370
|
-
// Dispose should not throw
|
|
371
|
-
assert.doesNotThrow(() => {
|
|
372
|
-
runtime.dispose();
|
|
373
|
-
}, "dispose should not throw");
|
|
374
|
-
|
|
375
|
-
// After dispose, attempting to use the context should fail
|
|
376
|
-
await assert.rejects(
|
|
377
|
-
async () => {
|
|
378
|
-
await runtime.context.eval(`1 + 1`);
|
|
379
|
-
},
|
|
380
|
-
/released|disposed/i,
|
|
381
|
-
"context should be released after dispose"
|
|
382
|
-
);
|
|
383
|
-
});
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
describe("fs integration", () => {
|
|
387
|
-
test("getDirectory works when handler provided", async () => {
|
|
388
|
-
const files = new Map<
|
|
389
|
-
string,
|
|
390
|
-
{ data: Uint8Array; lastModified: number; type: string }
|
|
391
|
-
>();
|
|
392
|
-
const directories = new Set<string>(["/"]); // Root directory exists
|
|
393
|
-
|
|
394
|
-
const createHandler = () => ({
|
|
395
|
-
async getFileHandle(path: string, options?: { create?: boolean }) {
|
|
396
|
-
if (!files.has(path) && !options?.create) {
|
|
397
|
-
throw new Error("[NotFoundError]File not found");
|
|
398
|
-
}
|
|
399
|
-
if (!files.has(path) && options?.create) {
|
|
400
|
-
files.set(path, {
|
|
401
|
-
data: new Uint8Array(0),
|
|
402
|
-
lastModified: Date.now(),
|
|
403
|
-
type: "",
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
},
|
|
407
|
-
async getDirectoryHandle(path: string, options?: { create?: boolean }) {
|
|
408
|
-
if (!directories.has(path) && !options?.create) {
|
|
409
|
-
throw new Error("[NotFoundError]Directory not found");
|
|
410
|
-
}
|
|
411
|
-
if (options?.create) {
|
|
412
|
-
directories.add(path);
|
|
413
|
-
}
|
|
414
|
-
},
|
|
415
|
-
async removeEntry(path: string) {
|
|
416
|
-
files.delete(path);
|
|
417
|
-
directories.delete(path);
|
|
418
|
-
},
|
|
419
|
-
async readDirectory(path: string) {
|
|
420
|
-
const entries: Array<{ name: string; kind: "file" | "directory" }> =
|
|
421
|
-
[];
|
|
422
|
-
for (const filePath of files.keys()) {
|
|
423
|
-
const dir = filePath.substring(0, filePath.lastIndexOf("/")) || "/";
|
|
424
|
-
if (dir === path) {
|
|
425
|
-
entries.push({
|
|
426
|
-
name: filePath.substring(filePath.lastIndexOf("/") + 1),
|
|
427
|
-
kind: "file",
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
for (const dirPath of directories) {
|
|
432
|
-
if (dirPath !== path && dirPath.startsWith(path)) {
|
|
433
|
-
const relativePath = dirPath.substring(path.length);
|
|
434
|
-
const parts = relativePath.split("/").filter(Boolean);
|
|
435
|
-
if (parts.length === 1) {
|
|
436
|
-
entries.push({ name: parts[0], kind: "directory" });
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
return entries;
|
|
441
|
-
},
|
|
442
|
-
async readFile(path: string) {
|
|
443
|
-
const file = files.get(path);
|
|
444
|
-
if (!file) {
|
|
445
|
-
throw new Error("[NotFoundError]File not found");
|
|
446
|
-
}
|
|
447
|
-
return {
|
|
448
|
-
data: file.data,
|
|
449
|
-
size: file.data.length,
|
|
450
|
-
lastModified: file.lastModified,
|
|
451
|
-
type: file.type,
|
|
452
|
-
};
|
|
453
|
-
},
|
|
454
|
-
async writeFile(path: string, data: Uint8Array) {
|
|
455
|
-
const existing = files.get(path);
|
|
456
|
-
files.set(path, {
|
|
457
|
-
data,
|
|
458
|
-
lastModified: Date.now(),
|
|
459
|
-
type: existing?.type ?? "",
|
|
460
|
-
});
|
|
461
|
-
},
|
|
462
|
-
async truncateFile(path: string, size: number) {
|
|
463
|
-
const file = files.get(path);
|
|
464
|
-
if (file) {
|
|
465
|
-
file.data = file.data.slice(0, size);
|
|
466
|
-
}
|
|
467
|
-
},
|
|
468
|
-
async getFileMetadata(path: string) {
|
|
469
|
-
const file = files.get(path);
|
|
470
|
-
if (!file) {
|
|
471
|
-
throw new Error("[NotFoundError]File not found");
|
|
472
|
-
}
|
|
473
|
-
return {
|
|
474
|
-
size: file.data.length,
|
|
475
|
-
lastModified: file.lastModified,
|
|
476
|
-
type: file.type,
|
|
477
|
-
};
|
|
478
|
-
},
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
const runtime = await createRuntime({
|
|
482
|
-
fs: {
|
|
483
|
-
getDirectory: async () => createHandler(),
|
|
484
|
-
},
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
try {
|
|
488
|
-
const result = await runtime.context.eval(
|
|
489
|
-
`
|
|
490
|
-
(async () => {
|
|
491
|
-
const root = await getDirectory("/");
|
|
492
|
-
return root.kind;
|
|
493
|
-
})()
|
|
494
|
-
`,
|
|
495
|
-
{ promise: true }
|
|
496
|
-
);
|
|
497
|
-
assert.strictEqual(result, "directory");
|
|
498
|
-
} finally {
|
|
499
|
-
runtime.dispose();
|
|
500
|
-
}
|
|
501
|
-
});
|
|
502
|
-
});
|
|
503
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import ivm from "isolated-vm";
|
|
2
|
-
import { setupCore } from "@ricsam/isolate-core";
|
|
3
|
-
import { setupConsole } from "@ricsam/isolate-console";
|
|
4
|
-
import { setupEncoding } from "@ricsam/isolate-encoding";
|
|
5
|
-
import { setupTimers } from "@ricsam/isolate-timers";
|
|
6
|
-
import { setupPath } from "@ricsam/isolate-path";
|
|
7
|
-
import { setupCrypto } from "@ricsam/isolate-crypto";
|
|
8
|
-
import { setupFetch } from "@ricsam/isolate-fetch";
|
|
9
|
-
import { setupFs } from "@ricsam/isolate-fs";
|
|
10
|
-
|
|
11
|
-
import type { ConsoleOptions, ConsoleHandle } from "@ricsam/isolate-console";
|
|
12
|
-
import type { FetchOptions, FetchHandle } from "@ricsam/isolate-fetch";
|
|
13
|
-
import type { FsOptions, FsHandle } from "@ricsam/isolate-fs";
|
|
14
|
-
import type { CoreHandle } from "@ricsam/isolate-core";
|
|
15
|
-
import type { EncodingHandle } from "@ricsam/isolate-encoding";
|
|
16
|
-
import type { TimersHandle } from "@ricsam/isolate-timers";
|
|
17
|
-
import type { PathHandle } from "@ricsam/isolate-path";
|
|
18
|
-
import type { CryptoHandle } from "@ricsam/isolate-crypto";
|
|
19
|
-
|
|
20
|
-
export interface RuntimeOptions {
|
|
21
|
-
/** Isolate memory limit in MB */
|
|
22
|
-
memoryLimit?: number;
|
|
23
|
-
/** Console options */
|
|
24
|
-
console?: ConsoleOptions;
|
|
25
|
-
/** Fetch options */
|
|
26
|
-
fetch?: FetchOptions;
|
|
27
|
-
/** File system options (optional - fs only set up if provided) */
|
|
28
|
-
fs?: FsOptions;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface RuntimeHandle {
|
|
32
|
-
/** The isolate instance */
|
|
33
|
-
readonly isolate: ivm.Isolate;
|
|
34
|
-
/** The context instance */
|
|
35
|
-
readonly context: ivm.Context;
|
|
36
|
-
/** The fetch handle for serve() and WebSocket dispatching */
|
|
37
|
-
readonly fetch: FetchHandle;
|
|
38
|
-
/** Process pending timers */
|
|
39
|
-
tick(ms?: number): Promise<void>;
|
|
40
|
-
/** Dispose all resources */
|
|
41
|
-
dispose(): void;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Create a fully configured isolated-vm runtime
|
|
46
|
-
*
|
|
47
|
-
* Sets up all WHATWG APIs: fetch, fs, console, crypto, encoding, timers
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* const runtime = await createRuntime({
|
|
51
|
-
* console: {
|
|
52
|
-
* onLog: (level, ...args) => console.log(`[${level}]`, ...args)
|
|
53
|
-
* },
|
|
54
|
-
* fetch: {
|
|
55
|
-
* onFetch: async (request) => fetch(request)
|
|
56
|
-
* }
|
|
57
|
-
* });
|
|
58
|
-
*
|
|
59
|
-
* await runtime.context.eval(`
|
|
60
|
-
* console.log("Hello from sandbox!");
|
|
61
|
-
* const response = await fetch("https://example.com");
|
|
62
|
-
* `);
|
|
63
|
-
*
|
|
64
|
-
* runtime.dispose();
|
|
65
|
-
*/
|
|
66
|
-
export async function createRuntime(
|
|
67
|
-
options?: RuntimeOptions
|
|
68
|
-
): Promise<RuntimeHandle> {
|
|
69
|
-
const opts = options ?? {};
|
|
70
|
-
|
|
71
|
-
// Create isolate with optional memory limit
|
|
72
|
-
const isolate = new ivm.Isolate({
|
|
73
|
-
memoryLimit: opts.memoryLimit,
|
|
74
|
-
});
|
|
75
|
-
const context = await isolate.createContext();
|
|
76
|
-
|
|
77
|
-
// Store all handles for disposal
|
|
78
|
-
const handles: {
|
|
79
|
-
core?: CoreHandle;
|
|
80
|
-
console?: ConsoleHandle;
|
|
81
|
-
encoding?: EncodingHandle;
|
|
82
|
-
timers?: TimersHandle;
|
|
83
|
-
path?: PathHandle;
|
|
84
|
-
crypto?: CryptoHandle;
|
|
85
|
-
fetch?: FetchHandle;
|
|
86
|
-
fs?: FsHandle;
|
|
87
|
-
} = {};
|
|
88
|
-
|
|
89
|
-
// Setup all APIs in order
|
|
90
|
-
// Core must be first as it provides Blob, File, streams, URL, etc.
|
|
91
|
-
handles.core = await setupCore(context);
|
|
92
|
-
|
|
93
|
-
// Console
|
|
94
|
-
handles.console = await setupConsole(context, opts.console);
|
|
95
|
-
|
|
96
|
-
// Encoding (btoa/atob)
|
|
97
|
-
handles.encoding = await setupEncoding(context);
|
|
98
|
-
|
|
99
|
-
// Timers (setTimeout, setInterval)
|
|
100
|
-
handles.timers = await setupTimers(context);
|
|
101
|
-
|
|
102
|
-
// Path module
|
|
103
|
-
handles.path = await setupPath(context);
|
|
104
|
-
|
|
105
|
-
// Crypto (randomUUID, getRandomValues)
|
|
106
|
-
handles.crypto = await setupCrypto(context);
|
|
107
|
-
|
|
108
|
-
// Fetch API
|
|
109
|
-
handles.fetch = await setupFetch(context, opts.fetch);
|
|
110
|
-
|
|
111
|
-
// File system (only if handler provided)
|
|
112
|
-
if (opts.fs) {
|
|
113
|
-
handles.fs = await setupFs(context, opts.fs);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return {
|
|
117
|
-
isolate,
|
|
118
|
-
context,
|
|
119
|
-
fetch: handles.fetch!,
|
|
120
|
-
async tick(ms?: number) {
|
|
121
|
-
await handles.timers!.tick(ms);
|
|
122
|
-
},
|
|
123
|
-
dispose() {
|
|
124
|
-
// Dispose all handles
|
|
125
|
-
handles.fs?.dispose();
|
|
126
|
-
handles.fetch?.dispose();
|
|
127
|
-
handles.crypto?.dispose();
|
|
128
|
-
handles.path?.dispose();
|
|
129
|
-
handles.timers?.dispose();
|
|
130
|
-
handles.encoding?.dispose();
|
|
131
|
-
handles.console?.dispose();
|
|
132
|
-
handles.core?.dispose();
|
|
133
|
-
|
|
134
|
-
// Release context and dispose isolate
|
|
135
|
-
context.release();
|
|
136
|
-
isolate.dispose();
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Re-export all package types and functions
|
|
142
|
-
export { setupCore } from "@ricsam/isolate-core";
|
|
143
|
-
export type { CoreHandle, SetupCoreOptions } from "@ricsam/isolate-core";
|
|
144
|
-
|
|
145
|
-
export { setupConsole } from "@ricsam/isolate-console";
|
|
146
|
-
export type { ConsoleHandle, ConsoleOptions } from "@ricsam/isolate-console";
|
|
147
|
-
|
|
148
|
-
export { setupCrypto } from "@ricsam/isolate-crypto";
|
|
149
|
-
export type { CryptoHandle } from "@ricsam/isolate-crypto";
|
|
150
|
-
|
|
151
|
-
export { setupEncoding } from "@ricsam/isolate-encoding";
|
|
152
|
-
export type { EncodingHandle } from "@ricsam/isolate-encoding";
|
|
153
|
-
|
|
154
|
-
export { setupFetch } from "@ricsam/isolate-fetch";
|
|
155
|
-
export type { FetchHandle, FetchOptions, WebSocketCommand, UpgradeRequest } from "@ricsam/isolate-fetch";
|
|
156
|
-
|
|
157
|
-
export { setupFs, createNodeFileSystemHandler } from "@ricsam/isolate-fs";
|
|
158
|
-
export type { FsHandle, FsOptions, FileSystemHandler, NodeFileSystemHandlerOptions } from "@ricsam/isolate-fs";
|
|
159
|
-
|
|
160
|
-
export { setupPath } from "@ricsam/isolate-path";
|
|
161
|
-
export type { PathHandle } from "@ricsam/isolate-path";
|
|
162
|
-
|
|
163
|
-
export { setupTimers } from "@ricsam/isolate-timers";
|
|
164
|
-
export type { TimersHandle } from "@ricsam/isolate-timers";
|