Skip to content

The villain_lair scenarios

example/villain_lair/ is fifteen runnable Dart programs, one per file, each isolating one piece of the runtime. The cast (Dr. Nefarious, Gary, Janet, Mr. Whiskers, Doug) is window dressing that helps the file read; the substance is in the API calls and the plugin shapes.

This page is an index. Each scenario file is short enough (~100-200 lines) that reading it directly is faster than wrapping prose around it. Use the entries below to pick the file that matches what you are trying to learn.

Terminal window
cd example/villain_lair
dart pub get
dart run bin/01_hello_lair.dart

Replace the filename to run any other. Each main() is at the bottom of the file, so they are also editable: change a priority, drop a plugin, swap an event payload, run again.

The smallest possible plugin: one SessionPlugin, one service, one session, one greeting. Read this first.

Concepts: Plugins, Service Registry, Runtime.

Emitting events on a session bus, subscribing with the auto-tracking on<T>() helper on Plugin, and tapping every event for tracing with bind().

Concepts: Event Bus, Event Patterns.

Typed request/response. Multiple handlers can respond; the first non-null wins. Sync and async pairs both work; maybeRequest returns null instead of throwing.

Concepts: Event Bus, Event Patterns.

The densest scenario. Three competing registrations at priorities 100, 50, and 10. Covers registerSingleton, registerLazySingleton, registerFactory, resolve, maybeResolve, resolveAfter, namespaces, and listing helpers in one file.

Concepts: Service Registry.

StatefulPluginService. Subscriptions made in attach are cancelled on detach automatically. The scenario verifies cleanup by issuing a request after detach and watching it fail.

Concepts: Plugin Services.

Both priority systems use the same polarity: higher runs first, higher wins. The event bus invokes the highest-priority handler first so it can mutate or stop the cascade; the registry resolves to the highest-priority registration. One scenario shows both, including a high-priority handler that stops the cascade with envelope.stop().

Concepts: Event Bus, Service Registry.

Two sessions running in parallel never bleed state. Each gets its own service registry, its own bus, its own context. One disposes while the other keeps running.

Concepts: Sessions.

The full settings model in one file. PluginConfig for plugin-level enablement; ServiceSettings for service config and priority overrides; ConfigNode for typed reads with coercion; wildcard keys (*:death_ray) for current-winner targeting; JSON round-trip.

Concepts: Configuration, Settings & Overrides.

GlobalPlugin attaches once at runtime.init() and lives until disposal. SessionPlugin attaches per session. Cross-scope communication is always explicit: context.globalBus.emit() for session-to-global, runtime.sessions.emit() for global-to-every-session.

Concepts: Plugins, Sessions.

Custom Capability subclasses attached at registration. Reading them does not instantiate the service: resolveRaw() returns the wrapper. Useful for UI listings, routing, feature detection.

Concepts: Capabilities.

The three things an on<T> handler can do with an EventEnvelope: continue the cascade (optionally mutating), short-circuit with envelope.stop(value), or mark stopped and keep doing side work. The cleanest distillation of the pre-commit interception pattern.

Concepts: Event Patterns.

Plugin.dependencies declares required plugin ids. The runtime disables a plugin transitively if a dependency is missing or itself disabled. FeatureFlag.experimental keeps a plugin off by default; FeatureFlag.locked keeps it on regardless.

Concepts: Plugins.

PluginRuntime reconciling settings mid-session. Disabled plugins detach; survivors receive onPluginSettingsChanged. The contrast between full updateSettings (lifecycle-aware) and updateSettingsSnapshot (config-only) lands here.

Concepts: Runtime, Settings & Overrides.

One bus, many lanes. Handlers without an identifier see every event of that type; identifier-scoped handlers see only their lane’s. The mechanism behind multiplexing one event type across tools, panels, or tenants.

Concepts: Event Bus.

The capstone. A full multi-plugin system in one file: a global coordinator, session plugins with declared dependencies, a StatefulPluginService tracking inventory, identifier-scoped request/response, event mutation for plan rewrites, settings injection, live reconciliation. Every API earlier scenarios introduced shows up here doing its job.

Concepts: every concept page touches this one.