CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ngrx--store-devtools

Developer tools for @ngrx/store providing comprehensive debugging and time-travel capabilities for Angular applications.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

state-types.mddocs/

State Management Types

Enhanced state structures and type definitions that provide devtools metadata alongside application state, enabling comprehensive debugging capabilities.

Capabilities

Lifted State Interface

The primary enhanced state structure containing both application state and devtools metadata.

/**
 * Enhanced state containing devtools metadata alongside application state
 */
interface LiftedState {
  /** Monitor's internal state for custom monitors */
  monitorState: any;
  /** Counter for generating unique action IDs */
  nextActionId: number;
  /** Map of action IDs to lifted actions */
  actionsById: LiftedActions;
  /** Array of action IDs that are staged for execution */
  stagedActionIds: number[];
  /** Array of action IDs that have been skipped */
  skippedActionIds: number[];
  /** The last committed application state */
  committedState: any;
  /** Index of the currently displayed state */
  currentStateIndex: number;
  /** Array of computed states with their results */
  computedStates: ComputedState[];
  /** Whether state changes are locked */
  isLocked: boolean;
  /** Whether action recording is paused */
  isPaused: boolean;
}

Usage Example:

import { Injectable } from "@angular/core";
import { StoreDevtools, LiftedState } from "@ngrx/store-devtools";
import { map, filter } from "rxjs/operators";

@Injectable()
export class StateAnalysisService {
  constructor(private devtools: StoreDevtools) {}

  // Monitor state changes
  monitorStateChanges() {
    return this.devtools.liftedState.pipe(
      map((liftedState: LiftedState) => ({
        totalActions: liftedState.nextActionId,
        currentIndex: liftedState.currentStateIndex,
        skippedCount: liftedState.skippedActionIds.length,
        isLocked: liftedState.isLocked,
        isPaused: liftedState.isPaused,
      }))
    );
  }

  // Get current application state
  getCurrentAppState() {
    return this.devtools.liftedState.pipe(
      map((liftedState: LiftedState) => {
        const currentState = liftedState.computedStates[liftedState.currentStateIndex];
        return currentState ? currentState.state : liftedState.committedState;
      })
    );
  }

  // Check for state errors
  getStateErrors() {
    return this.devtools.liftedState.pipe(
      map((liftedState: LiftedState) => 
        liftedState.computedStates.filter(state => state.error)
      ),
      filter(errorStates => errorStates.length > 0)
    );
  }
}

Computed State Interface

Represents a single computed state with potential error information.

/**
 * Single computed state containing the result of applying actions
 */
interface ComputedState {
  /** The computed application state */
  state: any;
  /** Error that occurred during state computation, if any */
  error: any;
}

Lifted Action Interface

Wrapper for actions that includes devtools metadata.

/**
 * Wrapper for actions that includes devtools metadata
 */
interface LiftedAction {
  /** Action type string */
  type: string;
  /** The original action object */
  action: Action;
}

Lifted Actions Map

Collection of lifted actions indexed by their IDs.

/**
 * Map of action IDs to lifted actions
 */
interface LiftedActions {
  [id: number]: LiftedAction;
}

Usage Example:

import { Injectable } from "@angular/core";
import { StoreDevtools, LiftedActions, LiftedAction } from "@ngrx/store-devtools";

@Injectable()
export class ActionAnalysisService {
  constructor(private devtools: StoreDevtools) {}

  // Get actions by type
  getActionsByType(actionType: string) {
    return this.devtools.liftedState.pipe(
      map(liftedState => liftedState.actionsById),
      map((actionsById: LiftedActions) => 
        Object.values(actionsById).filter((liftedAction: LiftedAction) => 
          liftedAction.action.type === actionType
        )
      )
    );
  }

  // Get action frequency statistics
  getActionFrequencyStats() {
    return this.devtools.liftedState.pipe(
      map(liftedState => liftedState.actionsById),
      map((actionsById: LiftedActions) => {
        const frequency: { [actionType: string]: number } = {};
        
        Object.values(actionsById).forEach((liftedAction: LiftedAction) => {
          const type = liftedAction.action.type;
          frequency[type] = (frequency[type] || 0) + 1;
        });
        
        return frequency;
      })
    );
  }

