Skip to content

Plugin

import * as Plugin from '@dxos/app-framework/Plugin';

Plugin defines a plugin: its metadata and the modules it is made of. A plugin does nothing by itself; its modules contribute everything it adds to the app.

A plugin’s metadata names it. The key is a reverse-domain DXN whose last segment is camelCase.

import * as
import Plugin
Plugin
from '@dxos/app-framework/Plugin';
import {
import DXN
DXN
} from '@dxos/echo';
const
const meta: {
readonly profile: {
readonly key: string;
readonly name: string;
readonly description?: string | undefined;
readonly homePage?: string | undefined;
readonly source?: string | undefined;
readonly tags?: readonly string[] | undefined;
readonly screenshots?: readonly {
readonly light?: string | undefined;
readonly dark?: string | undefined;
}[] | undefined;
readonly icon?: {
readonly key: string;
readonly hue?: string | undefined;
} | undefined;
readonly dependsOn?: readonly string[] | undefined;
readonly spec?: string | undefined;
readonly createdAt?: string | undefined;
readonly author?: string | undefined;
};
readonly release?: {
...;
} | undefined;
}
meta
=
import Plugin
Plugin
.
const makeMeta: (options: Plugin.MakeMetaOptions) => Plugin.Meta

Constructs a bundled-plugin

Meta

from a canonical DXN. profile.key is the bare NSID derived from key.

makeMeta
({
key: DXN.DXN
key
:
import DXN
DXN
.
const make: <"com.example.plugin.hello">(nsid: "com.example.plugin.hello", version?: string) => DXN.DXN

Constructs a DXN from an NSID (and optional version). Throws if the result is not a valid DXN. Use tryMake for non-throwing string parsing.

Static NSID strings are validated at compile time via

Name

: the final segment must be camelCase (no hyphens). Template-literal strings with a runtime prefix are accepted here but still validated at runtime.

@example make('org.dxos.type.calendar') → 'dxn:org.dxos.type.calendar'

@example make('org.dxos.type.calendar', '1.0.0') → 'dxn:org.dxos.type.calendar:1.0.0'

make
('com.example.plugin.hello'),
name: string
name
: 'Hello',
description?: string | undefined
description
: 'Contributes one greeting.',
});

Plugins in a package with a dx.config.ts describe the same metadata there, and Plugin.config derives the meta. Create a plugin shows that form.

Add modules and choose their activation events

Section titled “Add modules and choose their activation events”

A module declares the capabilities it provides and requires, and optionally the event it activatesOn. The plugin manager activates a module when its event fires and every capability it requires has been contributed. A module with no activatesOn activates when the app is idle after startup.

import * as
import Effect
Effect
from 'effect/Effect';
import * as
import ActivationEvents
ActivationEvents
from '@dxos/app-framework/ActivationEvents';
import * as
import Capability
Capability
from '@dxos/app-framework/Capability';
const
const Greeting: Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">
Greeting
=
import Capability
Capability
.
const make: <string>() => <S>(identifier: NsidParam<S>) => Capability.MultiTag<string, S> (+1 overload)
make
<string>()(
'com.example.plugin.hello.capability.greeting',
);
const
const GreetingModule: Capability.Module<void>
GreetingModule
=
import Capability
Capability
.
const inlineModule: <readonly [Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">], readonly [], void, void>(name: string, spec: ModuleSpec<readonly [Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">], readonly [], void, void>, activate: (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">> | readonly Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">>[], Error, Capability.Requirements<...>>) => Capability.Module<...>

Helper to define an eager module body with the same spec-carrying shape as

lazyModule

— for bodies that are plain values (translations, schema) or too small to justify a chunk, so plugin definitions stay a uniform chain of spec-carrying modules.

@param ― name The module name — used to auto-compute module IDs.

@param ― spec The requires/provides declaration.

@param ― activate The module body.

