Skip to main content

Plugins

2 min read

Performance Plugin

The performance plugin measures runtime behavior using existing plugin hooks – no core modifications needed.


Basic Usage

import { performancePlugin } from '@directive-run/core/plugins';

const perf = performancePlugin();

const system = createSystem({
  module: myModule,
  plugins: [perf],
});

system.start();

// After the system has been running, check metrics
const snapshot = perf.getSnapshot();
console.log(snapshot.resolvers);
console.log(snapshot.reconcile);

Options

OptionTypeDefaultDescription
onSlowConstraint(id: string, durationMs: number) => voidCallback when a constraint exceeds the slow threshold
onSlowResolver(id: string, durationMs: number) => voidCallback when a resolver exceeds the slow threshold
slowConstraintThresholdMsnumber16Threshold in ms for slow constraint warnings (one frame)
slowResolverThresholdMsnumber1000Threshold in ms for slow resolver warnings
const perf = performancePlugin({
  slowConstraintThresholdMs: 8,
  slowResolverThresholdMs: 500,
  onSlowConstraint: (id, ms) => {
    console.warn(`Slow constraint "${id}": ${ms.toFixed(1)}ms`);
  },
  onSlowResolver: (id, ms) => {
    console.warn(`Slow resolver "${id}": ${ms.toFixed(0)}ms`);
  },
});

Performance Snapshot

Call perf.getSnapshot() to get a full metrics snapshot at any time:

const snapshot = perf.getSnapshot();

Snapshot Shape

interface PerformanceSnapshot {
  constraints: Record<string, ConstraintMetrics>;
  resolvers: Record<string, ResolverMetrics>;
  effects: Record<string, EffectMetrics>;
  reconcile: ReconcileMetrics;
  uptime: number; // ms since system.start()
}

Constraint Metrics

FieldTypeDescription
evaluationsnumberTotal number of evaluations
totalDurationMsnumberCumulative evaluation time
avgDurationMsnumberAverage evaluation time
maxDurationMsnumberSlowest single evaluation
lastEvaluatedAtnumberTimestamp of last evaluation

Resolver Metrics

FieldTypeDescription
startsnumberTotal resolver starts
completionsnumberSuccessful completions
errorsnumberFailed attempts
retriesnumberRetry attempts
cancellationsnumberCanceled resolvers
totalDurationMsnumberCumulative resolve time
avgDurationMsnumberAverage resolve time
maxDurationMsnumberSlowest single resolve
lastCompletedAtnumberTimestamp of last completion

Effect Metrics

FieldTypeDescription
runsnumberTotal effect executions
errorsnumberErrors thrown
lastRunAtnumberTimestamp of last run

Reconcile Metrics

FieldTypeDescription
runsnumberTotal reconciliation cycles
totalDurationMsnumberCumulative reconciliation time
avgDurationMsnumberAverage cycle duration
maxDurationMsnumberSlowest cycle

Reset

Clear all collected metrics:

perf.reset();

Identifying Bottlenecks

Use the snapshot to find performance issues:

const snapshot = perf.getSnapshot();

// Find the slowest resolver
const slowest = Object.entries(snapshot.resolvers)
  .sort(([, a], [, b]) => b.maxDurationMs - a.maxDurationMs)[0];

if (slowest) {
  console.log(`Slowest resolver: "${slowest[0]}" (${slowest[1].maxDurationMs}ms max)`);
}

// Check reconciliation health
if (snapshot.reconcile.avgDurationMs > 16) {
  console.warn(`Reconciliation averaging ${snapshot.reconcile.avgDurationMs.toFixed(1)}ms (above 16ms frame budget)`);
}

Production

The performance plugin uses performance.now() for timing and has minimal overhead. You can run it in production for real-user monitoring, or limit it to development:

const plugins = [];

if (process.env.NODE_ENV !== 'production') {
  plugins.push(performancePlugin({
    onSlowResolver: (id, ms) => console.warn(`Slow: ${id} (${ms}ms)`),
  }));
}

Next Steps

Previous
Persistence

We care about your data. We'll never share your email.

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 State Management for TypeScript