  // Find actions with specific payload properties
  findActionsWithPayload(propertyName: string, propertyValue: any) {
    return this.devtools.liftedState.pipe(
      map(liftedState => liftedState.actionsById),
      map((actionsById: LiftedActions) => 
        Object.entries(actionsById)
          .filter(([id, liftedAction]) => {
            const payload = (liftedAction.action as any).payload;
            return payload && payload[propertyName] === propertyValue;
          })
          .map(([id, liftedAction]) => ({ id: parseInt(id), action: liftedAction }))
      )
    );
  }
}

Core Action Types

Additional action types that work with the devtools system.

/**
 * NgRx store initialization action type
 */
type InitAction = {
  readonly type: typeof INIT;
};

/**
 * NgRx store reducer update action type
 */
type UpdateReducerAction = {
  readonly type: typeof UPDATE;
};

/**
 * Union of core NgRx actions
 */
type CoreActions = InitAction | UpdateReducerAction;

/**
 * Union of all devtools and core actions
 */
type Actions = DevtoolsActions.All | CoreActions;

Recompute Constants

Constants related to state recomputation.

/** Constant for recompute action type */
const RECOMPUTE = '@ngrx/store-devtools/recompute';

/** Recompute action object */
const RECOMPUTE_ACTION = { type: RECOMPUTE };

/** NgRx store initialization action object */
const INIT_ACTION = { type: INIT };

Advanced Usage Example:

import { Injectable } from "@angular/core";
import { Store } from "@ngrx/store";
import { 
  StoreDevtools, 
  LiftedState, 
  ComputedState,
  LiftedAction,
  RECOMPUTE_ACTION 
} from "@ngrx/store-devtools";
import { withLatestFrom, tap, map, distinctUntilChanged, take } from "rxjs/operators";

@Injectable()
export class AdvancedStateService {
  constructor(
    private store: Store,
    private devtools: StoreDevtools
  ) {}

  // Compare states at different points in time
  compareStates(index1: number, index2: number) {
    return this.devtools.liftedState.pipe(
      map((liftedState: LiftedState) => {
        const state1 = liftedState.computedStates[index1];
        const state2 = liftedState.computedStates[index2];
        
        return {
          state1: state1 ? state1.state : null,
          state2: state2 ? state2.state : null,
          error1: state1 ? state1.error : null,
          error2: state2 ? state2.error : null,
          hasErrors: (state1?.error || state2?.error) !== null,
        };
      })
    );
  }

  // Track state size changes
  trackStateSizeChanges() {
    return this.devtools.liftedState.pipe(
      map((liftedState: LiftedState) => {
        const currentState = liftedState.computedStates[liftedState.currentStateIndex];
        if (!currentState) return 0;
        
        return JSON.stringify(currentState.state).length;
      }),
      distinctUntilChanged(),
      tap(size => console.log(`State size changed to: ${size} characters`))
    );
  }

  // Export simplified state history
  exportStateHistory() {
    return this.devtools.liftedState.pipe(
      take(1),
      map((liftedState: LiftedState) => {
        const history = liftedState.stagedActionIds.map(actionId => {
          const liftedAction = liftedState.actionsById[actionId];
          const computedState = liftedState.computedStates.find(
            (state, index) => liftedState.stagedActionIds[index] === actionId
          );
          
          return {
            actionId,
            actionType: liftedAction?.action.type,
            hasError: computedState?.error !== null,
            timestamp: (liftedAction?.action as any).timestamp || Date.now(),
          };
        });
        
        return {
          totalActions: liftedState.nextActionId,
          currentIndex: liftedState.currentStateIndex,
          isLocked: liftedState.isLocked,
          isPaused: liftedState.isPaused,
          history,
        };
      })
    );
  }

  // Force recompute of all states
  forceRecompute() {
    this.devtools.dispatch(RECOMPUTE_ACTION);
  }

  // Get performance metrics
  getPerformanceMetrics() {
    return this.devtools.liftedState.pipe(
      map((liftedState: LiftedState) => {
        const totalActions = liftedState.nextActionId;
        const errorCount = liftedState.computedStates.filter(state => state.error).length;
        const skippedCount = liftedState.skippedActionIds.length;
        
        return {
          totalActions,
          errorCount,
          skippedCount,
          activeActions: totalActions - skippedCount,
          errorRate: totalActions > 0 ? (errorCount / totalActions) * 100 : 0,
          memoryUsage: JSON.stringify(liftedState).length,
        };
      })
    );
  }
}

docs

actions.md

configuration.md

debugging-service.md

index.md

state-types.md

tile.json