@ricsam/isolate-daemon 0.1.15 → 0.1.16
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/dist/cjs/callback-fs-handler.cjs +17 -11
- package/dist/cjs/callback-fs-handler.cjs.map +3 -3
- package/dist/cjs/connection.cjs +105 -54
- package/dist/cjs/connection.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/callback-fs-handler.mjs +17 -11
- package/dist/mjs/callback-fs-handler.mjs.map +3 -3
- package/dist/mjs/connection.mjs +105 -54
- package/dist/mjs/connection.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/types.d.ts +11 -0
- package/package.json +1 -1
|
@@ -60,13 +60,19 @@ function createCallbackFileSystemHandler(options) {
|
|
|
60
60
|
const getCallbackId = (name) => {
|
|
61
61
|
return callbackContext.fs[name];
|
|
62
62
|
};
|
|
63
|
-
const getConnection = () => {
|
|
64
|
-
|
|
63
|
+
const getConnection = async () => {
|
|
64
|
+
if (callbackContext.connection) {
|
|
65
|
+
return callbackContext.connection;
|
|
66
|
+
}
|
|
67
|
+
if (callbackContext.reconnectionPromise) {
|
|
68
|
+
return callbackContext.reconnectionPromise.promise;
|
|
69
|
+
}
|
|
70
|
+
return connection;
|
|
65
71
|
};
|
|
66
72
|
return {
|
|
67
73
|
async getFileHandle(path, opts) {
|
|
68
74
|
const fullPath = resolvePath(path);
|
|
69
|
-
const conn = getConnection();
|
|
75
|
+
const conn = await getConnection();
|
|
70
76
|
if (opts?.create) {
|
|
71
77
|
const writeFileId = getCallbackId("writeFile");
|
|
72
78
|
if (writeFileId !== undefined) {
|
|
@@ -108,7 +114,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
108
114
|
},
|
|
109
115
|
async getDirectoryHandle(path, opts) {
|
|
110
116
|
const fullPath = resolvePath(path);
|
|
111
|
-
const conn = getConnection();
|
|
117
|
+
const conn = await getConnection();
|
|
112
118
|
if (opts?.create) {
|
|
113
119
|
const mkdirId = getCallbackId("mkdir");
|
|
114
120
|
if (mkdirId !== undefined) {
|
|
@@ -140,7 +146,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
140
146
|
},
|
|
141
147
|
async removeEntry(path, opts) {
|
|
142
148
|
const fullPath = resolvePath(path);
|
|
143
|
-
const conn = getConnection();
|
|
149
|
+
const conn = await getConnection();
|
|
144
150
|
let isFile = true;
|
|
145
151
|
const statId = getCallbackId("stat");
|
|
146
152
|
if (statId !== undefined) {
|
|
@@ -169,7 +175,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
169
175
|
},
|
|
170
176
|
async readDirectory(path) {
|
|
171
177
|
const fullPath = resolvePath(path);
|
|
172
|
-
const conn = getConnection();
|
|
178
|
+
const conn = await getConnection();
|
|
173
179
|
const readdirId = getCallbackId("readdir");
|
|
174
180
|
if (readdirId === undefined) {
|
|
175
181
|
throw new Error(`[NotAllowedError]Directory reading not supported`);
|
|
@@ -196,7 +202,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
196
202
|
},
|
|
197
203
|
async readFile(path) {
|
|
198
204
|
const fullPath = resolvePath(path);
|
|
199
|
-
const conn = getConnection();
|
|
205
|
+
const conn = await getConnection();
|
|
200
206
|
const readFileId = getCallbackId("readFile");
|
|
201
207
|
if (readFileId === undefined) {
|
|
202
208
|
throw new Error(`[NotAllowedError]File reading not supported`);
|
|
@@ -234,7 +240,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
234
240
|
},
|
|
235
241
|
async writeFile(path, data, position) {
|
|
236
242
|
const fullPath = resolvePath(path);
|
|
237
|
-
const conn = getConnection();
|
|
243
|
+
const conn = await getConnection();
|
|
238
244
|
const writeFileId = getCallbackId("writeFile");
|
|
239
245
|
if (writeFileId === undefined) {
|
|
240
246
|
throw new Error(`[NotAllowedError]File writing not supported`);
|
|
@@ -278,7 +284,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
278
284
|
},
|
|
279
285
|
async truncateFile(path, size) {
|
|
280
286
|
const fullPath = resolvePath(path);
|
|
281
|
-
const conn = getConnection();
|
|
287
|
+
const conn = await getConnection();
|
|
282
288
|
const readFileId = getCallbackId("readFile");
|
|
283
289
|
const writeFileId = getCallbackId("writeFile");
|
|
284
290
|
if (readFileId === undefined || writeFileId === undefined) {
|
|
@@ -303,7 +309,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
303
309
|
},
|
|
304
310
|
async getFileMetadata(path) {
|
|
305
311
|
const fullPath = resolvePath(path);
|
|
306
|
-
const conn = getConnection();
|
|
312
|
+
const conn = await getConnection();
|
|
307
313
|
const statId = getCallbackId("stat");
|
|
308
314
|
if (statId === undefined) {
|
|
309
315
|
throw new Error(`[NotAllowedError]File stat not supported`);
|
|
@@ -325,4 +331,4 @@ function createCallbackFileSystemHandler(options) {
|
|
|
325
331
|
};
|
|
326
332
|
}
|
|
327
333
|
|
|
328
|
-
//# debugId=
|
|
334
|
+
//# debugId=9A1D4676DB79541764756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/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 @ricsam/isolate-fs.\n */\n\nimport type { FileSystemHandler } from \"@ricsam/isolate-fs\";\nimport type { ConnectionState, CallbackContext } from \"./types.cjs\";\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 (connection: ConnectionState, callbackId: number, args: unknown[]): Promise<unknown>;\n}\n\ninterface CallbackFsHandlerOptions {\n connection: ConnectionState;\n callbackContext: CallbackContext;\n invokeClientCallback: InvokeClientCallback;\n basePath?: string;\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 { connection, callbackContext, invokeClientCallback, basePath = \"\" } = 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)\n const getConnection = (): ConnectionState => {\n return callbackContext.connection || connection;\n };\n\n return {\n async getFileHandle(path: string, opts?: { create?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = 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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 = getConnection();\n\n if (opts?.create) {\n const mkdirId = getCallbackId(\"mkdir\");\n if (mkdirId !== undefined) {\n try {\n await invokeClientCallback(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 invokeClientCallback(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 = 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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 = 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 invokeClientCallback(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 invokeClientCallback(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 = 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 invokeClientCallback(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 invokeClientCallback(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 = 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 invokeClientCallback(\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 invokeClientCallback(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 invokeClientCallback(conn, writeFileId, [\n fullPath,\n newData,\n ]);\n return;\n }\n }\n }\n\n await invokeClientCallback(conn, writeFileId, [fullPath, data]);\n },\n\n async truncateFile(path: string, size: number): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = 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 invokeClientCallback(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 invokeClientCallback(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 = 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 invokeClientCallback(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 @ricsam/isolate-fs.\n */\n\nimport type { FileSystemHandler } from \"@ricsam/isolate-fs\";\nimport type { ConnectionState, CallbackContext } from \"./types.cjs\";\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 (connection: ConnectionState, callbackId: number, args: unknown[]): Promise<unknown>;\n}\n\ninterface CallbackFsHandlerOptions {\n connection: ConnectionState;\n callbackContext: CallbackContext;\n invokeClientCallback: InvokeClientCallback;\n basePath?: string;\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 { connection, callbackContext, invokeClientCallback, basePath = \"\" } = 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 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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(\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 invokeClientCallback(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 invokeClientCallback(conn, writeFileId, [\n fullPath,\n newData,\n ]);\n return;\n }\n }\n }\n\n await invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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 invokeClientCallback(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;AAmBO,SAAS,+BAA+B,CAC7C,SACmB;AAAA,EACnB,QAAQ,YAAY,iBAAiB,sBAAsB,WAAW,OAAO;AAAA,EAE7E,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,IACb;AAAA,IAEA,MAAM,YAAY,SAAS,SAAS,GAAG,IAAI,SAAS,MAAM,GAAG,EAAE,IAAI;AAAA,IACnE,OAAO,GAAG,aAAa;AAAA;AAAA,EAIzB,MAAM,gBAAgB,CAAC,SAA0D;AAAA,IAC/E,OAAO,gBAAgB,GAAG;AAAA;AAAA,EAI5B,MAAM,gBAAgB,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "9A1D4676DB79541764756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/connection.cjs
CHANGED
|
@@ -250,16 +250,42 @@ async function hardDeleteRuntime(instance, state) {
|
|
|
250
250
|
instance.disposedAt = undefined;
|
|
251
251
|
instance.ownerConnection = null;
|
|
252
252
|
if (instance.callbackContext) {
|
|
253
|
+
if (instance.callbackContext.reconnectionPromise) {
|
|
254
|
+
clearTimeout(instance.callbackContext.reconnectionPromise.timeoutId);
|
|
255
|
+
instance.callbackContext.reconnectionPromise.promise.catch(() => {});
|
|
256
|
+
instance.callbackContext.reconnectionPromise.reject(new Error("Runtime was permanently disposed"));
|
|
257
|
+
instance.callbackContext.reconnectionPromise = undefined;
|
|
258
|
+
}
|
|
253
259
|
instance.callbackContext.connection = null;
|
|
254
260
|
}
|
|
255
261
|
}
|
|
256
262
|
}
|
|
263
|
+
var RECONNECTION_TIMEOUT_MS = 30000;
|
|
257
264
|
function softDeleteRuntime(instance, state) {
|
|
258
265
|
instance.isDisposed = true;
|
|
259
266
|
instance.disposedAt = Date.now();
|
|
260
267
|
instance.ownerConnection = null;
|
|
261
268
|
if (instance.callbackContext) {
|
|
262
269
|
instance.callbackContext.connection = null;
|
|
270
|
+
let resolve;
|
|
271
|
+
let reject;
|
|
272
|
+
const promise = new Promise((res, rej) => {
|
|
273
|
+
resolve = res;
|
|
274
|
+
reject = rej;
|
|
275
|
+
});
|
|
276
|
+
promise.catch(() => {});
|
|
277
|
+
const timeoutId = setTimeout(() => {
|
|
278
|
+
if (instance.callbackContext?.reconnectionPromise) {
|
|
279
|
+
instance.callbackContext.reconnectionPromise = undefined;
|
|
280
|
+
reject(new Error("Reconnection timeout: no client reconnected within timeout"));
|
|
281
|
+
}
|
|
282
|
+
}, RECONNECTION_TIMEOUT_MS);
|
|
283
|
+
instance.callbackContext.reconnectionPromise = {
|
|
284
|
+
promise,
|
|
285
|
+
resolve,
|
|
286
|
+
reject,
|
|
287
|
+
timeoutId
|
|
288
|
+
};
|
|
263
289
|
}
|
|
264
290
|
instance.callbacks.clear();
|
|
265
291
|
instance.runtime.timers.clearAll();
|
|
@@ -281,6 +307,11 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
|
281
307
|
const testEnvOptions = message.options.testEnvironment != null && typeof message.options.testEnvironment === "object" ? message.options.testEnvironment : undefined;
|
|
282
308
|
if (instance.callbackContext) {
|
|
283
309
|
instance.callbackContext.connection = connection;
|
|
310
|
+
if (instance.callbackContext.reconnectionPromise) {
|
|
311
|
+
clearTimeout(instance.callbackContext.reconnectionPromise.timeoutId);
|
|
312
|
+
instance.callbackContext.reconnectionPromise.resolve(connection);
|
|
313
|
+
instance.callbackContext.reconnectionPromise = undefined;
|
|
314
|
+
}
|
|
284
315
|
instance.callbackContext.consoleOnEntry = callbacks?.console?.onEntry?.callbackId;
|
|
285
316
|
instance.callbackContext.fetch = callbacks?.fetch?.callbackId;
|
|
286
317
|
instance.callbackContext.moduleLoader = callbacks?.moduleLoader?.callbackId;
|
|
@@ -375,6 +406,35 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
|
375
406
|
instance.returnedIterators = new Map;
|
|
376
407
|
instance.nextLocalCallbackId = 1e6;
|
|
377
408
|
}
|
|
409
|
+
async function waitForConnection(callbackContext) {
|
|
410
|
+
if (callbackContext.connection) {
|
|
411
|
+
return callbackContext.connection;
|
|
412
|
+
}
|
|
413
|
+
if (callbackContext.reconnectionPromise) {
|
|
414
|
+
return callbackContext.reconnectionPromise.promise;
|
|
415
|
+
}
|
|
416
|
+
throw new Error("No connection available and no reconnection pending");
|
|
417
|
+
}
|
|
418
|
+
async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args, label, invokeClientCallback) {
|
|
419
|
+
const conn = await waitForConnection(callbackContext);
|
|
420
|
+
const cbId = getCallbackId();
|
|
421
|
+
if (cbId === undefined) {
|
|
422
|
+
throw new Error(`${label} callback not available`);
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
return await invokeClientCallback(conn, cbId, args);
|
|
426
|
+
} catch (err) {
|
|
427
|
+
if (callbackContext.reconnectionPromise && !callbackContext.connection) {
|
|
428
|
+
const newConn = await callbackContext.reconnectionPromise.promise;
|
|
429
|
+
const newCbId = getCallbackId();
|
|
430
|
+
if (newCbId === undefined) {
|
|
431
|
+
throw new Error(`${label} callback not available after reconnection`);
|
|
432
|
+
}
|
|
433
|
+
return invokeClientCallback(newConn, newCbId, args);
|
|
434
|
+
}
|
|
435
|
+
throw err;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
378
438
|
async function evictOldestDisposedRuntime(state) {
|
|
379
439
|
let oldest = null;
|
|
380
440
|
let oldestTime = Infinity;
|
|
@@ -561,11 +621,16 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
561
621
|
}
|
|
562
622
|
return await callback(...args);
|
|
563
623
|
} else {
|
|
564
|
-
const conn = callbackContext
|
|
565
|
-
|
|
566
|
-
|
|
624
|
+
const conn = await waitForConnection(callbackContext);
|
|
625
|
+
try {
|
|
626
|
+
return await invokeClientCallback(conn, callbackId, args);
|
|
627
|
+
} catch (err) {
|
|
628
|
+
if (callbackContext.reconnectionPromise && !callbackContext.connection) {
|
|
629
|
+
const newConn = await callbackContext.reconnectionPromise.promise;
|
|
630
|
+
return invokeClientCallback(newConn, callbackId, args);
|
|
631
|
+
}
|
|
632
|
+
throw err;
|
|
567
633
|
}
|
|
568
|
-
return invokeClientCallback(conn, callbackId, args);
|
|
569
634
|
}
|
|
570
635
|
};
|
|
571
636
|
customFnMarshalOptions = { createMarshalContext, addCallbackIdsToRefs, invokeCallback };
|
|
@@ -582,27 +647,20 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
582
647
|
const nextCallbackId = callbackContext_.custom.get(`${name}:next`);
|
|
583
648
|
const returnCallbackId = callbackContext_.custom.get(`${name}:return`);
|
|
584
649
|
async function* bridgedIterator() {
|
|
585
|
-
const
|
|
586
|
-
if (!conn || startCallbackId === undefined) {
|
|
587
|
-
throw new Error(`AsyncIterator callback '${name}' not available`);
|
|
588
|
-
}
|
|
589
|
-
const startResult = await invokeClientCallback(conn, startCallbackId, args);
|
|
650
|
+
const startResult = await invokeCallbackWithReconnect(callbackContext_, () => callbackContext_.custom.get(`${name}:start`), args, `AsyncIterator '${name}' start`, invokeClientCallback);
|
|
590
651
|
const iteratorId = startResult.iteratorId;
|
|
591
652
|
try {
|
|
592
653
|
while (true) {
|
|
593
|
-
const
|
|
594
|
-
if (!nextConn || nextCallbackId === undefined) {
|
|
595
|
-
throw new Error(`AsyncIterator callback '${name}' not available`);
|
|
596
|
-
}
|
|
597
|
-
const nextResult = await invokeClientCallback(nextConn, nextCallbackId, [iteratorId]);
|
|
654
|
+
const nextResult = await invokeCallbackWithReconnect(callbackContext_, () => callbackContext_.custom.get(`${name}:next`), [iteratorId], `AsyncIterator '${name}' next`, invokeClientCallback);
|
|
598
655
|
if (nextResult.done)
|
|
599
656
|
return nextResult.value;
|
|
600
657
|
yield nextResult.value;
|
|
601
658
|
}
|
|
602
659
|
} finally {
|
|
603
660
|
const retConn = callbackContext_.connection;
|
|
604
|
-
|
|
605
|
-
|
|
661
|
+
const retCbId = callbackContext_.custom.get(`${name}:return`);
|
|
662
|
+
if (retConn && retCbId !== undefined) {
|
|
663
|
+
await invokeClientCallback(retConn, retCbId, [iteratorId]).catch(() => {});
|
|
606
664
|
}
|
|
607
665
|
}
|
|
608
666
|
}
|
|
@@ -613,12 +671,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
613
671
|
bridgedCustomFunctions[name] = {
|
|
614
672
|
type: registration.type,
|
|
615
673
|
fn: async (...args) => {
|
|
616
|
-
|
|
617
|
-
const cbId = callbackContext_.custom.get(name);
|
|
618
|
-
if (!conn || cbId === undefined) {
|
|
619
|
-
throw new Error(`Custom function callback '${name}' not available`);
|
|
620
|
-
}
|
|
621
|
-
return invokeClientCallback(conn, cbId, args);
|
|
674
|
+
return invokeCallbackWithReconnect(callbackContext_, () => callbackContext_.custom.get(name), args, `Custom function '${name}'`, invokeClientCallback);
|
|
622
675
|
}
|
|
623
676
|
};
|
|
624
677
|
}
|
|
@@ -627,12 +680,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
627
680
|
let moduleLoader;
|
|
628
681
|
if (moduleLoaderCallback) {
|
|
629
682
|
moduleLoader = async (specifier, importer) => {
|
|
630
|
-
|
|
631
|
-
const cbId = callbackContext.moduleLoader;
|
|
632
|
-
if (!conn || cbId === undefined) {
|
|
633
|
-
throw new Error("Module loader callback not available");
|
|
634
|
-
}
|
|
635
|
-
return invokeClientCallback(conn, cbId, [specifier, importer]);
|
|
683
|
+
return invokeCallbackWithReconnect(callbackContext, () => callbackContext.moduleLoader, [specifier, importer], "Module loader", invokeClientCallback);
|
|
636
684
|
};
|
|
637
685
|
}
|
|
638
686
|
let testEnvironment;
|
|
@@ -663,19 +711,8 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
663
711
|
if (playwrightCallbacks) {
|
|
664
712
|
playwrightOptions = {
|
|
665
713
|
handler: async (op) => {
|
|
666
|
-
const conn = callbackContext.connection;
|
|
667
|
-
const callbackId = callbackContext.playwright.handlerCallbackId;
|
|
668
|
-
if (!conn || callbackId === undefined) {
|
|
669
|
-
return {
|
|
670
|
-
ok: false,
|
|
671
|
-
error: {
|
|
672
|
-
name: "Error",
|
|
673
|
-
message: "Playwright handler callback not available"
|
|
674
|
-
}
|
|
675
|
-
};
|
|
676
|
-
}
|
|
677
714
|
try {
|
|
678
|
-
const resultJson = await
|
|
715
|
+
const resultJson = await invokeCallbackWithReconnect(callbackContext, () => callbackContext.playwright.handlerCallbackId, [JSON.stringify(op)], "Playwright handler", invokeClientCallback);
|
|
679
716
|
return JSON.parse(resultJson);
|
|
680
717
|
} catch (err) {
|
|
681
718
|
const error = err;
|
|
@@ -715,18 +752,13 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
715
752
|
}
|
|
716
753
|
},
|
|
717
754
|
fetch: async (url, init) => {
|
|
718
|
-
const conn = callbackContext.connection;
|
|
719
|
-
const callbackId = callbackContext.fetch;
|
|
720
|
-
if (!conn || callbackId === undefined) {
|
|
721
|
-
throw new Error("Fetch callback not available");
|
|
722
|
-
}
|
|
723
755
|
const serialized = {
|
|
724
756
|
url,
|
|
725
757
|
method: init.method,
|
|
726
758
|
headers: init.headers,
|
|
727
759
|
body: init.rawBody
|
|
728
760
|
};
|
|
729
|
-
const result = await
|
|
761
|
+
const result = await invokeCallbackWithReconnect(callbackContext, () => callbackContext.fetch, [serialized], "Fetch", invokeClientCallback);
|
|
730
762
|
if (result && typeof result === "object" && result.__streamingResponse) {
|
|
731
763
|
const response = result.response;
|
|
732
764
|
response.__isCallbackStream = true;
|
|
@@ -736,10 +768,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
736
768
|
},
|
|
737
769
|
fs: {
|
|
738
770
|
getDirectory: async (dirPath) => {
|
|
739
|
-
const conn = callbackContext
|
|
740
|
-
if (!conn) {
|
|
741
|
-
throw new Error("FS callbacks not available");
|
|
742
|
-
}
|
|
771
|
+
const conn = await waitForConnection(callbackContext);
|
|
743
772
|
return import_callback_fs_handler.createCallbackFileSystemHandler({
|
|
744
773
|
connection: conn,
|
|
745
774
|
callbackContext,
|
|
@@ -1525,13 +1554,35 @@ async function handleRunTests(message, connection, state) {
|
|
|
1525
1554
|
return;
|
|
1526
1555
|
}
|
|
1527
1556
|
instance.lastActivity = Date.now();
|
|
1557
|
+
if (instance.pendingTestRun) {
|
|
1558
|
+
try {
|
|
1559
|
+
const results = await instance.pendingTestRun.promise;
|
|
1560
|
+
const currentConn = instance.callbackContext?.connection;
|
|
1561
|
+
if (currentConn) {
|
|
1562
|
+
sendOk(currentConn.socket, message.requestId, results);
|
|
1563
|
+
}
|
|
1564
|
+
} catch (err) {
|
|
1565
|
+
const error = err;
|
|
1566
|
+
const currentConn = instance.callbackContext?.connection;
|
|
1567
|
+
if (currentConn) {
|
|
1568
|
+
sendError(currentConn.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1528
1573
|
try {
|
|
1529
1574
|
const timeout = message.timeout ?? 30000;
|
|
1530
|
-
const
|
|
1531
|
-
|
|
1575
|
+
const runPromise = instance.runtime.testEnvironment.runTests(timeout);
|
|
1576
|
+
instance.pendingTestRun = { promise: runPromise };
|
|
1577
|
+
const results = await runPromise;
|
|
1578
|
+
instance.pendingTestRun = undefined;
|
|
1579
|
+
const currentConn = instance.callbackContext?.connection ?? connection;
|
|
1580
|
+
sendOk(currentConn.socket, message.requestId, results);
|
|
1532
1581
|
} catch (err) {
|
|
1582
|
+
instance.pendingTestRun = undefined;
|
|
1533
1583
|
const error = err;
|
|
1534
|
-
|
|
1584
|
+
const currentConn = instance.callbackContext?.connection ?? connection;
|
|
1585
|
+
sendError(currentConn.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1535
1586
|
}
|
|
1536
1587
|
}
|
|
1537
1588
|
async function handleResetTestEnv(message, connection, state) {
|
|
@@ -1610,4 +1661,4 @@ async function handleClearCollectedData(message, connection, state) {
|
|
|
1610
1661
|
}
|
|
1611
1662
|
}
|
|
1612
1663
|
|
|
1613
|
-
//# debugId=
|
|
1664
|
+
//# debugId=D9712A3ECDBAF18B64756E2164756E21
|