@geekmidas/studio 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/{DataBrowser-hGwiTffZ.d.cts → DataBrowser-B-jz8KBR.d.mts} +5 -2
  2. package/dist/DataBrowser-B-jz8KBR.d.mts.map +1 -0
  3. package/dist/{DataBrowser-SOcqmZb2.d.mts → DataBrowser-BTe9HWJy.d.cts} +5 -2
  4. package/dist/DataBrowser-BTe9HWJy.d.cts.map +1 -0
  5. package/dist/{DataBrowser-c-Gs6PZB.cjs → DataBrowser-D8c_pBf4.cjs} +4 -4
  6. package/dist/DataBrowser-D8c_pBf4.cjs.map +1 -0
  7. package/dist/{DataBrowser-DQ3-ZxdV.mjs → DataBrowser-kgcI9ApJ.mjs} +4 -4
  8. package/dist/DataBrowser-kgcI9ApJ.mjs.map +1 -0
  9. package/dist/Studio-CYzz3wD2.d.cts +152 -0
  10. package/dist/Studio-CYzz3wD2.d.cts.map +1 -0
  11. package/dist/Studio-D5yGscb8.d.mts +152 -0
  12. package/dist/Studio-D5yGscb8.d.mts.map +1 -0
  13. package/dist/data/index.cjs +1 -1
  14. package/dist/data/index.d.cts +1 -1
  15. package/dist/data/index.d.mts +1 -1
  16. package/dist/data/index.mjs +1 -1
  17. package/dist/index.cjs +33 -3
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +4 -131
  20. package/dist/index.d.mts +4 -131
  21. package/dist/index.mjs +33 -3
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/server/hono.cjs +168 -21
  24. package/dist/server/hono.cjs.map +1 -1
  25. package/dist/server/hono.d.cts +13 -2
  26. package/dist/server/hono.d.cts.map +1 -0
  27. package/dist/server/hono.d.mts +13 -2
  28. package/dist/server/hono.d.mts.map +1 -0
  29. package/dist/server/hono.mjs +168 -21
  30. package/dist/server/hono.mjs.map +1 -1
  31. package/dist/types-BZv87Ikv.mjs.map +1 -1
  32. package/dist/types-CMttUZYk.cjs.map +1 -1
  33. package/package.json +5 -5
  34. package/src/Studio.ts +341 -292
  35. package/src/__tests__/Studio.spec.ts +447 -0
  36. package/src/data/DataBrowser.ts +147 -143
  37. package/src/data/__tests__/DataBrowser.integration.spec.ts +404 -404
  38. package/src/data/__tests__/filtering.integration.spec.ts +726 -726
  39. package/src/data/__tests__/introspection.integration.spec.ts +340 -340
  40. package/src/data/__tests__/pagination.spec.ts +123 -0
  41. package/src/data/filtering.ts +154 -154
  42. package/src/data/introspection.ts +141 -141
  43. package/src/data/pagination.ts +15 -15
  44. package/src/index.ts +22 -24
  45. package/src/server/__tests__/hono.integration.spec.ts +605 -347
  46. package/src/server/hono.ts +392 -190
  47. package/src/types.ts +138 -138
  48. package/src/ui-assets.ts +10 -13
  49. package/tsconfig.json +9 -0
  50. package/tsdown.config.ts +9 -9
  51. package/ui/package.json +28 -22
  52. package/ui/src/App.tsx +95 -235
  53. package/ui/src/api.ts +184 -42
  54. package/ui/src/components/FilterPanel.tsx +198 -198
  55. package/ui/src/components/NavRail.tsx +183 -0
  56. package/ui/src/components/RowDetail.tsx +106 -106
  57. package/ui/src/components/StudioHeader.tsx +109 -0
  58. package/ui/src/components/TableList.tsx +49 -49
  59. package/ui/src/components/TableView.tsx +530 -485
  60. package/ui/src/main.tsx +3 -3
  61. package/ui/src/pages/DashboardPage.tsx +500 -0
  62. package/ui/src/pages/DatabasePage.tsx +226 -0
  63. package/ui/src/pages/EndpointDetailsPage.tsx +288 -0
  64. package/ui/src/pages/ExceptionsPage.tsx +268 -0
  65. package/ui/src/pages/LogsPage.tsx +228 -0
  66. package/ui/src/pages/MonitoringPage.tsx +46 -0
  67. package/ui/src/pages/PerformancePage.tsx +307 -0
  68. package/ui/src/pages/RequestsPage.tsx +379 -0
  69. package/ui/src/providers/StudioProvider.tsx +194 -0
  70. package/ui/src/styles.css +53 -142
  71. package/ui/src/types.ts +154 -30
  72. package/ui/tsconfig.tsbuildinfo +1 -1
  73. package/ui/vite.config.ts +6 -6
  74. package/dist/DataBrowser-DQ3-ZxdV.mjs.map +0 -1
  75. package/dist/DataBrowser-c-Gs6PZB.cjs.map +0 -1
