Better plugin error handling

This commit is contained in:
Gregory Schier
2026-01-02 10:20:44 -08:00
parent 0146ee586f
commit 11694921e3
4 changed files with 533 additions and 381 deletions

850
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
import type { PluginContext } from '@yaakapp-internal/plugins';
import type { BootRequest, InternalEvent } from '@yaakapp/api'; import type { BootRequest, InternalEvent } from '@yaakapp/api';
import type { PluginContext } from '@yaakapp-internal/plugins';
import type { EventChannel } from './EventChannel'; import type { EventChannel } from './EventChannel';
import { PluginInstance, type PluginWorkerData } from './PluginInstance'; import { PluginInstance, type PluginWorkerData } from './PluginInstance';

View File

@@ -69,15 +69,25 @@ export class PluginInstance {
const fileChangeCallback = async () => { const fileChangeCallback = async () => {
await this.#mod?.dispose?.(); await this.#mod?.dispose?.();
this.#importModule(); this.#importModule();
await this.#mod?.init?.(this.#newCtx(workerData.context)); const ctx = this.#newCtx(workerData.context);
return this.#sendPayload( try {
workerData.context, await this.#mod?.init?.(ctx);
{ this.#sendPayload(
type: 'reload_response', workerData.context,
silent: false, {
}, type: 'reload_response',
null, silent: false,
); },
null,
);
} catch (err: unknown) {
ctx.toast.show({
message: `Failed to initialize plugin ${this.#workerData.bootRequest.dir.split('/').pop()}: ${err}`,
color: 'notice',
icon: 'alert_triangle',
timeout: 30000,
});
}
}; };
if (this.#workerData.bootRequest.watch) { if (this.#workerData.bootRequest.watch) {

View File

@@ -8,14 +8,15 @@ use crate::events::{
CallHttpAuthenticationActionArgs, CallHttpAuthenticationActionRequest, CallHttpAuthenticationActionArgs, CallHttpAuthenticationActionRequest,
CallHttpAuthenticationRequest, CallHttpAuthenticationResponse, CallHttpRequestActionRequest, CallHttpAuthenticationRequest, CallHttpAuthenticationResponse, CallHttpRequestActionRequest,
CallTemplateFunctionArgs, CallTemplateFunctionRequest, CallTemplateFunctionResponse, CallTemplateFunctionArgs, CallTemplateFunctionRequest, CallTemplateFunctionResponse,
CallWebsocketRequestActionRequest, CallWorkspaceActionRequest, EmptyPayload, ErrorResponse, CallWebsocketRequestActionRequest, CallWorkspaceActionRequest, Color, EmptyPayload,
FilterRequest, FilterResponse, GetFolderActionsResponse, GetGrpcRequestActionsResponse, ErrorResponse, FilterRequest, FilterResponse, GetFolderActionsResponse,
GetHttpAuthenticationConfigRequest, GetHttpAuthenticationConfigResponse, GetGrpcRequestActionsResponse, GetHttpAuthenticationConfigRequest,
GetHttpAuthenticationSummaryResponse, GetHttpRequestActionsResponse, GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse,
GetTemplateFunctionConfigRequest, GetTemplateFunctionConfigResponse, GetHttpRequestActionsResponse, GetTemplateFunctionConfigRequest,
GetTemplateFunctionSummaryResponse, GetThemesRequest, GetThemesResponse, GetTemplateFunctionConfigResponse, GetTemplateFunctionSummaryResponse, GetThemesRequest,
GetWebsocketRequestActionsResponse, GetWorkspaceActionsResponse, ImportRequest, ImportResponse, GetThemesResponse, GetWebsocketRequestActionsResponse, GetWorkspaceActionsResponse, Icon,
InternalEvent, InternalEventPayload, JsonPrimitive, PluginContext, RenderPurpose, ImportRequest, ImportResponse, InternalEvent, InternalEventPayload, JsonPrimitive,
PluginContext, RenderPurpose, ShowToastRequest,
}; };
use crate::native_template_functions::{template_function_keyring, template_function_secure}; use crate::native_template_functions::{template_function_keyring, template_function_secure};
use crate::nodejs::start_nodejs_plugin_runtime; use crate::nodejs::start_nodejs_plugin_runtime;
@@ -30,7 +31,7 @@ use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tauri::path::BaseDirectory; use tauri::path::BaseDirectory;
use tauri::{AppHandle, Manager, Runtime, WebviewWindow, is_dev}; use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow, is_dev};
use tokio::fs::read_dir; use tokio::fs::read_dir;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tokio::sync::mpsc::error::TrySendError; use tokio::sync::mpsc::error::TrySendError;
@@ -286,6 +287,21 @@ impl PluginManager {
} }
if let Err(e) = self.add_plugin(plugin_context, &plugin).await { if let Err(e) = self.add_plugin(plugin_context, &plugin).await {
warn!("Failed to add plugin {} {e:?}", plugin.directory); warn!("Failed to add plugin {} {e:?}", plugin.directory);
// Extract a user-friendly plugin name from the directory path
let plugin_name = plugin.directory.split('/').last().unwrap_or(&plugin.directory);
// Show a toast for all plugin failures
let toast = ShowToastRequest {
message: format!("Failed to start plugin '{}': {}", plugin_name, e),
color: Some(Color::Danger),
icon: Some(Icon::AlertTriangle),
timeout: Some(10000),
};
if let Err(emit_err) = app_handle.emit("show_toast", toast) {
error!("Failed to emit toast for plugin error: {emit_err:?}");
}
} }
} }