@alepha/devtools 0.16.1 → 0.19.2

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 (238) hide show
  1. package/README.md +1 -5
  2. package/dist/index.browser.js +224 -0
  3. package/dist/index.browser.js.map +1 -0
  4. package/dist/index.d.ts +351 -323
  5. package/dist/index.js +350 -186
  6. package/dist/index.js.map +1 -1
  7. package/package.json +33 -25
  8. package/src/assets.ts +6 -0
  9. package/src/{api/entities → entities}/logs.ts +2 -2
  10. package/src/index.browser.ts +11 -0
  11. package/src/index.shared.ts +15 -0
  12. package/src/index.ts +11 -37
  13. package/src/{api/providers → providers}/DevToolsMetadataProvider.ts +84 -47
  14. package/src/providers/DevToolsProvider.ts +394 -0
  15. package/src/{api/schemas → schemas}/DevActionMetadata.ts +8 -0
  16. package/src/{api/schemas → schemas}/DevEntityMetadata.ts +3 -0
  17. package/src/{api/schemas → schemas}/DevMetadata.ts +13 -2
  18. package/src/{api/schemas → schemas}/DevPageMetadata.ts +3 -0
  19. package/src/{api/schemas → schemas}/DevTopicMetadata.ts +1 -0
  20. package/src/ui/AppRouter.tsx +73 -59
  21. package/src/ui/components/DevLayout.tsx +103 -84
  22. package/src/ui/components/configuration/ConfigAtoms.page.tsx +5 -0
  23. package/src/ui/components/configuration/ConfigAtoms.tsx +511 -0
  24. package/src/ui/components/configuration/ConfigEnv.page.tsx +5 -0
  25. package/src/ui/components/configuration/ConfigEnv.tsx +230 -0
  26. package/src/ui/components/configuration/DevConfiguration.tsx +36 -0
  27. package/src/ui/components/configuration/index.ts +3 -0
  28. package/src/ui/components/dashboard/DevDashboard.tsx +501 -0
  29. package/src/ui/components/database/DatabaseEditor.page.tsx +23 -0
  30. package/src/ui/components/database/DatabaseEditor.tsx +402 -0
  31. package/src/ui/components/database/DatabaseErd.page.tsx +28 -0
  32. package/src/ui/components/database/DatabaseErd.tsx +107 -0
  33. package/src/ui/components/database/DevDatabase.tsx +36 -0
  34. package/src/ui/components/database/EntityNode.tsx +83 -0
  35. package/src/ui/components/emails/DevEmails.tsx +250 -0
  36. package/src/ui/components/explorer/DevExplorer.tsx +351 -0
  37. package/src/ui/components/explorer/ExplorerTree.tsx +178 -0
  38. package/src/ui/components/explorer/panels/DevPanelAction.tsx +499 -0
  39. package/src/ui/components/explorer/panels/DevPanelCache.tsx +73 -0
  40. package/src/ui/components/explorer/panels/DevPanelPage.tsx +96 -0
  41. package/src/ui/components/explorer/panels/DevPanelQueue.tsx +51 -0
  42. package/src/ui/components/explorer/panels/DevPanelTopic.tsx +56 -0
  43. package/src/ui/components/explorer/panels/index.ts +5 -0
  44. package/src/ui/components/graph/DevDependencyGraph.tsx +35 -60
  45. package/src/ui/components/graph/GraphControls.tsx +10 -11
  46. package/src/ui/components/graph/NodeDetails.tsx +22 -29
  47. package/src/ui/components/graph/ProviderNode.tsx +4 -4
  48. package/src/ui/components/graph/helpers.ts +1 -1
  49. package/src/ui/components/logs/DevLogs.tsx +661 -0
  50. package/src/ui/components/logs/index.ts +1 -0
  51. package/src/ui/components/shared/TreeView.tsx +189 -0
  52. package/src/ui/components/sms/DevSms.tsx +225 -0
  53. package/src/ui/main.css +17 -0
  54. package/src/ui/main.ts +2 -6
  55. package/LICENSE +0 -21
  56. package/assets/devtools/actions.html +0 -21
  57. package/assets/devtools/actions.html.br +0 -0
  58. package/assets/devtools/actions.html.gz +0 -0
  59. package/assets/devtools/asset.BZV40eAE.css +0 -1
  60. package/assets/devtools/asset.BZV40eAE.css.br +0 -0
  61. package/assets/devtools/asset.BZV40eAE.css.gz +0 -0
  62. package/assets/devtools/asset.CBnMq2vO.css +0 -1
  63. package/assets/devtools/asset.CBnMq2vO.css.br +0 -0
  64. package/assets/devtools/asset.CBnMq2vO.css.gz +0 -0
  65. package/assets/devtools/atoms.html +0 -21
  66. package/assets/devtools/atoms.html.br +0 -0
  67. package/assets/devtools/atoms.html.gz +0 -0
  68. package/assets/devtools/caches.html +0 -21
  69. package/assets/devtools/caches.html.br +0 -0
  70. package/assets/devtools/caches.html.gz +0 -0
  71. package/assets/devtools/chunk.6INqNjF0.js +0 -1
  72. package/assets/devtools/chunk.6INqNjF0.js.br +0 -0
  73. package/assets/devtools/chunk.6INqNjF0.js.gz +0 -0
  74. package/assets/devtools/chunk.9vpWpXSF.js +0 -1
  75. package/assets/devtools/chunk.9vpWpXSF.js.br +0 -0
  76. package/assets/devtools/chunk.9vpWpXSF.js.gz +0 -0
  77. package/assets/devtools/chunk.B4peH6PS.js +0 -1
  78. package/assets/devtools/chunk.B4peH6PS.js.br +0 -0
  79. package/assets/devtools/chunk.B4peH6PS.js.gz +0 -0
  80. package/assets/devtools/chunk.B8CNjZzU.js +0 -1
  81. package/assets/devtools/chunk.B8CNjZzU.js.br +0 -0
  82. package/assets/devtools/chunk.B8CNjZzU.js.gz +0 -0
  83. package/assets/devtools/chunk.Bgd10SVI.js +0 -1
  84. package/assets/devtools/chunk.Bgd10SVI.js.br +0 -0
  85. package/assets/devtools/chunk.Bgd10SVI.js.gz +0 -0
  86. package/assets/devtools/chunk.BjFrJKj1.js +0 -1
  87. package/assets/devtools/chunk.BjFrJKj1.js.br +0 -2
  88. package/assets/devtools/chunk.BjFrJKj1.js.gz +0 -0
  89. package/assets/devtools/chunk.BlqFPyLh.js +0 -1
  90. package/assets/devtools/chunk.BlqFPyLh.js.br +0 -0
  91. package/assets/devtools/chunk.BlqFPyLh.js.gz +0 -0
  92. package/assets/devtools/chunk.BqBNmfN9.js +0 -1
  93. package/assets/devtools/chunk.BqBNmfN9.js.br +0 -0
  94. package/assets/devtools/chunk.BqBNmfN9.js.gz +0 -0
  95. package/assets/devtools/chunk.Bt0_vkJm.js +0 -2
  96. package/assets/devtools/chunk.Bt0_vkJm.js.br +0 -0
  97. package/assets/devtools/chunk.Bt0_vkJm.js.gz +0 -0
  98. package/assets/devtools/chunk.C3GuU4pz.js +0 -2
  99. package/assets/devtools/chunk.C3GuU4pz.js.br +0 -0
  100. package/assets/devtools/chunk.C3GuU4pz.js.gz +0 -0
  101. package/assets/devtools/chunk.CGwoN_Mo.js +0 -1
  102. package/assets/devtools/chunk.CGwoN_Mo.js.br +0 -0
  103. package/assets/devtools/chunk.CGwoN_Mo.js.gz +0 -0
  104. package/assets/devtools/chunk.CJCvhHA7.js +0 -1
  105. package/assets/devtools/chunk.CJCvhHA7.js.br +0 -2
  106. package/assets/devtools/chunk.CJCvhHA7.js.gz +0 -0
  107. package/assets/devtools/chunk.CKr2VE6v.js +0 -1
  108. package/assets/devtools/chunk.CKr2VE6v.js.br +0 -0
  109. package/assets/devtools/chunk.CKr2VE6v.js.gz +0 -0
  110. package/assets/devtools/chunk.CLvTwbkw.js +0 -1
  111. package/assets/devtools/chunk.CLvTwbkw.js.br +0 -0
  112. package/assets/devtools/chunk.CLvTwbkw.js.gz +0 -0
  113. package/assets/devtools/chunk.CR13dZhE.js +0 -7
  114. package/assets/devtools/chunk.CR13dZhE.js.br +0 -0
  115. package/assets/devtools/chunk.CR13dZhE.js.gz +0 -0
  116. package/assets/devtools/chunk.C_C-cVqs.js +0 -1
  117. package/assets/devtools/chunk.C_C-cVqs.js.br +0 -1
  118. package/assets/devtools/chunk.C_C-cVqs.js.gz +0 -0
  119. package/assets/devtools/chunk.CjevPbPy.js +0 -1
  120. package/assets/devtools/chunk.CjevPbPy.js.br +0 -0
  121. package/assets/devtools/chunk.CjevPbPy.js.gz +0 -0
  122. package/assets/devtools/chunk.CkNMZqAe.js +0 -1
  123. package/assets/devtools/chunk.CkNMZqAe.js.br +0 -0
  124. package/assets/devtools/chunk.CkNMZqAe.js.gz +0 -0
  125. package/assets/devtools/chunk.Cl1Mlnqx.js +0 -1
  126. package/assets/devtools/chunk.Cl1Mlnqx.js.br +0 -0
  127. package/assets/devtools/chunk.Cl1Mlnqx.js.gz +0 -0
  128. package/assets/devtools/chunk.CyY8OGdZ.js +0 -1
  129. package/assets/devtools/chunk.CyY8OGdZ.js.br +0 -0
  130. package/assets/devtools/chunk.CyY8OGdZ.js.gz +0 -0
  131. package/assets/devtools/chunk.Cyx9kLqD.js +0 -1
  132. package/assets/devtools/chunk.Cyx9kLqD.js.br +0 -0
  133. package/assets/devtools/chunk.Cyx9kLqD.js.gz +0 -0
  134. package/assets/devtools/chunk.D1MGgxUI.js +0 -1
  135. package/assets/devtools/chunk.D1MGgxUI.js.br +0 -0
  136. package/assets/devtools/chunk.D1MGgxUI.js.gz +0 -0
  137. package/assets/devtools/chunk.D5Ci-dwk.js +0 -1
  138. package/assets/devtools/chunk.D5Ci-dwk.js.br +0 -0
  139. package/assets/devtools/chunk.D5Ci-dwk.js.gz +0 -0
  140. package/assets/devtools/chunk.DFrWQW5x.js +0 -9
  141. package/assets/devtools/chunk.DFrWQW5x.js.br +0 -0
  142. package/assets/devtools/chunk.DFrWQW5x.js.gz +0 -0
  143. package/assets/devtools/chunk.DaVlli3f.js +0 -1
  144. package/assets/devtools/chunk.DaVlli3f.js.br +0 -0
  145. package/assets/devtools/chunk.DaVlli3f.js.gz +0 -0
  146. package/assets/devtools/chunk.DdyBCs50.js +0 -1
  147. package/assets/devtools/chunk.DdyBCs50.js.br +0 -0
  148. package/assets/devtools/chunk.DdyBCs50.js.gz +0 -0
  149. package/assets/devtools/chunk.Dl0THvrP.js +0 -1
  150. package/assets/devtools/chunk.Dl0THvrP.js.br +0 -0
  151. package/assets/devtools/chunk.Dl0THvrP.js.gz +0 -0
  152. package/assets/devtools/chunk.DwUNDm68.js +0 -1
  153. package/assets/devtools/chunk.DwUNDm68.js.br +0 -0
  154. package/assets/devtools/chunk.DwUNDm68.js.gz +0 -0
  155. package/assets/devtools/chunk.DzDkh4C6.js +0 -1
  156. package/assets/devtools/chunk.DzDkh4C6.js.br +0 -0
  157. package/assets/devtools/chunk.DzDkh4C6.js.gz +0 -0
  158. package/assets/devtools/chunk.QTExp4CY.js +0 -1
  159. package/assets/devtools/chunk.QTExp4CY.js.br +0 -0
  160. package/assets/devtools/chunk.QTExp4CY.js.gz +0 -0
  161. package/assets/devtools/chunk.ReCPcJln.js +0 -1
  162. package/assets/devtools/chunk.ReCPcJln.js.br +0 -0
  163. package/assets/devtools/chunk.ReCPcJln.js.gz +0 -0
  164. package/assets/devtools/chunk.UEhIKOMY.js +0 -1
  165. package/assets/devtools/chunk.UEhIKOMY.js.br +0 -0
  166. package/assets/devtools/chunk.UEhIKOMY.js.gz +0 -0
  167. package/assets/devtools/chunk.mWQqK3dU.js +0 -1
  168. package/assets/devtools/chunk.mWQqK3dU.js.br +0 -0
  169. package/assets/devtools/chunk.mWQqK3dU.js.gz +0 -0
  170. package/assets/devtools/chunk.uyVen0u2.js +0 -1
  171. package/assets/devtools/chunk.uyVen0u2.js.br +0 -0
  172. package/assets/devtools/chunk.uyVen0u2.js.gz +0 -0
  173. package/assets/devtools/chunk.yLRX_cUF.js +0 -1
  174. package/assets/devtools/chunk.yLRX_cUF.js.br +0 -0
  175. package/assets/devtools/chunk.yLRX_cUF.js.gz +0 -0
  176. package/assets/devtools/chunk.zuZxBYZg.js +0 -1
  177. package/assets/devtools/chunk.zuZxBYZg.js.br +0 -0
  178. package/assets/devtools/chunk.zuZxBYZg.js.gz +0 -0
  179. package/assets/devtools/db.html +0 -21
  180. package/assets/devtools/db.html.br +0 -0
  181. package/assets/devtools/db.html.gz +0 -0
  182. package/assets/devtools/entry.Cry3rxEI.js +0 -79
  183. package/assets/devtools/entry.Cry3rxEI.js.br +0 -0
  184. package/assets/devtools/entry.Cry3rxEI.js.gz +0 -0
  185. package/assets/devtools/env.html +0 -21
  186. package/assets/devtools/env.html.br +0 -0
  187. package/assets/devtools/env.html.gz +0 -0
  188. package/assets/devtools/graph.html +0 -22
  189. package/assets/devtools/graph.html.br +0 -0
  190. package/assets/devtools/graph.html.gz +0 -0
  191. package/assets/devtools/index.html +0 -21
  192. package/assets/devtools/index.html.br +0 -0
  193. package/assets/devtools/index.html.gz +0 -0
  194. package/assets/devtools/logs.html +0 -21
  195. package/assets/devtools/logs.html.br +0 -0
  196. package/assets/devtools/logs.html.gz +0 -0
  197. package/assets/devtools/queues.html +0 -21
  198. package/assets/devtools/queues.html.br +0 -0
  199. package/assets/devtools/queues.html.gz +0 -0
  200. package/assets/devtools/topics.html +0 -21
  201. package/assets/devtools/topics.html.br +0 -0
  202. package/assets/devtools/topics.html.gz +0 -0
  203. package/src/api/DevToolsProvider.ts +0 -157
  204. package/src/api/providers/DevToolsDatabaseProvider.ts +0 -27
  205. package/src/api/repositories/LogRepository.ts +0 -8
  206. package/src/api/schemas/DevCommandMetadata.ts +0 -9
  207. package/src/ui/components/DevAtomsViewer.tsx +0 -637
  208. package/src/ui/components/DevCacheInspector.tsx +0 -423
  209. package/src/ui/components/DevDashboard.tsx +0 -38
  210. package/src/ui/components/DevEnvExplorer.tsx +0 -462
  211. package/src/ui/components/DevLogViewer.tsx +0 -252
  212. package/src/ui/components/DevQueueMonitor.tsx +0 -51
  213. package/src/ui/components/DevTopicsViewer.tsx +0 -686
  214. package/src/ui/components/actions/ActionGroup.tsx +0 -37
  215. package/src/ui/components/actions/ActionItem.tsx +0 -138
  216. package/src/ui/components/actions/DevActionsExplorer.tsx +0 -132
  217. package/src/ui/components/actions/MethodBadge.tsx +0 -18
  218. package/src/ui/components/actions/SchemaViewer.tsx +0 -21
  219. package/src/ui/components/actions/TryItPanel.tsx +0 -140
  220. package/src/ui/components/actions/constants.ts +0 -7
  221. package/src/ui/components/actions/helpers.ts +0 -18
  222. package/src/ui/components/actions/index.ts +0 -8
  223. package/src/ui/components/db/ColumnBadge.tsx +0 -55
  224. package/src/ui/components/db/DevDbStudio.tsx +0 -485
  225. package/src/ui/components/db/constants.ts +0 -11
  226. package/src/ui/components/db/index.ts +0 -4
  227. package/src/ui/components/db/types.ts +0 -7
  228. package/src/ui/styles.css +0 -1
  229. /package/src/{api/schemas → schemas}/DevAtomMetadata.ts +0 -0
  230. /package/src/{api/schemas → schemas}/DevBucketMetadata.ts +0 -0
  231. /package/src/{api/schemas → schemas}/DevCacheMetadata.ts +0 -0
  232. /package/src/{api/schemas → schemas}/DevEnvMetadata.ts +0 -0
  233. /package/src/{api/schemas → schemas}/DevModuleMetadata.ts +0 -0
  234. /package/src/{api/schemas → schemas}/DevProviderMetadata.ts +0 -0
  235. /package/src/{api/schemas → schemas}/DevQueueMetadata.ts +0 -0
  236. /package/src/{api/schemas → schemas}/DevRealmMetadata.ts +0 -0
  237. /package/src/{api/schemas → schemas}/DevRouteMetadata.ts +0 -0
  238. /package/src/{api/schemas → schemas}/DevSchedulerMetadata.ts +0 -0
