@ricsam/isolate 0.1.3 → 0.1.5
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 +25 -2
- package/dist/cjs/internal/async-context/index.cjs +140 -0
- package/dist/cjs/internal/async-context/index.cjs.map +10 -0
- package/dist/cjs/internal/client/connection.cjs +175 -123
- package/dist/cjs/internal/client/connection.cjs.map +3 -3
- package/dist/cjs/internal/console/index.cjs +2 -2
- package/dist/cjs/internal/console/index.cjs.map +2 -2
- package/dist/cjs/internal/core/index.cjs +13 -5
- package/dist/cjs/internal/core/index.cjs.map +3 -3
- package/dist/cjs/internal/crypto/index.cjs +2 -2
- package/dist/cjs/internal/crypto/index.cjs.map +2 -2
- package/dist/cjs/internal/daemon/connection.cjs +77 -12
- package/dist/cjs/internal/daemon/connection.cjs.map +3 -3
- package/dist/cjs/internal/encoding/index.cjs.map +1 -1
- package/dist/cjs/internal/fetch/index.cjs +80 -18
- package/dist/cjs/internal/fetch/index.cjs.map +3 -3
- package/dist/cjs/internal/fetch/stream-state.cjs.map +1 -1
- package/dist/cjs/internal/fs/index.cjs +2 -2
- package/dist/cjs/internal/fs/index.cjs.map +2 -2
- package/dist/cjs/internal/module-loader/bundle.cjs +225 -8
- package/dist/cjs/internal/module-loader/bundle.cjs.map +3 -3
- package/dist/cjs/internal/path/index.cjs.map +1 -1
- package/dist/cjs/internal/playwright/index.cjs +2 -2
- package/dist/cjs/internal/playwright/index.cjs.map +2 -2
- package/dist/cjs/internal/runtime/index.cjs +78 -6
- package/dist/cjs/internal/runtime/index.cjs.map +3 -3
- package/dist/cjs/internal/test-environment/index.cjs +2 -2
- package/dist/cjs/internal/test-environment/index.cjs.map +2 -2
- package/dist/cjs/internal/timers/index.cjs +9 -4
- package/dist/cjs/internal/timers/index.cjs.map +3 -3
- package/dist/cjs/internal/typecheck/isolate-types.cjs +36 -1
- package/dist/cjs/internal/typecheck/isolate-types.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/internal/async-context/index.mjs +100 -0
- package/dist/mjs/internal/async-context/index.mjs.map +10 -0
- package/dist/mjs/internal/client/connection.mjs +176 -123
- package/dist/mjs/internal/client/connection.mjs.map +3 -3
- package/dist/mjs/internal/console/index.mjs +2 -2
- package/dist/mjs/internal/console/index.mjs.map +2 -2
- package/dist/mjs/internal/core/index.mjs +13 -5
- package/dist/mjs/internal/core/index.mjs.map +3 -3
- package/dist/mjs/internal/crypto/index.mjs +2 -2
- package/dist/mjs/internal/crypto/index.mjs.map +2 -2
- package/dist/mjs/internal/daemon/connection.mjs +77 -12
- package/dist/mjs/internal/daemon/connection.mjs.map +3 -3
- package/dist/mjs/internal/encoding/index.mjs.map +1 -1
- package/dist/mjs/internal/fetch/index.mjs +80 -18
- package/dist/mjs/internal/fetch/index.mjs.map +3 -3
- package/dist/mjs/internal/fetch/stream-state.mjs.map +1 -1
- package/dist/mjs/internal/fs/index.mjs +2 -2
- package/dist/mjs/internal/fs/index.mjs.map +2 -2
- package/dist/mjs/internal/module-loader/bundle.mjs +225 -8
- package/dist/mjs/internal/module-loader/bundle.mjs.map +3 -3
- package/dist/mjs/internal/path/index.mjs.map +1 -1
- package/dist/mjs/internal/playwright/index.mjs +2 -2
- package/dist/mjs/internal/playwright/index.mjs.map +2 -2
- package/dist/mjs/internal/runtime/index.mjs +78 -6
- package/dist/mjs/internal/runtime/index.mjs.map +3 -3
- package/dist/mjs/internal/test-environment/index.mjs +2 -2
- package/dist/mjs/internal/test-environment/index.mjs.map +2 -2
- package/dist/mjs/internal/timers/index.mjs +9 -4
- package/dist/mjs/internal/timers/index.mjs.map +3 -3
- package/dist/mjs/internal/typecheck/isolate-types.mjs +36 -1
- package/dist/mjs/internal/typecheck/isolate-types.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/internal/async-context/index.d.ts +5 -0
- package/dist/types/internal/console/index.d.ts +1 -1
- package/dist/types/internal/core/index.d.ts +2 -2
- package/dist/types/internal/crypto/index.d.ts +1 -1
- package/dist/types/internal/daemon/types.d.ts +1 -0
- package/dist/types/internal/encoding/index.d.ts +1 -1
- package/dist/types/internal/fetch/index.d.ts +1 -1
- package/dist/types/internal/fetch/stream-state.d.ts +1 -1
- package/dist/types/internal/fs/index.d.ts +1 -1
- package/dist/types/internal/path/index.d.ts +1 -1
- package/dist/types/internal/playwright/index.d.ts +1 -1
- package/dist/types/internal/test-environment/index.d.ts +1 -1
- package/dist/types/internal/timers/index.d.ts +1 -1
- package/dist/types/internal/typecheck/isolate-types.d.ts +2 -2
- package/package.json +8 -3
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/internal/fetch/stream-state.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import ivm from \"isolated-vm\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface StreamState {\n /** Buffered chunks waiting to be read */\n queue: Uint8Array[];\n\n /** Total bytes in queue (for backpressure) */\n queueSize: number;\n\n /** Stream has been closed (no more data) */\n closed: boolean;\n\n /** Stream encountered an error */\n errored: boolean;\n\n /** The error value if errored */\n errorValue: unknown;\n\n /** A pull is waiting for data */\n pullWaiting: boolean;\n\n /** Resolve function for waiting pull */\n pullResolve: ((chunk: Uint8Array | null) => void) | null;\n\n /** Reject function for waiting pull */\n pullReject: ((error: unknown) => void) | null;\n}\n\nexport interface StreamStateRegistry {\n /** Create a new stream and return its ID */\n create(): number;\n\n /** Get stream state by ID */\n get(streamId: number): StreamState | undefined;\n\n /** Push a chunk to the stream's queue */\n push(streamId: number, chunk: Uint8Array): boolean;\n\n /** Pull a chunk from the stream (returns Promise that resolves when data available) */\n pull(\n streamId: number\n ): Promise<{ value: Uint8Array; done: false } | { done: true }>;\n\n /** Close the stream (no more data) */\n close(streamId: number): void;\n\n /** Error the stream */\n error(streamId: number, errorValue: unknown): void;\n\n /** Check if stream queue is above high-water mark */\n isQueueFull(streamId: number): boolean;\n\n /** Delete stream state (cleanup) */\n delete(streamId: number): void;\n\n /** Clear all streams (context cleanup) */\n clear(): void;\n\n /** Cancel a stream and call its cleanup function */\n cancel(streamId: number): void;\n\n /** Register a cleanup function for a stream */\n setCleanup(streamId: number, cleanup: () => Promise<void>): void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Maximum bytes to buffer before backpressure kicks in */\nexport const HIGH_WATER_MARK = 64 * 1024; // 64KB\n\n/** Maximum number of chunks in queue */\nexport const MAX_QUEUE_CHUNKS = 16;\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\nexport function createStreamStateRegistry(): StreamStateRegistry {\n const streams = new Map<number, StreamState>();\n const cleanups = new Map<number, () => Promise<void>>();\n let nextStreamId = 1;\n\n return {\n create(): number {\n const streamId = nextStreamId++;\n streams.set(streamId, {\n queue: [],\n queueSize: 0,\n closed: false,\n errored: false,\n errorValue: undefined,\n pullWaiting: false,\n pullResolve: null,\n pullReject: null,\n });\n return streamId;\n },\n\n get(streamId: number): StreamState | undefined {\n return streams.get(streamId);\n },\n\n push(streamId: number, chunk: Uint8Array): boolean {\n const state = streams.get(streamId);\n if (!state) return false;\n if (state.closed || state.errored) return false;\n\n // If a pull is waiting, deliver directly\n if (state.pullWaiting && state.pullResolve) {\n state.pullWaiting = false;\n const resolve = state.pullResolve;\n state.pullResolve = null;\n state.pullReject = null;\n resolve(chunk);\n return true;\n }\n\n // Otherwise queue the chunk\n state.queue.push(chunk);\n state.queueSize += chunk.length;\n return true;\n },\n\n async pull(\n streamId: number\n ): Promise<{ value: Uint8Array; done: false } | { done: true }> {\n const state = streams.get(streamId);\n if (!state) {\n return { done: true };\n }\n\n // If queue has data, return it first (even if stream is errored)\n if (state.queue.length > 0) {\n const chunk = state.queue.shift()!;\n state.queueSize -= chunk.length;\n return { value: chunk, done: false };\n }\n\n // If errored (and queue is empty), throw\n if (state.errored) {\n throw state.errorValue;\n }\n\n // If closed and queue empty, we're done\n if (state.closed) {\n return { done: true };\n }\n\n // Wait for data\n return new Promise((resolve, reject) => {\n state.pullWaiting = true;\n state.pullResolve = (chunk) => {\n if (chunk === null) {\n resolve({ done: true });\n } else {\n resolve({ value: chunk, done: false });\n }\n };\n state.pullReject = reject;\n });\n },\n\n close(streamId: number): void {\n const state = streams.get(streamId);\n if (!state) return;\n\n state.closed = true;\n\n // If a pull is waiting, resolve with done\n if (state.pullWaiting && state.pullResolve) {\n state.pullWaiting = false;\n const resolve = state.pullResolve;\n state.pullResolve = null;\n state.pullReject = null;\n resolve(null);\n }\n },\n\n error(streamId: number, errorValue: unknown): void {\n const state = streams.get(streamId);\n if (!state) return;\n\n state.errored = true;\n state.errorValue = errorValue;\n\n // If a pull is waiting, reject it\n if (state.pullWaiting && state.pullReject) {\n state.pullWaiting = false;\n const reject = state.pullReject;\n state.pullResolve = null;\n state.pullReject = null;\n reject(errorValue);\n }\n },\n\n isQueueFull(streamId: number): boolean {\n const state = streams.get(streamId);\n if (!state) return true;\n return (\n state.queueSize >= HIGH_WATER_MARK ||\n state.queue.length >= MAX_QUEUE_CHUNKS\n );\n },\n\n delete(streamId: number): void {\n const state = streams.get(streamId);\n if (state && state.pullWaiting && state.pullReject) {\n state.pullReject(new Error(\"Stream deleted\"));\n }\n streams.delete(streamId);\n cleanups.delete(streamId);\n },\n\n clear(): void {\n for (const [streamId] of streams) {\n const cleanup = cleanups.get(streamId);\n if (cleanup) {\n cleanup().catch(() => {});\n cleanups.delete(streamId);\n }\n this.delete(streamId);\n }\n },\n\n cancel(streamId: number): void {\n this.close(streamId);\n const cleanup = cleanups.get(streamId);\n if (cleanup) {\n cleanup().catch(() => {});\n cleanups.delete(streamId);\n }\n },\n\n setCleanup(streamId: number, cleanup: () => Promise<void>): void {\n cleanups.set(streamId, cleanup);\n },\n };\n}\n\n// ============================================================================\n// Context-Scoped Registry\n// ============================================================================\n\nconst contextRegistries = new WeakMap<ivm.Context, StreamStateRegistry>();\n\nexport function getStreamRegistryForContext(\n context: ivm.Context\n): StreamStateRegistry {\n let registry = contextRegistries.get(context);\n if (!registry) {\n registry = createStreamStateRegistry();\n contextRegistries.set(context, registry);\n }\n return registry;\n}\n\nexport function clearStreamRegistryForContext(context: ivm.Context): void {\n const registry = contextRegistries.get(context);\n if (registry) {\n registry.clear();\n contextRegistries.delete(context);\n }\n}\n\n// ============================================================================\n// Native Stream Reader\n// ============================================================================\n\n/**\n * Start reading from a native ReadableStream and push to host queue.\n * Respects backpressure by pausing when queue is full.\n *\n * @param nativeStream The native ReadableStream to read from\n * @param streamId The stream ID in the registry\n * @param registry The stream state registry\n * @returns Async cleanup function to cancel the reader\n */\nexport function startNativeStreamReader(\n nativeStream: ReadableStream<Uint8Array>,\n streamId: number,\n registry: StreamStateRegistry\n): () => Promise<void> {\n let cancelled = false;\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n let readLoopPromise: Promise<void> | null = null;\n\n const CHUNK_SIZE = 64 * 1024; // 64KB max chunk size\n\n async function readLoop() {\n try {\n reader = nativeStream.getReader();\n\n while (!cancelled) {\n // Respect backpressure - wait if queue is full\n while (registry.isQueueFull(streamId) && !cancelled) {\n await new Promise((resolve) => setTimeout(resolve, 1));\n }\n if (cancelled) break;\n\n const { done, value } = await reader.read();\n\n if (done) {\n registry.close(streamId);\n break;\n }\n\n if (value) {\n // Split large chunks to maintain granularity\n if (value.length > CHUNK_SIZE) {\n for (let offset = 0; offset < value.length; offset += CHUNK_SIZE) {\n const chunk = value.slice(\n offset,\n Math.min(offset + CHUNK_SIZE, value.length)\n );\n registry.push(streamId, chunk);\n }\n } else {\n registry.push(streamId, value);\n }\n }\n }\n } catch (error) {\n registry.error(streamId, error);\n } finally {\n if (reader) {\n try {\n reader.releaseLock();\n } catch {\n // Ignore release errors\n }\n }\n }\n }\n\n // Start the read loop and save the promise\n readLoopPromise = readLoop();\n\n // Return async cleanup function\n return async () => {\n cancelled = true;\n if (reader) {\n try {\n await reader.cancel();\n } catch {\n // Ignore cancel errors\n }\n }\n // Wait for read loop to finish\n if (readLoopPromise) {\n try {\n await readLoopPromise;\n } catch {\n // Ignore read loop errors during cleanup\n }\n }\n };\n}\n"
|
|
5
|
+
"import ivm from \"@ricsam/isolated-vm\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface StreamState {\n /** Buffered chunks waiting to be read */\n queue: Uint8Array[];\n\n /** Total bytes in queue (for backpressure) */\n queueSize: number;\n\n /** Stream has been closed (no more data) */\n closed: boolean;\n\n /** Stream encountered an error */\n errored: boolean;\n\n /** The error value if errored */\n errorValue: unknown;\n\n /** A pull is waiting for data */\n pullWaiting: boolean;\n\n /** Resolve function for waiting pull */\n pullResolve: ((chunk: Uint8Array | null) => void) | null;\n\n /** Reject function for waiting pull */\n pullReject: ((error: unknown) => void) | null;\n}\n\nexport interface StreamStateRegistry {\n /** Create a new stream and return its ID */\n create(): number;\n\n /** Get stream state by ID */\n get(streamId: number): StreamState | undefined;\n\n /** Push a chunk to the stream's queue */\n push(streamId: number, chunk: Uint8Array): boolean;\n\n /** Pull a chunk from the stream (returns Promise that resolves when data available) */\n pull(\n streamId: number\n ): Promise<{ value: Uint8Array; done: false } | { done: true }>;\n\n /** Close the stream (no more data) */\n close(streamId: number): void;\n\n /** Error the stream */\n error(streamId: number, errorValue: unknown): void;\n\n /** Check if stream queue is above high-water mark */\n isQueueFull(streamId: number): boolean;\n\n /** Delete stream state (cleanup) */\n delete(streamId: number): void;\n\n /** Clear all streams (context cleanup) */\n clear(): void;\n\n /** Cancel a stream and call its cleanup function */\n cancel(streamId: number): void;\n\n /** Register a cleanup function for a stream */\n setCleanup(streamId: number, cleanup: () => Promise<void>): void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Maximum bytes to buffer before backpressure kicks in */\nexport const HIGH_WATER_MARK = 64 * 1024; // 64KB\n\n/** Maximum number of chunks in queue */\nexport const MAX_QUEUE_CHUNKS = 16;\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\nexport function createStreamStateRegistry(): StreamStateRegistry {\n const streams = new Map<number, StreamState>();\n const cleanups = new Map<number, () => Promise<void>>();\n let nextStreamId = 1;\n\n return {\n create(): number {\n const streamId = nextStreamId++;\n streams.set(streamId, {\n queue: [],\n queueSize: 0,\n closed: false,\n errored: false,\n errorValue: undefined,\n pullWaiting: false,\n pullResolve: null,\n pullReject: null,\n });\n return streamId;\n },\n\n get(streamId: number): StreamState | undefined {\n return streams.get(streamId);\n },\n\n push(streamId: number, chunk: Uint8Array): boolean {\n const state = streams.get(streamId);\n if (!state) return false;\n if (state.closed || state.errored) return false;\n\n // If a pull is waiting, deliver directly\n if (state.pullWaiting && state.pullResolve) {\n state.pullWaiting = false;\n const resolve = state.pullResolve;\n state.pullResolve = null;\n state.pullReject = null;\n resolve(chunk);\n return true;\n }\n\n // Otherwise queue the chunk\n state.queue.push(chunk);\n state.queueSize += chunk.length;\n return true;\n },\n\n async pull(\n streamId: number\n ): Promise<{ value: Uint8Array; done: false } | { done: true }> {\n const state = streams.get(streamId);\n if (!state) {\n return { done: true };\n }\n\n // If queue has data, return it first (even if stream is errored)\n if (state.queue.length > 0) {\n const chunk = state.queue.shift()!;\n state.queueSize -= chunk.length;\n return { value: chunk, done: false };\n }\n\n // If errored (and queue is empty), throw\n if (state.errored) {\n throw state.errorValue;\n }\n\n // If closed and queue empty, we're done\n if (state.closed) {\n return { done: true };\n }\n\n // Wait for data\n return new Promise((resolve, reject) => {\n state.pullWaiting = true;\n state.pullResolve = (chunk) => {\n if (chunk === null) {\n resolve({ done: true });\n } else {\n resolve({ value: chunk, done: false });\n }\n };\n state.pullReject = reject;\n });\n },\n\n close(streamId: number): void {\n const state = streams.get(streamId);\n if (!state) return;\n\n state.closed = true;\n\n // If a pull is waiting, resolve with done\n if (state.pullWaiting && state.pullResolve) {\n state.pullWaiting = false;\n const resolve = state.pullResolve;\n state.pullResolve = null;\n state.pullReject = null;\n resolve(null);\n }\n },\n\n error(streamId: number, errorValue: unknown): void {\n const state = streams.get(streamId);\n if (!state) return;\n\n state.errored = true;\n state.errorValue = errorValue;\n\n // If a pull is waiting, reject it\n if (state.pullWaiting && state.pullReject) {\n state.pullWaiting = false;\n const reject = state.pullReject;\n state.pullResolve = null;\n state.pullReject = null;\n reject(errorValue);\n }\n },\n\n isQueueFull(streamId: number): boolean {\n const state = streams.get(streamId);\n if (!state) return true;\n return (\n state.queueSize >= HIGH_WATER_MARK ||\n state.queue.length >= MAX_QUEUE_CHUNKS\n );\n },\n\n delete(streamId: number): void {\n const state = streams.get(streamId);\n if (state && state.pullWaiting && state.pullReject) {\n state.pullReject(new Error(\"Stream deleted\"));\n }\n streams.delete(streamId);\n cleanups.delete(streamId);\n },\n\n clear(): void {\n for (const [streamId] of streams) {\n const cleanup = cleanups.get(streamId);\n if (cleanup) {\n cleanup().catch(() => {});\n cleanups.delete(streamId);\n }\n this.delete(streamId);\n }\n },\n\n cancel(streamId: number): void {\n this.close(streamId);\n const cleanup = cleanups.get(streamId);\n if (cleanup) {\n cleanup().catch(() => {});\n cleanups.delete(streamId);\n }\n },\n\n setCleanup(streamId: number, cleanup: () => Promise<void>): void {\n cleanups.set(streamId, cleanup);\n },\n };\n}\n\n// ============================================================================\n// Context-Scoped Registry\n// ============================================================================\n\nconst contextRegistries = new WeakMap<ivm.Context, StreamStateRegistry>();\n\nexport function getStreamRegistryForContext(\n context: ivm.Context\n): StreamStateRegistry {\n let registry = contextRegistries.get(context);\n if (!registry) {\n registry = createStreamStateRegistry();\n contextRegistries.set(context, registry);\n }\n return registry;\n}\n\nexport function clearStreamRegistryForContext(context: ivm.Context): void {\n const registry = contextRegistries.get(context);\n if (registry) {\n registry.clear();\n contextRegistries.delete(context);\n }\n}\n\n// ============================================================================\n// Native Stream Reader\n// ============================================================================\n\n/**\n * Start reading from a native ReadableStream and push to host queue.\n * Respects backpressure by pausing when queue is full.\n *\n * @param nativeStream The native ReadableStream to read from\n * @param streamId The stream ID in the registry\n * @param registry The stream state registry\n * @returns Async cleanup function to cancel the reader\n */\nexport function startNativeStreamReader(\n nativeStream: ReadableStream<Uint8Array>,\n streamId: number,\n registry: StreamStateRegistry\n): () => Promise<void> {\n let cancelled = false;\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n let readLoopPromise: Promise<void> | null = null;\n\n const CHUNK_SIZE = 64 * 1024; // 64KB max chunk size\n\n async function readLoop() {\n try {\n reader = nativeStream.getReader();\n\n while (!cancelled) {\n // Respect backpressure - wait if queue is full\n while (registry.isQueueFull(streamId) && !cancelled) {\n await new Promise((resolve) => setTimeout(resolve, 1));\n }\n if (cancelled) break;\n\n const { done, value } = await reader.read();\n\n if (done) {\n registry.close(streamId);\n break;\n }\n\n if (value) {\n // Split large chunks to maintain granularity\n if (value.length > CHUNK_SIZE) {\n for (let offset = 0; offset < value.length; offset += CHUNK_SIZE) {\n const chunk = value.slice(\n offset,\n Math.min(offset + CHUNK_SIZE, value.length)\n );\n registry.push(streamId, chunk);\n }\n } else {\n registry.push(streamId, value);\n }\n }\n }\n } catch (error) {\n registry.error(streamId, error);\n } finally {\n if (reader) {\n try {\n reader.releaseLock();\n } catch {\n // Ignore release errors\n }\n }\n }\n }\n\n // Start the read loop and save the promise\n readLoopPromise = readLoop();\n\n // Return async cleanup function\n return async () => {\n cancelled = true;\n if (reader) {\n try {\n await reader.cancel();\n } catch {\n // Ignore cancel errors\n }\n }\n // Wait for read loop to finish\n if (readLoopPromise) {\n try {\n await readLoopPromise;\n } catch {\n // Ignore read loop errors during cleanup\n }\n }\n };\n}\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": "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",
|
|
8
8
|
"debugId": "1047E8C8B03EA8FA64756E2164756E21",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/internal/fs/index.ts
|
|
2
|
-
import ivm from "isolated-vm";
|
|
2
|
+
import ivm from "@ricsam/isolated-vm";
|
|
3
3
|
import { setupCore, clearAllInstanceState } from "../core/index.mjs";
|
|
4
4
|
import { createNodeFileSystemHandler } from "./node-adapter.mjs";
|
|
5
5
|
var instanceStateMap = new WeakMap;
|
|
@@ -780,4 +780,4 @@ export {
|
|
|
780
780
|
clearAllInstanceState
|
|
781
781
|
};
|
|
782
782
|
|
|
783
|
-
//# debugId=
|
|
783
|
+
//# debugId=195466EF06E0806564756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/internal/fs/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import ivm from \"isolated-vm\";\nimport { setupCore, clearAllInstanceState } from \"../core/index.mjs\";\n\nexport { clearAllInstanceState };\n\n// ============================================================================\n// FileSystemHandler Interface\n// ============================================================================\n\nexport interface FileSystemHandler {\n /** Get or create a file handle at the given path */\n getFileHandle(\n path: string,\n options?: { create?: boolean }\n ): Promise<void>;\n\n /** Get or create a directory handle at the given path */\n getDirectoryHandle(\n path: string,\n options?: { create?: boolean }\n ): Promise<void>;\n\n /** Remove a file or directory at the given path */\n removeEntry(\n path: string,\n options?: { recursive?: boolean }\n ): Promise<void>;\n\n /** List contents of a directory */\n readDirectory(\n path: string\n ): Promise<Array<{ name: string; kind: \"file\" | \"directory\" }>>;\n\n /** Read file content */\n readFile(\n path: string\n ): Promise<{ data: Uint8Array; size: number; lastModified: number; type: string }>;\n\n /** Write data to a file */\n writeFile(\n path: string,\n data: Uint8Array,\n position?: number\n ): Promise<void>;\n\n /** Truncate a file to a specific size */\n truncateFile(path: string, size: number): Promise<void>;\n\n /** Get file metadata without reading content */\n getFileMetadata(\n path: string\n ): Promise<{ size: number; lastModified: number; type: string }>;\n}\n\nexport interface FsOptions {\n /** Get a file system handler for the given path */\n getDirectory(path: string): Promise<FileSystemHandler>;\n}\n\nexport interface FsHandle {\n dispose(): void;\n}\n\n// ============================================================================\n// Instance State Management\n// ============================================================================\n\nconst instanceStateMap = new WeakMap<ivm.Context, Map<number, unknown>>();\nlet nextInstanceId = 1;\n\nfunction getInstanceStateMapForContext(\n context: ivm.Context\n): Map<number, unknown> {\n let map = instanceStateMap.get(context);\n if (!map) {\n map = new Map();\n instanceStateMap.set(context, map);\n }\n return map;\n}\n\n// ============================================================================\n// State Types\n// ============================================================================\n\ninterface DirectoryHandleState {\n instanceId: number;\n path: string; // Path within handler's root, e.g., \"/\" or \"/subdir\"\n name: string; // Directory name, e.g., \"\" for root or \"subdir\"\n handler: FileSystemHandler; // Handler for this directory tree\n}\n\ninterface FileHandleState {\n instanceId: number;\n path: string; // Path within handler's root, e.g., \"/file.txt\"\n name: string; // File name, e.g., \"file.txt\"\n handler: FileSystemHandler; // Handler for this file\n}\n\ninterface WritableStreamState {\n instanceId: number;\n filePath: string; // Path to the file being written\n position: number; // Current write position (for seek)\n buffer: Uint8Array[]; // Buffered writes before close\n closed: boolean; // Whether stream has been closed\n handler: FileSystemHandler; // Handler for this stream\n}\n\n// ============================================================================\n// FileSystemDirectoryHandle Implementation\n// ============================================================================\n\nfunction setupFileSystemDirectoryHandle(\n context: ivm.Context,\n stateMap: Map<number, unknown>\n): void {\n const global = context.global;\n\n // Property getters\n global.setSync(\n \"__FileSystemDirectoryHandle_get_name\",\n new ivm.Callback((instanceId: number) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n return state?.name ?? \"\";\n })\n );\n\n global.setSync(\n \"__FileSystemDirectoryHandle_get_path\",\n new ivm.Callback((instanceId: number) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n return state?.path ?? \"/\";\n })\n );\n\n // getFileHandle - async reference\n const getFileHandleRef = new ivm.Reference(\n async (instanceId: number, name: string, optionsJson: string) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n if (!state) {\n throw new Error(\"[NotFoundError]Directory handle not found\");\n }\n\n const options = JSON.parse(optionsJson) as { create?: boolean };\n const childPath = state.path === \"/\" ? `/${name}` : `${state.path}/${name}`;\n\n try {\n await state.handler.getFileHandle(childPath, options);\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n\n // Create file handle state with parent's handler\n const fileInstanceId = nextInstanceId++;\n const fileState: FileHandleState = {\n instanceId: fileInstanceId,\n path: childPath,\n name,\n handler: state.handler,\n };\n stateMap.set(fileInstanceId, fileState);\n\n return JSON.stringify({ instanceId: fileInstanceId });\n }\n );\n global.setSync(\"__FileSystemDirectoryHandle_getFileHandle_ref\", getFileHandleRef);\n\n // getDirectoryHandle - async reference\n const getDirectoryHandleRef = new ivm.Reference(\n async (instanceId: number, name: string, optionsJson: string) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n if (!state) {\n throw new Error(\"[NotFoundError]Directory handle not found\");\n }\n\n const options = JSON.parse(optionsJson) as { create?: boolean };\n const childPath = state.path === \"/\" ? `/${name}` : `${state.path}/${name}`;\n\n try {\n await state.handler.getDirectoryHandle(childPath, options);\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n\n // Create directory handle state with parent's handler\n const dirInstanceId = nextInstanceId++;\n const dirState: DirectoryHandleState = {\n instanceId: dirInstanceId,\n path: childPath,\n name,\n handler: state.handler,\n };\n stateMap.set(dirInstanceId, dirState);\n\n return JSON.stringify({ instanceId: dirInstanceId });\n }\n );\n global.setSync(\"__FileSystemDirectoryHandle_getDirectoryHandle_ref\", getDirectoryHandleRef);\n\n // removeEntry - async reference\n const removeEntryRef = new ivm.Reference(\n async (instanceId: number, name: string, optionsJson: string) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n if (!state) {\n throw new Error(\"[NotFoundError]Directory handle not found\");\n }\n\n const options = JSON.parse(optionsJson) as { recursive?: boolean };\n const childPath = state.path === \"/\" ? `/${name}` : `${state.path}/${name}`;\n\n try {\n await state.handler.removeEntry(childPath, options);\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n }\n );\n global.setSync(\"__FileSystemDirectoryHandle_removeEntry_ref\", removeEntryRef);\n\n // readDirectory - async reference (for entries/keys/values)\n const readDirectoryRef = new ivm.Reference(async (instanceId: number) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n if (!state) {\n throw new Error(\"[NotFoundError]Directory handle not found\");\n }\n\n try {\n const entries = await state.handler.readDirectory(state.path);\n\n // Create handle states for each entry and return with instance IDs\n const result = entries.map((entry) => {\n const entryId = nextInstanceId++;\n const entryPath = state.path === \"/\" ? `/${entry.name}` : `${state.path}/${entry.name}`;\n\n if (entry.kind === \"file\") {\n const fileState: FileHandleState = {\n instanceId: entryId,\n path: entryPath,\n name: entry.name,\n handler: state.handler,\n };\n stateMap.set(entryId, fileState);\n } else {\n const dirState: DirectoryHandleState = {\n instanceId: entryId,\n path: entryPath,\n name: entry.name,\n handler: state.handler,\n };\n stateMap.set(entryId, dirState);\n }\n\n return {\n name: entry.name,\n kind: entry.kind,\n instanceId: entryId,\n };\n });\n\n return JSON.stringify(result);\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n });\n global.setSync(\"__FileSystemDirectoryHandle_readDirectory_ref\", readDirectoryRef);\n\n // isSameEntry - sync callback\n global.setSync(\n \"__FileSystemDirectoryHandle_isSameEntry\",\n new ivm.Callback((id1: number, id2: number) => {\n const state1 = stateMap.get(id1) as DirectoryHandleState | undefined;\n const state2 = stateMap.get(id2) as DirectoryHandleState | undefined;\n if (!state1 || !state2) return false;\n return state1.path === state2.path;\n })\n );\n\n // resolve - async reference\n const resolveRef = new ivm.Reference(\n async (instanceId: number, descendantId: number) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n const descendantState = stateMap.get(descendantId) as\n | DirectoryHandleState\n | FileHandleState\n | undefined;\n\n if (!state || !descendantState) {\n return \"null\";\n }\n\n // Check if descendant is actually a descendant\n const basePath = state.path === \"/\" ? \"\" : state.path;\n if (!descendantState.path.startsWith(basePath + \"/\") && descendantState.path !== state.path) {\n return \"null\";\n }\n\n // Build path components\n const relativePath = descendantState.path.slice(basePath.length);\n const components = relativePath.split(\"/\").filter((c) => c.length > 0);\n\n return JSON.stringify(components);\n }\n );\n global.setSync(\"__FileSystemDirectoryHandle_resolve_ref\", resolveRef);\n\n // Inject FileSystemDirectoryHandle class\n const directoryHandleCode = `\n(function() {\n const _directoryHandleInstanceIds = new WeakMap();\n\n function __decodeError(err) {\n if (!(err instanceof Error)) return err;\n const match = err.message.match(/^\\\\[(TypeError|RangeError|NotFoundError|TypeMismatchError|InvalidModificationError|Error)\\\\](.*)$/);\n if (match) {\n if (['NotFoundError', 'TypeMismatchError', 'InvalidModificationError'].includes(match[1])) {\n return new DOMException(match[2], match[1]);\n }\n const ErrorType = globalThis[match[1]] || Error;\n return new ErrorType(match[2]);\n }\n return err;\n }\n\n class FileSystemDirectoryHandle {\n constructor(path, name) {\n // Internal construction from instance ID\n if (typeof path === 'number' && name === null) {\n _directoryHandleInstanceIds.set(this, path);\n return;\n }\n const instanceId = __FileSystemDirectoryHandle_construct(path, name);\n _directoryHandleInstanceIds.set(this, instanceId);\n }\n\n static _fromInstanceId(instanceId) {\n return new FileSystemDirectoryHandle(instanceId, null);\n }\n\n _getInstanceId() {\n return _directoryHandleInstanceIds.get(this);\n }\n\n get kind() {\n return 'directory';\n }\n\n get name() {\n return __FileSystemDirectoryHandle_get_name(this._getInstanceId());\n }\n\n async getFileHandle(name, options = {}) {\n try {\n const resultJson = await __FileSystemDirectoryHandle_getFileHandle_ref.apply(\n undefined,\n [this._getInstanceId(), name, JSON.stringify(options)],\n { result: { promise: true, copy: true } }\n );\n const result = JSON.parse(resultJson);\n return FileSystemFileHandle._fromInstanceId(result.instanceId);\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async getDirectoryHandle(name, options = {}) {\n try {\n const resultJson = await __FileSystemDirectoryHandle_getDirectoryHandle_ref.apply(\n undefined,\n [this._getInstanceId(), name, JSON.stringify(options)],\n { result: { promise: true, copy: true } }\n );\n const result = JSON.parse(resultJson);\n return FileSystemDirectoryHandle._fromInstanceId(result.instanceId);\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async removeEntry(name, options = {}) {\n try {\n await __FileSystemDirectoryHandle_removeEntry_ref.apply(\n undefined,\n [this._getInstanceId(), name, JSON.stringify(options)],\n { result: { promise: true, copy: true } }\n );\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async *entries() {\n let entriesJson;\n try {\n entriesJson = await __FileSystemDirectoryHandle_readDirectory_ref.apply(\n undefined,\n [this._getInstanceId()],\n { result: { promise: true, copy: true } }\n );\n } catch (err) {\n throw __decodeError(err);\n }\n const entries = JSON.parse(entriesJson);\n for (const entry of entries) {\n if (entry.kind === 'file') {\n yield [entry.name, FileSystemFileHandle._fromInstanceId(entry.instanceId)];\n } else {\n yield [entry.name, FileSystemDirectoryHandle._fromInstanceId(entry.instanceId)];\n }\n }\n }\n\n async *keys() {\n for await (const [name] of this.entries()) {\n yield name;\n }\n }\n\n async *values() {\n for await (const [, handle] of this.entries()) {\n yield handle;\n }\n }\n\n [Symbol.asyncIterator]() {\n return this.entries();\n }\n\n async isSameEntry(other) {\n if (!(other instanceof FileSystemDirectoryHandle)) {\n return false;\n }\n return __FileSystemDirectoryHandle_isSameEntry(\n this._getInstanceId(),\n other._getInstanceId()\n );\n }\n\n async resolve(possibleDescendant) {\n try {\n const resultJson = await __FileSystemDirectoryHandle_resolve_ref.apply(\n undefined,\n [this._getInstanceId(), possibleDescendant._getInstanceId()],\n { result: { promise: true, copy: true } }\n );\n return resultJson === 'null' ? null : JSON.parse(resultJson);\n } catch (err) {\n throw __decodeError(err);\n }\n }\n }\n\n globalThis.FileSystemDirectoryHandle = FileSystemDirectoryHandle;\n})();\n`;\n\n context.evalSync(directoryHandleCode);\n}\n\n// ============================================================================\n// FileSystemFileHandle Implementation\n// ============================================================================\n\nfunction setupFileSystemFileHandle(\n context: ivm.Context,\n stateMap: Map<number, unknown>\n): void {\n const global = context.global;\n\n // Property getters\n global.setSync(\n \"__FileSystemFileHandle_get_name\",\n new ivm.Callback((instanceId: number) => {\n const state = stateMap.get(instanceId) as FileHandleState | undefined;\n return state?.name ?? \"\";\n })\n );\n\n global.setSync(\n \"__FileSystemFileHandle_get_path\",\n new ivm.Callback((instanceId: number) => {\n const state = stateMap.get(instanceId) as FileHandleState | undefined;\n return state?.path ?? \"\";\n })\n );\n\n // getFile - async reference\n const getFileRef = new ivm.Reference(async (instanceId: number) => {\n const state = stateMap.get(instanceId) as FileHandleState | undefined;\n if (!state) {\n throw new Error(\"[NotFoundError]File handle not found\");\n }\n\n try {\n const fileData = await state.handler.readFile(state.path);\n return JSON.stringify({\n name: state.name,\n data: Array.from(fileData.data),\n size: fileData.size,\n lastModified: fileData.lastModified,\n type: fileData.type,\n });\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n });\n global.setSync(\"__FileSystemFileHandle_getFile_ref\", getFileRef);\n\n // createWritable - async reference\n const createWritableRef = new ivm.Reference(\n async (instanceId: number, _optionsJson: string) => {\n const state = stateMap.get(instanceId) as FileHandleState | undefined;\n if (!state) {\n throw new Error(\"[NotFoundError]File handle not found\");\n }\n\n // Create writable stream state with handler reference\n const streamInstanceId = nextInstanceId++;\n const streamState: WritableStreamState = {\n instanceId: streamInstanceId,\n filePath: state.path,\n position: 0,\n buffer: [],\n closed: false,\n handler: state.handler,\n };\n stateMap.set(streamInstanceId, streamState);\n\n return streamInstanceId;\n }\n );\n global.setSync(\"__FileSystemFileHandle_createWritable_ref\", createWritableRef);\n\n // isSameEntry - sync callback\n global.setSync(\n \"__FileSystemFileHandle_isSameEntry\",\n new ivm.Callback((id1: number, id2: number) => {\n const state1 = stateMap.get(id1) as FileHandleState | undefined;\n const state2 = stateMap.get(id2) as FileHandleState | undefined;\n if (!state1 || !state2) return false;\n return state1.path === state2.path;\n })\n );\n\n // Inject FileSystemFileHandle class\n const fileHandleCode = `\n(function() {\n const _fileHandleInstanceIds = new WeakMap();\n\n function __decodeError(err) {\n if (!(err instanceof Error)) return err;\n const match = err.message.match(/^\\\\[(TypeError|RangeError|NotFoundError|TypeMismatchError|InvalidModificationError|Error)\\\\](.*)$/);\n if (match) {\n if (['NotFoundError', 'TypeMismatchError', 'InvalidModificationError'].includes(match[1])) {\n return new DOMException(match[2], match[1]);\n }\n const ErrorType = globalThis[match[1]] || Error;\n return new ErrorType(match[2]);\n }\n return err;\n }\n\n class FileSystemFileHandle {\n constructor(path, name) {\n // Internal construction from instance ID\n if (typeof path === 'number' && name === null) {\n _fileHandleInstanceIds.set(this, path);\n return;\n }\n const instanceId = __FileSystemFileHandle_construct(path, name);\n _fileHandleInstanceIds.set(this, instanceId);\n }\n\n static _fromInstanceId(instanceId) {\n return new FileSystemFileHandle(instanceId, null);\n }\n\n _getInstanceId() {\n return _fileHandleInstanceIds.get(this);\n }\n\n get kind() {\n return 'file';\n }\n\n get name() {\n return __FileSystemFileHandle_get_name(this._getInstanceId());\n }\n\n async getFile() {\n try {\n const metadataJson = await __FileSystemFileHandle_getFile_ref.apply(\n undefined,\n [this._getInstanceId()],\n { result: { promise: true, copy: true } }\n );\n const metadata = JSON.parse(metadataJson);\n // Create File object from metadata and content\n const content = new Uint8Array(metadata.data);\n return new File([content], metadata.name, {\n type: metadata.type,\n lastModified: metadata.lastModified\n });\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async createWritable(options = {}) {\n try {\n const streamId = await __FileSystemFileHandle_createWritable_ref.apply(\n undefined,\n [this._getInstanceId(), JSON.stringify(options)],\n { result: { promise: true, copy: true } }\n );\n return FileSystemWritableFileStream._fromInstanceId(streamId);\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async isSameEntry(other) {\n if (!(other instanceof FileSystemFileHandle)) {\n return false;\n }\n return __FileSystemFileHandle_isSameEntry(\n this._getInstanceId(),\n other._getInstanceId()\n );\n }\n }\n\n globalThis.FileSystemFileHandle = FileSystemFileHandle;\n})();\n`;\n\n context.evalSync(fileHandleCode);\n}\n\n// ============================================================================\n// FileSystemWritableFileStream Implementation\n// ============================================================================\n\nfunction setupFileSystemWritableFileStream(\n context: ivm.Context,\n stateMap: Map<number, unknown>\n): void {\n const global = context.global;\n\n // write - async reference\n const writeRef = new ivm.Reference(\n async (instanceId: number, bytesJson: string, position: number | null) => {\n const state = stateMap.get(instanceId) as WritableStreamState | undefined;\n if (!state) {\n throw new Error(\"[InvalidStateError]Stream not found\");\n }\n if (state.closed) {\n throw new Error(\"[InvalidStateError]Stream is closed\");\n }\n\n const bytes = JSON.parse(bytesJson) as number[];\n const data = new Uint8Array(bytes);\n\n // Update position if specified\n if (position !== null) {\n state.position = position;\n }\n\n // Write to handler\n try {\n await state.handler.writeFile(state.filePath, data, state.position);\n state.position += data.length;\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n }\n );\n global.setSync(\"__FileSystemWritableFileStream_write_ref\", writeRef);\n\n // seek - sync callback\n global.setSync(\n \"__FileSystemWritableFileStream_seek\",\n new ivm.Callback((instanceId: number, position: number) => {\n const state = stateMap.get(instanceId) as WritableStreamState | undefined;\n if (!state) {\n throw new Error(\"[InvalidStateError]Stream not found\");\n }\n if (state.closed) {\n throw new Error(\"[InvalidStateError]Stream is closed\");\n }\n state.position = position;\n })\n );\n\n // truncate - async reference\n const truncateRef = new ivm.Reference(async (instanceId: number, size: number) => {\n const state = stateMap.get(instanceId) as WritableStreamState | undefined;\n if (!state) {\n throw new Error(\"[InvalidStateError]Stream not found\");\n }\n if (state.closed) {\n throw new Error(\"[InvalidStateError]Stream is closed\");\n }\n\n try {\n await state.handler.truncateFile(state.filePath, size);\n // Adjust position if it's beyond the new size\n if (state.position > size) {\n state.position = size;\n }\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n });\n global.setSync(\"__FileSystemWritableFileStream_truncate_ref\", truncateRef);\n\n // close - async reference\n const closeRef = new ivm.Reference(async (instanceId: number) => {\n const state = stateMap.get(instanceId) as WritableStreamState | undefined;\n if (!state) {\n throw new Error(\"[InvalidStateError]Stream not found\");\n }\n if (state.closed) {\n throw new Error(\"[InvalidStateError]Stream is already closed\");\n }\n\n state.closed = true;\n });\n global.setSync(\"__FileSystemWritableFileStream_close_ref\", closeRef);\n\n // abort - async reference\n const abortRef = new ivm.Reference(async (instanceId: number, _reason: string | null) => {\n const state = stateMap.get(instanceId) as WritableStreamState | undefined;\n if (!state) {\n throw new Error(\"[InvalidStateError]Stream not found\");\n }\n\n state.closed = true;\n state.buffer = []; // Discard any buffered data\n });\n global.setSync(\"__FileSystemWritableFileStream_abort_ref\", abortRef);\n\n // locked - sync callback\n global.setSync(\n \"__FileSystemWritableFileStream_get_locked\",\n new ivm.Callback((instanceId: number) => {\n const state = stateMap.get(instanceId) as WritableStreamState | undefined;\n return state ? !state.closed : false;\n })\n );\n\n // Inject FileSystemWritableFileStream class\n const writableStreamCode = `\n(function() {\n const _writableStreamInstanceIds = new WeakMap();\n\n function __decodeError(err) {\n if (!(err instanceof Error)) return err;\n const match = err.message.match(/^\\\\[(TypeError|RangeError|InvalidStateError|NotFoundError|Error)\\\\](.*)$/);\n if (match) {\n if (['InvalidStateError', 'NotFoundError'].includes(match[1])) {\n return new DOMException(match[2], match[1]);\n }\n const ErrorType = globalThis[match[1]] || Error;\n return new ErrorType(match[2]);\n }\n return err;\n }\n\n class FileSystemWritableFileStream {\n constructor(instanceId) {\n _writableStreamInstanceIds.set(this, instanceId);\n }\n\n static _fromInstanceId(instanceId) {\n return new FileSystemWritableFileStream(instanceId);\n }\n\n _getInstanceId() {\n return _writableStreamInstanceIds.get(this);\n }\n\n async write(data) {\n try {\n // Handle different data types\n let writeData;\n let position = null;\n let type = 'write';\n\n if (data && typeof data === 'object' && !ArrayBuffer.isView(data) &&\n !(data instanceof Blob) && !(data instanceof ArrayBuffer) &&\n !Array.isArray(data) && typeof data.type === 'string') {\n // WriteParams object: { type, data, position, size }\n type = data.type || 'write';\n if (type === 'seek') {\n return this.seek(data.position);\n }\n if (type === 'truncate') {\n return this.truncate(data.size);\n }\n writeData = data.data;\n position = data.position ?? null;\n } else {\n writeData = data;\n }\n\n // Convert data to bytes array for transfer\n let bytes;\n if (typeof writeData === 'string') {\n bytes = Array.from(new TextEncoder().encode(writeData));\n } else if (writeData instanceof Blob) {\n const buffer = await writeData.arrayBuffer();\n bytes = Array.from(new Uint8Array(buffer));\n } else if (writeData instanceof ArrayBuffer) {\n bytes = Array.from(new Uint8Array(writeData));\n } else if (ArrayBuffer.isView(writeData)) {\n bytes = Array.from(new Uint8Array(writeData.buffer, writeData.byteOffset, writeData.byteLength));\n } else if (Array.isArray(writeData)) {\n bytes = writeData;\n } else {\n throw new TypeError('Invalid data type for write');\n }\n\n await __FileSystemWritableFileStream_write_ref.apply(\n undefined,\n [this._getInstanceId(), JSON.stringify(bytes), position],\n { result: { promise: true, copy: true } }\n );\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n seek(position) {\n try {\n __FileSystemWritableFileStream_seek(this._getInstanceId(), position);\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async truncate(size) {\n try {\n await __FileSystemWritableFileStream_truncate_ref.apply(\n undefined,\n [this._getInstanceId(), size],\n { result: { promise: true, copy: true } }\n );\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async close() {\n try {\n await __FileSystemWritableFileStream_close_ref.apply(\n undefined,\n [this._getInstanceId()],\n { result: { promise: true, copy: true } }\n );\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async abort(reason) {\n try {\n await __FileSystemWritableFileStream_abort_ref.apply(\n undefined,\n [this._getInstanceId(), reason ? String(reason) : null],\n { result: { promise: true, copy: true } }\n );\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n get locked() {\n return __FileSystemWritableFileStream_get_locked(this._getInstanceId());\n }\n\n getWriter() {\n const stream = this;\n let released = false;\n let closedResolve;\n let closedReject;\n const closedPromise = new Promise((resolve, reject) => {\n closedResolve = resolve;\n closedReject = reject;\n });\n\n return {\n get closed() {\n return closedPromise;\n },\n get desiredSize() {\n return 1;\n },\n get ready() {\n return Promise.resolve();\n },\n async write(chunk) {\n if (released) {\n return Promise.reject(new TypeError('Writer has been released'));\n }\n try {\n await stream.write(chunk);\n return Promise.resolve();\n } catch (err) {\n return Promise.reject(err);\n }\n },\n async close() {\n if (released) {\n return Promise.reject(new TypeError('Writer has been released'));\n }\n try {\n await stream.close();\n closedResolve();\n return Promise.resolve();\n } catch (err) {\n closedReject(err);\n return Promise.reject(err);\n }\n },\n async abort(reason) {\n if (released) {\n return Promise.reject(new TypeError('Writer has been released'));\n }\n try {\n await stream.abort(reason);\n closedReject(reason || new Error('Stream aborted'));\n return Promise.resolve();\n } catch (err) {\n return Promise.reject(err);\n }\n },\n releaseLock() {\n released = true;\n }\n };\n }\n }\n\n globalThis.FileSystemWritableFileStream = FileSystemWritableFileStream;\n})();\n`;\n\n context.evalSync(writableStreamCode);\n}\n\n// ============================================================================\n// Global getDirectory(path) Implementation\n// ============================================================================\n\nfunction setupGetDirectoryGlobal(\n context: ivm.Context,\n stateMap: Map<number, unknown>,\n options: FsOptions\n): void {\n const global = context.global;\n\n // getDirectory - async reference that creates directory handle at specified path\n const getDirectoryRef = new ivm.Reference(async (path: string) => {\n // Get handler for this path from the options factory\n const handler = await options.getDirectory(path);\n\n const instanceId = nextInstanceId++;\n // Path is \"/\" since handler is rooted at the requested path\n const state: DirectoryHandleState = {\n instanceId,\n path: \"/\",\n name: path.split(\"/\").filter(Boolean).pop() || \"\",\n handler,\n };\n stateMap.set(instanceId, state);\n return instanceId;\n });\n global.setSync(\"__getDirectory_ref\", getDirectoryRef);\n\n // Inject global getDirectory (async)\n const getDirectoryCode = `\n(function() {\n globalThis.getDirectory = async function(path) {\n const instanceId = await __getDirectory_ref.apply(\n undefined,\n [path],\n { result: { promise: true, copy: true } }\n );\n return FileSystemDirectoryHandle._fromInstanceId(instanceId);\n };\n})();\n`;\n context.evalSync(getDirectoryCode);\n}\n\n// ============================================================================\n// Main Setup Function\n// ============================================================================\n\n/**\n * Setup File System Access API in an isolated-vm context\n *\n * Provides an OPFS-compatible FileSystemDirectoryHandle API\n *\n * @example\n * const handle = await setupFs(context, {\n * getDirectory: async (path) => {\n * // Return a FileSystemHandler rooted at the given path\n * return createNodeFileSystemHandler(`./data${path}`);\n * }\n * });\n *\n * await context.eval(`\n * const root = await getDirectory(\"/uploads\");\n * const fileHandle = await root.getFileHandle(\"test.txt\", { create: true });\n * const writable = await fileHandle.createWritable();\n * await writable.write(\"hello world\");\n * await writable.close();\n * `);\n */\nexport async function setupFs(\n context: ivm.Context,\n options: FsOptions\n): Promise<FsHandle> {\n // Setup core APIs first (Blob, File, AbortController, Streams, etc.)\n await setupCore(context);\n\n const stateMap = getInstanceStateMapForContext(context);\n\n // Setup FileSystemDirectoryHandle\n setupFileSystemDirectoryHandle(context, stateMap);\n\n // Setup FileSystemFileHandle\n setupFileSystemFileHandle(context, stateMap);\n\n // Setup FileSystemWritableFileStream\n setupFileSystemWritableFileStream(context, stateMap);\n\n // Setup global getDirectory(path)\n setupGetDirectoryGlobal(context, stateMap, options);\n\n return {\n dispose() {\n // Clear state for this context\n stateMap.clear();\n },\n };\n}\n\n// Export node adapter\nexport { createNodeFileSystemHandler } from \"./node-adapter.mjs\";\nexport type { NodeFileSystemHandlerOptions } from \"./node-adapter.mjs\";\n"
|
|
5
|
+
"import ivm from \"@ricsam/isolated-vm\";\nimport { setupCore, clearAllInstanceState } from \"../core/index.mjs\";\n\nexport { clearAllInstanceState };\n\n// ============================================================================\n// FileSystemHandler Interface\n// ============================================================================\n\nexport interface FileSystemHandler {\n /** Get or create a file handle at the given path */\n getFileHandle(\n path: string,\n options?: { create?: boolean }\n ): Promise<void>;\n\n /** Get or create a directory handle at the given path */\n getDirectoryHandle(\n path: string,\n options?: { create?: boolean }\n ): Promise<void>;\n\n /** Remove a file or directory at the given path */\n removeEntry(\n path: string,\n options?: { recursive?: boolean }\n ): Promise<void>;\n\n /** List contents of a directory */\n readDirectory(\n path: string\n ): Promise<Array<{ name: string; kind: \"file\" | \"directory\" }>>;\n\n /** Read file content */\n readFile(\n path: string\n ): Promise<{ data: Uint8Array; size: number; lastModified: number; type: string }>;\n\n /** Write data to a file */\n writeFile(\n path: string,\n data: Uint8Array,\n position?: number\n ): Promise<void>;\n\n /** Truncate a file to a specific size */\n truncateFile(path: string, size: number): Promise<void>;\n\n /** Get file metadata without reading content */\n getFileMetadata(\n path: string\n ): Promise<{ size: number; lastModified: number; type: string }>;\n}\n\nexport interface FsOptions {\n /** Get a file system handler for the given path */\n getDirectory(path: string): Promise<FileSystemHandler>;\n}\n\nexport interface FsHandle {\n dispose(): void;\n}\n\n// ============================================================================\n// Instance State Management\n// ============================================================================\n\nconst instanceStateMap = new WeakMap<ivm.Context, Map<number, unknown>>();\nlet nextInstanceId = 1;\n\nfunction getInstanceStateMapForContext(\n context: ivm.Context\n): Map<number, unknown> {\n let map = instanceStateMap.get(context);\n if (!map) {\n map = new Map();\n instanceStateMap.set(context, map);\n }\n return map;\n}\n\n// ============================================================================\n// State Types\n// ============================================================================\n\ninterface DirectoryHandleState {\n instanceId: number;\n path: string; // Path within handler's root, e.g., \"/\" or \"/subdir\"\n name: string; // Directory name, e.g., \"\" for root or \"subdir\"\n handler: FileSystemHandler; // Handler for this directory tree\n}\n\ninterface FileHandleState {\n instanceId: number;\n path: string; // Path within handler's root, e.g., \"/file.txt\"\n name: string; // File name, e.g., \"file.txt\"\n handler: FileSystemHandler; // Handler for this file\n}\n\ninterface WritableStreamState {\n instanceId: number;\n filePath: string; // Path to the file being written\n position: number; // Current write position (for seek)\n buffer: Uint8Array[]; // Buffered writes before close\n closed: boolean; // Whether stream has been closed\n handler: FileSystemHandler; // Handler for this stream\n}\n\n// ============================================================================\n// FileSystemDirectoryHandle Implementation\n// ============================================================================\n\nfunction setupFileSystemDirectoryHandle(\n context: ivm.Context,\n stateMap: Map<number, unknown>\n): void {\n const global = context.global;\n\n // Property getters\n global.setSync(\n \"__FileSystemDirectoryHandle_get_name\",\n new ivm.Callback((instanceId: number) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n return state?.name ?? \"\";\n })\n );\n\n global.setSync(\n \"__FileSystemDirectoryHandle_get_path\",\n new ivm.Callback((instanceId: number) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n return state?.path ?? \"/\";\n })\n );\n\n // getFileHandle - async reference\n const getFileHandleRef = new ivm.Reference(\n async (instanceId: number, name: string, optionsJson: string) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n if (!state) {\n throw new Error(\"[NotFoundError]Directory handle not found\");\n }\n\n const options = JSON.parse(optionsJson) as { create?: boolean };\n const childPath = state.path === \"/\" ? `/${name}` : `${state.path}/${name}`;\n\n try {\n await state.handler.getFileHandle(childPath, options);\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n\n // Create file handle state with parent's handler\n const fileInstanceId = nextInstanceId++;\n const fileState: FileHandleState = {\n instanceId: fileInstanceId,\n path: childPath,\n name,\n handler: state.handler,\n };\n stateMap.set(fileInstanceId, fileState);\n\n return JSON.stringify({ instanceId: fileInstanceId });\n }\n );\n global.setSync(\"__FileSystemDirectoryHandle_getFileHandle_ref\", getFileHandleRef);\n\n // getDirectoryHandle - async reference\n const getDirectoryHandleRef = new ivm.Reference(\n async (instanceId: number, name: string, optionsJson: string) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n if (!state) {\n throw new Error(\"[NotFoundError]Directory handle not found\");\n }\n\n const options = JSON.parse(optionsJson) as { create?: boolean };\n const childPath = state.path === \"/\" ? `/${name}` : `${state.path}/${name}`;\n\n try {\n await state.handler.getDirectoryHandle(childPath, options);\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n\n // Create directory handle state with parent's handler\n const dirInstanceId = nextInstanceId++;\n const dirState: DirectoryHandleState = {\n instanceId: dirInstanceId,\n path: childPath,\n name,\n handler: state.handler,\n };\n stateMap.set(dirInstanceId, dirState);\n\n return JSON.stringify({ instanceId: dirInstanceId });\n }\n );\n global.setSync(\"__FileSystemDirectoryHandle_getDirectoryHandle_ref\", getDirectoryHandleRef);\n\n // removeEntry - async reference\n const removeEntryRef = new ivm.Reference(\n async (instanceId: number, name: string, optionsJson: string) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n if (!state) {\n throw new Error(\"[NotFoundError]Directory handle not found\");\n }\n\n const options = JSON.parse(optionsJson) as { recursive?: boolean };\n const childPath = state.path === \"/\" ? `/${name}` : `${state.path}/${name}`;\n\n try {\n await state.handler.removeEntry(childPath, options);\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n }\n );\n global.setSync(\"__FileSystemDirectoryHandle_removeEntry_ref\", removeEntryRef);\n\n // readDirectory - async reference (for entries/keys/values)\n const readDirectoryRef = new ivm.Reference(async (instanceId: number) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n if (!state) {\n throw new Error(\"[NotFoundError]Directory handle not found\");\n }\n\n try {\n const entries = await state.handler.readDirectory(state.path);\n\n // Create handle states for each entry and return with instance IDs\n const result = entries.map((entry) => {\n const entryId = nextInstanceId++;\n const entryPath = state.path === \"/\" ? `/${entry.name}` : `${state.path}/${entry.name}`;\n\n if (entry.kind === \"file\") {\n const fileState: FileHandleState = {\n instanceId: entryId,\n path: entryPath,\n name: entry.name,\n handler: state.handler,\n };\n stateMap.set(entryId, fileState);\n } else {\n const dirState: DirectoryHandleState = {\n instanceId: entryId,\n path: entryPath,\n name: entry.name,\n handler: state.handler,\n };\n stateMap.set(entryId, dirState);\n }\n\n return {\n name: entry.name,\n kind: entry.kind,\n instanceId: entryId,\n };\n });\n\n return JSON.stringify(result);\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n });\n global.setSync(\"__FileSystemDirectoryHandle_readDirectory_ref\", readDirectoryRef);\n\n // isSameEntry - sync callback\n global.setSync(\n \"__FileSystemDirectoryHandle_isSameEntry\",\n new ivm.Callback((id1: number, id2: number) => {\n const state1 = stateMap.get(id1) as DirectoryHandleState | undefined;\n const state2 = stateMap.get(id2) as DirectoryHandleState | undefined;\n if (!state1 || !state2) return false;\n return state1.path === state2.path;\n })\n );\n\n // resolve - async reference\n const resolveRef = new ivm.Reference(\n async (instanceId: number, descendantId: number) => {\n const state = stateMap.get(instanceId) as DirectoryHandleState | undefined;\n const descendantState = stateMap.get(descendantId) as\n | DirectoryHandleState\n | FileHandleState\n | undefined;\n\n if (!state || !descendantState) {\n return \"null\";\n }\n\n // Check if descendant is actually a descendant\n const basePath = state.path === \"/\" ? \"\" : state.path;\n if (!descendantState.path.startsWith(basePath + \"/\") && descendantState.path !== state.path) {\n return \"null\";\n }\n\n // Build path components\n const relativePath = descendantState.path.slice(basePath.length);\n const components = relativePath.split(\"/\").filter((c) => c.length > 0);\n\n return JSON.stringify(components);\n }\n );\n global.setSync(\"__FileSystemDirectoryHandle_resolve_ref\", resolveRef);\n\n // Inject FileSystemDirectoryHandle class\n const directoryHandleCode = `\n(function() {\n const _directoryHandleInstanceIds = new WeakMap();\n\n function __decodeError(err) {\n if (!(err instanceof Error)) return err;\n const match = err.message.match(/^\\\\[(TypeError|RangeError|NotFoundError|TypeMismatchError|InvalidModificationError|Error)\\\\](.*)$/);\n if (match) {\n if (['NotFoundError', 'TypeMismatchError', 'InvalidModificationError'].includes(match[1])) {\n return new DOMException(match[2], match[1]);\n }\n const ErrorType = globalThis[match[1]] || Error;\n return new ErrorType(match[2]);\n }\n return err;\n }\n\n class FileSystemDirectoryHandle {\n constructor(path, name) {\n // Internal construction from instance ID\n if (typeof path === 'number' && name === null) {\n _directoryHandleInstanceIds.set(this, path);\n return;\n }\n const instanceId = __FileSystemDirectoryHandle_construct(path, name);\n _directoryHandleInstanceIds.set(this, instanceId);\n }\n\n static _fromInstanceId(instanceId) {\n return new FileSystemDirectoryHandle(instanceId, null);\n }\n\n _getInstanceId() {\n return _directoryHandleInstanceIds.get(this);\n }\n\n get kind() {\n return 'directory';\n }\n\n get name() {\n return __FileSystemDirectoryHandle_get_name(this._getInstanceId());\n }\n\n async getFileHandle(name, options = {}) {\n try {\n const resultJson = await __FileSystemDirectoryHandle_getFileHandle_ref.apply(\n undefined,\n [this._getInstanceId(), name, JSON.stringify(options)],\n { result: { promise: true, copy: true } }\n );\n const result = JSON.parse(resultJson);\n return FileSystemFileHandle._fromInstanceId(result.instanceId);\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async getDirectoryHandle(name, options = {}) {\n try {\n const resultJson = await __FileSystemDirectoryHandle_getDirectoryHandle_ref.apply(\n undefined,\n [this._getInstanceId(), name, JSON.stringify(options)],\n { result: { promise: true, copy: true } }\n );\n const result = JSON.parse(resultJson);\n return FileSystemDirectoryHandle._fromInstanceId(result.instanceId);\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async removeEntry(name, options = {}) {\n try {\n await __FileSystemDirectoryHandle_removeEntry_ref.apply(\n undefined,\n [this._getInstanceId(), name, JSON.stringify(options)],\n { result: { promise: true, copy: true } }\n );\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async *entries() {\n let entriesJson;\n try {\n entriesJson = await __FileSystemDirectoryHandle_readDirectory_ref.apply(\n undefined,\n [this._getInstanceId()],\n { result: { promise: true, copy: true } }\n );\n } catch (err) {\n throw __decodeError(err);\n }\n const entries = JSON.parse(entriesJson);\n for (const entry of entries) {\n if (entry.kind === 'file') {\n yield [entry.name, FileSystemFileHandle._fromInstanceId(entry.instanceId)];\n } else {\n yield [entry.name, FileSystemDirectoryHandle._fromInstanceId(entry.instanceId)];\n }\n }\n }\n\n async *keys() {\n for await (const [name] of this.entries()) {\n yield name;\n }\n }\n\n async *values() {\n for await (const [, handle] of this.entries()) {\n yield handle;\n }\n }\n\n [Symbol.asyncIterator]() {\n return this.entries();\n }\n\n async isSameEntry(other) {\n if (!(other instanceof FileSystemDirectoryHandle)) {\n return false;\n }\n return __FileSystemDirectoryHandle_isSameEntry(\n this._getInstanceId(),\n other._getInstanceId()\n );\n }\n\n async resolve(possibleDescendant) {\n try {\n const resultJson = await __FileSystemDirectoryHandle_resolve_ref.apply(\n undefined,\n [this._getInstanceId(), possibleDescendant._getInstanceId()],\n { result: { promise: true, copy: true } }\n );\n return resultJson === 'null' ? null : JSON.parse(resultJson);\n } catch (err) {\n throw __decodeError(err);\n }\n }\n }\n\n globalThis.FileSystemDirectoryHandle = FileSystemDirectoryHandle;\n})();\n`;\n\n context.evalSync(directoryHandleCode);\n}\n\n// ============================================================================\n// FileSystemFileHandle Implementation\n// ============================================================================\n\nfunction setupFileSystemFileHandle(\n context: ivm.Context,\n stateMap: Map<number, unknown>\n): void {\n const global = context.global;\n\n // Property getters\n global.setSync(\n \"__FileSystemFileHandle_get_name\",\n new ivm.Callback((instanceId: number) => {\n const state = stateMap.get(instanceId) as FileHandleState | undefined;\n return state?.name ?? \"\";\n })\n );\n\n global.setSync(\n \"__FileSystemFileHandle_get_path\",\n new ivm.Callback((instanceId: number) => {\n const state = stateMap.get(instanceId) as FileHandleState | undefined;\n return state?.path ?? \"\";\n })\n );\n\n // getFile - async reference\n const getFileRef = new ivm.Reference(async (instanceId: number) => {\n const state = stateMap.get(instanceId) as FileHandleState | undefined;\n if (!state) {\n throw new Error(\"[NotFoundError]File handle not found\");\n }\n\n try {\n const fileData = await state.handler.readFile(state.path);\n return JSON.stringify({\n name: state.name,\n data: Array.from(fileData.data),\n size: fileData.size,\n lastModified: fileData.lastModified,\n type: fileData.type,\n });\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n });\n global.setSync(\"__FileSystemFileHandle_getFile_ref\", getFileRef);\n\n // createWritable - async reference\n const createWritableRef = new ivm.Reference(\n async (instanceId: number, _optionsJson: string) => {\n const state = stateMap.get(instanceId) as FileHandleState | undefined;\n if (!state) {\n throw new Error(\"[NotFoundError]File handle not found\");\n }\n\n // Create writable stream state with handler reference\n const streamInstanceId = nextInstanceId++;\n const streamState: WritableStreamState = {\n instanceId: streamInstanceId,\n filePath: state.path,\n position: 0,\n buffer: [],\n closed: false,\n handler: state.handler,\n };\n stateMap.set(streamInstanceId, streamState);\n\n return streamInstanceId;\n }\n );\n global.setSync(\"__FileSystemFileHandle_createWritable_ref\", createWritableRef);\n\n // isSameEntry - sync callback\n global.setSync(\n \"__FileSystemFileHandle_isSameEntry\",\n new ivm.Callback((id1: number, id2: number) => {\n const state1 = stateMap.get(id1) as FileHandleState | undefined;\n const state2 = stateMap.get(id2) as FileHandleState | undefined;\n if (!state1 || !state2) return false;\n return state1.path === state2.path;\n })\n );\n\n // Inject FileSystemFileHandle class\n const fileHandleCode = `\n(function() {\n const _fileHandleInstanceIds = new WeakMap();\n\n function __decodeError(err) {\n if (!(err instanceof Error)) return err;\n const match = err.message.match(/^\\\\[(TypeError|RangeError|NotFoundError|TypeMismatchError|InvalidModificationError|Error)\\\\](.*)$/);\n if (match) {\n if (['NotFoundError', 'TypeMismatchError', 'InvalidModificationError'].includes(match[1])) {\n return new DOMException(match[2], match[1]);\n }\n const ErrorType = globalThis[match[1]] || Error;\n return new ErrorType(match[2]);\n }\n return err;\n }\n\n class FileSystemFileHandle {\n constructor(path, name) {\n // Internal construction from instance ID\n if (typeof path === 'number' && name === null) {\n _fileHandleInstanceIds.set(this, path);\n return;\n }\n const instanceId = __FileSystemFileHandle_construct(path, name);\n _fileHandleInstanceIds.set(this, instanceId);\n }\n\n static _fromInstanceId(instanceId) {\n return new FileSystemFileHandle(instanceId, null);\n }\n\n _getInstanceId() {\n return _fileHandleInstanceIds.get(this);\n }\n\n get kind() {\n return 'file';\n }\n\n get name() {\n return __FileSystemFileHandle_get_name(this._getInstanceId());\n }\n\n async getFile() {\n try {\n const metadataJson = await __FileSystemFileHandle_getFile_ref.apply(\n undefined,\n [this._getInstanceId()],\n { result: { promise: true, copy: true } }\n );\n const metadata = JSON.parse(metadataJson);\n // Create File object from metadata and content\n const content = new Uint8Array(metadata.data);\n return new File([content], metadata.name, {\n type: metadata.type,\n lastModified: metadata.lastModified\n });\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async createWritable(options = {}) {\n try {\n const streamId = await __FileSystemFileHandle_createWritable_ref.apply(\n undefined,\n [this._getInstanceId(), JSON.stringify(options)],\n { result: { promise: true, copy: true } }\n );\n return FileSystemWritableFileStream._fromInstanceId(streamId);\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async isSameEntry(other) {\n if (!(other instanceof FileSystemFileHandle)) {\n return false;\n }\n return __FileSystemFileHandle_isSameEntry(\n this._getInstanceId(),\n other._getInstanceId()\n );\n }\n }\n\n globalThis.FileSystemFileHandle = FileSystemFileHandle;\n})();\n`;\n\n context.evalSync(fileHandleCode);\n}\n\n// ============================================================================\n// FileSystemWritableFileStream Implementation\n// ============================================================================\n\nfunction setupFileSystemWritableFileStream(\n context: ivm.Context,\n stateMap: Map<number, unknown>\n): void {\n const global = context.global;\n\n // write - async reference\n const writeRef = new ivm.Reference(\n async (instanceId: number, bytesJson: string, position: number | null) => {\n const state = stateMap.get(instanceId) as WritableStreamState | undefined;\n if (!state) {\n throw new Error(\"[InvalidStateError]Stream not found\");\n }\n if (state.closed) {\n throw new Error(\"[InvalidStateError]Stream is closed\");\n }\n\n const bytes = JSON.parse(bytesJson) as number[];\n const data = new Uint8Array(bytes);\n\n // Update position if specified\n if (position !== null) {\n state.position = position;\n }\n\n // Write to handler\n try {\n await state.handler.writeFile(state.filePath, data, state.position);\n state.position += data.length;\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n }\n );\n global.setSync(\"__FileSystemWritableFileStream_write_ref\", writeRef);\n\n // seek - sync callback\n global.setSync(\n \"__FileSystemWritableFileStream_seek\",\n new ivm.Callback((instanceId: number, position: number) => {\n const state = stateMap.get(instanceId) as WritableStreamState | undefined;\n if (!state) {\n throw new Error(\"[InvalidStateError]Stream not found\");\n }\n if (state.closed) {\n throw new Error(\"[InvalidStateError]Stream is closed\");\n }\n state.position = position;\n })\n );\n\n // truncate - async reference\n const truncateRef = new ivm.Reference(async (instanceId: number, size: number) => {\n const state = stateMap.get(instanceId) as WritableStreamState | undefined;\n if (!state) {\n throw new Error(\"[InvalidStateError]Stream not found\");\n }\n if (state.closed) {\n throw new Error(\"[InvalidStateError]Stream is closed\");\n }\n\n try {\n await state.handler.truncateFile(state.filePath, size);\n // Adjust position if it's beyond the new size\n if (state.position > size) {\n state.position = size;\n }\n } catch (err) {\n if (err instanceof Error) {\n throw new Error(err.message);\n }\n throw err;\n }\n });\n global.setSync(\"__FileSystemWritableFileStream_truncate_ref\", truncateRef);\n\n // close - async reference\n const closeRef = new ivm.Reference(async (instanceId: number) => {\n const state = stateMap.get(instanceId) as WritableStreamState | undefined;\n if (!state) {\n throw new Error(\"[InvalidStateError]Stream not found\");\n }\n if (state.closed) {\n throw new Error(\"[InvalidStateError]Stream is already closed\");\n }\n\n state.closed = true;\n });\n global.setSync(\"__FileSystemWritableFileStream_close_ref\", closeRef);\n\n // abort - async reference\n const abortRef = new ivm.Reference(async (instanceId: number, _reason: string | null) => {\n const state = stateMap.get(instanceId) as WritableStreamState | undefined;\n if (!state) {\n throw new Error(\"[InvalidStateError]Stream not found\");\n }\n\n state.closed = true;\n state.buffer = []; // Discard any buffered data\n });\n global.setSync(\"__FileSystemWritableFileStream_abort_ref\", abortRef);\n\n // locked - sync callback\n global.setSync(\n \"__FileSystemWritableFileStream_get_locked\",\n new ivm.Callback((instanceId: number) => {\n const state = stateMap.get(instanceId) as WritableStreamState | undefined;\n return state ? !state.closed : false;\n })\n );\n\n // Inject FileSystemWritableFileStream class\n const writableStreamCode = `\n(function() {\n const _writableStreamInstanceIds = new WeakMap();\n\n function __decodeError(err) {\n if (!(err instanceof Error)) return err;\n const match = err.message.match(/^\\\\[(TypeError|RangeError|InvalidStateError|NotFoundError|Error)\\\\](.*)$/);\n if (match) {\n if (['InvalidStateError', 'NotFoundError'].includes(match[1])) {\n return new DOMException(match[2], match[1]);\n }\n const ErrorType = globalThis[match[1]] || Error;\n return new ErrorType(match[2]);\n }\n return err;\n }\n\n class FileSystemWritableFileStream {\n constructor(instanceId) {\n _writableStreamInstanceIds.set(this, instanceId);\n }\n\n static _fromInstanceId(instanceId) {\n return new FileSystemWritableFileStream(instanceId);\n }\n\n _getInstanceId() {\n return _writableStreamInstanceIds.get(this);\n }\n\n async write(data) {\n try {\n // Handle different data types\n let writeData;\n let position = null;\n let type = 'write';\n\n if (data && typeof data === 'object' && !ArrayBuffer.isView(data) &&\n !(data instanceof Blob) && !(data instanceof ArrayBuffer) &&\n !Array.isArray(data) && typeof data.type === 'string') {\n // WriteParams object: { type, data, position, size }\n type = data.type || 'write';\n if (type === 'seek') {\n return this.seek(data.position);\n }\n if (type === 'truncate') {\n return this.truncate(data.size);\n }\n writeData = data.data;\n position = data.position ?? null;\n } else {\n writeData = data;\n }\n\n // Convert data to bytes array for transfer\n let bytes;\n if (typeof writeData === 'string') {\n bytes = Array.from(new TextEncoder().encode(writeData));\n } else if (writeData instanceof Blob) {\n const buffer = await writeData.arrayBuffer();\n bytes = Array.from(new Uint8Array(buffer));\n } else if (writeData instanceof ArrayBuffer) {\n bytes = Array.from(new Uint8Array(writeData));\n } else if (ArrayBuffer.isView(writeData)) {\n bytes = Array.from(new Uint8Array(writeData.buffer, writeData.byteOffset, writeData.byteLength));\n } else if (Array.isArray(writeData)) {\n bytes = writeData;\n } else {\n throw new TypeError('Invalid data type for write');\n }\n\n await __FileSystemWritableFileStream_write_ref.apply(\n undefined,\n [this._getInstanceId(), JSON.stringify(bytes), position],\n { result: { promise: true, copy: true } }\n );\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n seek(position) {\n try {\n __FileSystemWritableFileStream_seek(this._getInstanceId(), position);\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async truncate(size) {\n try {\n await __FileSystemWritableFileStream_truncate_ref.apply(\n undefined,\n [this._getInstanceId(), size],\n { result: { promise: true, copy: true } }\n );\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async close() {\n try {\n await __FileSystemWritableFileStream_close_ref.apply(\n undefined,\n [this._getInstanceId()],\n { result: { promise: true, copy: true } }\n );\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n async abort(reason) {\n try {\n await __FileSystemWritableFileStream_abort_ref.apply(\n undefined,\n [this._getInstanceId(), reason ? String(reason) : null],\n { result: { promise: true, copy: true } }\n );\n } catch (err) {\n throw __decodeError(err);\n }\n }\n\n get locked() {\n return __FileSystemWritableFileStream_get_locked(this._getInstanceId());\n }\n\n getWriter() {\n const stream = this;\n let released = false;\n let closedResolve;\n let closedReject;\n const closedPromise = new Promise((resolve, reject) => {\n closedResolve = resolve;\n closedReject = reject;\n });\n\n return {\n get closed() {\n return closedPromise;\n },\n get desiredSize() {\n return 1;\n },\n get ready() {\n return Promise.resolve();\n },\n async write(chunk) {\n if (released) {\n return Promise.reject(new TypeError('Writer has been released'));\n }\n try {\n await stream.write(chunk);\n return Promise.resolve();\n } catch (err) {\n return Promise.reject(err);\n }\n },\n async close() {\n if (released) {\n return Promise.reject(new TypeError('Writer has been released'));\n }\n try {\n await stream.close();\n closedResolve();\n return Promise.resolve();\n } catch (err) {\n closedReject(err);\n return Promise.reject(err);\n }\n },\n async abort(reason) {\n if (released) {\n return Promise.reject(new TypeError('Writer has been released'));\n }\n try {\n await stream.abort(reason);\n closedReject(reason || new Error('Stream aborted'));\n return Promise.resolve();\n } catch (err) {\n return Promise.reject(err);\n }\n },\n releaseLock() {\n released = true;\n }\n };\n }\n }\n\n globalThis.FileSystemWritableFileStream = FileSystemWritableFileStream;\n})();\n`;\n\n context.evalSync(writableStreamCode);\n}\n\n// ============================================================================\n// Global getDirectory(path) Implementation\n// ============================================================================\n\nfunction setupGetDirectoryGlobal(\n context: ivm.Context,\n stateMap: Map<number, unknown>,\n options: FsOptions\n): void {\n const global = context.global;\n\n // getDirectory - async reference that creates directory handle at specified path\n const getDirectoryRef = new ivm.Reference(async (path: string) => {\n // Get handler for this path from the options factory\n const handler = await options.getDirectory(path);\n\n const instanceId = nextInstanceId++;\n // Path is \"/\" since handler is rooted at the requested path\n const state: DirectoryHandleState = {\n instanceId,\n path: \"/\",\n name: path.split(\"/\").filter(Boolean).pop() || \"\",\n handler,\n };\n stateMap.set(instanceId, state);\n return instanceId;\n });\n global.setSync(\"__getDirectory_ref\", getDirectoryRef);\n\n // Inject global getDirectory (async)\n const getDirectoryCode = `\n(function() {\n globalThis.getDirectory = async function(path) {\n const instanceId = await __getDirectory_ref.apply(\n undefined,\n [path],\n { result: { promise: true, copy: true } }\n );\n return FileSystemDirectoryHandle._fromInstanceId(instanceId);\n };\n})();\n`;\n context.evalSync(getDirectoryCode);\n}\n\n// ============================================================================\n// Main Setup Function\n// ============================================================================\n\n/**\n * Setup File System Access API in an isolated-vm context\n *\n * Provides an OPFS-compatible FileSystemDirectoryHandle API\n *\n * @example\n * const handle = await setupFs(context, {\n * getDirectory: async (path) => {\n * // Return a FileSystemHandler rooted at the given path\n * return createNodeFileSystemHandler(`./data${path}`);\n * }\n * });\n *\n * await context.eval(`\n * const root = await getDirectory(\"/uploads\");\n * const fileHandle = await root.getFileHandle(\"test.txt\", { create: true });\n * const writable = await fileHandle.createWritable();\n * await writable.write(\"hello world\");\n * await writable.close();\n * `);\n */\nexport async function setupFs(\n context: ivm.Context,\n options: FsOptions\n): Promise<FsHandle> {\n // Setup core APIs first (Blob, File, AbortController, Streams, etc.)\n await setupCore(context);\n\n const stateMap = getInstanceStateMapForContext(context);\n\n // Setup FileSystemDirectoryHandle\n setupFileSystemDirectoryHandle(context, stateMap);\n\n // Setup FileSystemFileHandle\n setupFileSystemFileHandle(context, stateMap);\n\n // Setup FileSystemWritableFileStream\n setupFileSystemWritableFileStream(context, stateMap);\n\n // Setup global getDirectory(path)\n setupGetDirectoryGlobal(context, stateMap, options);\n\n return {\n dispose() {\n // Clear state for this context\n stateMap.clear();\n },\n };\n}\n\n// Export node adapter\nexport { createNodeFileSystemHandler } from \"./node-adapter.mjs\";\nexport type { NodeFileSystemHandlerOptions } from \"./node-adapter.mjs\";\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
8
|
+
"debugId": "195466EF06E0806564756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -43,6 +43,206 @@ if (!WebSocketShim) {
|
|
|
43
43
|
|
|
44
44
|
export { WebSocketShim as WebSocket };
|
|
45
45
|
export default WebSocketShim;
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
if (source === "async_hooks" || source === "node:async_hooks") {
|
|
49
|
+
return `
|
|
50
|
+
const AsyncContextShim = globalThis.AsyncContext;
|
|
51
|
+
const asyncInternals = globalThis.__isolateAsyncContextInternals;
|
|
52
|
+
|
|
53
|
+
if (!AsyncContextShim || !asyncInternals?.AsyncContextFrame) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
"node:async_hooks requires AsyncContext support in the isolate engine."
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { AsyncContextFrame, currentAsyncResource } = asyncInternals;
|
|
60
|
+
let nextAsyncResourceId = 1;
|
|
61
|
+
const NO_STORE = Symbol("AsyncLocalStorage.noStore");
|
|
62
|
+
|
|
63
|
+
function currentAsyncResourceState() {
|
|
64
|
+
return currentAsyncResource.get() ?? {
|
|
65
|
+
asyncId: 0,
|
|
66
|
+
triggerAsyncId: 0,
|
|
67
|
+
resource: undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
class AsyncResource {
|
|
72
|
+
#snapshot;
|
|
73
|
+
#asyncId;
|
|
74
|
+
#triggerAsyncId;
|
|
75
|
+
#destroyed;
|
|
76
|
+
|
|
77
|
+
constructor(type, options = {}) {
|
|
78
|
+
void type;
|
|
79
|
+
|
|
80
|
+
const normalizedOptions =
|
|
81
|
+
options && typeof options === "object" ? options : {};
|
|
82
|
+
const currentState = currentAsyncResourceState();
|
|
83
|
+
|
|
84
|
+
this.#snapshot = new AsyncContextShim.Snapshot();
|
|
85
|
+
this.#asyncId = nextAsyncResourceId++;
|
|
86
|
+
this.#triggerAsyncId = Number.isSafeInteger(normalizedOptions.triggerAsyncId)
|
|
87
|
+
? normalizedOptions.triggerAsyncId
|
|
88
|
+
: currentState.asyncId;
|
|
89
|
+
this.#destroyed = false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
runInAsyncScope(fn, thisArg, ...args) {
|
|
93
|
+
if (typeof fn !== "function") {
|
|
94
|
+
throw new TypeError("AsyncResource.runInAsyncScope requires a function");
|
|
95
|
+
}
|
|
96
|
+
const state = {
|
|
97
|
+
asyncId: this.#asyncId,
|
|
98
|
+
triggerAsyncId: this.#triggerAsyncId,
|
|
99
|
+
resource: this,
|
|
100
|
+
};
|
|
101
|
+
return this.#snapshot.run(
|
|
102
|
+
() => currentAsyncResource.run(
|
|
103
|
+
state,
|
|
104
|
+
() => Reflect.apply(fn, thisArg, args),
|
|
105
|
+
),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
bind(fn, thisArg) {
|
|
110
|
+
if (typeof fn !== "function") {
|
|
111
|
+
throw new TypeError("AsyncResource.bind requires a function");
|
|
112
|
+
}
|
|
113
|
+
const resource = this;
|
|
114
|
+
return function bound(...args) {
|
|
115
|
+
return resource.runInAsyncScope(
|
|
116
|
+
fn,
|
|
117
|
+
thisArg === undefined ? this : thisArg,
|
|
118
|
+
...args,
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
emitDestroy() {
|
|
124
|
+
if (this.#destroyed) {
|
|
125
|
+
throw new Error("AsyncResource.emitDestroy() must only be called once");
|
|
126
|
+
}
|
|
127
|
+
this.#destroyed = true;
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
asyncId() {
|
|
132
|
+
return this.#asyncId;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
triggerAsyncId() {
|
|
136
|
+
return this.#triggerAsyncId;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static bind(fn, type, thisArg) {
|
|
140
|
+
return new AsyncResource(type).bind(fn, thisArg);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
class AsyncLocalStorage {
|
|
145
|
+
#variable;
|
|
146
|
+
#defaultValue;
|
|
147
|
+
#token;
|
|
148
|
+
#disabled;
|
|
149
|
+
|
|
150
|
+
constructor(options = {}) {
|
|
151
|
+
const normalizedOptions =
|
|
152
|
+
options && typeof options === "object" ? options : {};
|
|
153
|
+
|
|
154
|
+
this.#defaultValue = normalizedOptions.defaultValue;
|
|
155
|
+
this.#token = Symbol("AsyncLocalStorage.token");
|
|
156
|
+
this.#disabled = false;
|
|
157
|
+
this.#variable = new AsyncContextShim.Variable({
|
|
158
|
+
defaultValue: NO_STORE,
|
|
159
|
+
name: normalizedOptions.name,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
get name() {
|
|
164
|
+
return this.#variable.name;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
disable() {
|
|
168
|
+
this.#token = Symbol("AsyncLocalStorage.token");
|
|
169
|
+
this.#disabled = true;
|
|
170
|
+
AsyncContextFrame.disable(this.#variable);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
enterWith(store) {
|
|
174
|
+
this.#disabled = false;
|
|
175
|
+
AsyncContextFrame.set(
|
|
176
|
+
new AsyncContextFrame(this.#variable, {
|
|
177
|
+
token: this.#token,
|
|
178
|
+
hasValue: true,
|
|
179
|
+
store,
|
|
180
|
+
}),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
run(store, callback, ...args) {
|
|
185
|
+
if (typeof callback !== "function") {
|
|
186
|
+
throw new TypeError("AsyncLocalStorage.run requires a function");
|
|
187
|
+
}
|
|
188
|
+
this.#disabled = false;
|
|
189
|
+
return this.#variable.run(
|
|
190
|
+
{
|
|
191
|
+
token: this.#token,
|
|
192
|
+
hasValue: true,
|
|
193
|
+
store,
|
|
194
|
+
},
|
|
195
|
+
callback,
|
|
196
|
+
...args,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
exit(callback, ...args) {
|
|
201
|
+
if (typeof callback !== "function") {
|
|
202
|
+
throw new TypeError("AsyncLocalStorage.exit requires a function");
|
|
203
|
+
}
|
|
204
|
+
return this.#variable.run(
|
|
205
|
+
{
|
|
206
|
+
token: this.#token,
|
|
207
|
+
hasValue: false,
|
|
208
|
+
store: undefined,
|
|
209
|
+
},
|
|
210
|
+
callback,
|
|
211
|
+
...args,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
getStore() {
|
|
216
|
+
const entry = this.#variable.get();
|
|
217
|
+
if (entry === NO_STORE) {
|
|
218
|
+
return this.#disabled ? undefined : this.#defaultValue;
|
|
219
|
+
}
|
|
220
|
+
if (!entry || entry.token !== this.#token) {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
if (!entry.hasValue) {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
return entry.store;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
static bind(fn) {
|
|
230
|
+
return AsyncResource.bind(fn, "AsyncLocalStorage.bind");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
static snapshot() {
|
|
234
|
+
const snapshot = new AsyncContextShim.Snapshot();
|
|
235
|
+
return function runInAsyncScope(fn, ...args) {
|
|
236
|
+
if (typeof fn !== "function") {
|
|
237
|
+
throw new TypeError("AsyncLocalStorage.snapshot requires a function");
|
|
238
|
+
}
|
|
239
|
+
return snapshot.run(fn, ...args);
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export { AsyncLocalStorage, AsyncResource };
|
|
245
|
+
export default { AsyncLocalStorage, AsyncResource };
|
|
46
246
|
`;
|
|
47
247
|
}
|
|
48
248
|
return `export default {};
|
|
@@ -137,21 +337,38 @@ function resolveImportEntryFromPackageJson(packageJson, subpath) {
|
|
|
137
337
|
return pickImportTarget(exportsField);
|
|
138
338
|
}
|
|
139
339
|
if (typeof exportsField === "object" && exportsField !== null && !Array.isArray(exportsField)) {
|
|
140
|
-
const
|
|
141
|
-
|
|
340
|
+
const exportsRecord = exportsField;
|
|
341
|
+
const directTarget = exportsRecord[exportKey];
|
|
342
|
+
if (directTarget !== undefined) {
|
|
343
|
+
return pickImportTarget(directTarget);
|
|
344
|
+
}
|
|
345
|
+
for (const [pattern, target] of Object.entries(exportsRecord)) {
|
|
346
|
+
if (!pattern.includes("*")) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const starIndex = pattern.indexOf("*");
|
|
350
|
+
const prefix = pattern.slice(0, starIndex);
|
|
351
|
+
const suffix = pattern.slice(starIndex + 1);
|
|
352
|
+
if (!exportKey.startsWith(prefix) || !exportKey.endsWith(suffix)) {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
const wildcardMatch = exportKey.slice(prefix.length, suffix.length > 0 ? -suffix.length : undefined);
|
|
356
|
+
return pickImportTarget(target, wildcardMatch);
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
142
359
|
}
|
|
143
360
|
return exportKey === "." ? pickImportTarget(exportsField) : null;
|
|
144
361
|
}
|
|
145
362
|
function isConditionalExportsObject(value) {
|
|
146
363
|
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).every((key) => !key.startsWith("."));
|
|
147
364
|
}
|
|
148
|
-
function pickImportTarget(value) {
|
|
365
|
+
function pickImportTarget(value, wildcardMatch) {
|
|
149
366
|
if (typeof value === "string") {
|
|
150
|
-
return value;
|
|
367
|
+
return wildcardMatch === undefined ? value : value.replaceAll("*", wildcardMatch);
|
|
151
368
|
}
|
|
152
369
|
if (Array.isArray(value)) {
|
|
153
370
|
for (const item of value) {
|
|
154
|
-
const target = pickImportTarget(item);
|
|
371
|
+
const target = pickImportTarget(item, wildcardMatch);
|
|
155
372
|
if (target) {
|
|
156
373
|
return target;
|
|
157
374
|
}
|
|
@@ -164,14 +381,14 @@ function pickImportTarget(value) {
|
|
|
164
381
|
const record = value;
|
|
165
382
|
for (const key of ["workerd", "worker", "edge-light", "import", "module", "default", "browser"]) {
|
|
166
383
|
if (key in record) {
|
|
167
|
-
const target = pickImportTarget(record[key]);
|
|
384
|
+
const target = pickImportTarget(record[key], wildcardMatch);
|
|
168
385
|
if (target) {
|
|
169
386
|
return target;
|
|
170
387
|
}
|
|
171
388
|
}
|
|
172
389
|
}
|
|
173
390
|
for (const nestedValue of Object.values(record)) {
|
|
174
|
-
const target = pickImportTarget(nestedValue);
|
|
391
|
+
const target = pickImportTarget(nestedValue, wildcardMatch);
|
|
175
392
|
if (target) {
|
|
176
393
|
return target;
|
|
177
394
|
}
|
|
@@ -424,4 +641,4 @@ export {
|
|
|
424
641
|
bundleHostFile
|
|
425
642
|
};
|
|
426
643
|
|
|
427
|
-
//# debugId=
|
|
644
|
+
//# debugId=0A04C742B1AF50E764756E2164756E21
|