@ricsam/isolate-runtime 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 ADDED
@@ -0,0 +1,15 @@
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/package.json CHANGED
@@ -1,10 +1,36 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-runtime",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for @ricsam/isolate-runtime",
5
- "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./src/index.ts",
10
+ "types": "./src/index.ts"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "test": "node --test --experimental-strip-types 'src/**/*.test.ts'",
16
+ "typecheck": "tsc --noEmit"
17
+ },
18
+ "dependencies": {
19
+ "@ricsam/isolate-core": "*",
20
+ "@ricsam/isolate-console": "*",
21
+ "@ricsam/isolate-crypto": "*",
22
+ "@ricsam/isolate-encoding": "*",
23
+ "@ricsam/isolate-fetch": "*",
24
+ "@ricsam/isolate-fs": "*",
25
+ "@ricsam/isolate-timers": "*",
26
+ "isolated-vm": "^6"
27
+ },
28
+ "devDependencies": {
29
+ "@ricsam/isolate-test-utils": "*",
30
+ "@types/node": "^24",
31
+ "typescript": "^5"
32
+ },
33
+ "peerDependencies": {
34
+ "isolated-vm": "^6"
35
+ }
10
36
  }
@@ -0,0 +1,503 @@
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 ADDED
@@ -0,0 +1,164 @@
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";
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src"
5
+ },
6
+ "include": ["src/**/*"],
7
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
8
+ }
package/README.md DELETED
@@ -1,45 +0,0 @@
1
- # @ricsam/isolate-runtime
2
-
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
4
-
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
6
-
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
8
-
9
- ## Purpose
10
-
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@ricsam/isolate-runtime`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
15
-
16
- ## What is OIDC Trusted Publishing?
17
-
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
19
-
20
- ## Setup Instructions
21
-
22
- To properly configure OIDC trusted publishing for this package:
23
-
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
28
-
29
- ## DO NOT USE THIS PACKAGE
30
-
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
36
-
37
- ## More Information
38
-
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
42
-
43
- ---
44
-
45
- **Maintained for OIDC setup purposes only**