diff --git a/.forgejo/workflows/dimensionlab-website.yml b/.forgejo/workflows/dimensionlab-website.yml
deleted file mode 100644
index 198a8aa..0000000
--- a/.forgejo/workflows/dimensionlab-website.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-name: Dimension Lab website
-
-on:
- pull_request:
- types:
- - opened
- - synchronize
- - reopened
- push:
- branches:
- - main
- workflow_dispatch:
-
-concurrency:
- group: dimensionlab-website-${{ github.ref }}
- cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
-
-jobs:
- ci:
- runs-on: docker
- timeout-minutes: 30
- steps:
- - name: Checkout
- uses: https://data.forgejo.org/actions/checkout@v4
- with:
- fetch-depth: 0
- submodules: false
-
- - name: Initialize submodules
- run: |
- git config --global url."https://git.dimensionlab.net/".insteadOf "ssh://git@git.dimensionlab.net/"
- git submodule update --init --recursive
-
- - name: Install Bun
- run: |
- curl -fsSL https://bun.sh/install | bash -s "bun-v1.3.14"
- "$HOME/.bun/bin/bun" --version
-
- - name: Check, test, and build
- run: |
- export BUN_INSTALL="$HOME/.bun"
- export PATH="$BUN_INSTALL/bin:$PATH"
- bun install --frozen-lockfile
- bun run check
- bun run test
- bun run build
-
- deploy:
- needs: ci
- if: github.event_name == 'push' && github.ref == 'refs/heads/main'
- runs-on: deploy
- timeout-minutes: 30
- steps:
- - name: Checkout
- run: |
- if [ -d .git ]; then
- git remote set-url origin git@git.dimensionlab.net:vince/dimensionlab-website.git
- else
- git init
- git remote add origin git@git.dimensionlab.net:vince/dimensionlab-website.git
- fi
- git fetch --force --prune --depth=1 origin "$GITHUB_SHA"
- git checkout --force --detach "$GITHUB_SHA"
- git clean -ffdx
-
- - name: Initialize submodules
- run: |
- git config --global url."https://git.dimensionlab.net/".insteadOf "ssh://git@git.dimensionlab.net/"
- git submodule update --init --recursive
-
- - name: Verify Podman deployment socket
- run: |
- command -v podman
- command -v systemctl
- unit="$(timeout 15s podman inspect dimensionlab-website --format '{{ index .Config.Labels "PODMAN_SYSTEMD_UNIT" }}')"
- test "$unit" = "dimensionlab-website.service"
-
- - name: Deploy production website
- env:
- DEPLOY_CONTAINER_CLI: podman
- DEPLOY_EVENT_NAME: ${{ github.event_name }}
- DEPLOY_REF: ${{ github.ref }}
- DEPLOY_RESTART_STRATEGY: quadlet-container
- DEPLOY_SHA: ${{ github.sha }}
- run: scripts/deploy-dimensionlab-website.sh
diff --git a/README.md b/README.md
index 85177c5..00d9708 100644
--- a/README.md
+++ b/README.md
@@ -161,27 +161,3 @@ The env file must provide `AGENT_CONFIG_TOKEN`. Runtime defaults inside the
image set `HOST=0.0.0.0`, `PORT=3000`,
`DATABASE_URL=file:/data/dimensionlab.sqlite`, and
`DASHBOARD_MIGRATIONS_DIR=/repo/apps/web/drizzle`.
-
-### Forgejo Actions Deployment
-
-Merges to `main` run `.forgejo/workflows/dimensionlab-website.yml`. Pull
-requests run check, test, and build only; the deploy job is guarded to run only
-for `push` events on `refs/heads/main`.
-
-The workflow uses two runner classes. Pull request CI runs on the containerized
-`docker` runner. Production deployment runs on a separate host runner with the
-`deploy:host` label so the guarded deploy script can use the user's rootless
-`podman` and `systemctl --user` commands directly. The deploy job uses a
-shell-only `git fetch` checkout so the host runner does not need a Node runtime
-for checkout actions.
-
-```yaml
-runner:
- labels:
- - deploy:host
-```
-
-The deploy job also performs a host preflight against the
-`dimensionlab-website.service` Podman label before it builds or restarts the
-production container. Do not give the general pull request runner deployment
-socket access; keep deploy privileges on the dedicated `deploy` runner.
diff --git a/apps/web/Containerfile b/apps/web/Containerfile
index 32387a4..9ec2baa 100644
--- a/apps/web/Containerfile
+++ b/apps/web/Containerfile
@@ -11,7 +11,7 @@ RUN bunx turbo prune @dimensionlab/web --docker
FROM base AS deps
COPY --from=pruner /repo/out/json/ ./
-RUN bun install --frozen-lockfile --ignore-scripts
+RUN bun install --frozen-lockfile
FROM deps AS build
diff --git a/apps/web/package.json b/apps/web/package.json
index 015fc55..790663c 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -8,8 +8,8 @@
"build": "rm -rf build && vite build && bun build src/server/index.ts --target bun --outdir build",
"preview": "HOST=0.0.0.0 PORT=4173 bun build/index.js",
"check": "tsc --noEmit",
- "test": "bun --bun vitest run",
- "test:unit": "bun --bun vitest run",
+ "test": "vitest run",
+ "test:unit": "vitest run",
"test:e2e": "env -u NO_COLOR playwright test",
"db:generate": "drizzle-kit generate",
"db:check": "drizzle-kit check"
diff --git a/apps/web/src/App.test.tsx b/apps/web/src/App.test.tsx
index c1add67..fbb133d 100644
--- a/apps/web/src/App.test.tsx
+++ b/apps/web/src/App.test.tsx
@@ -1,13 +1,6 @@
import { renderToString } from "react-dom/server";
import { describe, expect, test } from "vitest";
-import { genericDashboardFixture } from "@dimensionlab/dashboard-model/fixtures";
-import { dimensionLabDashboardFixture } from "$lib/dashboard-seed/dimensionlab";
-import {
- AppStateView,
- dashboardTileMatchKey,
- dashboardHydrationTiles,
- restoreDashboardTileSnapshots,
-} from "./App";
+import { AppStateView } from "./App";
describe("React app dashboard state view", () => {
test("renders loading dashboard state", () => {
@@ -48,191 +41,4 @@ describe("React app dashboard state view", () => {
expect(html).toContain("Dark");
expect(html).toContain("Light");
});
-
- test("renders the dashboard shell while individual items hydrate", () => {
- const html = renderToString(
- ,
- );
-
- expect(html).toContain("Operations Console");
- expect(html).toContain("Service Uptime");
- expect(html).toContain("Identity");
- expect(html).toContain("Environment");
- expect(html).not.toContain("Loading Dashboard");
- expect(html).toContain(
- 'data-severity="loading" data-model-id="service-uptime"',
- );
- expect(html).toContain('data-severity="loading" data-model-id="identity"');
- expect(html).toContain('data-severity="loading" data-model-id="ambient"');
- expect(html).toContain('data-severity="loading" data-model-id="runtime:status"');
- });
-
- test("hydrates every status cell from the Dimension Lab shell", () => {
- expect(dashboardHydrationTiles(dimensionLabDashboardFixture)).toContainEqual({
- kind: "status",
- stripId: "footer-status",
- id: "auto-refresh",
- });
- });
-
- test("prioritizes status, telemetry, modules, then services for hydration", () => {
- const tiles = dashboardHydrationTiles(dimensionLabDashboardFixture);
- const kinds = tiles.map((tile) => tile.kind);
- const firstServiceIndex = kinds.indexOf("service");
-
- expect(kinds.slice(0, 5)).toEqual([
- "status",
- "status",
- "status",
- "status",
- "status",
- ]);
- expect(kinds.lastIndexOf("telemetry")).toBeLessThan(kinds.indexOf("module"));
- expect(kinds.lastIndexOf("module")).toBeLessThan(firstServiceIndex);
- expect(
- tiles.filter((tile) => tile.kind === "telemetry").map((tile) => tile.id),
- ).toEqual(
- dimensionLabDashboardFixture.telemetry
- .filter((card) => card.datasource?.type === "external")
- .map((card) => card.id),
- );
- expect(
- tiles.filter((tile) => tile.kind === "service").map((tile) => ({
- groupId: tile.groupId,
- id: tile.id,
- })),
- ).toEqual(
- dimensionLabDashboardFixture.serviceGroups.flatMap((group) =>
- group.services
- .filter((service) => service.datasource?.type === "external")
- .map((service) => ({
- groupId: group.id,
- id: service.id,
- }))
- ),
- );
- });
-
- test("applies restored tile snapshots without marking them as loading", () => {
- const restored = restoreDashboardTileSnapshots(
- {
- state: "ready",
- document: dimensionLabDashboardFixture,
- schemaVersion: "dashboard.v1",
- currentRevisionId: "revision-a",
- },
- [
- {
- ageMs: 60_000,
- response: {
- state: "ready",
- tile: { kind: "telemetry", id: "infra-ram" },
- item: {
- ...dimensionLabDashboardFixture.telemetry[0],
- detail: "cached - stale 60s",
- severity: "stale",
- },
- },
- },
- ],
- );
-
- expect(restored.restoredItemIds).toEqual(new Set(["telemetry:infra-ram"]));
- if (restored.dashboard.state !== "ready") {
- throw new Error("Expected dashboard to be ready");
- }
- expect(restored.dashboard.document.telemetry[0]).toMatchObject({
- id: "infra-ram",
- detail: "cached - stale 60s",
- severity: "stale",
- });
- });
-
- test("does not restore aggregate health snapshots over the fresh shell", () => {
- const restored = restoreDashboardTileSnapshots(
- {
- state: "ready",
- document: dimensionLabDashboardFixture,
- schemaVersion: "dashboard.v1",
- currentRevisionId: "revision-a",
- },
- [
- {
- ageMs: 60_000,
- response: {
- state: "ready",
- tile: { kind: "status", stripId: "footer-status", id: "system-status" },
- item: {
- id: "system-status",
- label: "System Status",
- value: "20 services down",
- severity: "stale",
- },
- },
- },
- {
- ageMs: 60_000,
- response: {
- state: "ready",
- tile: { kind: "module", id: "runtime-health-summary" },
- item: {
- id: "runtime-health-summary",
- kind: "summary",
- title: "Runtime Health",
- value: "20 services down",
- detail: "0 warnings - 8 services ok - stale 60s",
- severity: "stale",
- },
- },
- },
- ],
- );
-
- expect(restored.restoredItemIds).toEqual(new Set());
- if (restored.dashboard.state !== "ready") {
- throw new Error("Expected dashboard to be ready");
- }
- expect(
- restored.dashboard.document.statusStrips[0].items.find((item) =>
- item.id === "system-status"
- ),
- ).toMatchObject({
- id: "system-status",
- value: "Fallback operational",
- });
- expect(
- restored.dashboard.document.modules?.find((module) =>
- module.id === "runtime-health-summary"
- ),
- ).toMatchObject({
- id: "runtime-health-summary",
- value: "fallback",
- });
- });
-
- test("uses structured tile match keys for delimiter-bearing ids", () => {
- expect(
- dashboardTileMatchKey({ kind: "service", groupId: "a:b", id: "c" }),
- ).not.toBe(
- dashboardTileMatchKey({ kind: "service", groupId: "a", id: "b:c" }),
- );
- expect(
- dashboardTileMatchKey({ kind: "status", stripId: "a:b", id: "c" }),
- ).not.toBe(
- dashboardTileMatchKey({ kind: "status", stripId: "a", id: "b:c" }),
- );
- });
});
diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx
index 403bef9..0099173 100644
--- a/apps/web/src/App.tsx
+++ b/apps/web/src/App.tsx
@@ -1,30 +1,5 @@
import { useEffect, useState } from "react";
-import type {
- DashboardDocument,
- DashboardModule,
- ServiceEntry,
- StatusItem,
- TelemetryCard,
-} from "@dimensionlab/dashboard-model";
import type { DashboardRuntimeState } from "$lib/server/dashboard";
-import {
- attachDashboardRefreshLifecycle,
- collectVisibleDashboardModelIds,
- createDashboardPerformanceMarks,
- createDashboardRefreshDelay,
- createDashboardTileSnapshotStore,
- createDashboardTileBackoff,
- createDashboardRequestAborter,
- isPersistableDashboardTileSnapshot,
- runViewportAwareDashboardHydrationQueue,
- shouldPauseDashboardRefresh,
- subscribeToDashboardTileEvents,
- waitForDashboardHydrationIdle,
- type DashboardIntersectionObserverFactory,
- type DashboardTileReference,
- type DashboardTileSnapshotStoreContext,
- type RestoredDashboardTileSnapshot,
-} from "$lib/client/dashboard-refresh";
import { dashboardDocumentToUiDashboard } from "$lib/ui-adapter/model-renderer";
import {
DashboardFrame,
@@ -34,46 +9,21 @@ import {
resolveInitialUiTheme,
type UiTheme,
type UiSeverity,
- type UiDashboardPreview,
} from "@dimensionlab/ui";
-type DashboardTileResponse =
- | {
- state: "ready";
- tile: DashboardTileReference;
- item: DashboardModule | ServiceEntry | StatusItem | TelemetryCard;
- }
- | {
- state: "not_found";
- tile: DashboardTileReference;
- message: string;
- }
- | {
- state: "disabled";
- tile: DashboardTileReference;
- message: string;
- };
-
-type DashboardTilesBatchResponse = {
- state: "ready";
- tiles: DashboardTileResponse[];
+const loadingDashboardState: DashboardRuntimeState = {
+ state: "loading",
+ title: "Loading Dashboard",
+ subtitle: "Fetching active model",
+ message: "Waiting for the active dashboard document.",
};
-type DashboardTileHydrationResult = "aborted" | "failed" | "ready";
-
-const dashboardTileHydrationConcurrency = 6;
-const dashboardFallbackRefreshIntervalMs = 30_000;
-const dashboardViewportObservationTimeoutMs = 80;
-type DashboardTileSnapshotStore = ReturnType;
-
export function AppStateView({
dashboard,
onThemeChange,
theme,
- hydratingItemIds,
}: {
dashboard: DashboardRuntimeState;
- hydratingItemIds?: ReadonlySet;
onThemeChange?: (theme: UiTheme) => void;
theme?: UiTheme;
}) {
@@ -83,14 +33,9 @@ export function AppStateView({
) : null;
if (dashboard.state === "ready") {
- const uiDashboard = markHydratingItems(
- dashboardDocumentToUiDashboard(dashboard.document),
- hydratingItemIds,
- );
-
return (
);
@@ -144,10 +89,7 @@ export function resolveDocumentMetadata(dashboard: DashboardRuntimeState): {
export default function App() {
const [dashboard, setDashboard] =
- useState(null);
- const [hydratingItemIds, setHydratingItemIds] = useState>(
- () => new Set(),
- );
+ useState(loadingDashboardState);
const [theme, setTheme] = useState(() => {
if (typeof window === "undefined") return "dark";
@@ -155,8 +97,6 @@ export default function App() {
});
useEffect(() => {
- if (!dashboard) return;
-
const metadata = resolveDocumentMetadata(dashboard);
document.title = metadata.title;
@@ -179,404 +119,47 @@ export default function App() {
useEffect(() => {
let cancelled = false;
let refreshTimer: number | undefined;
- let unsubscribeTileEvents: (() => void) | undefined;
- let lastRefreshIntervalMs = dashboardFallbackRefreshIntervalMs;
- let hydrationRun = 0;
- const requestAborter = createDashboardRequestAborter();
- const performanceMarks = createDashboardPerformanceMarks();
- const refreshDelay = createDashboardRefreshDelay();
- const tileBackoff = createDashboardTileBackoff();
- const tileSnapshotStore = createDashboardTileSnapshotStore(
- getTileSnapshotStorage(),
- );
-
- function refreshPaused() {
- return shouldPauseDashboardRefresh({
- visibilityState: document.visibilityState,
- online: navigator.onLine,
- });
- }
-
- function clearRefreshTimer() {
- if (refreshTimer !== undefined) {
- window.clearTimeout(refreshTimer);
- refreshTimer = undefined;
- }
- }
-
- function scheduleNextRefresh(baseDelayMs: number) {
- clearRefreshTimer();
- if (cancelled || refreshPaused()) return;
-
- refreshTimer = window.setTimeout(() => {
- refreshTimer = undefined;
- void loadDashboard();
- }, refreshDelay.nextDelayMs(baseDelayMs));
- }
-
- function pauseRefreshes() {
- clearRefreshTimer();
- unsubscribeTileEvents?.();
- unsubscribeTileEvents = undefined;
- requestAborter.abortActiveRequests();
- setHydratingItemIds(new Set());
- }
async function loadDashboard() {
- if (cancelled || refreshPaused()) return;
+ const response = await fetch("/api/dashboard");
+ const nextDashboard = (await response.json()) as DashboardRuntimeState;
- clearRefreshTimer();
- const shellSignal = requestAborter.beginShellRun();
+ if (cancelled) return;
+ setDashboard(nextDashboard);
- try {
- const response = await fetch("/api/dashboard", { signal: shellSignal });
- const nextDashboard = (await response.json()) as DashboardRuntimeState;
-
- if (cancelled || shellSignal.aborted || refreshPaused()) return;
- refreshDelay.recordSuccess();
- performanceMarks.markShellLoad();
- const restored = restoreDashboardTileSnapshots(
- nextDashboard,
- nextDashboard.state === "ready"
- ? tileSnapshotStore.restore({
- currentRevisionId: nextDashboard.currentRevisionId,
- schemaVersion: nextDashboard.schemaVersion,
- })
- : [],
- );
- setDashboard(restored.dashboard);
-
- const currentRun = ++hydrationRun;
- if (
- restored.dashboard.state === "ready" &&
- restored.dashboard.liveDatasourceHydration?.enabled !== false
- ) {
- const tiles = dashboardHydrationTiles(restored.dashboard.document).filter(
- (tile) => tileBackoff.canAttempt(dashboardTileKey(tile)),
- );
- const tileSignal = requestAborter.beginTileRun();
- setHydratingItemIds(
- new Set(
- tiles
- .map(dashboardTileKey)
- .filter((key) => !restored.restoredItemIds.has(key)),
- ),
- );
- hydrateDashboardTiles(
- tiles,
- currentRun,
- tileSignal,
- {
- currentRevisionId: restored.dashboard.currentRevisionId,
- schemaVersion: restored.dashboard.schemaVersion,
- },
- );
- subscribeDashboardTileEvents(
- currentRun,
- tileSignal,
- {
- currentRevisionId: restored.dashboard.currentRevisionId,
- schemaVersion: restored.dashboard.schemaVersion,
- },
- );
- } else {
- setHydratingItemIds(new Set());
- }
-
- const refreshIntervalSeconds =
- nextDashboard.state === "ready"
- ? nextDashboard.document.metadata.refreshIntervalSeconds
- : undefined;
-
- if (refreshIntervalSeconds && !refreshPaused()) {
- lastRefreshIntervalMs = refreshIntervalSeconds * 1000;
- scheduleNextRefresh(lastRefreshIntervalMs);
- }
- } catch (error) {
- if (!isAbortError(error) && !cancelled) {
- refreshDelay.recordFailure();
- console.error("Dashboard refresh failed", error);
- scheduleNextRefresh(lastRefreshIntervalMs);
- }
+ if (refreshTimer) {
+ window.clearInterval(refreshTimer);
+ refreshTimer = undefined;
}
- }
- function hydrateDashboardTiles(
- tiles: DashboardTileReference[],
- run: number,
- signal: AbortSignal,
- snapshotContext: DashboardTileSnapshotStoreContext,
- ) {
- const modelIds = tiles.map(dashboardTileModelId);
+ const refreshIntervalSeconds =
+ nextDashboard.state === "ready"
+ ? nextDashboard.document.metadata.refreshIntervalSeconds
+ : undefined;
- void runViewportAwareDashboardHydrationQueue({
- batchSize: dashboardTileHydrationConcurrency,
- collectVisibleModelIds: async () => {
- await waitForDashboardRenderFrame(signal);
- return collectVisibleDashboardModelIds({
- clearTimeout: window.clearTimeout.bind(window),
- createObserver: getDashboardIntersectionObserverFactory(),
- documentTarget: document,
- modelIds,
- setTimeout: window.setTimeout.bind(window),
- signal,
- timeoutMs: dashboardViewportObservationTimeoutMs,
- });
- },
- concurrency: dashboardTileHydrationConcurrency,
- getModelId: dashboardTileModelId,
- hydrate: async (tile) => {
- const key = dashboardTileKey(tile);
- const result = await hydrateDashboardTile(
- tile,
- run,
- signal,
- snapshotContext,
- );
-
- if (result === "ready") {
- tileBackoff.recordSuccess(key);
- } else if (result === "failed") {
- tileBackoff.recordFailure(key);
- }
- },
- hydrateBatch: async (batch) => {
- const results = await hydrateDashboardTileBatch(
- batch,
- run,
- signal,
- snapshotContext,
- );
-
- for (const { result, tile } of results) {
- const key = dashboardTileKey(tile);
- if (result === "ready") {
- tileBackoff.recordSuccess(key);
- } else if (result === "failed") {
- tileBackoff.recordFailure(key);
- }
- }
- },
- items: tiles,
- onAllItemsSettled: () => performanceMarks.markAllTilesSettled(),
- onVisibleItemsSettled: () => performanceMarks.markVisibleTilesReady(),
- signal,
- waitForIdle: () =>
- waitForDashboardHydrationIdle({
- ...getDashboardIdleCallbacks(),
- clearTimeout: window.clearTimeout.bind(window),
- setTimeout: window.setTimeout.bind(window),
- signal,
- }),
- });
- }
-
- function subscribeDashboardTileEvents(
- run: number,
- signal: AbortSignal,
- snapshotContext: DashboardTileSnapshotStoreContext,
- ) {
- unsubscribeTileEvents?.();
- unsubscribeTileEvents = subscribeToDashboardTileEvents({
- onTile: (event) => {
- const tileResponse = event as DashboardTileResponse;
- if (!isDashboardTileResponse(tileResponse)) return;
- applyDashboardTileHydrationResponse(
- tileResponse.tile,
- tileResponse,
- run,
- signal,
- snapshotContext,
- );
- },
- onUnavailable: () => {
- unsubscribeTileEvents = undefined;
- },
- });
- }
-
- async function hydrateDashboardTileBatch(
- tiles: DashboardTileReference[],
- run: number,
- signal: AbortSignal,
- snapshotContext: DashboardTileSnapshotStoreContext,
- ): Promise> {
- try {
- const response = await fetch("/api/dashboard/tiles", {
- body: JSON.stringify({ tiles }),
- headers: { "Content-Type": "application/json" },
- method: "POST",
- signal,
- });
- const batchResponse = (await response.json()) as DashboardTilesBatchResponse;
-
- if (
- cancelled ||
- signal.aborted ||
- run !== hydrationRun ||
- !response.ok ||
- batchResponse.state !== "ready"
- ) {
- throw new Error("Dashboard tile batch hydration failed");
- }
-
- const responsesByKey = new Map(
- batchResponse.tiles.map((tileResponse) => [
- dashboardTileMatchKey(tileResponse.tile),
- tileResponse,
- ]),
- );
-
- return tiles.map((tile) => {
- const key = dashboardTileKey(tile);
- try {
- return {
- tile,
- result: applyDashboardTileHydrationResponse(
- tile,
- responsesByKey.get(dashboardTileMatchKey(tile)),
- run,
- signal,
- snapshotContext,
- ),
- };
- } finally {
- finishDashboardTileHydration(key, run, signal);
- }
- });
- } catch (error) {
- if (signal.aborted || cancelled || run !== hydrationRun) {
- return tiles.map((tile) => ({ result: "aborted", tile }));
- }
-
- return Promise.all(
- tiles.map(async (tile) => ({
- tile,
- result: await hydrateDashboardTile(tile, run, signal, snapshotContext),
- })),
+ if (refreshIntervalSeconds) {
+ refreshTimer = window.setInterval(
+ () => void loadDashboard(),
+ refreshIntervalSeconds * 1000,
);
}
}
- async function hydrateDashboardTile(
- tile: DashboardTileReference,
- run: number,
- signal: AbortSignal,
- snapshotContext: DashboardTileSnapshotStoreContext,
- ): Promise {
- const key = dashboardTileKey(tile);
-
- try {
- const response = await fetch(dashboardTileUrl(tile), { signal });
- const tileResponse = (await response.json()) as DashboardTileResponse;
-
- if (!response.ok) {
- return "failed";
- }
-
- const result = applyDashboardTileHydrationResponse(
- tile,
- tileResponse,
- run,
- signal,
- snapshotContext,
- );
- if (result !== "ready") {
- return signal.aborted || cancelled || run !== hydrationRun
- ? "aborted"
- : "failed";
- }
-
- return "ready";
- } catch (error) {
- if (!isAbortError(error) && !cancelled) {
- console.error("Dashboard tile hydration failed", error);
- return "failed";
- }
- return "aborted";
- } finally {
- finishDashboardTileHydration(key, run, signal);
- }
- }
-
- function applyDashboardTileHydrationResponse(
- tile: DashboardTileReference,
- tileResponse: DashboardTileResponse | undefined,
- run: number,
- signal: AbortSignal,
- snapshotContext: DashboardTileSnapshotStoreContext,
- ): DashboardTileHydrationResult {
- if (
- cancelled ||
- signal.aborted ||
- run !== hydrationRun ||
- !tileResponse ||
- dashboardTileMatchKey(tileResponse.tile) !== dashboardTileMatchKey(tile) ||
- tileResponse.state !== "ready"
- ) {
- return signal.aborted || cancelled || run !== hydrationRun
- ? "aborted"
- : "failed";
- }
-
- const readyTileResponse = tileResponse;
- setDashboard((current) =>
- current?.state === "ready"
- ? {
- ...current,
- document: applyDashboardTile(current.document, readyTileResponse),
- }
- : current,
- );
- tileSnapshotStore.saveReadyTile({
- ...snapshotContext,
- response: readyTileResponse,
- });
- performanceMarks.markFirstTileReady();
- return "ready";
- }
-
- function finishDashboardTileHydration(
- key: string,
- run: number,
- signal: AbortSignal,
- ) {
- if (!cancelled && !signal.aborted && run === hydrationRun) {
- setHydratingItemIds((current) => {
- const next = new Set(current);
- next.delete(key);
- return next;
- });
- }
- }
-
- const detachRefreshLifecycle = attachDashboardRefreshLifecycle({
- documentTarget: document,
- windowTarget: window,
- loadDashboard,
- pauseRefreshes,
- refreshPaused: () => cancelled || refreshPaused(),
- });
-
void loadDashboard();
return () => {
cancelled = true;
- clearRefreshTimer();
- unsubscribeTileEvents?.();
- requestAborter.abortActiveRequests();
- detachRefreshLifecycle();
+ if (refreshTimer) window.clearInterval(refreshTimer);
};
}, []);
- return dashboard ? (
+ return (
- ) : null;
+ );
}
function stateSeverity(state: DashboardRuntimeState["state"]): UiSeverity {
@@ -598,278 +181,3 @@ function getThemeStorage(): Storage | undefined {
return undefined;
}
}
-
-function getTileSnapshotStorage(): Storage | undefined {
- try {
- return window.localStorage;
- } catch {
- return undefined;
- }
-}
-
-type DashboardIdleWindow = Window & {
- cancelIdleCallback?: (handle: number) => void;
- requestIdleCallback?: (
- callback: () => void,
- options?: { timeout?: number },
- ) => number;
-};
-
-function getDashboardIdleCallbacks() {
- const idleWindow = window as DashboardIdleWindow;
- return {
- cancelIdleCallback: idleWindow.cancelIdleCallback?.bind(idleWindow),
- requestIdleCallback: idleWindow.requestIdleCallback?.bind(idleWindow),
- };
-}
-
-function getDashboardIntersectionObserverFactory():
- | DashboardIntersectionObserverFactory
- | undefined {
- if (typeof window.IntersectionObserver === "undefined") return undefined;
-
- return (callback) =>
- new window.IntersectionObserver((entries) => {
- callback(entries);
- });
-}
-
-async function waitForDashboardRenderFrame(signal: AbortSignal): Promise {
- if (signal.aborted) return;
-
- await new Promise((resolve) => {
- let settled = false;
- let frame: number | undefined;
-
- function finish() {
- if (settled) return;
- settled = true;
- if (frame !== undefined) window.cancelAnimationFrame(frame);
- signal.removeEventListener("abort", finish);
- resolve();
- }
-
- signal.addEventListener("abort", finish, { once: true });
- frame = window.requestAnimationFrame(finish);
- });
-}
-
-function isAbortError(error: unknown): boolean {
- return (
- typeof error === "object" &&
- error !== null &&
- "name" in error &&
- error.name === "AbortError"
- );
-}
-
-function isDashboardTileResponse(value: unknown): value is DashboardTileResponse {
- return (
- typeof value === "object" &&
- value !== null &&
- "state" in value &&
- (value.state === "ready" ||
- value.state === "not_found" ||
- value.state === "disabled") &&
- "tile" in value &&
- typeof value.tile === "object" &&
- value.tile !== null
- );
-}
-
-export function restoreDashboardTileSnapshots(
- dashboard: DashboardRuntimeState,
- snapshots: RestoredDashboardTileSnapshot[],
-): {
- dashboard: DashboardRuntimeState;
- restoredItemIds: Set;
-} {
- if (dashboard.state !== "ready" || snapshots.length === 0) {
- return {
- dashboard,
- restoredItemIds: new Set(),
- };
- }
-
- return snapshots.filter((snapshot) =>
- isPersistableDashboardTileSnapshot(snapshot.response.tile)
- ).reduce(
- (current, snapshot) => ({
- dashboard: {
- ...current.dashboard,
- document: applyDashboardTile(
- current.dashboard.document,
- snapshot.response as Extract,
- ),
- },
- restoredItemIds: new Set([
- ...current.restoredItemIds,
- dashboardTileKey(snapshot.response.tile),
- ]),
- }),
- {
- dashboard,
- restoredItemIds: new Set(),
- },
- );
-}
-
-function markHydratingItems(
- dashboard: UiDashboardPreview,
- hydratingItemIds?: ReadonlySet,
-): UiDashboardPreview {
- if (!hydratingItemIds?.size) return dashboard;
-
- return {
- ...dashboard,
- telemetry: dashboard.telemetry.map((card) =>
- hydratingItemIds.has(`telemetry:${card.id}`)
- ? {
- ...card,
- severity: "loading",
- detail: "loading live telemetry",
- }
- : card,
- ),
- serviceGroups: dashboard.serviceGroups.map((group) => ({
- ...group,
- services: group.services.map((service) =>
- hydratingItemIds.has(`service:${group.id}:${service.id}`)
- ? {
- ...service,
- severity: "loading",
- detail: "loading",
- }
- : service,
- ),
- })),
- modules: dashboard.modules.map((module) =>
- hydratingItemIds.has(`module:${module.id}`)
- ? {
- ...module,
- severity: "loading",
- detail: "loading live data",
- }
- : module,
- ),
- statusItems: dashboard.statusItems.map((item) =>
- hydratingItemIds.has(`status:${item.id}`)
- ? {
- ...item,
- severity: "loading",
- value: "loading",
- }
- : item,
- ),
- };
-}
-
-export function dashboardHydrationTiles(
- document: DashboardDocument,
-): DashboardTileReference[] {
- const telemetry = document.telemetry
- .filter((card) => card.datasource?.type === "external")
- .map((card): DashboardTileReference => ({ kind: "telemetry", id: card.id }));
- const services = document.serviceGroups.flatMap((group) =>
- group.services
- .filter((service) => service.datasource?.type === "external")
- .map((service): DashboardTileReference => ({
- kind: "service",
- groupId: group.id,
- id: service.id,
- })),
- );
- const modules = (document.modules || [])
- .filter((module) =>
- module.datasource?.type === "external" ||
- module.id === "runtime-health-summary"
- )
- .map((module): DashboardTileReference => ({ kind: "module", id: module.id }));
- const status = document.statusStrips.flatMap((strip) =>
- strip.items
- .map((item): DashboardTileReference => ({
- kind: "status",
- stripId: strip.id,
- id: item.id,
- })),
- );
-
- return [...status, ...telemetry, ...modules, ...services];
-}
-
-function dashboardTileKey(tile: DashboardTileReference): string {
- if (tile.kind === "status") return `${tile.kind}:${tile.stripId}:${tile.id}`;
- if (tile.kind === "service") return `${tile.kind}:${tile.groupId}:${tile.id}`;
- return `${tile.kind}:${tile.id}`;
-}
-
-export function dashboardTileMatchKey(tile: DashboardTileReference): string {
- return JSON.stringify(tile);
-}
-
-function dashboardTileModelId(tile: DashboardTileReference): string {
- if (tile.kind === "status") return `${tile.stripId}:${tile.id}`;
- return tile.id;
-}
-
-function dashboardTileUrl(tile: DashboardTileReference): string {
- const parts = tile.kind === "status"
- ? ["api", "dashboard", "tile", tile.kind, tile.stripId, tile.id]
- : tile.kind === "service"
- ? ["api", "dashboard", "tile", tile.kind, tile.groupId, tile.id]
- : ["api", "dashboard", "tile", tile.kind, tile.id];
- return `/${parts.map(encodeURIComponent).join("/")}`;
-}
-
-function applyDashboardTile(
- document: DashboardDocument,
- response: Extract,
-): DashboardDocument {
- if (response.tile.kind === "telemetry") {
- return {
- ...document,
- telemetry: document.telemetry.map((card) =>
- card.id === response.tile.id ? response.item as TelemetryCard : card,
- ),
- };
- }
-
- if (response.tile.kind === "service") {
- const tile = response.tile;
- return {
- ...document,
- serviceGroups: document.serviceGroups.map((group) => ({
- ...group,
- services: group.id === tile.groupId
- ? group.services.map((service) =>
- service.id === tile.id ? response.item as ServiceEntry : service,
- )
- : group.services,
- })),
- };
- }
-
- if (response.tile.kind === "module") {
- return {
- ...document,
- modules: (document.modules || []).map((module) =>
- module.id === response.tile.id ? response.item as DashboardModule : module,
- ),
- };
- }
-
- const tile = response.tile;
- return {
- ...document,
- statusStrips: document.statusStrips.map((strip) =>
- strip.id === tile.stripId
- ? {
- ...strip,
- items: strip.items.map((item) =>
- item.id === tile.id ? response.item as StatusItem : item,
- ),
- }
- : strip,
- ),
- };
-}
diff --git a/apps/web/src/lib/client/dashboard-refresh.test.ts b/apps/web/src/lib/client/dashboard-refresh.test.ts
deleted file mode 100644
index 77ea795..0000000
--- a/apps/web/src/lib/client/dashboard-refresh.test.ts
+++ /dev/null
@@ -1,633 +0,0 @@
-import { describe, expect, test } from "vitest";
-import {
- attachDashboardRefreshLifecycle,
- collectVisibleDashboardModelIds,
- createDashboardPerformanceMarks,
- createDashboardRefreshDelay,
- dashboardPerformanceMarks,
- createDashboardTileSnapshotStore,
- createDashboardTileBackoff,
- createDashboardRequestAborter,
- runDashboardHydrationQueue,
- runDashboardHydrationBatchQueue,
- runViewportAwareDashboardHydrationQueue,
- shouldPauseDashboardRefresh,
- splitDashboardHydrationItemsByVisibility,
- subscribeToDashboardTileEvents,
- waitForDashboardHydrationIdle,
- type DashboardIntersectionEntry,
-} from "./dashboard-refresh";
-
-describe("dashboard refresh lifecycle", () => {
- test("pauses refreshes when the document is hidden or the browser is offline", () => {
- expect(
- shouldPauseDashboardRefresh({ visibilityState: "visible", online: true }),
- ).toBe(false);
- expect(
- shouldPauseDashboardRefresh({ visibilityState: "hidden", online: true }),
- ).toBe(true);
- expect(
- shouldPauseDashboardRefresh({ visibilityState: "visible", online: false }),
- ).toBe(true);
- });
-
- test("aborts shell and tile requests when a new shell run starts", () => {
- const aborter = createDashboardRequestAborter();
- const shellSignal = aborter.beginShellRun();
- const tileSignal = aborter.beginTileRun();
-
- const nextShellSignal = aborter.beginShellRun();
-
- expect(shellSignal.aborted).toBe(true);
- expect(tileSignal.aborted).toBe(true);
- expect(nextShellSignal.aborted).toBe(false);
- });
-
- test("aborts active requests when refreshes are paused", () => {
- const aborter = createDashboardRequestAborter();
- const shellSignal = aborter.beginShellRun();
- const tileSignal = aborter.beginTileRun();
-
- aborter.abortActiveRequests();
-
- expect(shellSignal.aborted).toBe(true);
- expect(tileSignal.aborted).toBe(true);
- });
-
- test("pauses on hidden, offline, and pagehide events then resumes immediately when visible", () => {
- const documentTarget = new EventTarget();
- const windowTarget = new EventTarget();
- const calls: string[] = [];
- let paused = true;
-
- const detach = attachDashboardRefreshLifecycle({
- documentTarget,
- windowTarget,
- loadDashboard: () => {
- calls.push("load");
- },
- pauseRefreshes: () => {
- calls.push("pause");
- },
- refreshPaused: () => paused,
- });
-
- documentTarget.dispatchEvent(new Event("visibilitychange"));
- windowTarget.dispatchEvent(new Event("offline"));
- windowTarget.dispatchEvent(new Event("pagehide"));
-
- paused = false;
- documentTarget.dispatchEvent(new Event("visibilitychange"));
- windowTarget.dispatchEvent(new Event("online"));
-
- detach();
- documentTarget.dispatchEvent(new Event("visibilitychange"));
- windowTarget.dispatchEvent(new Event("online"));
-
- expect(calls).toEqual(["pause", "pause", "pause", "load", "load"]);
- });
-
- test("limits tile hydration concurrency", async () => {
- let active = 0;
- let maxActive = 0;
- const started: number[] = [];
- const releases = new Map void>();
-
- const queue = runDashboardHydrationQueue({
- concurrency: 2,
- items: [1, 2, 3, 4],
- signal: new AbortController().signal,
- hydrate: async (item) => {
- active += 1;
- maxActive = Math.max(maxActive, active);
- started.push(item);
- await new Promise((resolve) => releases.set(item, resolve));
- active -= 1;
- },
- });
-
- await waitFor(() => started.length === 2);
- expect(started).toEqual([1, 2]);
- expect(maxActive).toBe(2);
-
- releases.get(1)?.();
- await waitFor(() => started.length === 3);
- expect(started).toEqual([1, 2, 3]);
- expect(maxActive).toBe(2);
-
- releases.get(2)?.();
- releases.get(3)?.();
- await waitFor(() => started.length === 4);
- releases.get(4)?.();
- await queue;
- expect(maxActive).toBe(2);
- });
-
- test("hydrates queued work in fixed-size batches", async () => {
- const batches: number[][] = [];
-
- await runDashboardHydrationBatchQueue({
- batchSize: 3,
- hydrateBatch: (items) => {
- batches.push(items);
- },
- items: [1, 2, 3, 4, 5, 6, 7],
- signal: new AbortController().signal,
- });
-
- expect(batches).toEqual([
- [1, 2, 3],
- [4, 5, 6],
- [7],
- ]);
- });
-
- test("hydrates visible items before deferred items and waits for idle", async () => {
- const calls: string[] = [];
- const idleReleases: Array<() => void> = [];
-
- const hydration = runViewportAwareDashboardHydrationQueue({
- collectVisibleModelIds: async () => new Set(["b"]),
- concurrency: 1,
- getModelId: (item) => item,
- hydrate: async (item) => {
- calls.push(item);
- },
- items: ["a", "b", "c"],
- onAllItemsSettled: () => calls.push("all-settled"),
- onVisibleItemsSettled: () => calls.push("visible-settled"),
- signal: new AbortController().signal,
- waitForIdle: async () => {
- calls.push("idle");
- await new Promise((resolve) => idleReleases.push(resolve));
- },
- });
-
- await waitFor(() => calls.includes("idle"));
- expect(calls).toEqual(["b", "visible-settled", "idle"]);
-
- idleReleases.shift()?.();
- await hydration;
- expect(calls).toEqual([
- "b",
- "visible-settled",
- "idle",
- "a",
- "c",
- "all-settled",
- ]);
- });
-
- test("splits visible hydration items while preserving document order", () => {
- expect(splitDashboardHydrationItemsByVisibility({
- getModelId: (item) => item.id,
- items: [{ id: "status" }, { id: "telemetry" }, { id: "service" }],
- visibleModelIds: new Set(["service", "status"]),
- })).toEqual({
- visible: [{ id: "status" }, { id: "service" }],
- deferred: [{ id: "telemetry" }],
- });
- });
-
- test("collects visible data-model-id elements with IntersectionObserver", async () => {
- const elements = [
- modelElement("status"),
- modelElement("telemetry"),
- modelElement("unrelated"),
- ];
- let callback:
- | ((entries: DashboardIntersectionEntry[]) => void)
- | undefined;
- let finishObservation: (() => void) | undefined;
- let disconnected = false;
- const observed: string[] = [];
-
- const visible = collectVisibleDashboardModelIds({
- createObserver: (observerCallback) => {
- callback = observerCallback;
- return {
- disconnect() {
- disconnected = true;
- },
- observe(element) {
- observed.push(element.getAttribute("data-model-id") || "");
- },
- };
- },
- documentTarget: {
- querySelectorAll: () => elements,
- },
- modelIds: ["status", "telemetry"],
- setTimeout: (handler) => {
- finishObservation = handler;
- return 1 as unknown as ReturnType;
- },
- clearTimeout: () => undefined,
- signal: new AbortController().signal,
- });
-
- callback?.([
- {
- isIntersecting: false,
- intersectionRatio: 0,
- target: elements[0],
- },
- {
- isIntersecting: true,
- target: elements[1],
- },
- ]);
- finishObservation?.();
-
- expect(await visible).toEqual(new Set(["telemetry"]));
- expect(observed).toEqual(["status", "telemetry"]);
- expect(disconnected).toBe(true);
- });
-
- test("waits for requestIdleCallback when available", async () => {
- let idleCallback: (() => void) | undefined;
- let cancelledIdle: number | undefined;
- const wait = waitForDashboardHydrationIdle({
- cancelIdleCallback: (handle) => {
- cancelledIdle = handle;
- },
- requestIdleCallback: (callback) => {
- idleCallback = callback;
- return 7;
- },
- signal: new AbortController().signal,
- });
-
- expect(cancelledIdle).toBeUndefined();
- idleCallback?.();
- await wait;
- expect(cancelledIdle).toBe(7);
- });
-
- test("jitters refresh delays and slows repeated failures", () => {
- const delay = createDashboardRefreshDelay({
- jitterRatio: 0.1,
- random: () => 1,
- });
-
- expect(delay.nextDelayMs(1_000)).toBe(1_100);
- delay.recordFailure();
- expect(delay.nextDelayMs(1_000)).toBe(2_200);
- delay.recordFailure();
- expect(delay.nextDelayMs(1_000)).toBe(4_400);
- delay.recordSuccess();
- expect(delay.nextDelayMs(1_000)).toBe(1_100);
- });
-
- test("marks dashboard performance milestones once per shell run", () => {
- const marks: string[] = [];
- const performanceMarks = createDashboardPerformanceMarks({
- mark: (name) => marks.push(name),
- });
-
- performanceMarks.markShellLoad();
- performanceMarks.markFirstTileReady();
- performanceMarks.markFirstTileReady();
- performanceMarks.markVisibleTilesReady();
- performanceMarks.markAllTilesSettled();
- performanceMarks.markShellLoad();
- performanceMarks.markFirstTileReady();
-
- expect(marks).toEqual([
- dashboardPerformanceMarks.shellLoad,
- dashboardPerformanceMarks.firstTileReady,
- dashboardPerformanceMarks.visibleTilesReady,
- dashboardPerformanceMarks.allTilesSettled,
- dashboardPerformanceMarks.shellLoad,
- dashboardPerformanceMarks.firstTileReady,
- ]);
- });
-
- test("subscribes to dashboard tile events", () => {
- const received: unknown[] = [];
- let listener: ((event: MessageEvent) => void) | undefined;
- let closed = false;
-
- const unsubscribe = subscribeToDashboardTileEvents({
- createEventSource: (url) => {
- expect(url).toBe("/api/dashboard/events");
- return {
- addEventListener(_type, eventListener) {
- listener = eventListener;
- },
- close() {
- closed = true;
- },
- onerror: null,
- };
- },
- onTile: (tile) => received.push(tile),
- });
-
- listener?.({ data: JSON.stringify({ state: "ready" }) } as MessageEvent);
- expect(received).toEqual([{ state: "ready" }]);
- unsubscribe();
- expect(closed).toBe(true);
- });
-
- test("falls back when dashboard tile events are unavailable or fail", () => {
- let unavailableCount = 0;
-
- subscribeToDashboardTileEvents({
- createEventSource: undefined,
- onTile: () => undefined,
- onUnavailable: () => {
- unavailableCount += 1;
- },
- });
-
- let errorHandler: (() => void) | null = null;
- const unsubscribe = subscribeToDashboardTileEvents({
- createEventSource: () => ({
- addEventListener: () => undefined,
- close: () => undefined,
- get onerror() {
- return errorHandler;
- },
- set onerror(handler) {
- errorHandler = handler;
- },
- }),
- onTile: () => undefined,
- onUnavailable: () => {
- unavailableCount += 1;
- },
- });
-
- if (!errorHandler) throw new Error("expected error handler");
- const triggerError = errorHandler as unknown as () => void;
- triggerError();
- unsubscribe();
- expect(unavailableCount).toBe(2);
- });
-
- test("backs off failed tile keys and resets after success", () => {
- const backoff = createDashboardTileBackoff();
-
- backoff.recordFailure("telemetry:infra-ram", 1_000);
- expect(backoff.canAttempt("telemetry:infra-ram", 15_999)).toBe(false);
- expect(backoff.canAttempt("telemetry:infra-ram", 16_000)).toBe(true);
-
- backoff.recordFailure("telemetry:infra-ram", 16_000);
- expect(backoff.canAttempt("telemetry:infra-ram", 45_999)).toBe(false);
- expect(backoff.canAttempt("telemetry:infra-ram", 46_000)).toBe(true);
-
- backoff.recordFailure("telemetry:infra-ram", 46_000);
- backoff.recordFailure("telemetry:infra-ram", 106_000);
- expect(backoff.canAttempt("telemetry:infra-ram", 225_999)).toBe(false);
- expect(backoff.canAttempt("telemetry:infra-ram", 226_000)).toBe(true);
-
- backoff.recordSuccess("telemetry:infra-ram");
- expect(backoff.canAttempt("telemetry:infra-ram", 107_000)).toBe(true);
- });
-
- test("stores only ready tile snapshots and restores them for matching revisions", () => {
- const storage = createMemoryStorage();
- let now = 1_000;
- const store = createDashboardTileSnapshotStore(storage, {
- now: () => now,
- });
-
- store.saveReadyTile({
- currentRevisionId: "revision-a",
- response: {
- state: "ready",
- tile: { kind: "telemetry", id: "infra-ram" },
- item: {
- id: "infra-ram",
- label: "Infra RAM",
- value: { kind: "percent", value: 42 },
- detail: "live",
- severity: "ok",
- },
- },
- schemaVersion: "dashboard.v1",
- });
- store.saveTile({
- currentRevisionId: "revision-a",
- response: {
- state: "not_found",
- tile: { kind: "telemetry", id: "missing" },
- message: "Missing",
- },
- schemaVersion: "dashboard.v1",
- });
- store.saveTile({
- currentRevisionId: "revision-a",
- response: {
- state: "disabled",
- tile: { kind: "module", id: "disabled-module" },
- message: "Disabled",
- },
- schemaVersion: "dashboard.v1",
- });
-
- now = 61_000;
-
- const restored = store.restore({
- currentRevisionId: "revision-a",
- schemaVersion: "dashboard.v1",
- });
-
- expect(restored).toEqual([
- {
- ageMs: 60_000,
- response: {
- state: "ready",
- tile: { kind: "telemetry", id: "infra-ram" },
- item: {
- id: "infra-ram",
- label: "Infra RAM",
- value: { kind: "percent", value: 42 },
- detail: "live - stale 60s",
- severity: "stale",
- },
- },
- },
- ]);
- expect(store.restore({
- currentRevisionId: "revision-b",
- schemaVersion: "dashboard.v1",
- })).toEqual([]);
- });
-
- test("ignores malformed stored tile snapshots", () => {
- const storage = createMemoryStorage();
- storage.setItem(
- "dimensionlab.dashboard.tiles.v1",
- JSON.stringify({
- currentRevisionId: "revision-a",
- schemaVersion: "dashboard.v1",
- tiles: [
- {},
- {
- item: {
- id: "incomplete",
- detail: "live",
- },
- savedAt: 1_000,
- tile: { kind: "telemetry", id: "incomplete" },
- },
- {
- item: {
- id: "infra-ram",
- label: "Infra RAM",
- value: { kind: "percent", value: 42 },
- detail: "live",
- severity: "ok",
- },
- savedAt: 1_000,
- tile: { kind: "telemetry", id: "infra-ram" },
- },
- {
- item: { id: "broken-detail", detail: 42 },
- savedAt: 1_000,
- tile: { kind: "telemetry", id: "broken-detail" },
- },
- ],
- version: 1,
- }),
- );
-
- const store = createDashboardTileSnapshotStore(storage, {
- now: () => 16_000,
- });
-
- expect(store.restore({
- currentRevisionId: "revision-a",
- schemaVersion: "dashboard.v1",
- })).toEqual([
- {
- ageMs: 15_000,
- response: {
- state: "ready",
- tile: { kind: "telemetry", id: "infra-ram" },
- item: {
- id: "infra-ram",
- label: "Infra RAM",
- value: { kind: "percent", value: 42 },
- detail: "live - stale 15s",
- severity: "stale",
- },
- },
- },
- ]);
- });
-
- test("ignores restored aggregate health snapshots", () => {
- const storage = createMemoryStorage();
- storage.setItem(
- "dimensionlab.dashboard.tiles.v1",
- JSON.stringify({
- currentRevisionId: "revision-a",
- schemaVersion: "dashboard.v1",
- tiles: [
- {
- item: {
- id: "system-status",
- label: "System Status",
- value: "20 services down",
- severity: "danger",
- },
- savedAt: 1_000,
- tile: { kind: "status", stripId: "footer-status", id: "system-status" },
- },
- {
- item: {
- id: "runtime-health-summary",
- kind: "summary",
- title: "Runtime Health",
- value: "20 services down",
- detail: "0 warnings - 8 services ok",
- severity: "danger",
- },
- savedAt: 1_000,
- tile: { kind: "module", id: "runtime-health-summary" },
- },
- {
- item: {
- id: "infra-ram",
- label: "Infra RAM",
- value: { kind: "percent", value: 42 },
- detail: "live",
- severity: "ok",
- },
- savedAt: 1_000,
- tile: { kind: "telemetry", id: "infra-ram" },
- },
- ],
- version: 1,
- }),
- );
-
- const store = createDashboardTileSnapshotStore(storage, {
- now: () => 16_000,
- });
-
- expect(store.restore({
- currentRevisionId: "revision-a",
- schemaVersion: "dashboard.v1",
- })).toEqual([
- {
- ageMs: 15_000,
- response: {
- state: "ready",
- tile: { kind: "telemetry", id: "infra-ram" },
- item: {
- id: "infra-ram",
- label: "Infra RAM",
- value: { kind: "percent", value: 42 },
- detail: "live - stale 15s",
- severity: "stale",
- },
- },
- },
- ]);
- });
-});
-
-async function waitFor(predicate: () => boolean) {
- for (let attempt = 0; attempt < 20; attempt += 1) {
- if (predicate()) return;
- await Promise.resolve();
- }
-
- throw new Error("condition was not met");
-}
-
-function createMemoryStorage(): Storage {
- const values = new Map();
- return {
- get length() {
- return values.size;
- },
- clear() {
- values.clear();
- },
- getItem(key) {
- return values.get(key) ?? null;
- },
- key(index) {
- return [...values.keys()][index] ?? null;
- },
- removeItem(key) {
- values.delete(key);
- },
- setItem(key, value) {
- values.set(key, value);
- },
- };
-}
-
-function modelElement(modelId: string) {
- return {
- getAttribute(name: string) {
- return name === "data-model-id" ? modelId : null;
- },
- };
-}
diff --git a/apps/web/src/lib/client/dashboard-refresh.ts b/apps/web/src/lib/client/dashboard-refresh.ts
deleted file mode 100644
index 7c50359..0000000
--- a/apps/web/src/lib/client/dashboard-refresh.ts
+++ /dev/null
@@ -1,886 +0,0 @@
-export interface DashboardRefreshPauseState {
- visibilityState: DocumentVisibilityState;
- online: boolean;
-}
-
-export type DashboardTileReference =
- | { kind: "telemetry"; id: string }
- | { kind: "service"; groupId: string; id: string }
- | { kind: "module"; id: string }
- | { kind: "status"; stripId: string; id: string };
-
-type SnapshotMetricValue = Record;
-
-export type DashboardTileSnapshotItem = {
- detail?: string;
- id: string;
- label?: string;
- severity?: string;
- value?: SnapshotMetricValue | string;
-} & Record;
-
-export type DashboardTileSnapshotResponse =
- | {
- state: "ready";
- tile: DashboardTileReference;
- item: DashboardTileSnapshotItem;
- }
- | {
- state: "not_found";
- tile: DashboardTileReference;
- message: string;
- }
- | {
- state: "disabled";
- tile: DashboardTileReference;
- message: string;
- };
-
-export function shouldPauseDashboardRefresh(
- state: DashboardRefreshPauseState,
-): boolean {
- return state.visibilityState !== "visible" || !state.online;
-}
-
-export function createDashboardRequestAborter() {
- let shellController: AbortController | undefined;
- let tileController: AbortController | undefined;
-
- function abortController(controller: AbortController | undefined) {
- if (controller && !controller.signal.aborted) {
- controller.abort();
- }
- }
-
- return {
- beginShellRun(): AbortSignal {
- abortController(shellController);
- abortController(tileController);
- shellController = new AbortController();
- tileController = undefined;
- return shellController.signal;
- },
- beginTileRun(): AbortSignal {
- abortController(tileController);
- tileController = new AbortController();
- return tileController.signal;
- },
- abortActiveRequests(): void {
- abortController(shellController);
- abortController(tileController);
- shellController = undefined;
- tileController = undefined;
- },
- };
-}
-
-type DashboardRefreshEventTarget = Pick<
- EventTarget,
- "addEventListener" | "removeEventListener"
->;
-
-export interface DashboardRefreshLifecycleOptions {
- documentTarget: DashboardRefreshEventTarget;
- windowTarget: DashboardRefreshEventTarget;
- loadDashboard: () => void | Promise;
- pauseRefreshes: () => void;
- refreshPaused: () => boolean;
-}
-
-export function attachDashboardRefreshLifecycle(
- options: DashboardRefreshLifecycleOptions,
-): () => void {
- function handleRefreshLifecycleChange() {
- if (options.refreshPaused()) {
- options.pauseRefreshes();
- return;
- }
-
- void options.loadDashboard();
- }
-
- function handlePageHide() {
- options.pauseRefreshes();
- }
-
- options.documentTarget.addEventListener(
- "visibilitychange",
- handleRefreshLifecycleChange,
- );
- options.windowTarget.addEventListener("online", handleRefreshLifecycleChange);
- options.windowTarget.addEventListener("offline", handleRefreshLifecycleChange);
- options.windowTarget.addEventListener("pagehide", handlePageHide);
-
- return () => {
- options.documentTarget.removeEventListener(
- "visibilitychange",
- handleRefreshLifecycleChange,
- );
- options.windowTarget.removeEventListener("online", handleRefreshLifecycleChange);
- options.windowTarget.removeEventListener("offline", handleRefreshLifecycleChange);
- options.windowTarget.removeEventListener("pagehide", handlePageHide);
- };
-}
-
-export interface DashboardHydrationQueueOptions {
- concurrency: number;
- hydrate: (item: TItem) => Promise | void;
- items: TItem[];
- signal: AbortSignal;
-}
-
-export async function runDashboardHydrationQueue(
- options: DashboardHydrationQueueOptions,
-): Promise {
- const concurrency = Math.max(1, Math.floor(options.concurrency));
- let nextIndex = 0;
-
- async function worker() {
- while (!options.signal.aborted) {
- const item = options.items[nextIndex];
- nextIndex += 1;
- if (item === undefined) return;
-
- await options.hydrate(item);
- }
- }
-
- const workerCount = Math.min(concurrency, options.items.length);
- await Promise.all(
- Array.from({ length: workerCount }, () => worker()),
- );
-}
-
-export interface DashboardViewportHydrationQueueOptions {
- batchSize?: number;
- collectVisibleModelIds: () => Promise>;
- concurrency: number;
- getModelId: (item: TItem) => string;
- hydrate: (item: TItem) => Promise | void;
- hydrateBatch?: (items: TItem[]) => Promise | void;
- items: TItem[];
- onAllItemsSettled?: () => void;
- onVisibleItemsSettled?: () => void;
- signal: AbortSignal;
- waitForIdle: () => Promise;
-}
-
-export async function runViewportAwareDashboardHydrationQueue(
- options: DashboardViewportHydrationQueueOptions,
-): Promise {
- const visibleModelIds = await options.collectVisibleModelIds();
- if (options.signal.aborted) return;
-
- const { visible, deferred } = splitDashboardHydrationItemsByVisibility({
- getModelId: options.getModelId,
- items: options.items,
- visibleModelIds,
- });
-
- await runDashboardHydrationItems(options, visible);
- if (options.signal.aborted) return;
- options.onVisibleItemsSettled?.();
-
- if (!deferred.length) {
- options.onAllItemsSettled?.();
- return;
- }
-
- await options.waitForIdle();
- if (options.signal.aborted) return;
-
- await runDashboardHydrationItems(options, deferred);
- options.onAllItemsSettled?.();
-}
-
-async function runDashboardHydrationItems(
- options: DashboardViewportHydrationQueueOptions,
- items: TItem[],
-): Promise {
- if (!options.hydrateBatch) {
- await runDashboardHydrationQueue({
- concurrency: options.concurrency,
- hydrate: options.hydrate,
- items,
- signal: options.signal,
- });
- return;
- }
-
- await runDashboardHydrationBatchQueue({
- batchSize: options.batchSize ?? options.concurrency,
- hydrateBatch: options.hydrateBatch,
- items,
- signal: options.signal,
- });
-}
-
-export async function runDashboardHydrationBatchQueue(options: {
- batchSize: number;
- hydrateBatch: (items: TItem[]) => Promise | void;
- items: TItem[];
- signal: AbortSignal;
-}): Promise {
- const batchSize = Math.max(1, Math.floor(options.batchSize));
-
- for (let index = 0; index < options.items.length; index += batchSize) {
- if (options.signal.aborted) return;
- await options.hydrateBatch(options.items.slice(index, index + batchSize));
- }
-}
-
-export function splitDashboardHydrationItemsByVisibility(options: {
- getModelId: (item: TItem) => string;
- items: TItem[];
- visibleModelIds: ReadonlySet;
-}): {
- deferred: TItem[];
- visible: TItem[];
-} {
- const visible: TItem[] = [];
- const deferred: TItem[] = [];
-
- for (const item of options.items) {
- if (options.visibleModelIds.has(options.getModelId(item))) {
- visible.push(item);
- } else {
- deferred.push(item);
- }
- }
-
- return { visible, deferred };
-}
-
-export interface DashboardModelElement {
- getAttribute(name: string): string | null;
-}
-
-export interface DashboardViewportElementSource {
- querySelectorAll(selector: string): ArrayLike;
-}
-
-export interface DashboardIntersectionEntry {
- intersectionRatio?: number;
- isIntersecting: boolean;
- target: DashboardModelElement;
-}
-
-export interface DashboardIntersectionObserver {
- disconnect(): void;
- observe(element: DashboardModelElement): void;
-}
-
-export type DashboardIntersectionObserverFactory = (
- callback: (entries: DashboardIntersectionEntry[]) => void,
-) => DashboardIntersectionObserver;
-
-type DashboardTimerHandle = ReturnType;
-
-export interface DashboardVisibleModelIdCollectorOptions {
- clearTimeout?: (handle: DashboardTimerHandle) => void;
- createObserver?: DashboardIntersectionObserverFactory;
- documentTarget: DashboardViewportElementSource;
- modelIds: Iterable;
- setTimeout?: (callback: () => void, timeoutMs: number) => DashboardTimerHandle;
- signal: AbortSignal;
- timeoutMs?: number;
-}
-
-export async function collectVisibleDashboardModelIds(
- options: DashboardVisibleModelIdCollectorOptions,
-): Promise> {
- const targetIds = new Set(options.modelIds);
- if (!targetIds.size || options.signal.aborted) return new Set();
-
- const elements = Array.from(
- options.documentTarget.querySelectorAll("[data-model-id]"),
- ).filter((element) => {
- const modelId = element.getAttribute("data-model-id");
- return modelId ? targetIds.has(modelId) : false;
- });
-
- if (!elements.length) return new Set();
- if (!options.createObserver) return targetIds;
- const createObserver = options.createObserver;
-
- const setTimer =
- options.setTimeout ||
- ((callback: () => void, timeoutMs: number) =>
- globalThis.setTimeout(callback, timeoutMs));
- const clearTimer =
- options.clearTimeout ||
- ((handle: DashboardTimerHandle) => globalThis.clearTimeout(handle));
- const timeoutMs = options.timeoutMs ?? 80;
-
- return new Promise((resolve) => {
- const visibleModelIds = new Set();
- let settled = false;
- let timeoutHandle: DashboardTimerHandle | undefined;
- let observer: DashboardIntersectionObserver | undefined;
-
- function cleanup() {
- if (timeoutHandle !== undefined) {
- clearTimer(timeoutHandle);
- }
- observer?.disconnect();
- options.signal.removeEventListener("abort", finish);
- }
-
- function finish() {
- if (settled) return;
- settled = true;
- cleanup();
- resolve(visibleModelIds);
- }
-
- observer = createObserver((entries) => {
- for (const entry of entries) {
- const modelId = entry.target.getAttribute("data-model-id");
- if (
- modelId &&
- targetIds.has(modelId) &&
- (entry.isIntersecting || (entry.intersectionRatio ?? 0) > 0)
- ) {
- visibleModelIds.add(modelId);
- }
- }
- });
-
- for (const element of elements) {
- observer.observe(element);
- }
-
- options.signal.addEventListener("abort", finish, { once: true });
- timeoutHandle = setTimer(finish, timeoutMs);
- });
-}
-
-export interface DashboardHydrationIdleOptions {
- cancelIdleCallback?: (handle: number) => void;
- clearTimeout?: (handle: DashboardTimerHandle) => void;
- requestIdleCallback?: (
- callback: () => void,
- options?: { timeout?: number },
- ) => number;
- setTimeout?: (callback: () => void, timeoutMs: number) => DashboardTimerHandle;
- signal: AbortSignal;
- timeoutMs?: number;
-}
-
-export async function waitForDashboardHydrationIdle(
- options: DashboardHydrationIdleOptions,
-): Promise {
- if (options.signal.aborted) return;
-
- const setTimer =
- options.setTimeout ||
- ((callback: () => void, timeoutMs: number) =>
- globalThis.setTimeout(callback, timeoutMs));
- const clearTimer =
- options.clearTimeout ||
- ((handle: DashboardTimerHandle) => globalThis.clearTimeout(handle));
-
- await new Promise((resolve) => {
- let settled = false;
- let idleHandle: number | undefined;
- let timeoutHandle: DashboardTimerHandle | undefined;
-
- function cleanup() {
- if (idleHandle !== undefined) {
- options.cancelIdleCallback?.(idleHandle);
- }
- if (timeoutHandle !== undefined) {
- clearTimer(timeoutHandle);
- }
- options.signal.removeEventListener("abort", finish);
- }
-
- function finish() {
- if (settled) return;
- settled = true;
- cleanup();
- resolve();
- }
-
- options.signal.addEventListener("abort", finish, { once: true });
- if (options.requestIdleCallback) {
- idleHandle = options.requestIdleCallback(finish, {
- timeout: options.timeoutMs ?? 1_000,
- });
- } else {
- timeoutHandle = setTimer(finish, 0);
- }
- });
-}
-
-export interface DashboardRefreshDelayOptions {
- failureMultiplierLimit?: number;
- jitterRatio?: number;
- random?: () => number;
-}
-
-export function createDashboardRefreshDelay(
- options: DashboardRefreshDelayOptions = {},
-) {
- const random = options.random || Math.random;
- const jitterRatio = options.jitterRatio ?? 0.1;
- const failureMultiplierLimit = options.failureMultiplierLimit ?? 8;
- let consecutiveFailures = 0;
-
- return {
- nextDelayMs(baseDelayMs: number): number {
- const failureMultiplier = consecutiveFailures
- ? Math.min(2 ** consecutiveFailures, failureMultiplierLimit)
- : 1;
- const jitterFactor = 1 + ((random() * 2) - 1) * jitterRatio;
- return Math.max(0, Math.round(baseDelayMs * failureMultiplier * jitterFactor));
- },
- recordFailure(): void {
- consecutiveFailures += 1;
- },
- recordSuccess(): void {
- consecutiveFailures = 0;
- },
- };
-}
-
-export interface DashboardPerformanceMarkOptions {
- mark?: (name: string) => void;
-}
-
-export const dashboardPerformanceMarks = {
- allTilesSettled: "dashboard:all-tiles-settled",
- firstTileReady: "dashboard:first-tile-ready",
- shellLoad: "dashboard:shell-load",
- visibleTilesReady: "dashboard:visible-tiles-ready",
-} as const;
-
-export function createDashboardPerformanceMarks(
- options: DashboardPerformanceMarkOptions = {},
-) {
- const mark = options.mark ||
- globalThis.performance?.mark?.bind(globalThis.performance);
- let firstTileReadyMarked = false;
-
- function safeMark(name: string) {
- try {
- mark?.(name);
- } catch {
- // Performance marks are diagnostics only.
- }
- }
-
- return {
- markAllTilesSettled(): void {
- safeMark(dashboardPerformanceMarks.allTilesSettled);
- },
- markFirstTileReady(): void {
- if (firstTileReadyMarked) return;
- firstTileReadyMarked = true;
- safeMark(dashboardPerformanceMarks.firstTileReady);
- },
- markShellLoad(): void {
- firstTileReadyMarked = false;
- safeMark(dashboardPerformanceMarks.shellLoad);
- },
- markVisibleTilesReady(): void {
- safeMark(dashboardPerformanceMarks.visibleTilesReady);
- },
- };
-}
-
-export interface DashboardTileEventSource {
- addEventListener(
- type: "dashboard-tile",
- listener: (event: MessageEvent) => void,
- ): void;
- close(): void;
- onerror: (() => void) | null;
-}
-
-export interface DashboardTileEventSubscriptionOptions {
- createEventSource?: (url: string) => DashboardTileEventSource;
- onTile: (data: unknown) => void;
- onUnavailable?: () => void;
- url?: string;
-}
-
-export function subscribeToDashboardTileEvents(
- options: DashboardTileEventSubscriptionOptions,
-): () => void {
- const createEventSource = options.createEventSource ||
- (typeof globalThis.EventSource !== "undefined"
- ? (url: string) => new globalThis.EventSource(url)
- : undefined);
-
- if (!createEventSource) {
- options.onUnavailable?.();
- return () => undefined;
- }
-
- const source = createEventSource(options.url || "/api/dashboard/events");
- source.addEventListener("dashboard-tile", (event) => {
- try {
- options.onTile(JSON.parse(event.data));
- } catch {
- // Ignore malformed diagnostics from an optional live transport.
- }
- });
- source.onerror = () => {
- source.close();
- options.onUnavailable?.();
- };
-
- return () => source.close();
-}
-
-const dashboardTileBackoffDelaysMs = [15_000, 30_000, 60_000, 120_000];
-
-export function createDashboardTileBackoff() {
- const failures = new Map();
-
- return {
- canAttempt(key: string, now = Date.now()): boolean {
- const failure = failures.get(key);
- return !failure || now >= failure.nextAttemptAt;
- },
- recordFailure(key: string, now = Date.now()): void {
- const previousAttempts = failures.get(key)?.attempts || 0;
- const attempts = previousAttempts + 1;
- const delay =
- dashboardTileBackoffDelaysMs[
- Math.min(attempts - 1, dashboardTileBackoffDelaysMs.length - 1)
- ];
- failures.set(key, {
- attempts,
- nextAttemptAt: now + delay,
- });
- },
- recordSuccess(key: string): void {
- failures.delete(key);
- },
- };
-}
-
-interface DashboardTileSnapshotRecord {
- item: DashboardTileSnapshotItem;
- savedAt: number;
- tile: DashboardTileReference;
-}
-
-interface DashboardTileSnapshotPayload {
- currentRevisionId: string;
- schemaVersion: string;
- tiles: DashboardTileSnapshotRecord[];
- version: 1;
-}
-
-export interface DashboardTileSnapshotStoreContext {
- currentRevisionId: string;
- schemaVersion: string;
-}
-
-export interface DashboardTileSnapshotStoreOptions {
- now?: () => number;
-}
-
-export interface RestoredDashboardTileSnapshot {
- ageMs: number;
- response: Extract;
-}
-
-const dashboardTileSnapshotStorageKey = "dimensionlab.dashboard.tiles.v1";
-
-export function createDashboardTileSnapshotStore(
- storage: Storage | undefined,
- options: DashboardTileSnapshotStoreOptions = {},
-) {
- const now = options.now || Date.now;
-
- function read(): DashboardTileSnapshotPayload | null {
- if (!storage) return null;
-
- try {
- const serialized = storage.getItem(dashboardTileSnapshotStorageKey);
- if (!serialized) return null;
-
- const payload = JSON.parse(serialized) as Partial;
- if (
- payload.version !== 1 ||
- typeof payload.currentRevisionId !== "string" ||
- typeof payload.schemaVersion !== "string" ||
- !Array.isArray(payload.tiles)
- ) {
- return null;
- }
-
- return {
- currentRevisionId: payload.currentRevisionId,
- schemaVersion: payload.schemaVersion,
- tiles: payload.tiles
- .filter(isDashboardTileSnapshotRecord)
- .filter((record) => isPersistableDashboardTileSnapshot(record.tile)),
- version: 1,
- };
- } catch {
- return null;
- }
- }
-
- function write(payload: DashboardTileSnapshotPayload): void {
- if (!storage) return;
-
- try {
- storage.setItem(dashboardTileSnapshotStorageKey, JSON.stringify(payload));
- } catch {
- // Best-effort warm-start cache; quota and privacy failures are non-fatal.
- }
- }
-
- function matchingPayload(
- context: DashboardTileSnapshotStoreContext,
- ): DashboardTileSnapshotPayload {
- const payload = read();
- if (
- payload &&
- payload.currentRevisionId === context.currentRevisionId &&
- payload.schemaVersion === context.schemaVersion
- ) {
- return payload;
- }
-
- return {
- currentRevisionId: context.currentRevisionId,
- schemaVersion: context.schemaVersion,
- tiles: [],
- version: 1,
- };
- }
-
- function saveReadyTile(
- input: DashboardTileSnapshotStoreContext & {
- response: Extract;
- },
- ): void {
- if (!isPersistableDashboardTileSnapshot(input.response.tile)) return;
-
- const payload = matchingPayload(input);
- const key = dashboardTileSnapshotKey(input.response.tile);
- const nextRecord: DashboardTileSnapshotRecord = {
- item: input.response.item,
- savedAt: now(),
- tile: input.response.tile,
- };
- payload.tiles = [
- nextRecord,
- ...payload.tiles.filter((record) =>
- dashboardTileSnapshotKey(record.tile) !== key
- ),
- ];
- write(payload);
- }
-
- return {
- restore(context: DashboardTileSnapshotStoreContext): RestoredDashboardTileSnapshot[] {
- const payload = read();
- if (
- !payload ||
- payload.currentRevisionId !== context.currentRevisionId ||
- payload.schemaVersion !== context.schemaVersion
- ) {
- return [];
- }
-
- const restoredAt = now();
- return payload.tiles.map((record) => ({
- ageMs: Math.max(0, restoredAt - record.savedAt),
- response: {
- state: "ready",
- tile: record.tile,
- item: {
- ...record.item,
- detail: staleDashboardTileDetail(record.item.detail, restoredAt - record.savedAt),
- severity: "stale",
- },
- },
- }));
- },
- saveReadyTile,
- saveTile(input: DashboardTileSnapshotStoreContext & {
- response: DashboardTileSnapshotResponse;
- }): void {
- if (input.response.state === "ready") {
- saveReadyTile({
- currentRevisionId: input.currentRevisionId,
- response: input.response,
- schemaVersion: input.schemaVersion,
- });
- }
- },
- };
-}
-
-function dashboardTileSnapshotKey(tile: DashboardTileReference): string {
- return JSON.stringify(tile);
-}
-
-export function isPersistableDashboardTileSnapshot(
- tile: DashboardTileReference,
-): boolean {
- if (tile.kind === "status" && tile.id === "system-status") return false;
- if (tile.kind === "module" && tile.id === "runtime-health-summary") return false;
- return true;
-}
-
-function isDashboardTileSnapshotRecord(
- value: unknown,
-): value is DashboardTileSnapshotRecord {
- if (!isSnapshotRecord(value)) return false;
-
- return (
- typeof value.savedAt === "number" &&
- Number.isFinite(value.savedAt) &&
- isDashboardTileReference(value.tile) &&
- isDashboardTileSnapshotItem(value.tile, value.item)
- );
-}
-
-function isDashboardTileReference(
- value: unknown,
-): value is DashboardTileReference {
- if (!isSnapshotRecord(value) || typeof value.kind !== "string") return false;
-
- switch (value.kind) {
- case "telemetry":
- case "module":
- return typeof value.id === "string";
- case "service":
- return typeof value.groupId === "string" && typeof value.id === "string";
- case "status":
- return typeof value.stripId === "string" && typeof value.id === "string";
- default:
- return false;
- }
-}
-
-function isDashboardTileSnapshotItem(
- tile: DashboardTileReference,
- value: unknown,
-): value is DashboardTileSnapshotItem {
- if (!isSnapshotRecord(value) || value.id !== tile.id) return false;
-
- switch (tile.kind) {
- case "telemetry":
- return (
- typeof value.label === "string" &&
- isMetricValue(value.value) &&
- isSeverity(value.severity) &&
- isOptionalString(value.detail) &&
- isOptionalString(value.description) &&
- isOptionalString(value.icon) &&
- isOptionalNumberArray(value.sparkline)
- );
- case "service":
- return (
- typeof value.label === "string" &&
- typeof value.description === "string" &&
- isSeverity(value.severity) &&
- isOptionalString(value.detail) &&
- isOptionalString(value.icon) &&
- isOptionalLink(value.link)
- );
- case "module":
- return (
- (value.kind === "summary" ||
- value.kind === "weather" ||
- value.kind === "custom") &&
- isOptionalString(value.title) &&
- isOptionalString(value.label) &&
- isOptionalString(value.value) &&
- isOptionalString(value.detail) &&
- isOptionalString(value.icon) &&
- (value.severity === undefined || isSeverity(value.severity))
- );
- case "status":
- return (
- typeof value.label === "string" &&
- typeof value.value === "string" &&
- isOptionalLink(value.link) &&
- (value.severity === undefined || isSeverity(value.severity))
- );
- }
-}
-
-function isSnapshotRecord(value: unknown): value is Record {
- return typeof value === "object" && value !== null;
-}
-
-function isMetricValue(value: unknown): boolean {
- if (!isSnapshotRecord(value) || typeof value.kind !== "string") return false;
-
- if (value.kind === "text") {
- return (
- typeof value.value === "string" &&
- isOptionalString(value.unit)
- );
- }
-
- const numericKinds = ["bytes", "latency", "number", "percent", "temperature"];
- if (!numericKinds.includes(value.kind)) return false;
- if (typeof value.value !== "number" || !Number.isFinite(value.value)) return false;
- if (value.kind === "percent" && (value.value < 0 || value.value > 100)) {
- return false;
- }
-
- const precision = value.precision;
- return (
- isOptionalString(value.unit) &&
- (precision === undefined ||
- (typeof precision === "number" &&
- Number.isInteger(precision) &&
- precision >= 0 &&
- precision <= 4))
- );
-}
-
-function isSeverity(value: unknown): boolean {
- return (
- value === "neutral" ||
- value === "ok" ||
- value === "warning" ||
- value === "danger" ||
- value === "stale" ||
- value === "unavailable"
- );
-}
-
-function isOptionalString(value: unknown): boolean {
- return value === undefined || typeof value === "string";
-}
-
-function isOptionalNumberArray(value: unknown): boolean {
- return (
- value === undefined ||
- (Array.isArray(value) &&
- value.every((item) => typeof item === "number" && Number.isFinite(item)))
- );
-}
-
-function isOptionalLink(value: unknown): boolean {
- return (
- value === undefined ||
- (isSnapshotRecord(value) &&
- typeof value.href === "string" &&
- isOptionalString(value.label) &&
- (value.external === undefined || typeof value.external === "boolean"))
- );
-}
-
-function staleDashboardTileDetail(
- detail: string | undefined,
- ageMs: number,
-): string | undefined {
- if (!detail) return detail;
- const ageSeconds = Math.max(0, Math.floor(ageMs / 1_000));
- return ageSeconds > 0 ? `${detail} - stale ${ageSeconds}s` : detail;
-}
diff --git a/apps/web/src/lib/dashboard-seed/dimensionlab.test.ts b/apps/web/src/lib/dashboard-seed/dimensionlab.test.ts
index 1a97c3b..31c01d9 100644
--- a/apps/web/src/lib/dashboard-seed/dimensionlab.test.ts
+++ b/apps/web/src/lib/dashboard-seed/dimensionlab.test.ts
@@ -128,6 +128,7 @@ const verifiedSeedIconIds = new Set([
"mdi:pulse",
"mdi:robot-outline",
"mdi:router-network",
+ "mdi:text-box-search",
"mdi:thermometer",
"mdi:web",
"mdi:weather-sunny",
diff --git a/apps/web/src/lib/dashboard-seed/dimensionlab.ts b/apps/web/src/lib/dashboard-seed/dimensionlab.ts
index 59162e6..6cc544b 100644
--- a/apps/web/src/lib/dashboard-seed/dimensionlab.ts
+++ b/apps/web/src/lib/dashboard-seed/dimensionlab.ts
@@ -431,6 +431,14 @@ export const dimensionLabDashboardFixture: DashboardDocument = {
href: "https://models.dimensionlab.net",
datasource: uptimeMonitor(7),
}),
+ service({
+ id: "prompt-registry",
+ label: "Prompt Registry",
+ description: "Shared prompts, traces, evals",
+ icon: "mdi:text-box-search",
+ href: "https://prompts.dimensionlab.net",
+ datasource: uptimeMonitor(20),
+ }),
]),
group("systems", "Systems", [
service({
diff --git a/apps/web/src/lib/server/dashboard.test.ts b/apps/web/src/lib/server/dashboard.test.ts
index 5d93422..7851ebb 100644
--- a/apps/web/src/lib/server/dashboard.test.ts
+++ b/apps/web/src/lib/server/dashboard.test.ts
@@ -66,6 +66,9 @@ describe("dashboard runtime loader", () => {
expect(runtime.document.statusStrips[0]?.items.map((item) => item.id)).toContain(
"auto-refresh",
);
+ expect(runtime.document.serviceGroups.flatMap((group) => group.services).map((service) => service.id)).toContain(
+ "prompt-registry",
+ );
expect(store.listRevisions()).toHaveLength(2);
expect(store.getActiveDashboard()?.revision.actor).toBe("initial-seed");
});
@@ -135,5 +138,13 @@ function olderDimensionLabSeed() {
...strip,
items: strip.items.filter((item) => item.id !== "auto-refresh"),
}));
+ document.serviceGroups = document.serviceGroups.map((group) =>
+ group.id === "ai-automation"
+ ? {
+ ...group,
+ services: group.services.filter((service) => service.id !== "prompt-registry"),
+ }
+ : group,
+ );
return document;
}
diff --git a/apps/web/src/lib/server/dashboard.ts b/apps/web/src/lib/server/dashboard.ts
index 33afa3a..47ebfa2 100644
--- a/apps/web/src/lib/server/dashboard.ts
+++ b/apps/web/src/lib/server/dashboard.ts
@@ -18,9 +18,6 @@ export interface DashboardRuntimeReady {
document: DashboardDocument;
schemaVersion: string;
currentRevisionId: string;
- liveDatasourceHydration?: {
- enabled: boolean;
- };
}
export interface DashboardRuntimeEmpty {
diff --git a/apps/web/src/lib/server/datasources/dashboard-datasources.test.ts b/apps/web/src/lib/server/datasources/dashboard-datasources.test.ts
index d24b48b..61acdd3 100644
--- a/apps/web/src/lib/server/datasources/dashboard-datasources.test.ts
+++ b/apps/web/src/lib/server/datasources/dashboard-datasources.test.ts
@@ -3,7 +3,7 @@ import {
DASHBOARD_SCHEMA_VERSION,
type DashboardDocument,
} from "@dimensionlab/dashboard-model";
-import { resolveDashboardDatasources, resolveDashboardTile } from ".";
+import { resolveDashboardDatasources } from ".";
describe("dashboard datasource resolution", () => {
test("hydrates telemetry, service health, weather, and summary data from live adapters", async () => {
@@ -124,100 +124,6 @@ describe("dashboard datasource resolution", () => {
expect(resolved).not.toBe(testDocument);
expect(testDocument.telemetry[0].value.value).toBe(1);
});
-
- test("shares service health snapshots across aggregate tile hydration", async () => {
- let resolveFetch: ((response: Response) => void) | undefined;
- const fetch = vi.fn((input: RequestInfo | URL) => {
- const url = String(input);
- if (url !== "https://service.example/health") {
- throw new Error(`Unhandled test request: ${url}`);
- }
-
- return new Promise((resolve) => {
- resolveFetch = resolve;
- });
- });
- const document = testDashboard();
-
- const moduleTile = resolveDashboardTile(
- document,
- { kind: "module", id: "runtime-health-summary" },
- { fetch },
- );
- const statusTile = resolveDashboardTile(
- document,
- { kind: "status", stripId: "footer", id: "system-status" },
- { fetch },
- );
-
- await Promise.resolve();
- expect(fetch).toHaveBeenCalledTimes(1);
-
- resolveFetch?.(jsonResponse({ status: "UP", ping: 42 }));
- expect(await moduleTile).toMatchObject({
- state: "ready",
- item: {
- id: "runtime-health-summary",
- severity: "ok",
- value: "all systems operational",
- },
- });
- expect(await statusTile).toMatchObject({
- state: "ready",
- item: {
- id: "system-status",
- severity: "ok",
- value: "All systems operational",
- },
- });
- });
-
- test("isolates service health snapshots by datasource fetch context", async () => {
- const firstFetch = vi.fn(async () =>
- jsonResponse({
- status: "UP",
- ping: 42,
- })
- );
- const secondFetch = vi.fn(async () =>
- jsonResponse({
- status: "DOWN",
- ping: 0,
- })
- );
- const document = testDashboard();
- document.serviceGroups[0].services[0] = {
- ...document.serviceGroups[0].services[0],
- id: "api-isolated",
- datasource: {
- type: "external",
- adapter: "http-status",
- reference: "GET https://service.example/isolated",
- },
- };
-
- await resolveDashboardTile(
- document,
- { kind: "status", stripId: "footer", id: "system-status" },
- { fetch: firstFetch },
- );
- const second = await resolveDashboardTile(
- document,
- { kind: "status", stripId: "footer", id: "system-status" },
- { fetch: secondFetch },
- );
-
- expect(firstFetch).toHaveBeenCalledTimes(1);
- expect(secondFetch).toHaveBeenCalledTimes(1);
- expect(second).toMatchObject({
- state: "ready",
- item: {
- id: "system-status",
- severity: "danger",
- value: "1 service down",
- },
- });
- });
});
const testDocument = testDashboard();
diff --git a/apps/web/src/lib/server/datasources/index.ts b/apps/web/src/lib/server/datasources/index.ts
index 62df13a..d69db86 100644
--- a/apps/web/src/lib/server/datasources/index.ts
+++ b/apps/web/src/lib/server/datasources/index.ts
@@ -10,38 +10,8 @@ import type {
TelemetryCard,
} from "@dimensionlab/dashboard-model";
-export type DashboardTileReference =
- | { kind: "telemetry"; id: string }
- | { kind: "service"; groupId: string; id: string }
- | { kind: "module"; id: string }
- | { kind: "status"; stripId: string; id: string };
-
-export type DashboardTileItem =
- | DashboardModule
- | ServiceEntry
- | StatusItem
- | TelemetryCard;
-
-export type DashboardTileResolution =
- | {
- state: "ready";
- tile: DashboardTileReference;
- item: DashboardTileItem;
- }
- | {
- state: "not_found";
- tile: DashboardTileReference;
- message: string;
- }
- | {
- state: "disabled";
- tile: DashboardTileReference;
- message: string;
- };
-
export interface DatasourceResolutionOptions {
fetch?: DatasourceFetch;
- now?: () => number;
prometheusBaseUrl?: string;
prometheusRangeSeconds?: number;
prometheusStepSeconds?: number;
@@ -76,90 +46,16 @@ export async function resolveDashboardDatasources(
};
}
-export async function resolveDashboardTile(
- document: DashboardDocument,
- tile: DashboardTileReference,
- options: DatasourceResolutionOptions = {},
-): Promise {
- const context = datasourceContext(options);
-
- if (tile.kind === "telemetry") {
- const card = document.telemetry.find((item) => item.id === tile.id);
- if (!card) return missingTile(tile);
-
- return {
- state: "ready",
- tile,
- item: await resolveTelemetryCard(card, context),
- };
- }
-
- if (tile.kind === "service") {
- const service = document.serviceGroups
- .find((group) => group.id === tile.groupId)
- ?.services.find((item) => item.id === tile.id);
- if (!service) return missingTile(tile);
-
- return {
- state: "ready",
- tile,
- item: await resolveService(service, context),
- };
- }
-
- if (tile.kind === "module") {
- const module = document.modules?.find((item) => item.id === tile.id);
- if (!module) return missingTile(tile);
-
- const item = module.id === "runtime-health-summary"
- ? runtimeHealthSummary(
- module,
- await serviceGroupsSnapshot(document, context),
- )
- : await resolveModule(module, context);
-
- return { state: "ready", tile, item };
- }
-
- const strip = document.statusStrips.find((item) => item.id === tile.stripId);
- const statusItem = strip?.items.find((item) => item.id === tile.id);
- if (!strip || !statusItem) return missingTile(tile);
-
- return {
- state: "ready",
- tile,
- item: await resolveStatusTile(
- statusItem,
- document.metadata.refreshIntervalSeconds,
- document,
- context,
- ),
- };
-}
-
interface DatasourceContext {
fetch: DatasourceFetch;
- fetchIdentity: number;
- now: () => number;
prometheusBaseUrl: string;
prometheusRangeSeconds: number;
prometheusStepSeconds: number;
requestTimeoutMs: number;
- serviceGroupsSnapshot?: Promise;
}
type DatasourceFetch = (input: string, init?: RequestInit) => Promise;
-interface ServiceGroupsSnapshotEntry {
- expiresAt: number;
- snapshot: Promise;
-}
-
-const serviceGroupsSnapshotTtlMs = 30_000;
-const serviceGroupsSnapshotCache = new Map();
-const datasourceFetchIdentities = new WeakMap();
-let nextDatasourceFetchIdentity = 1;
-
interface PrometheusVectorResult {
metric?: Record;
value?: [number, string];
@@ -171,12 +67,8 @@ interface PrometheusMatrixResult {
}
function datasourceContext(options: DatasourceResolutionOptions): DatasourceContext {
- const fetch = options.fetch || globalThis.fetch;
-
return {
- fetch,
- fetchIdentity: datasourceFetchIdentity(fetch),
- now: options.now || Date.now,
+ fetch: options.fetch || globalThis.fetch,
prometheusBaseUrl:
options.prometheusBaseUrl ||
process.env.PROMETHEUS_BASE_URL ||
@@ -187,68 +79,6 @@ function datasourceContext(options: DatasourceResolutionOptions): DatasourceCont
};
}
-function datasourceFetchIdentity(fetch: DatasourceFetch): number {
- const existing = datasourceFetchIdentities.get(fetch);
- if (existing) return existing;
-
- const next = nextDatasourceFetchIdentity;
- nextDatasourceFetchIdentity += 1;
- datasourceFetchIdentities.set(fetch, next);
- return next;
-}
-
-function serviceGroupsSnapshot(
- document: DashboardDocument,
- context: DatasourceContext,
-): Promise {
- if (context.serviceGroupsSnapshot) return context.serviceGroupsSnapshot;
-
- const key = serviceGroupsSnapshotKey(document, context);
- const now = context.now();
- const cached = serviceGroupsSnapshotCache.get(key);
- if (cached && cached.expiresAt > now) {
- context.serviceGroupsSnapshot = cached.snapshot;
- return cached.snapshot;
- }
-
- const snapshot = Promise.all(
- document.serviceGroups.map((group) => resolveServiceGroup(group, context)),
- );
- context.serviceGroupsSnapshot = snapshot;
- serviceGroupsSnapshotCache.set(key, {
- expiresAt: now + serviceGroupsSnapshotTtlMs,
- snapshot,
- });
- snapshot.catch(() => {
- if (serviceGroupsSnapshotCache.get(key)?.snapshot === snapshot) {
- serviceGroupsSnapshotCache.delete(key);
- }
- });
- return snapshot;
-}
-
-function serviceGroupsSnapshotKey(
- document: DashboardDocument,
- context: DatasourceContext,
-): string {
- return JSON.stringify(
- {
- fetchIdentity: context.fetchIdentity,
- prometheusBaseUrl: context.prometheusBaseUrl,
- prometheusRangeSeconds: context.prometheusRangeSeconds,
- prometheusStepSeconds: context.prometheusStepSeconds,
- requestTimeoutMs: context.requestTimeoutMs,
- serviceGroups: document.serviceGroups.map((group) => ({
- id: group.id,
- services: group.services.map((service) => ({
- datasource: service.datasource,
- id: service.id,
- })),
- })),
- },
- );
-}
-
async function resolveTelemetryCard(
card: TelemetryCard,
context: DatasourceContext,
@@ -560,66 +390,6 @@ function resolveStatusItem(
return structuredClone(item);
}
-async function resolveStatusTile(
- item: StatusItem,
- refreshIntervalSeconds: number | undefined,
- document: DashboardDocument,
- context: DatasourceContext,
-): Promise {
- if (item.id === "system-status") {
- const resolvedGroups = await serviceGroupsSnapshot(document, context);
- const health = serviceHealthSummary(resolvedGroups);
- return {
- ...structuredClone(item),
- value: health.value,
- severity: health.severity,
- };
- }
-
- if (item.id === "last-sync") {
- return {
- ...structuredClone(item),
- value: "just now",
- severity: "ok",
- };
- }
-
- if (item.id === "uptime") {
- const uptime = await prometheusScalar(
- 'time() - node_boot_time_seconds{job="node",host="linux-infra"}',
- context,
- ).catch(() => null);
- return uptime === null
- ? structuredClone(item)
- : {
- ...structuredClone(item),
- value: formatDuration(uptime),
- severity: "ok",
- };
- }
-
- if (item.id === "load-avg") {
- const loadAverage = await prometheusLoadAverage(context).catch(() => null);
- return loadAverage
- ? {
- ...structuredClone(item),
- value: loadAverage,
- severity: "neutral",
- }
- : structuredClone(item);
- }
-
- if (item.id === "auto-refresh" && refreshIntervalSeconds) {
- return {
- ...structuredClone(item),
- value: `${refreshIntervalSeconds}s`,
- severity: "neutral",
- };
- }
-
- return structuredClone(item);
-}
-
function serviceHealthSummary(serviceGroups: ServiceGroup[]): {
severity: Severity;
value: string;
@@ -903,17 +673,3 @@ function formatDuration(totalSeconds: number): string {
const minutes = Math.floor((seconds % 3_600) / 60);
return `${days}d ${hours}h ${minutes}m`;
}
-
-function missingTile(tile: DashboardTileReference): DashboardTileResolution {
- return {
- state: "not_found",
- tile,
- message: `Dashboard tile not found: ${tileKey(tile)}`,
- };
-}
-
-function tileKey(tile: DashboardTileReference): string {
- if (tile.kind === "status") return `${tile.kind}:${tile.stripId}:${tile.id}`;
- if (tile.kind === "service") return `${tile.kind}:${tile.groupId}:${tile.id}`;
- return `${tile.kind}:${tile.id}`;
-}
diff --git a/apps/web/src/lib/workspace-boundary.test.ts b/apps/web/src/lib/workspace-boundary.test.ts
index 58cf203..38a3f1b 100644
--- a/apps/web/src/lib/workspace-boundary.test.ts
+++ b/apps/web/src/lib/workspace-boundary.test.ts
@@ -1,6 +1,4 @@
-import { spawnSync } from "node:child_process";
-import { chmodSync, existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
-import { tmpdir } from "node:os";
+import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, test } from "vitest";
@@ -44,8 +42,6 @@ describe("workspace boundaries", () => {
"turbo run check test:unit build @dimensionlab/ui#build-storybook @dimensionlab/web#test:e2e",
);
expect(webPackage.scripts).not.toHaveProperty("test:qa");
- expect(webPackage.scripts?.test).toBe("bun --bun vitest run");
- expect(webPackage.scripts?.["test:unit"]).toBe("bun --bun vitest run");
expect(turboConfig.tasks).not.toHaveProperty("test:qa");
expect(turboConfig.globalDependencies).toEqual(
expect.arrayContaining(["bun.lock", "tsconfig.base.json"]),
@@ -199,7 +195,7 @@ describe("workspace boundaries", () => {
const sourceCopy = "COPY --from=pruner /repo/out/full/ ./";
const tsconfigCopy =
"COPY --from=pruner /repo/tsconfig.base.json /repo/tsconfig.json ./";
- const installCommand = "RUN bun install --frozen-lockfile --ignore-scripts";
+ const installCommand = "RUN bun install --frozen-lockfile";
expect(containerfile).toContain("AS pruner");
expect(containerfile).toContain(pruneCommand);
@@ -257,186 +253,6 @@ describe("workspace boundaries", () => {
"rm -rf build && vite build && bun build src/server/index.ts --target bun --outdir build",
);
});
-
- test("defines Forgejo CI and main-branch deploy automation", () => {
- const workflow = readFileSync(
- join(root, ".forgejo/workflows/dimensionlab-website.yml"),
- "utf8",
- );
-
- expect(workflow).toContain("name: Dimension Lab website");
- expect(workflow).toContain("pull_request:");
- expect(workflow).toContain("push:");
- expect(workflow).toContain("branches:");
- expect(workflow).toContain("- main");
- expect(workflow).toContain("runs-on: docker");
- expect(workflow).toContain("bun install --frozen-lockfile");
- expect(workflow).toContain("bun run check");
- expect(workflow).toContain("bun run test");
- expect(workflow).toContain("bun run build");
- expect(workflow).toContain("needs: ci");
- expect(workflow).toContain("runs-on: deploy");
- expect(workflow).toContain("github.event_name == 'push'");
- expect(workflow).toContain("github.ref == 'refs/heads/main'");
- expect(workflow).toContain(
- "git remote add origin git@git.dimensionlab.net:vince/dimensionlab-website.git",
- );
- expect(workflow).toContain('git fetch --force --prune --depth=1 origin "$GITHUB_SHA"');
- expect(workflow).toContain("podman inspect dimensionlab-website");
- expect(workflow).toContain("DEPLOY_CONTAINER_CLI: podman");
- expect(workflow).toContain("PODMAN_SYSTEMD_UNIT");
- expect(workflow).toContain("scripts/deploy-dimensionlab-website.sh");
- });
-
- test("keeps production deployment behind a guarded script", () => {
- const deployScript = readFileSync(
- join(root, "scripts/deploy-dimensionlab-website.sh"),
- "utf8",
- );
-
- expect(deployScript).toContain("refs/heads/main");
- expect(deployScript).toContain("dimensionlab-website.service");
- expect(deployScript).toContain("localhost/dimensionlab-website");
- expect(deployScript).toContain("apps/web/Containerfile");
- expect(deployScript).toContain("rollback-");
- expect(deployScript).toContain("https://dimensionlab.net");
- expect(deployScript).toContain("/api/dashboard/tiles");
- expect(deployScript).toContain("--dry-run");
- expect(deployScript).toContain("DEPLOY_RESTART_STRATEGY");
- });
-
- test("rolls back the latest image when production smoke checks fail", () => {
- const result = runDeployScriptWithFakes(
- {
- curl: failingCurlCommand,
- git: fakeGitCommand,
- podman: fakePodmanCommand("dimensionlab-website.service"),
- },
- {
- DEPLOY_CONTAINER_CLI: "podman",
- DEPLOY_EVENT_NAME: "push",
- DEPLOY_REF: "refs/heads/main",
- DEPLOY_RESTART_STRATEGY: "quadlet-container",
- DEPLOY_SHA: "1234567890abcdef",
- DEPLOY_SMOKE_TIMEOUT_SECONDS: "0",
- },
- );
-
- expect(result.status).toBe(1);
- expect(result.stderr).toContain("smoke checks failed");
- expect(result.stderr).toContain("rolling back to localhost/dimensionlab-website:rollback-");
- expect(result.log).toMatch(
- /tag localhost\/dimensionlab-website:latest localhost\/dimensionlab-website:rollback-\d{14}/,
- );
- expect(result.log).toMatch(
- /tag localhost\/dimensionlab-website:rollback-\d{14} localhost\/dimensionlab-website:latest/,
- );
- expect(result.log.match(/^stop dimensionlab-website$/gm)).toHaveLength(2);
- });
-
- test("rolls back the latest image when the container fails to restart", () => {
- const result = runDeployScriptWithFakes(
- {
- curl: passingCurlCommand,
- git: fakeGitCommand,
- podman: fakePodmanCommand("dimensionlab-website.service"),
- },
- {
- DEPLOY_CONTAINER_CLI: "podman",
- DEPLOY_CONTAINER_START_TIMEOUT_SECONDS: "1",
- DEPLOY_TEST_CONTAINER_IMAGE: "wrong",
- DEPLOY_EVENT_NAME: "push",
- DEPLOY_REF: "refs/heads/main",
- DEPLOY_RESTART_STRATEGY: "quadlet-container",
- DEPLOY_SHA: "1234567890abcdef",
- },
- );
-
- expect(result.status).toBe(1);
- expect(result.stderr).toContain("did not restart on localhost/dimensionlab-website:latest");
- expect(result.stderr).toContain("rolling back to localhost/dimensionlab-website:rollback-");
- expect(result.stderr).toContain("rollback image is running");
- expect(result.log).toMatch(
- /tag localhost\/dimensionlab-website:rollback-\d{14} localhost\/dimensionlab-website:latest/,
- );
- expect(result.log.match(/^stop dimensionlab-website$/gm)).toHaveLength(2);
- });
-
- test.each([
- {
- env: { DEPLOY_EVENT_NAME: "pull_request", DEPLOY_REF: "refs/heads/main" },
- message: "production deploys only run for push",
- },
- {
- env: { DEPLOY_EVENT_NAME: "push", DEPLOY_REF: "refs/heads/codex/test" },
- message: "expected refs/heads/main",
- },
- ])("refuses guarded deploy contexts before host mutations", ({ env, message }) => {
- const result = runDeployScriptWithFakes(
- {
- curl: passingCurlCommand,
- git: fakeGitCommand,
- podman: fakePodmanCommand("dimensionlab-website.service"),
- },
- {
- DEPLOY_CONTAINER_CLI: "podman",
- DEPLOY_SHA: "1234567890abcdef",
- ...env,
- },
- );
-
- expect(result.status).toBe(1);
- expect(result.stderr).toContain(message);
- expect(result.log).toBe("");
- });
-
- test("refuses stop-based deploys unless the container belongs to the expected unit", () => {
- const result = runDeployScriptWithFakes(
- {
- curl: passingCurlCommand,
- git: fakeGitCommand,
- podman: fakePodmanCommand("other.service"),
- },
- {
- DEPLOY_CONTAINER_CLI: "podman",
- DEPLOY_EVENT_NAME: "push",
- DEPLOY_REF: "refs/heads/main",
- DEPLOY_RESTART_STRATEGY: "quadlet-container",
- DEPLOY_SHA: "1234567890abcdef",
- },
- );
-
- expect(result.status).toBe(1);
- expect(result.stderr).toContain(
- "refusing to stop dimensionlab-website; expected PODMAN_SYSTEMD_UNIT=dimensionlab-website.service",
- );
- expect(result.log).not.toContain("build ");
- expect(result.log).not.toContain("stop dimensionlab-website");
- });
-
- test("checks the expected unit before auto falls back to stopping the container", () => {
- const result = runDeployScriptWithFakes(
- {
- curl: passingCurlCommand,
- git: fakeGitCommand,
- podman: fakePodmanCommand("other.service"),
- systemctl: fakeSystemctlCommand({ active: false, show: true }),
- },
- {
- DEPLOY_CONTAINER_CLI: "podman",
- DEPLOY_EVENT_NAME: "push",
- DEPLOY_REF: "refs/heads/main",
- DEPLOY_RESTART_STRATEGY: "auto",
- DEPLOY_SHA: "1234567890abcdef",
- },
- );
-
- expect(result.status).toBe(1);
- expect(result.stderr).toContain(
- "refusing to stop dimensionlab-website; expected PODMAN_SYSTEMD_UNIT=dimensionlab-website.service",
- );
- expect(result.log).not.toContain("stop dimensionlab-website");
- });
});
type WorkspacePackageExport =
@@ -453,126 +269,3 @@ function expectPackageExport(
): void {
expect(actual).toMatchObject(expected);
}
-
-function runDeployScriptWithFakes(
- commands: Record,
- env: Record,
-): { log: string; status: number | null; stderr: string; stdout: string } {
- const tempDir = mkdtempSync(join(tmpdir(), "dimensionlab-deploy-test-"));
- const logPath = join(tempDir, "commands.log");
-
- for (const [name, source] of Object.entries(commands)) {
- const commandPath = join(tempDir, name);
- writeFileSync(commandPath, source);
- chmodSync(commandPath, 0o755);
- }
-
- const result = spawnSync("bash", [join(root, "scripts/deploy-dimensionlab-website.sh")], {
- cwd: root,
- encoding: "utf8",
- env: {
- ...process.env,
- ...env,
- DEPLOY_TEST_LOG: logPath,
- PATH: `${tempDir}:${process.env.PATH ?? ""}`,
- },
- });
-
- return {
- log: existsSync(logPath) ? readFileSync(logPath, "utf8") : "",
- status: result.status,
- stderr: result.stderr,
- stdout: result.stdout,
- };
-}
-
-const fakeGitCommand = `#!/usr/bin/env bash
-case "$1" in
- branch)
- echo main
- ;;
- rev-parse)
- echo 1234567890abcdef
- ;;
- config|submodule)
- exit 0
- ;;
-esac
-`;
-
-function fakePodmanCommand(systemdUnit: string): string {
- return `#!/usr/bin/env bash
-state_file="$DEPLOY_TEST_LOG.state"
-[ -f "$state_file" ] || printf 'initial' > "$state_file"
-printf '%s\\n' "$*" >> "$DEPLOY_TEST_LOG"
-if [ "$1" = "image" ] && [ "$2" = "inspect" ]; then
- if [ "$4" = "--format" ]; then
- case "$3" in
- *:rollback-*)
- echo sha256:old
- ;;
- *)
- if [ "$(cat "$state_file")" = "rollback" ]; then
- echo sha256:old
- else
- echo sha256:new
- fi
- ;;
- esac
- fi
- exit 0
-fi
-if [ "$1" = "tag" ] && [ "$2" != "localhost/dimensionlab-website:latest" ]; then
- printf 'rollback' > "$state_file"
-fi
-if [ "$1" = "inspect" ]; then
- case "$*" in
- *PODMAN_SYSTEMD_UNIT*)
- echo ${systemdUnit}
- ;;
- *State.Running*)
- echo true
- ;;
- *'.Image'*|*'{{.Image}}'*)
- if [ "$(cat "$state_file")" = "rollback" ]; then
- echo sha256:old
- elif [ "\${DEPLOY_TEST_CONTAINER_IMAGE:-new}" = "wrong" ]; then
- echo sha256:wrong
- else
- echo sha256:new
- fi
- ;;
- esac
-fi
-`;
-}
-
-const failingCurlCommand = `#!/usr/bin/env bash
-printf 'curl %s\\n' "$*" >> "$DEPLOY_TEST_LOG"
-exit 22
-`;
-
-const passingCurlCommand = `#!/usr/bin/env bash
-printf 'curl %s\\n' "$*" >> "$DEPLOY_TEST_LOG"
-if [ "$*" = *'/api/dashboard/tiles'* ]; then
- printf '{"state":"ready","tiles":[]}'
-fi
-`;
-
-function fakeSystemctlCommand(options: { active: boolean; show: boolean }): string {
- const activeStatus = options.active ? 0 : 3;
- const showStatus = options.show ? 0 : 1;
-
- return `#!/usr/bin/env bash
-printf 'systemctl %s\\n' "$*" >> "$DEPLOY_TEST_LOG"
-if [ "$1" = "--user" ] && [ "$2" = "is-active" ]; then
- exit ${activeStatus}
-fi
-if [ "$1" = "--user" ] && [ "$2" = "show" ]; then
- exit ${showStatus}
-fi
-if [ "$1" = "--user" ] && [ "$2" = "restart" ]; then
- exit 0
-fi
-`;
-}
diff --git a/apps/web/src/server/index.test.ts b/apps/web/src/server/index.test.ts
deleted file mode 100644
index af1035d..0000000
--- a/apps/web/src/server/index.test.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-import { afterAll, describe, expect, test, vi } from "vitest";
-import { handleRequest } from "./index";
-
-describe("server request routing", () => {
- const consoleInfo = vi.spyOn(console, "info").mockImplementation(() => undefined);
-
- afterAll(() => {
- consoleInfo.mockRestore();
- });
-
- test("routes dashboard tile batch requests", async () => {
- const response = await handleRequest(
- new Request("https://example.test/api/dashboard/tiles", {
- method: "POST",
- body: JSON.stringify({
- tiles: [
- {
- kind: "status",
- stripId: "footer-status",
- id: "auto-refresh",
- },
- ],
- }),
- }),
- );
-
- expect(response.status).toBe(200);
- await expect(response.json()).resolves.toMatchObject({
- state: "ready",
- tiles: [
- {
- state: "ready",
- tile: {
- kind: "status",
- stripId: "footer-status",
- id: "auto-refresh",
- },
- },
- ],
- });
- });
-
- test("rejects non-post dashboard tile batch requests", async () => {
- const response = await handleRequest(
- new Request("https://example.test/api/dashboard/tiles", {
- method: "GET",
- }),
- );
-
- expect(response.status).toBe(405);
- expect(response.headers.get("allow")).toBe("POST");
- });
-
- test("routes dashboard event stream requests", async () => {
- const response = await handleRequest(
- new Request("https://example.test/api/dashboard/events", {
- method: "GET",
- }),
- );
-
- expect(response.status).toBe(200);
- expect(response.headers.get("content-type")).toBe(
- "text/event-stream; charset=utf-8",
- );
- });
-
- test("rejects non-get dashboard event stream requests", async () => {
- const response = await handleRequest(
- new Request("https://example.test/api/dashboard/events", {
- method: "POST",
- }),
- );
-
- expect(response.status).toBe(405);
- expect(response.headers.get("allow")).toBe("GET");
- });
-});
diff --git a/apps/web/src/server/index.ts b/apps/web/src/server/index.ts
index 9cbcbd9..860d042 100644
--- a/apps/web/src/server/index.ts
+++ b/apps/web/src/server/index.ts
@@ -1,11 +1,6 @@
import { extname, normalize } from "node:path";
import { handleAgentDashboardRoute } from "./routes/agent-dashboard";
-import {
- handleDashboardEventsRoute,
- handleDashboardRoute,
- handleDashboardTileRoute,
- handleDashboardTilesRoute,
-} from "./routes/dashboard";
+import { handleDashboardRoute } from "./routes/dashboard";
const host = process.env.HOST || "0.0.0.0";
const port = Number(process.env.PORT || 3000);
@@ -28,21 +23,6 @@ export async function handleRequest(request: Request): Promise {
return handleDashboardRoute();
}
- if (url.pathname === "/api/dashboard/tiles") {
- if (request.method !== "POST") return methodNotAllowed(["POST"]);
- return handleDashboardTilesRoute(request);
- }
-
- if (url.pathname === "/api/dashboard/events") {
- if (request.method !== "GET") return methodNotAllowed(["GET"]);
- return handleDashboardEventsRoute({ signal: request.signal });
- }
-
- if (url.pathname.startsWith("/api/dashboard/tile/")) {
- if (request.method !== "GET") return methodNotAllowed(["GET"]);
- return handleDashboardTileRoute(url.pathname);
- }
-
if (url.pathname === "/api/agent/dashboard") {
if (request.method !== "POST") return methodNotAllowed(["POST"]);
return handleAgentDashboardRoute(request);
diff --git a/apps/web/src/server/routes/dashboard.test.ts b/apps/web/src/server/routes/dashboard.test.ts
index a4cf7ef..21ad58d 100644
--- a/apps/web/src/server/routes/dashboard.test.ts
+++ b/apps/web/src/server/routes/dashboard.test.ts
@@ -1,34 +1,11 @@
-import { afterAll, afterEach, describe, expect, test, vi } from "vitest";
+import { describe, expect, test } from "vitest";
import { dimensionLabDashboardFixture } from "$lib/dashboard-seed/dimensionlab";
-import {
- createDashboardTileCache,
- dashboardTileCacheKey,
- handleDashboardEventsRoute,
- handleDashboardTilesRoute,
- handleDashboardTileRoute,
- loadDashboardResponse,
- loadDashboardTilesResponse,
- loadDashboardTileResponse,
- type DashboardTileResolutionLogEvent,
-} from "./dashboard";
+import { loadDashboardResponse } from "./dashboard";
describe("dashboard API route", () => {
- const consoleInfo = vi.spyOn(console, "info").mockImplementation(() => undefined);
-
- afterEach(() => {
- consoleInfo.mockClear();
- });
-
- afterAll(() => {
- consoleInfo.mockRestore();
- });
-
- test("returns the ready dashboard shell without hydrating live datasources", async () => {
- const fetch = vi.spyOn(globalThis, "fetch").mockRejectedValue(
- new Error("live datasource fetch should not run for the shell response"),
- );
-
+ test("returns ready dashboard runtime state from the existing model loader", async () => {
const response = await loadDashboardResponse({
+ disableLiveDatasources: true,
refreshSeedDocument: true,
seedIfEmpty: true,
});
@@ -38,762 +15,5 @@ describe("dashboard API route", () => {
expect(response.document.metadata.title).toBe(
dimensionLabDashboardFixture.metadata.title,
);
- expect(fetch).not.toHaveBeenCalled();
-
- fetch.mockRestore();
- });
-
- test("reports when client-side live hydration is disabled", async () => {
- const previous = process.env.DISABLE_LIVE_DATASOURCES;
- process.env.DISABLE_LIVE_DATASOURCES = "1";
-
- try {
- const response = await loadDashboardResponse({
- refreshSeedDocument: true,
- seedIfEmpty: true,
- });
-
- expect(response.state).toBe("ready");
- if (response.state !== "ready") throw new Error("expected ready dashboard");
- expect(response.liveDatasourceHydration).toEqual({ enabled: false });
- } finally {
- if (previous === undefined) {
- delete process.env.DISABLE_LIVE_DATASOURCES;
- } else {
- process.env.DISABLE_LIVE_DATASOURCES = previous;
- }
- }
- });
-
- test("hydrates a telemetry tile independently from the dashboard shell", async () => {
- const fetch = vi.fn(async (input: RequestInfo | URL) => {
- const url = String(input);
-
- if (url.startsWith("https://prometheus.example/api/v1/query_range")) {
- return jsonResponse({
- status: "success",
- data: {
- result: [
- {
- metric: { host: "linux-infra" },
- values: [
- [1771430000, "10"],
- [1771430060, "20"],
- [1771430120, "42"],
- ],
- },
- ],
- },
- });
- }
-
- if (url.startsWith("https://prometheus.example/api/v1/query")) {
- return jsonResponse({
- status: "success",
- data: {
- result: [
- {
- metric: { host: "linux-infra" },
- value: [1771430400, "42"],
- },
- ],
- },
- });
- }
-
- throw new Error(`Unhandled test request: ${url}`);
- });
-
- const response = await loadDashboardTileResponse(
- { kind: "telemetry", id: "infra-ram" },
- {
- fetch,
- prometheusBaseUrl: "https://prometheus.example",
- refreshSeedDocument: true,
- seedIfEmpty: true,
- },
- );
-
- expect(response.state).toBe("ready");
- if (response.state !== "ready") throw new Error("expected ready tile");
- expect(response.tile).toEqual({ kind: "telemetry", id: "infra-ram" });
- expect(response.item).toMatchObject({
- id: "infra-ram",
- value: { kind: "percent", value: 42 },
- severity: "ok",
- detail: "linux-infra",
- sparkline: [10, 20, 42],
- });
- expect(fetch).toHaveBeenCalledWith(
- expect.stringContaining("/api/v1/query?"),
- expect.objectContaining({ cache: "no-store" }),
- );
- expect(fetch).toHaveBeenCalledWith(
- expect.stringContaining("/api/v1/query_range?"),
- expect.objectContaining({ cache: "no-store" }),
- );
- });
-
- test("hydrates a service tile with its service group identity", async () => {
- const fetch = vi.fn(async () =>
- jsonResponse({
- status: "UP",
- ping: 42,
- }),
- );
-
- const response = await loadDashboardTileResponse(
- { kind: "service", groupId: "essentials", id: "vaultwarden" },
- {
- fetch,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- },
- );
-
- expect(response.state).toBe("ready");
- if (response.state !== "ready") throw new Error("expected ready tile");
- expect(response.tile).toEqual({
- kind: "service",
- groupId: "essentials",
- id: "vaultwarden",
- });
- expect(response.item).toMatchObject({
- id: "vaultwarden",
- severity: "ok",
- detail: "42 ms",
- });
- });
-
- test("caches ready tile responses until the tile ttl expires", async () => {
- const cache = createDashboardTileCache();
- let now = 1_000;
- const fetch = vi.fn(async () => {
- now += 7;
- return jsonResponse({
- status: "UP",
- ping: 42,
- });
- });
- const tile = { kind: "service", groupId: "essentials", id: "vaultwarden" } as const;
-
- const first = await loadDashboardTileResponse(tile, {
- fetch,
- now: () => now,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- });
- const second = await loadDashboardTileResponse(tile, {
- fetch,
- now: () => now + 29_999,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- });
- now += 30_001;
- const third = await loadDashboardTileResponse(tile, {
- fetch,
- now: () => now,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- });
-
- expect(first).toEqual(second);
- expect(third.state).toBe("ready");
- expect(fetch).toHaveBeenCalledTimes(2);
- });
-
- test("logs tile duration and cache hit or miss metadata", async () => {
- let now = 1_000;
- const logs: DashboardTileResolutionLogEvent[] = [];
- const tile = { kind: "service", groupId: "essentials", id: "vaultwarden" } as const;
- const service = dimensionLabDashboardFixture.serviceGroups
- .flatMap((group) => group.services)
- .find((item) => item.id === tile.id);
- if (!service) throw new Error("missing service fixture");
-
- let cacheCalls = 0;
- const tileCache = {
- async resolve() {
- cacheCalls += 1;
- if (cacheCalls === 1) now += 7;
- const cacheState = cacheCalls === 1 ? "miss" as const : "hit" as const;
-
- return {
- cache: cacheState,
- coalesced: false,
- response: {
- state: "ready" as const,
- tile,
- item: service,
- },
- };
- },
- };
-
- await loadDashboardTileResponse(tile, {
- logTileResolution: (event) => logs.push(event),
- now: () => now,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache,
- });
- now += 10;
- await loadDashboardTileResponse(tile, {
- logTileResolution: (event) => logs.push(event),
- now: () => now,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache,
- });
-
- expect(logs).toEqual([
- expect.objectContaining({
- cache: "miss",
- coalesced: false,
- durationMs: 7,
- status: "ready",
- tileKey: dashboardTileCacheKey(tile),
- }),
- expect.objectContaining({
- cache: "hit",
- coalesced: false,
- durationMs: 0,
- status: "ready",
- tileKey: dashboardTileCacheKey(tile),
- }),
- ]);
- });
-
- test("keeps telemetry tiles cached for fifteen seconds", async () => {
- const cache = createDashboardTileCache();
- let now = 1_000;
- const fetch = telemetryFetch();
- const tile = { kind: "telemetry", id: "infra-ram" } as const;
-
- await loadDashboardTileResponse(tile, {
- fetch,
- now: () => now,
- prometheusBaseUrl: "https://prometheus.example",
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- });
- await loadDashboardTileResponse(tile, {
- fetch,
- now: () => now + 14_999,
- prometheusBaseUrl: "https://prometheus.example",
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- });
- now += 15_001;
- await loadDashboardTileResponse(tile, {
- fetch,
- now: () => now,
- prometheusBaseUrl: "https://prometheus.example",
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- });
-
- expect(fetch).toHaveBeenCalledTimes(4);
- });
-
- test("keeps weather module tiles cached for ten minutes", async () => {
- const cache = createDashboardTileCache();
- let now = 1_000;
- const fetch = vi.fn(async () =>
- jsonResponse({
- current: {
- apparent_temperature: 19,
- temperature_2m: 20,
- weather_code: 0,
- wind_speed_10m: 11,
- },
- }),
- );
- const tile = { kind: "module", id: "weather-amsterdam" } as const;
-
- await loadDashboardTileResponse(tile, {
- fetch,
- now: () => now,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- });
- await loadDashboardTileResponse(tile, {
- fetch,
- now: () => now + 599_999,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- });
- now += 600_001;
- await loadDashboardTileResponse(tile, {
- fetch,
- now: () => now,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- });
-
- expect(fetch).toHaveBeenCalledTimes(2);
- });
-
- test("coalesces concurrent tile requests for the same cache key", async () => {
- const cache = createDashboardTileCache();
- const logs: DashboardTileResolutionLogEvent[] = [];
- let resolveFetch: ((response: Response) => void) | undefined;
- const fetch = vi.fn(() =>
- new Promise((resolve) => {
- resolveFetch = resolve;
- })
- );
- const tile = { kind: "service", groupId: "essentials", id: "vaultwarden" } as const;
-
- const first = loadDashboardTileResponse(tile, {
- fetch,
- logTileResolution: (event) => logs.push(event),
- now: () => 1_000,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- });
- const second = loadDashboardTileResponse(tile, {
- fetch,
- logTileResolution: (event) => logs.push(event),
- now: () => 1_000,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- });
-
- await Promise.resolve();
- expect(fetch).toHaveBeenCalledTimes(1);
-
- resolveFetch?.(jsonResponse({ status: "UP", ping: 42 }));
- expect(await first).toEqual(await second);
- expect(logs).toEqual([
- expect.objectContaining({
- cache: "miss",
- coalesced: false,
- status: "ready",
- tileKey: dashboardTileCacheKey(tile),
- }),
- expect.objectContaining({
- cache: "miss",
- coalesced: true,
- status: "ready",
- tileKey: dashboardTileCacheKey(tile),
- }),
- ]);
- });
-
- test("logs coalesced metadata when shared tile requests fail", async () => {
- const cache = createDashboardTileCache();
- let rejectLoad: ((error: Error) => void) | undefined;
- const load = vi.fn(() =>
- new Promise((_resolve, reject) => {
- rejectLoad = reject;
- })
- );
-
- const first = cache.resolve("tile-a", 30_000, 1_000, load);
- const second = cache.resolve("tile-a", 30_000, 1_000, load);
-
- await Promise.resolve();
- expect(load).toHaveBeenCalledTimes(1);
-
- rejectLoad?.(new TypeError("upstream failed"));
- await expect(first).rejects.toThrow("upstream failed");
- await expect(second).rejects.toMatchObject({
- cache: "miss",
- coalesced: true,
- cause: expect.any(TypeError),
- });
- });
-
- test("logs coalesced metadata for failed tile cache resolution", async () => {
- const logs: DashboardTileResolutionLogEvent[] = [];
- const tile = { kind: "service", groupId: "essentials", id: "vaultwarden" } as const;
-
- await expect(
- loadDashboardTileResponse(tile, {
- logTileResolution: (event) => logs.push(event),
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: {
- async resolve() {
- throw {
- cache: "miss",
- cause: new TypeError("coalesced cache failed"),
- coalesced: true,
- };
- },
- },
- }),
- ).rejects.toThrow("coalesced cache failed");
-
- expect(logs).toEqual([
- expect.objectContaining({
- cache: "miss",
- coalesced: true,
- errorCategory: "TypeError",
- status: "error",
- tileKey: dashboardTileCacheKey(tile),
- }),
- ]);
- });
-
- test("logs error categories for failed tile cache resolution", async () => {
- const logs: DashboardTileResolutionLogEvent[] = [];
- const tile = { kind: "service", groupId: "essentials", id: "vaultwarden" } as const;
-
- await expect(
- loadDashboardTileResponse(tile, {
- logTileResolution: (event) => logs.push(event),
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: {
- async resolve() {
- throw new TypeError("cache failed");
- },
- },
- }),
- ).rejects.toThrow("cache failed");
-
- expect(logs).toEqual([
- expect.objectContaining({
- cache: "miss",
- coalesced: false,
- errorCategory: "TypeError",
- status: "error",
- tileKey: dashboardTileCacheKey(tile),
- }),
- ]);
- });
-
- test("uses structured tile cache keys when identifiers contain delimiters", () => {
- expect(
- dashboardTileCacheKey({ kind: "service", groupId: "a:b", id: "c" }),
- ).not.toBe(
- dashboardTileCacheKey({ kind: "service", groupId: "a", id: "b:c" }),
- );
- expect(
- dashboardTileCacheKey({ kind: "status", stripId: "a:b", id: "c" }),
- ).not.toBe(
- dashboardTileCacheKey({ kind: "status", stripId: "a", id: "b:c" }),
- );
- });
-
- test("uses thirty-second status aggregate and five-minute static status ttl buckets", async () => {
- const cache = createDashboardTileCache();
- const fetch = vi.fn(async () => jsonResponse({ status: "UP", ping: 42 }));
- const serviceCheckCount = dimensionLabDashboardFixture.serviceGroups
- .flatMap((group) => group.services).length;
-
- await loadDashboardTileResponse(
- { kind: "status", stripId: "footer-status", id: "system-status" },
- {
- fetch,
- now: () => 1_000,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- },
- );
- await loadDashboardTileResponse(
- { kind: "status", stripId: "footer-status", id: "system-status" },
- {
- fetch,
- now: () => 31_001,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- },
- );
-
- await loadDashboardTileResponse(
- { kind: "status", stripId: "footer-status", id: "auto-refresh" },
- {
- fetch,
- now: () => 1_000,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- },
- );
- await loadDashboardTileResponse(
- { kind: "status", stripId: "footer-status", id: "auto-refresh" },
- {
- fetch,
- now: () => 300_999,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- },
- );
- await loadDashboardTileResponse(
- { kind: "status", stripId: "footer-status", id: "auto-refresh" },
- {
- fetch,
- now: () => 301_001,
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: cache,
- },
- );
-
- expect(fetch).toHaveBeenCalledTimes(serviceCheckCount * 2);
- });
-
- test("serves tile route responses with short private cache headers", async () => {
- const response = await handleDashboardTileRoute(
- "/api/dashboard/tile/service/essentials/vaultwarden",
- {
- fetch: vi.fn(async () => jsonResponse({ status: "UP", ping: 42 })),
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: createDashboardTileCache(),
- },
- );
-
- expect(response.headers.get("cache-control")).toBe(
- "private, max-age=5, stale-while-revalidate=30",
- );
- });
-
- test("serves batch tile route responses", async () => {
- const response = await handleDashboardTilesRoute(
- new Request("https://example.test/api/dashboard/tiles", {
- method: "POST",
- body: JSON.stringify({
- tiles: [
- {
- kind: "status",
- stripId: "footer-status",
- id: "auto-refresh",
- },
- ],
- }),
- }),
- {
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: createDashboardTileCache(),
- },
- );
-
- expect(response.status).toBe(200);
- expect(response.headers.get("cache-control")).toBe(
- "private, max-age=5, stale-while-revalidate=30",
- );
- await expect(response.json()).resolves.toMatchObject({
- state: "ready",
- tiles: [
- {
- state: "ready",
- tile: {
- kind: "status",
- stripId: "footer-status",
- id: "auto-refresh",
- },
- },
- ],
- });
- });
-
- test("streams ready dashboard tile events", async () => {
- const controller = new AbortController();
- const response = await handleDashboardEventsRoute({
- refreshSeedDocument: true,
- seedIfEmpty: true,
- signal: controller.signal,
- tileCache: {
- async resolve(key) {
- controller.abort();
- const tile = JSON.parse(key);
-
- return {
- cache: "miss",
- coalesced: false,
- response: {
- state: "ready",
- tile,
- item: {
- id: tile.id,
- label: "Status",
- severity: "ok",
- value: "ok",
- },
- },
- };
- },
- },
- });
-
- expect(response.headers.get("content-type")).toBe(
- "text/event-stream; charset=utf-8",
- );
- expect(response.headers.get("cache-control")).toBe("no-cache");
- const body = await response.text();
- expect(body).toContain("event: dashboard-tile");
- expect(body).toContain('"state":"ready"');
- expect(body).toContain('"tile"');
- });
-
- test("rejects invalid batch tile requests", async () => {
- const response = await handleDashboardTilesRoute(
- new Request("https://example.test/api/dashboard/tiles", {
- method: "POST",
- body: JSON.stringify({
- tiles: [{ kind: "service", id: "missing-group" }],
- }),
- }),
- );
-
- expect(response.status).toBe(400);
- });
-
- test("resolves batch tile responses with server concurrency capped at six", async () => {
- let active = 0;
- let maxActive = 0;
- const started: string[] = [];
- const releases = new Map void>();
- const service = dimensionLabDashboardFixture.serviceGroups
- .flatMap((group) => group.services)[0];
- if (!service) throw new Error("missing service fixture");
- const tiles = Array.from({ length: 7 }, (_, index) => ({
- kind: "service" as const,
- groupId: "essentials",
- id: `service-${index}`,
- }));
-
- const batch = loadDashboardTilesResponse(tiles, {
- refreshSeedDocument: true,
- seedIfEmpty: true,
- tileCache: {
- async resolve(key) {
- active += 1;
- maxActive = Math.max(maxActive, active);
- started.push(key);
- await new Promise((resolve) => releases.set(key, resolve));
- active -= 1;
-
- return {
- cache: "miss",
- coalesced: false,
- response: {
- state: "ready",
- tile: JSON.parse(key),
- item: service,
- },
- };
- },
- },
- });
-
- await waitFor(() => started.length === 6);
- expect(maxActive).toBe(6);
- releases.get(started[0])?.();
- await waitFor(() => started.length === 7);
- for (const release of releases.values()) release();
-
- await expect(batch).resolves.toMatchObject({
- state: "ready",
- tiles: expect.arrayContaining([
- expect.objectContaining({
- state: "ready",
- tile: tiles[0],
- }),
- ]),
- });
- expect(maxActive).toBe(6);
- });
-
- test("does not hydrate tile routes when live datasources are disabled", async () => {
- const previous = process.env.DISABLE_LIVE_DATASOURCES;
- process.env.DISABLE_LIVE_DATASOURCES = "1";
- const fetch = vi.spyOn(globalThis, "fetch").mockRejectedValue(
- new Error("live datasource fetch should not run when disabled"),
- );
-
- try {
- const response = await loadDashboardTileResponse(
- { kind: "telemetry", id: "infra-ram" },
- {
- refreshSeedDocument: true,
- seedIfEmpty: true,
- },
- );
-
- expect(response.state).toBe("disabled");
- expect(fetch).not.toHaveBeenCalled();
- } finally {
- fetch.mockRestore();
- if (previous === undefined) {
- delete process.env.DISABLE_LIVE_DATASOURCES;
- } else {
- process.env.DISABLE_LIVE_DATASOURCES = previous;
- }
- }
});
});
-
-function jsonResponse(payload: unknown): Response {
- return new Response(JSON.stringify(payload), {
- headers: { "content-type": "application/json" },
- });
-}
-
-async function waitFor(predicate: () => boolean) {
- for (let attempt = 0; attempt < 20; attempt += 1) {
- if (predicate()) return;
- await Promise.resolve();
- }
-
- throw new Error("condition was not met");
-}
-
-function telemetryFetch() {
- return vi.fn(async (input: RequestInfo | URL) => {
- const url = String(input);
-
- if (url.startsWith("https://prometheus.example/api/v1/query_range")) {
- return jsonResponse({
- status: "success",
- data: {
- result: [
- {
- metric: { host: "linux-infra" },
- values: [
- [1771430000, "10"],
- [1771430060, "20"],
- [1771430120, "42"],
- ],
- },
- ],
- },
- });
- }
-
- if (url.startsWith("https://prometheus.example/api/v1/query")) {
- return jsonResponse({
- status: "success",
- data: {
- result: [
- {
- metric: { host: "linux-infra" },
- value: [1771430400, "42"],
- },
- ],
- },
- });
- }
-
- throw new Error(`Unhandled test request: ${url}`);
- });
-}
diff --git a/apps/web/src/server/routes/dashboard.ts b/apps/web/src/server/routes/dashboard.ts
index c42227c..21c30e4 100644
--- a/apps/web/src/server/routes/dashboard.ts
+++ b/apps/web/src/server/routes/dashboard.ts
@@ -1,138 +1,21 @@
-import type { DashboardDocument } from "@dimensionlab/dashboard-model";
import {
loadDashboardRuntime,
type DashboardRuntimeOptions,
type DashboardRuntimeState,
} from "$lib/server/dashboard";
-import {
- resolveDashboardDatasources,
- resolveDashboardTile,
- type DashboardTileReference,
- type DashboardTileResolution,
- type DatasourceResolutionOptions,
-} from "$lib/server/datasources";
+import { resolveDashboardDatasources } from "$lib/server/datasources";
export interface LoadDashboardResponseOptions
extends Pick<
DashboardRuntimeOptions,
"refreshSeedDocument" | "seedIfEmpty" | "seedDocument"
- >,
- DatasourceResolutionOptions {
+ > {
disableLiveDatasources?: boolean;
- hydrateLiveDatasources?: boolean;
- logTileResolution?: (event: DashboardTileResolutionLogEvent) => void;
- now?: () => number;
- tileCache?: DashboardTileCache;
-}
-
-export interface DashboardEventsRouteOptions extends LoadDashboardResponseOptions {
- signal?: AbortSignal;
-}
-
-interface DashboardTileCacheEntry {
- expiresAt: number;
- response: DashboardTileResolution;
-}
-
-export interface DashboardTileCache {
- resolve(
- key: string,
- ttlMs: number,
- now: number,
- load: () => Promise,
- ): Promise;
-}
-
-const dashboardTileResponseHeaders = {
- "Cache-Control": "private, max-age=5, stale-while-revalidate=30",
-};
-
-const dashboardBatchTileConcurrency = 6;
-
-const defaultDashboardTileCache = createDashboardTileCache();
-
-interface DashboardTileCacheResult {
- cache: "hit" | "miss";
- coalesced: boolean;
- response: DashboardTileResolution;
-}
-
-export interface DashboardTileResolutionLogEvent {
- cache: "bypass" | "hit" | "miss";
- coalesced: boolean;
- durationMs: number;
- errorCategory?: string;
- status: DashboardTileResolution["state"] | "error";
- tileKey: string;
-}
-
-export interface DashboardTilesBatchResponse {
- state: "ready";
- tiles: DashboardTileResolution[];
-}
-
-export function createDashboardTileCache(): DashboardTileCache {
- const entries = new Map();
- const inFlight = new Map>();
-
- return {
- async resolve(key, ttlMs, now, load) {
- const cached = entries.get(key);
- if (cached && cached.expiresAt > now) {
- return {
- cache: "hit",
- coalesced: false,
- response: cached.response,
- };
- }
-
- const active = inFlight.get(key);
- if (active) {
- return active
- .then((response) => ({
- cache: "miss" as const,
- coalesced: true,
- response,
- }))
- .catch((error) => {
- throw new DashboardTileCacheResolutionError(error, {
- cache: "miss",
- coalesced: true,
- });
- });
- }
-
- const request = load()
- .then((response) => {
- if (response.state === "ready") {
- entries.set(key, {
- expiresAt: now + ttlMs,
- response,
- });
- }
-
- return response;
- })
- .finally(() => {
- inFlight.delete(key);
- });
-
- inFlight.set(key, request);
- return request.then((response) => ({
- cache: "miss" as const,
- coalesced: false,
- response,
- }));
- },
- };
}
export async function loadDashboardResponse(
options: LoadDashboardResponseOptions = {},
): Promise {
- const liveHydrationEnabled =
- !options.disableLiveDatasources &&
- process.env.DISABLE_LIVE_DATASOURCES !== "1";
const dashboard = loadDashboardRuntime(undefined, {
refreshSeedDocument: options.refreshSeedDocument ?? true,
seedIfEmpty: options.seedIfEmpty ?? true,
@@ -143,467 +26,16 @@ export async function loadDashboardResponse(
return dashboard;
}
- if (
- !options.hydrateLiveDatasources ||
- options.disableLiveDatasources ||
- process.env.DISABLE_LIVE_DATASOURCES === "1"
- ) {
- return {
- ...dashboard,
- liveDatasourceHydration: {
- enabled: liveHydrationEnabled,
- },
- };
+ if (options.disableLiveDatasources || process.env.DISABLE_LIVE_DATASOURCES === "1") {
+ return dashboard;
}
return {
...dashboard,
- document: await resolveDashboardDatasources(dashboard.document, options),
- liveDatasourceHydration: {
- enabled: false,
- },
- };
-}
-
-export async function loadDashboardTileResponse(
- tile: DashboardTileReference,
- options: LoadDashboardResponseOptions = {},
-): Promise {
- if (
- options.disableLiveDatasources ||
- process.env.DISABLE_LIVE_DATASOURCES === "1"
- ) {
- const tileKey = dashboardTileCacheKey(tile);
- const startedAt = options.now?.() ?? Date.now();
- const response = {
- state: "disabled",
- tile,
- message: "Live datasource hydration is disabled.",
- } satisfies DashboardTileResolution;
- logDashboardTileResolution(options, {
- cache: "bypass",
- coalesced: false,
- durationMs: elapsedDashboardTileMs(startedAt, options),
- status: response.state,
- tileKey,
- });
- return response;
- }
-
- const tileKey = dashboardTileCacheKey(tile);
- const startedAt = options.now?.() ?? Date.now();
-
- const dashboard = loadDashboardRuntime(undefined, {
- refreshSeedDocument: options.refreshSeedDocument ?? true,
- seedIfEmpty: options.seedIfEmpty ?? true,
- seedDocument: options.seedDocument,
- });
-
- if (dashboard.state !== "ready") {
- const response = {
- state: "not_found",
- tile,
- message: `Dashboard is not ready: ${dashboard.state}`,
- } satisfies DashboardTileResolution;
- logDashboardTileResolution(options, {
- cache: "bypass",
- coalesced: false,
- durationMs: elapsedDashboardTileMs(startedAt, options),
- status: response.state,
- tileKey,
- });
- return response;
- }
-
- const cache = options.tileCache || defaultDashboardTileCache;
- const now = options.now?.() ?? Date.now();
-
- try {
- const result = await cache.resolve(
- tileKey,
- dashboardTileTtlMs(dashboard.document, tile),
- now,
- () => resolveDashboardTile(dashboard.document, tile, options),
- );
- logDashboardTileResolution(options, {
- cache: result.cache,
- coalesced: result.coalesced,
- durationMs: elapsedDashboardTileMs(startedAt, options),
- status: result.response.state,
- tileKey,
- });
- return result.response;
- } catch (error) {
- const cacheError = dashboardTileCacheResolutionError(error);
- logDashboardTileResolution(options, {
- cache: cacheError?.cache ?? "miss",
- coalesced: cacheError?.coalesced ?? false,
- durationMs: elapsedDashboardTileMs(startedAt, options),
- errorCategory: dashboardTileErrorCategory(cacheError?.cause ?? error),
- status: "error",
- tileKey,
- });
- throw cacheError?.cause ?? error;
- }
-}
-
-export async function loadDashboardTilesResponse(
- tiles: DashboardTileReference[],
- options: LoadDashboardResponseOptions = {},
-): Promise {
- return {
- state: "ready",
- tiles: await resolveDashboardTilesBatch(tiles, options),
+ document: await resolveDashboardDatasources(dashboard.document),
};
}
export async function handleDashboardRoute(): Promise {
return Response.json(await loadDashboardResponse());
}
-
-export async function handleDashboardTileRoute(
- pathname: string,
- options: LoadDashboardResponseOptions = {},
-): Promise {
- const tile = parseDashboardTilePath(pathname);
- if (!tile) {
- return Response.json(
- { ok: false, message: "Invalid dashboard tile route" },
- { status: 404 },
- );
- }
-
- const response = await loadDashboardTileResponse(tile, options);
- return Response.json(response, {
- status: response.state === "not_found" ? 404 : 200,
- headers: dashboardTileResponseHeaders,
- });
-}
-
-export async function handleDashboardTilesRoute(
- request: Request,
- options: LoadDashboardResponseOptions = {},
-): Promise {
- const tiles = await parseDashboardTilesBatchRequest(request);
- if (!tiles) {
- return Response.json(
- { ok: false, message: "Invalid dashboard tiles request" },
- { status: 400 },
- );
- }
-
- return Response.json(await loadDashboardTilesResponse(tiles, options), {
- headers: dashboardTileResponseHeaders,
- });
-}
-
-export async function handleDashboardEventsRoute(
- options: DashboardEventsRouteOptions = {},
-): Promise {
- const stream = new ReadableStream({
- async start(controller) {
- const encoder = new TextEncoder();
- const dashboard = await loadDashboardResponse({
- ...options,
- hydrateLiveDatasources: false,
- });
-
- if (dashboard.state !== "ready" || options.signal?.aborted) {
- controller.close();
- return;
- }
-
- try {
- for (const tile of dashboardEventTiles(dashboard.document)) {
- if (options.signal?.aborted) break;
- const resolution = await loadDashboardTileResponse(tile, options);
- if (resolution.state === "ready") {
- controller.enqueue(
- encoder.encode(dashboardTileEventChunk(resolution)),
- );
- }
- }
- } finally {
- controller.close();
- }
- },
- });
-
- return new Response(stream, {
- headers: {
- "Cache-Control": "no-cache",
- "Connection": "keep-alive",
- "Content-Type": "text/event-stream; charset=utf-8",
- "X-Accel-Buffering": "no",
- },
- });
-}
-
-export function dashboardTileCacheKey(tile: DashboardTileReference): string {
- return JSON.stringify(tile);
-}
-
-function logDashboardTileResolution(
- options: LoadDashboardResponseOptions,
- event: DashboardTileResolutionLogEvent,
-): void {
- if (options.logTileResolution) {
- options.logTileResolution(event);
- return;
- }
-
- console.info("dashboard.tile", event);
-}
-
-function elapsedDashboardTileMs(
- startedAt: number,
- options: LoadDashboardResponseOptions,
-): number {
- const now = options.now?.() ?? Date.now();
- return Math.max(0, now - startedAt);
-}
-
-function dashboardTileErrorCategory(error: unknown): string {
- if (
- typeof error === "object" &&
- error !== null &&
- "name" in error &&
- typeof error.name === "string"
- ) {
- return error.name;
- }
-
- return "unknown";
-}
-
-async function resolveDashboardTilesBatch(
- tiles: DashboardTileReference[],
- options: LoadDashboardResponseOptions,
-): Promise {
- const results: DashboardTileResolution[] = new Array(tiles.length);
- let nextIndex = 0;
-
- async function worker() {
- while (nextIndex < tiles.length) {
- const index = nextIndex;
- nextIndex += 1;
- results[index] = await loadDashboardTileResponse(tiles[index], options);
- }
- }
-
- await Promise.all(
- Array.from(
- { length: Math.min(dashboardBatchTileConcurrency, tiles.length) },
- () => worker(),
- ),
- );
-
- return results;
-}
-
-async function parseDashboardTilesBatchRequest(
- request: Request,
-): Promise {
- try {
- const body = await request.json() as unknown;
- if (
- typeof body !== "object" ||
- body === null ||
- !("tiles" in body) ||
- !Array.isArray(body.tiles)
- ) {
- return null;
- }
-
- const tiles = body.tiles.map(parseDashboardTileReference);
- return tiles.every((tile): tile is DashboardTileReference => tile !== null)
- ? tiles
- : null;
- } catch {
- return null;
- }
-}
-
-function parseDashboardTileReference(value: unknown): DashboardTileReference | null {
- if (typeof value !== "object" || value === null || !("kind" in value)) {
- return null;
- }
-
- if (
- (value.kind === "telemetry" || value.kind === "module") &&
- "id" in value &&
- typeof value.id === "string"
- ) {
- return { kind: value.kind, id: value.id };
- }
-
- if (
- value.kind === "service" &&
- "groupId" in value &&
- typeof value.groupId === "string" &&
- "id" in value &&
- typeof value.id === "string"
- ) {
- return {
- kind: "service",
- groupId: value.groupId,
- id: value.id,
- };
- }
-
- if (
- value.kind === "status" &&
- "stripId" in value &&
- typeof value.stripId === "string" &&
- "id" in value &&
- typeof value.id === "string"
- ) {
- return {
- kind: "status",
- stripId: value.stripId,
- id: value.id,
- };
- }
-
- return null;
-}
-
-function dashboardEventTiles(document: DashboardDocument): DashboardTileReference[] {
- const status = document.statusStrips.flatMap((strip) =>
- strip.items.map((item): DashboardTileReference => ({
- kind: "status",
- stripId: strip.id,
- id: item.id,
- })),
- );
- const telemetry = document.telemetry
- .filter((card) => card.datasource?.type === "external")
- .map((card): DashboardTileReference => ({ kind: "telemetry", id: card.id }));
- const modules = (document.modules || [])
- .filter((module) =>
- module.datasource?.type === "external" ||
- module.id === "runtime-health-summary"
- )
- .map((module): DashboardTileReference => ({ kind: "module", id: module.id }));
- const services = document.serviceGroups.flatMap((group) =>
- group.services
- .filter((service) => service.datasource?.type === "external")
- .map((service): DashboardTileReference => ({
- kind: "service",
- groupId: group.id,
- id: service.id,
- })),
- );
-
- return [...status, ...telemetry, ...modules, ...services];
-}
-
-function dashboardTileEventChunk(resolution: DashboardTileResolution): string {
- return [
- "event: dashboard-tile",
- `data: ${JSON.stringify(resolution)}`,
- "",
- "",
- ].join("\n");
-}
-
-class DashboardTileCacheResolutionError extends Error {
- readonly cache: "hit" | "miss";
- readonly coalesced: boolean;
- override readonly cause: unknown;
-
- constructor(
- cause: unknown,
- metadata: Pick,
- ) {
- super("Dashboard tile cache resolution failed");
- this.name = "DashboardTileCacheResolutionError";
- this.cause = cause;
- this.cache = metadata.cache;
- this.coalesced = metadata.coalesced;
- }
-}
-
-function dashboardTileCacheResolutionError(
- error: unknown,
-): DashboardTileCacheResolutionFailure | null {
- if (
- typeof error === "object" &&
- error !== null &&
- "cache" in error &&
- (error.cache === "hit" || error.cache === "miss") &&
- "coalesced" in error &&
- typeof error.coalesced === "boolean" &&
- "cause" in error
- ) {
- return {
- cache: error.cache,
- cause: error.cause,
- coalesced: error.coalesced,
- };
- }
-
- return null;
-}
-
-interface DashboardTileCacheResolutionFailure {
- cache: "hit" | "miss";
- cause: unknown;
- coalesced: boolean;
-}
-
-function dashboardTileTtlMs(
- document: DashboardDocument,
- tile: DashboardTileReference,
-): number {
- if (tile.kind === "telemetry") return 15_000;
- if (tile.kind === "service") return 30_000;
-
- if (tile.kind === "module") {
- const module = document.modules?.find((item) => item.id === tile.id);
- if (
- module?.datasource?.type === "external" &&
- module.datasource.adapter === "weather"
- ) {
- return 10 * 60_000;
- }
- return 30_000;
- }
-
- if (["system-status", "uptime", "load-avg"].includes(tile.id)) {
- return 30_000;
- }
-
- return 5 * 60_000;
-}
-
-function parseDashboardTilePath(pathname: string): DashboardTileReference | null {
- const parts = pathname.split("/").filter(Boolean);
- const [, dashboard, tileRoot, kind, firstId, secondId] = parts;
- if (dashboard !== "dashboard" || tileRoot !== "tile" || !kind || !firstId) {
- return null;
- }
-
- const id = decodeURIComponent(firstId);
- if (kind === "telemetry" || kind === "module") {
- return { kind, id };
- }
-
- if (kind === "service" && secondId) {
- return {
- kind,
- groupId: id,
- id: decodeURIComponent(secondId),
- };
- }
-
- if (kind === "status" && secondId) {
- return {
- kind,
- stripId: id,
- id: decodeURIComponent(secondId),
- };
- }
-
- return null;
-}
diff --git a/apps/web/tests/e2e/dashboard.spec.ts b/apps/web/tests/e2e/dashboard.spec.ts
index 3eb0944..48eac8b 100644
--- a/apps/web/tests/e2e/dashboard.spec.ts
+++ b/apps/web/tests/e2e/dashboard.spec.ts
@@ -16,6 +16,7 @@ const linkedServiceIds = [
"open-webui",
"comfyui",
"models",
+ "prompt-registry",
"adminer",
"assistant",
"suna",
@@ -103,7 +104,7 @@ test.describe("dashboard page QA gate", () => {
expect(metrics.runtimeBottom).toBeLessThanOrEqual(956);
expect(metrics.footerBottom).toBeLessThanOrEqual(956);
expect(metrics.telemetryCardCount).toBe(16);
- expect(metrics.serviceRowCount).toBe(27);
+ expect(metrics.serviceRowCount).toBe(28);
expect(metrics.footerCellCount).toBe(5);
expect(metrics.clippedItems).toEqual([]);
});
diff --git a/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png b/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png
index a1c0ada..c548a8a 100644
Binary files a/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png and b/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png differ
diff --git a/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png b/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png
index f5d5163..a007213 100644
Binary files a/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png and b/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png differ
diff --git a/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png b/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png
index 48293a4..0542e73 100644
Binary files a/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png and b/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png differ
diff --git a/scripts/deploy-dimensionlab-website.sh b/scripts/deploy-dimensionlab-website.sh
deleted file mode 100755
index 4fac4cd..0000000
--- a/scripts/deploy-dimensionlab-website.sh
+++ /dev/null
@@ -1,362 +0,0 @@
-#!/usr/bin/env bash
-set -Eeuo pipefail
-
-APP_NAME="${APP_NAME:-dimensionlab-website}"
-SERVICE_NAME="${SERVICE_NAME:-dimensionlab-website.service}"
-CONTAINER_NAME="${CONTAINER_NAME:-dimensionlab-website}"
-IMAGE_REPO="${IMAGE_REPO:-localhost/dimensionlab-website}"
-CONTAINERFILE="${CONTAINERFILE:-apps/web/Containerfile}"
-PUBLIC_URL="${PUBLIC_URL:-https://dimensionlab.net/}"
-TILE_BATCH_URL="${TILE_BATCH_URL:-https://dimensionlab.net/api/dashboard/tiles}"
-DEPLOY_RESTART_STRATEGY="${DEPLOY_RESTART_STRATEGY:-auto}"
-DEPLOY_SMOKE_TIMEOUT_SECONDS="${DEPLOY_SMOKE_TIMEOUT_SECONDS:-120}"
-DEPLOY_CONTAINER_START_TIMEOUT_SECONDS="${DEPLOY_CONTAINER_START_TIMEOUT_SECONDS:-90}"
-
-dry_run=false
-rollback_tag=""
-release_tag=""
-latest_tag="${IMAGE_REPO}:latest"
-container_cli=""
-deployment_started=false
-rollback_done=false
-rollback_in_progress=false
-
-usage() {
- cat <&2
- if [ "${deployment_started:-false}" = "true" ] && [ "${rollback_in_progress:-false}" != "true" ]; then
- rollback || true
- fi
- exit 1
-}
-
-run() {
- if "$dry_run"; then
- printf '[deploy:%s] DRY-RUN:' "$APP_NAME"
- printf ' %q' "$@"
- printf '\n'
- return 0
- fi
-
- "$@"
-}
-
-for arg in "$@"; do
- case "$arg" in
- --dry-run)
- dry_run=true
- ;;
- -h|--help)
- usage
- exit 0
- ;;
- *)
- usage >&2
- fail "unknown argument: $arg"
- ;;
- esac
-done
-
-deployment_ref() {
- printf '%s' "${DEPLOY_REF:-${GITHUB_REF:-${FORGEJO_REF:-}}}"
-}
-
-deployment_event() {
- printf '%s' "${DEPLOY_EVENT_NAME:-${GITHUB_EVENT_NAME:-${FORGEJO_EVENT_NAME:-}}}"
-}
-
-require_main_push() {
- local event
- local ref
-
- event="$(deployment_event)"
- ref="$(deployment_ref)"
-
- if [ -n "$event" ] && [ "$event" != "push" ]; then
- fail "refusing to deploy for event '$event'; production deploys only run for push"
- fi
-
- if [ -n "$ref" ]; then
- [ "$ref" = "refs/heads/main" ] || fail "refusing to deploy ref '$ref'; expected refs/heads/main"
- return 0
- fi
-
- local branch
- branch="$(git branch --show-current 2>/dev/null || true)"
- [ "$branch" = "main" ] || fail "refusing to deploy branch '$branch'; expected main"
-}
-
-select_container_cli() {
- if [ -n "${DEPLOY_CONTAINER_CLI:-}" ]; then
- command -v "$DEPLOY_CONTAINER_CLI" >/dev/null 2>&1 || fail "container CLI not found: $DEPLOY_CONTAINER_CLI"
- container_cli="$DEPLOY_CONTAINER_CLI"
- return 0
- fi
-
- if command -v podman >/dev/null 2>&1; then
- container_cli="podman"
- return 0
- fi
-
- if command -v docker >/dev/null 2>&1; then
- container_cli="docker"
- return 0
- fi
-
- fail "podman or docker is required"
-}
-
-current_sha() {
- if [ -n "${DEPLOY_SHA:-${GITHUB_SHA:-}}" ]; then
- printf '%s' "${DEPLOY_SHA:-${GITHUB_SHA:-}}"
- return 0
- fi
-
- git rev-parse HEAD
-}
-
-tag_existing_latest_for_rollback() {
- rollback_tag="${IMAGE_REPO}:rollback-$(date -u +%Y%m%d%H%M%S)"
- if "$container_cli" image inspect "$latest_tag" >/dev/null 2>&1; then
- log "tagging current latest image as $rollback_tag"
- run "$container_cli" tag "$latest_tag" "$rollback_tag"
- else
- log "no existing $latest_tag image found; rollback image tag will not be created"
- rollback_tag=""
- fi
-}
-
-container_systemd_unit() {
- "$container_cli" inspect "$CONTAINER_NAME" \
- --format '{{ index .Config.Labels "PODMAN_SYSTEMD_UNIT" }}' 2>/dev/null || true
-}
-
-require_container_managed_by_service() {
- local unit
-
- if "$dry_run"; then
- log "DRY-RUN: would require $CONTAINER_NAME to be managed by $SERVICE_NAME"
- return 0
- fi
-
- unit="$(container_systemd_unit)"
- [ "$unit" = "$SERVICE_NAME" ] || fail "refusing to stop $CONTAINER_NAME; expected PODMAN_SYSTEMD_UNIT=$SERVICE_NAME, got '${unit:-unset}'"
-}
-
-validate_restart_strategy() {
- case "$DEPLOY_RESTART_STRATEGY" in
- systemctl)
- if ! "$dry_run" && ! systemctl --user show "$SERVICE_NAME" >/dev/null 2>&1; then
- fail "systemctl --user cannot access $SERVICE_NAME"
- fi
- ;;
- quadlet-container|kill-container)
- require_container_managed_by_service
- ;;
- auto)
- if "$dry_run"; then
- log "DRY-RUN: would validate automatic restart strategy"
- elif ! command -v systemctl >/dev/null 2>&1 || ! systemctl --user show "$SERVICE_NAME" >/dev/null 2>&1; then
- require_container_managed_by_service
- fi
- ;;
- *)
- fail "unknown DEPLOY_RESTART_STRATEGY: $DEPLOY_RESTART_STRATEGY"
- ;;
- esac
-}
-
-initialize_submodules() {
- log "initializing submodules"
- run git config --global url."https://git.dimensionlab.net/".insteadOf "ssh://git@git.dimensionlab.net/"
- run git submodule update --init --recursive
-}
-
-build_image() {
- local sha
- local short_sha
-
- sha="$(current_sha)"
- short_sha="${sha:0:12}"
- release_tag="${IMAGE_REPO}:${short_sha}"
-
- [ -f "$CONTAINERFILE" ] || fail "containerfile not found: $CONTAINERFILE"
-
- log "building $release_tag and $latest_tag from $CONTAINERFILE"
- run "$container_cli" build -f "$CONTAINERFILE" -t "$release_tag" -t "$latest_tag" .
-}
-
-restart_service() {
- log "restarting $SERVICE_NAME with strategy $DEPLOY_RESTART_STRATEGY"
-
- case "$DEPLOY_RESTART_STRATEGY" in
- systemctl)
- run systemctl --user restart "$SERVICE_NAME"
- ;;
- quadlet-container|kill-container)
- require_container_managed_by_service
- run "$container_cli" stop "$CONTAINER_NAME"
- ;;
- auto)
- if command -v systemctl >/dev/null 2>&1 && systemctl --user is-active "$SERVICE_NAME" >/dev/null 2>&1; then
- run systemctl --user restart "$SERVICE_NAME"
- else
- require_container_managed_by_service
- run "$container_cli" stop "$CONTAINER_NAME"
- fi
- ;;
- *)
- fail "unknown DEPLOY_RESTART_STRATEGY: $DEPLOY_RESTART_STRATEGY"
- ;;
- esac
-}
-
-latest_image_id() {
- "$container_cli" image inspect "$latest_tag" --format '{{.Id}}' 2>/dev/null || true
-}
-
-container_image_id() {
- "$container_cli" inspect "$CONTAINER_NAME" --format '{{.Image}}' 2>/dev/null || true
-}
-
-container_running() {
- local running
- running="$("$container_cli" inspect "$CONTAINER_NAME" --format '{{.State.Running}}' 2>/dev/null || true)"
- [ "$running" = "true" ]
-}
-
-wait_for_container_restart() {
- local expected_image
-
- if "$dry_run"; then
- log "DRY-RUN: would wait for $CONTAINER_NAME to run $latest_tag"
- return 0
- fi
-
- expected_image="$(latest_image_id)"
- [ -n "$expected_image" ] || fail "could not resolve image id for $latest_tag"
-
- wait_for_container_image "$expected_image" "new image" || fail "$CONTAINER_NAME did not restart on $latest_tag within ${DEPLOY_CONTAINER_START_TIMEOUT_SECONDS}s"
-}
-
-wait_for_container_image() {
- local expected_image="$1"
- local label="$2"
- local deadline
-
- deadline=$((SECONDS + DEPLOY_CONTAINER_START_TIMEOUT_SECONDS))
-
- while [ "$SECONDS" -lt "$deadline" ]; do
- if container_running && [ "$(container_image_id)" = "$expected_image" ]; then
- log "$CONTAINER_NAME is running the $label"
- return 0
- fi
-
- sleep 2
- done
-
- return 1
-}
-
-smoke_get() {
- local url="$1"
- curl -fsS --max-time 10 -o /dev/null "$url"
-}
-
-smoke_tiles() {
- local response
-
- response="$(
- curl -fsS --max-time 20 \
- -H "content-type: application/json" \
- --data '{"tiles":[{"kind":"status","stripId":"footer-status","id":"system-status"}]}' \
- "$TILE_BATCH_URL"
- )"
-
- [[ "$response" == *'"state":"ready"'* ]]
-}
-
-wait_for_smoke() {
- local deadline
-
- if "$dry_run"; then
- log "DRY-RUN: would smoke check $PUBLIC_URL and $TILE_BATCH_URL"
- return 0
- fi
-
- deadline=$((SECONDS + DEPLOY_SMOKE_TIMEOUT_SECONDS))
- until smoke_get "$PUBLIC_URL" && smoke_tiles; do
- if [ "$SECONDS" -ge "$deadline" ]; then
- fail "smoke checks failed for $PUBLIC_URL and $TILE_BATCH_URL"
- fi
-
- sleep 3
- done
-
- log "smoke checks passed"
-}
-
-rollback() {
- local rollback_image
-
- if [ "$deployment_started" != "true" ] || [ -z "$rollback_tag" ] || [ "$rollback_done" = "true" ]; then
- return 0
- fi
-
- rollback_done=true
- rollback_in_progress=true
- printf '[deploy:%s] rolling back to %s\n' "$APP_NAME" "$rollback_tag" >&2
- rollback_image="$("$container_cli" image inspect "$rollback_tag" --format '{{.Id}}' 2>/dev/null || true)"
- "$container_cli" tag "$rollback_tag" "$latest_tag" || true
- if container_running; then
- restart_service || true
- else
- printf '[deploy:%s] waiting for %s to recover with rollback image\n' "$APP_NAME" "$SERVICE_NAME" >&2
- fi
- if [ -n "$rollback_image" ] && wait_for_container_image "$rollback_image" "rollback image"; then
- printf '[deploy:%s] rollback image is running\n' "$APP_NAME" >&2
- else
- printf '[deploy:%s] ERROR: rollback image did not become healthy\n' "$APP_NAME" >&2
- fi
- rollback_in_progress=false
-}
-
-on_error() {
- local status=$?
- rollback
- exit "$status"
-}
-
-trap on_error ERR
-
-require_main_push
-select_container_cli
-validate_restart_strategy
-
-log "using container CLI: $container_cli"
-log "target image: $latest_tag"
-log "target service: $SERVICE_NAME"
-
-initialize_submodules
-tag_existing_latest_for_rollback
-build_image
-deployment_started=true
-restart_service
-wait_for_container_restart
-wait_for_smoke
-
-log "deployment finished"