MCPcopy
hub / github.com/opentrace/opentrace / ServerGraphStore

Class ServerGraphStore

ui/src/store/serverStore.ts:71–366  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

69}
70
71export class ServerGraphStore implements GraphStore {
72 private readonly baseUrl: string;
73 private _hasData = false;
74
75 // Visualization caps threaded into /api/graph as ?maxNodes=&maxEdges=.
76 // Defaults match LadybugStore and SettingsDrawer's DEFAULT_MAX_*. The
77 // agent caps the response server-side (see serve.py:fetch_graph).
78 private maxVisNodes = 20000;
79 private maxVisEdges = 20000;
80
81 constructor(baseUrl: string) {
82 // Strip trailing slash for consistent URL building
83 this.baseUrl = baseUrl.replace(/\/+$/, '');
84
85 // Apply persisted visualization limits so the first fetch uses them
86 // without waiting for the user to open Settings and click Update.
87 // Mirrors LadybugStore's behavior in ensureReady().
88 try {
89 const savedNodes = localStorage.getItem('ot:maxVisNodes');
90 const savedEdges = localStorage.getItem('ot:maxVisEdges');
91 const n = savedNodes != null ? Number(savedNodes) : NaN;
92 const e = savedEdges != null ? Number(savedEdges) : NaN;
93 // Integers only — the /api/graph contract rejects non-integer caps,
94 // so a persisted decimal would make every fetch 400.
95 if (Number.isInteger(n) && n > 0) this.maxVisNodes = n;
96 if (Number.isInteger(e) && e > 0) this.maxVisEdges = e;
97 } catch {
98 /* localStorage unavailable (SSR, restricted browser) — keep defaults */
99 }
100 }
101
102 // ---- Helpers --------------------------------------------------------
103
104 private async get<T>(
105 path: string,
106 params?: Record<string, string>,
107 ): Promise<T> {
108 const base = this.baseUrl.endsWith('/') ? this.baseUrl : this.baseUrl + '/';
109 const url = new URL(path.startsWith('/') ? path.slice(1) : path, base);
110 if (params) {
111 for (const [k, v] of Object.entries(params)) {
112 if (v !== undefined && v !== '') url.searchParams.set(k, v);
113 }
114 }
115 const res = await fetch(url.toString());
116 if (!res.ok) {
117 const body = await res.text().catch(() => '');
118 throw new Error(`Server error ${res.status}: ${body}`);
119 }
120 return res.json() as Promise<T>;
121 }
122
123 private async post<T>(path: string, body: unknown): Promise<T> {
124 const base = this.baseUrl.endsWith('/') ? this.baseUrl : this.baseUrl + '/';
125 const url = new URL(path.startsWith('/') ? path.slice(1) : path, base);
126 const res = await fetch(url.toString(), {
127 method: 'POST',
128 headers: { 'Content-Type': 'application/json' },

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected