Plugin Dependencies & Lifecycles
Plugin Dependencies & Lifecycle
Plugins can declare dependencies and participate in the Phestus runtime lifecycle.
Dependencies describe the components required by a plugin, while lifecycle hooks allow a plugin to perform initialization and shutdown work.
Dependencies
Plugin dependencies are declared in the manifest:
dependencies: [
{
type: "module",
slug: "event",
version: "0.1.0",
},
],
A dependency has three required properties:
{
type: "module",
slug: "event",
version: "0.1.0",
}
Dependency Types
Phestus supports three dependency types:
type: "plugin"
type: "module"
type: "provider"
For example:
dependencies: [
{
type: "plugin",
slug: "database",
version: "0.1.0",
},
{
type: "module",
slug: "event",
version: "0.1.0",
},
{
type: "provider",
slug: "redis",
version: "0.1.0",
},
],
Each dependency identifies the component and the version required by the plugin.
Why Declare Dependencies?
Dependencies make the requirements of a plugin explicit.
Instead of assuming that another component exists, a plugin can declare the relationship in its manifest:
dependencies: [
{
type: "module",
slug: "event",
version: "0.1.0",
},
],
This allows the Phestus runtime to resolve plugin dependencies before execution.
Plugin dependencies are resolved through the plugin resolver and dependency resolver.
Lifecycle
Plugins can define two lifecycle hooks:
initialize?(context: PhestusContext): Promise<void>;
shutdown?(context: PhestusContext): Promise<void>;
These hooks are optional.
A plugin does not need to implement either hook if it has no plugin-level lifecycle work to perform.
Initialization
The initialize hook runs when the Phestus runtime initializes the plugin.
const plugin: PhestusPlugin = {
manifest: {
slug: "example",
name: "Example",
version: "0.1.0",
provides: [],
},
async initialize(context) {
context.logger.info(
"Example plugin initialized.",
);
},
};
The context provides access to shared runtime services:
context.service
context.logger
context.eventBus
Use initialization for work that needs to happen when the plugin becomes active.
For example:
async initialize(context) {
context.logger.info(
"Starting Example Plugin",
);
// Plugin initialization work...
}
Shutdown
Plugins can also clean up resources when Phestus shuts down:
const plugin: PhestusPlugin = {
manifest: {
slug: "example",
name: "Example",
version: "0.1.0",
provides: [],
},
async shutdown(context) {
context.logger.info(
"Example plugin shutting down.",
);
// Cleanup work...
},
};
Shutdown is useful for releasing resources created by the plugin during initialization.
Examples include:
- Closing connections
- Stopping background processes
- Removing subscriptions
- Cleaning up plugin-owned resources
Keeping Lifecycle Work in the Right Place
Plugins should use lifecycle hooks for plugin-level behavior.
Modules and providers may have their own lifecycle responsibilities, so a plugin should not use initialize() as a replacement for those abstractions.
A useful separation is:
Plugin
└── Coordinates plugin-level lifecycle
Module
└── Owns capability lifecycle
Provider
└── Owns implementation lifecycle
This keeps each component responsible for its own behavior.
Complete Example
import type {
PhestusPlugin,
} from "@phestus/sdk";
export const examplePlugin: PhestusPlugin = {
manifest: {
slug: "example",
name: "Example Plugin",
version: "0.1.0",
dependencies: [
{
type: "module",
slug: "event",
version: "0.1.0",
},
],
provides: [
{
moduleSlug: "event",
providerSlug: "example",
},
],
},
providers: [
exampleProvider,
],
async initialize(context) {
context.logger.info(
"Example Plugin initialized.",
);
},
async shutdown(context) {
context.logger.info(
"Example Plugin shutting down.",
);
},
};
The resulting plugin has four responsibilities:
Example Plugin
│
├── Manifest
│ ├── Identity
│ ├── Dependencies
│ └── Capabilities
│
├── Provider
│ └── Example Event Provider
│
├── Initialization
│
└── Shutdown
Together, these pieces define the plugin's complete integration with the Phestus runtime.