CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-single-spa

The router for easy microfrontends that enables multiple frameworks to coexist on the same page.

Pending
Overview
Eval results
Files

application-status-lifecycle.mddocs/

Application Status & Lifecycle

Status constants and utilities for monitoring application states and lifecycle management.

Capabilities

Application Status Constants

Single-spa defines status constants that represent different states in an application's lifecycle.

// Application loading states
const NOT_LOADED = "NOT_LOADED";
const LOADING_SOURCE_CODE = "LOADING_SOURCE_CODE";
const LOAD_ERROR = "LOAD_ERROR";

// Application lifecycle states
const NOT_BOOTSTRAPPED = "NOT_BOOTSTRAPPED";
const BOOTSTRAPPING = "BOOTSTRAPPING";
const NOT_MOUNTED = "NOT_MOUNTED";
const MOUNTING = "MOUNTING";
const MOUNTED = "MOUNTED";
const UNMOUNTING = "UNMOUNTING";
const UNLOADING = "UNLOADING";

// Error states
const SKIP_BECAUSE_BROKEN = "SKIP_BECAUSE_BROKEN";

Usage Examples:

import { 
  getAppStatus, 
  MOUNTED, 
  NOT_LOADED, 
  SKIP_BECAUSE_BROKEN 
} from "single-spa";

function checkApplicationHealth() {
  const apps = ["navbar", "products", "dashboard"];
  
  apps.forEach(appName => {
    const status = getAppStatus(appName);
    switch (status) {
      case MOUNTED:
        console.log(`${appName} is running normally`);
        break;
      case NOT_LOADED:
        console.log(`${appName} hasn't been loaded yet`);
        break;
      case SKIP_BECAUSE_BROKEN:
        console.error(`${appName} is broken and being skipped`);
        break;
      default:
        console.log(`${appName} status: ${status}`);
    }
  });
}

Get Application Status

Gets the current lifecycle status of a specific application.

/**
 * Get the current status of an application
 * @param appName - Name of the application
 * @returns Current status string or null if application not found
 */
function getAppStatus(appName: string): string | null;

Usage Examples:

import { getAppStatus, MOUNTED, MOUNTING } from "single-spa";

// Check if application is ready
function isAppReady(appName) {
  const status = getAppStatus(appName);
  return status === MOUNTED;
}

// Wait for application to be mounted
async function waitForAppToMount(appName, timeout = 5000) {
  const startTime = Date.now();
  
  while (Date.now() - startTime < timeout) {
    const status = getAppStatus(appName);
    if (status === MOUNTED) {
      return true;
    }
    if (status === MOUNTING) {
      await new Promise(resolve => setTimeout(resolve, 100));
      continue;
    }
    throw new Error(`App ${appName} failed to mount (status: ${status})`);
  }
  
  throw new Error(`Timeout waiting for ${appName} to mount`);
}

Get Mounted Applications

Returns an array of currently mounted application names.

/**
 * Get names of currently mounted applications
 * @returns Array of mounted application names
 */
function getMountedApps(): string[];

Usage Examples:

import { getMountedApps } from "single-spa";

// Monitor currently active applications
function logActiveApplications() {
  const mounted = getMountedApps();
  console.log(`Currently active: ${mounted.join(", ")}`);
}

// Check if specific app is mounted
function isAppMounted(appName) {
  return getMountedApps().includes(appName);
}

// Performance monitoring
function trackApplicationUsage() {
  const mountedApps = getMountedApps();
  analytics.track("applications_active", {
    count: mountedApps.length,
    apps: mountedApps
  });
}

Lifecycle Hooks and Monitoring

Application Lifecycle Events

Monitor application lifecycle changes using custom events:

// Listen for application status changes
window.addEventListener("single-spa:app-change", (event) => {
  const { 
    newAppStatuses, 
    appsByNewStatus, 
    totalAppChanges 
  } = event.detail;
  
  console.log("Application status changes:", newAppStatuses);
  console.log("Apps by status:", appsByNewStatus);
});

// Listen for routing events that trigger app changes
window.addEventListener("single-spa:routing-event", (event) => {
  console.log("Navigation event:", event.detail);
});

Status Monitoring Utilities

import { 
  getAppStatus, 
  getMountedApps, 
  getAppNames,
  MOUNTED,
  SKIP_BECAUSE_BROKEN 
} from "single-spa";