@@ -0,0 +1,501 @@
1
+ import { devMetadataSchema } from "@alepha/devtools";
2
+ import { ActionButton, Flex, ui } from "@alepha/ui";
3
+ import {
4
+ Badge,
5
+ Card,
6
+ Grid,
7
+ ScrollArea,
8
+ SimpleGrid,
9
+ Text,
10
+ Title,
11
+ } from "@mantine/core";
12
+ import {
13
+ IconApi,
14
+ IconAtom,
15
+ IconBrandNodejs,
16
+ IconBroadcast,
17
+ IconClock,
18
+ IconCpu,
19
+ IconDatabase,
20
+ IconFileText,
21
+ IconPlug,
22
+ IconRefresh,
23
+ IconRoute,
24
+ IconServer,
25
+ IconSettings,
26
+ IconStack2,
27
+ IconVariable,
28
+ } from "@tabler/icons-react";
29
+ import { useInject } from "alepha/react";
30
+ import { useRouter } from "alepha/react/router";
31
+ import { HttpClient } from "alepha/server";
32
+ import { useCallback, useEffect, useMemo, useState } from "react";
33
+
34
+ interface LogEntry {
35
+ level: string;
36
+ message: string;
37
+ module: string;
38
+ service: string;
39
+ data?: any;
40
+ timestamp: number;
41
+ }
42
+
43
+ const formatUptime = (seconds: number): string => {
44
+ if (seconds < 60) return `${Math.floor(seconds)}s`;
45
+ if (seconds < 3600)
46
+ return `${Math.floor(seconds / 60)}m ${Math.floor(seconds % 60)}s`;
47
+ const h = Math.floor(seconds / 3600);
48
+ const m = Math.floor((seconds % 3600) / 60);
49
+ return `${h}h ${m}m`;
50
+ };
51
+
52
+ const formatBytes = (bytes: number): string => {
53
+ if (bytes < 1024) return `${bytes} B`;
54
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
55
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
56
+ };
57
+
58
+ const levelColors: Record<string, string> = {
59
+ ERROR: "red",
60
+ WARN: "yellow",
61
+ INFO: "blue",
62
+ DEBUG: "gray",
63
+ TRACE: "dimmed",
64
+ };
65
+
66
+ const eventTypeColors: Record<string, string> = {
67
+ http: "teal",
68
+ db: "violet",
69
+ error: "red",
70
+ };
71
+
72
+ const detectEventType = (data: any): string | undefined => {
73
+ if (!data || typeof data !== "object") return undefined;
74
+ if (data.status && data.method && data.path && data.duration) return "http";
75
+ if (data.type === "db:query") return "db";
76
+ return undefined;
77
+ };
78
+
79
+ const formatEvent = (entry: LogEntry): string => {
80
+ const data = entry.data;
81
+ const eventType = detectEventType(data);
82
+ if (eventType === "http") {
83
+ return `${data.method} ${data.path} → ${data.status} (${data.duration}ms)`;
84
+ }
85
+ if (eventType === "db") {
86
+ return `${data.operation} (${Math.round(data.duration)}ms)`;
87
+ }
88
+ return entry.message;
89
+ };
90
+
91
+ export const DevDashboard = () => {
92
+ const http = useInject(HttpClient);
93
+ const router = useRouter();
94
+ const [metadata, setMetadata] = useState<any>(null);
95
+ const [logs, setLogs] = useState<LogEntry[]>([]);
96
+ const [events, setEvents] = useState<LogEntry[]>([]);
97
+ const [reloading, setReloading] = useState(false);
98
+
99
+ const fetchData = useCallback(async () => {
100
+ if (document.visibilityState !== "visible") return;
101
+ try {
102
+ const [metaRes, logsRes, eventsRes] = await Promise.all([
103
+ http.fetch("/__devtools/api/metadata", {
104
+ schema: { response: devMetadataSchema },
105
+ }),
106
+ http.fetch("/__devtools/api/logs?limit=10&level=DEBUG"),
107
+ http.fetch("/__devtools/api/logs?limit=20&type=http,db"),
108
+ ]);
109
+ setMetadata(metaRes.data);
110
+ setLogs((logsRes.data as any)?.logs ?? []);
111
+ setEvents((eventsRes.data as any)?.logs ?? []);
112
+ } catch {
113
+ // silently fail
114
+ }
115
+ }, [http]);
116
+
117
+ const handleReload = useCallback(async () => {
118
+ setReloading(true);
119
+ try {
120
+ await http.fetch("/__devtools/api/reload", { method: "POST" });
121
+ setTimeout(() => {
122
+ fetchData();
123
+ setReloading(false);
124
+ }, 1000);
125
+ } catch {
126
+ setReloading(false);
127
+ }
128
+ }, [http, fetchData]);
129
+
130
+ useEffect(() => {
131
+ fetchData();
132
+ const interval = setInterval(fetchData, 10_000);
133
+ return () => clearInterval(interval);
134
+ }, [fetchData]);
135
+
136
+ const system = metadata?.system;
137
+
138
+ const primitiveStats = useMemo(() => {
139
+ if (!metadata) return [];
140
+ return [
141
+ {
142
+ label: "Actions",
143
+ count: metadata.actions?.length ?? 0,
144
+ icon: IconApi,
145
+ color: "teal",
146
+ },
147
+ {
148
+ label: "Pages",
149
+ count: metadata.pages?.length ?? 0,
150
+ icon: IconFileText,
151
+ color: "blue",
152
+ },
153
+ {
154
+ label: "Entities",
155
+ count: metadata.entities?.length ?? 0,
156
+ icon: IconDatabase,
157
+ color: "violet",
158
+ },
159
+ {
160
+ label: "Queues",
161
+ count: metadata.queues?.length ?? 0,
162
+ icon: IconStack2,
163
+ color: "orange",
164
+ },
165
+ {
166
+ label: "Topics",
167
+ count: metadata.topics?.length ?? 0,
168
+ icon: IconBroadcast,
169
+ color: "pink",
170
+ },
171
+ {
172
+ label: "Caches",
173
+ count: metadata.caches?.length ?? 0,
174
+ icon: IconRoute,
175
+ color: "cyan",
176
+ },
177
+ {
178
+ label: "Providers",
179
+ count: metadata.providers?.length ?? 0,
180
+ icon: IconPlug,
181
+ color: "grape",
182
+ },
183
+ {
184
+ label: "Atoms",
185
+ count: metadata.atoms?.length ?? 0,
186
+ icon: IconAtom,
187
+ color: "lime",
188
+ },
189
+ {
190
+ label: "Env Vars",
191
+ count: metadata.envs?.length ?? 0,
192
+ icon: IconVariable,
193
+ color: "yellow",
194
+ },
195
+ ];
196
+ }, [metadata]);
197
+
198
+ return (
199
+ <Flex
200
+ p="lg"
201
+ w={"100%"}
202
+ flex={1}
203
+ direction="column"
204
+ style={{ overflow: "auto" }}
205
+ >
206
+ <Flex direction="column" gap="lg">
207
+ {/* App Stats */}
208
+ <div>
209
+ <Flex justify="space-between" align="center" mb="sm">
210
+ <Title order={5} c="dimmed" tt="uppercase" fz="xs" fw={600} lts={1}>
211
+ System
212
+ </Title>
213
+ <ActionButton
214
+ size="xs"
215
+ variant="subtle"
216
+ color="gray"
217
+ loading={reloading}
218
+ onClick={handleReload}
219
+ icon={<IconRefresh size={14} />}
220
+ >
221
+ Reload
222
+ </ActionButton>
223
+ </Flex>
224
+ <SimpleGrid cols={{ base: 2, sm: 3, md: 4, lg: 6 }} spacing="sm">
225
+ {system && (
226
+ <>
227
+ <StatCard
228
+ icon={IconServer}
229
+ label="Runtime"
230
+ value={system.runtime === "bun" ? "Bun" : "Node.js"}
231
+ />
232
+ <StatCard
233
+ icon={IconBrandNodejs}
234
+ label="Version"
235
+ value={system.nodeVersion}
236
+ />
237
+ <StatCard
238
+ icon={IconSettings}
239
+ label="Mode"
240
+ value={system.mode}
241
+ />
242
+ <StatCard
243
+ icon={IconRoute}
244
+ label="Port"
245
+ value={String(system.port)}
246
+ />
247
+ <StatCard
248
+ icon={IconClock}
249
+ label="Uptime"
250
+ value={formatUptime(system.uptime)}
251
+ />
252
+ <StatCard
253
+ icon={IconCpu}
254
+ label="Memory"
255
+ value={formatBytes(system.memoryUsage)}
256
+ />
257
+ </>
258
+ )}
259
+ </SimpleGrid>
260
+ </div>
261
+
262
+ <Grid gutter="lg">
263
+ {/* Recent Events */}
264
+ <Grid.Col span={{ base: 12, md: 7 }}>
265
+ <Card
266
+ padding="md"
267
+ radius="md"
268
+ style={{
269
+ background: ui.colors.surface,
270
+ border: `1px solid ${ui.colors.border}`,
271
+ }}
272
+ >
273
+ <Title
274
+ order={5}
275
+ mb="sm"
276
+ c="dimmed"
277
+ tt="uppercase"
278
+ fz="xs"
279
+ fw={600}
280
+ lts={1}
281
+ >
282
+ Recent Events
283
+ </Title>
284
+ <ScrollArea h={280}>
285
+ <Flex direction="column" gap={4}>
286
+ {events.length === 0 && (
287
+ <Text c="dimmed" fz="sm" ta="center" py="xl">
288
+ No events yet
289
+ </Text>
290
+ )}
291
+ {events.map((entry, i) => {
292
+ const eventType = detectEventType(entry.data) ?? "log";
293
+ return (
294
+ <Flex
295
+ key={`${entry.timestamp}-${i}`}
296
+ gap="xs"
297
+ px="xs"
298
+ py={4}
299
+ style={{
300
+ borderRadius: 4,
301
+ cursor: "pointer",
302
+ transition: "background 150ms",
303
+ }}
304
+ onMouseEnter={(e) => {
305
+ (e.currentTarget as HTMLElement).style.background =
306
+ `${ui.colors.elevated}`;
307
+ }}
308
+ onMouseLeave={(e) => {
309
+ (e.currentTarget as HTMLElement).style.background =
310
+ "transparent";
311
+ }}
312
+ >
313
+ <Text
314
+ fz="xs"
315
+ c="dimmed"
316
+ ff="monospace"
317
+ w={55}
318
+ style={{ flexShrink: 0 }}
319
+ >
320
+ {new Date(entry.timestamp).toLocaleTimeString("en", {
321
+ hour12: false,
322
+ })}
323
+ </Text>
324
+ <Badge
325
+ size="xs"
326
+ variant="light"
327
+ color={eventTypeColors[eventType] ?? "gray"}
328
+ style={{ flexShrink: 0 }}
329
+ >
330
+ {eventType === "http"
331
+ ? "HTTP"
332
+ : eventType === "db"
333
+ ? "DB"
334
+ : "LOG"}
335
+ </Badge>
336
+ <Text
337
+ fz="xs"
338
+ ff="monospace"
339
+ truncate
340
+ style={{ flex: 1 }}
341
+ >
342
+ {formatEvent(entry)}
343
+ </Text>
344
+ </Flex>
345
+ );
346
+ })}
347
+ </Flex>
348
+ </ScrollArea>
349
+ </Card>
350
+ </Grid.Col>
351
+
352
+ {/* Recent Logs */}
353
+ <Grid.Col span={{ base: 12, md: 5 }}>
354
+ <Card
355
+ padding="md"
356
+ radius="md"
357
+ style={{
358
+ background: ui.colors.surface,
359
+ border: `1px solid ${ui.colors.border}`,
360
+ }}
361
+ >
362
+ <Flex justify="space-between" mb="sm">
363
+ <Title
364
+ order={5}
365
+ c="dimmed"
366
+ tt="uppercase"
367
+ fz="xs"
368
+ fw={600}
369
+ lts={1}
370
+ >
371
+ Recent Logs
372
+ </Title>
373
+ <Text
374
+ fz="xs"
375
+ c="blue"
376
+ style={{ cursor: "pointer" }}
377
+ onClick={() => router.push("/logs")}
378
+ >
379
+ View all
380
+ </Text>
381
+ </Flex>
382
+ <ScrollArea h={280}>
383
+ <Flex direction="column" gap={4}>
384
+ {logs.length === 0 && (
385
+ <Text c="dimmed" fz="sm" ta="center" py="xl">
386
+ No logs yet
387
+ </Text>
388
+ )}
389
+ {logs.map((entry, i) => (
390
+ <Flex
391
+ key={`${entry.timestamp}-${i}`}
392
+ gap="xs"
393
+ px="xs"
394
+ py={4}
395
+ style={{ borderRadius: 4 }}
396
+ >
397
+ <Badge
398
+ size="xs"
399
+ variant="light"
400
+ color={levelColors[entry.level] ?? "gray"}
401
+ w={50}
402
+ style={{ flexShrink: 0 }}
403
+ >
404
+ {entry.level}
405
+ </Badge>
406
+ <Text
407
+ fz="xs"
408
+ c="dimmed"
409
+ w={80}
410
+ truncate
411
+ style={{ flexShrink: 0 }}
412
+ >
413
+ {entry.module}
414
+ </Text>
415
+ <Text fz="xs" ff="monospace" truncate style={{ flex: 1 }}>
416
+ {entry.message}
417
+ </Text>
418
+ </Flex>
419
+ ))}
420
+ </Flex>
421
+ </ScrollArea>
422
+ </Card>
423
+ </Grid.Col>
424
+ </Grid>
425
+
426
+ {/* Primitives Stats */}
427
+ <div>
428
+ <Title
429
+ order={5}
430
+ mb="sm"
431
+ c="dimmed"
432
+ tt="uppercase"
433
+ fz="xs"
434
+ fw={600}
435
+ lts={1}
436
+ >
437
+ Primitives
438
+ </Title>
439
+ <SimpleGrid cols={{ base: 2, sm: 3, md: 5 }} spacing="sm">
440
+ {primitiveStats.map((stat) => (
441
+ <Card
442
+ key={stat.label}
443
+ padding="sm"
444
+ radius="md"
445
+ style={{
446
+ background: ui.colors.surface,
447
+ border: `1px solid ${ui.colors.border}`,
448
+ }}
449
+ >
450
+ <Flex gap="sm">
451
+ <stat.icon size={18} opacity={0.5} />
452
+ <div style={{ flex: 1 }}>
453
+ <Text fz="xs" c="dimmed">
454
+ {stat.label}
455
+ </Text>
456
+ <Text fz="lg" fw={700} lh={1.2}>
457
+ {stat.count}
458
+ </Text>
459
+ </div>
460
+ </Flex>
461
+ </Card>
462
+ ))}
463
+ </SimpleGrid>
464
+ </div>
465
+ </Flex>
466
+ </Flex>
467
+ );
468
+ };
469
+
470
+ const StatCard = ({
471
+ icon: Icon,
472
+ label,
473
+ value,
474
+ }: {
475
+ icon: any;
476
+ label: string;
477
+ value: string;
478
+ }) => (
479
+ <Card
480
+ padding="sm"
481
+ radius="md"
482
+ style={{
483
+ background: ui.colors.surface,
484
+ border: `1px solid ${ui.colors.border}`,
485
+ }}
486
+ >
487
+ <Flex gap="xs">
488
+ <Icon size={16} opacity={0.4} />
489
+ <div>
490
+ <Text fz={10} c="dimmed" tt="uppercase" lts={0.5}>
491
+ {label}
492
+ </Text>
493
+ <Text fz="sm" fw={600} ff="monospace" lh={1.3}>
494
+ {value}
495
+ </Text>
496
+ </div>
497
+ </Flex>
498
+ </Card>
499
+ );
500
+
501
+ export default DevDashboard;
@@ -0,0 +1,23 @@
1
+ import { devMetadataSchema } from "@alepha/devtools";
2
+ import { useInject } from "alepha/react";
3
+ import { HttpClient } from "alepha/server";
4
+ import { useEffect, useState } from "react";
5
+ import { DatabaseEditor } from "./DatabaseEditor.tsx";
6
+
7
+ const DatabaseEditorPage = () => {
8
+ const http = useInject(HttpClient);
9
+ const [entities, setEntities] = useState<any[]>([]);
10
+
11
+ useEffect(() => {
12
+ http
13
+ .fetch("/__devtools/api/metadata", {
14
+ schema: { response: devMetadataSchema },
15
+ })
16
+ .then((res) => setEntities(res.data.entities ?? []))
17
+ .catch(() => {});
18
+ }, [http]);
19
+
20
+ return <DatabaseEditor entities={entities} />;
21
+ };
22
+
23
+ export default DatabaseEditorPage;