@@ -0,0 +1,194 @@
1
+ import {
2
+ createContext,
3
+ type ReactNode,
4
+ useCallback,
5
+ useContext,
6
+ useEffect,
7
+ useState,
8
+ } from 'react';
9
+ import * as api from '../api';
10
+ import type {
11
+ ExceptionEntry,
12
+ LogEntry,
13
+ MetricsSnapshot,
14
+ RequestEntry,
15
+ StudioStats,
16
+ WebSocketMessage,
17
+ } from '../types';
18
+
19
+ interface StudioContextValue {
20
+ // Connection state
21
+ connected: boolean;
22
+
23
+ // Stats
24
+ stats: StudioStats | null;
25
+
26
+ // Data
27
+ requests: RequestEntry[];
28
+ logs: LogEntry[];
29
+ exceptions: ExceptionEntry[];
30
+
31
+ // Real-time metrics from WebSocket
32
+ realtimeMetrics: MetricsSnapshot | null;
33
+
34
+ // Loading state
35
+ loading: boolean;
36
+
37
+ // Actions
38
+ refresh: () => Promise<void>;
39
+ addRequest: (request: RequestEntry) => void;
40
+ addLog: (log: LogEntry) => void;
41
+ addException: (exception: ExceptionEntry) => void;
42
+ }
43
+
44
+ const StudioContext = createContext<StudioContextValue | null>(null);
45
+
46
+ export function useStudio() {
47
+ const context = useContext(StudioContext);
48
+ if (!context) {
49
+ throw new Error('useStudio must be used within a StudioProvider');
50
+ }
51
+ return context;
52
+ }
53
+
54
+ interface StudioProviderProps {
55
+ children: ReactNode;
56
+ }
57
+
58
+ export function StudioProvider({ children }: StudioProviderProps) {
59
+ const [connected, setConnected] = useState(false);
60
+ const [stats, setStats] = useState<StudioStats | null>(null);
61
+ const [requests, setRequests] = useState<RequestEntry[]>([]);
62
+ const [logs, setLogs] = useState<LogEntry[]>([]);
63
+ const [exceptions, setExceptions] = useState<ExceptionEntry[]>([]);
64
+ const [realtimeMetrics, setRealtimeMetrics] =
65
+ useState<MetricsSnapshot | null>(null);
66
+ const [loading, setLoading] = useState(true);
67
+
68
+ // Load initial data
69
+ const loadInitialData = useCallback(async () => {
70
+ try {
71
+ setLoading(true);
72
+ const [statsData, requestsData, exceptionsData, logsData] =
73
+ await Promise.all([
74
+ api.getStats(),
75
+ api.getRequests({ limit: 100 }),
76
+ api.getExceptions({ limit: 100 }),
77
+ api.getLogs({ limit: 100 }),
78
+ ]);
79
+
80
+ setStats(statsData);
81
+ setRequests(requestsData);
82
+ setExceptions(exceptionsData);
83
+ setLogs(logsData);
84
+ } catch (_error) {
85
+ } finally {
86
+ setLoading(false);
87
+ }
88
+ }, []);
89
+
90
+ // Refresh data
91
+ const refresh = useCallback(async () => {
92
+ await loadInitialData();
93
+ }, [loadInitialData]);
94
+
95
+ // Add new entries
96
+ const addRequest = useCallback((request: RequestEntry) => {
97
+ setRequests((prev) => [request, ...prev].slice(0, 100));
98
+ setStats((prev) =>
99
+ prev ? { ...prev, requests: prev.requests + 1 } : prev,
100
+ );
101
+ }, []);
102
+
103
+ const addLog = useCallback((log: LogEntry) => {
104
+ setLogs((prev) => [log, ...prev].slice(0, 100));
105
+ setStats((prev) => (prev ? { ...prev, logs: prev.logs + 1 } : prev));
106
+ }, []);
107
+
108
+ const addException = useCallback((exception: ExceptionEntry) => {
109
+ setExceptions((prev) => [exception, ...prev].slice(0, 100));
110
+ setStats((prev) =>
111
+ prev ? { ...prev, exceptions: prev.exceptions + 1 } : prev,
112
+ );
113
+ }, []);
114
+
115
+ // Load data on mount
116
+ useEffect(() => {
117
+ loadInitialData();
118
+ }, [loadInitialData]);
119
+
120
+ // WebSocket connection for real-time updates
121
+ useEffect(() => {
122
+ let ws: WebSocket | null = null;
123
+ let reconnectTimeout: ReturnType<typeof setTimeout>;
124
+
125
+ function connect() {
126
+ try {
127
+ ws = api.createWebSocket();
128
+
129
+ ws.onopen = () => {
130
+ setConnected(true);
131
+ };
132
+
133
+ ws.onclose = () => {
134
+ setConnected(false);
135
+ reconnectTimeout = setTimeout(connect, 3000);
136
+ };
137
+
138
+ ws.onerror = () => {
139
+ ws?.close();
140
+ };
141
+
142
+ ws.onmessage = (event) => {
143
+ try {
144
+ const message: WebSocketMessage = JSON.parse(event.data);
145
+
146
+ switch (message.type) {
147
+ case 'request':
148
+ addRequest(message.payload as RequestEntry);
149
+ break;
150
+ case 'exception':
151
+ addException(message.payload as ExceptionEntry);
152
+ break;
153
+ case 'log':
154
+ addLog(message.payload as LogEntry);
155
+ break;
156
+ case 'metrics':
157
+ setRealtimeMetrics(message.payload as MetricsSnapshot);
158
+ break;
159
+ }
160
+ } catch {
161
+ // Ignore parse errors
162
+ }
163
+ };
164
+ } catch {
165
+ reconnectTimeout = setTimeout(connect, 3000);
166
+ }
167
+ }
168
+
169
+ connect();
170
+
171
+ return () => {
172
+ clearTimeout(reconnectTimeout);
173
+ ws?.close();
174
+ };
175
+ }, [addRequest, addLog, addException]);
176
+
177
+ const value: StudioContextValue = {
178
+ connected,
179
+ stats,
180
+ requests,
181
+ logs,
182
+ exceptions,
183
+ realtimeMetrics,
184
+ loading,
185
+ refresh,
186
+ addRequest,
187
+ addLog,
188
+ addException,
189
+ };
190
+
191
+ return (
192
+ <StudioContext.Provider value={value}>{children}</StudioContext.Provider>
193
+ );
194
+ }
package/ui/src/styles.css CHANGED
@@ -1,194 +1,105 @@
1
1
  @import "tailwindcss";
