FDO_SDK is the abstract base class every plugin must extend. When you instantiate your subclass, the SDK automatically registers the plugin with PluginRegistry and emits an init event to the FDO host.
Complete example
import { FDO_SDK, PluginRegistry } from "@anikitenko/fdo-sdk";
import type { PluginMetadata } from "@anikitenko/fdo-sdk";
export class MyPlugin extends FDO_SDK {
public metadata: PluginMetadata = {
id: "my-plugin",
name: "My Plugin",
version: "1.0.0",
author: "acme",
description: "A minimal FDO plugin example.",
icon: "cube",
};
init(): void {
PluginRegistry.registerHandler("my-plugin.greet", async (data) => {
return { message: `Hello, ${(data as { name: string }).name}!` };
});
this.log("Plugin initialized.");
}
render(): string {
return `<div class="fdo-plugin"><h1>Hello from MyPlugin</h1></div>`;
}
renderOnLoad(): string {
return `() => { console.log("MyPlugin UI loaded"); }`;
}
}
export default new MyPlugin();
Static members
API_VERSION
public static readonly API_VERSION: string = "1.0.0"
The current SDK API version in major.minor.patch format. The FDO host validates major-version compatibility against this value at plugin init time.
TYPE_TAG
static readonly TYPE_TAG: symbol
A unique Symbol used internally to identify FDO_SDK instances. You do not use this directly.
Constructor
Instantiating your plugin subclass automatically registers it with PluginRegistry and emits the init event to the host. You should never call super() with arguments — just call super() from your subclass constructor (or omit the constructor entirely if you have no setup logic).
Instantiate your plugin exactly once, at module load time. Registering multiple instances of the same plugin class in one module produces undefined behavior.
Lifecycle methods
init()
Called by the FDO host after the plugin is loaded. You must override this method — the base implementation throws immediately.
Use init() to register handlers, configure stores, and perform any one-time setup your plugin needs.
The base class throws an Error if init() is not overridden. Your subclass must provide its own implementation.
init(): void {
PluginRegistry.registerHandler("my-plugin.ping", async () => {
return { pong: true };
});
this.log("ready");
}
render()
Called by the FDO host to obtain the plugin’s UI. You must override this method — the base implementation throws immediately.
Return a complete HTML string. The host renders it inside a sandboxed iframe. render() must be synchronous; returning a Promise throws a runtime error.
The base class throws an Error if render() is not overridden. Do not return a Promise — async render is not supported.
render(): string {
return `
<div style="font-family: sans-serif; padding: 1rem;">
<h2>Status: OK</h2>
<p>Plugin is running.</p>
</div>
`;
}
renderOnLoad()
public renderOnLoad(): string
Override this method to return a JavaScript function string that the host executes after your plugin UI loads in the iframe. The default implementation returns '() => {}'.
The return value must be a synchronous string representation of a function. The host evaluates it in the iframe context.
renderOnLoad(): string {
return `() => {
document.getElementById("status").textContent = "Loaded at " + new Date().toLocaleTimeString();
}`;
}
serializeRender()
public serializeRender(): string
Serializes the output of render() for host transport. The FDO host calls this method — you do not call it yourself.
Override render(), not this method. If render() returns a Promise, this method throws.
Returns: A JSON-serialized string of the render() output.
serializeRenderOnLoad()
public serializeRenderOnLoad(): string
Serializes the output of renderOnLoad() for host transport. The FDO host calls this method — you do not call it yourself.
Override renderOnLoad(), not this method. If renderOnLoad() returns a Promise, this method throws.
Returns: A JSON-serialized string of the renderOnLoad() output.
Logging methods
All logging methods write to the plugin’s scoped log file and are safe to call from anywhere inside your plugin class.
log()
public log(message: string): void
The message to write at the default log level.
this.log("Handler registered successfully.");
error()
public error(error: Error): void
An Error object. The message and stack trace are both captured.
try {
riskyOperation();
} catch (err) {
this.error(err instanceof Error ? err : new Error(String(err)));
}
info()
public info(message: string, ...meta: unknown[]): void
Optional additional metadata values to attach to the log entry.
this.info("User action received", { action: "click", target: "submit" });
warn()
public warn(message: string, ...meta: unknown[]): void
Optional additional metadata values.
this.warn("Config value missing, using default.", { key: "timeout" });
debug()
public debug(message: string, ...meta: unknown[]): void
Optional additional metadata values.
this.debug("Handler invoked", { handler: "my-plugin.greet", input });
verbose()
public verbose(message: string, ...meta: unknown[]): void
The verbose-level message.
Optional additional metadata values.
this.verbose("Detailed trace", { step: 1, payload });
silly()
public silly(message: string, ...meta: unknown[]): void
The silly-level message (most verbose).
Optional additional metadata values.
this.silly("Raw buffer dump", rawBytes);
Event tracking
event()
public event(name: string, payload?: Record<string, unknown>): string
Emits a named structured event to the plugin log and returns a correlation ID you can use to trace related log entries.
The event name. Use dot-separated namespacing (e.g., "my-plugin.action.start").
Optional key-value metadata to attach to the event. Defaults to {}.
Returns: string — a correlation ID for the emitted event.
const correlationId = this.event("my-plugin.export.start", {
format: "csv",
rowCount: 1000,
});
this.info("Export started", { correlationId });
Log directory
getLogDirectory()
public getLogDirectory(): string
Returns the absolute path of the directory where the plugin’s log files are written. Use this to display or report the log location to users.
Returns: string — the absolute path to the log directory.
const logDir = this.getLogDirectory();
this.info("Logs are at", { path: logDir });