feat(FN-1348): export HeartbeatMonitor and HeartbeatTriggerScheduler from @fusion/engine
This commit is contained in:
@@ -3074,6 +3074,11 @@ export function fetchMissionHealth(missionId: string, projectId?: string): Promi
|
||||
return api<MissionHealth>(withProjectId(`/missions/${encodeURIComponent(missionId)}/health`, projectId));
|
||||
}
|
||||
|
||||
/** Fetch health metrics for all missions in a single batched request. */
|
||||
export function fetchMissionsHealth(projectId?: string): Promise<Record<string, MissionHealth>> {
|
||||
return api<Record<string, MissionHealth>>(withProjectId("/missions/health", projectId));
|
||||
}
|
||||
|
||||
/** Add milestone to mission */
|
||||
export function createMilestone(
|
||||
missionId: string,
|
||||
|
||||
@@ -71,6 +71,7 @@ import {
|
||||
startMissionAutopilot,
|
||||
stopMissionAutopilot,
|
||||
fetchMissionHealth,
|
||||
fetchMissionsHealth,
|
||||
fetchMissionEvents,
|
||||
} from "../api";
|
||||
import type { AutopilotStatus as AutopilotStatusType, AutopilotState } from "./mission-types";
|
||||
@@ -429,21 +430,14 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
return;
|
||||
}
|
||||
|
||||
const healthResults = await Promise.allSettled(
|
||||
missionList.map(async (mission) => {
|
||||
const health = await fetchMissionHealth(mission.id, projectId);
|
||||
return [mission.id, health] as const;
|
||||
}),
|
||||
);
|
||||
// Use batched endpoint for optimal performance (1 request instead of N)
|
||||
const healthRecord = await fetchMissionsHealth(projectId);
|
||||
|
||||
setMissionHealthById((prev) => {
|
||||
const next = new Map(prev);
|
||||
for (const result of healthResults) {
|
||||
if (result.status === "fulfilled") {
|
||||
const [missionId, health] = result.value;
|
||||
if (isMissionHealth(health)) {
|
||||
next.set(missionId, health);
|
||||
}
|
||||
for (const [missionId, health] of Object.entries(healthRecord)) {
|
||||
if (isMissionHealth(health)) {
|
||||
next.set(missionId, health);
|
||||
}
|
||||
}
|
||||
return next;
|
||||
|
||||
@@ -251,22 +251,34 @@ export function createMissionRouter(
|
||||
/**
|
||||
* GET /api/missions
|
||||
* List all missions ordered by createdAt desc, with status summary
|
||||
* Uses batched query for optimal performance.
|
||||
*/
|
||||
router.get(
|
||||
"/",
|
||||
catchTypedHandler(async (_req, res) => {
|
||||
const missions = missionStore.listMissions();
|
||||
// Sort by createdAt desc
|
||||
missions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
||||
// Attach status summary to each mission
|
||||
const missionsWithSummary = missions.map((mission) => ({
|
||||
...mission,
|
||||
summary: missionStore.getMissionSummary(mission.id),
|
||||
}));
|
||||
const missionsWithSummary = missionStore.listMissionsWithSummaries();
|
||||
res.json(missionsWithSummary);
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* GET /api/missions/health
|
||||
* Get health metrics for all missions in a single batched request.
|
||||
* Returns a map of mission ID → health object.
|
||||
*/
|
||||
router.get(
|
||||
"/health",
|
||||
catchTypedHandler(async (_req, res) => {
|
||||
const healthMap = missionStore.listMissionsHealth();
|
||||
// Convert Map to Record for JSON serialization
|
||||
const result: Record<string, ReturnType<typeof healthMap.get>> = {};
|
||||
for (const [missionId, health] of healthMap) {
|
||||
result[missionId] = health;
|
||||
}
|
||||
res.json(result);
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* POST /api/missions
|
||||
* Create a new mission
|
||||
|
||||
Reference in New Issue
Block a user