2
+ @import "@geekmidas/ui/styles";
2
3
 
3
4
  @theme {
4
- /* Supabase-inspired dark theme */
5
- --color-studio-bg: #171717;
6
- --color-studio-surface: #1c1c1c;
7
- --color-studio-border: #2e2e2e;
8
- --color-studio-hover: #262626;
9
- --color-studio-active: #333333;
10
- --color-studio-accent: #3ecf8e;
11
- --color-studio-accent-hover: #4ade94;
12
-
13
- /* Legacy colors for compatibility */
14
- --color-bg-primary: #171717;
15
- --color-bg-secondary: #1c1c1c;
16
- --color-bg-tertiary: #262626;
17
- --color-border: #2e2e2e;
5
+ /* Dev Studio theme - extends @geekmidas/ui theme */
6
+ --color-studio-bg: var(--color-background);
7
+ --color-studio-surface: var(--color-surface);
8
+ --color-studio-border: var(--color-border);
9
+ --color-studio-hover: var(--color-surface-hover);
10
+ --color-studio-active: #333333;
11
+ --color-studio-accent: var(--color-accent);
12
+ --color-studio-accent-hover: #4ade94;
13
+
14
+ /* Legacy colors for compatibility */
15
+ --color-bg-primary: var(--color-background);
16
+ --color-bg-secondary: var(--color-surface);
17
+ --color-bg-tertiary: var(--color-surface-hover);
18
18
  }
