@symbiosis-lab/moss-api 0.2.0 → 0.4.0
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/index.d.mts +23 -11
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +31 -15
- package/dist/index.mjs.map +1 -1
- package/dist/testing/index.d.mts +253 -0
- package/dist/testing/index.d.mts.map +1 -0
- package/dist/testing/index.mjs +359 -0
- package/dist/testing/index.mjs.map +1 -0
- package/package.json +5 -1
package/dist/index.d.mts
CHANGED
|
@@ -457,6 +457,9 @@ declare function executeBinary(options: ExecuteOptions): Promise<ExecuteResult>;
|
|
|
457
457
|
*
|
|
458
458
|
* Allows plugins to store and retrieve authentication cookies
|
|
459
459
|
* for external services (e.g., Matters.town, GitHub).
|
|
460
|
+
*
|
|
461
|
+
* Cookies are automatically scoped to the plugin's registered domain
|
|
462
|
+
* (defined in manifest.json) - plugins cannot access other plugins' cookies.
|
|
460
463
|
*/
|
|
461
464
|
/**
|
|
462
465
|
* A cookie stored for plugin authentication
|
|
@@ -472,37 +475,46 @@ interface Cookie {
|
|
|
472
475
|
path?: string;
|
|
473
476
|
}
|
|
474
477
|
/**
|
|
475
|
-
* Get stored cookies for
|
|
478
|
+
* Get stored cookies for the current plugin.
|
|
476
479
|
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
+
* The plugin's identity is automatically detected from the runtime context.
|
|
481
|
+
* Cookies are filtered by the domain declared in the plugin's manifest.json.
|
|
482
|
+
*
|
|
483
|
+
* **Must be called from within a plugin hook** (process, generate, deploy, syndicate).
|
|
484
|
+
*
|
|
485
|
+
* @returns Array of cookies for the plugin's registered domain
|
|
486
|
+
* @throws Error if called outside of a plugin hook execution
|
|
480
487
|
*
|
|
481
488
|
* @example
|
|
482
489
|
* ```typescript
|
|
483
|
-
*
|
|
490
|
+
* // Inside a hook function:
|
|
491
|
+
* const cookies = await getPluginCookie();
|
|
484
492
|
* const token = cookies.find(c => c.name === "__access_token");
|
|
485
493
|
* if (token) {
|
|
486
494
|
* // Use token for authenticated requests
|
|
487
495
|
* }
|
|
488
496
|
* ```
|
|
489
497
|
*/
|
|
490
|
-
declare function getPluginCookie(
|
|
498
|
+
declare function getPluginCookie(): Promise<Cookie[]>;
|
|
491
499
|
/**
|
|
492
|
-
* Store cookies for
|
|
500
|
+
* Store cookies for the current plugin.
|
|
501
|
+
*
|
|
502
|
+
* The plugin's identity is automatically detected from the runtime context.
|
|
503
|
+
*
|
|
504
|
+
* **Must be called from within a plugin hook** (process, generate, deploy, syndicate).
|
|
493
505
|
*
|
|
494
|
-
* @param pluginName - Name of the plugin
|
|
495
|
-
* @param projectPath - Absolute path to the project directory
|
|
496
506
|
* @param cookies - Array of cookies to store
|
|
507
|
+
* @throws Error if called outside of a plugin hook execution
|
|
497
508
|
*
|
|
498
509
|
* @example
|
|
499
510
|
* ```typescript
|
|
500
|
-
*
|
|
511
|
+
* // Inside a hook function:
|
|
512
|
+
* await setPluginCookie([
|
|
501
513
|
* { name: "session", value: "abc123" }
|
|
502
514
|
* ]);
|
|
503
515
|
* ```
|
|
504
516
|
*/
|
|
505
|
-
declare function setPluginCookie(
|
|
517
|
+
declare function setPluginCookie(cookies: Cookie[]): Promise<void>;
|
|
506
518
|
//#endregion
|
|
507
519
|
export { AfterDeployContext, ArticleInfo, BaseContext, BeforeBuildContext, CompleteMessage, Cookie, DeploymentInfo, DownloadOptions, DownloadResult, ErrorMessage, ExecuteOptions, ExecuteResult, FetchOptions, FetchResult, HookResult, LogMessage, OnBuildContext, OnDeployContext, PluginCategory, PluginManifest, PluginMessage, ProgressMessage, ProjectInfo, SourceFiles, TauriCore, closeBrowser, downloadAsset, error, executeBinary, fetchUrl, fileExists, getMessageContext, getPluginCookie, getTauriCore, isTauriAvailable, listFiles, log, openBrowser, readFile, reportComplete, reportError, reportProgress, sendMessage, setMessageContext, setPluginCookie, warn, writeFile };
|
|
508
520
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/plugin.ts","../src/types/context.ts","../src/types/hooks.ts","../src/types/messages.ts","../src/utils/tauri.ts","../src/utils/messaging.ts","../src/utils/logger.ts","../src/utils/browser.ts","../src/utils/filesystem.ts","../src/utils/http.ts","../src/utils/binary.ts","../src/utils/cookies.ts"],"sourcesContent":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/plugin.ts","../src/types/context.ts","../src/types/hooks.ts","../src/types/messages.ts","../src/utils/tauri.ts","../src/utils/messaging.ts","../src/utils/logger.ts","../src/utils/browser.ts","../src/utils/filesystem.ts","../src/utils/http.ts","../src/utils/binary.ts","../src/utils/cookies.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -388,47 +388,63 @@ async function executeBinary(options) {
|
|
|
388
388
|
*
|
|
389
389
|
* Allows plugins to store and retrieve authentication cookies
|
|
390
390
|
* for external services (e.g., Matters.town, GitHub).
|
|
391
|
+
*
|
|
392
|
+
* Cookies are automatically scoped to the plugin's registered domain
|
|
393
|
+
* (defined in manifest.json) - plugins cannot access other plugins' cookies.
|
|
391
394
|
*/
|
|
392
395
|
/**
|
|
393
|
-
* Get stored cookies for
|
|
396
|
+
* Get stored cookies for the current plugin.
|
|
394
397
|
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
+
* The plugin's identity is automatically detected from the runtime context.
|
|
399
|
+
* Cookies are filtered by the domain declared in the plugin's manifest.json.
|
|
400
|
+
*
|
|
401
|
+
* **Must be called from within a plugin hook** (process, generate, deploy, syndicate).
|
|
402
|
+
*
|
|
403
|
+
* @returns Array of cookies for the plugin's registered domain
|
|
404
|
+
* @throws Error if called outside of a plugin hook execution
|
|
398
405
|
*
|
|
399
406
|
* @example
|
|
400
407
|
* ```typescript
|
|
401
|
-
*
|
|
408
|
+
* // Inside a hook function:
|
|
409
|
+
* const cookies = await getPluginCookie();
|
|
402
410
|
* const token = cookies.find(c => c.name === "__access_token");
|
|
403
411
|
* if (token) {
|
|
404
412
|
* // Use token for authenticated requests
|
|
405
413
|
* }
|
|
406
414
|
* ```
|
|
407
415
|
*/
|
|
408
|
-
async function getPluginCookie(
|
|
416
|
+
async function getPluginCookie() {
|
|
417
|
+
const context = window.__MOSS_CURRENT_CONTEXT__;
|
|
418
|
+
if (!context) throw new Error("getPluginCookie() must be called from within a plugin hook. Ensure you're calling this from process(), generate(), deploy(), or syndicate().");
|
|
409
419
|
return getTauriCore().invoke("get_plugin_cookie", {
|
|
410
|
-
pluginName,
|
|
411
|
-
projectPath
|
|
420
|
+
pluginName: context.plugin_name,
|
|
421
|
+
projectPath: context.project_path
|
|
412
422
|
});
|
|
413
423
|
}
|
|
414
424
|
/**
|
|
415
|
-
* Store cookies for
|
|
425
|
+
* Store cookies for the current plugin.
|
|
426
|
+
*
|
|
427
|
+
* The plugin's identity is automatically detected from the runtime context.
|
|
428
|
+
*
|
|
429
|
+
* **Must be called from within a plugin hook** (process, generate, deploy, syndicate).
|
|
416
430
|
*
|
|
417
|
-
* @param pluginName - Name of the plugin
|
|
418
|
-
* @param projectPath - Absolute path to the project directory
|
|
419
431
|
* @param cookies - Array of cookies to store
|
|
432
|
+
* @throws Error if called outside of a plugin hook execution
|
|
420
433
|
*
|
|
421
434
|
* @example
|
|
422
435
|
* ```typescript
|
|
423
|
-
*
|
|
436
|
+
* // Inside a hook function:
|
|
437
|
+
* await setPluginCookie([
|
|
424
438
|
* { name: "session", value: "abc123" }
|
|
425
439
|
* ]);
|
|
426
440
|
* ```
|
|
427
441
|
*/
|
|
428
|
-
async function setPluginCookie(
|
|
442
|
+
async function setPluginCookie(cookies) {
|
|
443
|
+
const context = window.__MOSS_CURRENT_CONTEXT__;
|
|
444
|
+
if (!context) throw new Error("setPluginCookie() must be called from within a plugin hook. Ensure you're calling this from process(), generate(), deploy(), or syndicate().");
|
|
429
445
|
await getTauriCore().invoke("set_plugin_cookie", {
|
|
430
|
-
pluginName,
|
|
431
|
-
projectPath,
|
|
446
|
+
pluginName: context.plugin_name,
|
|
447
|
+
projectPath: context.project_path,
|
|
432
448
|
cookies
|
|
433
449
|
});
|
|
434
450
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/utils/tauri.ts","../src/utils/messaging.ts","../src/utils/logger.ts","../src/utils/browser.ts","../src/utils/filesystem.ts","../src/utils/http.ts","../src/utils/binary.ts","../src/utils/cookies.ts"],"sourcesContent":["/**\n * Tauri core utilities for plugin communication\n */\n\nexport interface TauriCore {\n invoke: <T>(cmd: string, args?: Record<string, unknown>) => Promise<T>;\n}\n\ninterface TauriWindow {\n __TAURI__?: {\n core: TauriCore;\n };\n}\n\n/**\n * Get the Tauri core API\n *\n * @deprecated Use higher-level APIs instead:\n * - File operations: `readFile`, `writeFile`, `listFiles`, `fileExists`\n * - HTTP: `fetchUrl`, `downloadAsset`\n * - Binary execution: `executeBinary`\n * - Cookies: `getPluginCookie`, `setPluginCookie`\n *\n * @throws Error if Tauri is not available\n */\nexport function getTauriCore(): TauriCore {\n const w = window as unknown as TauriWindow;\n if (!w.__TAURI__?.core) {\n throw new Error(\"Tauri core not available\");\n }\n return w.__TAURI__.core;\n}\n\n/**\n * Check if Tauri is available\n */\nexport function isTauriAvailable(): boolean {\n const w = window as unknown as TauriWindow;\n return !!w.__TAURI__?.core;\n}\n","/**\n * Plugin messaging utilities for communicating with Moss\n */\n\nimport type { PluginMessage } from \"../types/messages\";\nimport { getTauriCore, isTauriAvailable } from \"./tauri\";\n\nlet currentPluginName = \"\";\nlet currentHookName = \"\";\n\n/**\n * Set the message context for subsequent messages\n * This is typically called automatically by the plugin runtime\n */\nexport function setMessageContext(pluginName: string, hookName: string): void {\n currentPluginName = pluginName;\n currentHookName = hookName;\n}\n\n/**\n * Get the current message context\n */\nexport function getMessageContext(): { pluginName: string; hookName: string } {\n return { pluginName: currentPluginName, hookName: currentHookName };\n}\n\n/**\n * Send a message to Moss\n * Silently fails if Tauri is unavailable (useful for testing)\n */\nexport async function sendMessage(message: PluginMessage): Promise<void> {\n if (!isTauriAvailable()) {\n return;\n }\n\n try {\n await getTauriCore().invoke(\"plugin_message\", {\n pluginName: currentPluginName,\n hookName: currentHookName,\n message,\n });\n } catch {\n // Silently fail - logging would be recursive\n }\n}\n\n/**\n * Report progress to Moss\n */\nexport async function reportProgress(\n phase: string,\n current: number,\n total: number,\n message?: string\n): Promise<void> {\n await sendMessage({ type: \"progress\", phase, current, total, message });\n}\n\n/**\n * Report an error to Moss\n */\nexport async function reportError(\n error: string,\n context?: string,\n fatal = false\n): Promise<void> {\n await sendMessage({ type: \"error\", error, context, fatal });\n}\n\n/**\n * Report completion to Moss\n */\nexport async function reportComplete(result: unknown): Promise<void> {\n await sendMessage({ type: \"complete\", result });\n}\n","/**\n * Logging utilities for plugins\n */\n\nimport { sendMessage } from \"./messaging\";\n\n/**\n * Log an informational message\n */\nexport async function log(message: string): Promise<void> {\n await sendMessage({ type: \"log\", level: \"log\", message });\n}\n\n/**\n * Log a warning message\n */\nexport async function warn(message: string): Promise<void> {\n await sendMessage({ type: \"log\", level: \"warn\", message });\n}\n\n/**\n * Log an error message\n */\nexport async function error(message: string): Promise<void> {\n await sendMessage({ type: \"log\", level: \"error\", message });\n}\n","/**\n * Browser utilities for plugins\n * Abstracts Tauri browser commands to decouple plugins from internal APIs\n */\n\nimport { getTauriCore } from \"./tauri\";\n\n/**\n * Open a URL in the plugin browser window\n */\nexport async function openBrowser(url: string): Promise<void> {\n await getTauriCore().invoke(\"open_plugin_browser\", { url });\n}\n\n/**\n * Close the plugin browser window\n */\nexport async function closeBrowser(): Promise<void> {\n await getTauriCore().invoke(\"close_plugin_browser\", {});\n}\n","/**\n * File system operations for Moss plugins\n *\n * These functions abstract away the underlying Tauri commands,\n * providing a clean API for plugins to read/write project files.\n */\n\nimport { getTauriCore } from \"./tauri\";\n\n/**\n * Read a file from the project directory\n *\n * @param projectPath - Absolute path to the project directory\n * @param relativePath - Path relative to the project root\n * @returns File contents as a string\n * @throws Error if file cannot be read\n *\n * @example\n * ```typescript\n * const content = await readFile(\"/path/to/project\", \"src/index.ts\");\n * ```\n */\nexport async function readFile(\n projectPath: string,\n relativePath: string\n): Promise<string> {\n return getTauriCore().invoke<string>(\"read_project_file\", {\n projectPath,\n relativePath,\n });\n}\n\n/**\n * Write content to a file in the project directory\n *\n * Creates parent directories if they don't exist.\n *\n * @param projectPath - Absolute path to the project directory\n * @param relativePath - Path relative to the project root\n * @param content - Content to write to the file\n * @throws Error if file cannot be written\n *\n * @example\n * ```typescript\n * await writeFile(\"/path/to/project\", \"output/result.md\", \"# Hello World\");\n * ```\n */\nexport async function writeFile(\n projectPath: string,\n relativePath: string,\n content: string\n): Promise<void> {\n await getTauriCore().invoke(\"write_project_file\", {\n projectPath,\n relativePath,\n data: content,\n });\n}\n\n/**\n * List all files in a project directory\n *\n * Returns file paths relative to the project root.\n *\n * @param projectPath - Absolute path to the project directory\n * @returns Array of relative file paths\n * @throws Error if directory cannot be listed\n *\n * @example\n * ```typescript\n * const files = await listFiles(\"/path/to/project\");\n * // [\"src/index.ts\", \"package.json\", \"README.md\"]\n * ```\n */\nexport async function listFiles(projectPath: string): Promise<string[]> {\n return getTauriCore().invoke<string[]>(\"list_project_files\", {\n projectPath,\n });\n}\n\n/**\n * Check if a file exists in the project directory\n *\n * @param projectPath - Absolute path to the project directory\n * @param relativePath - Path relative to the project root\n * @returns true if file exists, false otherwise\n *\n * @example\n * ```typescript\n * if (await fileExists(\"/path/to/project\", \"config.json\")) {\n * // load config\n * }\n * ```\n */\nexport async function fileExists(\n projectPath: string,\n relativePath: string\n): Promise<boolean> {\n try {\n await readFile(projectPath, relativePath);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * HTTP operations for Moss plugins\n *\n * These functions provide HTTP capabilities that bypass browser CORS\n * restrictions by using Rust's HTTP client under the hood.\n */\n\nimport { getTauriCore } from \"./tauri\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Options for HTTP fetch requests\n */\nexport interface FetchOptions {\n /** Timeout in milliseconds (default: 30000) */\n timeoutMs?: number;\n}\n\n/**\n * Result from an HTTP fetch operation\n */\nexport interface FetchResult {\n /** HTTP status code */\n status: number;\n /** Whether the request was successful (2xx status) */\n ok: boolean;\n /** Content-Type header from response */\n contentType: string | null;\n /** Response body as Uint8Array */\n body: Uint8Array;\n /** Get response body as text */\n text(): string;\n}\n\n/**\n * Options for asset download\n */\nexport interface DownloadOptions {\n /** Timeout in milliseconds (default: 30000) */\n timeoutMs?: number;\n}\n\n/**\n * Result from an asset download operation\n */\nexport interface DownloadResult {\n /** HTTP status code */\n status: number;\n /** Whether the request was successful (2xx status) */\n ok: boolean;\n /** Content-Type header from response */\n contentType: string | null;\n /** Number of bytes written to disk */\n bytesWritten: number;\n /** Actual path where file was saved (relative to project) */\n actualPath: string;\n}\n\n// ============================================================================\n// Internal Types (Tauri response shapes)\n// ============================================================================\n\ninterface TauriFetchResult {\n status: number;\n ok: boolean;\n body_base64: string;\n content_type: string | null;\n}\n\ninterface TauriDownloadResult {\n status: number;\n ok: boolean;\n content_type: string | null;\n bytes_written: number;\n actual_path: string;\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Fetch a URL using Rust's HTTP client (bypasses CORS)\n *\n * @param url - URL to fetch\n * @param options - Optional fetch configuration\n * @returns Fetch result with status, body, and helpers\n * @throws Error if network request fails\n *\n * @example\n * ```typescript\n * const result = await fetchUrl(\"https://api.example.com/data\");\n * if (result.ok) {\n * const data = JSON.parse(result.text());\n * }\n * ```\n */\nexport async function fetchUrl(\n url: string,\n options: FetchOptions = {}\n): Promise<FetchResult> {\n const { timeoutMs = 30000 } = options;\n\n const result = await getTauriCore().invoke<TauriFetchResult>(\"fetch_url\", {\n url,\n timeoutMs,\n });\n\n // Decode base64 body to Uint8Array\n const binaryString = atob(result.body_base64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n return {\n status: result.status,\n ok: result.ok,\n contentType: result.content_type,\n body: bytes,\n text(): string {\n return new TextDecoder().decode(bytes);\n },\n };\n}\n\n/**\n * Download a URL and save directly to disk\n *\n * Downloads the file and writes it directly to disk without passing\n * the binary data through JavaScript. The filename is derived from\n * the URL, and file extension is inferred from Content-Type if needed.\n *\n * @param url - URL to download\n * @param projectPath - Absolute path to the project directory\n * @param targetDir - Target directory within project (e.g., \"assets\")\n * @param options - Optional download configuration\n * @returns Download result with actual path where file was saved\n * @throws Error if download or write fails\n *\n * @example\n * ```typescript\n * const result = await downloadAsset(\n * \"https://example.com/image\",\n * \"/path/to/project\",\n * \"assets\"\n * );\n * if (result.ok) {\n * console.log(`Saved to ${result.actualPath}`); // e.g., \"assets/image.png\"\n * }\n * ```\n */\nexport async function downloadAsset(\n url: string,\n projectPath: string,\n targetDir: string,\n options: DownloadOptions = {}\n): Promise<DownloadResult> {\n const { timeoutMs = 30000 } = options;\n\n const result = await getTauriCore().invoke<TauriDownloadResult>(\n \"download_asset\",\n {\n url,\n projectPath,\n targetDir,\n timeoutMs,\n }\n );\n\n return {\n status: result.status,\n ok: result.ok,\n contentType: result.content_type,\n bytesWritten: result.bytes_written,\n actualPath: result.actual_path,\n };\n}\n","/**\n * Binary execution for Moss plugins\n *\n * Allows plugins to execute external binaries (git, npm, etc.)\n * in a controlled environment.\n */\n\nimport { getTauriCore } from \"./tauri\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Options for executing a binary\n */\nexport interface ExecuteOptions {\n /** Path to the binary (can be just the name if in PATH) */\n binaryPath: string;\n /** Arguments to pass to the binary */\n args: string[];\n /** Working directory for execution */\n workingDir: string;\n /** Timeout in milliseconds (default: 60000) */\n timeoutMs?: number;\n /** Additional environment variables */\n env?: Record<string, string>;\n}\n\n/**\n * Result from binary execution\n */\nexport interface ExecuteResult {\n /** Whether the command succeeded (exit code 0) */\n success: boolean;\n /** Exit code from the process */\n exitCode: number;\n /** Standard output from the process */\n stdout: string;\n /** Standard error output from the process */\n stderr: string;\n}\n\n// ============================================================================\n// Internal Types (Tauri response shape)\n// ============================================================================\n\ninterface TauriBinaryResult {\n success: boolean;\n exit_code: number;\n stdout: string;\n stderr: string;\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Execute an external binary\n *\n * @param options - Execution options including binary path, args, and working directory\n * @returns Execution result with stdout, stderr, and exit code\n * @throws Error if binary cannot be executed\n *\n * @example\n * ```typescript\n * // Run git status\n * const result = await executeBinary({\n * binaryPath: \"git\",\n * args: [\"status\"],\n * workingDir: \"/path/to/repo\",\n * });\n *\n * if (result.success) {\n * console.log(result.stdout);\n * } else {\n * console.error(result.stderr);\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Run npm install with timeout\n * const result = await executeBinary({\n * binaryPath: \"npm\",\n * args: [\"install\"],\n * workingDir: \"/path/to/project\",\n * timeoutMs: 120000,\n * env: { NODE_ENV: \"production\" },\n * });\n * ```\n */\nexport async function executeBinary(\n options: ExecuteOptions\n): Promise<ExecuteResult> {\n const { binaryPath, args, workingDir, timeoutMs = 60000, env } = options;\n\n const result = await getTauriCore().invoke<TauriBinaryResult>(\n \"execute_binary\",\n {\n binaryPath,\n args,\n workingDir,\n timeoutMs,\n env,\n }\n );\n\n return {\n success: result.success,\n exitCode: result.exit_code,\n stdout: result.stdout,\n stderr: result.stderr,\n };\n}\n","/**\n * Cookie management for Moss plugins\n *\n * Allows plugins to store and retrieve authentication cookies\n * for external services (e.g., Matters.town, GitHub).\n */\n\nimport { getTauriCore } from \"./tauri\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * A cookie stored for plugin authentication\n */\nexport interface Cookie {\n /** Cookie name */\n name: string;\n /** Cookie value */\n value: string;\n /** Optional domain for the cookie */\n domain?: string;\n /** Optional path for the cookie */\n path?: string;\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get stored cookies for a plugin\n *\n * @param pluginName - Name of the plugin\n * @param projectPath - Absolute path to the project directory\n * @returns Array of stored cookies\n *\n * @example\n * ```typescript\n * const cookies = await getPluginCookie(\"matters-syndicator\", \"/path/to/project\");\n * const token = cookies.find(c => c.name === \"__access_token\");\n * if (token) {\n * // Use token for authenticated requests\n * }\n * ```\n */\nexport async function getPluginCookie(\n pluginName: string,\n projectPath: string\n): Promise<Cookie[]> {\n return getTauriCore().invoke<Cookie[]>(\"get_plugin_cookie\", {\n pluginName,\n projectPath,\n });\n}\n\n/**\n * Store cookies for a plugin\n *\n * @param pluginName - Name of the plugin\n * @param projectPath - Absolute path to the project directory\n * @param cookies - Array of cookies to store\n *\n * @example\n * ```typescript\n * await setPluginCookie(\"my-plugin\", \"/path/to/project\", [\n * { name: \"session\", value: \"abc123\" }\n * ]);\n * ```\n */\nexport async function setPluginCookie(\n pluginName: string,\n projectPath: string,\n cookies: Cookie[]\n): Promise<void> {\n await getTauriCore().invoke(\"set_plugin_cookie\", {\n pluginName,\n projectPath,\n cookies,\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/utils/tauri.ts","../src/utils/messaging.ts","../src/utils/logger.ts","../src/utils/browser.ts","../src/utils/filesystem.ts","../src/utils/http.ts","../src/utils/binary.ts","../src/utils/cookies.ts"],"sourcesContent":["/**\n * Tauri core utilities for plugin communication\n */\n\nexport interface TauriCore {\n invoke: <T>(cmd: string, args?: Record<string, unknown>) => Promise<T>;\n}\n\ninterface TauriWindow {\n __TAURI__?: {\n core: TauriCore;\n };\n}\n\n/**\n * Get the Tauri core API\n *\n * @deprecated Use higher-level APIs instead:\n * - File operations: `readFile`, `writeFile`, `listFiles`, `fileExists`\n * - HTTP: `fetchUrl`, `downloadAsset`\n * - Binary execution: `executeBinary`\n * - Cookies: `getPluginCookie`, `setPluginCookie`\n *\n * @throws Error if Tauri is not available\n */\nexport function getTauriCore(): TauriCore {\n const w = window as unknown as TauriWindow;\n if (!w.__TAURI__?.core) {\n throw new Error(\"Tauri core not available\");\n }\n return w.__TAURI__.core;\n}\n\n/**\n * Check if Tauri is available\n */\nexport function isTauriAvailable(): boolean {\n const w = window as unknown as TauriWindow;\n return !!w.__TAURI__?.core;\n}\n","/**\n * Plugin messaging utilities for communicating with Moss\n */\n\nimport type { PluginMessage } from \"../types/messages\";\nimport { getTauriCore, isTauriAvailable } from \"./tauri\";\n\nlet currentPluginName = \"\";\nlet currentHookName = \"\";\n\n/**\n * Set the message context for subsequent messages\n * This is typically called automatically by the plugin runtime\n */\nexport function setMessageContext(pluginName: string, hookName: string): void {\n currentPluginName = pluginName;\n currentHookName = hookName;\n}\n\n/**\n * Get the current message context\n */\nexport function getMessageContext(): { pluginName: string; hookName: string } {\n return { pluginName: currentPluginName, hookName: currentHookName };\n}\n\n/**\n * Send a message to Moss\n * Silently fails if Tauri is unavailable (useful for testing)\n */\nexport async function sendMessage(message: PluginMessage): Promise<void> {\n if (!isTauriAvailable()) {\n return;\n }\n\n try {\n await getTauriCore().invoke(\"plugin_message\", {\n pluginName: currentPluginName,\n hookName: currentHookName,\n message,\n });\n } catch {\n // Silently fail - logging would be recursive\n }\n}\n\n/**\n * Report progress to Moss\n */\nexport async function reportProgress(\n phase: string,\n current: number,\n total: number,\n message?: string\n): Promise<void> {\n await sendMessage({ type: \"progress\", phase, current, total, message });\n}\n\n/**\n * Report an error to Moss\n */\nexport async function reportError(\n error: string,\n context?: string,\n fatal = false\n): Promise<void> {\n await sendMessage({ type: \"error\", error, context, fatal });\n}\n\n/**\n * Report completion to Moss\n */\nexport async function reportComplete(result: unknown): Promise<void> {\n await sendMessage({ type: \"complete\", result });\n}\n","/**\n * Logging utilities for plugins\n */\n\nimport { sendMessage } from \"./messaging\";\n\n/**\n * Log an informational message\n */\nexport async function log(message: string): Promise<void> {\n await sendMessage({ type: \"log\", level: \"log\", message });\n}\n\n/**\n * Log a warning message\n */\nexport async function warn(message: string): Promise<void> {\n await sendMessage({ type: \"log\", level: \"warn\", message });\n}\n\n/**\n * Log an error message\n */\nexport async function error(message: string): Promise<void> {\n await sendMessage({ type: \"log\", level: \"error\", message });\n}\n","/**\n * Browser utilities for plugins\n * Abstracts Tauri browser commands to decouple plugins from internal APIs\n */\n\nimport { getTauriCore } from \"./tauri\";\n\n/**\n * Open a URL in the plugin browser window\n */\nexport async function openBrowser(url: string): Promise<void> {\n await getTauriCore().invoke(\"open_plugin_browser\", { url });\n}\n\n/**\n * Close the plugin browser window\n */\nexport async function closeBrowser(): Promise<void> {\n await getTauriCore().invoke(\"close_plugin_browser\", {});\n}\n","/**\n * File system operations for Moss plugins\n *\n * These functions abstract away the underlying Tauri commands,\n * providing a clean API for plugins to read/write project files.\n */\n\nimport { getTauriCore } from \"./tauri\";\n\n/**\n * Read a file from the project directory\n *\n * @param projectPath - Absolute path to the project directory\n * @param relativePath - Path relative to the project root\n * @returns File contents as a string\n * @throws Error if file cannot be read\n *\n * @example\n * ```typescript\n * const content = await readFile(\"/path/to/project\", \"src/index.ts\");\n * ```\n */\nexport async function readFile(\n projectPath: string,\n relativePath: string\n): Promise<string> {\n return getTauriCore().invoke<string>(\"read_project_file\", {\n projectPath,\n relativePath,\n });\n}\n\n/**\n * Write content to a file in the project directory\n *\n * Creates parent directories if they don't exist.\n *\n * @param projectPath - Absolute path to the project directory\n * @param relativePath - Path relative to the project root\n * @param content - Content to write to the file\n * @throws Error if file cannot be written\n *\n * @example\n * ```typescript\n * await writeFile(\"/path/to/project\", \"output/result.md\", \"# Hello World\");\n * ```\n */\nexport async function writeFile(\n projectPath: string,\n relativePath: string,\n content: string\n): Promise<void> {\n await getTauriCore().invoke(\"write_project_file\", {\n projectPath,\n relativePath,\n data: content,\n });\n}\n\n/**\n * List all files in a project directory\n *\n * Returns file paths relative to the project root.\n *\n * @param projectPath - Absolute path to the project directory\n * @returns Array of relative file paths\n * @throws Error if directory cannot be listed\n *\n * @example\n * ```typescript\n * const files = await listFiles(\"/path/to/project\");\n * // [\"src/index.ts\", \"package.json\", \"README.md\"]\n * ```\n */\nexport async function listFiles(projectPath: string): Promise<string[]> {\n return getTauriCore().invoke<string[]>(\"list_project_files\", {\n projectPath,\n });\n}\n\n/**\n * Check if a file exists in the project directory\n *\n * @param projectPath - Absolute path to the project directory\n * @param relativePath - Path relative to the project root\n * @returns true if file exists, false otherwise\n *\n * @example\n * ```typescript\n * if (await fileExists(\"/path/to/project\", \"config.json\")) {\n * // load config\n * }\n * ```\n */\nexport async function fileExists(\n projectPath: string,\n relativePath: string\n): Promise<boolean> {\n try {\n await readFile(projectPath, relativePath);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * HTTP operations for Moss plugins\n *\n * These functions provide HTTP capabilities that bypass browser CORS\n * restrictions by using Rust's HTTP client under the hood.\n */\n\nimport { getTauriCore } from \"./tauri\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Options for HTTP fetch requests\n */\nexport interface FetchOptions {\n /** Timeout in milliseconds (default: 30000) */\n timeoutMs?: number;\n}\n\n/**\n * Result from an HTTP fetch operation\n */\nexport interface FetchResult {\n /** HTTP status code */\n status: number;\n /** Whether the request was successful (2xx status) */\n ok: boolean;\n /** Content-Type header from response */\n contentType: string | null;\n /** Response body as Uint8Array */\n body: Uint8Array;\n /** Get response body as text */\n text(): string;\n}\n\n/**\n * Options for asset download\n */\nexport interface DownloadOptions {\n /** Timeout in milliseconds (default: 30000) */\n timeoutMs?: number;\n}\n\n/**\n * Result from an asset download operation\n */\nexport interface DownloadResult {\n /** HTTP status code */\n status: number;\n /** Whether the request was successful (2xx status) */\n ok: boolean;\n /** Content-Type header from response */\n contentType: string | null;\n /** Number of bytes written to disk */\n bytesWritten: number;\n /** Actual path where file was saved (relative to project) */\n actualPath: string;\n}\n\n// ============================================================================\n// Internal Types (Tauri response shapes)\n// ============================================================================\n\ninterface TauriFetchResult {\n status: number;\n ok: boolean;\n body_base64: string;\n content_type: string | null;\n}\n\ninterface TauriDownloadResult {\n status: number;\n ok: boolean;\n content_type: string | null;\n bytes_written: number;\n actual_path: string;\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Fetch a URL using Rust's HTTP client (bypasses CORS)\n *\n * @param url - URL to fetch\n * @param options - Optional fetch configuration\n * @returns Fetch result with status, body, and helpers\n * @throws Error if network request fails\n *\n * @example\n * ```typescript\n * const result = await fetchUrl(\"https://api.example.com/data\");\n * if (result.ok) {\n * const data = JSON.parse(result.text());\n * }\n * ```\n */\nexport async function fetchUrl(\n url: string,\n options: FetchOptions = {}\n): Promise<FetchResult> {\n const { timeoutMs = 30000 } = options;\n\n const result = await getTauriCore().invoke<TauriFetchResult>(\"fetch_url\", {\n url,\n timeoutMs,\n });\n\n // Decode base64 body to Uint8Array\n const binaryString = atob(result.body_base64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n return {\n status: result.status,\n ok: result.ok,\n contentType: result.content_type,\n body: bytes,\n text(): string {\n return new TextDecoder().decode(bytes);\n },\n };\n}\n\n/**\n * Download a URL and save directly to disk\n *\n * Downloads the file and writes it directly to disk without passing\n * the binary data through JavaScript. The filename is derived from\n * the URL, and file extension is inferred from Content-Type if needed.\n *\n * @param url - URL to download\n * @param projectPath - Absolute path to the project directory\n * @param targetDir - Target directory within project (e.g., \"assets\")\n * @param options - Optional download configuration\n * @returns Download result with actual path where file was saved\n * @throws Error if download or write fails\n *\n * @example\n * ```typescript\n * const result = await downloadAsset(\n * \"https://example.com/image\",\n * \"/path/to/project\",\n * \"assets\"\n * );\n * if (result.ok) {\n * console.log(`Saved to ${result.actualPath}`); // e.g., \"assets/image.png\"\n * }\n * ```\n */\nexport async function downloadAsset(\n url: string,\n projectPath: string,\n targetDir: string,\n options: DownloadOptions = {}\n): Promise<DownloadResult> {\n const { timeoutMs = 30000 } = options;\n\n const result = await getTauriCore().invoke<TauriDownloadResult>(\n \"download_asset\",\n {\n url,\n projectPath,\n targetDir,\n timeoutMs,\n }\n );\n\n return {\n status: result.status,\n ok: result.ok,\n contentType: result.content_type,\n bytesWritten: result.bytes_written,\n actualPath: result.actual_path,\n };\n}\n","/**\n * Binary execution for Moss plugins\n *\n * Allows plugins to execute external binaries (git, npm, etc.)\n * in a controlled environment.\n */\n\nimport { getTauriCore } from \"./tauri\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Options for executing a binary\n */\nexport interface ExecuteOptions {\n /** Path to the binary (can be just the name if in PATH) */\n binaryPath: string;\n /** Arguments to pass to the binary */\n args: string[];\n /** Working directory for execution */\n workingDir: string;\n /** Timeout in milliseconds (default: 60000) */\n timeoutMs?: number;\n /** Additional environment variables */\n env?: Record<string, string>;\n}\n\n/**\n * Result from binary execution\n */\nexport interface ExecuteResult {\n /** Whether the command succeeded (exit code 0) */\n success: boolean;\n /** Exit code from the process */\n exitCode: number;\n /** Standard output from the process */\n stdout: string;\n /** Standard error output from the process */\n stderr: string;\n}\n\n// ============================================================================\n// Internal Types (Tauri response shape)\n// ============================================================================\n\ninterface TauriBinaryResult {\n success: boolean;\n exit_code: number;\n stdout: string;\n stderr: string;\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Execute an external binary\n *\n * @param options - Execution options including binary path, args, and working directory\n * @returns Execution result with stdout, stderr, and exit code\n * @throws Error if binary cannot be executed\n *\n * @example\n * ```typescript\n * // Run git status\n * const result = await executeBinary({\n * binaryPath: \"git\",\n * args: [\"status\"],\n * workingDir: \"/path/to/repo\",\n * });\n *\n * if (result.success) {\n * console.log(result.stdout);\n * } else {\n * console.error(result.stderr);\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Run npm install with timeout\n * const result = await executeBinary({\n * binaryPath: \"npm\",\n * args: [\"install\"],\n * workingDir: \"/path/to/project\",\n * timeoutMs: 120000,\n * env: { NODE_ENV: \"production\" },\n * });\n * ```\n */\nexport async function executeBinary(\n options: ExecuteOptions\n): Promise<ExecuteResult> {\n const { binaryPath, args, workingDir, timeoutMs = 60000, env } = options;\n\n const result = await getTauriCore().invoke<TauriBinaryResult>(\n \"execute_binary\",\n {\n binaryPath,\n args,\n workingDir,\n timeoutMs,\n env,\n }\n );\n\n return {\n success: result.success,\n exitCode: result.exit_code,\n stdout: result.stdout,\n stderr: result.stderr,\n };\n}\n","/**\n * Cookie management for Moss plugins\n *\n * Allows plugins to store and retrieve authentication cookies\n * for external services (e.g., Matters.town, GitHub).\n *\n * Cookies are automatically scoped to the plugin's registered domain\n * (defined in manifest.json) - plugins cannot access other plugins' cookies.\n */\n\nimport { getTauriCore } from \"./tauri\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * A cookie stored for plugin authentication\n */\nexport interface Cookie {\n /** Cookie name */\n name: string;\n /** Cookie value */\n value: string;\n /** Optional domain for the cookie */\n domain?: string;\n /** Optional path for the cookie */\n path?: string;\n}\n\n/**\n * Plugin execution context - set by plugin runtime during hook execution.\n * Used for auto-detecting the calling plugin's identity.\n */\ninterface PluginContext {\n plugin_name: string;\n project_path: string;\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get stored cookies for the current plugin.\n *\n * The plugin's identity is automatically detected from the runtime context.\n * Cookies are filtered by the domain declared in the plugin's manifest.json.\n *\n * **Must be called from within a plugin hook** (process, generate, deploy, syndicate).\n *\n * @returns Array of cookies for the plugin's registered domain\n * @throws Error if called outside of a plugin hook execution\n *\n * @example\n * ```typescript\n * // Inside a hook function:\n * const cookies = await getPluginCookie();\n * const token = cookies.find(c => c.name === \"__access_token\");\n * if (token) {\n * // Use token for authenticated requests\n * }\n * ```\n */\nexport async function getPluginCookie(): Promise<Cookie[]> {\n const context = (window as unknown as { __MOSS_CURRENT_CONTEXT__?: PluginContext }).__MOSS_CURRENT_CONTEXT__;\n\n if (!context) {\n throw new Error(\n \"getPluginCookie() must be called from within a plugin hook. \" +\n \"Ensure you're calling this from process(), generate(), deploy(), or syndicate().\"\n );\n }\n\n return getTauriCore().invoke<Cookie[]>(\"get_plugin_cookie\", {\n pluginName: context.plugin_name,\n projectPath: context.project_path,\n });\n}\n\n/**\n * Store cookies for the current plugin.\n *\n * The plugin's identity is automatically detected from the runtime context.\n *\n * **Must be called from within a plugin hook** (process, generate, deploy, syndicate).\n *\n * @param cookies - Array of cookies to store\n * @throws Error if called outside of a plugin hook execution\n *\n * @example\n * ```typescript\n * // Inside a hook function:\n * await setPluginCookie([\n * { name: \"session\", value: \"abc123\" }\n * ]);\n * ```\n */\nexport async function setPluginCookie(cookies: Cookie[]): Promise<void> {\n const context = (window as unknown as { __MOSS_CURRENT_CONTEXT__?: PluginContext }).__MOSS_CURRENT_CONTEXT__;\n\n if (!context) {\n throw new Error(\n \"setPluginCookie() must be called from within a plugin hook. \" +\n \"Ensure you're calling this from process(), generate(), deploy(), or syndicate().\"\n );\n }\n\n await getTauriCore().invoke(\"set_plugin_cookie\", {\n pluginName: context.plugin_name,\n projectPath: context.project_path,\n cookies,\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
//#region src/testing/mock-tauri.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Tauri IPC mocking utilities for testing Moss plugins
|
|
4
|
+
*
|
|
5
|
+
* Provides in-memory implementations of Tauri IPC commands that plugins use
|
|
6
|
+
* through moss-api. This enables integration testing without a running Tauri app.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { setupMockTauri } from "@symbiosis-lab/moss-api/testing";
|
|
11
|
+
*
|
|
12
|
+
* describe("my plugin", () => {
|
|
13
|
+
* let ctx: MockTauriContext;
|
|
14
|
+
*
|
|
15
|
+
* beforeEach(() => {
|
|
16
|
+
* ctx = setupMockTauri();
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* afterEach(() => {
|
|
20
|
+
* ctx.cleanup();
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* it("reads files", async () => {
|
|
24
|
+
* ctx.filesystem.setFile("/project/test.md", "# Hello");
|
|
25
|
+
* const content = await readFile("/project", "test.md");
|
|
26
|
+
* expect(content).toBe("# Hello");
|
|
27
|
+
* });
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* A file stored in the mock filesystem
|
|
33
|
+
*/
|
|
34
|
+
interface MockFile {
|
|
35
|
+
content: string;
|
|
36
|
+
createdAt: Date;
|
|
37
|
+
modifiedAt: Date;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* In-memory filesystem for testing file operations
|
|
41
|
+
*/
|
|
42
|
+
interface MockFilesystem {
|
|
43
|
+
/** Internal file storage */
|
|
44
|
+
files: Map<string, MockFile>;
|
|
45
|
+
/** Get a file by full path */
|
|
46
|
+
getFile(path: string): MockFile | undefined;
|
|
47
|
+
/** Set a file's content (creates or updates) */
|
|
48
|
+
setFile(path: string, content: string): void;
|
|
49
|
+
/** Delete a file */
|
|
50
|
+
deleteFile(path: string): boolean;
|
|
51
|
+
/** List files matching an optional pattern */
|
|
52
|
+
listFiles(pattern?: string): string[];
|
|
53
|
+
/** Clear all files */
|
|
54
|
+
clear(): void;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create a new mock filesystem instance
|
|
58
|
+
*/
|
|
59
|
+
declare function createMockFilesystem(): MockFilesystem;
|
|
60
|
+
/**
|
|
61
|
+
* Tracks download activity for testing concurrency and completion
|
|
62
|
+
*/
|
|
63
|
+
interface DownloadTracker {
|
|
64
|
+
/** Number of currently active downloads */
|
|
65
|
+
activeDownloads: number;
|
|
66
|
+
/** Maximum concurrent downloads observed */
|
|
67
|
+
maxConcurrent: number;
|
|
68
|
+
/** URLs of completed downloads */
|
|
69
|
+
completedDownloads: string[];
|
|
70
|
+
/** Failed downloads with error messages */
|
|
71
|
+
failedDownloads: Array<{
|
|
72
|
+
url: string;
|
|
73
|
+
error: string;
|
|
74
|
+
}>;
|
|
75
|
+
/** Mark a download as started */
|
|
76
|
+
startDownload(url: string): void;
|
|
77
|
+
/** Mark a download as ended */
|
|
78
|
+
endDownload(url: string, success: boolean, error?: string): void;
|
|
79
|
+
/** Reset all tracking state */
|
|
80
|
+
reset(): void;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Create a new download tracker instance
|
|
84
|
+
*/
|
|
85
|
+
declare function createDownloadTracker(): DownloadTracker;
|
|
86
|
+
/**
|
|
87
|
+
* Configuration for a mocked URL response
|
|
88
|
+
*/
|
|
89
|
+
interface MockUrlResponse {
|
|
90
|
+
/** HTTP status code */
|
|
91
|
+
status: number;
|
|
92
|
+
/** Whether the request was successful (2xx) */
|
|
93
|
+
ok: boolean;
|
|
94
|
+
/** Content-Type header */
|
|
95
|
+
contentType?: string;
|
|
96
|
+
/** Response body as base64 (for fetch_url) */
|
|
97
|
+
bodyBase64?: string;
|
|
98
|
+
/** Number of bytes written (for download_asset) */
|
|
99
|
+
bytesWritten?: number;
|
|
100
|
+
/** Actual file path where asset was saved */
|
|
101
|
+
actualPath?: string;
|
|
102
|
+
/** Artificial delay in milliseconds */
|
|
103
|
+
delay?: number;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* URL response configuration for mocking HTTP requests
|
|
107
|
+
*/
|
|
108
|
+
interface MockUrlConfig {
|
|
109
|
+
/** Map of URL to response(s) */
|
|
110
|
+
responses: Map<string, MockUrlResponse | MockUrlResponse[]>;
|
|
111
|
+
/** Default response for unregistered URLs */
|
|
112
|
+
defaultResponse: MockUrlResponse;
|
|
113
|
+
/** Set response for a URL (can be single or array for retry testing) */
|
|
114
|
+
setResponse(url: string, response: MockUrlResponse | MockUrlResponse[]): void;
|
|
115
|
+
/** Get response for a URL (handles retry sequences) */
|
|
116
|
+
getResponse(url: string): MockUrlResponse;
|
|
117
|
+
/** Reset all URL configurations */
|
|
118
|
+
reset(): void;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Create a new URL config instance
|
|
122
|
+
*/
|
|
123
|
+
declare function createMockUrlConfig(): MockUrlConfig;
|
|
124
|
+
/**
|
|
125
|
+
* Result for a mocked binary execution
|
|
126
|
+
*/
|
|
127
|
+
interface MockBinaryResult {
|
|
128
|
+
success: boolean;
|
|
129
|
+
exitCode: number;
|
|
130
|
+
stdout: string;
|
|
131
|
+
stderr: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Configuration for mocking binary execution
|
|
135
|
+
*/
|
|
136
|
+
interface MockBinaryConfig {
|
|
137
|
+
/** Map of binary commands to results */
|
|
138
|
+
results: Map<string, MockBinaryResult>;
|
|
139
|
+
/** Default result for unregistered binaries */
|
|
140
|
+
defaultResult: MockBinaryResult;
|
|
141
|
+
/** Set result for a binary command (key format: "binaryPath args...") */
|
|
142
|
+
setResult(key: string, result: MockBinaryResult): void;
|
|
143
|
+
/** Get result for a binary command */
|
|
144
|
+
getResult(binaryPath: string, args: string[]): MockBinaryResult;
|
|
145
|
+
/** Reset all configurations */
|
|
146
|
+
reset(): void;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Create a new binary config instance
|
|
150
|
+
*/
|
|
151
|
+
declare function createMockBinaryConfig(): MockBinaryConfig;
|
|
152
|
+
/**
|
|
153
|
+
* Mock cookie storage for plugin authentication testing
|
|
154
|
+
*/
|
|
155
|
+
interface MockCookieStorage {
|
|
156
|
+
/** Map of pluginName:projectPath to cookies */
|
|
157
|
+
cookies: Map<string, Array<{
|
|
158
|
+
name: string;
|
|
159
|
+
value: string;
|
|
160
|
+
domain?: string;
|
|
161
|
+
path?: string;
|
|
162
|
+
}>>;
|
|
163
|
+
/** Get cookies for a plugin/project */
|
|
164
|
+
getCookies(pluginName: string, projectPath: string): Array<{
|
|
165
|
+
name: string;
|
|
166
|
+
value: string;
|
|
167
|
+
domain?: string;
|
|
168
|
+
path?: string;
|
|
169
|
+
}>;
|
|
170
|
+
/** Set cookies for a plugin/project */
|
|
171
|
+
setCookies(pluginName: string, projectPath: string, cookies: Array<{
|
|
172
|
+
name: string;
|
|
173
|
+
value: string;
|
|
174
|
+
domain?: string;
|
|
175
|
+
path?: string;
|
|
176
|
+
}>): void;
|
|
177
|
+
/** Clear all cookies */
|
|
178
|
+
clear(): void;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Create a new cookie storage instance
|
|
182
|
+
*/
|
|
183
|
+
declare function createMockCookieStorage(): MockCookieStorage;
|
|
184
|
+
/**
|
|
185
|
+
* Tracks browser open/close calls for testing
|
|
186
|
+
*/
|
|
187
|
+
interface MockBrowserTracker {
|
|
188
|
+
/** URLs that were opened */
|
|
189
|
+
openedUrls: string[];
|
|
190
|
+
/** Number of times closeBrowser was called */
|
|
191
|
+
closeCount: number;
|
|
192
|
+
/** Whether browser is currently open */
|
|
193
|
+
isOpen: boolean;
|
|
194
|
+
/** Reset tracking state */
|
|
195
|
+
reset(): void;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Create a new browser tracker instance
|
|
199
|
+
*/
|
|
200
|
+
declare function createMockBrowserTracker(): MockBrowserTracker;
|
|
201
|
+
/**
|
|
202
|
+
* Context returned by setupMockTauri with all mock utilities
|
|
203
|
+
*/
|
|
204
|
+
interface MockTauriContext {
|
|
205
|
+
/** In-memory filesystem */
|
|
206
|
+
filesystem: MockFilesystem;
|
|
207
|
+
/** Download tracking for concurrency tests */
|
|
208
|
+
downloadTracker: DownloadTracker;
|
|
209
|
+
/** URL response configuration */
|
|
210
|
+
urlConfig: MockUrlConfig;
|
|
211
|
+
/** Binary execution configuration */
|
|
212
|
+
binaryConfig: MockBinaryConfig;
|
|
213
|
+
/** Cookie storage */
|
|
214
|
+
cookieStorage: MockCookieStorage;
|
|
215
|
+
/** Browser open/close tracking */
|
|
216
|
+
browserTracker: MockBrowserTracker;
|
|
217
|
+
/** Cleanup function - must be called after tests */
|
|
218
|
+
cleanup: () => void;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Set up mock Tauri IPC for testing
|
|
222
|
+
*
|
|
223
|
+
* This sets up `window.__TAURI__.core.invoke` to intercept all IPC calls
|
|
224
|
+
* and route them to in-memory implementations.
|
|
225
|
+
*
|
|
226
|
+
* @returns Context with mock utilities and cleanup function
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```typescript
|
|
230
|
+
* const ctx = setupMockTauri();
|
|
231
|
+
*
|
|
232
|
+
* // Set up test data
|
|
233
|
+
* ctx.filesystem.setFile("/project/article.md", "# Test");
|
|
234
|
+
* ctx.urlConfig.setResponse("https://example.com/image.png", {
|
|
235
|
+
* status: 200,
|
|
236
|
+
* ok: true,
|
|
237
|
+
* contentType: "image/png",
|
|
238
|
+
* bytesWritten: 1024,
|
|
239
|
+
* });
|
|
240
|
+
*
|
|
241
|
+
* // Run your plugin code...
|
|
242
|
+
*
|
|
243
|
+
* // Verify results
|
|
244
|
+
* expect(ctx.downloadTracker.completedDownloads).toHaveLength(1);
|
|
245
|
+
*
|
|
246
|
+
* // Cleanup
|
|
247
|
+
* ctx.cleanup();
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
declare function setupMockTauri(): MockTauriContext;
|
|
251
|
+
//#endregion
|
|
252
|
+
export { type DownloadTracker, type MockBinaryConfig, type MockBinaryResult, type MockBrowserTracker, type MockCookieStorage, type MockFile, type MockFilesystem, type MockTauriContext, type MockUrlConfig, type MockUrlResponse, createDownloadTracker, createMockBinaryConfig, createMockBrowserTracker, createMockCookieStorage, createMockFilesystem, createMockUrlConfig, setupMockTauri };
|
|
253
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/testing/mock-tauri.ts"],"sourcesContent":[],"mappings":";;AA0CA;AASA;;;;;AAkBA;AA0CA;AAoBA;AAiDA;AAoBA;;;;;;;;;AAgBA;AAiDA;AAUA;;;;;;;AAgBA;AAyCA;;AAEW,UApSM,QAAA,CAoSN;EAKN,OAAA,EAAA,MAAA;EAKQ,SAAA,EA5SA,IA4SA;EAAK,UAAA,EA3SJ,IA2SI;AASlB;AAiCA;AAcA;AA8DA;AAEc,UA7ZG,cAAA,CA6ZH;EAEK;EAEN,KAAA,EA/ZJ,GA+ZI,CAAA,MAAA,EA/ZQ,QA+ZR,CAAA;EAEG;EAEC,OAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAjaQ,QAiaR,GAAA,SAAA;EAEC;EAAkB,OAAA,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAmCpB;;;;;;;;;;iBAxbA,oBAAA,CAAA,GAAwB;;;;UA0CvB,eAAA;;;;;;;;mBAQE;;;;;;;;;;;;;;iBAYH,qBAAA,CAAA,GAAyB;;;;UAiDxB,eAAA;;;;;;;;;;;;;;;;;;;UAoBA,aAAA;;aAEJ,YAAY,kBAAkB;;mBAExB;;qCAEkB,kBAAkB;;4BAE3B;;;;;;;iBAQZ,mBAAA,CAAA,GAAuB;;;;UAiDtB,gBAAA;;;;;;;;;UAUA,gBAAA;;WAEN,YAAY;;iBAEN;;iCAEgB;;iDAEgB;;;;;;;iBAQjC,sBAAA,CAAA,GAA0B;;;;UAyCzB,iBAAA;;WAEN,YAAY;;;;;;;uDAKlB;;;;;;;+DAKQ;;;;;;;;;;;;iBASG,uBAAA,CAAA,GAA2B;;;;UAiC1B,kBAAA;;;;;;;;;;;;;iBAcD,wBAAA,CAAA,GAA4B;;;;UA8D3B,gBAAA;;cAEH;;mBAEK;;aAEN;;gBAEG;;iBAEC;;kBAEC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmCF,cAAA,CAAA,GAAkB"}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
//#region src/testing/mock-tauri.ts
|
|
2
|
+
/**
|
|
3
|
+
* Create a new mock filesystem instance
|
|
4
|
+
*/
|
|
5
|
+
function createMockFilesystem() {
|
|
6
|
+
const files = /* @__PURE__ */ new Map();
|
|
7
|
+
return {
|
|
8
|
+
files,
|
|
9
|
+
getFile(path) {
|
|
10
|
+
return files.get(path);
|
|
11
|
+
},
|
|
12
|
+
setFile(path, content) {
|
|
13
|
+
const now = /* @__PURE__ */ new Date();
|
|
14
|
+
const existing = files.get(path);
|
|
15
|
+
files.set(path, {
|
|
16
|
+
content,
|
|
17
|
+
createdAt: existing?.createdAt ?? now,
|
|
18
|
+
modifiedAt: now
|
|
19
|
+
});
|
|
20
|
+
},
|
|
21
|
+
deleteFile(path) {
|
|
22
|
+
return files.delete(path);
|
|
23
|
+
},
|
|
24
|
+
listFiles(pattern) {
|
|
25
|
+
const allPaths = Array.from(files.keys());
|
|
26
|
+
if (!pattern) return allPaths;
|
|
27
|
+
const regex = /* @__PURE__ */ new RegExp("^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$");
|
|
28
|
+
return allPaths.filter((p) => regex.test(p));
|
|
29
|
+
},
|
|
30
|
+
clear() {
|
|
31
|
+
files.clear();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Create a new download tracker instance
|
|
37
|
+
*/
|
|
38
|
+
function createDownloadTracker() {
|
|
39
|
+
let activeDownloads = 0;
|
|
40
|
+
let maxConcurrent = 0;
|
|
41
|
+
const completedDownloads = [];
|
|
42
|
+
const failedDownloads = [];
|
|
43
|
+
return {
|
|
44
|
+
get activeDownloads() {
|
|
45
|
+
return activeDownloads;
|
|
46
|
+
},
|
|
47
|
+
get maxConcurrent() {
|
|
48
|
+
return maxConcurrent;
|
|
49
|
+
},
|
|
50
|
+
get completedDownloads() {
|
|
51
|
+
return completedDownloads;
|
|
52
|
+
},
|
|
53
|
+
get failedDownloads() {
|
|
54
|
+
return failedDownloads;
|
|
55
|
+
},
|
|
56
|
+
startDownload(url) {
|
|
57
|
+
activeDownloads++;
|
|
58
|
+
if (activeDownloads > maxConcurrent) maxConcurrent = activeDownloads;
|
|
59
|
+
},
|
|
60
|
+
endDownload(url, success, error) {
|
|
61
|
+
activeDownloads--;
|
|
62
|
+
if (success) completedDownloads.push(url);
|
|
63
|
+
else failedDownloads.push({
|
|
64
|
+
url,
|
|
65
|
+
error: error || "Unknown error"
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
reset() {
|
|
69
|
+
activeDownloads = 0;
|
|
70
|
+
maxConcurrent = 0;
|
|
71
|
+
completedDownloads.length = 0;
|
|
72
|
+
failedDownloads.length = 0;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create a new URL config instance
|
|
78
|
+
*/
|
|
79
|
+
function createMockUrlConfig() {
|
|
80
|
+
const responses = /* @__PURE__ */ new Map();
|
|
81
|
+
const callCounts = /* @__PURE__ */ new Map();
|
|
82
|
+
const defaultResponse = {
|
|
83
|
+
status: 200,
|
|
84
|
+
ok: true,
|
|
85
|
+
contentType: "image/png",
|
|
86
|
+
bodyBase64: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
|
|
87
|
+
bytesWritten: 68,
|
|
88
|
+
actualPath: "assets/image.png"
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
responses,
|
|
92
|
+
defaultResponse,
|
|
93
|
+
setResponse(url, response) {
|
|
94
|
+
responses.set(url, response);
|
|
95
|
+
callCounts.set(url, 0);
|
|
96
|
+
},
|
|
97
|
+
getResponse(url) {
|
|
98
|
+
const config = responses.get(url);
|
|
99
|
+
if (!config) return defaultResponse;
|
|
100
|
+
if (Array.isArray(config)) {
|
|
101
|
+
const count = callCounts.get(url) || 0;
|
|
102
|
+
callCounts.set(url, count + 1);
|
|
103
|
+
return config[Math.min(count, config.length - 1)];
|
|
104
|
+
}
|
|
105
|
+
return config;
|
|
106
|
+
},
|
|
107
|
+
reset() {
|
|
108
|
+
responses.clear();
|
|
109
|
+
callCounts.clear();
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Create a new binary config instance
|
|
115
|
+
*/
|
|
116
|
+
function createMockBinaryConfig() {
|
|
117
|
+
const results = /* @__PURE__ */ new Map();
|
|
118
|
+
const defaultResult = {
|
|
119
|
+
success: true,
|
|
120
|
+
exitCode: 0,
|
|
121
|
+
stdout: "",
|
|
122
|
+
stderr: ""
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
results,
|
|
126
|
+
defaultResult,
|
|
127
|
+
setResult(key, result) {
|
|
128
|
+
results.set(key, result);
|
|
129
|
+
},
|
|
130
|
+
getResult(binaryPath, args) {
|
|
131
|
+
const exactKey = `${binaryPath} ${args.join(" ")}`.trim();
|
|
132
|
+
if (results.has(exactKey)) return results.get(exactKey);
|
|
133
|
+
if (results.has(binaryPath)) return results.get(binaryPath);
|
|
134
|
+
return defaultResult;
|
|
135
|
+
},
|
|
136
|
+
reset() {
|
|
137
|
+
results.clear();
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Create a new cookie storage instance
|
|
143
|
+
*/
|
|
144
|
+
function createMockCookieStorage() {
|
|
145
|
+
const cookies = /* @__PURE__ */ new Map();
|
|
146
|
+
return {
|
|
147
|
+
cookies,
|
|
148
|
+
getCookies(pluginName, projectPath) {
|
|
149
|
+
const key = `${pluginName}:${projectPath}`;
|
|
150
|
+
return cookies.get(key) || [];
|
|
151
|
+
},
|
|
152
|
+
setCookies(pluginName, projectPath, newCookies) {
|
|
153
|
+
const key = `${pluginName}:${projectPath}`;
|
|
154
|
+
cookies.set(key, newCookies);
|
|
155
|
+
},
|
|
156
|
+
clear() {
|
|
157
|
+
cookies.clear();
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Create a new browser tracker instance
|
|
163
|
+
*/
|
|
164
|
+
function createMockBrowserTracker() {
|
|
165
|
+
const openedUrls = [];
|
|
166
|
+
let closeCount = 0;
|
|
167
|
+
let isOpen = false;
|
|
168
|
+
return {
|
|
169
|
+
get openedUrls() {
|
|
170
|
+
return openedUrls;
|
|
171
|
+
},
|
|
172
|
+
get closeCount() {
|
|
173
|
+
return closeCount;
|
|
174
|
+
},
|
|
175
|
+
get isOpen() {
|
|
176
|
+
return isOpen;
|
|
177
|
+
},
|
|
178
|
+
reset() {
|
|
179
|
+
openedUrls.length = 0;
|
|
180
|
+
closeCount = 0;
|
|
181
|
+
isOpen = false;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Extract filename from URL (mimics Rust backend behavior)
|
|
187
|
+
*/
|
|
188
|
+
function extractFilenameFromUrl(url) {
|
|
189
|
+
try {
|
|
190
|
+
const segments = new URL(url).pathname.split("/").filter((s) => s.length > 0);
|
|
191
|
+
for (const segment of segments) if (/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(segment)) return `${segment}.png`;
|
|
192
|
+
const lastSegment = segments[segments.length - 1] || "image";
|
|
193
|
+
return lastSegment.includes(".") ? lastSegment : `${lastSegment}.png`;
|
|
194
|
+
} catch {
|
|
195
|
+
return "image.png";
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Set up mock Tauri IPC for testing
|
|
200
|
+
*
|
|
201
|
+
* This sets up `window.__TAURI__.core.invoke` to intercept all IPC calls
|
|
202
|
+
* and route them to in-memory implementations.
|
|
203
|
+
*
|
|
204
|
+
* @returns Context with mock utilities and cleanup function
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```typescript
|
|
208
|
+
* const ctx = setupMockTauri();
|
|
209
|
+
*
|
|
210
|
+
* // Set up test data
|
|
211
|
+
* ctx.filesystem.setFile("/project/article.md", "# Test");
|
|
212
|
+
* ctx.urlConfig.setResponse("https://example.com/image.png", {
|
|
213
|
+
* status: 200,
|
|
214
|
+
* ok: true,
|
|
215
|
+
* contentType: "image/png",
|
|
216
|
+
* bytesWritten: 1024,
|
|
217
|
+
* });
|
|
218
|
+
*
|
|
219
|
+
* // Run your plugin code...
|
|
220
|
+
*
|
|
221
|
+
* // Verify results
|
|
222
|
+
* expect(ctx.downloadTracker.completedDownloads).toHaveLength(1);
|
|
223
|
+
*
|
|
224
|
+
* // Cleanup
|
|
225
|
+
* ctx.cleanup();
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
function setupMockTauri() {
|
|
229
|
+
const filesystem = createMockFilesystem();
|
|
230
|
+
const downloadTracker = createDownloadTracker();
|
|
231
|
+
const urlConfig = createMockUrlConfig();
|
|
232
|
+
const binaryConfig = createMockBinaryConfig();
|
|
233
|
+
const cookieStorage = createMockCookieStorage();
|
|
234
|
+
const browserTracker = createMockBrowserTracker();
|
|
235
|
+
const invoke = async (cmd, args) => {
|
|
236
|
+
const payload = args;
|
|
237
|
+
switch (cmd) {
|
|
238
|
+
case "read_project_file": {
|
|
239
|
+
const fullPath = `${payload?.projectPath}/${payload?.relativePath}`;
|
|
240
|
+
const file = filesystem.getFile(fullPath);
|
|
241
|
+
if (file) return file.content;
|
|
242
|
+
throw new Error(`File not found: ${fullPath}`);
|
|
243
|
+
}
|
|
244
|
+
case "write_project_file": {
|
|
245
|
+
const projectPath = payload?.projectPath;
|
|
246
|
+
const relativePath = payload?.relativePath;
|
|
247
|
+
const content = payload?.data;
|
|
248
|
+
const fullPath = `${projectPath}/${relativePath}`;
|
|
249
|
+
filesystem.setFile(fullPath, content);
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
case "list_project_files": {
|
|
253
|
+
const projectPath = payload?.projectPath;
|
|
254
|
+
return filesystem.listFiles().filter((p) => p.startsWith(projectPath + "/")).map((p) => p.substring(projectPath.length + 1));
|
|
255
|
+
}
|
|
256
|
+
case "fetch_url": {
|
|
257
|
+
const url = payload?.url;
|
|
258
|
+
const response = urlConfig.getResponse(url);
|
|
259
|
+
if (response.delay) return new Promise((resolve) => setTimeout(() => resolve({
|
|
260
|
+
status: response.status,
|
|
261
|
+
ok: response.ok,
|
|
262
|
+
body_base64: response.bodyBase64 || "",
|
|
263
|
+
content_type: response.contentType || null
|
|
264
|
+
}), response.delay));
|
|
265
|
+
return {
|
|
266
|
+
status: response.status,
|
|
267
|
+
ok: response.ok,
|
|
268
|
+
body_base64: response.bodyBase64 || "",
|
|
269
|
+
content_type: response.contentType || null
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
case "download_asset": {
|
|
273
|
+
const url = payload?.url;
|
|
274
|
+
const targetDir = payload?.targetDir;
|
|
275
|
+
const response = urlConfig.getResponse(url);
|
|
276
|
+
downloadTracker.startDownload(url);
|
|
277
|
+
if (response.status === 0) {
|
|
278
|
+
downloadTracker.endDownload(url, false, "Network error");
|
|
279
|
+
throw new Error("Network timeout");
|
|
280
|
+
}
|
|
281
|
+
const actualPath = response.actualPath || `${targetDir}/${extractFilenameFromUrl(url)}`;
|
|
282
|
+
const result = {
|
|
283
|
+
status: response.status,
|
|
284
|
+
ok: response.ok,
|
|
285
|
+
content_type: response.contentType || null,
|
|
286
|
+
bytes_written: response.bytesWritten || 0,
|
|
287
|
+
actual_path: actualPath
|
|
288
|
+
};
|
|
289
|
+
if (response.delay) return new Promise((resolve) => setTimeout(() => {
|
|
290
|
+
downloadTracker.endDownload(url, response.ok);
|
|
291
|
+
resolve(result);
|
|
292
|
+
}, response.delay));
|
|
293
|
+
downloadTracker.endDownload(url, response.ok);
|
|
294
|
+
return result;
|
|
295
|
+
}
|
|
296
|
+
case "get_plugin_cookie": {
|
|
297
|
+
const pluginName = payload?.pluginName;
|
|
298
|
+
const projectPath = payload?.projectPath;
|
|
299
|
+
return cookieStorage.getCookies(pluginName, projectPath);
|
|
300
|
+
}
|
|
301
|
+
case "set_plugin_cookie": {
|
|
302
|
+
const pluginName = payload?.pluginName;
|
|
303
|
+
const projectPath = payload?.projectPath;
|
|
304
|
+
const cookies = payload?.cookies;
|
|
305
|
+
cookieStorage.setCookies(pluginName, projectPath, cookies);
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
case "open_plugin_browser": {
|
|
309
|
+
const url = payload?.url;
|
|
310
|
+
browserTracker.openedUrls.push(url);
|
|
311
|
+
browserTracker.isOpen = true;
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
case "close_plugin_browser":
|
|
315
|
+
browserTracker.closeCount++;
|
|
316
|
+
browserTracker.isOpen = false;
|
|
317
|
+
return null;
|
|
318
|
+
case "execute_binary": {
|
|
319
|
+
const binaryPath = payload?.binaryPath;
|
|
320
|
+
const binaryArgs = payload?.args;
|
|
321
|
+
const result = binaryConfig.getResult(binaryPath, binaryArgs);
|
|
322
|
+
return {
|
|
323
|
+
success: result.success,
|
|
324
|
+
exit_code: result.exitCode,
|
|
325
|
+
stdout: result.stdout,
|
|
326
|
+
stderr: result.stderr
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
case "plugin_message": return null;
|
|
330
|
+
default:
|
|
331
|
+
console.warn(`Unhandled IPC command: ${cmd}`);
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
if (typeof globalThis.window === "undefined") globalThis.window = {};
|
|
336
|
+
const win = globalThis.window;
|
|
337
|
+
win.__TAURI__ = { core: { invoke } };
|
|
338
|
+
return {
|
|
339
|
+
filesystem,
|
|
340
|
+
downloadTracker,
|
|
341
|
+
urlConfig,
|
|
342
|
+
binaryConfig,
|
|
343
|
+
cookieStorage,
|
|
344
|
+
browserTracker,
|
|
345
|
+
cleanup: () => {
|
|
346
|
+
delete win.__TAURI__;
|
|
347
|
+
filesystem.clear();
|
|
348
|
+
downloadTracker.reset();
|
|
349
|
+
urlConfig.reset();
|
|
350
|
+
binaryConfig.reset();
|
|
351
|
+
cookieStorage.clear();
|
|
352
|
+
browserTracker.reset();
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
//#endregion
|
|
358
|
+
export { createDownloadTracker, createMockBinaryConfig, createMockBrowserTracker, createMockCookieStorage, createMockFilesystem, createMockUrlConfig, setupMockTauri };
|
|
359
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["completedDownloads: string[]","failedDownloads: Array<{ url: string; error: string }>","defaultResponse: MockUrlResponse","defaultResult: MockBinaryResult","openedUrls: string[]"],"sources":["../../src/testing/mock-tauri.ts"],"sourcesContent":["/**\n * Tauri IPC mocking utilities for testing Moss plugins\n *\n * Provides in-memory implementations of Tauri IPC commands that plugins use\n * through moss-api. This enables integration testing without a running Tauri app.\n *\n * @example\n * ```typescript\n * import { setupMockTauri } from \"@symbiosis-lab/moss-api/testing\";\n *\n * describe(\"my plugin\", () => {\n * let ctx: MockTauriContext;\n *\n * beforeEach(() => {\n * ctx = setupMockTauri();\n * });\n *\n * afterEach(() => {\n * ctx.cleanup();\n * });\n *\n * it(\"reads files\", async () => {\n * ctx.filesystem.setFile(\"/project/test.md\", \"# Hello\");\n * const content = await readFile(\"/project\", \"test.md\");\n * expect(content).toBe(\"# Hello\");\n * });\n * });\n * ```\n */\n\n// Define minimal types for invoke args\ninterface InvokeArgs {\n [key: string]: unknown;\n}\n\n// ============================================================================\n// Mock Filesystem\n// ============================================================================\n\n/**\n * A file stored in the mock filesystem\n */\nexport interface MockFile {\n content: string;\n createdAt: Date;\n modifiedAt: Date;\n}\n\n/**\n * In-memory filesystem for testing file operations\n */\nexport interface MockFilesystem {\n /** Internal file storage */\n files: Map<string, MockFile>;\n /** Get a file by full path */\n getFile(path: string): MockFile | undefined;\n /** Set a file's content (creates or updates) */\n setFile(path: string, content: string): void;\n /** Delete a file */\n deleteFile(path: string): boolean;\n /** List files matching an optional pattern */\n listFiles(pattern?: string): string[];\n /** Clear all files */\n clear(): void;\n}\n\n/**\n * Create a new mock filesystem instance\n */\nexport function createMockFilesystem(): MockFilesystem {\n const files = new Map<string, MockFile>();\n\n return {\n files,\n getFile(path: string) {\n return files.get(path);\n },\n setFile(path: string, content: string) {\n const now = new Date();\n const existing = files.get(path);\n files.set(path, {\n content,\n createdAt: existing?.createdAt ?? now,\n modifiedAt: now,\n });\n },\n deleteFile(path: string) {\n return files.delete(path);\n },\n listFiles(pattern?: string) {\n const allPaths = Array.from(files.keys());\n if (!pattern) return allPaths;\n // Simple glob matching\n const regex = new RegExp(\n \"^\" + pattern.replace(/\\*/g, \".*\").replace(/\\?/g, \".\") + \"$\"\n );\n return allPaths.filter((p) => regex.test(p));\n },\n clear() {\n files.clear();\n },\n };\n}\n\n// ============================================================================\n// Download Tracker\n// ============================================================================\n\n/**\n * Tracks download activity for testing concurrency and completion\n */\nexport interface DownloadTracker {\n /** Number of currently active downloads */\n activeDownloads: number;\n /** Maximum concurrent downloads observed */\n maxConcurrent: number;\n /** URLs of completed downloads */\n completedDownloads: string[];\n /** Failed downloads with error messages */\n failedDownloads: Array<{ url: string; error: string }>;\n /** Mark a download as started */\n startDownload(url: string): void;\n /** Mark a download as ended */\n endDownload(url: string, success: boolean, error?: string): void;\n /** Reset all tracking state */\n reset(): void;\n}\n\n/**\n * Create a new download tracker instance\n */\nexport function createDownloadTracker(): DownloadTracker {\n let activeDownloads = 0;\n let maxConcurrent = 0;\n const completedDownloads: string[] = [];\n const failedDownloads: Array<{ url: string; error: string }> = [];\n\n return {\n get activeDownloads() {\n return activeDownloads;\n },\n get maxConcurrent() {\n return maxConcurrent;\n },\n get completedDownloads() {\n return completedDownloads;\n },\n get failedDownloads() {\n return failedDownloads;\n },\n startDownload(url: string) {\n activeDownloads++;\n if (activeDownloads > maxConcurrent) {\n maxConcurrent = activeDownloads;\n }\n },\n endDownload(url: string, success: boolean, error?: string) {\n activeDownloads--;\n if (success) {\n completedDownloads.push(url);\n } else {\n failedDownloads.push({ url, error: error || \"Unknown error\" });\n }\n },\n reset() {\n activeDownloads = 0;\n maxConcurrent = 0;\n completedDownloads.length = 0;\n failedDownloads.length = 0;\n },\n };\n}\n\n// ============================================================================\n// URL Response Configuration\n// ============================================================================\n\n/**\n * Configuration for a mocked URL response\n */\nexport interface MockUrlResponse {\n /** HTTP status code */\n status: number;\n /** Whether the request was successful (2xx) */\n ok: boolean;\n /** Content-Type header */\n contentType?: string;\n /** Response body as base64 (for fetch_url) */\n bodyBase64?: string;\n /** Number of bytes written (for download_asset) */\n bytesWritten?: number;\n /** Actual file path where asset was saved */\n actualPath?: string;\n /** Artificial delay in milliseconds */\n delay?: number;\n}\n\n/**\n * URL response configuration for mocking HTTP requests\n */\nexport interface MockUrlConfig {\n /** Map of URL to response(s) */\n responses: Map<string, MockUrlResponse | MockUrlResponse[]>;\n /** Default response for unregistered URLs */\n defaultResponse: MockUrlResponse;\n /** Set response for a URL (can be single or array for retry testing) */\n setResponse(url: string, response: MockUrlResponse | MockUrlResponse[]): void;\n /** Get response for a URL (handles retry sequences) */\n getResponse(url: string): MockUrlResponse;\n /** Reset all URL configurations */\n reset(): void;\n}\n\n/**\n * Create a new URL config instance\n */\nexport function createMockUrlConfig(): MockUrlConfig {\n const responses = new Map<string, MockUrlResponse | MockUrlResponse[]>();\n const callCounts = new Map<string, number>();\n\n // Default: 1x1 red PNG\n const defaultResponse: MockUrlResponse = {\n status: 200,\n ok: true,\n contentType: \"image/png\",\n bodyBase64:\n \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==\",\n bytesWritten: 68,\n actualPath: \"assets/image.png\",\n };\n\n return {\n responses,\n defaultResponse,\n setResponse(url: string, response: MockUrlResponse | MockUrlResponse[]) {\n responses.set(url, response);\n callCounts.set(url, 0);\n },\n getResponse(url: string): MockUrlResponse {\n const config = responses.get(url);\n if (!config) return defaultResponse;\n\n if (Array.isArray(config)) {\n const count = callCounts.get(url) || 0;\n callCounts.set(url, count + 1);\n // Return the response at the current index, or the last one if exceeded\n return config[Math.min(count, config.length - 1)];\n }\n\n return config;\n },\n reset() {\n responses.clear();\n callCounts.clear();\n },\n };\n}\n\n// ============================================================================\n// Binary Execution Tracker\n// ============================================================================\n\n/**\n * Result for a mocked binary execution\n */\nexport interface MockBinaryResult {\n success: boolean;\n exitCode: number;\n stdout: string;\n stderr: string;\n}\n\n/**\n * Configuration for mocking binary execution\n */\nexport interface MockBinaryConfig {\n /** Map of binary commands to results */\n results: Map<string, MockBinaryResult>;\n /** Default result for unregistered binaries */\n defaultResult: MockBinaryResult;\n /** Set result for a binary command (key format: \"binaryPath args...\") */\n setResult(key: string, result: MockBinaryResult): void;\n /** Get result for a binary command */\n getResult(binaryPath: string, args: string[]): MockBinaryResult;\n /** Reset all configurations */\n reset(): void;\n}\n\n/**\n * Create a new binary config instance\n */\nexport function createMockBinaryConfig(): MockBinaryConfig {\n const results = new Map<string, MockBinaryResult>();\n\n const defaultResult: MockBinaryResult = {\n success: true,\n exitCode: 0,\n stdout: \"\",\n stderr: \"\",\n };\n\n return {\n results,\n defaultResult,\n setResult(key: string, result: MockBinaryResult) {\n results.set(key, result);\n },\n getResult(binaryPath: string, args: string[]): MockBinaryResult {\n // Try exact match first\n const exactKey = `${binaryPath} ${args.join(\" \")}`.trim();\n if (results.has(exactKey)) {\n return results.get(exactKey)!;\n }\n // Try binary name only\n if (results.has(binaryPath)) {\n return results.get(binaryPath)!;\n }\n return defaultResult;\n },\n reset() {\n results.clear();\n },\n };\n}\n\n// ============================================================================\n// Cookie Storage\n// ============================================================================\n\n/**\n * Mock cookie storage for plugin authentication testing\n */\nexport interface MockCookieStorage {\n /** Map of pluginName:projectPath to cookies */\n cookies: Map<string, Array<{ name: string; value: string; domain?: string; path?: string }>>;\n /** Get cookies for a plugin/project */\n getCookies(\n pluginName: string,\n projectPath: string\n ): Array<{ name: string; value: string; domain?: string; path?: string }>;\n /** Set cookies for a plugin/project */\n setCookies(\n pluginName: string,\n projectPath: string,\n cookies: Array<{ name: string; value: string; domain?: string; path?: string }>\n ): void;\n /** Clear all cookies */\n clear(): void;\n}\n\n/**\n * Create a new cookie storage instance\n */\nexport function createMockCookieStorage(): MockCookieStorage {\n const cookies = new Map<\n string,\n Array<{ name: string; value: string; domain?: string; path?: string }>\n >();\n\n return {\n cookies,\n getCookies(pluginName: string, projectPath: string) {\n const key = `${pluginName}:${projectPath}`;\n return cookies.get(key) || [];\n },\n setCookies(\n pluginName: string,\n projectPath: string,\n newCookies: Array<{ name: string; value: string; domain?: string; path?: string }>\n ) {\n const key = `${pluginName}:${projectPath}`;\n cookies.set(key, newCookies);\n },\n clear() {\n cookies.clear();\n },\n };\n}\n\n// ============================================================================\n// Browser Tracker\n// ============================================================================\n\n/**\n * Tracks browser open/close calls for testing\n */\nexport interface MockBrowserTracker {\n /** URLs that were opened */\n openedUrls: string[];\n /** Number of times closeBrowser was called */\n closeCount: number;\n /** Whether browser is currently open */\n isOpen: boolean;\n /** Reset tracking state */\n reset(): void;\n}\n\n/**\n * Create a new browser tracker instance\n */\nexport function createMockBrowserTracker(): MockBrowserTracker {\n const openedUrls: string[] = [];\n let closeCount = 0;\n let isOpen = false;\n\n return {\n get openedUrls() {\n return openedUrls;\n },\n get closeCount() {\n return closeCount;\n },\n get isOpen() {\n return isOpen;\n },\n reset() {\n openedUrls.length = 0;\n closeCount = 0;\n isOpen = false;\n },\n };\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Extract filename from URL (mimics Rust backend behavior)\n */\nfunction extractFilenameFromUrl(url: string): string {\n try {\n const urlObj = new URL(url);\n const pathname = urlObj.pathname;\n const segments = pathname.split(\"/\").filter((s) => s.length > 0);\n\n // Try to find UUID in path\n for (const segment of segments) {\n if (\n /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(\n segment\n )\n ) {\n return `${segment}.png`; // Default to PNG for mock\n }\n }\n\n // Fallback to last segment or hash\n const lastSegment = segments[segments.length - 1] || \"image\";\n return lastSegment.includes(\".\") ? lastSegment : `${lastSegment}.png`;\n } catch {\n return \"image.png\";\n }\n}\n\n// ============================================================================\n// Main Setup Function\n// ============================================================================\n\n/**\n * Context returned by setupMockTauri with all mock utilities\n */\nexport interface MockTauriContext {\n /** In-memory filesystem */\n filesystem: MockFilesystem;\n /** Download tracking for concurrency tests */\n downloadTracker: DownloadTracker;\n /** URL response configuration */\n urlConfig: MockUrlConfig;\n /** Binary execution configuration */\n binaryConfig: MockBinaryConfig;\n /** Cookie storage */\n cookieStorage: MockCookieStorage;\n /** Browser open/close tracking */\n browserTracker: MockBrowserTracker;\n /** Cleanup function - must be called after tests */\n cleanup: () => void;\n}\n\n/**\n * Set up mock Tauri IPC for testing\n *\n * This sets up `window.__TAURI__.core.invoke` to intercept all IPC calls\n * and route them to in-memory implementations.\n *\n * @returns Context with mock utilities and cleanup function\n *\n * @example\n * ```typescript\n * const ctx = setupMockTauri();\n *\n * // Set up test data\n * ctx.filesystem.setFile(\"/project/article.md\", \"# Test\");\n * ctx.urlConfig.setResponse(\"https://example.com/image.png\", {\n * status: 200,\n * ok: true,\n * contentType: \"image/png\",\n * bytesWritten: 1024,\n * });\n *\n * // Run your plugin code...\n *\n * // Verify results\n * expect(ctx.downloadTracker.completedDownloads).toHaveLength(1);\n *\n * // Cleanup\n * ctx.cleanup();\n * ```\n */\nexport function setupMockTauri(): MockTauriContext {\n const filesystem = createMockFilesystem();\n const downloadTracker = createDownloadTracker();\n const urlConfig = createMockUrlConfig();\n const binaryConfig = createMockBinaryConfig();\n const cookieStorage = createMockCookieStorage();\n const browserTracker = createMockBrowserTracker();\n\n // Create invoke handler\n const invoke = async (cmd: string, args?: InvokeArgs): Promise<unknown> => {\n const payload = args as Record<string, unknown> | undefined;\n\n switch (cmd) {\n // ======================================================================\n // Filesystem Operations\n // ======================================================================\n case \"read_project_file\": {\n const projectPath = payload?.projectPath as string;\n const relativePath = payload?.relativePath as string;\n const fullPath = `${projectPath}/${relativePath}`;\n const file = filesystem.getFile(fullPath);\n if (file) {\n return file.content;\n }\n throw new Error(`File not found: ${fullPath}`);\n }\n\n case \"write_project_file\": {\n const projectPath = payload?.projectPath as string;\n const relativePath = payload?.relativePath as string;\n const content = payload?.data as string; // Note: moss-api uses 'data' not 'content'\n const fullPath = `${projectPath}/${relativePath}`;\n filesystem.setFile(fullPath, content);\n return null;\n }\n\n case \"list_project_files\": {\n const projectPath = payload?.projectPath as string;\n // Return all file paths relative to the project path\n const allPaths = filesystem.listFiles();\n return allPaths\n .filter((p) => p.startsWith(projectPath + \"/\"))\n .map((p) => p.substring(projectPath.length + 1));\n }\n\n // ======================================================================\n // HTTP Operations\n // ======================================================================\n case \"fetch_url\": {\n const url = payload?.url as string;\n const response = urlConfig.getResponse(url);\n\n if (response.delay) {\n return new Promise((resolve) =>\n setTimeout(\n () =>\n resolve({\n status: response.status,\n ok: response.ok,\n body_base64: response.bodyBase64 || \"\",\n content_type: response.contentType || null,\n }),\n response.delay\n )\n );\n }\n\n return {\n status: response.status,\n ok: response.ok,\n body_base64: response.bodyBase64 || \"\",\n content_type: response.contentType || null,\n };\n }\n\n case \"download_asset\": {\n const url = payload?.url as string;\n const targetDir = payload?.targetDir as string;\n const response = urlConfig.getResponse(url);\n\n downloadTracker.startDownload(url);\n\n // status 0 simulates a network error/timeout - throw an error\n if (response.status === 0) {\n downloadTracker.endDownload(url, false, \"Network error\");\n throw new Error(\"Network timeout\");\n }\n\n // Generate actual_path based on URL or use configured value\n const actualPath =\n response.actualPath || `${targetDir}/${extractFilenameFromUrl(url)}`;\n\n const result = {\n status: response.status,\n ok: response.ok,\n content_type: response.contentType || null,\n bytes_written: response.bytesWritten || 0,\n actual_path: actualPath,\n };\n\n if (response.delay) {\n return new Promise((resolve) =>\n setTimeout(() => {\n downloadTracker.endDownload(url, response.ok);\n resolve(result);\n }, response.delay)\n );\n }\n\n downloadTracker.endDownload(url, response.ok);\n return result;\n }\n\n // ======================================================================\n // Cookie Operations\n // ======================================================================\n case \"get_plugin_cookie\": {\n const pluginName = payload?.pluginName as string;\n const projectPath = payload?.projectPath as string;\n return cookieStorage.getCookies(pluginName, projectPath);\n }\n\n case \"set_plugin_cookie\": {\n const pluginName = payload?.pluginName as string;\n const projectPath = payload?.projectPath as string;\n const cookies = payload?.cookies as Array<{\n name: string;\n value: string;\n domain?: string;\n path?: string;\n }>;\n cookieStorage.setCookies(pluginName, projectPath, cookies);\n return null;\n }\n\n // ======================================================================\n // Browser Operations\n // ======================================================================\n case \"open_plugin_browser\": {\n const url = payload?.url as string;\n browserTracker.openedUrls.push(url);\n (browserTracker as { isOpen: boolean }).isOpen = true;\n return null;\n }\n\n case \"close_plugin_browser\": {\n (browserTracker as { closeCount: number }).closeCount++;\n (browserTracker as { isOpen: boolean }).isOpen = false;\n return null;\n }\n\n // ======================================================================\n // Binary Execution\n // ======================================================================\n case \"execute_binary\": {\n const binaryPath = payload?.binaryPath as string;\n const binaryArgs = payload?.args as string[];\n const result = binaryConfig.getResult(binaryPath, binaryArgs);\n\n return {\n success: result.success,\n exit_code: result.exitCode,\n stdout: result.stdout,\n stderr: result.stderr,\n };\n }\n\n // ======================================================================\n // Messaging (silent no-op)\n // ======================================================================\n case \"plugin_message\": {\n // Silently accept plugin messages (logs, progress, errors, etc.)\n return null;\n }\n\n default:\n console.warn(`Unhandled IPC command: ${cmd}`);\n return null;\n }\n };\n\n // Set up window.__TAURI__ directly (moss-api checks for this)\n const w = globalThis as unknown as {\n window?: {\n __TAURI__?: { core?: { invoke: typeof invoke } };\n };\n };\n\n // Ensure window exists (for Node.js environments like happy-dom)\n if (typeof w.window === \"undefined\") {\n (globalThis as unknown as { window: object }).window = {};\n }\n\n const win = (globalThis as unknown as { window: { __TAURI__?: { core?: { invoke: typeof invoke } } } }).window;\n win.__TAURI__ = {\n core: { invoke },\n };\n\n return {\n filesystem,\n downloadTracker,\n urlConfig,\n binaryConfig,\n cookieStorage,\n browserTracker,\n cleanup: () => {\n // Clear the mock Tauri interface\n delete win.__TAURI__;\n filesystem.clear();\n downloadTracker.reset();\n urlConfig.reset();\n binaryConfig.reset();\n cookieStorage.clear();\n browserTracker.reset();\n },\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiosis-lab/moss-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Official API for building Moss plugins - types and utilities for plugin development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.mjs",
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
".": {
|
|
11
11
|
"import": "./dist/index.mjs",
|
|
12
12
|
"types": "./dist/index.d.mts"
|
|
13
|
+
},
|
|
14
|
+
"./testing": {
|
|
15
|
+
"import": "./dist/testing/index.mjs",
|
|
16
|
+
"types": "./dist/testing/index.d.mts"
|
|
13
17
|
}
|
|
14
18
|
},
|
|
15
19
|
"files": [
|