Skip to content

ActivationEvent

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

An activation event tells the plugin manager when a module should load. Modules wait for their event, so a plugin’s code loads when it is needed, not all at startup.

ActivationEvent.make declares an event from an NSID. ActivationEvent.pluginStart returns a plugin’s own start event, which the plugin manager fires on demand and while it is idle.

import * as
import ActivationEvent
ActivationEvent
from '@dxos/app-framework/ActivationEvent';
// The last segment of the NSID must be camelCase; the compiler rejects
// `index-ready`.
export const
const IndexRequested: ActivationEvent.ActivationEvent
IndexRequested
=
import ActivationEvent
ActivationEvent
.
const make: <"com.example.plugin.search.event.indexRequested">(nsid: "com.example.plugin.search.event.indexRequested", specifier?: string) => ActivationEvent.ActivationEvent

Helper to define an activation event from an NSID. Static NSID strings are validated at compile time via

DXN.Name

.

make
(
'com.example.plugin.search.event.indexRequested',
);
// Every plugin has a start event of its own, fired on demand and by the host's
// idle trickle.
export const
const SearchStart: ActivationEvent.ActivationEvent
SearchStart
=
import ActivationEvent
ActivationEvent
.
const pluginStart: (pluginKey: string | DXN) => ActivationEvent.ActivationEvent

A plugin's feature-start event, by convention <pluginKey>.event.start. A plugin's off-critical-path modules declare activatesOn on its own start event (conventionally exported as <Name>Events.Start from the plugin's types); cross-plugin contributions (a skill, a markdown extension) declare the CONSUMING plugin's start event. Deriving the id from the key alone lets fire sites (enable, idle, demand signals) activate a feature without importing its package.

pluginStart
(
'com.example.plugin.search',
);

Order modules with events and requirements

Section titled “Order modules with events and requirements”

A module’s activatesOn can name one event, or combine several with ActivationEvent.oneOf and ActivationEvent.allOf.

import * as
import Effect
Effect
from 'effect/Effect';
import * as
import ActivationEvent
ActivationEvent
from '@dxos/app-framework/ActivationEvent';
import * as
import Capability
Capability
from '@dxos/app-framework/Capability';
export const
const IndexModule: Capability.Module<void>
IndexModule
=
import Capability
Capability
.
const inlineModule: <readonly [Capability.Tag<Map<string, string>, "com.example.plugin.search.capability.index">], readonly [], void, void>(name: string, spec: ModuleSpec<readonly [Capability.Tag<Map<string, string>, "com.example.plugin.search.capability.index">], readonly [], void, void>, activate: (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.search.capability.index", "single">> | readonly Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.search.capability.index", "single">>[], 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
(
'Index',
{
provides: readonly [Capability.Tag<Map<string, string>, "com.example.plugin.search.capability.index">]
provides
: [
const Index: Capability.Tag<Map<string, string>, "com.example.plugin.search.capability.index">
Index
],
activatesOn?: ActivationEvent.Events | undefined

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

activatesOn
:
import ActivationEvent
ActivationEvent
.
const oneOf: (...events: ActivationEvent.ActivationEvent[]) => {
type: "one-of";
events: ActivationEvent.ActivationEvent[];
}

Helper to create an activation event that triggers when any of the given events are activated.

oneOf
(
const IndexRequested: ActivationEvent.ActivationEvent
IndexRequested
,
const SearchStart: ActivationEvent.ActivationEvent
SearchStart
),
},
() =>
import Effect
Effect
.
const succeed: <Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.search.capability.index", "single">>>(value: Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.search.capability.index", "single">>) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.search.capability.index", "single">>, 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.Tag<Map<string, string>, "com.example.plugin.search.capability.index">>(capability: Capability.Tag<Map<string, string>, "com.example.plugin.search.capability.index">, implementation: Map<string, string>) => Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.search.capability.index", "single">> (+1 overload)
contribute
(
const Index: Capability.Tag<Map<string, string>, "com.example.plugin.search.capability.index">
Index
, new
var Map: MapConstructor
new () => Map<any, any> (+3 overloads)
Map
())),
);

Requirements order modules within an event. A module that requires a capability activates after the module that provides it, whichever event activated the provider. Registration order does not matter.

import * as
import Effect
Effect
from 'effect/Effect';
import * as
import Capability
Capability
from '@dxos/app-framework/Capability';
// No `activatesOn`: this module waits for `Index`, so it activates whenever the
// module above does.
export const
const QueryModule: Capability.Module<void>
QueryModule
=
import Capability
Capability
.
const inlineModule: <readonly [], readonly [Capability.Tag<Map<string, string>, "com.example.plugin.search.capability.index">], void, void>(name: string, spec: ModuleSpec<readonly [], readonly [Capability.Tag<Map<string, string>, "com.example.plugin.search.capability.index">], void, void>, activate: (props: void) => Effect.Effect<void | readonly never[], Error, Capability.Requirements<readonly [Capability.Tag<Map<string, string>, "com.example.plugin.search.capability.index">]>>) => Capability.Module<void>

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
(
'Query',
{
requires?: readonly [Capability.Tag<Map<string, string>, "com.example.plugin.search.capability.index">] | undefined
requires
: [
const Index: Capability.Tag<Map<string, string>, "com.example.plugin.search.capability.index">
Index
],
provides: readonly []
provides
: [] },
() =>
import Effect
Effect
.
const gen: <Effect.Effect<Map<string, string>, never, Capability.CapabilityIdentifier<"com.example.plugin.search.capability.index", "single">>, void>(f: () => Generator<Effect.Effect<Map<string, string>, never, Capability.CapabilityIdentifier<"com.example.plugin.search.capability.index", "single">>, void, never>) => Effect.Effect<void, never, Capability.CapabilityIdentifier<"com.example.plugin.search.capability.index", "single">> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to use

Use when you want to write effectful code that looks and behaves like synchronous code, while still handling asynchronous tasks, errors, and complex control flow such as loops and conditions.

Generator functions work similarly to async/await but keep errors, requirements, and interruption in the Effect type. You can yield* values from effects and return the final result at the end.

Example (Sequencing effects with generators)

import { Data, Effect } from "effect"
class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, DiscountRateError> =>
discountRate === 0
? Effect.fail(new DiscountRateError())
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function*() {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})
await Effect.runPromise(program) // => "Final amount to charge: 96"

@stability ― stable

@category ― constructors

@since ― 2.0.0

gen
(function* () {
const
const index: Map<string, string>
index
= yield*
const Index: Capability.Tag<Map<string, string>, "com.example.plugin.search.capability.index">
Index
;
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(
const index: Map<string, string>
index
.
Map<string, string>.size: number

@returns ― the number of elements in the Map.

size
);
}),
);