19
19
 
20
20
  * {
21
- box-sizing: border-box;
21
+ box-sizing: border-box;
22
22
  }
23
23
 
24
24
  body {
25
- margin: 0;
26
- padding: 0;
27
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
28
- "Helvetica Neue", Arial, sans-serif;
29
- -webkit-font-smoothing: antialiased;
30
- -moz-osx-font-smoothing: grayscale;
31
- background: var(--color-studio-bg);
25
+ margin: 0;
26
+ padding: 0;
27
+ font-family:
28
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
29
+ Arial, sans-serif;
30
+ -webkit-font-smoothing: antialiased;
31
+ -moz-osx-font-smoothing: grayscale;
32
+ background: var(--color-background);
32
33
  }
33
34
 
34
35
  /* Custom scrollbar */
35
36
  ::-webkit-scrollbar {
36
- width: 8px;
37
- height: 8px;
37
+ width: 8px;
38
+ height: 8px;
38
39
  }
39
40
 
40
41
  ::-webkit-scrollbar-track {
41
- background: transparent;
42
+ background: transparent;
42
43
  }
43
44
 
44
45
  ::-webkit-scrollbar-thumb {
45
- background: #404040;
46
- border-radius: 4px;
46
+ background: #404040;
47
+ border-radius: 4px;
47
48
  }
48
49
 
49
50
  ::-webkit-scrollbar-thumb:hover {
50
- background: #525252;
51
+ background: #525252;
51
52
  }
52
53
 
53
54
  /* Table styles */
54
55
  .data-grid {
55
- border-collapse: collapse;
56
- width: 100%;
56
+ border-collapse: collapse;
57
+ width: 100%;
57
58
  }
58
59
 
59
60
  .data-grid th {
60
- position: sticky;
61
- top: 0;
62
- background: var(--color-studio-surface);
63
- z-index: 10;
61
+ position: sticky;
62
+ top: 0;
63
+ background: var(--color-surface);
64
+ z-index: 10;
64
65
  }
65
66
 
66
67
  .data-grid th,
67
68
  .data-grid td {
68
- border-bottom: 1px solid var(--color-studio-border);
69
- text-align: left;
69
+ border-bottom: 1px solid var(--color-border);
70
+ text-align: left;
70
71
  }
71
72
 
72
73
  .data-grid tbody tr:hover {
73
- background: var(--color-studio-hover);
74
+ background: var(--color-surface-hover);
74
75
  }
75
76
 
76
77
  /* Row number column */