// Create a status dashboard
function createStatusDashboard() {
  const allApps = getAppNames();
  const mountedApps = getMountedApps();
  
  const statusReport = allApps.map(appName => ({
    name: appName,
    status: getAppStatus(appName),
    isMounted: mountedApps.includes(appName)
  }));
  
  return statusReport;
}

// Health check function
function performHealthCheck() {
  const allApps = getAppNames();
  const brokenApps = allApps.filter(appName => 
    getAppStatus(appName) === SKIP_BECAUSE_BROKEN
  );
  
  if (brokenApps.length > 0) {
    console.error("Broken applications detected:", brokenApps);
    // Trigger alerts or recovery procedures
  }
  
  return {
    healthy: allApps.length - brokenApps.length,
    broken: brokenApps.length,
    total: allApps.length
  };
}

Lifecycle State Machine

Understanding the application lifecycle flow:

NOT_LOADED
    ↓ (when activeWhen returns true)
LOADING_SOURCE_CODE
    ↓ (app loaded successfully)
NOT_BOOTSTRAPPED
    ↓ (bootstrap called)
BOOTSTRAPPING
    ↓ (bootstrap complete)
NOT_MOUNTED
    ↓ (mount called)
MOUNTING
    ↓ (mount complete)
MOUNTED
    ↓ (update called - optional, internal state not exported)
UPDATING (internal)
    ↓ (update complete)
MOUNTED
    ↓ (when activeWhen returns false)
UNMOUNTING
    ↓ (unmount complete)
NOT_MOUNTED
    ↓ (unload called - optional)
UNLOADING
    ↓ (unload complete)
NOT_LOADED

// Error states can occur at any point:
LOAD_ERROR (if loading fails)
SKIP_BECAUSE_BROKEN (if any lifecycle fails)

Advanced Status Patterns

import { getAppStatus, addErrorHandler } from "single-spa";

// Retry mechanism for failed applications
class ApplicationManager {
  constructor() {
    this.retryAttempts = new Map();
    this.maxRetries = 3;
  }
  
  async retryFailedApp(appName) {
    const attempts = this.retryAttempts.get(appName) || 0;
    
    if (attempts >= this.maxRetries) {
      console.error(`Max retries exceeded for ${appName}`);
      return false;
    }
    
    this.retryAttempts.set(appName, attempts + 1);
    
    try {
      // Trigger app reload/remount logic
      await triggerAppChange();
      
      // Check if app recovered
      const status = getAppStatus(appName);
      if (status === MOUNTED) {
        this.retryAttempts.delete(appName);
        return true;
      }
    } catch (error) {
      console.error(`Retry failed for ${appName}:`, error);
    }
    
    return false;
  }
}

// Circuit breaker pattern
class ApplicationCircuitBreaker {
  constructor(appName, threshold = 5) {
    this.appName = appName;
    this.threshold = threshold;
    this.failures = 0;
    this.isOpen = false;
    this.lastFailure = null;
  }
  
  recordSuccess() {
    this.failures = 0;
    this.isOpen = false;
  }
  
  recordFailure() {
    this.failures++;
    this.lastFailure = Date.now();
    
    if (this.failures >= this.threshold) {
      this.isOpen = true;
      console.warn(`Circuit breaker opened for ${this.appName}`);
    }
  }
  
  canProceed() {
    if (!this.isOpen) return true;
    
    // Check if enough time has passed to try again
    const timeSinceLastFailure = Date.now() - this.lastFailure;
    return timeSinceLastFailure > 60000; // 1 minute cooldown
  }
}

Types

interface SingleSpaCustomEventDetail {
  newAppStatuses: SingleSpaNewAppStatus;
  appsByNewStatus: SingleSpaAppsByNewStatus;
  totalAppChanges: number;
  originalEvent?: Event;
  oldUrl: string;
  newUrl: string;
  navigationIsCanceled: boolean;
  cancelNavigation?: () => void;
}

interface SingleSpaNewAppStatus {
  [appName: string]: "MOUNTED" | "NOT_MOUNTED" | "NOT_LOADED" | "SKIP_BECAUSE_BROKEN";
}

interface SingleSpaAppsByNewStatus {
  MOUNTED: string[];
  NOT_MOUNTED: string[];
  NOT_LOADED: string[];
  SKIP_BECAUSE_BROKEN: string[];
}

Install with Tessl CLI

npx tessl i tessl/npm-single-spa

docs

application-management.md

application-status-lifecycle.md

configuration-timeouts.md

error-handling.md

index.md

navigation-routing.md

parcels-system.md

tile.json