diff --git a/desktop/flipper-common/src/index.tsx b/desktop/flipper-common/src/index.tsx index b63073455..7c10cfdc2 100644 --- a/desktop/flipper-common/src/index.tsx +++ b/desktop/flipper-common/src/index.tsx @@ -41,6 +41,8 @@ export { UserUnauthorizedError, UserNotSignedInError, NoLongerConnectedToClientError, + UserError, + SystemError, isConnectivityOrAuthError, isError, isAuthError, diff --git a/desktop/flipper-common/src/utils/errors.tsx b/desktop/flipper-common/src/utils/errors.tsx index 05f83e306..781c24113 100644 --- a/desktop/flipper-common/src/utils/errors.tsx +++ b/desktop/flipper-common/src/utils/errors.tsx @@ -30,6 +30,24 @@ export function isConnectivityOrAuthError( ); } +export class SystemError extends Error { + name = 'SystemError'; + readonly context?: unknown; + constructor(msg: string, ...args: unknown[]) { + super(msg); + this.context = args; + } +} + +export class UserError extends Error { + name = 'UserError'; + readonly context?: unknown; + constructor(msg: string, ...args: unknown[]) { + super(msg); + this.context = args; + } +} + export class UnableToExtractClientQueryError extends Error { constructor(msg: string) { super(msg); diff --git a/desktop/flipper-server-companion/src/companion.tsx b/desktop/flipper-server-companion/src/companion.tsx index 72d2197d5..f46b69c0f 100644 --- a/desktop/flipper-server-companion/src/companion.tsx +++ b/desktop/flipper-server-companion/src/companion.tsx @@ -7,7 +7,7 @@ * @format */ -import {FlipperServer, Logger} from 'flipper-common'; +import {FlipperServer, Logger, UserError, SystemError} from 'flipper-common'; import {BaseDevice} from 'flipper-frontend-core'; import {_SandyPluginDefinition} from 'flipper-plugin'; import {HeadlessClient} from './HeadlessClient'; @@ -121,8 +121,9 @@ export class FlipperServerCompanion { destroyClient(clientId: string) { const client = this.clients.get(clientId); if (!client) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.destroyClient -> client not found', + client, ); } client.destroy(); @@ -136,8 +137,9 @@ export class FlipperServerCompanion { destroyDevice(deviceSerial: string) { const device = this.devices.get(deviceSerial); if (!device) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.destroyDevice -> device not found', + deviceSerial, ); } device.destroy(); @@ -159,8 +161,9 @@ export class FlipperServerCompanion { const clientInfo = await this.flipperServer.exec('client-find', clientId); if (!clientInfo) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.createHeadlessClientIfNeeded -> client not found', + clientId, ); } @@ -206,8 +209,9 @@ export class FlipperServerCompanion { deviceSerial, ); if (!deviceInfo) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.createHeadlessDeviceIfNeeded -> device not found', + deviceSerial, ); } @@ -228,8 +232,9 @@ export class FlipperServerCompanion { const handler: (...args: any[]) => Promise = this.commandHandler[event]; if (!handler) { - throw new Error( - `Unimplemented FlipperServerCompanion command: ${event}`, + throw new UserError( + `Unimplemented FlipperServerCompanion command`, + event, ); } const result = await handler(...args); @@ -267,14 +272,18 @@ export class FlipperServerCompanion { const pluginInstance = client.sandyPluginStates.get(pluginId); if (!pluginInstance) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-plugin-start -> plugin not found', + clientId, + pluginId, ); } if (!client.connected.get()) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-plugin-start -> client not connected', + clientId, + pluginId, ); } @@ -283,15 +292,19 @@ export class FlipperServerCompanion { 'companion-plugin-stop': async (clientId, pluginId) => { const client = this.clients.get(clientId); if (!client) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-plugin-stop -> client not found', + clientId, + pluginId, ); } const pluginInstance = client.sandyPluginStates.get(pluginId); if (!pluginInstance) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-plugin-stop -> plugin not found', + clientId, + pluginId, ); } @@ -302,8 +315,10 @@ export class FlipperServerCompanion { } if (!pluginInstance.activated) { - throw new Error( + throw new SystemError( 'FlipperServerCompanion.companion-plugin-stop -> plugin not activated', + clientId, + pluginId, ); } @@ -312,33 +327,53 @@ export class FlipperServerCompanion { 'companion-plugin-exec': async (clientId, pluginId, api, params) => { const client = this.clients.get(clientId); if (!client) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-plugin-exec -> client not found', + clientId, + pluginId, + api, + params, ); } const pluginInstance = client.sandyPluginStates.get(pluginId); if (!pluginInstance) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-plugin-exec -> plugin not found', + clientId, + pluginId, + api, + params, ); } if (!client.connected.get()) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-plugin-exec -> client not connected', + clientId, + pluginId, + api, + params, ); } if (!pluginInstance.companionApi) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-plugin-exec -> plugin does not expose API', + clientId, + pluginId, + api, + params, ); } if (typeof pluginInstance.companionApi[api] !== 'function') { - throw new Error( + throw new SystemError( 'FlipperServerCompanion.companion-plugin-exec -> plugin does not expose requested API method or it is not callable', + clientId, + pluginId, + api, + params, ); } @@ -368,20 +403,26 @@ export class FlipperServerCompanion { const pluginDefinition = this.loadablePlugins.get(pluginId); if (!pluginDefinition) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-device-plugin-start -> plugin definition not found', + deviceSerial, + pluginId, ); } if (!device.connected.get()) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-device-plugin-start -> device not connected', + deviceSerial, + pluginId, ); } if (!device.supportsPlugin(pluginDefinition)) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-device-plugin-start -> device does not support plugin', + deviceSerial, + pluginId, ); } @@ -391,27 +432,35 @@ export class FlipperServerCompanion { 'companion-device-plugin-stop': async (deviceSerial, pluginId) => { const device = this.devices.get(deviceSerial); if (!device) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-device-plugin-stop -> client not found', + deviceSerial, + pluginId, ); } const pluginInstance = device.sandyPluginStates.get(pluginId); if (!pluginInstance) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-device-plugin-stop -> plugin not found', + deviceSerial, + pluginId, ); } if (!device.connected.get()) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-device-plugin-stop -> device not connected', + deviceSerial, + pluginId, ); } if (!pluginInstance.activated) { - throw new Error( + throw new SystemError( 'FlipperServerCompanion.companion-device-plugin-stop -> plugin not activated', + deviceSerial, + pluginId, ); } @@ -425,33 +474,53 @@ export class FlipperServerCompanion { ) => { const device = this.devices.get(deviceSerial); if (!device) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-device-plugin-exec -> device not found', + deviceSerial, + pluginId, + api, + params, ); } const pluginInstance = device.sandyPluginStates.get(pluginId); if (!pluginInstance) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-device-plugin-exec -> plugin not found', + deviceSerial, + pluginId, + api, + params, ); } if (!device.connected.get()) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-plugin-exec -> client not connected', + deviceSerial, + pluginId, + api, + params, ); } if (!pluginInstance.companionApi) { - throw new Error( + throw new UserError( 'FlipperServerCompanion.companion-plugin-exec -> plugin does not expose API', + deviceSerial, + pluginId, + api, + params, ); } if (typeof pluginInstance.companionApi[api] !== 'function') { - throw new Error( + throw new SystemError( 'FlipperServerCompanion.companion-plugin-exec -> plugin does not expose requested API method or it is not callable', + deviceSerial, + pluginId, + api, + params, ); } diff --git a/desktop/flipper-server/src/startSocketServer.tsx b/desktop/flipper-server/src/startSocketServer.tsx index 9d18fd144..94d99f40c 100644 --- a/desktop/flipper-server/src/startSocketServer.tsx +++ b/desktop/flipper-server/src/startSocketServer.tsx @@ -14,6 +14,8 @@ import { ExecResponseErrorWebSocketMessage, ServerEventWebSocketMessage, GenericWebSocketError, + UserError, + SystemError, } from 'flipper-common'; import {FlipperServerImpl} from 'flipper-server-core'; import {WebSocketServer} from 'ws'; @@ -99,6 +101,20 @@ export function startSocketServer( } }) .catch((error: any) => { + if (error instanceof UserError) { + console.warn( + `flipper-server.startSocketServer.exec: ${error.message}`, + error.context, + error.stack, + ); + } + if (error instanceof SystemError) { + console.error( + `flipper-server.startSocketServer.exec: ${error.message}`, + error.context, + error.stack, + ); + } if (connected) { // TODO: Serialize error // TODO: log if verbose console.warn('Failed to handle response', error);