inlineModule
(
'Greeting',
{
provides: readonly [Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">]
provides
: [
const Greeting: Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">
Greeting
],
activatesOn?: Events | undefined

Activates when this runtime event fires instead of during the dependency pass.

activatesOn
:
import ActivationEvents
ActivationEvents
.
const Startup: ActivationEvent

Fired when the app is started. Defined in core; see

ActivationEvent$.Startup

.

Startup
},
() =>
import Effect
Effect
.
const succeed: <Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">>>(value: Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">>) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">>, never, never>

Creates an Effect that always succeeds with a given value.

When to use

Use when an effect should complete successfully with a specific value without any errors or external dependencies.

Example (Creating a successful effect)

import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)
Effect.runSync(success) // => 42

@see ― fail to create an effect that represents a failure.

@stability ― stable

@category ― constructors

@since ― 2.0.0

succeed
(
import Capability
Capability
.
const contribute: <Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">>(capability: Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">, implementation: string) => Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">> (+1 overload)
contribute
(
const Greeting: Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">
Greeting
, 'Hello, world')),
);

Capability.inlineModule keeps the body in the same file. Capability.lazyModule loads it from another chunk when the module activates, so a plugin with many modules costs little at startup.

import * as
import Effect
Effect
from 'effect/Effect';
import * as
import ActivationEvents
ActivationEvents
from '@dxos/app-framework/ActivationEvents';
import * as
import Capability
Capability
from '@dxos/app-framework/Capability';
// The module body loads only when its event fires. The loader's default export
// is checked against `provides`.
const
const LazyGreetingModule: Capability.Module<void>
LazyGreetingModule
=
import Capability
Capability
.
const lazyModule: <readonly [Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">], readonly [], void, void>(name: string, spec: ModuleSpec<readonly [Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">], readonly [], void, void>, loader: Capability.LoadModule<void, readonly [], readonly [Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">]>) => Capability.Module<void>

Helper to define a lazily loaded module body with an eager requires/provides spec. The spec is available without importing the chunk (dependency ordering happens before code-splitting resolves) and the loaded default export is type-checked against it. The returned

Module

is opaque with respect to requires/provides/props, parameterized only by Options — this is what eliminates TS2883 at every call site. The lazy pairing of

makeModule

.

@param ― name The export name (e.g., 'AppGraphBuilder') - used to auto-compute module IDs.

@param ― spec The requires/provides declaration, matching the module authoring site.

@param ― loader The lazy loader function.

lazyModule
(
'LazyGreeting',
{
provides: readonly [Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">]
provides
: [
const Greeting: Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">
Greeting
],
activatesOn?: Events | undefined

Activates when this runtime event fires instead of during the dependency pass.

activatesOn
:
import ActivationEvents
ActivationEvents
.
const Idle: ActivationEvent

Fired once by the host at main-thread idle after the app is interactive.

For REGISTRATION contributions only — graph builders, operation handler sets, settings: the app-wide indexes a user can reach from anywhere (the navtree, the operation registry, a settings read) and which therefore no single surface can gate. A feature's own modules ride its plugin start event, fired when its surface renders; putting a feature body here instead is what turned the former DeferredStartup into a second startup pass.

This is also what omitting activatesOn normalizes to, so a module that must run at boot has to say activatesOn: ActivationEvents.Startup explicitly. Defined in core; see

ActivationEvent$.Idle

.

Idle
},
async () => ({
default: (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">> | readonly Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">>[], Error, Capability.Requirements<readonly []>>
default
:
import Capability
Capability
.
const makeModule: <void, Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">>, never, never>(fn: (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">>, never, Scope>) => (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">>, never, Scope>

Helper to define a capability module with explicit typing. Wraps the default export function to provide better type inference and make the pattern explicit.

This helper provides explicit typing for the module activation function, making it clear that the function should:

  • Access declared requires via yield* (or the ambient Capability.Service/Plugin.Service)
  • Return an array of typed

Contribution

s (see

contribute

/

contributeAll

)

@example

// Module without options - single capability
export default Capability.makeModule(
Effect.fnUntraced(function* () {
const client = yield* ClientCapabilities.Client;
return Capability.contribute(Capabilities.SettingsStore, store);
})
);
// Module with multiple capabilities
export default Capability.makeModule(
Effect.fnUntraced(function* () {
return [
Capability.contribute(Capabilities.SettingsStore, store),
Capability.contribute(Capabilities.Translations, translations),
];
})
);
// Module with required options (context accessed via layer)
export default Capability.makeModule(
Effect.fnUntraced(function* (props: { observability: boolean }) {
const invoker = yield* Capabilities.OperationInvoker;
return Capability.contribute(Capabilities.OperationHandler, ...);
})
);
// Module with scoped resources (closed automatically on deactivation)
export default Capability.makeModule(
Effect.fnUntraced(function* () {
const scope = yield* Scope.Scope;
yield* Scope.addFinalizer(scope, Effect.sync(() => cleanup()));
return Capability.contribute(Capabilities.MyCapability, implementation);
})
);

makeModule
(() =>
import Effect
Effect
.
const succeed: <Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">>>(value: Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">>) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">>, never, never>

Creates an Effect that always succeeds with a given value.

When to use

Use when an effect should complete successfully with a specific value without any errors or external dependencies.

Example (Creating a successful effect)

import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)
Effect.runSync(success) // => 42

@see ― fail to create an effect that represents a failure.

@stability ― stable

@category ― constructors

@since ― 2.0.0

succeed
(
import Capability
Capability
.
const contribute: <Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">>(capability: Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">, implementation: string) => Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.hello.capability.greeting", "multi">> (+1 overload)
contribute
(
const Greeting: Capability.MultiTag<string, "com.example.plugin.hello.capability.greeting">
Greeting
, 'Hello, later')),
),
}),
);

