@ricsam/isolate 0.1.10 → 0.1.12
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 +257 -18
- package/dist/cjs/bridge/diagnostics.cjs +37 -2
- package/dist/cjs/bridge/diagnostics.cjs.map +3 -3
- package/dist/cjs/bridge/runtime-bindings.cjs +230 -51
- package/dist/cjs/bridge/runtime-bindings.cjs.map +3 -3
- package/dist/cjs/bridge/sandbox-isolate.cjs +464 -0
- package/dist/cjs/bridge/sandbox-isolate.cjs.map +10 -0
- package/dist/cjs/host/create-isolate-host.cjs +130 -25
- package/dist/cjs/host/create-isolate-host.cjs.map +3 -3
- package/dist/cjs/host/nested-host-controller.cjs +369 -0
- package/dist/cjs/host/nested-host-controller.cjs.map +10 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/internal/browser-source.cjs +102 -0
- package/dist/cjs/internal/browser-source.cjs.map +10 -0
- package/dist/cjs/internal/client/connection.cjs +163 -172
- package/dist/cjs/internal/client/connection.cjs.map +3 -3
- package/dist/cjs/internal/daemon/callback-fs-handler.cjs +3 -3
- package/dist/cjs/internal/daemon/callback-fs-handler.cjs.map +3 -3
- package/dist/cjs/internal/daemon/connection.cjs +156 -13
- package/dist/cjs/internal/daemon/connection.cjs.map +3 -3
- package/dist/cjs/internal/playwright/client.cjs +4 -2
- package/dist/cjs/internal/playwright/client.cjs.map +3 -3
- package/dist/cjs/internal/playwright/handler.cjs +298 -25
- package/dist/cjs/internal/playwright/handler.cjs.map +3 -3
- package/dist/cjs/internal/playwright/index.cjs +54 -8
- package/dist/cjs/internal/playwright/index.cjs.map +3 -3
- package/dist/cjs/internal/playwright/types.cjs +3 -1
- package/dist/cjs/internal/playwright/types.cjs.map +3 -3
- package/dist/cjs/internal/protocol/codec.cjs +16 -5
- package/dist/cjs/internal/protocol/codec.cjs.map +3 -3
- package/dist/cjs/internal/protocol/marshalValue.cjs +37 -6
- package/dist/cjs/internal/protocol/marshalValue.cjs.map +3 -3
- package/dist/cjs/internal/protocol/types.cjs +2 -1
- package/dist/cjs/internal/protocol/types.cjs.map +3 -3
- package/dist/cjs/internal/runtime/index.cjs +377 -22
- package/dist/cjs/internal/runtime/index.cjs.map +3 -3
- package/dist/cjs/internal/typecheck/index.cjs +2 -1
- package/dist/cjs/internal/typecheck/index.cjs.map +3 -3
- package/dist/cjs/internal/typecheck/isolate-types.cjs +218 -13
- package/dist/cjs/internal/typecheck/isolate-types.cjs.map +3 -3
- package/dist/cjs/internal/typecheck/typecheck.cjs +2 -3
- package/dist/cjs/internal/typecheck/typecheck.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/playwright.cjs +76 -0
- package/dist/cjs/playwright.cjs.map +10 -0
- package/dist/cjs/runtime/namespaced-runtime.cjs +181 -0
- package/dist/cjs/runtime/namespaced-runtime.cjs.map +10 -0
- package/dist/cjs/runtime/script-runtime.cjs +14 -12
- package/dist/cjs/runtime/script-runtime.cjs.map +3 -3
- package/dist/cjs/runtime/test-runtime.cjs +113 -0
- package/dist/cjs/runtime/test-runtime.cjs.map +10 -0
- package/dist/cjs/server/app-server.cjs +16 -9
- package/dist/cjs/server/app-server.cjs.map +3 -3
- package/dist/cjs/typecheck/index.cjs +2 -1
- package/dist/cjs/typecheck/index.cjs.map +3 -3
- package/dist/mjs/bridge/diagnostics.mjs +37 -2
- package/dist/mjs/bridge/diagnostics.mjs.map +3 -3
- package/dist/mjs/bridge/runtime-bindings.mjs +233 -51
- package/dist/mjs/bridge/runtime-bindings.mjs.map +3 -3
- package/dist/mjs/bridge/sandbox-isolate.mjs +424 -0
- package/dist/mjs/bridge/sandbox-isolate.mjs.map +10 -0
- package/dist/mjs/host/create-isolate-host.mjs +132 -25
- package/dist/mjs/host/create-isolate-host.mjs.map +3 -3
- package/dist/mjs/host/nested-host-controller.mjs +333 -0
- package/dist/mjs/host/nested-host-controller.mjs.map +10 -0
- package/dist/mjs/index.mjs.map +1 -1
- package/dist/mjs/internal/browser-source.mjs +62 -0
- package/dist/mjs/internal/browser-source.mjs.map +10 -0
- package/dist/mjs/internal/client/connection.mjs +165 -173
- package/dist/mjs/internal/client/connection.mjs.map +3 -3
- package/dist/mjs/internal/daemon/callback-fs-handler.mjs +3 -3
- package/dist/mjs/internal/daemon/callback-fs-handler.mjs.map +3 -3
- package/dist/mjs/internal/daemon/connection.mjs +156 -13
- package/dist/mjs/internal/daemon/connection.mjs.map +3 -3
- package/dist/mjs/internal/playwright/client.mjs +7 -3
- package/dist/mjs/internal/playwright/client.mjs.map +3 -3
- package/dist/mjs/internal/playwright/handler.mjs +300 -26
- package/dist/mjs/internal/playwright/handler.mjs.map +3 -3
- package/dist/mjs/internal/playwright/index.mjs +59 -9
- package/dist/mjs/internal/playwright/index.mjs.map +3 -3
- package/dist/mjs/internal/playwright/types.mjs +3 -1
- package/dist/mjs/internal/playwright/types.mjs.map +3 -3
- package/dist/mjs/internal/protocol/codec.mjs +16 -5
- package/dist/mjs/internal/protocol/codec.mjs.map +3 -3
- package/dist/mjs/internal/protocol/marshalValue.mjs +38 -6
- package/dist/mjs/internal/protocol/marshalValue.mjs.map +3 -3
- package/dist/mjs/internal/protocol/types.mjs +2 -1
- package/dist/mjs/internal/protocol/types.mjs.map +3 -3
- package/dist/mjs/internal/runtime/index.mjs +377 -22
- package/dist/mjs/internal/runtime/index.mjs.map +3 -3
- package/dist/mjs/internal/typecheck/index.mjs +3 -1
- package/dist/mjs/internal/typecheck/index.mjs.map +3 -3
- package/dist/mjs/internal/typecheck/isolate-types.mjs +218 -13
- package/dist/mjs/internal/typecheck/isolate-types.mjs.map +3 -3
- package/dist/mjs/internal/typecheck/typecheck.mjs +2 -3
- package/dist/mjs/internal/typecheck/typecheck.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/playwright.mjs +47 -0
- package/dist/mjs/playwright.mjs.map +10 -0
- package/dist/mjs/runtime/namespaced-runtime.mjs +143 -0
- package/dist/mjs/runtime/namespaced-runtime.mjs.map +10 -0
- package/dist/mjs/runtime/script-runtime.mjs +16 -12
- package/dist/mjs/runtime/script-runtime.mjs.map +3 -3
- package/dist/mjs/runtime/test-runtime.mjs +78 -0
- package/dist/mjs/runtime/test-runtime.mjs.map +10 -0
- package/dist/mjs/server/app-server.mjs +23 -11
- package/dist/mjs/server/app-server.mjs.map +3 -3
- package/dist/mjs/typecheck/index.mjs +2 -1
- package/dist/mjs/typecheck/index.mjs.map +3 -3
- package/dist/types/bridge/diagnostics.d.ts +6 -1
- package/dist/types/bridge/runtime-bindings.d.ts +5 -1
- package/dist/types/bridge/sandbox-isolate.d.ts +21 -0
- package/dist/types/host/nested-host-controller.d.ts +15 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/internal/browser-source.d.ts +10 -0
- package/dist/types/internal/client/types.d.ts +9 -0
- package/dist/types/internal/daemon/types.d.ts +0 -2
- package/dist/types/internal/playwright/client.d.ts +2 -2
- package/dist/types/internal/playwright/handler.d.ts +27 -4
- package/dist/types/internal/playwright/index.d.ts +2 -2
- package/dist/types/internal/playwright/types.d.ts +33 -1
- package/dist/types/internal/protocol/codec.d.ts +12 -2
- package/dist/types/internal/protocol/marshalValue.d.ts +3 -2
- package/dist/types/internal/protocol/types.d.ts +33 -2
- package/dist/types/internal/runtime/index.d.ts +5 -0
- package/dist/types/internal/typecheck/index.d.ts +1 -1
- package/dist/types/internal/typecheck/isolate-types.d.ts +6 -4
- package/dist/types/internal/typecheck/typecheck.d.ts +1 -1
- package/dist/types/playwright.d.ts +26 -0
- package/dist/types/runtime/namespaced-runtime.d.ts +11 -0
- package/dist/types/runtime/script-runtime.d.ts +2 -1
- package/dist/types/runtime/test-runtime.d.ts +4 -0
- package/dist/types/server/app-server.d.ts +2 -1
- package/dist/types/types.d.ts +75 -33
- package/package.json +8 -3
- package/dist/cjs/browser/browser-runtime.cjs +0 -157
- package/dist/cjs/browser/browser-runtime.cjs.map +0 -10
- package/dist/mjs/browser/browser-runtime.mjs +0 -93
- package/dist/mjs/browser/browser-runtime.mjs.map +0 -10
- package/dist/types/browser/browser-runtime.d.ts +0 -3
|
@@ -25,10 +25,10 @@ function createCallbackFileSystemHandler(options) {
|
|
|
25
25
|
const resolvePath = (path) => {
|
|
26
26
|
const cleanPath = path.startsWith("/") ? path.slice(1) : path;
|
|
27
27
|
if (!basePath || basePath === "/") {
|
|
28
|
-
return `/${cleanPath}
|
|
28
|
+
return cleanPath ? `/${cleanPath}` : "/";
|
|
29
29
|
}
|
|
30
30
|
const cleanBase = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
31
|
-
return `${cleanBase}/${cleanPath}
|
|
31
|
+
return cleanPath ? `${cleanBase}/${cleanPath}` : cleanBase;
|
|
32
32
|
};
|
|
33
33
|
const getCallbackId = (name) => {
|
|
34
34
|
return callbackContext.fs[name];
|
|
@@ -312,4 +312,4 @@ export {
|
|
|
312
312
|
createCallbackFileSystemHandler
|
|
313
313
|
};
|
|
314
314
|
|
|
315
|
-
//# debugId=
|
|
315
|
+
//# debugId=0F2A89C86438A46464756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/internal/daemon/callback-fs-handler.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * Callback-based FileSystemHandler adapter.\n *\n * Adapts simple client callbacks (readFile, writeFile, etc.) to the\n * FileSystemHandler interface used by ../fs/index.ts.\n */\n\nimport type { FileSystemHandler } from \"../fs/index.mjs\";\nimport type { ConnectionState, CallbackContext } from \"./types.mjs\";\n\n/** Common MIME type mappings by file extension. */\nconst MIME_TYPES: Record<string, string> = {\n txt: \"text/plain\",\n html: \"text/html\",\n htm: \"text/html\",\n css: \"text/css\",\n js: \"text/javascript\",\n json: \"application/json\",\n xml: \"application/xml\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n svg: \"image/svg+xml\",\n pdf: \"application/pdf\",\n};\n\ninterface InvokeClientCallback {\n (\n connection: ConnectionState,\n callbackId: number,\n args: unknown[],\n options?: { signal?: AbortSignal },\n ): Promise<unknown>;\n}\n\ninterface CallbackFsHandlerOptions {\n connection: ConnectionState;\n callbackContext: CallbackContext;\n invokeClientCallback: InvokeClientCallback;\n basePath?: string;\n getSignal?: () => AbortSignal | undefined;\n}\n\n/**\n * Create a FileSystemHandler that invokes client callbacks.\n *\n * Maps WHATWG FileSystem API operations to simple POSIX-like callbacks.\n * Uses callbackContext for dynamic callback ID lookup to support runtime reuse.\n */\nexport function createCallbackFileSystemHandler(\n options: CallbackFsHandlerOptions\n): FileSystemHandler {\n const {\n connection,\n callbackContext,\n invokeClientCallback,\n basePath = \"\",\n getSignal,\n } = options;\n\n const resolvePath = (path: string): string => {\n // Remove leading slash from the path\n const cleanPath = path.startsWith(\"/\") ? path.slice(1) : path;\n // Handle root case\n if (!basePath || basePath === \"/\") {\n return `/${cleanPath}`;\n }\n // Remove trailing slash from basePath\n const cleanBase = basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n return `${cleanBase}/${cleanPath}`;\n };\n\n // Helper to get current callback ID (supports runtime reuse)\n const getCallbackId = (name: keyof CallbackContext[\"fs\"]): number | undefined => {\n return callbackContext.fs[name];\n };\n\n // Helper to get current connection (supports runtime reuse and reconnection)\n const getConnection = async (): Promise<ConnectionState> => {\n if (callbackContext.connection) {\n return callbackContext.connection;\n }\n if (callbackContext.reconnectionPromise) {\n return callbackContext.reconnectionPromise.promise;\n }\n // Fall back to the originally captured connection\n return connection;\n };\n\n const invokeFsCallback = async (\n conn: ConnectionState,\n callbackId: number,\n args: unknown[],\n ): Promise<unknown> => {\n return invokeClientCallback(conn, callbackId, args, {\n signal: getSignal?.(),\n });\n };\n\n return {\n async getFileHandle(path: string, opts?: { create?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n if (opts?.create) {\n // Ensure file exists by writing empty content if it doesn't exist\n const writeFileId = getCallbackId(\"writeFile\");\n if (writeFileId !== undefined) {\n try {\n // Check if file exists first\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n await invokeFsCallback(conn, statId, [fullPath]);\n // File exists, nothing to do\n return;\n } catch {\n // File doesn't exist, create it\n }\n }\n // Create empty file\n await invokeFsCallback(conn, writeFileId, [\n fullPath,\n new Uint8Array(0),\n ]);\n } catch (err) {\n const error = err as Error;\n throw new Error(`[NotFoundError]${error.message}`);\n }\n }\n return;\n }\n\n // Check file exists\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const result = (await invokeFsCallback(conn, statId, [\n fullPath,\n ])) as { isFile: boolean };\n if (!result.isFile) {\n throw new Error(`[TypeMismatchError]Not a file: ${fullPath}`);\n }\n } catch (err) {\n const error = err as Error;\n if (error.message.includes(\"TypeMismatchError\")) throw error;\n throw new Error(`[NotFoundError]File not found: ${fullPath}`);\n }\n }\n },\n\n async getDirectoryHandle(path: string, opts?: { create?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n if (opts?.create) {\n const mkdirId = getCallbackId(\"mkdir\");\n if (mkdirId !== undefined) {\n try {\n await invokeFsCallback(conn, mkdirId, [\n fullPath,\n { recursive: true },\n ]);\n } catch {\n // Ignore error if directory already exists\n }\n }\n return;\n }\n\n // Check directory exists\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const result = (await invokeFsCallback(conn, statId, [\n fullPath,\n ])) as { isDirectory: boolean };\n if (!result.isDirectory) {\n throw new Error(`[TypeMismatchError]Not a directory: ${fullPath}`);\n }\n } catch (err) {\n const error = err as Error;\n if (error.message.includes(\"TypeMismatchError\")) throw error;\n throw new Error(`[NotFoundError]Directory not found: ${fullPath}`);\n }\n }\n },\n\n async removeEntry(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n // Check if it's a file or directory\n let isFile = true;\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const result = (await invokeFsCallback(conn, statId, [\n fullPath,\n ])) as { isFile: boolean; isDirectory: boolean };\n isFile = result.isFile;\n } catch {\n throw new Error(`[NotFoundError]Entry not found: ${fullPath}`);\n }\n }\n\n if (isFile) {\n const unlinkId = getCallbackId(\"unlink\");\n if (unlinkId === undefined) {\n throw new Error(`[NotAllowedError]File deletion not supported`);\n }\n await invokeFsCallback(conn, unlinkId, [fullPath]);\n } else {\n const rmdirId = getCallbackId(\"rmdir\");\n if (rmdirId === undefined) {\n throw new Error(`[NotAllowedError]Directory deletion not supported`);\n }\n // Note: recursive option may need special handling\n await invokeFsCallback(conn, rmdirId, [fullPath]);\n }\n },\n\n async readDirectory(\n path: string\n ): Promise<Array<{ name: string; kind: \"file\" | \"directory\" }>> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const readdirId = getCallbackId(\"readdir\");\n if (readdirId === undefined) {\n throw new Error(`[NotAllowedError]Directory reading not supported`);\n }\n\n const entries = (await invokeFsCallback(conn, readdirId, [\n fullPath,\n ])) as string[];\n\n // We need to stat each entry to determine if it's a file or directory\n const result: Array<{ name: string; kind: \"file\" | \"directory\" }> = [];\n\n const statId = getCallbackId(\"stat\");\n for (const name of entries) {\n const entryPath = fullPath ? `${fullPath}/${name}` : name;\n let kind: \"file\" | \"directory\" = \"file\";\n\n if (statId !== undefined) {\n try {\n const stat = (await invokeFsCallback(conn, statId, [\n entryPath,\n ])) as { isFile: boolean; isDirectory: boolean };\n kind = stat.isDirectory ? \"directory\" : \"file\";\n } catch {\n // Default to file if stat fails\n }\n }\n\n result.push({ name, kind });\n }\n\n return result;\n },\n\n async readFile(\n path: string\n ): Promise<{ data: Uint8Array; size: number; lastModified: number; type: string }> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const readFileId = getCallbackId(\"readFile\");\n if (readFileId === undefined) {\n throw new Error(`[NotAllowedError]File reading not supported`);\n }\n\n const data = (await invokeFsCallback(conn, readFileId, [\n fullPath,\n ])) as Uint8Array | ArrayBuffer | number[];\n\n // Convert to Uint8Array if needed\n let bytes: Uint8Array;\n if (data instanceof Uint8Array) {\n bytes = data;\n } else if (Array.isArray(data)) {\n bytes = new Uint8Array(data);\n } else if (data instanceof ArrayBuffer) {\n bytes = new Uint8Array(data);\n } else {\n bytes = new Uint8Array(0);\n }\n\n // Get metadata if stat is available\n let size = bytes.length;\n let lastModified = Date.now();\n\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const stat = (await invokeFsCallback(conn, statId, [\n fullPath,\n ])) as { size: number; lastModified?: number };\n size = stat.size;\n if (stat.lastModified) {\n lastModified = stat.lastModified;\n }\n } catch {\n // Use byte length as fallback\n }\n }\n\n // Determine MIME type from extension\n const ext = path.split(\".\").pop()?.toLowerCase() || \"\";\n const type = MIME_TYPES[ext] || \"application/octet-stream\";\n\n return { data: bytes, size, lastModified, type };\n },\n\n async writeFile(path: string, data: Uint8Array, position?: number): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const writeFileId = getCallbackId(\"writeFile\");\n if (writeFileId === undefined) {\n throw new Error(`[NotAllowedError]File writing not supported`);\n }\n\n // Note: position parameter for partial writes may need special handling\n // Simple implementation overwrites entire file\n if (position !== undefined && position > 0) {\n // For positional writes, we need to read existing content and merge\n const readFileId = getCallbackId(\"readFile\");\n if (readFileId !== undefined) {\n try {\n const existing = (await invokeFsCallback(\n conn,\n readFileId,\n [fullPath]\n )) as Uint8Array | ArrayBuffer | number[];\n\n let existingBytes: Uint8Array;\n if (existing instanceof Uint8Array) {\n existingBytes = existing;\n } else if (Array.isArray(existing)) {\n existingBytes = new Uint8Array(existing);\n } else if (existing instanceof ArrayBuffer) {\n existingBytes = new Uint8Array(existing);\n } else {\n existingBytes = new Uint8Array(0);\n }\n\n // Create merged buffer\n const newSize = Math.max(existingBytes.length, position + data.length);\n const merged = new Uint8Array(newSize);\n merged.set(existingBytes);\n merged.set(data, position);\n\n await invokeFsCallback(conn, writeFileId, [\n fullPath,\n merged,\n ]);\n return;\n } catch {\n // File doesn't exist, create new one at position\n const newData = new Uint8Array(position + data.length);\n newData.set(data, position);\n await invokeFsCallback(conn, writeFileId, [\n fullPath,\n newData,\n ]);\n return;\n }\n }\n }\n\n await invokeFsCallback(conn, writeFileId, [fullPath, data]);\n },\n\n async truncateFile(path: string, size: number): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const readFileId = getCallbackId(\"readFile\");\n const writeFileId = getCallbackId(\"writeFile\");\n if (readFileId === undefined || writeFileId === undefined) {\n throw new Error(`[NotAllowedError]File truncation not supported`);\n }\n\n // Read existing content\n const existing = (await invokeFsCallback(conn, readFileId, [\n fullPath,\n ])) as Uint8Array | ArrayBuffer | number[];\n\n let existingBytes: Uint8Array;\n if (existing instanceof Uint8Array) {\n existingBytes = existing;\n } else if (Array.isArray(existing)) {\n existingBytes = new Uint8Array(existing);\n } else if (existing instanceof ArrayBuffer) {\n existingBytes = new Uint8Array(existing);\n } else {\n existingBytes = new Uint8Array(0);\n }\n\n // Create truncated buffer\n const truncated = new Uint8Array(size);\n truncated.set(existingBytes.slice(0, size));\n\n await invokeFsCallback(conn, writeFileId, [fullPath, truncated]);\n },\n\n async getFileMetadata(\n path: string\n ): Promise<{ size: number; lastModified: number; type: string }> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const statId = getCallbackId(\"stat\");\n if (statId === undefined) {\n throw new Error(`[NotAllowedError]File stat not supported`);\n }\n\n const stat = (await invokeFsCallback(conn, statId, [\n fullPath,\n ])) as { size: number; lastModified?: number; isFile: boolean };\n\n if (!stat.isFile) {\n throw new Error(`[TypeMismatchError]Not a file: ${fullPath}`);\n }\n\n // Determine MIME type from extension\n const ext = path.split(\".\").pop()?.toLowerCase() || \"\";\n const type = MIME_TYPES[ext] || \"application/octet-stream\";\n\n return {\n size: stat.size,\n lastModified: stat.lastModified ?? Date.now(),\n type,\n };\n },\n };\n}\n"
|
|
5
|
+
"/**\n * Callback-based FileSystemHandler adapter.\n *\n * Adapts simple client callbacks (readFile, writeFile, etc.) to the\n * FileSystemHandler interface used by ../fs/index.ts.\n */\n\nimport type { FileSystemHandler } from \"../fs/index.mjs\";\nimport type { ConnectionState, CallbackContext } from \"./types.mjs\";\n\n/** Common MIME type mappings by file extension. */\nconst MIME_TYPES: Record<string, string> = {\n txt: \"text/plain\",\n html: \"text/html\",\n htm: \"text/html\",\n css: \"text/css\",\n js: \"text/javascript\",\n json: \"application/json\",\n xml: \"application/xml\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n svg: \"image/svg+xml\",\n pdf: \"application/pdf\",\n};\n\ninterface InvokeClientCallback {\n (\n connection: ConnectionState,\n callbackId: number,\n args: unknown[],\n options?: { signal?: AbortSignal },\n ): Promise<unknown>;\n}\n\ninterface CallbackFsHandlerOptions {\n connection: ConnectionState;\n callbackContext: CallbackContext;\n invokeClientCallback: InvokeClientCallback;\n basePath?: string;\n getSignal?: () => AbortSignal | undefined;\n}\n\n/**\n * Create a FileSystemHandler that invokes client callbacks.\n *\n * Maps WHATWG FileSystem API operations to simple POSIX-like callbacks.\n * Uses callbackContext for dynamic callback ID lookup to support runtime reuse.\n */\nexport function createCallbackFileSystemHandler(\n options: CallbackFsHandlerOptions\n): FileSystemHandler {\n const {\n connection,\n callbackContext,\n invokeClientCallback,\n basePath = \"\",\n getSignal,\n } = options;\n\n const resolvePath = (path: string): string => {\n // Remove leading slash from the path\n const cleanPath = path.startsWith(\"/\") ? path.slice(1) : path;\n // Handle root case\n if (!basePath || basePath === \"/\") {\n return cleanPath ? `/${cleanPath}` : \"/\";\n }\n // Remove trailing slash from basePath\n const cleanBase = basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n return cleanPath ? `${cleanBase}/${cleanPath}` : cleanBase;\n };\n\n // Helper to get current callback ID (supports runtime reuse)\n const getCallbackId = (name: keyof CallbackContext[\"fs\"]): number | undefined => {\n return callbackContext.fs[name];\n };\n\n // Helper to get current connection (supports runtime reuse and reconnection)\n const getConnection = async (): Promise<ConnectionState> => {\n if (callbackContext.connection) {\n return callbackContext.connection;\n }\n if (callbackContext.reconnectionPromise) {\n return callbackContext.reconnectionPromise.promise;\n }\n // Fall back to the originally captured connection\n return connection;\n };\n\n const invokeFsCallback = async (\n conn: ConnectionState,\n callbackId: number,\n args: unknown[],\n ): Promise<unknown> => {\n return invokeClientCallback(conn, callbackId, args, {\n signal: getSignal?.(),\n });\n };\n\n return {\n async getFileHandle(path: string, opts?: { create?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n if (opts?.create) {\n // Ensure file exists by writing empty content if it doesn't exist\n const writeFileId = getCallbackId(\"writeFile\");\n if (writeFileId !== undefined) {\n try {\n // Check if file exists first\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n await invokeFsCallback(conn, statId, [fullPath]);\n // File exists, nothing to do\n return;\n } catch {\n // File doesn't exist, create it\n }\n }\n // Create empty file\n await invokeFsCallback(conn, writeFileId, [\n fullPath,\n new Uint8Array(0),\n ]);\n } catch (err) {\n const error = err as Error;\n throw new Error(`[NotFoundError]${error.message}`);\n }\n }\n return;\n }\n\n // Check file exists\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const result = (await invokeFsCallback(conn, statId, [\n fullPath,\n ])) as { isFile: boolean };\n if (!result.isFile) {\n throw new Error(`[TypeMismatchError]Not a file: ${fullPath}`);\n }\n } catch (err) {\n const error = err as Error;\n if (error.message.includes(\"TypeMismatchError\")) throw error;\n throw new Error(`[NotFoundError]File not found: ${fullPath}`);\n }\n }\n },\n\n async getDirectoryHandle(path: string, opts?: { create?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n if (opts?.create) {\n const mkdirId = getCallbackId(\"mkdir\");\n if (mkdirId !== undefined) {\n try {\n await invokeFsCallback(conn, mkdirId, [\n fullPath,\n { recursive: true },\n ]);\n } catch {\n // Ignore error if directory already exists\n }\n }\n return;\n }\n\n // Check directory exists\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const result = (await invokeFsCallback(conn, statId, [\n fullPath,\n ])) as { isDirectory: boolean };\n if (!result.isDirectory) {\n throw new Error(`[TypeMismatchError]Not a directory: ${fullPath}`);\n }\n } catch (err) {\n const error = err as Error;\n if (error.message.includes(\"TypeMismatchError\")) throw error;\n throw new Error(`[NotFoundError]Directory not found: ${fullPath}`);\n }\n }\n },\n\n async removeEntry(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n // Check if it's a file or directory\n let isFile = true;\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const result = (await invokeFsCallback(conn, statId, [\n fullPath,\n ])) as { isFile: boolean; isDirectory: boolean };\n isFile = result.isFile;\n } catch {\n throw new Error(`[NotFoundError]Entry not found: ${fullPath}`);\n }\n }\n\n if (isFile) {\n const unlinkId = getCallbackId(\"unlink\");\n if (unlinkId === undefined) {\n throw new Error(`[NotAllowedError]File deletion not supported`);\n }\n await invokeFsCallback(conn, unlinkId, [fullPath]);\n } else {\n const rmdirId = getCallbackId(\"rmdir\");\n if (rmdirId === undefined) {\n throw new Error(`[NotAllowedError]Directory deletion not supported`);\n }\n // Note: recursive option may need special handling\n await invokeFsCallback(conn, rmdirId, [fullPath]);\n }\n },\n\n async readDirectory(\n path: string\n ): Promise<Array<{ name: string; kind: \"file\" | \"directory\" }>> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const readdirId = getCallbackId(\"readdir\");\n if (readdirId === undefined) {\n throw new Error(`[NotAllowedError]Directory reading not supported`);\n }\n\n const entries = (await invokeFsCallback(conn, readdirId, [\n fullPath,\n ])) as string[];\n\n // We need to stat each entry to determine if it's a file or directory\n const result: Array<{ name: string; kind: \"file\" | \"directory\" }> = [];\n\n const statId = getCallbackId(\"stat\");\n for (const name of entries) {\n const entryPath = fullPath ? `${fullPath}/${name}` : name;\n let kind: \"file\" | \"directory\" = \"file\";\n\n if (statId !== undefined) {\n try {\n const stat = (await invokeFsCallback(conn, statId, [\n entryPath,\n ])) as { isFile: boolean; isDirectory: boolean };\n kind = stat.isDirectory ? \"directory\" : \"file\";\n } catch {\n // Default to file if stat fails\n }\n }\n\n result.push({ name, kind });\n }\n\n return result;\n },\n\n async readFile(\n path: string\n ): Promise<{ data: Uint8Array; size: number; lastModified: number; type: string }> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const readFileId = getCallbackId(\"readFile\");\n if (readFileId === undefined) {\n throw new Error(`[NotAllowedError]File reading not supported`);\n }\n\n const data = (await invokeFsCallback(conn, readFileId, [\n fullPath,\n ])) as Uint8Array | ArrayBuffer | number[];\n\n // Convert to Uint8Array if needed\n let bytes: Uint8Array;\n if (data instanceof Uint8Array) {\n bytes = data;\n } else if (Array.isArray(data)) {\n bytes = new Uint8Array(data);\n } else if (data instanceof ArrayBuffer) {\n bytes = new Uint8Array(data);\n } else {\n bytes = new Uint8Array(0);\n }\n\n // Get metadata if stat is available\n let size = bytes.length;\n let lastModified = Date.now();\n\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const stat = (await invokeFsCallback(conn, statId, [\n fullPath,\n ])) as { size: number; lastModified?: number };\n size = stat.size;\n if (stat.lastModified) {\n lastModified = stat.lastModified;\n }\n } catch {\n // Use byte length as fallback\n }\n }\n\n // Determine MIME type from extension\n const ext = path.split(\".\").pop()?.toLowerCase() || \"\";\n const type = MIME_TYPES[ext] || \"application/octet-stream\";\n\n return { data: bytes, size, lastModified, type };\n },\n\n async writeFile(path: string, data: Uint8Array, position?: number): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const writeFileId = getCallbackId(\"writeFile\");\n if (writeFileId === undefined) {\n throw new Error(`[NotAllowedError]File writing not supported`);\n }\n\n // Note: position parameter for partial writes may need special handling\n // Simple implementation overwrites entire file\n if (position !== undefined && position > 0) {\n // For positional writes, we need to read existing content and merge\n const readFileId = getCallbackId(\"readFile\");\n if (readFileId !== undefined) {\n try {\n const existing = (await invokeFsCallback(\n conn,\n readFileId,\n [fullPath]\n )) as Uint8Array | ArrayBuffer | number[];\n\n let existingBytes: Uint8Array;\n if (existing instanceof Uint8Array) {\n existingBytes = existing;\n } else if (Array.isArray(existing)) {\n existingBytes = new Uint8Array(existing);\n } else if (existing instanceof ArrayBuffer) {\n existingBytes = new Uint8Array(existing);\n } else {\n existingBytes = new Uint8Array(0);\n }\n\n // Create merged buffer\n const newSize = Math.max(existingBytes.length, position + data.length);\n const merged = new Uint8Array(newSize);\n merged.set(existingBytes);\n merged.set(data, position);\n\n await invokeFsCallback(conn, writeFileId, [\n fullPath,\n merged,\n ]);\n return;\n } catch {\n // File doesn't exist, create new one at position\n const newData = new Uint8Array(position + data.length);\n newData.set(data, position);\n await invokeFsCallback(conn, writeFileId, [\n fullPath,\n newData,\n ]);\n return;\n }\n }\n }\n\n await invokeFsCallback(conn, writeFileId, [fullPath, data]);\n },\n\n async truncateFile(path: string, size: number): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const readFileId = getCallbackId(\"readFile\");\n const writeFileId = getCallbackId(\"writeFile\");\n if (readFileId === undefined || writeFileId === undefined) {\n throw new Error(`[NotAllowedError]File truncation not supported`);\n }\n\n // Read existing content\n const existing = (await invokeFsCallback(conn, readFileId, [\n fullPath,\n ])) as Uint8Array | ArrayBuffer | number[];\n\n let existingBytes: Uint8Array;\n if (existing instanceof Uint8Array) {\n existingBytes = existing;\n } else if (Array.isArray(existing)) {\n existingBytes = new Uint8Array(existing);\n } else if (existing instanceof ArrayBuffer) {\n existingBytes = new Uint8Array(existing);\n } else {\n existingBytes = new Uint8Array(0);\n }\n\n // Create truncated buffer\n const truncated = new Uint8Array(size);\n truncated.set(existingBytes.slice(0, size));\n\n await invokeFsCallback(conn, writeFileId, [fullPath, truncated]);\n },\n\n async getFileMetadata(\n path: string\n ): Promise<{ size: number; lastModified: number; type: string }> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const statId = getCallbackId(\"stat\");\n if (statId === undefined) {\n throw new Error(`[NotAllowedError]File stat not supported`);\n }\n\n const stat = (await invokeFsCallback(conn, statId, [\n fullPath,\n ])) as { size: number; lastModified?: number; isFile: boolean };\n\n if (!stat.isFile) {\n throw new Error(`[TypeMismatchError]Not a file: ${fullPath}`);\n }\n\n // Determine MIME type from extension\n const ext = path.split(\".\").pop()?.toLowerCase() || \"\";\n const type = MIME_TYPES[ext] || \"application/octet-stream\";\n\n return {\n size: stat.size,\n lastModified: stat.lastModified ?? Date.now(),\n type,\n };\n },\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";AAWA,IAAM,aAAqC;AAAA,EACzC,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAyBO,SAAS,+BAA+B,CAC7C,SACmB;AAAA,EACnB;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,MACE;AAAA,EAEJ,MAAM,cAAc,CAAC,SAAyB;AAAA,IAE5C,MAAM,YAAY,KAAK,WAAW,GAAG,IAAI,KAAK,MAAM,CAAC,IAAI;AAAA,IAEzD,IAAI,CAAC,YAAY,aAAa,KAAK;AAAA,MACjC,OAAO,IAAI;AAAA,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "0F2A89C86438A46464756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -181,6 +181,9 @@ async function handleMessage(message, connection, state) {
|
|
|
181
181
|
case MessageType.DISPOSE_RUNTIME:
|
|
182
182
|
await handleDisposeRuntime(message, connection, state);
|
|
183
183
|
break;
|
|
184
|
+
case MessageType.DISPOSE_NAMESPACE:
|
|
185
|
+
await handleDisposeNamespace(message, connection, state);
|
|
186
|
+
break;
|
|
184
187
|
case MessageType.EVAL:
|
|
185
188
|
await handleEval(message, connection, state);
|
|
186
189
|
break;
|
|
@@ -334,6 +337,7 @@ async function hardDeleteRuntime(instance, state, reason) {
|
|
|
334
337
|
}
|
|
335
338
|
await instance.runtime.dispose();
|
|
336
339
|
} finally {
|
|
340
|
+
detachRuntimeFromOwningConnection(instance, state);
|
|
337
341
|
state.isolates.delete(instance.isolateId);
|
|
338
342
|
if (instance.namespaceId != null) {
|
|
339
343
|
const indexed = state.namespacedRuntimes.get(instance.namespaceId);
|
|
@@ -356,6 +360,12 @@ async function hardDeleteRuntime(instance, state, reason) {
|
|
|
356
360
|
logRuntimeLifecycle(state, wasPooled ? "evicted pooled" : "hard-disposed", instance, reason);
|
|
357
361
|
}
|
|
358
362
|
}
|
|
363
|
+
function detachRuntimeFromOwningConnection(instance, state) {
|
|
364
|
+
if (!instance.ownerConnection) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
state.connections.get(instance.ownerConnection)?.isolates.delete(instance.isolateId);
|
|
368
|
+
}
|
|
359
369
|
var RECONNECTION_TIMEOUT_MS = 30000;
|
|
360
370
|
function softDeleteRuntime(instance, state, reason) {
|
|
361
371
|
const runtimeAbortWasAborted = instance.runtimeAbortController?.signal.aborted ?? false;
|
|
@@ -518,7 +528,6 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
|
518
528
|
instance.returnedCallbacks = new Map;
|
|
519
529
|
instance.returnedPromises = new Map;
|
|
520
530
|
instance.returnedIterators = new Map;
|
|
521
|
-
instance.nextLocalCallbackId = 1e6;
|
|
522
531
|
logRuntimeLifecycle(state, "reused pooled", instance);
|
|
523
532
|
logSuspiciousRuntimeOperation(state, "reuse state", instance, connection, {
|
|
524
533
|
disposedForMs,
|
|
@@ -584,14 +593,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
584
593
|
const existing = state.namespacedRuntimes.get(namespaceId);
|
|
585
594
|
if (existing) {
|
|
586
595
|
if (!existing.isDisposed) {
|
|
587
|
-
|
|
588
|
-
sendOk(connection.socket, message.requestId, {
|
|
589
|
-
isolateId: existing.isolateId,
|
|
590
|
-
reused: true
|
|
591
|
-
});
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
|
-
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, `Namespace "${namespaceId}" already has an active runtime`);
|
|
596
|
+
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, `Namespace "${namespaceId}" already has an active runtime`, { name: "NamespaceInUseError" });
|
|
595
597
|
return;
|
|
596
598
|
}
|
|
597
599
|
reuseNamespacedRuntime(existing, connection, message, state);
|
|
@@ -602,7 +604,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
602
604
|
return;
|
|
603
605
|
}
|
|
604
606
|
if (state.namespacedCreatesInFlight.has(namespaceId)) {
|
|
605
|
-
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, `Namespace "${namespaceId}" creation already in progress
|
|
607
|
+
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, `Namespace "${namespaceId}" creation already in progress`, { name: "NamespaceInUseError" });
|
|
606
608
|
return;
|
|
607
609
|
}
|
|
608
610
|
state.namespacedCreatesInFlight.add(namespaceId);
|
|
@@ -657,7 +659,6 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
657
659
|
returnedCallbacks: new Map,
|
|
658
660
|
returnedPromises: new Map,
|
|
659
661
|
returnedIterators: new Map,
|
|
660
|
-
nextLocalCallbackId: 1e6,
|
|
661
662
|
namespaceId,
|
|
662
663
|
isDisposed: false,
|
|
663
664
|
isPoisoned: false,
|
|
@@ -894,6 +895,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
894
895
|
return { ok: false, error: { name: error.name, message: error.message } };
|
|
895
896
|
}
|
|
896
897
|
},
|
|
898
|
+
hasDefaultPage: message.options.playwright?.hasDefaultPage,
|
|
897
899
|
timeout: message.options.playwright?.timeout,
|
|
898
900
|
console: playwrightCallbacks.console,
|
|
899
901
|
onEvent: (event) => {
|
|
@@ -1166,6 +1168,21 @@ async function handleDisposeRuntime(message, connection, state) {
|
|
|
1166
1168
|
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1167
1169
|
}
|
|
1168
1170
|
}
|
|
1171
|
+
async function handleDisposeNamespace(message, connection, state) {
|
|
1172
|
+
const instance = state.namespacedRuntimes.get(message.namespaceId);
|
|
1173
|
+
if (!instance) {
|
|
1174
|
+
sendOk(connection.socket, message.requestId);
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
const requestReason = typeof message.reason === "string" && message.reason.length > 0 ? message.reason : "client requested namespace dispose";
|
|
1178
|
+
try {
|
|
1179
|
+
await hardDeleteRuntime(instance, state, requestReason);
|
|
1180
|
+
sendOk(connection.socket, message.requestId);
|
|
1181
|
+
} catch (err) {
|
|
1182
|
+
const error = err;
|
|
1183
|
+
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1169
1186
|
async function handleEval(message, connection, state) {
|
|
1170
1187
|
const instance = state.isolates.get(message.isolateId);
|
|
1171
1188
|
if (!instance) {
|
|
@@ -1575,10 +1592,136 @@ async function handleCallbackInvoke(message, connection, state) {
|
|
|
1575
1592
|
return;
|
|
1576
1593
|
}
|
|
1577
1594
|
try {
|
|
1578
|
-
|
|
1595
|
+
const allocateLocalCallbackId = () => {
|
|
1596
|
+
if (!instance.ownerConnection) {
|
|
1597
|
+
throw new Error("Cannot register callback without an active client connection");
|
|
1598
|
+
}
|
|
1599
|
+
return connection.nextLocalCallbackId++;
|
|
1600
|
+
};
|
|
1601
|
+
const createMarshalContext = () => ({
|
|
1602
|
+
registerCallback: (fn) => {
|
|
1603
|
+
const callbackId = allocateLocalCallbackId();
|
|
1604
|
+
instance.returnedCallbacks.set(callbackId, fn);
|
|
1605
|
+
return callbackId;
|
|
1606
|
+
},
|
|
1607
|
+
registerPromise: (promise) => {
|
|
1608
|
+
const promiseId = allocateLocalCallbackId();
|
|
1609
|
+
instance.returnedPromises.set(promiseId, promise);
|
|
1610
|
+
return promiseId;
|
|
1611
|
+
},
|
|
1612
|
+
registerIterator: (iterator) => {
|
|
1613
|
+
const iteratorId = allocateLocalCallbackId();
|
|
1614
|
+
instance.returnedIterators.set(iteratorId, iterator);
|
|
1615
|
+
return iteratorId;
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
const addCallbackIdsToRefs = (value) => {
|
|
1619
|
+
if (value === null || typeof value !== "object") {
|
|
1620
|
+
return value;
|
|
1621
|
+
}
|
|
1622
|
+
if (isPromiseRef(value)) {
|
|
1623
|
+
if ("__resolveCallbackId" in value) {
|
|
1624
|
+
return value;
|
|
1625
|
+
}
|
|
1626
|
+
const resolveCallbackId = allocateLocalCallbackId();
|
|
1627
|
+
instance.returnedCallbacks.set(resolveCallbackId, async (promiseId) => {
|
|
1628
|
+
const promise = instance.returnedPromises.get(promiseId);
|
|
1629
|
+
if (!promise) {
|
|
1630
|
+
throw new Error(`Promise ${promiseId} not found`);
|
|
1631
|
+
}
|
|
1632
|
+
const result2 = await promise;
|
|
1633
|
+
instance.returnedPromises.delete(promiseId);
|
|
1634
|
+
const ctx = createMarshalContext();
|
|
1635
|
+
const marshalled = await marshalValue(result2, ctx);
|
|
1636
|
+
return addCallbackIdsToRefs(marshalled);
|
|
1637
|
+
});
|
|
1638
|
+
return { ...value, __resolveCallbackId: resolveCallbackId };
|
|
1639
|
+
}
|
|
1640
|
+
if (isAsyncIteratorRef(value)) {
|
|
1641
|
+
if ("__nextCallbackId" in value) {
|
|
1642
|
+
return value;
|
|
1643
|
+
}
|
|
1644
|
+
const nextCallbackId = allocateLocalCallbackId();
|
|
1645
|
+
instance.returnedCallbacks.set(nextCallbackId, async (iteratorId) => {
|
|
1646
|
+
const iterator = instance.returnedIterators.get(iteratorId);
|
|
1647
|
+
if (!iterator) {
|
|
1648
|
+
throw new Error(`Iterator ${iteratorId} not found`);
|
|
1649
|
+
}
|
|
1650
|
+
const result2 = await iterator.next();
|
|
1651
|
+
if (result2.done) {
|
|
1652
|
+
instance.returnedIterators.delete(iteratorId);
|
|
1653
|
+
}
|
|
1654
|
+
const ctx = createMarshalContext();
|
|
1655
|
+
const marshalledValue = await marshalValue(result2.value, ctx);
|
|
1656
|
+
return {
|
|
1657
|
+
done: result2.done,
|
|
1658
|
+
value: addCallbackIdsToRefs(marshalledValue)
|
|
1659
|
+
};
|
|
1660
|
+
});
|
|
1661
|
+
const returnCallbackId = allocateLocalCallbackId();
|
|
1662
|
+
instance.returnedCallbacks.set(returnCallbackId, async (iteratorId, returnValue) => {
|
|
1663
|
+
const iterator = instance.returnedIterators.get(iteratorId);
|
|
1664
|
+
instance.returnedIterators.delete(iteratorId);
|
|
1665
|
+
if (!iterator || !iterator.return) {
|
|
1666
|
+
return { done: true, value: undefined };
|
|
1667
|
+
}
|
|
1668
|
+
const result2 = await iterator.return(returnValue);
|
|
1669
|
+
const ctx = createMarshalContext();
|
|
1670
|
+
const marshalledValue = await marshalValue(result2.value, ctx);
|
|
1671
|
+
return {
|
|
1672
|
+
done: true,
|
|
1673
|
+
value: addCallbackIdsToRefs(marshalledValue)
|
|
1674
|
+
};
|
|
1675
|
+
});
|
|
1676
|
+
const throwCallbackId = allocateLocalCallbackId();
|
|
1677
|
+
instance.returnedCallbacks.set(throwCallbackId, async (iteratorId, errorValue) => {
|
|
1678
|
+
const iterator = instance.returnedIterators.get(iteratorId);
|
|
1679
|
+
if (!iterator) {
|
|
1680
|
+
throw new Error(`Iterator ${iteratorId} not found`);
|
|
1681
|
+
}
|
|
1682
|
+
try {
|
|
1683
|
+
if (!iterator.throw) {
|
|
1684
|
+
throw Object.assign(new Error(errorValue?.message ?? "Iterator does not support throw()"), { name: errorValue?.name ?? "Error", stack: errorValue?.stack });
|
|
1685
|
+
}
|
|
1686
|
+
const thrownError = Object.assign(new Error(errorValue?.message ?? "Iterator throw()"), { name: errorValue?.name ?? "Error", stack: errorValue?.stack });
|
|
1687
|
+
const result2 = await iterator.throw(thrownError);
|
|
1688
|
+
if (result2.done) {
|
|
1689
|
+
instance.returnedIterators.delete(iteratorId);
|
|
1690
|
+
}
|
|
1691
|
+
const ctx = createMarshalContext();
|
|
1692
|
+
const marshalledValue = await marshalValue(result2.value, ctx);
|
|
1693
|
+
return {
|
|
1694
|
+
done: result2.done,
|
|
1695
|
+
value: addCallbackIdsToRefs(marshalledValue)
|
|
1696
|
+
};
|
|
1697
|
+
} catch (error) {
|
|
1698
|
+
instance.returnedIterators.delete(iteratorId);
|
|
1699
|
+
throw error;
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
return {
|
|
1703
|
+
...value,
|
|
1704
|
+
__nextCallbackId: nextCallbackId,
|
|
1705
|
+
__returnCallbackId: returnCallbackId,
|
|
1706
|
+
__throwCallbackId: throwCallbackId
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
if (Array.isArray(value)) {
|
|
1710
|
+
return value.map((item) => addCallbackIdsToRefs(item));
|
|
1711
|
+
}
|
|
1712
|
+
const result = {};
|
|
1713
|
+
for (const key of Object.keys(value)) {
|
|
1714
|
+
result[key] = addCallbackIdsToRefs(value[key]);
|
|
1715
|
+
}
|
|
1716
|
+
return result;
|
|
1717
|
+
};
|
|
1718
|
+
const rawResult = await requestContextStorage.run({
|
|
1579
1719
|
...message.context ?? {},
|
|
1580
1720
|
signal: getCurrentHostSignal(instance)
|
|
1581
1721
|
}, async () => await callback(...message.args));
|
|
1722
|
+
const marshalContext = createMarshalContext();
|
|
1723
|
+
const marshalledResult = await marshalValue(rawResult, marshalContext);
|
|
1724
|
+
response.result = addCallbackIdsToRefs(marshalledResult);
|
|
1582
1725
|
} catch (err) {
|
|
1583
1726
|
const error = err;
|
|
1584
1727
|
response.error = {
|
|
@@ -2087,4 +2230,4 @@ export {
|
|
|
2087
2230
|
handleConnection
|
|
2088
2231
|
};
|
|
2089
2232
|
|
|
2090
|
-
//# debugId=
|
|
2233
|
+
//# debugId=B375B10E50F2EE8464756E2164756E21
|