Skip to content
docs
GitHub

SDK and lifecycle

The complete public PluginContext API and the lifecycle of an executable plugin.

Import types, HostContext and useHostContext() from @lomi-dev/plugin-sdk. This reference describes SDK 1.1.0-alpha.1 and host API 1 in the local Lomi source set. See Your first plugin for setup. The main SDK export requires the application runtime; do not execute it directly in Node.

Export a named activate function from the module referenced by entry:

import type { PluginContext } from '@lomi-dev/plugin-sdk';
export function activate(context: PluginContext) {
context.subscribe('context', (snapshot) => {
console.log(snapshot.workspaceName);
});
}

activate(context) may return nothing, a cleanup function, or a promise of either. The module may also export deactivate(): void | Promise<void>.

lazy activation starts when a declared view opens or a command runs. startup runs after the workbench is ready. Simultaneous triggers share one activation. Importing the package alone does not activate it, and safe startup prevents activation.

Phase Meaning
discovered A valid enabled package is waiting for activation
incompatible Metadata or contract prevents use
disabled Code is disabled, including during safe startup
activating Host prepares the revision, loads CSS and the module, then awaits activate()
active Activation succeeded
failed Activation failed; inspect the error and retry option
deactivating Host is releasing resources

An enabled lazy plugin can remain discovered until used. Later command, timer or rendering errors may be recorded while the plugin remains active. Read status and error together.

Register contributions and prepare resources in activate(). Do not await your own command through executeCommand() there: command execution waits for activation to finish. Call it from a later user action. Avoid cyclic cross-plugin command calls during activation too.

Member Contract
id Read-only package ID.
signal AbortSignal, aborted on deactivation or failed activation.
snapshot() Returns the current HostSnapshot.
registerView(id, component) Registers a declared React view receiving ViewProps. Returns Dispose.
registerCommand(id, handler) Registers a declared () => void | Promise<void> command. Returns Dispose.
registerFill(id, component) Registers a declared React component for an interface slot. Returns Dispose.
registerDirtyView(panelId, view) Registers unsaved-work handling for a retained panel owned by this plugin. Returns Dispose.
openView(id, state?) Opens one of this plugin’s declared views. Returns Promise<string> with its panel ID. Omitted state is null.
executeCommand(id) Runs a declared plugin command through activation and context checks. Returns Promise<void>.
subscribe(event, handler) Subscribes to a host event. Returns Dispose.
interval(callback, milliseconds) Registers a timer from 50 to 2147483647 ms. Returns Dispose.
add(dispose) Registers cleanup for a resource you own. Returns an idempotent Dispose.
assetUrl(path) Resolves a relative asset path inside the installed package revision.

Dispose is () => void. Call it when a registration is no longer needed; calling it again has no effect. The host also calls remaining disposers during deactivation. Registering the same contribution twice, registering undeclared IDs or using registration methods after deactivation throws.

openView() only opens views owned by the calling plugin and requires an open workspace. executeCommand() can address declared plugin commands, including commands from another plugin. Built-in application actions are not part of this public method’s contract.

useHostContext() subscribes a React component to the same current context returned by snapshot():

Field Type and meaning
projectPath string or null: active project folder.
workspaceName string or null: active workspace name.
activePanelId string or null: active panel, including a focused plugin sidebar.
viewType string or null: built-in panel type or declared plugin view ID.
appearance light or dark.
themeRevision Number identifying the native theme source revision.

This is the active host context, not a fixed project binding for each mounted view. Keep any document-specific path in your own retained model.

Event Payload
context HostSnapshot.
theme { revision: number, appearance: 'light' | 'dark' }.
contributions { owner: string }.

The theme event uses a local application revision. It can change after an appearance preference or preview update without changing snapshot().themeRevision. These are not interchangeable counters. The event bus does not stream terminal output.

Subscribing does not immediately deliver the current snapshot. Read snapshot() to initialize, then use subscribe('context', ...) for changes. contributions reports completed owner activation or deactivation; it is not a file-change or panel-state event.

The host aborts signal, runs registered disposers in reverse order and calls the optional module deactivate(). One cleanup error does not prevent the other registered cleanup functions from running.

Use context.interval() and context.subscribe() for host-managed resources. For a browser listener, add an explicit disposer:

const onResize = () => {
// Update plugin-owned layout measurements here.
};
window.addEventListener('resize', onResize);
context.add(() => window.removeEventListener('resize', onResize));

This fragment belongs inside activate(). Resources created outside the context remain your responsibility. Handle your own rejected promises and event-handler errors. A React error boundary handles render failures, not arbitrary asynchronous work. An infinite loop blocks the main JavaScript thread; a promise timeout cannot stop it.

There is no public SDK filesystem, terminal, HTTP, notification or secret-storage service. Do not present internal host imports or native commands as stable SDK methods.

Pass context.signal to operations supporting AbortSignal, such as browser fetch. After await, check context.signal.aborted before updating your model or registering more contributions. Handle expected cancellation without displaying it as a user-facing failure.

Intervals and subscriptions report callback errors, but an interval does not wait for its previous asynchronous run. Prevent overlapping work when an operation can take longer than the interval. context.add() accepts a synchronous Dispose; use module deactivate() for asynchronous shutdown.

Deactivation does not evict an ESM module. Module variables may survive reactivation of the same revision. Initialize and release them deliberately, and do not retain an old PluginContext with an aborted signal.

Import Exports / purpose
@lomi-dev/plugin-sdk Types: Json, Dispose, HostSnapshot, PanelDescriptor, ViewProps, DirtyView, CommandContext, PluginManifest, HostEvents, PluginContext, PluginModule; runtime: HostContext, useHostContext()
@lomi-dev/plugin-sdk/manifest parsePlugin(value) returns a manifest or throws; packagePath(value) and jsonState(value) validate by throwing; also pluginId and validShortcut
@lomi-dev/plugin-sdk/shortcuts validShortcut(value): boolean, ordered modifiers and keyNames
@lomi-dev/plugin-sdk/compatibility compatibility and limits, including API, runtime, compatible versions and package limits
@lomi-dev/plugin-sdk/build buildPlugin(options): Promise<string> returns the output directory; Node only
@lomi-dev/plugin-sdk/package safePath(root, path), packageFiles(root), validatePackage(root); asynchronous Node file checks
@lomi-dev/plugin-sdk/plugin.schema.json Editor schema
@lomi-dev/plugin-sdk/contract-fixtures.json Compatibility cases for host and tool integrators
@lomi-dev/plugin-sdk/package.json Installed package metadata

packageFiles() returns a map of relative names to bytes. validatePackage() returns { manifest, files }. safePath() checks existence, symlinks and directory containment. These functions can reject their promises.

Manifest and shortcut validators do not require a running application. Inside plugin code, the bundler only exposes the main SDK runtime entry; import tooling subpaths from author scripts, not plugin runtime code. See the manual example for building without the CLI.

Source: complete type declarations, host implementation.