Plugin.define starts a plugin from its metadata, Plugin.addModule adds each module, and Plugin.make returns the factory the plugin manager calls.

import * as
import Plugin
Plugin
from '@dxos/app-framework/Plugin';
export const
const HelloPlugin: Plugin.PluginFactory<void>
HelloPlugin
=
import Plugin
Plugin
.
const define: <void>(meta: Plugin.Meta) => Plugin.PluginBuilder<void>

Creates a new PluginBuilder to start building a plugin.

define
(
const meta: {
readonly profile: {
readonly key: string;
readonly name: string;
readonly description?: string | undefined;
readonly homePage?: string | undefined;
readonly source?: string | undefined;
readonly tags?: readonly string[] | undefined;
readonly screenshots?: readonly {
readonly light?: string | undefined;
readonly dark?: string | undefined;
}[] | undefined;
readonly icon?: {
readonly key: string;
readonly hue?: string | undefined;
} | undefined;
readonly dependsOn?: readonly string[] | undefined;
readonly spec?: string | undefined;
readonly createdAt?: string | undefined;
readonly author?: string | undefined;
};
readonly release?: {
...;
} | undefined;
}
meta
).
Pipeable.pipe<Plugin.PluginBuilder<void>, Plugin.PluginBuilder<void>, Plugin.PluginFactory<void>>(this: Plugin.PluginBuilder<void>, ab: (_: Plugin.PluginBuilder<void>) => Plugin.PluginBuilder<void>, bc: (_: Plugin.PluginBuilder<void>) => Plugin.PluginFactory<void>): Plugin.PluginFactory<void> (+21 overloads)
pipe
(
import Plugin
Plugin
.
function addModule<void>(module: Capability.Module<void> | undefined, options?: {
id?: string;
}): (builder: Plugin.PluginBuilder<void>) => Plugin.PluginBuilder<void> (+3 overloads)

Adds a module to a plugin builder. Supports both pipeline and direct call styles.

Accepts either:

  • A spec-carrying module (

Capability.lazyModule

/

Capability.inlineModule

): requires/provides/activatesOn/props all come from the module's own spec (declared where it is authored), so the call needs only an optional id override.

  • An inline authoring record (or a function receiving plugin options): declares provides/requires (dependency mode) or activatesOn + requires/provides (runtime-event mode); the activate effect's environment is constrained to the declared requires and its return must cover the declared provides.

addModule
(
const GreetingModule: Capability.Module<void>
GreetingModule
),
import Plugin
Plugin
.
function make<T>(builder: Plugin.PluginBuilder<T>): Plugin.PluginFactory<T>

Creates a Plugin from a builder. Supports both pipeline and direct call styles. Always returns a factory function (options: T) => Plugin. When T is void, the function takes no arguments: () => Plugin.

make
,
);

