/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format */ import assert from 'assert'; import {assertNotNull} from '../comms/Utilities'; import { FlipperServerForServerAddOn, ServerAddOnCleanup, ServerAddOn as ServerAddOnFn, ServerAddOnStartDetails, } from 'flipper-common'; import {ServerAddOnDesktopToModuleConnection} from './ServerAddOnDesktopToModuleConnection'; import {ServerAddOnModuleToDesktopConnection} from './ServerAddOnModuleToDesktopConnection'; // @ts-ignore import defaultPlugins from '../defaultPlugins'; interface ServerAddOnModule { default: ServerAddOnFn; } const loadPlugin = ( pluginName: string, details: ServerAddOnStartDetails, ): ServerAddOnModule => { console.debug('loadPlugin', pluginName, details); if (details.isBundled) { const bundledPlugin = defaultPlugins[pluginName]; assertNotNull( bundledPlugin, `loadPlugin (isBundled = true) -> plugin ${pluginName} not found.`, ); return bundledPlugin; } assertNotNull( details.path, `loadPlugin (isBundled = false) -> server add-on path is empty plugin ${pluginName}.`, ); // eslint-disable-next-line no-eval const serverAddOnModule = eval(`require("${details.path}")`); return serverAddOnModule; }; export class ServerAddOn { private owners: Set; constructor( public readonly pluginName: string, private readonly cleanup: ServerAddOnCleanup, public readonly connection: ServerAddOnDesktopToModuleConnection, initialOwner: string, ) { this.owners = new Set([initialOwner]); } static async start( pluginName: string, details: ServerAddOnStartDetails, initialOwner: string, flipperServer: FlipperServerForServerAddOn, ): Promise { console.info('ServerAddOn.start', pluginName, details); const {default: serverAddOn} = loadPlugin(pluginName, details); assertNotNull(serverAddOn); assert( typeof serverAddOn === 'function', `ServerAddOn ${pluginName} must export "serverAddOn" function as a default export.`, ); const serverAddOnModuleToDesktopConnection = new ServerAddOnModuleToDesktopConnection(pluginName); const cleanup = await serverAddOn(serverAddOnModuleToDesktopConnection, { flipperServer, }); assert( typeof cleanup === 'function', `ServerAddOn ${pluginName} must return a clean up function, instead it returned ${typeof cleanup}.`, ); const desktopToModuleConnection = new ServerAddOnDesktopToModuleConnection( serverAddOnModuleToDesktopConnection, flipperServer, ); return new ServerAddOn( pluginName, cleanup, desktopToModuleConnection, initialOwner, ); } addOwner(owner: string) { this.owners.add(owner); } removeOwner(owner: string) { const ownerExisted = this.owners.delete(owner); if (!this.owners.size && ownerExisted) { return this.stop(); } } async stop() { console.info('ServerAddOn.stop', this.pluginName); await this.cleanup(); } }