77
78
  .row-number {
78
- color: #525252;
79
- font-size: 12px;
80
- font-variant-numeric: tabular-nums;
81
- user-select: none;
79
+ color: #525252;
80
+ font-size: 12px;
81
+ font-variant-numeric: tabular-nums;
82
+ user-select: none;
82
83
  }
83
84
 
84
85
  /* NULL value styling */
85
86
  .cell-null {
86
- color: #525252;
87
- font-style: italic;
87
+ color: #525252;
88
+ font-style: italic;
88
89
  }
89
90
 
90
91
  /* Filter panel animation */
91
92
  .filter-panel {
92
- animation: slideDown 0.15s ease-out;
93
+ animation: slideDown 0.15s ease-out;
93
94
  }
94
95
 
95
96
  @keyframes slideDown {
96
- from {
97
- opacity: 0;
98
- transform: translateY(-8px);
99
- }
100
- to {
101
- opacity: 1;
102
- transform: translateY(0);
103
- }
104
- }
105
-
106
- /* Button styles */
107
- .btn {
108
- display: inline-flex;
109
- align-items: center;
110
- gap: 0.5rem;
111
- padding: 0.375rem 0.75rem;
112
- font-size: 0.875rem;
113
- border-radius: 0.375rem;
114
- transition: all 0.15s ease;
115
- cursor: pointer;
116
- border: none;
117
- outline: none;
118
- }
119
-
120
- .btn-default {
121
- background: var(--color-studio-hover);
122
- color: #a3a3a3;
123
- }
124
-
125
- .btn-default:hover {
126
- background: var(--color-studio-active);
127
- color: #e5e5e5;
128
- }
129
-
130
- .btn-primary {
131
- background: var(--color-studio-accent);
132
- color: #000;
133
- }
134
-
135
- .btn-primary:hover {
136
- background: var(--color-studio-accent-hover);
137
- }
138
-
139
- /* Input styles */
140
- .input {
141
- background: var(--color-studio-bg);
142
- border: 1px solid var(--color-studio-border);
143
- border-radius: 0.375rem;
144
- padding: 0.375rem 0.75rem;
145
- font-size: 0.875rem;
146
- color: #e5e5e5;
147
- outline: none;
148
- transition: border-color 0.15s ease;
149
- }
150
-
151
- .input:focus {
152
- border-color: var(--color-studio-accent);
153
- }
154
-
155
- .input::placeholder {
156
- color: #525252;
157
- }
158
-
159
- /* Select styles */
160
- .select {
161
- background: var(--color-studio-bg);
162
- border: 1px solid var(--color-studio-border);
163
- border-radius: 0.375rem;
164
- padding: 0.375rem 0.75rem;
165
- font-size: 0.875rem;
166
- color: #e5e5e5;
167
- outline: none;
168
- cursor: pointer;
169
- appearance: none;
170
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%23525252'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E");
171
- background-repeat: no-repeat;
172
- background-position: right 0.5rem center;
173
- background-size: 1rem;
174
- padding-right: 2rem;
175
- }
176
-
177
- .select:focus {
178
- border-color: var(--color-studio-accent);
179
- }
180
-
181
- /* Badge */
182
- .badge {
183
- display: inline-flex;
184
- align-items: center;
185
- justify-content: center;
186
- min-width: 1.25rem;
187
- height: 1.25rem;
188
- padding: 0 0.375rem;
189
- font-size: 0.75rem;
190
- font-weight: 500;
191
- border-radius: 9999px;
192
- background: var(--color-studio-accent);
193
- color: #000;
97
+ from {
98
+ opacity: 0;
99
+ transform: translateY(-8px);
100
+ }
101
+ to {
102
+ opacity: 1;
103
+ transform: translateY(0);
104
+ }
194
105
  }