A module body returns the contributions it declared in provides. The compiler checks the match: a missing or undeclared contribution is a type error at Plugin.addModule. Capability covers declaring and requesting capabilities.

A plugin that is published describes itself in a dx.config.ts manifest. Plugin.config takes the metadata, the registry’s fields such as tags, and the modules, each made with a maker from AppCapability that names the file to load. It returns meta and make, so the registry reads the plugin without importing its modules.

dx.config.ts
import * as
import Plugin
Plugin
from '@dxos/app-framework/Plugin';
import * as
import AppCapability
AppCapability
from '@dxos/app-toolkit/AppCapability';
import * as
import AppSurface
AppSurface
from '@dxos/app-toolkit/AppSurface';
import * as
import SpaceCapability
SpaceCapability
from '@dxos/plugin-space/SpaceCapability';
import {
const StashNamespace: {
key: "com.example.plugin.stash";
make: <T>() => <const N extends string>(name: N) => MultiTag<T, `com.example.plugin.stash.capability.${N}`>;
makeSingleton: <T>() => <const N extends string>(name: N) => Tag<T, `com.example.plugin.stash.capability.${N}`>;
}
StashNamespace
} from './src/namespace.ts';
const
const config: Plugin.PluginConfig<void>
config
:
import Plugin
Plugin
.
type PluginConfig<T = void> = {
readonly plugin: {
readonly key: string;
readonly name: string;
readonly description?: string | undefined;
readonly author?: string | undefined;
readonly homePage?: string | undefined;
readonly source?: string | undefined;
readonly screenshots?: readonly {
readonly light?: string | undefined;
readonly dark?: string | undefined;
}[] | undefined;
readonly tags?: readonly string[] | undefined;
readonly icon?: {
readonly key: string;
readonly hue?: string | undefined;
} | undefined;
readonly spec?: string | undefined;
readonly dependsOn?: readonly string[] | undefined;
};
readonly publish?: {
...;
} | undefined;
} & {
...;
}

A plugin's dx.config.ts default export: its config data, runtime

Meta

and factory.

PluginConfig
=
import Plugin
Plugin
.
const config: <void>({ modules, ...data }: Plugin.ConfigOptions) => Plugin.PluginConfig<void>

Defines a plugin entirely from its dx.config.ts.

@example

// dx.config.ts
export default Plugin.config({
plugin: { key: 'org.dxos.plugin.markdown', name: 'Markdown' },
modules: [AppCapability.schema(() => import('./src/capabilities/schema.ts'))],
});

