Docs/AI & agentsv1.0.0-rc.1

StateAnalyzer

Overview

StateAnalyzer performs static and dynamic analysis of store state. It measures structural complexity, detects anti-patterns (deep nesting, circular references, oversized state), identifies action sequence patterns, and produces a 0–100 health score. Results are cached by state content to avoid redundant computation on unchanged state.

Key Concepts

  • Score — 0–100 quality rating. 100 is ideal; each detected issue deducts points based on severity. Scores below 70 trigger insight logging when logInsights: true is set in @AIPowered.
  • Issues — specific problems found in the state structure or action history (e.g., DEEP_NESTING, CIRCULAR_REFERENCE, LARGE_STATE, HIGH_MEMORY).
  • Suggestions — actionable recommendations paired to issues, including estimated effort and priority.
  • Metrics — raw measurements: property count, nesting depth, array count, object count, circular reference count, estimated memory in KB.
  • Patterns — recurring action sequences detected in the dispatch history (requires action history to be passed).

API Reference

StateAnalyzerConfig

interface StateAnalyzerConfig {
  enablePatternDetection?: boolean;     // default true
  enablePerformanceAnalysis?: boolean;  // default true
  enableStructureAnalysis?: boolean;    // default true
  maxPatternHistory?: number;           // default 100
  complexityThreshold?: number;         // default 10
}

AnalysisResult

interface AnalysisResult {
  score: number;                   // 0–100
  issues: AnalysisIssue[];
  suggestions: AnalysisSuggestion[];
  metrics: AnalysisMetrics;
  patterns: DetectedPattern[];
}

AnalysisIssue

interface AnalysisIssue {
  severity: 'low' | 'medium' | 'high' | 'critical';
  type: string;        // e.g. 'DEEP_NESTING', 'CIRCULAR_REFERENCE', 'LARGE_STATE', 'HIGH_MEMORY'
  message: string;
  location?: string;
  impact: string;
}

AnalysisSuggestion

interface AnalysisSuggestion {
  type: string;                      // e.g. 'FLATTEN_STATE', 'SPLIT_STORE', 'OPTIMIZE_DATA'
  message: string;
  benefit: string;
  effort: 'low' | 'medium' | 'high';
  priority: number;                  // 1–10, higher is more urgent
}

AnalysisMetrics

interface AnalysisMetrics {
  stateComplexity: number;
  nestingDepth: number;
  propertyCount: number;
  arrayCount: number;
  objectCount: number;
  circularReferences: number;
  estimatedMemoryKB: number;
}

DetectedPattern

interface DetectedPattern {
  name: string;          // action type sequence, e.g. "ADD_ITEM → VALIDATE → SAVE"
  description: string;
  frequency: number;
  lastOccurrence: number;   // Unix timestamp ms
  examples: any[];
}

StateAnalyzer

class StateAnalyzer {
  constructor(config?: StateAnalyzerConfig)

  analyzeState(state: any, actionHistory?: any[]): AnalysisResult
  analyzeActionSequence(actions: any[]): {
    commonSequences: string[][];
    inefficientPatterns: string[];
    redundantActions: string[];
  }
  getOptimizationRecommendations(analysisResult: AnalysisResult): string[]
  clearCache(): void
  clearPatterns(): void
}

function createStateAnalyzer(config?: StateAnalyzerConfig): StateAnalyzer

Basic Usage

import { StateAnalyzer, createStateAnalyzer } from '@tagna/udiot';

interface AppState {
  users: User[];
  products: Product[];
  ui: { loading: boolean; activeTab: string };
}

const state: AppState = {
  users: [{ id: '1', name: 'Alice' }],
  products: [],
  ui: { loading: false, activeTab: 'home' },
};

const analyzer = createStateAnalyzer({
  enablePatternDetection: true,
  enablePerformanceAnalysis: true,
  enableStructureAnalysis: true,
});

const result = analyzer.analyzeState(state);

console.log('Score:', result.score);
// Score: 95

console.log('Issues:', result.issues);
// []  (clean state)

console.log('Metrics:', result.metrics);
// { nestingDepth: 2, propertyCount: 6, estimatedMemoryKB: 0.08, ... }

const recommendations = analyzer.getOptimizationRecommendations(result);
console.log('Recommendations:', recommendations);

Common Patterns

Running analysis on every dispatch

import { Store, ReactiveStore, Mutation } from '@tagna/udiot';
import { StateAnalyzer } from '@tagna/udiot';

const analyzer = new StateAnalyzer({ complexityThreshold: 15 });

@ReactiveStore({ name: 'MonitoredStore' })
class MonitoredStore extends Store<AppState> {
  constructor(initialState: AppState) {
    super(initialState);
    this.subscribe((state) => {
      const result = analyzer.analyzeState(state);
      if (result.score < 80) {
        console.warn('[StateAnalyzer] Score dropped to', result.score);
        result.issues.forEach(i => console.warn(` - ${i.severity}: ${i.message}`));
      }
    });
  }
}

Analyzing action sequences for inefficiencies

const actionHistory = [
  { type: 'SET_FILTER', timestamp: 1 },
  { type: 'SET_FILTER', timestamp: 50 },   // < 100ms apart — redundant
  { type: 'FETCH_ITEMS', timestamp: 100 },
  { type: 'SET_FILTER', timestamp: 150 },
  { type: 'FETCH_ITEMS', timestamp: 200 },
];

const { redundantActions, inefficientPatterns } = analyzer.analyzeActionSequence(actionHistory);
console.log('Redundant:', redundantActions);     // ['SET_FILTER']
console.log('Inefficient:', inefficientPatterns); // actions dispatched > 10 times

Integration with @AIPowered

When @AIPowered is applied to a store, it creates a StateAnalyzer internally. Retrieve it with the helper:

import { AIPowered, getAnalyzer, analyzeStore, Store, ReactiveStore } from '@tagna/udiot';

@ReactiveStore({ name: 'SmartStore' })
@AIPowered({ autoAnalyze: true })
class SmartStore extends Store<AppState> {
  constructor(initialState: AppState) { super(initialState); }
}

const store = new SmartStore({ /* ... */ });

// After some dispatches...
const result = analyzeStore(store);  // convenience function
console.log('Score:', result.score);
console.log('Recommendations:', result.recommendations);

// Or get the analyzer directly for fine-grained access
const analyzer = getAnalyzer(store);
const detailed = analyzer?.analyzeState(store.getState());

Integration Notes

  • analyzeState caches results by the JSON representation of the state. Large states that cannot be JSON-serialized (circular references, functions) will always produce a fresh analysis with a random cache key.
  • Pattern detection requires actionHistory to be passed explicitly to analyzeState, or it will return an empty patterns array. @AIPowered passes the internal action log automatically.
  • The clearCache() and clearPatterns() methods are useful in tests to ensure each assertion starts from a clean slate.

Pitfalls

  • analyzeState uses JSON.stringify to estimate memory usage. States with non-serializable values (Date objects, functions, class instances with circular refs) will produce inaccurate estimatedMemoryKB and may return NaN for the score.
  • The complexity score is heuristic-based, not a formal complexity metric. A score of 60 does not necessarily mean the application is broken; treat it as a guide rather than a hard threshold.
  • Caching on the full JSON string means a state with one changed property invalidates the entire cache entry. For very large states analyzed frequently, consider increasing complexityThreshold and disabling features you do not need to reduce CPU overhead.
From the canonical UDIOT public documentation.Try it in the playground ↗