Features
Plugins
Current plugin-author entry points.
A plugin is a function that returns a stack member. The root package exports the authoring helpers;
contracts and substrate are internal source modules, not package subpaths for app or plugin
author imports.
A plugin returned by definePlugin(...) is a
{ id, role, section, dependsOn?, start, capabilities? } bundle.
id— graph identity and the default resource id; the resolved value is published under this id.role— lifecycle classification.'service'means a long-lived resource (a container, an HTTP process);'task'means the body runs once at acquire and reaches "done" whenstartresolves (account, action, package publish). The supervisor uses the role to decide stop semantics.section— required dashboard bucket the plugin's rows belong to:'service' | 'package' | 'account' | 'action' | 'app' | 'other'. The supervisor stamps this onto every row at acquire-time so the renderer never has to pattern-match on plugin-name substrings. A long-lived resource is usually'service'.dependsOn— a single plugin/ref, an array, or an object of plugin/refs. Resolved upstream values are passed tostartin the same shape — single, positional array, or keyed object.start— theEffectthat produces the resolved value. May yield substrate services (ContainerRuntimeService,IdentityContext,StackPathsService) the supervisor provides.capabilities— optional array (or factory) of capability decls:codegenable,snapshotable,routable, strategy contributors, etc.
import { Effect } from 'effect';
import { account, codegenable, definePlugin, sui } from '@mysten-incubation/devstack';
interface CacheResolved {
readonly url: string;
readonly tokenAddress: string;
}
export const cache = (signer: ReturnType<typeof account>) =>
definePlugin({
id: 'cache',
role: 'service',
section: 'service',
// Resolved values arrive in the shape of `dependsOn`. Here the object
// keys (`sui`, `signer`) match the keys the start body destructures.
dependsOn: { sui: sui(), signer },
start: ({ sui, signer }) =>
Effect.succeed({
url: 'http://127.0.0.1:6379',
tokenAddress: signer.address,
} satisfies CacheResolved),
capabilities: ({ value }) => [
codegenable({
emitterName: 'cache',
outputPath: 'cache.ts',
emit: (ctx) =>
Effect.sync(() => {
ctx.exportConst('cache', value);
return ctx.done();
}),
}),
],
});Compose the plugin like any built-in factory:
import { account, defineDevstack, sui } from '@mysten-incubation/devstack';
import { cache } from './devstack/cache.js';
const publisher = account('publisher');
export default defineDevstack({ members: [sui(), publisher, cache(publisher)] });Use direct plugin/resource references for cross-plugin dependencies — accept the upstream plugin as
a value and put it in dependsOn. The resolved value arrives in start in the same shape
dependsOn declared.