package/ui/src/types.ts CHANGED
@@ -1,50 +1,174 @@
1
+ // ============================================================================
2
+ // Database Types
3
+ // ============================================================================
4
+
1
5
  export interface ColumnInfo {
2
- name: string;
3
- type: string;
4
- rawType: string;
5
- nullable: boolean;
6
- isPrimaryKey: boolean;
7
- isForeignKey: boolean;
8
- foreignKeyTable?: string;
9
- foreignKeyColumn?: string;
10
- defaultValue?: string;
6
+ name: string;
7
+ type: string;
8
+ rawType: string;
9
+ nullable: boolean;
10
+ isPrimaryKey: boolean;
11
+ isForeignKey: boolean;
12
+ foreignKeyTable?: string;
13
+ foreignKeyColumn?: string;
14
+ defaultValue?: string;
11
15
  }
12
16
 
13
17
  export interface TableInfo {
14
- name: string;
15
- schema: string;
16
- columns: ColumnInfo[];
17
- primaryKey: string[];
18
- estimatedRowCount?: number;
18
+ name: string;
19
+ schema: string;
20
+ columns: ColumnInfo[];
21
+ primaryKey: string[];
22
+ estimatedRowCount?: number;
19
23
  }
20
24
 
21
25
  export interface TableSummary {
22
- name: string;
23
- schema: string;
24
- columnCount: number;
25
- primaryKey: string[];
26
- estimatedRowCount?: number;
26
+ name: string;
27
+ schema: string;
28
+ columnCount: number;
29
+ primaryKey: string[];
30
+ estimatedRowCount?: number;
27
31
  }
28
32
 
29
33
  export interface SchemaInfo {
30
- tables: TableInfo[];
31
- updatedAt: string;
34
+ tables: TableInfo[];
35
+ updatedAt: string;
32
36
  }
33
37
 
34
38
  export interface QueryResult {
35
- rows: Record<string, unknown>[];
36
- hasMore: boolean;
37
- nextCursor: string | null;
38
- prevCursor: string | null;
39
+ rows: Record<string, unknown>[];
40
+ hasMore: boolean;
41
+ nextCursor: string | null;
42
+ prevCursor: string | null;
39
43
  }
40
44
 
41
45
  export interface FilterConfig {
42
- column: string;
43
- operator: string;
44
- value: string;
46
+ column: string;
47
+ operator: string;
48
+ value: string;
45
49
  }
46
50
 