config
({
plugin: {
readonly key: string;
readonly name: string;
readonly description?: string | undefined;
readonly author?: string | undefined;
readonly homePage?: string | undefined;
readonly source?: string | undefined;
readonly screenshots?: readonly {
readonly light?: string | undefined;
readonly dark?: string | undefined;
}[] | undefined;
readonly tags?: readonly string[] | undefined;
readonly icon?: {
readonly key: string;
readonly hue?: string | undefined;
} | undefined;
readonly spec?: string | undefined;
readonly dependsOn?: readonly string[] | undefined;
}
plugin
: {
key: string

Reverse-domain NSID — the plugin's globally-unique key (e.g. org.dxos.plugin.excalidraw).

key
:
const StashNamespace: {
key: "com.example.plugin.stash";
make: <T>() => <const N extends string>(name: N) => MultiTag<T, `com.example.plugin.stash.capability.${N}`>;
makeSingleton: <T>() => <const N extends string>(name: N) => Tag<T, `com.example.plugin.stash.capability.${N}`>;
}
StashNamespace
.
key: "com.example.plugin.stash"
key
,
name: string
name
: 'Stash',
author?: string | undefined

Author or organization name. Only used for bundled plugins. For plugins published to the registry this field is ignored — the verified publisher (handle ?? did) is used instead.

author
: 'Example',
description?: string | undefined
description
:
'A reading list: save links with a note, and mark them as read.',
icon?: {
readonly key: string;
readonly hue?: string | undefined;
} | undefined
icon
: {
key: string
key
: 'ph--tray-arrow-down--regular',
hue?: string | undefined
hue
: 'indigo' },
tags?: readonly string[] | undefined
tags
: ['labs'],
},
modules: readonly (Module<any> | undefined)[]

Module specs only; each body is a dynamic import, so reading the config loads no plugin code.

modules
: [
import AppCapability
AppCapability
.
const translations: (resources: Resource | Resource[] | (() => Promise<{
translations: Resource | Resource[];
}>), options?: {
name?: string;
environments?: readonly Environment[];
}) => Module<void>

Module contributing translations.

translations
(
() => import('./src/translations.ts', {
ImportCallOptions.with?: ImportAttributes | undefined
with
: {
type: string
type
: 'module-url' } }),
),
import AppCapability
AppCapability
.
const schema: (types: ReadonlyArray<AnyEntity> | (() => Promise<{
default: ReadonlyArray<AnyEntity>;
}>), options?: {
name?: string;
environments?: readonly Environment[];
}) => Module<void>

Module contributing schemas. Prefer the loader form — schema objects ride whatever barrels declare them, so an inline list drags those barrels into the plugin definition's static closure (the boot evaluation floor); a loader keeps them in the module body chunk.

schema
(
() =>
import('./src/capabilities/schema.ts', {
ImportCallOptions.with?: ImportAttributes | undefined
with
: {
type: string
type
: 'module-url' },
}),
),
import AppCapability
AppCapability
.
const operationHandler: <void, any, readonly [], readonly []>(loader: LoadModule<void, readonly [], readonly [MultiTag<OperationHandlerSet, "org.dxos.app-framework.capability.operationHandler">]>, options?: MakerOptions<readonly [], readonly [], void, any> | undefined) => Module<any>

Module maker contributing operation handlers. Handler sets register eagerly by default: a keyed set is a definition→loader map with a thin closure (definitions carry no implementations), so the operation registry is complete at boot while each handler's BODY still loads per invocation. The deferral axis is the operation, not the module. A feature plugin whose operations cannot be invoked before the app is interactive declares activatesOn: ActivationEvents.Idle to register in that wave instead of on the startup pass; the boot path (deck, layout, space, client) invokes operations during startup and stays eager.

Stated explicitly rather than inherited: omitting activatesOn now normalizes to Idle, which would leave the registry incomplete for exactly those boot-path invocations.

operationHandler
(
() =>
import('./src/capabilities/operation-handler.ts', {
ImportCallOptions.with?: ImportAttributes | undefined
with
: {
type: string
type
: 'module-url' },
}),
),
import SpaceCapability
SpaceCapability
.
const createObject: <void, any, readonly [], readonly []>(loader: LoadModule<void, readonly [], readonly [MultiTag<Readonly<{
id: string;
createObject: import("@dxos/plugin-space/SpaceSchema").CreateObject;
inputSchema?: import("effect/Schema").Codec<any, any>;
customPanel?: import("react").ComponentType<CreateObjectCustomPanelProps>;
}>, "org.dxos.plugin.space.capability.createObject">]>, options?: MakerOptions<readonly [], readonly [], void, any> | undefined) => Module<any>

Module maker contributing a typed create-object entry. Gated by default on the create flow opening (all consumers read the entries reactively); declare activatesOn to override.

createObject
(
() =>
import('./src/capabilities/create-object.ts', {
ImportCallOptions.with?: ImportAttributes | undefined
with
: {
type: string
type
: 'module-url' },
}),
),
import AppCapability
AppCapability
.
const appGraphBuilder: <void, any, readonly [], readonly []>(loader: LoadModule<void, readonly [], readonly [MultiTag<BuilderExtensions, "org.dxos.app-framework.capability.appGraphBuilder">]>, options?: MakerOptions<readonly [], readonly [], void, any> | undefined) => Module<any>

Module maker contributing app-graph node builders. Gated by default on the host's idle event rather than the contributing plugin's own start: a builder is what puts a plugin's items in the navtree, so gating it on the plugin's own start (fired when its surface renders) is a deadlock — the item never appears, so it can never be opened. Declare activatesOn to override.

appGraphBuilder
(
() =>
import('./src/capabilities/app-graph-builder.ts', {
ImportCallOptions.with?: ImportAttributes | undefined
with
: {
type: string
type
: 'module-url' },
}),
),
import AppCapability
AppCapability
.
const surface: <void, any, readonly [], readonly []>(loader: LoadModule<void, readonly [], readonly [MultiTag<any, "org.dxos.app-framework.capability.reactSurface">]>, options?: (MakerOptions<readonly [], readonly [], void, any> & {
roles?: readonly Role<any>[];
}) | undefined) => Module<any>

Module maker contributing React surfaces. Declaring roles (the role tokens the module's surfaces bind, e.g. AppSurface.Article or a plugin's <Plugin>Surface tokens) gates the module on those roles' demand events (

ActivationEvents.SurfacesRequested

) — it loads when a Surface for one of its roles first renders instead of at startup. Modules without declared roles stay eager; an explicit activatesOn wins over the derived gate.

surface
(
() =>
import('./src/capabilities/react-surface.ts', {
ImportCallOptions.with?: ImportAttributes | undefined
with
: {
type: string
type
: 'module-url' },
}),
{
roles?: readonly Role<any>[] | undefined
roles
: [
import AppSurface
AppSurface
.
const Article: Role<AppSurface.ArticleData<any, {}, unknown>>

Role token for the article role. Subject defaults to any until narrowed via

object

,

literal

, or

subject

. Extra passthrough props are permitted.

Article
],
},
),
import AppCapability
AppCapability
.
const skillDefinition: <void, any, readonly [], readonly []>(loader: LoadModule<void, readonly [], readonly [MultiTag<Definition, "org.dxos.app-framework.capability.skillDefinition">]>, options?: MakerOptions<readonly [], readonly [], void, any> | undefined) => Module<any>

Module maker contributing skill definitions. Gated by default on the assistant plugin's start event — a skill belongs to the assistant feature regardless of which plugin contributes it (skills register into a shared registry whose consumers are reactive); declare activatesOn to override.

skillDefinition
(
() =>
import('./src/capabilities/skill-definition.ts', {
ImportCallOptions.with?: ImportAttributes | undefined
with
: {
type: string
type
: 'module-url' },
}),
),
],
});
export const {
const meta: {
readonly profile: {
readonly key: string;
readonly name: string;
readonly description?: string | undefined;
readonly author?: string | undefined;
readonly homePage?: string | undefined;
readonly source?: string | undefined;
readonly screenshots?: readonly {
readonly light?: string | undefined;
readonly dark?: string | undefined;
}[] | undefined;
readonly tags?: readonly string[] | undefined;
readonly icon?: {
readonly key: string;
readonly hue?: string | undefined;
} | undefined;
readonly spec?: string | undefined;
readonly dependsOn?: readonly string[] | undefined;
readonly createdAt?: string | undefined;
};
readonly release?: {
...;
} | undefined;
}
meta
,
const make: Plugin.PluginFactory<void>
make
} =
const config: Plugin.PluginConfig<void>
config
;
export default
const config: Plugin.PluginConfig<void>
config
;

Testing a plugin boots a plugin in a headless app.

Page What it covers
Plugins and modules How plugins, modules, events and capabilities fit together.
ActivationEvent Declaring events and the built-in ones.
API reference Every export of @dxos/app-framework.