@ricsam/isolate-daemon 0.1.15 → 0.1.17
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 +140 -58
- package/dist/cjs/connection.cjs.map +3 -3
- package/dist/cjs/daemon.cjs +1 -5
- package/dist/cjs/daemon.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 +140 -58
- package/dist/mjs/connection.mjs.map +3 -3
- package/dist/mjs/daemon.mjs +1 -5
- package/dist/mjs/daemon.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/types.d.ts +15 -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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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,YAAsC;AAAA,IAC1D,IAAI,gBAAgB,YAAY;AAAA,MAC9B,OAAO,gBAAgB;AAAA,IACzB;AAAA,IACA,IAAI,gBAAgB,qBAAqB;AAAA,MACvC,OAAO,gBAAgB,oBAAoB;AAAA,IAC7C;AAAA,IAEA,OAAO;AAAA;AAAA,EAGT,OAAO;AAAA,SACC,cAAa,CAAC,MAAc,MAA4C;AAAA,MAC5E,MAAM,WAAW,YAAY,IAAI;AAAA,MACjC,MAAM,OAAO,MAAM,cAAc;AAAA,MAEjC,IAAI,MAAM,QAAQ;AAAA,QAEhB,MAAM,cAAc,cAAc,WAAW;AAAA,QAC7C,IAAI,gBAAgB,WAAW;AAAA,UAC7B,IAAI;AAAA,YAEF,MAAM,UAAS,cAAc,MAAM;AAAA,YACnC,IAAI,YAAW,WAAW;AAAA,cACxB,IAAI;AAAA,gBACF,MAAM,qBAAqB,MAAM,SAAQ,CAAC,QAAQ,CAAC;AAAA,gBAEnD;AAAA,gBACA,MAAM;AAAA,YAGV;AAAA,YAEA,MAAM,qBAAqB,MAAM,aAAa;AAAA,cAC5C;AAAA,cACA,IAAI,WAAW,CAAC;AAAA,YAClB,CAAC;AAAA,YACD,OAAO,KAAK;AAAA,YACZ,MAAM,QAAQ;AAAA,YACd,MAAM,IAAI,MAAM,kBAAkB,MAAM,SAAS;AAAA;AAAA,QAErD;AAAA,QACA;AAAA,MACF;AAAA,MAGA,MAAM,SAAS,cAAc,MAAM;AAAA,MACnC,IAAI,WAAW,WAAW;AAAA,QACxB,IAAI;AAAA,UACF,MAAM,SAAU,MAAM,qBAAqB,MAAM,QAAQ;AAAA,YACvD;AAAA,UACF,CAAC;AAAA,UACD,IAAI,CAAC,OAAO,QAAQ;AAAA,YAClB,MAAM,IAAI,MAAM,kCAAkC,UAAU;AAAA,UAC9D;AAAA,UACA,OAAO,KAAK;AAAA,UACZ,MAAM,QAAQ;AAAA,UACd,IAAI,MAAM,QAAQ,SAAS,mBAAmB;AAAA,YAAG,MAAM;AAAA,UACvD,MAAM,IAAI,MAAM,kCAAkC,UAAU;AAAA;AAAA,MAEhE;AAAA;AAAA,SAGI,mBAAkB,CAAC,MAAc,MAA4C;AAAA,MACjF,MAAM,WAAW,YAAY,IAAI;AAAA,MACjC,MAAM,OAAO,MAAM,cAAc;AAAA,MAEjC,IAAI,MAAM,QAAQ;AAAA,QAChB,MAAM,UAAU,cAAc,OAAO;AAAA,QACrC,IAAI,YAAY,WAAW;AAAA,UACzB,IAAI;AAAA,YACF,MAAM,qBAAqB,MAAM,SAAS;AAAA,cACxC;AAAA,cACA,EAAE,WAAW,KAAK;AAAA,YACpB,CAAC;AAAA,YACD,MAAM;AAAA,QAGV;AAAA,QACA;AAAA,MACF;AAAA,MAGA,MAAM,SAAS,cAAc,MAAM;AAAA,MACnC,IAAI,WAAW,WAAW;AAAA,QACxB,IAAI;AAAA,UACF,MAAM,SAAU,MAAM,qBAAqB,MAAM,QAAQ;AAAA,YACvD;AAAA,UACF,CAAC;AAAA,UACD,IAAI,CAAC,OAAO,aAAa;AAAA,YACvB,MAAM,IAAI,MAAM,uCAAuC,UAAU;AAAA,UACnE;AAAA,UACA,OAAO,KAAK;AAAA,UACZ,MAAM,QAAQ;AAAA,UACd,IAAI,MAAM,QAAQ,SAAS,mBAAmB;AAAA,YAAG,MAAM;AAAA,UACvD,MAAM,IAAI,MAAM,uCAAuC,UAAU;AAAA;AAAA,MAErE;AAAA;AAAA,SAGI,YAAW,CAAC,MAAc,MAA+C;AAAA,MAC7E,MAAM,WAAW,YAAY,IAAI;AAAA,MACjC,MAAM,OAAO,MAAM,cAAc;AAAA,MAGjC,IAAI,SAAS;AAAA,MACb,MAAM,SAAS,cAAc,MAAM;AAAA,MACnC,IAAI,WAAW,WAAW;AAAA,QACxB,IAAI;AAAA,UACF,MAAM,SAAU,MAAM,qBAAqB,MAAM,QAAQ;AAAA,YACvD;AAAA,UACF,CAAC;AAAA,UACD,SAAS,OAAO;AAAA,UAChB,MAAM;AAAA,UACN,MAAM,IAAI,MAAM,mCAAmC,UAAU;AAAA;AAAA,MAEjE;AAAA,MAEA,IAAI,QAAQ;AAAA,QACV,MAAM,WAAW,cAAc,QAAQ;AAAA,QACvC,IAAI,aAAa,WAAW;AAAA,UAC1B,MAAM,IAAI,MAAM,8CAA8C;AAAA,QAChE;AAAA,QACA,MAAM,qBAAqB,MAAM,UAAU,CAAC,QAAQ,CAAC;AAAA,MACvD,EAAO;AAAA,QACL,MAAM,UAAU,cAAc,OAAO;AAAA,QACrC,IAAI,YAAY,WAAW;AAAA,UACzB,MAAM,IAAI,MAAM,mDAAmD;AAAA,QACrE;AAAA,QAEA,MAAM,qBAAqB,MAAM,SAAS,CAAC,QAAQ,CAAC;AAAA;AAAA;AAAA,SAIlD,cAAa,CACjB,MAC8D;AAAA,MAC9D,MAAM,WAAW,YAAY,IAAI;AAAA,MACjC,MAAM,OAAO,MAAM,cAAc;AAAA,MAEjC,MAAM,YAAY,cAAc,SAAS;AAAA,MACzC,IAAI,cAAc,WAAW;AAAA,QAC3B,MAAM,IAAI,MAAM,kDAAkD;AAAA,MACpE;AAAA,MAEA,MAAM,UAAW,MAAM,qBAAqB,MAAM,WAAW;AAAA,QAC3D;AAAA,MACF,CAAC;AAAA,MAGD,MAAM,SAA8D,CAAC;AAAA,MAErE,MAAM,SAAS,cAAc,MAAM;AAAA,MACnC,WAAW,QAAQ,SAAS;AAAA,QAC1B,MAAM,YAAY,WAAW,GAAG,YAAY,SAAS;AAAA,QACrD,IAAI,OAA6B;AAAA,QAEjC,IAAI,WAAW,WAAW;AAAA,UACxB,IAAI;AAAA,YACF,MAAM,OAAQ,MAAM,qBAAqB,MAAM,QAAQ;AAAA,cACrD;AAAA,YACF,CAAC;AAAA,YACD,OAAO,KAAK,cAAc,cAAc;AAAA,YACxC,MAAM;AAAA,QAGV;AAAA,QAEA,OAAO,KAAK,EAAE,MAAM,KAAK,CAAC;AAAA,MAC5B;AAAA,MAEA,OAAO;AAAA;AAAA,SAGH,SAAQ,CACZ,MACiF;AAAA,MACjF,MAAM,WAAW,YAAY,IAAI;AAAA,MACjC,MAAM,OAAO,MAAM,cAAc;AAAA,MAEjC,MAAM,aAAa,cAAc,UAAU;AAAA,MAC3C,IAAI,eAAe,WAAW;AAAA,QAC5B,MAAM,IAAI,MAAM,6CAA6C;AAAA,MAC/D;AAAA,MAEA,MAAM,OAAQ,MAAM,qBAAqB,MAAM,YAAY;AAAA,QACzD;AAAA,MACF,CAAC;AAAA,MAGD,IAAI;AAAA,MACJ,IAAI,gBAAgB,YAAY;AAAA,QAC9B,QAAQ;AAAA,MACV,EAAO,SAAI,MAAM,QAAQ,IAAI,GAAG;AAAA,QAC9B,QAAQ,IAAI,WAAW,IAAI;AAAA,MAC7B,EAAO,SAAI,gBAAgB,aAAa;AAAA,QACtC,QAAQ,IAAI,WAAW,IAAI;AAAA,MAC7B,EAAO;AAAA,QACL,QAAQ,IAAI,WAAW,CAAC;AAAA;AAAA,MAI1B,IAAI,OAAO,MAAM;AAAA,MACjB,IAAI,eAAe,KAAK,IAAI;AAAA,MAE5B,MAAM,SAAS,cAAc,MAAM;AAAA,MACnC,IAAI,WAAW,WAAW;AAAA,QACxB,IAAI;AAAA,UACF,MAAM,OAAQ,MAAM,qBAAqB,MAAM,QAAQ;AAAA,YACrD;AAAA,UACF,CAAC;AAAA,UACD,OAAO,KAAK;AAAA,UACZ,IAAI,KAAK,cAAc;AAAA,YACrB,eAAe,KAAK;AAAA,UACtB;AAAA,UACA,MAAM;AAAA,MAGV;AAAA,MAGA,MAAM,MAAM,KAAK,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY,KAAK;AAAA,MACpD,MAAM,OAAO,WAAW,QAAQ;AAAA,MAEhC,OAAO,EAAE,MAAM,OAAO,MAAM,cAAc,KAAK;AAAA;AAAA,SAG3C,UAAS,CAAC,MAAc,MAAkB,UAAkC;AAAA,MAChF,MAAM,WAAW,YAAY,IAAI;AAAA,MACjC,MAAM,OAAO,MAAM,cAAc;AAAA,MAEjC,MAAM,cAAc,cAAc,WAAW;AAAA,MAC7C,IAAI,gBAAgB,WAAW;AAAA,QAC7B,MAAM,IAAI,MAAM,6CAA6C;AAAA,MAC/D;AAAA,MAIA,IAAI,aAAa,aAAa,WAAW,GAAG;AAAA,QAE1C,MAAM,aAAa,cAAc,UAAU;AAAA,QAC3C,IAAI,eAAe,WAAW;AAAA,UAC5B,IAAI;AAAA,YACF,MAAM,WAAY,MAAM,qBACtB,MACA,YACA,CAAC,QAAQ,CACX;AAAA,YAEA,IAAI;AAAA,YACJ,IAAI,oBAAoB,YAAY;AAAA,cAClC,gBAAgB;AAAA,YAClB,EAAO,SAAI,MAAM,QAAQ,QAAQ,GAAG;AAAA,cAClC,gBAAgB,IAAI,WAAW,QAAQ;AAAA,YACzC,EAAO,SAAI,oBAAoB,aAAa;AAAA,cAC1C,gBAAgB,IAAI,WAAW,QAAQ;AAAA,YACzC,EAAO;AAAA,cACL,gBAAgB,IAAI,WAAW,CAAC;AAAA;AAAA,YAIlC,MAAM,UAAU,KAAK,IAAI,cAAc,QAAQ,WAAW,KAAK,MAAM;AAAA,YACrE,MAAM,SAAS,IAAI,WAAW,OAAO;AAAA,YACrC,OAAO,IAAI,aAAa;AAAA,YACxB,OAAO,IAAI,MAAM,QAAQ;AAAA,YAEzB,MAAM,qBAAqB,MAAM,aAAa;AAAA,cAC5C;AAAA,cACA;AAAA,YACF,CAAC;AAAA,YACD;AAAA,YACA,MAAM;AAAA,YAEN,MAAM,UAAU,IAAI,WAAW,WAAW,KAAK,MAAM;AAAA,YACrD,QAAQ,IAAI,MAAM,QAAQ;AAAA,YAC1B,MAAM,qBAAqB,MAAM,aAAa;AAAA,cAC5C;AAAA,cACA;AAAA,YACF,CAAC;AAAA,YACD;AAAA;AAAA,QAEJ;AAAA,MACF;AAAA,MAEA,MAAM,qBAAqB,MAAM,aAAa,CAAC,UAAU,IAAI,CAAC;AAAA;AAAA,SAG1D,aAAY,CAAC,MAAc,MAA6B;AAAA,MAC5D,MAAM,WAAW,YAAY,IAAI;AAAA,MACjC,MAAM,OAAO,MAAM,cAAc;AAAA,MAEjC,MAAM,aAAa,cAAc,UAAU;AAAA,MAC3C,MAAM,cAAc,cAAc,WAAW;AAAA,MAC7C,IAAI,eAAe,aAAa,gBAAgB,WAAW;AAAA,QACzD,MAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE;AAAA,MAGA,MAAM,WAAY,MAAM,qBAAqB,MAAM,YAAY;AAAA,QAC7D;AAAA,MACF,CAAC;AAAA,MAED,IAAI;AAAA,MACJ,IAAI,oBAAoB,YAAY;AAAA,QAClC,gBAAgB;AAAA,MAClB,EAAO,SAAI,MAAM,QAAQ,QAAQ,GAAG;AAAA,QAClC,gBAAgB,IAAI,WAAW,QAAQ;AAAA,MACzC,EAAO,SAAI,oBAAoB,aAAa;AAAA,QAC1C,gBAAgB,IAAI,WAAW,QAAQ;AAAA,MACzC,EAAO;AAAA,QACL,gBAAgB,IAAI,WAAW,CAAC;AAAA;AAAA,MAIlC,MAAM,YAAY,IAAI,WAAW,IAAI;AAAA,MACrC,UAAU,IAAI,cAAc,MAAM,GAAG,IAAI,CAAC;AAAA,MAE1C,MAAM,qBAAqB,MAAM,aAAa,CAAC,UAAU,SAAS,CAAC;AAAA;AAAA,SAG/D,gBAAe,CACnB,MAC+D;AAAA,MAC/D,MAAM,WAAW,YAAY,IAAI;AAAA,MACjC,MAAM,OAAO,MAAM,cAAc;AAAA,MAEjC,MAAM,SAAS,cAAc,MAAM;AAAA,MACnC,IAAI,WAAW,WAAW;AAAA,QACxB,MAAM,IAAI,MAAM,0CAA0C;AAAA,MAC5D;AAAA,MAEA,MAAM,OAAQ,MAAM,qBAAqB,MAAM,QAAQ;AAAA,QACrD;AAAA,MACF,CAAC;AAAA,MAED,IAAI,CAAC,KAAK,QAAQ;AAAA,QAChB,MAAM,IAAI,MAAM,kCAAkC,UAAU;AAAA,MAC9D;AAAA,MAGA,MAAM,MAAM,KAAK,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY,KAAK;AAAA,MACpD,MAAM,OAAO,WAAW,QAAQ;AAAA,MAEhC,OAAO;AAAA,QACL,MAAM,KAAK;AAAA,QACX,cAAc,KAAK,gBAAgB,KAAK,IAAI;AAAA,QAC5C;AAAA,MACF;AAAA;AAAA,EAEJ;AAAA;",
|
|
8
|
+
"debugId": "9A1D4676DB79541764756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/connection.cjs
CHANGED
|
@@ -69,7 +69,9 @@ function handleConnection(socket, state) {
|
|
|
69
69
|
nextStreamId: 1,
|
|
70
70
|
activeStreams: new Map,
|
|
71
71
|
streamReceivers: new Map,
|
|
72
|
-
callbackStreamReceivers: new Map
|
|
72
|
+
callbackStreamReceivers: new Map,
|
|
73
|
+
dispatchAbortControllers: new Map,
|
|
74
|
+
earlyAbortedDispatches: new Set
|
|
73
75
|
};
|
|
74
76
|
state.connections.set(socket, connection);
|
|
75
77
|
const parser = import_isolate_protocol.createFrameParser();
|
|
@@ -86,6 +88,11 @@ function handleConnection(socket, state) {
|
|
|
86
88
|
}
|
|
87
89
|
});
|
|
88
90
|
socket.on("close", () => {
|
|
91
|
+
for (const [, controller] of connection.dispatchAbortControllers) {
|
|
92
|
+
controller.abort();
|
|
93
|
+
}
|
|
94
|
+
connection.dispatchAbortControllers.clear();
|
|
95
|
+
connection.earlyAbortedDispatches.clear();
|
|
89
96
|
for (const isolateId of connection.isolates) {
|
|
90
97
|
const instance = state.isolates.get(isolateId);
|
|
91
98
|
if (instance) {
|
|
@@ -147,6 +154,9 @@ async function handleMessage(message, connection, state) {
|
|
|
147
154
|
case import_isolate_protocol.MessageType.DISPATCH_REQUEST:
|
|
148
155
|
await handleDispatchRequest(message, connection, state);
|
|
149
156
|
break;
|
|
157
|
+
case import_isolate_protocol.MessageType.DISPATCH_REQUEST_ABORT:
|
|
158
|
+
handleDispatchRequestAbort(message, connection);
|
|
159
|
+
break;
|
|
150
160
|
case import_isolate_protocol.MessageType.CALLBACK_RESPONSE:
|
|
151
161
|
handleCallbackResponse(message, connection);
|
|
152
162
|
break;
|
|
@@ -250,16 +260,42 @@ async function hardDeleteRuntime(instance, state) {
|
|
|
250
260
|
instance.disposedAt = undefined;
|
|
251
261
|
instance.ownerConnection = null;
|
|
252
262
|
if (instance.callbackContext) {
|
|
263
|
+
if (instance.callbackContext.reconnectionPromise) {
|
|
264
|
+
clearTimeout(instance.callbackContext.reconnectionPromise.timeoutId);
|
|
265
|
+
instance.callbackContext.reconnectionPromise.promise.catch(() => {});
|
|
266
|
+
instance.callbackContext.reconnectionPromise.reject(new Error("Runtime was permanently disposed"));
|
|
267
|
+
instance.callbackContext.reconnectionPromise = undefined;
|
|
268
|
+
}
|
|
253
269
|
instance.callbackContext.connection = null;
|
|
254
270
|
}
|
|
255
271
|
}
|
|
256
272
|
}
|
|
273
|
+
var RECONNECTION_TIMEOUT_MS = 30000;
|
|
257
274
|
function softDeleteRuntime(instance, state) {
|
|
258
275
|
instance.isDisposed = true;
|
|
259
276
|
instance.disposedAt = Date.now();
|
|
260
277
|
instance.ownerConnection = null;
|
|
261
278
|
if (instance.callbackContext) {
|
|
262
279
|
instance.callbackContext.connection = null;
|
|
280
|
+
let resolve;
|
|
281
|
+
let reject;
|
|
282
|
+
const promise = new Promise((res, rej) => {
|
|
283
|
+
resolve = res;
|
|
284
|
+
reject = rej;
|
|
285
|
+
});
|
|
286
|
+
promise.catch(() => {});
|
|
287
|
+
const timeoutId = setTimeout(() => {
|
|
288
|
+
if (instance.callbackContext?.reconnectionPromise) {
|
|
289
|
+
instance.callbackContext.reconnectionPromise = undefined;
|
|
290
|
+
reject(new Error("Reconnection timeout: no client reconnected within timeout"));
|
|
291
|
+
}
|
|
292
|
+
}, RECONNECTION_TIMEOUT_MS);
|
|
293
|
+
instance.callbackContext.reconnectionPromise = {
|
|
294
|
+
promise,
|
|
295
|
+
resolve,
|
|
296
|
+
reject,
|
|
297
|
+
timeoutId
|
|
298
|
+
};
|
|
263
299
|
}
|
|
264
300
|
instance.callbacks.clear();
|
|
265
301
|
instance.runtime.timers.clearAll();
|
|
@@ -281,6 +317,11 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
|
281
317
|
const testEnvOptions = message.options.testEnvironment != null && typeof message.options.testEnvironment === "object" ? message.options.testEnvironment : undefined;
|
|
282
318
|
if (instance.callbackContext) {
|
|
283
319
|
instance.callbackContext.connection = connection;
|
|
320
|
+
if (instance.callbackContext.reconnectionPromise) {
|
|
321
|
+
clearTimeout(instance.callbackContext.reconnectionPromise.timeoutId);
|
|
322
|
+
instance.callbackContext.reconnectionPromise.resolve(connection);
|
|
323
|
+
instance.callbackContext.reconnectionPromise = undefined;
|
|
324
|
+
}
|
|
284
325
|
instance.callbackContext.consoleOnEntry = callbacks?.console?.onEntry?.callbackId;
|
|
285
326
|
instance.callbackContext.fetch = callbacks?.fetch?.callbackId;
|
|
286
327
|
instance.callbackContext.moduleLoader = callbacks?.moduleLoader?.callbackId;
|
|
@@ -375,6 +416,35 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
|
375
416
|
instance.returnedIterators = new Map;
|
|
376
417
|
instance.nextLocalCallbackId = 1e6;
|
|
377
418
|
}
|
|
419
|
+
async function waitForConnection(callbackContext) {
|
|
420
|
+
if (callbackContext.connection) {
|
|
421
|
+
return callbackContext.connection;
|
|
422
|
+
}
|
|
423
|
+
if (callbackContext.reconnectionPromise) {
|
|
424
|
+
return callbackContext.reconnectionPromise.promise;
|
|
425
|
+
}
|
|
426
|
+
throw new Error("No connection available and no reconnection pending");
|
|
427
|
+
}
|
|
428
|
+
async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args, label, invokeClientCallback) {
|
|
429
|
+
const conn = await waitForConnection(callbackContext);
|
|
430
|
+
const cbId = getCallbackId();
|
|
431
|
+
if (cbId === undefined) {
|
|
432
|
+
throw new Error(`${label} callback not available`);
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
return await invokeClientCallback(conn, cbId, args);
|
|
436
|
+
} catch (err) {
|
|
437
|
+
if (callbackContext.reconnectionPromise && !callbackContext.connection) {
|
|
438
|
+
const newConn = await callbackContext.reconnectionPromise.promise;
|
|
439
|
+
const newCbId = getCallbackId();
|
|
440
|
+
if (newCbId === undefined) {
|
|
441
|
+
throw new Error(`${label} callback not available after reconnection`);
|
|
442
|
+
}
|
|
443
|
+
return invokeClientCallback(newConn, newCbId, args);
|
|
444
|
+
}
|
|
445
|
+
throw err;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
378
448
|
async function evictOldestDisposedRuntime(state) {
|
|
379
449
|
let oldest = null;
|
|
380
450
|
let oldestTime = Infinity;
|
|
@@ -561,11 +631,16 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
561
631
|
}
|
|
562
632
|
return await callback(...args);
|
|
563
633
|
} else {
|
|
564
|
-
const conn = callbackContext
|
|
565
|
-
|
|
566
|
-
|
|
634
|
+
const conn = await waitForConnection(callbackContext);
|
|
635
|
+
try {
|
|
636
|
+
return await invokeClientCallback(conn, callbackId, args);
|
|
637
|
+
} catch (err) {
|
|
638
|
+
if (callbackContext.reconnectionPromise && !callbackContext.connection) {
|
|
639
|
+
const newConn = await callbackContext.reconnectionPromise.promise;
|
|
640
|
+
return invokeClientCallback(newConn, callbackId, args);
|
|
641
|
+
}
|
|
642
|
+
throw err;
|
|
567
643
|
}
|
|
568
|
-
return invokeClientCallback(conn, callbackId, args);
|
|
569
644
|
}
|
|
570
645
|
};
|
|
571
646
|
customFnMarshalOptions = { createMarshalContext, addCallbackIdsToRefs, invokeCallback };
|
|
@@ -582,27 +657,20 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
582
657
|
const nextCallbackId = callbackContext_.custom.get(`${name}:next`);
|
|
583
658
|
const returnCallbackId = callbackContext_.custom.get(`${name}:return`);
|
|
584
659
|
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);
|
|
660
|
+
const startResult = await invokeCallbackWithReconnect(callbackContext_, () => callbackContext_.custom.get(`${name}:start`), args, `AsyncIterator '${name}' start`, invokeClientCallback);
|
|
590
661
|
const iteratorId = startResult.iteratorId;
|
|
591
662
|
try {
|
|
592
663
|
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]);
|
|
664
|
+
const nextResult = await invokeCallbackWithReconnect(callbackContext_, () => callbackContext_.custom.get(`${name}:next`), [iteratorId], `AsyncIterator '${name}' next`, invokeClientCallback);
|
|
598
665
|
if (nextResult.done)
|
|
599
666
|
return nextResult.value;
|
|
600
667
|
yield nextResult.value;
|
|
601
668
|
}
|
|
602
669
|
} finally {
|
|
603
670
|
const retConn = callbackContext_.connection;
|
|
604
|
-
|
|
605
|
-
|
|
671
|
+
const retCbId = callbackContext_.custom.get(`${name}:return`);
|
|
672
|
+
if (retConn && retCbId !== undefined) {
|
|
673
|
+
await invokeClientCallback(retConn, retCbId, [iteratorId]).catch(() => {});
|
|
606
674
|
}
|
|
607
675
|
}
|
|
608
676
|
}
|
|
@@ -613,12 +681,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
613
681
|
bridgedCustomFunctions[name] = {
|
|
614
682
|
type: registration.type,
|
|
615
683
|
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);
|
|
684
|
+
return invokeCallbackWithReconnect(callbackContext_, () => callbackContext_.custom.get(name), args, `Custom function '${name}'`, invokeClientCallback);
|
|
622
685
|
}
|
|
623
686
|
};
|
|
624
687
|
}
|
|
@@ -627,12 +690,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
627
690
|
let moduleLoader;
|
|
628
691
|
if (moduleLoaderCallback) {
|
|
629
692
|
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]);
|
|
693
|
+
return invokeCallbackWithReconnect(callbackContext, () => callbackContext.moduleLoader, [specifier, importer], "Module loader", invokeClientCallback);
|
|
636
694
|
};
|
|
637
695
|
}
|
|
638
696
|
let testEnvironment;
|
|
@@ -663,19 +721,8 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
663
721
|
if (playwrightCallbacks) {
|
|
664
722
|
playwrightOptions = {
|
|
665
723
|
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
724
|
try {
|
|
678
|
-
const resultJson = await
|
|
725
|
+
const resultJson = await invokeCallbackWithReconnect(callbackContext, () => callbackContext.playwright.handlerCallbackId, [JSON.stringify(op)], "Playwright handler", invokeClientCallback);
|
|
679
726
|
return JSON.parse(resultJson);
|
|
680
727
|
} catch (err) {
|
|
681
728
|
const error = err;
|
|
@@ -715,18 +762,14 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
715
762
|
}
|
|
716
763
|
},
|
|
717
764
|
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
765
|
const serialized = {
|
|
724
766
|
url,
|
|
725
767
|
method: init.method,
|
|
726
768
|
headers: init.headers,
|
|
727
|
-
body: init.rawBody
|
|
769
|
+
body: init.rawBody,
|
|
770
|
+
signalAborted: init.signal.aborted
|
|
728
771
|
};
|
|
729
|
-
const result = await
|
|
772
|
+
const result = await invokeCallbackWithReconnect(callbackContext, () => callbackContext.fetch, [serialized], "Fetch", invokeClientCallback);
|
|
730
773
|
if (result && typeof result === "object" && result.__streamingResponse) {
|
|
731
774
|
const response = result.response;
|
|
732
775
|
response.__isCallbackStream = true;
|
|
@@ -736,10 +779,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
736
779
|
},
|
|
737
780
|
fs: {
|
|
738
781
|
getDirectory: async (dirPath) => {
|
|
739
|
-
const conn = callbackContext
|
|
740
|
-
if (!conn) {
|
|
741
|
-
throw new Error("FS callbacks not available");
|
|
742
|
-
}
|
|
782
|
+
const conn = await waitForConnection(callbackContext);
|
|
743
783
|
return import_callback_fs_handler.createCallbackFileSystemHandler({
|
|
744
784
|
connection: conn,
|
|
745
785
|
callbackContext,
|
|
@@ -967,6 +1007,12 @@ async function handleDispatchRequest(message, connection, state) {
|
|
|
967
1007
|
return;
|
|
968
1008
|
}
|
|
969
1009
|
instance.lastActivity = Date.now();
|
|
1010
|
+
const dispatchAbortController = new AbortController;
|
|
1011
|
+
connection.dispatchAbortControllers.set(message.requestId, dispatchAbortController);
|
|
1012
|
+
if (connection.earlyAbortedDispatches.has(message.requestId) || message.request.signalAborted) {
|
|
1013
|
+
dispatchAbortController.abort();
|
|
1014
|
+
connection.earlyAbortedDispatches.delete(message.requestId);
|
|
1015
|
+
}
|
|
970
1016
|
try {
|
|
971
1017
|
let requestBody = null;
|
|
972
1018
|
if (message.request.bodyStreamId !== undefined) {
|
|
@@ -977,9 +1023,12 @@ async function handleDispatchRequest(message, connection, state) {
|
|
|
977
1023
|
const request = new Request(message.request.url, {
|
|
978
1024
|
method: message.request.method,
|
|
979
1025
|
headers: message.request.headers,
|
|
980
|
-
body: requestBody
|
|
1026
|
+
body: requestBody,
|
|
1027
|
+
signal: dispatchAbortController.signal
|
|
1028
|
+
});
|
|
1029
|
+
const response = await instance.runtime.fetch.dispatchRequest(request, {
|
|
1030
|
+
signal: dispatchAbortController.signal
|
|
981
1031
|
});
|
|
982
|
-
const response = await instance.runtime.fetch.dispatchRequest(request);
|
|
983
1032
|
if (response.body) {
|
|
984
1033
|
await sendStreamedResponse(connection, message.requestId, response);
|
|
985
1034
|
} else {
|
|
@@ -999,7 +1048,18 @@ async function handleDispatchRequest(message, connection, state) {
|
|
|
999
1048
|
} catch (err) {
|
|
1000
1049
|
const error = err;
|
|
1001
1050
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1051
|
+
} finally {
|
|
1052
|
+
connection.dispatchAbortControllers.delete(message.requestId);
|
|
1053
|
+
connection.earlyAbortedDispatches.delete(message.requestId);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
function handleDispatchRequestAbort(message, connection) {
|
|
1057
|
+
const controller = connection.dispatchAbortControllers.get(message.targetRequestId);
|
|
1058
|
+
if (controller) {
|
|
1059
|
+
controller.abort();
|
|
1060
|
+
return;
|
|
1002
1061
|
}
|
|
1062
|
+
connection.earlyAbortedDispatches.add(message.targetRequestId);
|
|
1003
1063
|
}
|
|
1004
1064
|
function receiveStreamedBody(connection, streamId) {
|
|
1005
1065
|
return new Promise((resolve, reject) => {
|
|
@@ -1525,13 +1585,35 @@ async function handleRunTests(message, connection, state) {
|
|
|
1525
1585
|
return;
|
|
1526
1586
|
}
|
|
1527
1587
|
instance.lastActivity = Date.now();
|
|
1588
|
+
if (instance.pendingTestRun) {
|
|
1589
|
+
try {
|
|
1590
|
+
const results = await instance.pendingTestRun.promise;
|
|
1591
|
+
const currentConn = instance.callbackContext?.connection;
|
|
1592
|
+
if (currentConn) {
|
|
1593
|
+
sendOk(currentConn.socket, message.requestId, results);
|
|
1594
|
+
}
|
|
1595
|
+
} catch (err) {
|
|
1596
|
+
const error = err;
|
|
1597
|
+
const currentConn = instance.callbackContext?.connection;
|
|
1598
|
+
if (currentConn) {
|
|
1599
|
+
sendError(currentConn.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1528
1604
|
try {
|
|
1529
1605
|
const timeout = message.timeout ?? 30000;
|
|
1530
|
-
const
|
|
1531
|
-
|
|
1606
|
+
const runPromise = instance.runtime.testEnvironment.runTests(timeout);
|
|
1607
|
+
instance.pendingTestRun = { promise: runPromise };
|
|
1608
|
+
const results = await runPromise;
|
|
1609
|
+
instance.pendingTestRun = undefined;
|
|
1610
|
+
const currentConn = instance.callbackContext?.connection ?? connection;
|
|
1611
|
+
sendOk(currentConn.socket, message.requestId, results);
|
|
1532
1612
|
} catch (err) {
|
|
1613
|
+
instance.pendingTestRun = undefined;
|
|
1533
1614
|
const error = err;
|
|
1534
|
-
|
|
1615
|
+
const currentConn = instance.callbackContext?.connection ?? connection;
|
|
1616
|
+
sendError(currentConn.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1535
1617
|
}
|
|
1536
1618
|
}
|
|
1537
1619
|
async function handleResetTestEnv(message, connection, state) {
|
|
@@ -1610,4 +1692,4 @@ async function handleClearCollectedData(message, connection, state) {
|
|
|
1610
1692
|
}
|
|
1611
1693
|
}
|
|
1612
1694
|
|
|
1613
|
-
//# debugId=
|
|
1695
|
+
//# debugId=B1D2B1F7510AB4C564756E2164756E21
|