A module fires an event with Plugin.activate. This is how a plugin loads another plugin’s modules on demand, for example when a person opens the create dialog.

import * as
import Plugin
Plugin
from '@dxos/app-framework/Plugin';
export const
const requestIndex: Effect.Effect<boolean, Error, Plugin.Service>
requestIndex
=
import Plugin
Plugin
.
const activate: (event: ActivationEvent.ActivationEvent) => Effect.Effect<boolean, Error, Plugin.Service>

Activates plugins based on the activation event. Accesses the PluginManager via the Effect layer system.

@param ― event The activation event.

@returns ― Whether the activation was successful.

activate
(
const IndexRequested: ActivationEvent.ActivationEvent
IndexRequested
);
Event When it fires
ActivationEvents.Startup Once, while the app boots. Only modules needed for the first paint belong here.
ActivationEvents.Idle After the first paint, when the app is idle. The default for a module with no activatesOn.
ActivationEvents.SurfacesRequested(role) The first time a surface of that role is rendered.
ActivationEvents.CommandsRequested When the command palette or CLI asks for commands.
ActivationEvents.PluginAssetsRequested When a tool asks for plugin assets, such as a spec.
import * as
import ActivationEvents
ActivationEvents
from '@dxos/app-framework/ActivationEvents';
export const
const events: ActivationEvent.ActivationEvent[]
events
= [
import ActivationEvents
ActivationEvents
.
const Startup: ActivationEvent.ActivationEvent

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

ActivationEvent$.Startup

.

Startup
,
import ActivationEvents
ActivationEvents
.
const Idle: ActivationEvent.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
,
import ActivationEvents
ActivationEvents
.
const SurfacesRequested: (role: string) => ActivationEvent.ActivationEvent

Demand signal for React surfaces, keyed by role NSID. Fired when a Surface for the role first renders (and by availability checks that miss), so surface modules gated on their bound roles load exactly when a screen region that can host them appears.

SurfacesRequested
('org.dxos.role.article'),
];

Module makers such as AppCapability.surface set the right event for their capability, so most manifests never name one.

Page What it covers
Plugin Defining modules.
Capability Requirements between modules.