0
# Application Status & Lifecycle
1
2
Status constants and utilities for monitoring application states and lifecycle management.
3
4
## Capabilities
5
6
### Application Status Constants
7
8
Single-spa defines status constants that represent different states in an application's lifecycle.
9
10
```javascript { .api }
11
// Application loading states
12
const NOT_LOADED = "NOT_LOADED";
13
const LOADING_SOURCE_CODE = "LOADING_SOURCE_CODE";
14
const LOAD_ERROR = "LOAD_ERROR";
15
16
// Application lifecycle states
17
const NOT_BOOTSTRAPPED = "NOT_BOOTSTRAPPED";
18
const BOOTSTRAPPING = "BOOTSTRAPPING";
19
const NOT_MOUNTED = "NOT_MOUNTED";
20
const MOUNTING = "MOUNTING";
21
const MOUNTED = "MOUNTED";
22
const UNMOUNTING = "UNMOUNTING";
23
const UNLOADING = "UNLOADING";
24
25
// Error states
26
const SKIP_BECAUSE_BROKEN = "SKIP_BECAUSE_BROKEN";
27
```
28
29
**Usage Examples:**
30
31
```javascript
32
import {
33
getAppStatus,
34
MOUNTED,
35
NOT_LOADED,
36
SKIP_BECAUSE_BROKEN
37
} from "single-spa";
38
39
function checkApplicationHealth() {
40
const apps = ["navbar", "products", "dashboard"];
41
42
apps.forEach(appName => {
43
const status = getAppStatus(appName);
44
switch (status) {
45
case MOUNTED:
46
console.log(`${appName} is running normally`);
47
break;
48
case NOT_LOADED:
49
console.log(`${appName} hasn't been loaded yet`);
50
break;
51
case SKIP_BECAUSE_BROKEN:
52
console.error(`${appName} is broken and being skipped`);
53
break;
54
default:
55
console.log(`${appName} status: ${status}`);
56
}
57
});
58
}
59
```
60
61
### Get Application Status
62
63
Gets the current lifecycle status of a specific application.
64
65
```javascript { .api }
66
/**
67
* Get the current status of an application
68
* @param appName - Name of the application
69
* @returns Current status string or null if application not found
70
*/
71
function getAppStatus(appName: string): string | null;
72
```
73
74
**Usage Examples:**
75
76
```javascript
77
import { getAppStatus, MOUNTED, MOUNTING } from "single-spa";
78
79
// Check if application is ready
80
function isAppReady(appName) {
81
const status = getAppStatus(appName);
82
return status === MOUNTED;
83
}
84
85
// Wait for application to be mounted
86
async function waitForAppToMount(appName, timeout = 5000) {
87
const startTime = Date.now();
88
89
while (Date.now() - startTime < timeout) {
90
const status = getAppStatus(appName);
91
if (status === MOUNTED) {
92
return true;
93
}
94
if (status === MOUNTING) {
95
await new Promise(resolve => setTimeout(resolve, 100));
96
continue;
97
}
98
throw new Error(`App ${appName} failed to mount (status: ${status})`);
99
}
100
101
throw new Error(`Timeout waiting for ${appName} to mount`);
102
}
103
```
104
105
### Get Mounted Applications
106
107
Returns an array of currently mounted application names.
108
109
```javascript { .api }
110
/**
111
* Get names of currently mounted applications
112
* @returns Array of mounted application names
113
*/
114
function getMountedApps(): string[];
115
```
116
117
**Usage Examples:**
118
119
```javascript
120
import { getMountedApps } from "single-spa";
121
122
// Monitor currently active applications
123
function logActiveApplications() {
124
const mounted = getMountedApps();
125
console.log(`Currently active: ${mounted.join(", ")}`);
126
}
127
128
// Check if specific app is mounted
129
function isAppMounted(appName) {
130
return getMountedApps().includes(appName);
131
}
132
133
// Performance monitoring
134
function trackApplicationUsage() {
135
const mountedApps = getMountedApps();
136
analytics.track("applications_active", {
137
count: mountedApps.length,
138
apps: mountedApps
139
});
140
}
141
```
142
143
## Lifecycle Hooks and Monitoring
144
145
### Application Lifecycle Events
146
147
Monitor application lifecycle changes using custom events:
148
149
```javascript
150
// Listen for application status changes
151
window.addEventListener("single-spa:app-change", (event) => {
152
const {
153
newAppStatuses,
154
appsByNewStatus,
155
totalAppChanges
156
} = event.detail;
157
158
console.log("Application status changes:", newAppStatuses);
159
console.log("Apps by status:", appsByNewStatus);
160
});
161
162
// Listen for routing events that trigger app changes
163
window.addEventListener("single-spa:routing-event", (event) => {
164
console.log("Navigation event:", event.detail);
165
});
166
```
167
168
### Status Monitoring Utilities
169
170
```javascript
171
import {
172
getAppStatus,
173
getMountedApps,
174
getAppNames,
175
MOUNTED,
176
SKIP_BECAUSE_BROKEN
177
} from "single-spa";
178
179
// Create a status dashboard
180
function createStatusDashboard() {
181
const allApps = getAppNames();
182
const mountedApps = getMountedApps();
183
184
const statusReport = allApps.map(appName => ({
185
name: appName,
186
status: getAppStatus(appName),
187
isMounted: mountedApps.includes(appName)
188
}));
189
190
return statusReport;
191
}
192
193
// Health check function
194
function performHealthCheck() {
195
const allApps = getAppNames();
196
const brokenApps = allApps.filter(appName =>
197
getAppStatus(appName) === SKIP_BECAUSE_BROKEN
198
);
199
200
if (brokenApps.length > 0) {
201
console.error("Broken applications detected:", brokenApps);
202
// Trigger alerts or recovery procedures
203
}
204
205
return {
206
healthy: allApps.length - brokenApps.length,
207
broken: brokenApps.length,
208
total: allApps.length
209
};
210
}
211
```
212
213
### Lifecycle State Machine
214
215
Understanding the application lifecycle flow:
216
217
```
218
NOT_LOADED
219
↓ (when activeWhen returns true)
220
LOADING_SOURCE_CODE
221
↓ (app loaded successfully)
222
NOT_BOOTSTRAPPED
223
↓ (bootstrap called)
224
BOOTSTRAPPING
225
↓ (bootstrap complete)
226
NOT_MOUNTED
227
↓ (mount called)
228
MOUNTING
229
↓ (mount complete)
230
MOUNTED
231
↓ (update called - optional, internal state not exported)
232
UPDATING (internal)
233
↓ (update complete)
234
MOUNTED
235
↓ (when activeWhen returns false)
236
UNMOUNTING
237
↓ (unmount complete)
238
NOT_MOUNTED
239
↓ (unload called - optional)
240
UNLOADING
241
↓ (unload complete)
242
NOT_LOADED
243
244
// Error states can occur at any point:
245
LOAD_ERROR (if loading fails)
246
SKIP_BECAUSE_BROKEN (if any lifecycle fails)
247
```
248
249
### Advanced Status Patterns
250
251
```javascript
252
import { getAppStatus, addErrorHandler } from "single-spa";
253
254
// Retry mechanism for failed applications
255
class ApplicationManager {
256
constructor() {
257
this.retryAttempts = new Map();
258
this.maxRetries = 3;
259
}
260
261
async retryFailedApp(appName) {
262
const attempts = this.retryAttempts.get(appName) || 0;
263
264
if (attempts >= this.maxRetries) {
265
console.error(`Max retries exceeded for ${appName}`);
266
return false;
267
}
268
269
this.retryAttempts.set(appName, attempts + 1);
270
271
try {
272
// Trigger app reload/remount logic
273
await triggerAppChange();
274
275
// Check if app recovered
276
const status = getAppStatus(appName);
277
if (status === MOUNTED) {
278
this.retryAttempts.delete(appName);
279
return true;
280
}
281
} catch (error) {
282
console.error(`Retry failed for ${appName}:`, error);
283
}
284
285
return false;
286
}
287
}
288
289
// Circuit breaker pattern
290
class ApplicationCircuitBreaker {
291
constructor(appName, threshold = 5) {
292
this.appName = appName;
293
this.threshold = threshold;
294
this.failures = 0;
295
this.isOpen = false;
296
this.lastFailure = null;
297
}
298
299
recordSuccess() {
300
this.failures = 0;
301
this.isOpen = false;
302
}
303
304
recordFailure() {
305
this.failures++;
306
this.lastFailure = Date.now();
307
308
if (this.failures >= this.threshold) {
309
this.isOpen = true;
310
console.warn(`Circuit breaker opened for ${this.appName}`);
311
}
312
}
313
314
canProceed() {
315
if (!this.isOpen) return true;
316
317
// Check if enough time has passed to try again
318
const timeSinceLastFailure = Date.now() - this.lastFailure;
319
return timeSinceLastFailure > 60000; // 1 minute cooldown
320
}
321
}
322
```
323
324
## Types
325
326
```javascript { .api }
327
interface SingleSpaCustomEventDetail {
328
newAppStatuses: SingleSpaNewAppStatus;
329
appsByNewStatus: SingleSpaAppsByNewStatus;
330
totalAppChanges: number;
331
originalEvent?: Event;
332
oldUrl: string;
333
newUrl: string;
334
navigationIsCanceled: boolean;
335
cancelNavigation?: () => void;
336
}
337
338
interface SingleSpaNewAppStatus {
339
[appName: string]: "MOUNTED" | "NOT_MOUNTED" | "NOT_LOADED" | "SKIP_BECAUSE_BROKEN";
340
}
341
342
interface SingleSpaAppsByNewStatus {
343
MOUNTED: string[];
344
NOT_MOUNTED: string[];
345
NOT_LOADED: string[];
346
SKIP_BECAUSE_BROKEN: string[];
347
}
348
```