feat(FN-1348): export HeartbeatMonitor and HeartbeatTriggerScheduler from @fusion/engine

This commit is contained in:
gsxdsm
2026-04-09 09:42:21 -07:00
parent 32798c0e88
commit ac07be790b
8 changed files with 578 additions and 20 deletions

View File

@@ -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,

View File

@@ -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;