47
51
  export interface SortConfig {
48
- column: string;
49
- direction: 'asc' | 'desc';
52
+ column: string;
53
+ direction: 'asc' | 'desc';
54
+ }
55
+
56
+ // ============================================================================
57
+ // Monitoring Types (from Telescope)
58
+ // ============================================================================
59
+
60
+ export interface RequestEntry {
61
+ id: string;
62
+ method: string;
63
+ path: string;
64
+ status: number;
65
+ duration: number;
66
+ timestamp: string;
67
+ requestHeaders?: Record<string, string>;
68
+ requestBody?: unknown;
69
+ responseHeaders?: Record<string, string>;
70
+ responseBody?: unknown;
71
+ ip?: string;
72
+ userAgent?: string;
73
+ }
74
+
75
+ export interface ExceptionEntry {
76
+ id: string;
77
+ name: string;
78
+ message: string;
79
+ stack: string;
80
+ timestamp: string;
81
+ context?: Record<string, unknown>;
82
+ request?: {
83
+ method: string;
84
+ path: string;
85
+ };
86
+ }
87
+
88
+ export interface LogEntry {
89
+ id: string;
90
+ level: string;
91
+ message: string;
92
+ timestamp: string;
93
+ context?: Record<string, unknown>;
94
+ requestId?: string;
95
+ }
96
+
97
+ export interface StudioStats {
98
+ requests: number;
99
+ exceptions: number;
100
+ logs: number;
101
+ }
102
+
103
+ export type WebSocketMessage =
104
+ | { type: 'request'; payload: RequestEntry }
105
+ | { type: 'exception'; payload: ExceptionEntry }
106
+ | { type: 'log'; payload: LogEntry }
107
+ | { type: 'metrics'; payload: MetricsSnapshot };
108
+
109
+ // ============================================================================
110
+ // Metrics Types
111
+ // ============================================================================
112
+
113
+ export interface TimeSeriesPoint {
114
+ timestamp: number;
115
+ count: number;
116
+ avgDuration: number;
117
+ errorCount: number;
118
+ }
119
+
120
+ export interface RequestMetrics {
121
+ totalRequests: number;
122
+ avgDuration: number;
123
+ p50Duration: number;
124
+ p95Duration: number;
125
+ p99Duration: number;
126
+ errorRate: number;
127
+ successRate: number;
128
+ requestsPerSecond: number;
129
+ timeSeries: TimeSeriesPoint[];
130
+ }
131
+
132
+ export interface EndpointMetrics {
133
+ method: string;
134
+ path: string;
135
+ count: number;
136
+ avgDuration: number;
137
+ p95Duration: number;
138
+ errorRate: number;
139
+ lastSeen: number;
140
+ }
141
+
142
+ export interface EndpointDetails {
143
+ method: string;
144
+ path: string;
145
+ count: number;
146
+ avgDuration: number;
147
+ p50Duration: number;
148
+ p95Duration: number;
149
+ p99Duration: number;
150
+ errorRate: number;
151
+ successRate: number;
152
+ lastSeen: number;
153
+ statusDistribution: StatusDistribution;
154
+ timeSeries: TimeSeriesPoint[];
155
+ }
156
+
157
+ export interface StatusDistribution {
158
+ '2xx': number;
159
+ '3xx': number;
160
+ '4xx': number;
161
+ '5xx': number;
162
+ }
163
+
164
+ export interface MetricsSnapshot {
165
+ timestamp: number;
166
+ totalRequests: number;
167
+ requestsPerSecond: number;
168
+ avgDuration: number;
169
+ errorRate: number;
170
+ p50: number;
171
+ p95: number;
172
+ p99: number;
173
+ statusDistribution: StatusDistribution;
50
174
  }
@@ -1 +1 @@
1
- {"root":["./src/app.tsx","./src/api.ts","./src/main.tsx","./src/types.ts","./src/vite-env.d.ts","./src/components/filterpanel.tsx","./src/components/rowdetail.tsx","./src/components/tablelist.tsx","./src/components/tableview.tsx"],"version":"5.8.2"}
1
+ {"root":["./src/app.tsx","./src/api.ts","./src/main.tsx","./src/types.ts","./src/vite-env.d.ts","./src/components/filterpanel.tsx","./src/components/navrail.tsx","./src/components/rowdetail.tsx","./src/components/studioheader.tsx","./src/components/tablelist.tsx","./src/components/tableview.tsx","./src/pages/dashboardpage.tsx","./src/pages/databasepage.tsx","./src/pages/endpointdetailspage.tsx","./src/pages/exceptionspage.tsx","./src/pages/logspage.tsx","./src/pages/monitoringpage.tsx","./src/pages/performancepage.tsx","./src/pages/requestspage.tsx","./src/providers/studioprovider.tsx"],"version":"5.8.2"}
package/ui/vite.config.ts CHANGED
@@ -3,10 +3,10 @@ import react from '@vitejs/plugin-react';
3
3
  import { defineConfig } from 'vite';
4
4
 
5
5
  export default defineConfig({
6
- plugins: [react(), tailwindcss()],
7
- base: '/__studio/',
8
- build: {
9
- outDir: '../dist/ui',
10
- emptyOutDir: true,
11
- },
6
+ plugins: [react(), tailwindcss()],
7
+ base: '/__studio/',
8
+ build: {
9
+ outDir: '../dist/ui',
10
+ emptyOutDir: true,
11
+ },
12
12
  });