@symbiosis-lab/moss-api 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  [![CI](https://github.com/Symbiosis-Lab/moss-api/actions/workflows/ci.yml/badge.svg)](https://github.com/Symbiosis-Lab/moss-api/actions/workflows/ci.yml)
4
4
  [![codecov](https://codecov.io/gh/Symbiosis-Lab/moss-api/branch/main/graph/badge.svg)](https://codecov.io/gh/Symbiosis-Lab/moss-api)
5
- [![npm version](https://badge.fury.io/js/moss-api.svg)](https://www.npmjs.com/package/moss-api)
5
+ [![npm version](https://badge.fury.io/js/%40symbiosis-lab%2Fmoss-api.svg)](https://www.npmjs.com/package/@symbiosis-lab/moss-api)
6
6
 
7
7
  Official API for building Moss plugins. Provides types and utilities for plugin development.
8
8
 
9
9
  ## Installation
10
10
 
11
11
  ```bash
12
- npm install moss-api
12
+ npm install @symbiosis-lab/moss-api
13
13
  ```
14
14
 
15
15
  ## Usage
@@ -23,7 +23,7 @@ import type {
23
23
  HookResult,
24
24
  PluginManifest,
25
25
  PluginCategory,
26
- } from "moss-api";
26
+ } from "@symbiosis-lab/moss-api";
27
27
 
28
28
  // Define your plugin manifest
29
29
  const manifest: PluginManifest = {
@@ -51,7 +51,7 @@ import {
51
51
  log,
52
52
  warn,
53
53
  error,
54
- } from "moss-api";
54
+ } from "@symbiosis-lab/moss-api";
55
55
 
56
56
  // Set plugin context (call once at plugin initialization)
57
57
  setMessageContext("my-plugin", "on_deploy");
@@ -74,7 +74,7 @@ await reportComplete({ url: "https://example.com" });
74
74
  ### Browser Utilities
75
75
 
76
76
  ```typescript
77
- import { openBrowser, closeBrowser } from "moss-api";
77
+ import { openBrowser, closeBrowser } from "@symbiosis-lab/moss-api";
78
78
 
79
79
  // Open authentication page in plugin browser window
80
80
  await openBrowser("https://example.com/auth");
@@ -86,7 +86,7 @@ await closeBrowser();
86
86
  ### Tauri Utilities
87
87
 
88
88
  ```typescript
89
- import { getTauriCore, isTauriAvailable } from "moss-api";
89
+ import { getTauriCore, isTauriAvailable } from "@symbiosis-lab/moss-api";
90
90
 
91
91
  // Check if running in Tauri environment
92
92
  if (isTauriAvailable()) {
package/dist/index.d.mts CHANGED
@@ -86,7 +86,24 @@ interface DeploymentInfo {
86
86
  url: string;
87
87
  deployed_at: string;
88
88
  metadata: Record<string, string>;
89
+ /** DNS target for custom domain configuration */
90
+ dns_target?: DnsTarget;
89
91
  }
92
+ /**
93
+ * DNS target type for custom domain configuration
94
+ * Provided by deploy plugins to configure DNS records
95
+ */
96
+ type DnsTarget = {
97
+ type: "cname";
98
+ hostname: string;
99
+ } | {
100
+ type: "a";
101
+ ips: string[];
102
+ } | {
103
+ type: "github-pages";
104
+ a_records: string[];
105
+ cname_target: string;
106
+ };
90
107
  //#endregion
91
108
  //#region src/types/hooks.d.ts
92
109
  /**
@@ -621,5 +638,163 @@ declare function getPluginCookie(): Promise<Cookie[]>;
621
638
  */
622
639
  declare function setPluginCookie(cookies: Cookie[]): Promise<void>;
623
640
  //#endregion
624
- 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, listPluginFiles, log, openBrowser, pluginFileExists, readFile, readPluginFile, reportComplete, reportError, reportProgress, sendMessage, setMessageContext, setPluginCookie, warn, writeFile, writePluginFile };
641
+ //#region src/utils/window.d.ts
642
+ /**
643
+ * Window utilities for plugins
644
+ * Enables plugins to show custom dialogs and UI elements
645
+ */
646
+ /**
647
+ * Result from a dialog interaction
648
+ */
649
+ interface DialogResult {
650
+ type: "submitted" | "cancelled";
651
+ value?: unknown;
652
+ }
653
+ /**
654
+ * Options for showing a plugin dialog
655
+ */
656
+ interface ShowDialogOptions {
657
+ /** URL to load in the dialog (can be data: URL with embedded HTML) */
658
+ url: string;
659
+ /** Dialog window title */
660
+ title: string;
661
+ /** Dialog width in pixels */
662
+ width?: number;
663
+ /** Dialog height in pixels */
664
+ height?: number;
665
+ /** Maximum time to wait for user response in milliseconds */
666
+ timeoutMs?: number;
667
+ }
668
+ /**
669
+ * Show a plugin dialog and wait for user response
670
+ *
671
+ * The dialog can be an embedded HTML page (via data: URL) that communicates
672
+ * back to the plugin via the submitDialogResult function.
673
+ *
674
+ * @param options - Dialog configuration
675
+ * @returns Dialog result with submitted value or cancellation
676
+ *
677
+ * @example
678
+ * ```typescript
679
+ * const result = await showPluginDialog({
680
+ * url: createMyDialogUrl(),
681
+ * title: "Create Repository",
682
+ * width: 400,
683
+ * height: 300,
684
+ * });
685
+ *
686
+ * if (result.type === "submitted") {
687
+ * console.log("User submitted:", result.value);
688
+ * } else {
689
+ * console.log("User cancelled");
690
+ * }
691
+ * ```
692
+ */
693
+ declare function showPluginDialog(options: ShowDialogOptions): Promise<DialogResult>;
694
+ /**
695
+ * Submit a result from within a plugin dialog
696
+ *
697
+ * This is called from inside the dialog HTML to send data back to the plugin.
698
+ * The dialog will be closed automatically after submission.
699
+ *
700
+ * @param dialogId - The dialog ID (provided in the dialog's query string)
701
+ * @param value - The value to submit
702
+ * @returns Whether the submission was successful
703
+ *
704
+ * @example
705
+ * ```typescript
706
+ * // Inside dialog HTML:
707
+ * const dialogId = new URLSearchParams(location.search).get('dialogId');
708
+ * await submitDialogResult(dialogId, { repoName: 'my-repo' });
709
+ * ```
710
+ */
711
+ declare function submitDialogResult(dialogId: string, value: unknown): Promise<boolean>;
712
+ /**
713
+ * Cancel a plugin dialog
714
+ *
715
+ * This is called from inside the dialog HTML to cancel without submitting.
716
+ * The dialog will be closed automatically.
717
+ *
718
+ * @param dialogId - The dialog ID (provided in the dialog's query string)
719
+ *
720
+ * @example
721
+ * ```typescript
722
+ * // Inside dialog HTML:
723
+ * const dialogId = new URLSearchParams(location.search).get('dialogId');
724
+ * await cancelDialog(dialogId);
725
+ * ```
726
+ */
727
+ declare function cancelDialog(dialogId: string): Promise<boolean>;
728
+ //#endregion
729
+ //#region src/utils/events.d.ts
730
+ /**
731
+ * Event utilities for plugin communication
732
+ *
733
+ * Provides a way for plugins and dialog windows to communicate
734
+ * via Tauri's event system.
735
+ */
736
+ /**
737
+ * Check if Tauri event API is available
738
+ */
739
+ declare function isEventApiAvailable(): boolean;
740
+ /**
741
+ * Emit an event to other parts of the application
742
+ *
743
+ * @param event - Event name (e.g., "repo-created", "dialog-result")
744
+ * @param payload - Data to send with the event
745
+ *
746
+ * @example
747
+ * ```typescript
748
+ * // From dialog:
749
+ * await emitEvent("repo-name-validated", { name: "my-repo", available: true });
750
+ *
751
+ * // From plugin:
752
+ * await emitEvent("deployment-started", { url: "https://github.com/..." });
753
+ * ```
754
+ */
755
+ declare function emitEvent(event: string, payload?: unknown): Promise<void>;
756
+ /**
757
+ * Listen for events from other parts of the application
758
+ *
759
+ * @param event - Event name to listen for
760
+ * @param handler - Function to call when event is received
761
+ * @returns Cleanup function to stop listening
762
+ *
763
+ * @example
764
+ * ```typescript
765
+ * const unlisten = await onEvent<{ name: string; available: boolean }>(
766
+ * "repo-name-validated",
767
+ * (data) => {
768
+ * console.log(`Repo ${data.name} is ${data.available ? "available" : "taken"}`);
769
+ * }
770
+ * );
771
+ *
772
+ * // Later, to stop listening:
773
+ * unlisten();
774
+ * ```
775
+ */
776
+ declare function onEvent<T>(event: string, handler: (payload: T) => void): Promise<() => void>;
777
+ /**
778
+ * Wait for a single event occurrence
779
+ *
780
+ * @param event - Event name to wait for
781
+ * @param timeoutMs - Maximum time to wait (default: 30000ms)
782
+ * @returns Promise that resolves with the event payload
783
+ * @throws Error if timeout is reached
784
+ *
785
+ * @example
786
+ * ```typescript
787
+ * try {
788
+ * const result = await waitForEvent<{ confirmed: boolean }>("user-confirmed", 10000);
789
+ * if (result.confirmed) {
790
+ * // proceed
791
+ * }
792
+ * } catch (e) {
793
+ * console.log("User did not respond in time");
794
+ * }
795
+ * ```
796
+ */
797
+ declare function waitForEvent<T>(event: string, timeoutMs?: number): Promise<T>;
798
+ //#endregion
799
+ export { AfterDeployContext, ArticleInfo, BaseContext, BeforeBuildContext, CompleteMessage, Cookie, DeploymentInfo, DialogResult, DnsTarget, DownloadOptions, DownloadResult, ErrorMessage, ExecuteOptions, ExecuteResult, FetchOptions, FetchResult, HookResult, LogMessage, OnBuildContext, OnDeployContext, PluginCategory, PluginManifest, PluginMessage, ProgressMessage, ProjectInfo, ShowDialogOptions, SourceFiles, TauriCore, cancelDialog, closeBrowser, downloadAsset, emitEvent, error, executeBinary, fetchUrl, fileExists, getMessageContext, getPluginCookie, getTauriCore, isEventApiAvailable, isTauriAvailable, listFiles, listPluginFiles, log, onEvent, openBrowser, pluginFileExists, readFile, readPluginFile, reportComplete, reportError, reportProgress, sendMessage, setMessageContext, setPluginCookie, showPluginDialog, submitDialogResult, waitForEvent, warn, writeFile, writePluginFile };
625
800
  //# sourceMappingURL=index.d.mts.map
@@ -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/plugin-storage.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/plugin-storage.ts","../src/utils/http.ts","../src/utils/binary.ts","../src/utils/cookies.ts","../src/utils/window.ts","../src/utils/events.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -608,5 +608,194 @@ async function setPluginCookie(cookies) {
608
608
  }
609
609
 
610
610
  //#endregion
611
- export { closeBrowser, downloadAsset, error, executeBinary, fetchUrl, fileExists, getMessageContext, getPluginCookie, getTauriCore, isTauriAvailable, listFiles, listPluginFiles, log, openBrowser, pluginFileExists, readFile, readPluginFile, reportComplete, reportError, reportProgress, sendMessage, setMessageContext, setPluginCookie, warn, writeFile, writePluginFile };
611
+ //#region src/utils/window.ts
612
+ /**
613
+ * Window utilities for plugins
614
+ * Enables plugins to show custom dialogs and UI elements
615
+ */
616
+ /**
617
+ * Show a plugin dialog and wait for user response
618
+ *
619
+ * The dialog can be an embedded HTML page (via data: URL) that communicates
620
+ * back to the plugin via the submitDialogResult function.
621
+ *
622
+ * @param options - Dialog configuration
623
+ * @returns Dialog result with submitted value or cancellation
624
+ *
625
+ * @example
626
+ * ```typescript
627
+ * const result = await showPluginDialog({
628
+ * url: createMyDialogUrl(),
629
+ * title: "Create Repository",
630
+ * width: 400,
631
+ * height: 300,
632
+ * });
633
+ *
634
+ * if (result.type === "submitted") {
635
+ * console.log("User submitted:", result.value);
636
+ * } else {
637
+ * console.log("User cancelled");
638
+ * }
639
+ * ```
640
+ */
641
+ async function showPluginDialog(options) {
642
+ return await getTauriCore().invoke("show_plugin_dialog", {
643
+ url: options.url,
644
+ title: options.title,
645
+ width: options.width ?? 500,
646
+ height: options.height ?? 400,
647
+ timeoutMs: options.timeoutMs ?? 3e5
648
+ });
649
+ }
650
+ /**
651
+ * Submit a result from within a plugin dialog
652
+ *
653
+ * This is called from inside the dialog HTML to send data back to the plugin.
654
+ * The dialog will be closed automatically after submission.
655
+ *
656
+ * @param dialogId - The dialog ID (provided in the dialog's query string)
657
+ * @param value - The value to submit
658
+ * @returns Whether the submission was successful
659
+ *
660
+ * @example
661
+ * ```typescript
662
+ * // Inside dialog HTML:
663
+ * const dialogId = new URLSearchParams(location.search).get('dialogId');
664
+ * await submitDialogResult(dialogId, { repoName: 'my-repo' });
665
+ * ```
666
+ */
667
+ async function submitDialogResult(dialogId, value) {
668
+ return getTauriCore().invoke("submit_dialog_result", {
669
+ dialogId,
670
+ result: {
671
+ type: "submitted",
672
+ value
673
+ }
674
+ });
675
+ }
676
+ /**
677
+ * Cancel a plugin dialog
678
+ *
679
+ * This is called from inside the dialog HTML to cancel without submitting.
680
+ * The dialog will be closed automatically.
681
+ *
682
+ * @param dialogId - The dialog ID (provided in the dialog's query string)
683
+ *
684
+ * @example
685
+ * ```typescript
686
+ * // Inside dialog HTML:
687
+ * const dialogId = new URLSearchParams(location.search).get('dialogId');
688
+ * await cancelDialog(dialogId);
689
+ * ```
690
+ */
691
+ async function cancelDialog(dialogId) {
692
+ return getTauriCore().invoke("submit_dialog_result", {
693
+ dialogId,
694
+ result: { type: "cancelled" }
695
+ });
696
+ }
697
+
698
+ //#endregion
699
+ //#region src/utils/events.ts
700
+ /**
701
+ * Get Tauri event API
702
+ * @internal
703
+ */
704
+ function getTauriEvent() {
705
+ const w = window;
706
+ if (!w.__TAURI__?.event) throw new Error("Tauri event API not available");
707
+ return w.__TAURI__.event;
708
+ }
709
+ /**
710
+ * Check if Tauri event API is available
711
+ */
712
+ function isEventApiAvailable() {
713
+ return !!window.__TAURI__?.event;
714
+ }
715
+ /**
716
+ * Emit an event to other parts of the application
717
+ *
718
+ * @param event - Event name (e.g., "repo-created", "dialog-result")
719
+ * @param payload - Data to send with the event
720
+ *
721
+ * @example
722
+ * ```typescript
723
+ * // From dialog:
724
+ * await emitEvent("repo-name-validated", { name: "my-repo", available: true });
725
+ *
726
+ * // From plugin:
727
+ * await emitEvent("deployment-started", { url: "https://github.com/..." });
728
+ * ```
729
+ */
730
+ async function emitEvent(event, payload) {
731
+ await getTauriEvent().emit(event, payload);
732
+ }
733
+ /**
734
+ * Listen for events from other parts of the application
735
+ *
736
+ * @param event - Event name to listen for
737
+ * @param handler - Function to call when event is received
738
+ * @returns Cleanup function to stop listening
739
+ *
740
+ * @example
741
+ * ```typescript
742
+ * const unlisten = await onEvent<{ name: string; available: boolean }>(
743
+ * "repo-name-validated",
744
+ * (data) => {
745
+ * console.log(`Repo ${data.name} is ${data.available ? "available" : "taken"}`);
746
+ * }
747
+ * );
748
+ *
749
+ * // Later, to stop listening:
750
+ * unlisten();
751
+ * ```
752
+ */
753
+ async function onEvent(event, handler) {
754
+ return await getTauriEvent().listen(event, (e) => {
755
+ handler(e.payload);
756
+ });
757
+ }
758
+ /**
759
+ * Wait for a single event occurrence
760
+ *
761
+ * @param event - Event name to wait for
762
+ * @param timeoutMs - Maximum time to wait (default: 30000ms)
763
+ * @returns Promise that resolves with the event payload
764
+ * @throws Error if timeout is reached
765
+ *
766
+ * @example
767
+ * ```typescript
768
+ * try {
769
+ * const result = await waitForEvent<{ confirmed: boolean }>("user-confirmed", 10000);
770
+ * if (result.confirmed) {
771
+ * // proceed
772
+ * }
773
+ * } catch (e) {
774
+ * console.log("User did not respond in time");
775
+ * }
776
+ * ```
777
+ */
778
+ async function waitForEvent(event, timeoutMs = 3e4) {
779
+ return new Promise((resolve, reject) => {
780
+ let unlisten = null;
781
+ let timeoutId;
782
+ const cleanup = () => {
783
+ if (unlisten) unlisten();
784
+ clearTimeout(timeoutId);
785
+ };
786
+ timeoutId = setTimeout(() => {
787
+ cleanup();
788
+ reject(/* @__PURE__ */ new Error(`Timeout waiting for event: ${event}`));
789
+ }, timeoutMs);
790
+ onEvent(event, (payload) => {
791
+ cleanup();
792
+ resolve(payload);
793
+ }).then((unlistenFn) => {
794
+ unlisten = unlistenFn;
795
+ });
796
+ });
797
+ }
798
+
799
+ //#endregion
800
+ export { cancelDialog, closeBrowser, downloadAsset, emitEvent, error, executeBinary, fetchUrl, fileExists, getMessageContext, getPluginCookie, getTauriCore, isEventApiAvailable, isTauriAvailable, listFiles, listPluginFiles, log, onEvent, openBrowser, pluginFileExists, readFile, readPluginFile, reportComplete, reportError, reportProgress, sendMessage, setMessageContext, setPluginCookie, showPluginDialog, submitDialogResult, waitForEvent, warn, writeFile, writePluginFile };
612
801
  //# sourceMappingURL=index.mjs.map
@@ -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/context.ts","../src/utils/filesystem.ts","../src/utils/plugin-storage.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 * Internal context utilities for Moss plugins\n *\n * This module provides access to the plugin execution context that is\n * set by the plugin runtime before each hook execution.\n *\n * INTERNAL USE ONLY - not exported to plugins.\n * Plugins should use the higher-level APIs (readFile, writeFile, etc.)\n * which use this context internally.\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Internal plugin execution context\n *\n * Set by the plugin runtime (window.__MOSS_INTERNAL_CONTEXT__)\n * before each hook execution. Cleared after hook completes.\n */\nexport interface InternalPluginContext {\n /** Plugin name from manifest */\n plugin_name: string;\n /** Absolute path to the project directory */\n project_path: string;\n /** Absolute path to the .moss directory */\n moss_dir: string;\n}\n\n/**\n * Window interface with internal context\n */\ninterface ContextWindow {\n __MOSS_INTERNAL_CONTEXT__?: InternalPluginContext;\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get the internal plugin context\n *\n * This is used internally by moss-api utilities to resolve paths\n * and plugin identity. Plugins should not call this directly.\n *\n * @returns The current plugin execution context\n * @throws Error if called outside of a plugin hook execution\n *\n * @internal\n */\nexport function getInternalContext(): InternalPluginContext {\n const context = (window as unknown as ContextWindow).__MOSS_INTERNAL_CONTEXT__;\n\n if (!context) {\n throw new Error(\n \"This function 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 context;\n}\n\n/**\n * Check if we're currently inside a plugin hook execution\n *\n * @returns true if inside a hook, false otherwise\n *\n * @internal\n */\nexport function hasContext(): boolean {\n const context = (window as unknown as ContextWindow).__MOSS_INTERNAL_CONTEXT__;\n return context !== undefined;\n}\n","/**\n * File system operations for Moss plugins\n *\n * These functions provide access to project files (user content).\n * Project path is auto-detected from the runtime context.\n *\n * For plugin's private storage, use the plugin-storage API instead.\n */\n\nimport { getTauriCore } from \"./tauri\";\nimport { getInternalContext } from \"./context\";\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Read a file from the project directory\n *\n * Project path is auto-detected from the runtime context.\n *\n * @param relativePath - Path relative to the project root\n * @returns File contents as a string\n * @throws Error if file cannot be read or called outside a hook\n *\n * @example\n * ```typescript\n * // Read an article\n * const content = await readFile(\"article/hello-world.md\");\n *\n * // Read package.json\n * const pkg = JSON.parse(await readFile(\"package.json\"));\n * ```\n */\nexport async function readFile(relativePath: string): Promise<string> {\n const ctx = getInternalContext();\n\n return getTauriCore().invoke<string>(\"read_project_file\", {\n projectPath: ctx.project_path,\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 * Project path is auto-detected from the runtime context.\n *\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 or called outside a hook\n *\n * @example\n * ```typescript\n * // Write a generated article\n * await writeFile(\"article/new-post.md\", \"# Hello World\\n\\nContent here.\");\n *\n * // Write index page\n * await writeFile(\"index.md\", markdownContent);\n * ```\n */\nexport async function writeFile(\n relativePath: string,\n content: string\n): Promise<void> {\n const ctx = getInternalContext();\n\n await getTauriCore().invoke(\"write_project_file\", {\n projectPath: ctx.project_path,\n relativePath,\n data: content,\n });\n}\n\n/**\n * List all files in the project directory\n *\n * Returns file paths relative to the project root.\n * Project path is auto-detected from the runtime context.\n *\n * @returns Array of relative file paths\n * @throws Error if directory cannot be listed or called outside a hook\n *\n * @example\n * ```typescript\n * const files = await listFiles();\n * // [\"index.md\", \"article/hello.md\", \"assets/logo.png\"]\n *\n * const mdFiles = files.filter(f => f.endsWith(\".md\"));\n * ```\n */\nexport async function listFiles(): Promise<string[]> {\n const ctx = getInternalContext();\n\n return getTauriCore().invoke<string[]>(\"list_project_files\", {\n projectPath: ctx.project_path,\n });\n}\n\n/**\n * Check if a file exists in the project directory\n *\n * Project path is auto-detected from the runtime context.\n *\n * @param relativePath - Path relative to the project root\n * @returns true if file exists, false otherwise\n * @throws Error if called outside a hook\n *\n * @example\n * ```typescript\n * if (await fileExists(\"index.md\")) {\n * const content = await readFile(\"index.md\");\n * }\n * ```\n */\nexport async function fileExists(relativePath: string): Promise<boolean> {\n // First, verify we have context (this will throw if not in a hook)\n getInternalContext();\n\n try {\n await readFile(relativePath);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * Plugin storage API for Moss plugins\n *\n * Provides access to a plugin's private storage directory at:\n * .moss/plugins/{plugin-name}/\n *\n * Plugin identity is auto-detected from the runtime context -\n * plugins never need to know their own name or path.\n *\n * Config is just a file: readPluginFile(\"config.json\")\n */\n\nimport { getTauriCore } from \"./tauri\";\nimport { getInternalContext } from \"./context\";\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Read a file from the plugin's private storage directory\n *\n * Storage path: .moss/plugins/{plugin-name}/{relativePath}\n *\n * @param relativePath - Path relative to the plugin's storage directory\n * @returns File contents as a string\n * @throws Error if file cannot be read or called outside a hook\n *\n * @example\n * ```typescript\n * // Read plugin config\n * const configJson = await readPluginFile(\"config.json\");\n * const config = JSON.parse(configJson);\n *\n * // Read cached data\n * const cached = await readPluginFile(\"cache/articles.json\");\n * ```\n */\nexport async function readPluginFile(relativePath: string): Promise<string> {\n const ctx = getInternalContext();\n\n return getTauriCore().invoke<string>(\"read_plugin_file\", {\n pluginName: ctx.plugin_name,\n projectPath: ctx.project_path,\n relativePath,\n });\n}\n\n/**\n * Write a file to the plugin's private storage directory\n *\n * Creates parent directories if they don't exist.\n * Storage path: .moss/plugins/{plugin-name}/{relativePath}\n *\n * @param relativePath - Path relative to the plugin's storage directory\n * @param content - Content to write to the file\n * @throws Error if file cannot be written or called outside a hook\n *\n * @example\n * ```typescript\n * // Save plugin config\n * await writePluginFile(\"config.json\", JSON.stringify(config, null, 2));\n *\n * // Cache data\n * await writePluginFile(\"cache/articles.json\", JSON.stringify(articles));\n * ```\n */\nexport async function writePluginFile(\n relativePath: string,\n content: string\n): Promise<void> {\n const ctx = getInternalContext();\n\n await getTauriCore().invoke(\"write_plugin_file\", {\n pluginName: ctx.plugin_name,\n projectPath: ctx.project_path,\n relativePath,\n content,\n });\n}\n\n/**\n * List all files in the plugin's private storage directory\n *\n * Returns file paths relative to the plugin's storage directory.\n *\n * @returns Array of relative file paths\n * @throws Error if directory cannot be listed or called outside a hook\n *\n * @example\n * ```typescript\n * const files = await listPluginFiles();\n * // [\"config.json\", \"cache/articles.json\", \"cache/images.json\"]\n * ```\n */\nexport async function listPluginFiles(): Promise<string[]> {\n const ctx = getInternalContext();\n\n return getTauriCore().invoke<string[]>(\"list_plugin_files\", {\n pluginName: ctx.plugin_name,\n projectPath: ctx.project_path,\n });\n}\n\n/**\n * Check if a file exists in the plugin's private storage directory\n *\n * @param relativePath - Path relative to the plugin's storage directory\n * @returns true if file exists, false otherwise\n * @throws Error if called outside a hook\n *\n * @example\n * ```typescript\n * if (await pluginFileExists(\"config.json\")) {\n * const config = JSON.parse(await readPluginFile(\"config.json\"));\n * } else {\n * // Use default config\n * }\n * ```\n */\nexport async function pluginFileExists(relativePath: string): Promise<boolean> {\n const ctx = getInternalContext();\n\n return getTauriCore().invoke<boolean>(\"plugin_file_exists\", {\n pluginName: ctx.plugin_name,\n projectPath: ctx.project_path,\n relativePath,\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 * Project path for downloads is auto-detected from the runtime context.\n */\n\nimport { getTauriCore } from \"./tauri\";\nimport { getInternalContext } from \"./context\";\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 * Project path is auto-detected from the runtime context.\n *\n * @param url - URL to download\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, or called outside a hook\n *\n * @example\n * ```typescript\n * const result = await downloadAsset(\n * \"https://example.com/image\",\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 targetDir: string,\n options: DownloadOptions = {}\n): Promise<DownloadResult> {\n const ctx = getInternalContext();\n const { timeoutMs = 30000 } = options;\n\n const result = await getTauriCore().invoke<TauriDownloadResult>(\n \"download_asset\",\n {\n url,\n projectPath: ctx.project_path,\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 * Working directory is auto-detected from the runtime context\n * (always the project root).\n */\n\nimport { getTauriCore } from \"./tauri\";\nimport { getInternalContext } from \"./context\";\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 /** 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 * Working directory is auto-detected from the runtime context\n * (always the project root).\n *\n * @param options - Execution options including binary path and args\n * @returns Execution result with stdout, stderr, and exit code\n * @throws Error if binary cannot be executed or called outside a hook\n *\n * @example\n * ```typescript\n * // Run git status\n * const result = await executeBinary({\n * binaryPath: \"git\",\n * args: [\"status\"],\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 * timeoutMs: 120000,\n * env: { NODE_ENV: \"production\" },\n * });\n * ```\n */\nexport async function executeBinary(\n options: ExecuteOptions\n): Promise<ExecuteResult> {\n const ctx = getInternalContext();\n const { binaryPath, args, timeoutMs = 60000, env } = options;\n\n const result = await getTauriCore().invoke<TauriBinaryResult>(\n \"execute_binary\",\n {\n binaryPath,\n args,\n workingDir: ctx.project_path,\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\";\nimport { getInternalContext } from \"./context\";\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 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 ctx = getInternalContext();\n\n return getTauriCore().invoke<Cookie[]>(\"get_plugin_cookie\", {\n pluginName: ctx.plugin_name,\n projectPath: ctx.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 ctx = getInternalContext();\n\n await getTauriCore().invoke(\"set_plugin_cookie\", {\n pluginName: ctx.plugin_name,\n projectPath: ctx.project_path,\n cookies,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["unlisten: (() => void) | null","timeoutId: ReturnType<typeof setTimeout>"],"sources":["../src/utils/tauri.ts","../src/utils/messaging.ts","../src/utils/logger.ts","../src/utils/browser.ts","../src/utils/context.ts","../src/utils/filesystem.ts","../src/utils/plugin-storage.ts","../src/utils/http.ts","../src/utils/binary.ts","../src/utils/cookies.ts","../src/utils/window.ts","../src/utils/events.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 * Internal context utilities for Moss plugins\n *\n * This module provides access to the plugin execution context that is\n * set by the plugin runtime before each hook execution.\n *\n * INTERNAL USE ONLY - not exported to plugins.\n * Plugins should use the higher-level APIs (readFile, writeFile, etc.)\n * which use this context internally.\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Internal plugin execution context\n *\n * Set by the plugin runtime (window.__MOSS_INTERNAL_CONTEXT__)\n * before each hook execution. Cleared after hook completes.\n */\nexport interface InternalPluginContext {\n /** Plugin name from manifest */\n plugin_name: string;\n /** Absolute path to the project directory */\n project_path: string;\n /** Absolute path to the .moss directory */\n moss_dir: string;\n}\n\n/**\n * Window interface with internal context\n */\ninterface ContextWindow {\n __MOSS_INTERNAL_CONTEXT__?: InternalPluginContext;\n}\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Get the internal plugin context\n *\n * This is used internally by moss-api utilities to resolve paths\n * and plugin identity. Plugins should not call this directly.\n *\n * @returns The current plugin execution context\n * @throws Error if called outside of a plugin hook execution\n *\n * @internal\n */\nexport function getInternalContext(): InternalPluginContext {\n const context = (window as unknown as ContextWindow).__MOSS_INTERNAL_CONTEXT__;\n\n if (!context) {\n throw new Error(\n \"This function 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 context;\n}\n\n/**\n * Check if we're currently inside a plugin hook execution\n *\n * @returns true if inside a hook, false otherwise\n *\n * @internal\n */\nexport function hasContext(): boolean {\n const context = (window as unknown as ContextWindow).__MOSS_INTERNAL_CONTEXT__;\n return context !== undefined;\n}\n","/**\n * File system operations for Moss plugins\n *\n * These functions provide access to project files (user content).\n * Project path is auto-detected from the runtime context.\n *\n * For plugin's private storage, use the plugin-storage API instead.\n */\n\nimport { getTauriCore } from \"./tauri\";\nimport { getInternalContext } from \"./context\";\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Read a file from the project directory\n *\n * Project path is auto-detected from the runtime context.\n *\n * @param relativePath - Path relative to the project root\n * @returns File contents as a string\n * @throws Error if file cannot be read or called outside a hook\n *\n * @example\n * ```typescript\n * // Read an article\n * const content = await readFile(\"article/hello-world.md\");\n *\n * // Read package.json\n * const pkg = JSON.parse(await readFile(\"package.json\"));\n * ```\n */\nexport async function readFile(relativePath: string): Promise<string> {\n const ctx = getInternalContext();\n\n return getTauriCore().invoke<string>(\"read_project_file\", {\n projectPath: ctx.project_path,\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 * Project path is auto-detected from the runtime context.\n *\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 or called outside a hook\n *\n * @example\n * ```typescript\n * // Write a generated article\n * await writeFile(\"article/new-post.md\", \"# Hello World\\n\\nContent here.\");\n *\n * // Write index page\n * await writeFile(\"index.md\", markdownContent);\n * ```\n */\nexport async function writeFile(\n relativePath: string,\n content: string\n): Promise<void> {\n const ctx = getInternalContext();\n\n await getTauriCore().invoke(\"write_project_file\", {\n projectPath: ctx.project_path,\n relativePath,\n data: content,\n });\n}\n\n/**\n * List all files in the project directory\n *\n * Returns file paths relative to the project root.\n * Project path is auto-detected from the runtime context.\n *\n * @returns Array of relative file paths\n * @throws Error if directory cannot be listed or called outside a hook\n *\n * @example\n * ```typescript\n * const files = await listFiles();\n * // [\"index.md\", \"article/hello.md\", \"assets/logo.png\"]\n *\n * const mdFiles = files.filter(f => f.endsWith(\".md\"));\n * ```\n */\nexport async function listFiles(): Promise<string[]> {\n const ctx = getInternalContext();\n\n return getTauriCore().invoke<string[]>(\"list_project_files\", {\n projectPath: ctx.project_path,\n });\n}\n\n/**\n * Check if a file exists in the project directory\n *\n * Project path is auto-detected from the runtime context.\n *\n * @param relativePath - Path relative to the project root\n * @returns true if file exists, false otherwise\n * @throws Error if called outside a hook\n *\n * @example\n * ```typescript\n * if (await fileExists(\"index.md\")) {\n * const content = await readFile(\"index.md\");\n * }\n * ```\n */\nexport async function fileExists(relativePath: string): Promise<boolean> {\n // First, verify we have context (this will throw if not in a hook)\n getInternalContext();\n\n try {\n await readFile(relativePath);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * Plugin storage API for Moss plugins\n *\n * Provides access to a plugin's private storage directory at:\n * .moss/plugins/{plugin-name}/\n *\n * Plugin identity is auto-detected from the runtime context -\n * plugins never need to know their own name or path.\n *\n * Config is just a file: readPluginFile(\"config.json\")\n */\n\nimport { getTauriCore } from \"./tauri\";\nimport { getInternalContext } from \"./context\";\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Read a file from the plugin's private storage directory\n *\n * Storage path: .moss/plugins/{plugin-name}/{relativePath}\n *\n * @param relativePath - Path relative to the plugin's storage directory\n * @returns File contents as a string\n * @throws Error if file cannot be read or called outside a hook\n *\n * @example\n * ```typescript\n * // Read plugin config\n * const configJson = await readPluginFile(\"config.json\");\n * const config = JSON.parse(configJson);\n *\n * // Read cached data\n * const cached = await readPluginFile(\"cache/articles.json\");\n * ```\n */\nexport async function readPluginFile(relativePath: string): Promise<string> {\n const ctx = getInternalContext();\n\n return getTauriCore().invoke<string>(\"read_plugin_file\", {\n pluginName: ctx.plugin_name,\n projectPath: ctx.project_path,\n relativePath,\n });\n}\n\n/**\n * Write a file to the plugin's private storage directory\n *\n * Creates parent directories if they don't exist.\n * Storage path: .moss/plugins/{plugin-name}/{relativePath}\n *\n * @param relativePath - Path relative to the plugin's storage directory\n * @param content - Content to write to the file\n * @throws Error if file cannot be written or called outside a hook\n *\n * @example\n * ```typescript\n * // Save plugin config\n * await writePluginFile(\"config.json\", JSON.stringify(config, null, 2));\n *\n * // Cache data\n * await writePluginFile(\"cache/articles.json\", JSON.stringify(articles));\n * ```\n */\nexport async function writePluginFile(\n relativePath: string,\n content: string\n): Promise<void> {\n const ctx = getInternalContext();\n\n await getTauriCore().invoke(\"write_plugin_file\", {\n pluginName: ctx.plugin_name,\n projectPath: ctx.project_path,\n relativePath,\n content,\n });\n}\n\n/**\n * List all files in the plugin's private storage directory\n *\n * Returns file paths relative to the plugin's storage directory.\n *\n * @returns Array of relative file paths\n * @throws Error if directory cannot be listed or called outside a hook\n *\n * @example\n * ```typescript\n * const files = await listPluginFiles();\n * // [\"config.json\", \"cache/articles.json\", \"cache/images.json\"]\n * ```\n */\nexport async function listPluginFiles(): Promise<string[]> {\n const ctx = getInternalContext();\n\n return getTauriCore().invoke<string[]>(\"list_plugin_files\", {\n pluginName: ctx.plugin_name,\n projectPath: ctx.project_path,\n });\n}\n\n/**\n * Check if a file exists in the plugin's private storage directory\n *\n * @param relativePath - Path relative to the plugin's storage directory\n * @returns true if file exists, false otherwise\n * @throws Error if called outside a hook\n *\n * @example\n * ```typescript\n * if (await pluginFileExists(\"config.json\")) {\n * const config = JSON.parse(await readPluginFile(\"config.json\"));\n * } else {\n * // Use default config\n * }\n * ```\n */\nexport async function pluginFileExists(relativePath: string): Promise<boolean> {\n const ctx = getInternalContext();\n\n return getTauriCore().invoke<boolean>(\"plugin_file_exists\", {\n pluginName: ctx.plugin_name,\n projectPath: ctx.project_path,\n relativePath,\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 * Project path for downloads is auto-detected from the runtime context.\n */\n\nimport { getTauriCore } from \"./tauri\";\nimport { getInternalContext } from \"./context\";\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 * Project path is auto-detected from the runtime context.\n *\n * @param url - URL to download\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, or called outside a hook\n *\n * @example\n * ```typescript\n * const result = await downloadAsset(\n * \"https://example.com/image\",\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 targetDir: string,\n options: DownloadOptions = {}\n): Promise<DownloadResult> {\n const ctx = getInternalContext();\n const { timeoutMs = 30000 } = options;\n\n const result = await getTauriCore().invoke<TauriDownloadResult>(\n \"download_asset\",\n {\n url,\n projectPath: ctx.project_path,\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 * Working directory is auto-detected from the runtime context\n * (always the project root).\n */\n\nimport { getTauriCore } from \"./tauri\";\nimport { getInternalContext } from \"./context\";\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 /** 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 * Working directory is auto-detected from the runtime context\n * (always the project root).\n *\n * @param options - Execution options including binary path and args\n * @returns Execution result with stdout, stderr, and exit code\n * @throws Error if binary cannot be executed or called outside a hook\n *\n * @example\n * ```typescript\n * // Run git status\n * const result = await executeBinary({\n * binaryPath: \"git\",\n * args: [\"status\"],\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 * timeoutMs: 120000,\n * env: { NODE_ENV: \"production\" },\n * });\n * ```\n */\nexport async function executeBinary(\n options: ExecuteOptions\n): Promise<ExecuteResult> {\n const ctx = getInternalContext();\n const { binaryPath, args, timeoutMs = 60000, env } = options;\n\n const result = await getTauriCore().invoke<TauriBinaryResult>(\n \"execute_binary\",\n {\n binaryPath,\n args,\n workingDir: ctx.project_path,\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\";\nimport { getInternalContext } from \"./context\";\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 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 ctx = getInternalContext();\n\n return getTauriCore().invoke<Cookie[]>(\"get_plugin_cookie\", {\n pluginName: ctx.plugin_name,\n projectPath: ctx.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 ctx = getInternalContext();\n\n await getTauriCore().invoke(\"set_plugin_cookie\", {\n pluginName: ctx.plugin_name,\n projectPath: ctx.project_path,\n cookies,\n });\n}\n","/**\n * Window utilities for plugins\n * Enables plugins to show custom dialogs and UI elements\n */\n\nimport { getTauriCore } from \"./tauri\";\n\n/**\n * Result from a dialog interaction\n */\nexport interface DialogResult {\n type: \"submitted\" | \"cancelled\";\n value?: unknown;\n}\n\n/**\n * Options for showing a plugin dialog\n */\nexport interface ShowDialogOptions {\n /** URL to load in the dialog (can be data: URL with embedded HTML) */\n url: string;\n /** Dialog window title */\n title: string;\n /** Dialog width in pixels */\n width?: number;\n /** Dialog height in pixels */\n height?: number;\n /** Maximum time to wait for user response in milliseconds */\n timeoutMs?: number;\n}\n\n/**\n * Show a plugin dialog and wait for user response\n *\n * The dialog can be an embedded HTML page (via data: URL) that communicates\n * back to the plugin via the submitDialogResult function.\n *\n * @param options - Dialog configuration\n * @returns Dialog result with submitted value or cancellation\n *\n * @example\n * ```typescript\n * const result = await showPluginDialog({\n * url: createMyDialogUrl(),\n * title: \"Create Repository\",\n * width: 400,\n * height: 300,\n * });\n *\n * if (result.type === \"submitted\") {\n * console.log(\"User submitted:\", result.value);\n * } else {\n * console.log(\"User cancelled\");\n * }\n * ```\n */\nexport async function showPluginDialog(\n options: ShowDialogOptions\n): Promise<DialogResult> {\n const result = await getTauriCore().invoke<DialogResult>(\n \"show_plugin_dialog\",\n {\n url: options.url,\n title: options.title,\n width: options.width ?? 500,\n height: options.height ?? 400,\n timeoutMs: options.timeoutMs ?? 300000, // 5 minutes default\n }\n );\n return result;\n}\n\n/**\n * Submit a result from within a plugin dialog\n *\n * This is called from inside the dialog HTML to send data back to the plugin.\n * The dialog will be closed automatically after submission.\n *\n * @param dialogId - The dialog ID (provided in the dialog's query string)\n * @param value - The value to submit\n * @returns Whether the submission was successful\n *\n * @example\n * ```typescript\n * // Inside dialog HTML:\n * const dialogId = new URLSearchParams(location.search).get('dialogId');\n * await submitDialogResult(dialogId, { repoName: 'my-repo' });\n * ```\n */\nexport async function submitDialogResult(\n dialogId: string,\n value: unknown\n): Promise<boolean> {\n return getTauriCore().invoke<boolean>(\"submit_dialog_result\", {\n dialogId,\n result: { type: \"submitted\", value },\n });\n}\n\n/**\n * Cancel a plugin dialog\n *\n * This is called from inside the dialog HTML to cancel without submitting.\n * The dialog will be closed automatically.\n *\n * @param dialogId - The dialog ID (provided in the dialog's query string)\n *\n * @example\n * ```typescript\n * // Inside dialog HTML:\n * const dialogId = new URLSearchParams(location.search).get('dialogId');\n * await cancelDialog(dialogId);\n * ```\n */\nexport async function cancelDialog(dialogId: string): Promise<boolean> {\n return getTauriCore().invoke<boolean>(\"submit_dialog_result\", {\n dialogId,\n result: { type: \"cancelled\" },\n });\n}\n","/**\n * Event utilities for plugin communication\n *\n * Provides a way for plugins and dialog windows to communicate\n * via Tauri's event system.\n */\n\n/**\n * Tauri event listener interface\n */\ninterface TauriEventListen {\n (event: string, handler: (event: { payload: unknown }) => void): Promise<() => void>;\n}\n\n/**\n * Tauri event emit interface\n */\ninterface TauriEventEmit {\n (event: string, payload?: unknown): Promise<void>;\n}\n\ninterface TauriEventWindow {\n __TAURI__?: {\n event?: {\n listen: TauriEventListen;\n emit: TauriEventEmit;\n };\n };\n}\n\n/**\n * Get Tauri event API\n * @internal\n */\nfunction getTauriEvent(): { listen: TauriEventListen; emit: TauriEventEmit } {\n const w = window as unknown as TauriEventWindow;\n if (!w.__TAURI__?.event) {\n throw new Error(\"Tauri event API not available\");\n }\n return w.__TAURI__.event;\n}\n\n/**\n * Check if Tauri event API is available\n */\nexport function isEventApiAvailable(): boolean {\n const w = window as unknown as TauriEventWindow;\n return !!w.__TAURI__?.event;\n}\n\n/**\n * Emit an event to other parts of the application\n *\n * @param event - Event name (e.g., \"repo-created\", \"dialog-result\")\n * @param payload - Data to send with the event\n *\n * @example\n * ```typescript\n * // From dialog:\n * await emitEvent(\"repo-name-validated\", { name: \"my-repo\", available: true });\n *\n * // From plugin:\n * await emitEvent(\"deployment-started\", { url: \"https://github.com/...\" });\n * ```\n */\nexport async function emitEvent(event: string, payload?: unknown): Promise<void> {\n await getTauriEvent().emit(event, payload);\n}\n\n/**\n * Listen for events from other parts of the application\n *\n * @param event - Event name to listen for\n * @param handler - Function to call when event is received\n * @returns Cleanup function to stop listening\n *\n * @example\n * ```typescript\n * const unlisten = await onEvent<{ name: string; available: boolean }>(\n * \"repo-name-validated\",\n * (data) => {\n * console.log(`Repo ${data.name} is ${data.available ? \"available\" : \"taken\"}`);\n * }\n * );\n *\n * // Later, to stop listening:\n * unlisten();\n * ```\n */\nexport async function onEvent<T>(\n event: string,\n handler: (payload: T) => void\n): Promise<() => void> {\n const unlisten = await getTauriEvent().listen(event, (e) => {\n handler(e.payload as T);\n });\n return unlisten;\n}\n\n/**\n * Wait for a single event occurrence\n *\n * @param event - Event name to wait for\n * @param timeoutMs - Maximum time to wait (default: 30000ms)\n * @returns Promise that resolves with the event payload\n * @throws Error if timeout is reached\n *\n * @example\n * ```typescript\n * try {\n * const result = await waitForEvent<{ confirmed: boolean }>(\"user-confirmed\", 10000);\n * if (result.confirmed) {\n * // proceed\n * }\n * } catch (e) {\n * console.log(\"User did not respond in time\");\n * }\n * ```\n */\nexport async function waitForEvent<T>(\n event: string,\n timeoutMs: number = 30000\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let unlisten: (() => void) | null = null;\n let timeoutId: ReturnType<typeof setTimeout>;\n\n const cleanup = () => {\n if (unlisten) unlisten();\n clearTimeout(timeoutId);\n };\n\n timeoutId = setTimeout(() => {\n cleanup();\n reject(new Error(`Timeout waiting for event: ${event}`));\n }, timeoutMs);\n\n onEvent<T>(event, (payload) => {\n cleanup();\n resolve(payload);\n }).then((unlistenFn) => {\n unlisten = unlistenFn;\n });\n });\n}\n"],"mappings":"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"}
@@ -198,6 +198,37 @@ interface MockBrowserTracker {
198
198
  * Create a new browser tracker instance
199
199
  */
200
200
  declare function createMockBrowserTracker(): MockBrowserTracker;
201
+ /**
202
+ * Dialog result types matching the moss backend
203
+ */
204
+ interface MockDialogResult {
205
+ type: "submitted" | "cancelled";
206
+ value?: unknown;
207
+ }
208
+ /**
209
+ * Tracks dialog interactions for testing
210
+ */
211
+ interface MockDialogTracker {
212
+ /** Dialogs that were shown (with their URLs and titles) */
213
+ shownDialogs: Array<{
214
+ url: string;
215
+ title: string;
216
+ width: number;
217
+ height: number;
218
+ }>;
219
+ /** Configure the next dialog result (for automatic response) */
220
+ nextResult: MockDialogResult | null;
221
+ /** Submitted results by dialog ID */
222
+ submittedResults: Map<string, MockDialogResult>;
223
+ /** Set the result for the next dialog shown */
224
+ setNextResult(result: MockDialogResult): void;
225
+ /** Simulate user submitting a dialog result */
226
+ submitResult(dialogId: string, value: unknown): void;
227
+ /** Simulate user cancelling a dialog */
228
+ cancelDialog(dialogId: string): void;
229
+ /** Reset tracking state */
230
+ reset(): void;
231
+ }
201
232
  /**
202
233
  * Options for setting up mock Tauri environment
203
234
  */
@@ -223,6 +254,8 @@ interface MockTauriContext {
223
254
  cookieStorage: MockCookieStorage;
224
255
  /** Browser open/close tracking */
225
256
  browserTracker: MockBrowserTracker;
257
+ /** Dialog interaction tracking */
258
+ dialogTracker: MockDialogTracker;
226
259
  /** The project path used for internal context */
227
260
  projectPath: string;
228
261
  /** The plugin name used for internal context */
@@ -1 +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;AAUiB,UAraA,cAAA,CAqagB;EAEnB;EAEK,KAAA,EAvaV,GAuaU,CAAA,MAAA,EAvaE,QAuaF,CAAA;EAEN;EAEG,OAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAzaS,QAyaT,GAAA,SAAA;EAEC;EAEC,OAAA,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAAkB;EAyCpB,UAAA,CAAA,IAAA,EAAc,MAAA,CAAA,EAAA,OAAW;;;;;;;;;iBAxczB,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,qBAAA;;;;;;;;;UAUA,gBAAA;;cAEH;;mBAEK;;aAEN;;gBAEG;;iBAEC;;kBAEC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCF,cAAA,WAAyB,wBAAwB"}
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;AAoCA;AAQiB,UAzYA,cAAA,CAyYiB;EAElB;EAEF,KAAA,EA3YL,GA2YK,CAAA,MAAA,EA3YO,QA2YP,CAAA;EAEkB;EAAZ,OAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EA3YK,QA2YL,GAAA,SAAA;EAEI;EAAgB,OAAA,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAsFvB;EAUA,UAAA,CAAA,IAAA,EAAA,MAAgB,CAAA,EAAA,OAAA;EAEnB;EAEK,SAAA,CAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA,EAAA;EAEN;EAEG,KAAA,EAAA,EAAA,IAAA;;;;;AA+CA,iBAthBA,oBAAA,CAAA,CAshByB,EAthBD,cAshByB;;;;UA5ehD,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;;;;UAoC3B,gBAAA;;;;;;;UAQA,iBAAA;;gBAED;;;;;;;cAEF;;oBAEM,YAAY;;wBAER;;;;;;;;;;;UAsFP,qBAAA;;;;;;;;;UAUA,gBAAA;;cAEH;;mBAEK;;aAEN;;gBAEG;;iBAEC;;kBAEC;;iBAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCD,cAAA,WAAyB,wBAAwB"}
@@ -172,9 +172,15 @@ function createMockBrowserTracker() {
172
172
  get closeCount() {
173
173
  return closeCount;
174
174
  },
175
+ set closeCount(value) {
176
+ closeCount = value;
177
+ },
175
178
  get isOpen() {
176
179
  return isOpen;
177
180
  },
181
+ set isOpen(value) {
182
+ isOpen = value;
183
+ },
178
184
  reset() {
179
185
  openedUrls.length = 0;
180
186
  closeCount = 0;
@@ -183,6 +189,45 @@ function createMockBrowserTracker() {
183
189
  };
184
190
  }
185
191
  /**
192
+ * Create a new dialog tracker instance
193
+ */
194
+ function createMockDialogTracker() {
195
+ const shownDialogs = [];
196
+ const submittedResults = /* @__PURE__ */ new Map();
197
+ let nextResult = null;
198
+ return {
199
+ get shownDialogs() {
200
+ return shownDialogs;
201
+ },
202
+ get nextResult() {
203
+ return nextResult;
204
+ },
205
+ set nextResult(value) {
206
+ nextResult = value;
207
+ },
208
+ get submittedResults() {
209
+ return submittedResults;
210
+ },
211
+ setNextResult(result) {
212
+ nextResult = result;
213
+ },
214
+ submitResult(dialogId, value) {
215
+ submittedResults.set(dialogId, {
216
+ type: "submitted",
217
+ value
218
+ });
219
+ },
220
+ cancelDialog(dialogId) {
221
+ submittedResults.set(dialogId, { type: "cancelled" });
222
+ },
223
+ reset() {
224
+ shownDialogs.length = 0;
225
+ submittedResults.clear();
226
+ nextResult = null;
227
+ }
228
+ };
229
+ }
230
+ /**
186
231
  * Extract filename from URL (mimics Rust backend behavior)
187
232
  */
188
233
  function extractFilenameFromUrl(url) {
@@ -236,6 +281,7 @@ function setupMockTauri(options) {
236
281
  const binaryConfig = createMockBinaryConfig();
237
282
  const cookieStorage = createMockCookieStorage();
238
283
  const browserTracker = createMockBrowserTracker();
284
+ const dialogTracker = createMockDialogTracker();
239
285
  const invoke = async (cmd, args) => {
240
286
  const payload = args;
241
287
  switch (cmd) {
@@ -272,7 +318,7 @@ function setupMockTauri(options) {
272
318
  const pn = payload?.pluginName;
273
319
  const pp = payload?.projectPath;
274
320
  const rp = payload?.relativePath;
275
- const content = payload?.data;
321
+ const content = payload?.content;
276
322
  const fullPath = `${pp}/.moss/plugins/${pn}/${rp}`;
277
323
  filesystem.setFile(fullPath, content);
278
324
  return null;
@@ -349,6 +395,30 @@ function setupMockTauri(options) {
349
395
  browserTracker.closeCount++;
350
396
  browserTracker.isOpen = false;
351
397
  return null;
398
+ case "show_plugin_dialog": {
399
+ const url = payload?.url;
400
+ const title = payload?.title;
401
+ const width = payload?.width ?? 500;
402
+ const height = payload?.height ?? 400;
403
+ dialogTracker.shownDialogs.push({
404
+ url,
405
+ title,
406
+ width,
407
+ height
408
+ });
409
+ if (dialogTracker.nextResult) {
410
+ const result = dialogTracker.nextResult;
411
+ dialogTracker.nextResult = null;
412
+ return result;
413
+ }
414
+ return { type: "cancelled" };
415
+ }
416
+ case "submit_dialog_result": {
417
+ const dialogId = payload?.dialogId;
418
+ const result = payload?.result;
419
+ if (result) dialogTracker.submittedResults.set(dialogId, result);
420
+ return true;
421
+ }
352
422
  case "execute_binary": {
353
423
  const binaryPath = payload?.binaryPath;
354
424
  const binaryArgs = payload?.args;
@@ -381,6 +451,7 @@ function setupMockTauri(options) {
381
451
  binaryConfig,
382
452
  cookieStorage,
383
453
  browserTracker,
454
+ dialogTracker,
384
455
  projectPath,
385
456
  pluginName,
386
457
  cleanup: () => {
@@ -392,6 +463,7 @@ function setupMockTauri(options) {
392
463
  binaryConfig.reset();
393
464
  cookieStorage.clear();
394
465
  browserTracker.reset();
466
+ dialogTracker.reset();
395
467
  }
396
468
  };
397
469
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["completedDownloads: string[]","failedDownloads: Array<{ url: string; error: string }>","defaultResponse: MockUrlResponse","defaultResult: MockBinaryResult","openedUrls: string[]","projectPath","pluginName"],"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 * Options for setting up mock Tauri environment\n */\nexport interface SetupMockTauriOptions {\n /** Plugin name for internal context (default: \"test-plugin\") */\n pluginName?: string;\n /** Project path for internal context (default: \"/test/project\") */\n projectPath?: string;\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 /** The project path used for internal context */\n projectPath: string;\n /** The plugin name used for internal context */\n pluginName: string;\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. It also sets up\n * `__MOSS_INTERNAL_CONTEXT__` for the context-aware APIs.\n *\n * @param options - Optional configuration for project path and plugin name\n * @returns Context with mock utilities and cleanup function\n *\n * @example\n * ```typescript\n * const ctx = setupMockTauri({ projectPath: \"/my/project\", pluginName: \"my-plugin\" });\n *\n * // Set up test data\n * ctx.filesystem.setFile(\"/my/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(options?: SetupMockTauriOptions): MockTauriContext {\n const projectPath = options?.projectPath ?? \"/test/project\";\n const pluginName = options?.pluginName ?? \"test-plugin\";\n\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 case \"project_file_exists\": {\n const projectPath = payload?.projectPath as string;\n const relativePath = payload?.relativePath as string;\n const fullPath = `${projectPath}/${relativePath}`;\n return filesystem.getFile(fullPath) !== undefined;\n }\n\n // ======================================================================\n // Plugin Storage Operations\n // ======================================================================\n case \"read_plugin_file\": {\n const pn = payload?.pluginName as string;\n const pp = payload?.projectPath as string;\n const rp = payload?.relativePath as string;\n const fullPath = `${pp}/.moss/plugins/${pn}/${rp}`;\n const file = filesystem.getFile(fullPath);\n if (file) {\n return file.content;\n }\n throw new Error(`Plugin file not found: ${fullPath}`);\n }\n\n case \"write_plugin_file\": {\n const pn = payload?.pluginName as string;\n const pp = payload?.projectPath as string;\n const rp = payload?.relativePath as string;\n const content = payload?.data as string;\n const fullPath = `${pp}/.moss/plugins/${pn}/${rp}`;\n filesystem.setFile(fullPath, content);\n return null;\n }\n\n case \"list_plugin_files\": {\n const pn = payload?.pluginName as string;\n const pp = payload?.projectPath as string;\n const prefix = `${pp}/.moss/plugins/${pn}/`;\n const allPaths = filesystem.listFiles();\n return allPaths\n .filter((p) => p.startsWith(prefix))\n .map((p) => p.substring(prefix.length));\n }\n\n case \"plugin_file_exists\": {\n const pn = payload?.pluginName as string;\n const pp = payload?.projectPath as string;\n const rp = payload?.relativePath as string;\n const fullPath = `${pp}/.moss/plugins/${pn}/${rp}`;\n return filesystem.getFile(fullPath) !== undefined;\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 __MOSS_INTERNAL_CONTEXT__?: {\n plugin_name: string;\n project_path: string;\n moss_dir: string;\n };\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 {\n window: {\n __TAURI__?: { core?: { invoke: typeof invoke } };\n __MOSS_INTERNAL_CONTEXT__?: {\n plugin_name: string;\n project_path: string;\n moss_dir: string;\n };\n };\n }).window;\n\n win.__TAURI__ = {\n core: { invoke },\n };\n\n // Set up internal context for context-aware APIs\n win.__MOSS_INTERNAL_CONTEXT__ = {\n plugin_name: pluginName,\n project_path: projectPath,\n moss_dir: `${projectPath}/.moss`,\n };\n\n return {\n filesystem,\n downloadTracker,\n urlConfig,\n binaryConfig,\n cookieStorage,\n browserTracker,\n projectPath,\n pluginName,\n cleanup: () => {\n // Clear the mock Tauri interface and internal context\n delete win.__TAURI__;\n delete win.__MOSS_INTERNAL_CONTEXT__;\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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"}
1
+ {"version":3,"file":"index.mjs","names":["completedDownloads: string[]","failedDownloads: Array<{ url: string; error: string }>","defaultResponse: MockUrlResponse","defaultResult: MockBinaryResult","openedUrls: string[]","shownDialogs: Array<{ url: string; title: string; width: number; height: number }>","nextResult: MockDialogResult | null","projectPath","pluginName"],"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 set closeCount(value: number) {\n closeCount = value;\n },\n get isOpen() {\n return isOpen;\n },\n set isOpen(value: boolean) {\n isOpen = value;\n },\n reset() {\n openedUrls.length = 0;\n closeCount = 0;\n isOpen = false;\n },\n };\n}\n\n// ============================================================================\n// Dialog Tracker\n// ============================================================================\n\n/**\n * Dialog result types matching the moss backend\n */\nexport interface MockDialogResult {\n type: \"submitted\" | \"cancelled\";\n value?: unknown;\n}\n\n/**\n * Tracks dialog interactions for testing\n */\nexport interface MockDialogTracker {\n /** Dialogs that were shown (with their URLs and titles) */\n shownDialogs: Array<{ url: string; title: string; width: number; height: number }>;\n /** Configure the next dialog result (for automatic response) */\n nextResult: MockDialogResult | null;\n /** Submitted results by dialog ID */\n submittedResults: Map<string, MockDialogResult>;\n /** Set the result for the next dialog shown */\n setNextResult(result: MockDialogResult): void;\n /** Simulate user submitting a dialog result */\n submitResult(dialogId: string, value: unknown): void;\n /** Simulate user cancelling a dialog */\n cancelDialog(dialogId: string): void;\n /** Reset tracking state */\n reset(): void;\n}\n\n/**\n * Create a new dialog tracker instance\n */\nexport function createMockDialogTracker(): MockDialogTracker {\n const shownDialogs: Array<{ url: string; title: string; width: number; height: number }> = [];\n const submittedResults = new Map<string, MockDialogResult>();\n let nextResult: MockDialogResult | null = null;\n\n return {\n get shownDialogs() {\n return shownDialogs;\n },\n get nextResult() {\n return nextResult;\n },\n set nextResult(value: MockDialogResult | null) {\n nextResult = value;\n },\n get submittedResults() {\n return submittedResults;\n },\n setNextResult(result: MockDialogResult) {\n nextResult = result;\n },\n submitResult(dialogId: string, value: unknown) {\n submittedResults.set(dialogId, { type: \"submitted\", value });\n },\n cancelDialog(dialogId: string) {\n submittedResults.set(dialogId, { type: \"cancelled\" });\n },\n reset() {\n shownDialogs.length = 0;\n submittedResults.clear();\n nextResult = null;\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 * Options for setting up mock Tauri environment\n */\nexport interface SetupMockTauriOptions {\n /** Plugin name for internal context (default: \"test-plugin\") */\n pluginName?: string;\n /** Project path for internal context (default: \"/test/project\") */\n projectPath?: string;\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 /** Dialog interaction tracking */\n dialogTracker: MockDialogTracker;\n /** The project path used for internal context */\n projectPath: string;\n /** The plugin name used for internal context */\n pluginName: string;\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. It also sets up\n * `__MOSS_INTERNAL_CONTEXT__` for the context-aware APIs.\n *\n * @param options - Optional configuration for project path and plugin name\n * @returns Context with mock utilities and cleanup function\n *\n * @example\n * ```typescript\n * const ctx = setupMockTauri({ projectPath: \"/my/project\", pluginName: \"my-plugin\" });\n *\n * // Set up test data\n * ctx.filesystem.setFile(\"/my/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(options?: SetupMockTauriOptions): MockTauriContext {\n const projectPath = options?.projectPath ?? \"/test/project\";\n const pluginName = options?.pluginName ?? \"test-plugin\";\n\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 const dialogTracker = createMockDialogTracker();\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 case \"project_file_exists\": {\n const projectPath = payload?.projectPath as string;\n const relativePath = payload?.relativePath as string;\n const fullPath = `${projectPath}/${relativePath}`;\n return filesystem.getFile(fullPath) !== undefined;\n }\n\n // ======================================================================\n // Plugin Storage Operations\n // ======================================================================\n case \"read_plugin_file\": {\n const pn = payload?.pluginName as string;\n const pp = payload?.projectPath as string;\n const rp = payload?.relativePath as string;\n const fullPath = `${pp}/.moss/plugins/${pn}/${rp}`;\n const file = filesystem.getFile(fullPath);\n if (file) {\n return file.content;\n }\n throw new Error(`Plugin file not found: ${fullPath}`);\n }\n\n case \"write_plugin_file\": {\n const pn = payload?.pluginName as string;\n const pp = payload?.projectPath as string;\n const rp = payload?.relativePath as string;\n const content = payload?.content as string;\n const fullPath = `${pp}/.moss/plugins/${pn}/${rp}`;\n filesystem.setFile(fullPath, content);\n return null;\n }\n\n case \"list_plugin_files\": {\n const pn = payload?.pluginName as string;\n const pp = payload?.projectPath as string;\n const prefix = `${pp}/.moss/plugins/${pn}/`;\n const allPaths = filesystem.listFiles();\n return allPaths\n .filter((p) => p.startsWith(prefix))\n .map((p) => p.substring(prefix.length));\n }\n\n case \"plugin_file_exists\": {\n const pn = payload?.pluginName as string;\n const pp = payload?.projectPath as string;\n const rp = payload?.relativePath as string;\n const fullPath = `${pp}/.moss/plugins/${pn}/${rp}`;\n return filesystem.getFile(fullPath) !== undefined;\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 // Dialog Operations\n // ======================================================================\n case \"show_plugin_dialog\": {\n const url = payload?.url as string;\n const title = payload?.title as string;\n const width = (payload?.width as number) ?? 500;\n const height = (payload?.height as number) ?? 400;\n\n // Track the dialog\n (dialogTracker.shownDialogs as Array<{ url: string; title: string; width: number; height: number }>).push({\n url,\n title,\n width,\n height,\n });\n\n // If a next result is configured, return it immediately\n if (dialogTracker.nextResult) {\n const result = dialogTracker.nextResult;\n (dialogTracker as { nextResult: MockDialogResult | null }).nextResult = null;\n return result;\n }\n\n // Default to cancelled\n return { type: \"cancelled\" };\n }\n\n case \"submit_dialog_result\": {\n const dialogId = payload?.dialogId as string;\n const result = payload?.result as MockDialogResult;\n\n if (result) {\n dialogTracker.submittedResults.set(dialogId, result);\n }\n\n return true;\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 __MOSS_INTERNAL_CONTEXT__?: {\n plugin_name: string;\n project_path: string;\n moss_dir: string;\n };\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 {\n window: {\n __TAURI__?: { core?: { invoke: typeof invoke } };\n __MOSS_INTERNAL_CONTEXT__?: {\n plugin_name: string;\n project_path: string;\n moss_dir: string;\n };\n };\n }).window;\n\n win.__TAURI__ = {\n core: { invoke },\n };\n\n // Set up internal context for context-aware APIs\n win.__MOSS_INTERNAL_CONTEXT__ = {\n plugin_name: pluginName,\n project_path: projectPath,\n moss_dir: `${projectPath}/.moss`,\n };\n\n return {\n filesystem,\n downloadTracker,\n urlConfig,\n binaryConfig,\n cookieStorage,\n browserTracker,\n dialogTracker,\n projectPath,\n pluginName,\n cleanup: () => {\n // Clear the mock Tauri interface and internal context\n delete win.__TAURI__;\n delete win.__MOSS_INTERNAL_CONTEXT__;\n filesystem.clear();\n downloadTracker.reset();\n urlConfig.reset();\n binaryConfig.reset();\n cookieStorage.clear();\n browserTracker.reset();\n dialogTracker.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.5.2",
3
+ "version": "0.5.4",
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",