// file: crates/ksp-app-raw-transaction-ingest-desk/frontend/ts/main.ts // version: 8 import "bootstrap"; import ResizeObserver from "resize-observer-polyfill"; import "simplebar"; import { listen } from "@tauri-apps/api/event"; import type { ShellStatusDto } from "./bindings/ksp_app_raw_transaction_ingest_desk/dto_common/ShellStatusDto.ts"; import type { RawIngestCommitment } from "./bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestCommitment.ts"; import type { RawIngestRouteId } from "./bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteId.ts"; import type { RawIngestRouteInventoryDto } from "./bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteInventoryDto.ts"; import type { RawIngestRouteRuntimeDto } from "./bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteRuntimeDto.ts"; import type { RawIngestRouteStartRequestDto } from "./bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteStartRequestDto.ts"; import type { RawIngestRouteStopRequestDto } from "./bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteStopRequestDto.ts"; import type { RawIngestRouteMonitoringDto } from "./bindings/ksp_app_raw_transaction_ingest_desk/route_monitoring/RawIngestRouteMonitoringDto.ts"; import { frontendDebug, frontendInfo, frontendTrace, frontendWarn, installFrontendConsoleBridge } from "./frontend_log"; import { invokeKsp } from "./invoke"; (window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; installFrontendConsoleBridge("main"); type ViewId = "routes" | "diagnostics"; const viewTitles: Record = { routes: "Routes", diagnostics: "Diagnostics", }; const activeRuntimes = new Map(); const lastRuntimes = new Map(); const routeMonitoring = new Map(); const routeCards = new Map(); let routeInventory: RawIngestRouteInventoryDto | null = null; let selectedProfileId: string | null = null; let selectedMonitoringKey: string | null = null; function isViewId(value: string | undefined): value is ViewId { return value === "routes" || value === "diagnostics"; } function activateView(viewId: ViewId, source: "startup" | "user"): void { document.querySelectorAll("[data-view-panel]").forEach(panel => { panel.hidden = panel.dataset.viewPanel !== viewId; }); document.querySelectorAll("[data-view]").forEach(button => { const active = button.dataset.view === viewId; button.classList.toggle("active", active); button.setAttribute("aria-current", active ? "page" : "false"); }); const header = document.querySelector("#headerViewTitle"); if (header) { header.textContent = viewTitles[viewId]; } document.title = `Raw Transaction Ingest Desk — ${viewTitles[viewId]}`; frontendTrace("main", "Raw Transaction Ingest Desk view DOM updated", { viewId, source }); } function bindFrontendInteractions(): void { document.addEventListener( "click", event => { const source = event.target; if (!(source instanceof Element)) { return; } const control = source.closest('button, a, input, select, textarea, [role="button"], [role="tab"], [data-view]'); if (!control) { return; } frontendTrace("main", "Raw Transaction Ingest Desk frontend control clicked", { controlId: control.id || null, role: control.getAttribute("role"), tagName: control.tagName.toLowerCase(), viewId: control.dataset.view ?? null, }); }, true, ); } function bindNavigation(): void { document.querySelectorAll("[data-view]").forEach(button => { button.addEventListener("click", () => { const viewId = button.dataset.view; frontendDebug("main", "Raw Transaction Ingest Desk navigation tab clicked", { viewId: viewId ?? "unknown" }); if (isViewId(viewId)) { activateView(viewId, "user"); } }); }); } function reasonLabel(reason: string | null): string { const labels: Record = { profile_unresolved: "profil Config non résolu", network_mismatch: "réseau Transport/Store incohérent", missing_http_get_block: "HTTP getBlock indisponible", missing_http_get_transaction: "HTTP getTransaction indisponible", missing_http_block_scan: "HTTP block scan incomplet", missing_ws_logs_capability: "capability WS Logs absente", missing_ws_block_capability: "capability WS Block absente", missing_helius_transaction_capability: "capability Helius Transaction absente", missing_yellowstone_grpc: "Yellowstone gRPC absent", missing_required_secret: "secret Config requis non résolu", }; if (reason === null) { return "composable depuis Config"; } return labels[reason] ?? reason; } function runtimeKey(profileId: string, routeId: RawIngestRouteId): string { return `${profileId}::${routeId}`; } function lifecycleBadgeClass(state: string): string { if (state === "faulted") { return "badge text-bg-danger"; } if (state === "running") { return "badge text-bg-success"; } if (state === "starting" || state === "stopping") { return "badge text-bg-primary"; } if (state === "configured") { return "badge text-bg-info"; } return "badge text-bg-secondary"; } function yesNo(value: boolean): string { return value ? "oui" : "non"; } function optionalText(value: string | null): string { return value ?? "—"; } function safeCommandErrorText(caughtError: unknown, fallback: string): string { if (typeof caughtError === "object" && caughtError !== null) { const candidate = caughtError as { code?: unknown; domain?: unknown; message?: unknown }; if (typeof candidate.domain === "string" && typeof candidate.code === "string" && typeof candidate.message === "string") { return `${candidate.domain}/${candidate.code}: ${candidate.message}`; } } return fallback; } function renderRouteFeedback(message: string, level: "danger" | "info" | "success"): void { const feedback = document.querySelector("#routeCommandFeedback"); if (!feedback) { return; } feedback.hidden = false; feedback.classList.remove("alert-danger", "alert-info", "alert-success"); feedback.classList.add(`alert-${level}`); feedback.textContent = message; } function clearRouteFeedback(): void { const feedback = document.querySelector("#routeCommandFeedback"); if (!feedback) { return; } feedback.hidden = true; feedback.textContent = ""; } function monitoringRuntime(status: RawIngestRouteMonitoringDto): RawIngestRouteRuntimeDto { return { commitment: status.commitment, inventoryGeneration: status.inventoryGeneration, network: status.network, profileId: status.profileId, routeId: status.routeId, state: status.state, }; } function recordRouteMonitoring(status: RawIngestRouteMonitoringDto): void { const key = runtimeKey(status.profileId, status.routeId); const runtime = monitoringRuntime(status); routeMonitoring.set(key, status); lastRuntimes.set(key, runtime); if (runtimeIsActive(runtime)) { activeRuntimes.set(key, runtime); } else { activeRuntimes.delete(key); } } function selectRouteMonitoring(profileId: string, routeId: RawIngestRouteId, family: string, source: "event" | "user"): void { const key = runtimeKey(profileId, routeId); selectedMonitoringKey = key; renderMonitoringDetail(); frontendDebug("main", "Raw Transaction Ingest Desk route supervision selected", { family, routeId, selected: routeMonitoring.has(key), source, }); } function renderSelectedProfile(): void { if (routeInventory === null) { return; } const profile = routeInventory.profiles.find(candidate => candidate.profileId === selectedProfileId) ?? routeInventory.profiles[0]; if (!profile) { return; } selectedProfileId = profile.profileId; text("#routeNetwork", profile.network ?? "non résolu"); text("#routeSelectedProfile", profile.profileId); text("#headerProfile", profile.profileId); const routeRoot = document.querySelector("#routeFoundation"); if (routeRoot) { routeCards.clear(); routeRoot.replaceChildren(); for (const route of profile.routes) { const key = runtimeKey(profile.profileId, route.routeId); const runtime = activeRuntimes.get(key) ?? lastRuntimes.get(key) ?? null; const monitoring = routeMonitoring.get(key) ?? null; const projectedState = monitoring?.state ?? runtime?.state ?? route.state; const active = projectedState === "starting" || projectedState === "running" || projectedState === "stopping"; const column = document.createElement("div"); column.className = "col-12 col-xl-6 app-route-column"; const card = document.createElement("div"); card.className = "card h-100 shadow-sm app-route-card"; const body = document.createElement("div"); body.className = "card-body"; const heading = document.createElement("div"); heading.className = "d-flex align-items-start justify-content-between gap-3"; const headingText = document.createElement("div"); const title = document.createElement("h2"); title.className = "h6 mb-1"; title.textContent = route.label; const code = document.createElement("div"); code.className = "app-route-code text-body-secondary small"; code.textContent = route.routeId; headingText.append(title, code); const family = document.createElement("span"); family.className = "badge text-bg-light border text-dark"; family.textContent = route.family; heading.append(headingText, family); const meta = document.createElement("div"); meta.className = "d-flex flex-wrap gap-2 mt-3 app-route-meta"; const state = document.createElement("span"); state.className = lifecycleBadgeClass(projectedState); state.dataset.monitoringField = "state"; state.textContent = projectedState; const network = document.createElement("span"); network.className = "badge text-bg-light border text-dark"; network.textContent = route.network ?? "network unresolved"; const reason = document.createElement("span"); reason.className = route.selectable ? "badge text-bg-light border text-dark" : "badge text-bg-warning"; reason.textContent = reasonLabel(route.reason); meta.append(state, network, reason); body.append(heading, meta); if (monitoring) { const summary = document.createElement("div"); summary.className = "app-route-monitoring-summary mt-3"; const health = document.createElement("span"); health.className = monitoring.health === "healthy" ? "badge text-bg-success" : "badge text-bg-warning"; health.dataset.monitoringField = "health"; health.textContent = `health: ${monitoring.health}`; const activity = document.createElement("span"); activity.className = "badge text-bg-light border text-dark"; activity.dataset.monitoringField = "activity"; activity.textContent = `activity: ${monitoring.activity}`; const persisted = document.createElement("span"); persisted.className = "badge text-bg-light border text-dark"; persisted.dataset.monitoringField = "persisted"; persisted.textContent = `persisted: ${monitoring.persistedTotal}`; const sources = document.createElement("span"); sources.className = "badge text-bg-light border text-dark"; sources.dataset.monitoringField = "sources"; sources.textContent = `sources: ${monitoring.sourceActive}/${monitoring.sourceTotal}`; const gaps = document.createElement("span"); gaps.className = monitoring.openGapCount === 0 ? "badge text-bg-light border text-dark" : "badge text-bg-warning"; gaps.dataset.monitoringField = "gaps"; gaps.textContent = `gaps: ${monitoring.openGapCount}`; summary.append(health, activity, persisted, sources, gaps); body.append(summary); } const actions = document.createElement("div"); actions.className = "d-flex flex-wrap gap-2 mt-3"; if (route.selectable) { const start = document.createElement("button"); start.className = "btn btn-primary btn-sm"; start.type = "button"; start.textContent = "Start"; start.disabled = active; start.dataset.routeId = route.routeId; start.addEventListener("click", () => { selectedMonitoringKey = key; clearRouteFeedback(); void startRoute(route.routeId).catch(caughtError => { renderRouteFeedback(safeCommandErrorText(caughtError, "Le démarrage de la route a été refusé."), "danger"); frontendWarn("main", "Raw Transaction Ingest Desk route Start failed", { routeId: route.routeId }); }); }); actions.append(start); if (active && runtime !== null) { const stop = document.createElement("button"); stop.className = "btn btn-outline-danger btn-sm"; stop.type = "button"; stop.textContent = "Stop"; stop.disabled = projectedState === "stopping"; stop.dataset.routeId = route.routeId; stop.addEventListener("click", () => { clearRouteFeedback(); void stopRoute(runtime).catch(caughtError => { renderRouteFeedback(safeCommandErrorText(caughtError, "L'arrêt de la route a été refusé."), "danger"); frontendWarn("main", "Raw Transaction Ingest Desk route Stop failed", { routeId: route.routeId }); }); }); actions.append(stop); } } if (monitoring) { const supervise = document.createElement("button"); supervise.className = selectedMonitoringKey === key ? "btn btn-secondary btn-sm" : "btn btn-outline-secondary btn-sm"; supervise.type = "button"; supervise.textContent = "Supervision"; supervise.dataset.routeId = route.routeId; supervise.addEventListener("click", () => selectRouteMonitoring(profile.profileId, route.routeId, route.family, "user")); actions.append(supervise); } if (actions.childElementCount > 0) { body.append(actions); } card.append(body); routeCards.set(key, card); column.append(card); routeRoot.append(column); } } renderMonitoringDetail(); frontendTrace("main", "Raw Transaction Ingest Desk Config route inventory profile rendered", { activeRouteCount: activeRuntimes.size, monitoringCount: routeMonitoring.size, profileId: profile.profileId, routeCount: profile.routes.length, selectableCount: profile.routes.filter(route => route.selectable).length, }); } function renderMonitoringDetail(): void { const panel = document.querySelector("#routeMonitoringDetail"); if (!panel) { return; } const monitoring = selectedMonitoringKey === null ? null : (routeMonitoring.get(selectedMonitoringKey) ?? null); if (!monitoring) { panel.hidden = true; return; } panel.hidden = false; text("#monitorRouteId", monitoring.routeId); text("#monitorNetwork", monitoring.network); text("#monitorCommitment", monitoring.commitment); text("#monitorSequence", monitoring.sequence); text("#monitorHealth", monitoring.health); text("#monitorActivity", monitoring.activity); text("#monitorSourceState", optionalText(monitoring.sourceState)); text("#monitorFault", monitoring.faultDomain === null || monitoring.faultCode === null ? "—" : `${monitoring.faultDomain}/${monitoring.faultCode}`); const lifecycle = document.querySelector("#monitorLifecycle"); if (lifecycle) { lifecycle.className = lifecycleBadgeClass(monitoring.state); lifecycle.textContent = monitoring.state; } const health = document.querySelector("#monitorHealthBadge"); if (health) { health.className = monitoring.health === "healthy" ? "badge text-bg-success" : "badge text-bg-warning"; health.textContent = monitoring.health; } const activity = document.querySelector("#monitorActivityBadge"); if (activity) { activity.className = "badge text-bg-light border text-dark"; activity.textContent = monitoring.activity; } const values: Record = { monitorAdmissionQueue: `${monitoring.admissionQueueDepth} / ${monitoring.admissionQueueCapacity}`, monitorPersistence: `${monitoring.inFlightPersistence} / ${monitoring.persistenceConcurrency}`, monitorAdmittedTotal: monitoring.admittedTotal, monitorCanonicalizedTotal: monitoring.canonicalizedTotal, monitorPersistedTotal: monitoring.persistedTotal, monitorEntityInsertedTotal: monitoring.entityInsertedTotal, monitorEntityAlreadyPresentTotal: monitoring.entityAlreadyPresentTotal, monitorEntitySkippedPurgedTotal: monitoring.entitySkippedPurgedTotal, monitorObservationInsertedTotal: monitoring.observationInsertedTotal, monitorObservationAlreadyPresentTotal: monitoring.observationAlreadyPresentTotal, monitorContentConflictTotal: monitoring.contentConflictTotal, monitorStoreFailureTotal: monitoring.storeFailureTotal, monitorSourceFailureTotal: monitoring.sourceFailureTotal, monitorBackpressureWaitTotal: monitoring.backpressureWaitTotal, monitorHydrationPending: monitoring.hydrationPending.toString(), monitorProcessingFrontier: optionalText(monitoring.processingFrontierSlot), monitorOldestPending: optionalText(monitoring.oldestPendingSlot), monitorSourceCounts: `${monitoring.sourceActive} active / ${monitoring.sourceReconnecting} reconnecting / ${monitoring.sourceFailed} failed / ${monitoring.sourceTotal} total`, monitorSourceReconnectTotal: monitoring.sourceReconnectTotal, monitorSourceReplayAttemptTotal: monitoring.sourceReplayAttemptTotal, monitorSourceContinuityGapTotal: monitoring.sourceContinuityGapTotal, monitorContinuityPolicyObserved: yesNo(monitoring.continuityPolicyObserved), monitorContinuityFrontier: optionalText(monitoring.continuityFrontierSlot), monitorContinuityHasOpenGaps: yesNo(monitoring.continuityHasOpenGaps), monitorFailedSourceLossesReconciled: yesNo(monitoring.failedSourceLossesReconciled), monitorFutureTargetCoverage: yesNo(monitoring.futureTargetCoverage), monitorOpenGapCount: monitoring.openGapCount.toString(), monitorRepairingGapCount: monitoring.repairingGapCount.toString(), monitorRepairedGapTotal: monitoring.repairedGapTotal, monitorUnresolvedGapTotal: monitoring.unresolvedGapTotal, monitorOldestOpenGap: optionalText(monitoring.oldestOpenGapStartSlot), monitorReplayRepairTotal: monitoring.replayRepairTotal, monitorRedundantCoverageRepairTotal: monitoring.redundantCoverageRepairTotal, monitorHttpScanRepairTotal: monitoring.httpScanRepairTotal, monitorRepairBlockFetchTotal: monitoring.repairBlockFetchTotal, monitorRepairTransactionHydrationTotal: monitoring.repairTransactionHydrationTotal, }; for (const [id, value] of Object.entries(values)) { text(`#${id}`, value); } const gaps = document.querySelector("#monitorGapList"); if (gaps) { gaps.replaceChildren(); if (monitoring.gaps.length === 0) { const empty = document.createElement("div"); empty.className = "text-body-secondary small"; empty.textContent = "Aucun gap courant ou récent projeté."; gaps.append(empty); } else { for (const gap of monitoring.gaps) { const item = document.createElement("div"); item.className = "list-group-item"; const top = document.createElement("div"); top.className = "d-flex justify-content-between gap-3 flex-wrap"; const range = document.createElement("span"); range.className = "app-route-code"; range.textContent = `${gap.startSlot} – ${gap.endSlot}`; const badge = document.createElement("span"); badge.className = gap.state === "open" ? "badge text-bg-warning" : "badge text-bg-secondary"; badge.textContent = gap.state; top.append(range, badge); const meta = document.createElement("div"); meta.className = "small text-body-secondary mt-1"; meta.textContent = `gap ${gap.gapId} · ${gap.reason} · repair ${gap.lastMethod ?? "—"}`; item.append(top, meta); gaps.append(item); } } } frontendTrace("main", "Raw Transaction Ingest Desk route monitoring detail rendered", { gapCount: monitoring.gaps.length, routeId: monitoring.routeId, sequence: monitoring.sequence, state: monitoring.state, }); } function renderRouteMonitoringCard(status: RawIngestRouteMonitoringDto): boolean { const key = runtimeKey(status.profileId, status.routeId); const card = routeCards.get(key); if (!card) { return false; } const field = (name: string): HTMLElement | null => card.querySelector(`[data-monitoring-field="${name}"]`); const state = field("state"); const health = field("health"); const activity = field("activity"); const persisted = field("persisted"); const sources = field("sources"); const gaps = field("gaps"); if (!state || !health || !activity || !persisted || !sources || !gaps) { return false; } state.className = lifecycleBadgeClass(status.state); state.textContent = status.state; health.className = status.health === "healthy" ? "badge text-bg-success" : "badge text-bg-warning"; health.textContent = `health: ${status.health}`; activity.textContent = `activity: ${status.activity}`; persisted.textContent = `persisted: ${status.persistedTotal}`; sources.textContent = `sources: ${status.sourceActive}/${status.sourceTotal}`; gaps.className = status.openGapCount === 0 ? "badge text-bg-light border text-dark" : "badge text-bg-warning"; gaps.textContent = `gaps: ${status.openGapCount}`; return true; } function selectedCommitment(): RawIngestCommitment { const selector = document.querySelector("#routeCommitment"); return selector?.value === "finalized" ? "finalized" : "confirmed"; } function runtimeIsActive(runtime: RawIngestRouteRuntimeDto): boolean { return runtime.state === "starting" || runtime.state === "running" || runtime.state === "stopping"; } function renderMultiRouteRuntime(): void { const active = Array.from(activeRuntimes.values()); const state = document.querySelector("#routeRuntimeState"); if (state) { state.textContent = active.length === 0 ? "idle" : `${active.length} active`; state.className = active.length === 0 ? "badge text-bg-secondary" : "badge text-bg-primary"; } const identities = active.map(runtime => `${runtime.routeId}/${runtime.commitment}`).join(", "); text("#routeRuntimeIdentity", identities.length === 0 ? "aucun Worker actif" : identities); const store = document.querySelector("#headerStore"); if (store) { store.textContent = active.length === 0 ? "Store idle" : "Store shared"; store.className = active.length === 0 ? "badge text-bg-secondary" : "badge text-bg-success"; } const profile = document.querySelector("#routeProfile"); if (profile) { profile.disabled = active.length > 0; } } async function startRoute(routeId: RawIngestRouteId): Promise { if (routeInventory === null || selectedProfileId === null) { return; } const key = runtimeKey(selectedProfileId, routeId); const existing = activeRuntimes.get(key); if (existing !== undefined && runtimeIsActive(existing)) { return; } const request: RawIngestRouteStartRequestDto = { commitment: selectedCommitment(), inventoryGeneration: routeInventory.generation, profileId: selectedProfileId, routeId, }; frontendDebug("main", "Raw Transaction Ingest Desk route Start requested", { commitment: request.commitment, profileId: request.profileId, routeId: request.routeId, }); const response = await invokeKsp("main", "start_route", { request }); lastRuntimes.set(key, response); if (runtimeIsActive(response)) { activeRuntimes.set(key, response); } else { activeRuntimes.delete(key); } renderMultiRouteRuntime(); renderSelectedProfile(); renderRouteFeedback(`Route ${response.routeId} démarrée (${response.state}).`, "success"); await syncRouteMonitoring("start"); frontendInfo("main", "Raw Transaction Ingest Desk route Start acknowledged", { activeRouteCount: activeRuntimes.size, profileId: response.profileId, routeId: response.routeId, state: response.state, }); } async function stopRoute(runtime: RawIngestRouteRuntimeDto): Promise { const key = runtimeKey(runtime.profileId, runtime.routeId); const request: RawIngestRouteStopRequestDto = { profileId: runtime.profileId, routeId: runtime.routeId, }; frontendDebug("main", "Raw Transaction Ingest Desk targeted route Stop requested", { profileId: request.profileId, routeId: request.routeId, }); const response = await invokeKsp("main", "stop_route", { request }); lastRuntimes.set(key, response); if (runtimeIsActive(response)) { activeRuntimes.set(key, response); } else { activeRuntimes.delete(key); } renderMultiRouteRuntime(); renderSelectedProfile(); renderRouteFeedback(`Route ${response.routeId} arrêtée (${response.state}).`, "info"); await syncRouteMonitoring("stop"); frontendInfo("main", "Raw Transaction Ingest Desk targeted route Stop completed", { activeRouteCount: activeRuntimes.size, profileId: response.profileId, routeId: response.routeId, state: response.state, }); } function applyRouteMonitoring(status: RawIngestRouteMonitoringDto, source: "event" | "startup" | "start" | "stop" | "user"): void { const key = runtimeKey(status.profileId, status.routeId); const previous = routeMonitoring.get(key) ?? null; recordRouteMonitoring(status); if (selectedMonitoringKey === null && status.profileId === selectedProfileId) { selectedMonitoringKey = key; } renderMultiRouteRuntime(); const lifecycleChanged = previous === null || previous.state !== status.state; if (lifecycleChanged || !renderRouteMonitoringCard(status)) { renderSelectedProfile(); } else if (selectedMonitoringKey === key) { renderMonitoringDetail(); } frontendTrace("main", "Raw Transaction Ingest Desk route monitoring latest value applied", { lifecycleChanged, routeId: status.routeId, sequence: status.sequence, source, state: status.state, terminal: status.terminal, }); } async function syncRouteMonitoring(source: "startup" | "start" | "stop" | "user"): Promise { frontendDebug("main", "Raw Transaction Ingest Desk route monitoring resynchronization requested", { source }); const statuses = await invokeKsp("main", "get_route_monitoring"); routeMonitoring.clear(); activeRuntimes.clear(); lastRuntimes.clear(); for (const status of statuses) { recordRouteMonitoring(status); } if (selectedMonitoringKey !== null && !routeMonitoring.has(selectedMonitoringKey)) { selectedMonitoringKey = null; } if (selectedMonitoringKey === null) { const preferred = statuses.find(status => status.profileId === selectedProfileId) ?? statuses[0]; if (preferred) { selectedMonitoringKey = runtimeKey(preferred.profileId, preferred.routeId); } } renderMultiRouteRuntime(); renderSelectedProfile(); frontendDebug("main", "Raw Transaction Ingest Desk route monitoring resynchronization completed", { activeRouteCount: activeRuntimes.size, source, statusCount: statuses.length, }); } async function bindRouteMonitoring(): Promise { await listen("ksp-raw-ingest-route-status", event => { applyRouteMonitoring(event.payload, "event"); }); document.querySelector("#refreshRouteMonitoring")?.addEventListener("click", () => { frontendDebug("main", "Raw Transaction Ingest Desk route monitoring refresh button clicked"); void syncRouteMonitoring("user").catch(caughtError => { renderRouteFeedback(safeCommandErrorText(caughtError, "La resynchronisation du monitoring a échoué."), "danger"); frontendWarn("main", "Raw Transaction Ingest Desk route monitoring resynchronization failed"); }); }); frontendTrace("main", "Raw Transaction Ingest Desk route monitoring listener and resynchronization control installed"); } function renderRouteInventory(inventory: RawIngestRouteInventoryDto): void { routeInventory = inventory; const requestedProfile = selectedProfileId; const selectedStillExists = requestedProfile !== null && inventory.profiles.some(profile => profile.profileId === requestedProfile); selectedProfileId = selectedStillExists ? requestedProfile : inventory.defaultProfile; const selector = document.querySelector("#routeProfile"); if (selector) { selector.replaceChildren(); for (const profile of inventory.profiles) { const option = document.createElement("option"); option.value = profile.profileId; option.textContent = profile.isDefault ? `${profile.profileId} (default)` : profile.profileId; option.selected = profile.profileId === selectedProfileId; selector.append(option); } } text("#routeInventoryGeneration", inventory.generation.toString()); text("#runtimeInventoryGeneration", inventory.generation.toString()); text("#runtimeInventoryDefaultProfile", inventory.defaultProfile); renderSelectedProfile(); renderMultiRouteRuntime(); frontendTrace("main", "Raw Transaction Ingest Desk Config route inventory rendered", { generation: inventory.generation, profileCount: inventory.profiles.length, }); } function text(selector: string, value: string): void { const element = document.querySelector(selector); if (element) { element.textContent = value; } } function renderRuntimeStatus(status: ShellStatusDto): void { text("#runtimeVersion", status.applicationVersion); text("#runtimeShellPhase", status.shellPhase); text("#runtimeConfigDocuments", status.configDocumentCount.toString()); text("#runtimeCompositeProfile", status.activeCompositeProfile ?? "fallback/unresolved"); text("#runtimeLoggingProfile", status.activeLoggingProfile ?? "fallback"); text("#runtimeLoggingFallback", status.fallbackLoggingActive ? "oui" : "non"); text("#headerPhase", status.shellPhase); const diagnostic = document.querySelector("#runtimeDiagnostic"); if (diagnostic) { diagnostic.hidden = status.startupDiagnostic === null; diagnostic.textContent = status.startupDiagnostic ? `${status.startupDiagnostic.domain}/${status.startupDiagnostic.code}: ${status.startupDiagnostic.message}` : ""; } frontendTrace("main", "Raw Transaction Ingest Desk runtime status rendered", { fallbackLoggingActive: status.fallbackLoggingActive, hasStartupDiagnostic: status.startupDiagnostic !== null, }); } async function refreshRouteInventory(): Promise { frontendDebug("main", "Raw Transaction Ingest Desk Config route inventory refresh requested"); const inventory = await invokeKsp("main", "get_route_inventory"); renderRouteInventory(inventory); } async function refreshRuntimeStatus(): Promise { frontendDebug("main", "Raw Transaction Ingest Desk runtime status refresh requested"); const status = await invokeKsp("main", "get_runtime_status"); renderRuntimeStatus(status); } function bindRefreshControls(): void { document.querySelector("#refreshRouteInventory")?.addEventListener("click", () => { void refreshRouteInventory().catch(() => frontendWarn("main", "Raw Transaction Ingest Desk Config route inventory refresh failed")); }); document.querySelector("#refreshRuntimeStatus")?.addEventListener("click", () => { void refreshRuntimeStatus().catch(() => frontendWarn("main", "Raw Transaction Ingest Desk runtime status refresh failed")); }); document.querySelector("#routeProfile")?.addEventListener("change", event => { const selector = event.currentTarget; if (!(selector instanceof HTMLSelectElement)) { return; } selectedProfileId = selector.value; selectedMonitoringKey = null; const selected = routeInventory?.profiles.find(profile => profile.profileId === selectedProfileId) ?? null; frontendDebug("main", "Raw Transaction Ingest Desk logical network selected", { profileId: selectedProfileId, routeCount: selected?.routes.length ?? 0, selectionResolved: selected !== null, }); renderSelectedProfile(); }); document.querySelector("#routeCommitment")?.addEventListener("change", event => { const selector = event.currentTarget; if (!(selector instanceof HTMLSelectElement)) { return; } frontendDebug("main", "Raw Transaction Ingest Desk commitment selection changed", { commitment: selectedCommitment() }); }); } async function initializeMain(): Promise { frontendInfo("main", "Raw Transaction Ingest Desk main frontend loaded"); bindFrontendInteractions(); bindNavigation(); bindRefreshControls(); await bindRouteMonitoring(); activateView("routes", "startup"); await refreshRouteInventory(); await Promise.all([refreshRuntimeStatus(), syncRouteMonitoring("startup")]); frontendInfo("main", "Raw Transaction Ingest Desk multi-route shared-Store runtime frontend ready"); } void initializeMain().catch(() => frontendWarn("main", "Raw Transaction Ingest Desk frontend initialization failed"));