Plugins extend Directive systems with cross-cutting functionality like logging, persistence, and debugging. They hook into every stage of the system lifecycle without modifying core behavior.
Using Plugins
Add plugins when creating a system:
import{ createSystem }from'@directive-run/core';import{ loggingPlugin, devtoolsPlugin }from'@directive-run/core/plugins';// Pass plugins as an array –they hook into the system's lifecycle automaticallyconst system =createSystem({ module: myModule, plugins:[loggingPlugin(),devtoolsPlugin(),],});// Plugins are active as soon as the system startssystem.start();
Built-in Plugins
Plugin
Import
Purpose
loggingPlugin(options?)
@directive-run/core/plugins
Console logging for state changes, resolvers, and events
devtoolsPlugin(options?)
@directive-run/core/plugins
Browser devtools integration via window.__DIRECTIVE__
persistencePlugin(options)
@directive-run/core/plugins
Save and restore facts to storage
performancePlugin(options?)
@directive-run/core/plugins
Track constraint, resolver, effect, and reconciliation metrics
Standalone Utilities
These are standalone utilities imported from @directive-run/core/plugins. They integrate with the system but are not passed to the plugins array.
Utility
Import
Purpose
createCircuitBreaker(config?)
@directive-run/core/plugins
Fault isolation with automatic recovery for failing operations
createObservability(config?)
@directive-run/core/plugins
Metrics collection, dashboards, and alert thresholds
Plugin Order
Plugins execute in registration order. Put logging first to capture all events:
plugins:[// Logging first so it captures events from every plugin that followsloggingPlugin(),// Persistence restores saved state during init, before the engine runspersistencePlugin({ storage: localStorage, key:'my-app'}),// DevTools last –it can inspect the fully initialized systemdevtoolsPlugin(),]
If two plugins with the same name are registered, the second replaces the first with a warning.
onInit
system created
onStart
system.start()
onFactChange
facts mutated
onDerivation
values recomputed
onResolve
requirements fulfilled
onStop
system.stop()
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
onInit
onStart
onFactChange
onDerivation
onResolve
onStop
Conditional Plugins
Enable plugins based on environment:
// Start with the plugins you always wantconst plugins =[persistencePlugin({ key:'my-app'}),];// Add dev-only plugins conditionally so they're tree-shaken from productionif(process.env.NODE_ENV==='development'){// unshift puts logging first so it captures everything plugins.unshift(loggingPlugin()); plugins.push(devtoolsPlugin());}const system =createSystem({ module: myModule, plugins,});system.start();
Complete Hook Reference
Every hook is optional. Implement only the ones you need.
Lifecycle Hooks
Hook
Parameters
When it fires
onInit
(system)
Once on creation, before start(). Only async hook.
A requirement is canceled (constraint no longer active)
Resolver Hooks
Hook
Parameters
When it fires
onResolverStart
(resolver, req)
A resolver begins processing a requirement
onResolverComplete
(resolver, req, duration)
A resolver succeeds (duration in ms)
onResolverError
(resolver, req, error)
A resolver fails after all retries exhausted
onResolverRetry
(resolver, req, attempt)
A resolver retries after failure
onResolverCancel
(resolver, req)
A resolver is canceled (requirement no longer needed)
Effect Hooks
Hook
Parameters
When it fires
onEffectRun
(id)
An effect executes
onEffectError
(id, error)
An effect throws an error
Time-Travel Hooks
Hook
Parameters
When it fires
onSnapshot
(snapshot: { id, timestamp, facts, trigger })
A time-travel snapshot is captured
onHistoryNavigate
(from, to)
Time-travel navigation occurs
Trace Hooks
Hook
Parameters
When it fires
onTraceComplete
(entry: TraceEntry)
A trace entry finalizes (all resolvers settled). Requires trace to be enabled.
Error Boundary Hooks
Hook
Parameters
When it fires
onError
(error: DirectiveError)
Any error occurs in the system
onErrorRecovery
(error, strategy: RecoveryStrategy)
Error recovery is attempted
Error Handling
Errors thrown inside plugin hooks are caught and logged. A failing plugin never breaks the system or blocks other plugins from running:
const flakyPlugin: Plugin ={ name:'flaky',// Even if a hook throws, the system catches it and keeps runningonFactSet:(key, value)=>{thrownewError('Plugin crash');// Caught internally –other plugins and the system continue normally},};
Powered by Directive. This signup uses a Directive module with facts, derivations, constraints, and resolvers – zero useState, zero useEffect. Read how it works →
Directive - Constraint-Driven Runtime for TypeScript | AI Guardrails & State Management