@alepha/devtools 0.16.0 → 0.19.1

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 (236) 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 +349 -321
  5. package/dist/index.js +293 -186
  6. package/dist/index.js.map +1 -1
  7. package/package.json +30 -23
  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 +280 -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 +55 -59
  21. package/src/ui/components/DevLayout.tsx +104 -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 +482 -0
  29. package/src/ui/components/database/DatabaseEditor.page.tsx +23 -0
  30. package/src/ui/components/database/DatabaseEditor.tsx +399 -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/explorer/DevExplorer.tsx +351 -0
  36. package/src/ui/components/explorer/ExplorerTree.tsx +178 -0
  37. package/src/ui/components/explorer/panels/DevPanelAction.tsx +499 -0
  38. package/src/ui/components/explorer/panels/DevPanelCache.tsx +73 -0
  39. package/src/ui/components/explorer/panels/DevPanelPage.tsx +96 -0
  40. package/src/ui/components/explorer/panels/DevPanelQueue.tsx +51 -0
  41. package/src/ui/components/explorer/panels/DevPanelTopic.tsx +56 -0
  42. package/src/ui/components/explorer/panels/index.ts +5 -0
  43. package/src/ui/components/graph/DevDependencyGraph.tsx +35 -60
  44. package/src/ui/components/graph/GraphControls.tsx +10 -11
  45. package/src/ui/components/graph/NodeDetails.tsx +22 -29
  46. package/src/ui/components/graph/ProviderNode.tsx +4 -4
  47. package/src/ui/components/graph/helpers.ts +1 -1
  48. package/src/ui/components/logs/DevLogs.tsx +661 -0
  49. package/src/ui/components/logs/index.ts +1 -0
  50. package/src/ui/components/shared/TreeView.tsx +189 -0
  51. package/src/ui/main.css +17 -0
  52. package/src/ui/main.ts +2 -6
  53. package/LICENSE +0 -21
  54. package/assets/devtools/actions.html +0 -21
  55. package/assets/devtools/actions.html.br +0 -0
  56. package/assets/devtools/actions.html.gz +0 -0
  57. package/assets/devtools/asset.BJSLFcNT.css +0 -1
  58. package/assets/devtools/asset.BJSLFcNT.css.br +0 -0
  59. package/assets/devtools/asset.BJSLFcNT.css.gz +0 -0
  60. package/assets/devtools/asset.BZV40eAE.css +0 -1
  61. package/assets/devtools/asset.BZV40eAE.css.br +0 -0
  62. package/assets/devtools/asset.BZV40eAE.css.gz +0 -0
  63. package/assets/devtools/atoms.html +0 -21
  64. package/assets/devtools/atoms.html.br +0 -0
  65. package/assets/devtools/atoms.html.gz +0 -0
  66. package/assets/devtools/caches.html +0 -21
  67. package/assets/devtools/caches.html.br +0 -0
  68. package/assets/devtools/caches.html.gz +0 -0
  69. package/assets/devtools/chunk.1h5GuATm.js +0 -1
  70. package/assets/devtools/chunk.1h5GuATm.js.br +0 -0
  71. package/assets/devtools/chunk.1h5GuATm.js.gz +0 -0
  72. package/assets/devtools/chunk.3PgxxOdM.js +0 -1
  73. package/assets/devtools/chunk.3PgxxOdM.js.br +0 -0
  74. package/assets/devtools/chunk.3PgxxOdM.js.gz +0 -0
  75. package/assets/devtools/chunk.A_W3H6Aa.js +0 -1
  76. package/assets/devtools/chunk.A_W3H6Aa.js.br +0 -0
  77. package/assets/devtools/chunk.A_W3H6Aa.js.gz +0 -0
  78. package/assets/devtools/chunk.B5tL0VjH.js +0 -1
  79. package/assets/devtools/chunk.B5tL0VjH.js.br +0 -0
  80. package/assets/devtools/chunk.B5tL0VjH.js.gz +0 -0
  81. package/assets/devtools/chunk.B8p_Szro.js +0 -1
  82. package/assets/devtools/chunk.B8p_Szro.js.br +0 -0
  83. package/assets/devtools/chunk.B8p_Szro.js.gz +0 -0
  84. package/assets/devtools/chunk.BANy8c2v.js +0 -1
  85. package/assets/devtools/chunk.BANy8c2v.js.br +0 -0
  86. package/assets/devtools/chunk.BANy8c2v.js.gz +0 -0
  87. package/assets/devtools/chunk.BKph0hv1.js +0 -1
  88. package/assets/devtools/chunk.BKph0hv1.js.br +0 -0
  89. package/assets/devtools/chunk.BKph0hv1.js.gz +0 -0
  90. package/assets/devtools/chunk.BUs1kuwE.js +0 -1
  91. package/assets/devtools/chunk.BUs1kuwE.js.br +0 -0
  92. package/assets/devtools/chunk.BUs1kuwE.js.gz +0 -0
  93. package/assets/devtools/chunk.BVIEr21R.js +0 -1
  94. package/assets/devtools/chunk.BVIEr21R.js.br +0 -0
  95. package/assets/devtools/chunk.BVIEr21R.js.gz +0 -0
  96. package/assets/devtools/chunk.Bb3re2d8.js +0 -1
  97. package/assets/devtools/chunk.Bb3re2d8.js.br +0 -2
  98. package/assets/devtools/chunk.Bb3re2d8.js.gz +0 -0
  99. package/assets/devtools/chunk.BjFrJKj1.js +0 -1
  100. package/assets/devtools/chunk.BjFrJKj1.js.br +0 -2
  101. package/assets/devtools/chunk.BjFrJKj1.js.gz +0 -0
  102. package/assets/devtools/chunk.BkXzz14p.js +0 -1
  103. package/assets/devtools/chunk.BkXzz14p.js.br +0 -0
  104. package/assets/devtools/chunk.BkXzz14p.js.gz +0 -0
  105. package/assets/devtools/chunk.BlqFPyLh.js +0 -1
  106. package/assets/devtools/chunk.BlqFPyLh.js.br +0 -0
  107. package/assets/devtools/chunk.BlqFPyLh.js.gz +0 -0
  108. package/assets/devtools/chunk.BymZ9jU5.js +0 -1
  109. package/assets/devtools/chunk.BymZ9jU5.js.br +0 -0
  110. package/assets/devtools/chunk.BymZ9jU5.js.gz +0 -0
  111. package/assets/devtools/chunk.C0BD3Ujz.js +0 -1
  112. package/assets/devtools/chunk.C0BD3Ujz.js.br +0 -0
  113. package/assets/devtools/chunk.C0BD3Ujz.js.gz +0 -0
  114. package/assets/devtools/chunk.C63rzhbT.js +0 -1
  115. package/assets/devtools/chunk.C63rzhbT.js.br +0 -0
  116. package/assets/devtools/chunk.C63rzhbT.js.gz +0 -0
  117. package/assets/devtools/chunk.CJrYVzjN.js +0 -9
  118. package/assets/devtools/chunk.CJrYVzjN.js.br +0 -0
  119. package/assets/devtools/chunk.CJrYVzjN.js.gz +0 -0
  120. package/assets/devtools/chunk.CPGX3Xpx.js +0 -1
  121. package/assets/devtools/chunk.CPGX3Xpx.js.br +0 -1
  122. package/assets/devtools/chunk.CPGX3Xpx.js.gz +0 -0
  123. package/assets/devtools/chunk.Cf-3skUw.js +0 -1
  124. package/assets/devtools/chunk.Cf-3skUw.js.br +0 -0
  125. package/assets/devtools/chunk.Cf-3skUw.js.gz +0 -0
  126. package/assets/devtools/chunk.D7JLxcoJ.js +0 -7
  127. package/assets/devtools/chunk.D7JLxcoJ.js.br +0 -0
  128. package/assets/devtools/chunk.D7JLxcoJ.js.gz +0 -0
  129. package/assets/devtools/chunk.D7e5mBY4.js +0 -1
  130. package/assets/devtools/chunk.D7e5mBY4.js.br +0 -0
  131. package/assets/devtools/chunk.D7e5mBY4.js.gz +0 -0
  132. package/assets/devtools/chunk.DClU9Z1_.js +0 -1
  133. package/assets/devtools/chunk.DClU9Z1_.js.br +0 -0
  134. package/assets/devtools/chunk.DClU9Z1_.js.gz +0 -0
  135. package/assets/devtools/chunk.DE_M8b3Z.js +0 -1
  136. package/assets/devtools/chunk.DE_M8b3Z.js.br +0 -0
  137. package/assets/devtools/chunk.DE_M8b3Z.js.gz +0 -0
  138. package/assets/devtools/chunk.DWASJDBE.js +0 -1
  139. package/assets/devtools/chunk.DWASJDBE.js.br +0 -0
  140. package/assets/devtools/chunk.DWASJDBE.js.gz +0 -0
  141. package/assets/devtools/chunk.DfzRLqwW.js +0 -1
  142. package/assets/devtools/chunk.DfzRLqwW.js.br +0 -0
  143. package/assets/devtools/chunk.DfzRLqwW.js.gz +0 -0
  144. package/assets/devtools/chunk.Dww1YQtc.js +0 -1
  145. package/assets/devtools/chunk.Dww1YQtc.js.br +0 -0
  146. package/assets/devtools/chunk.Dww1YQtc.js.gz +0 -0
  147. package/assets/devtools/chunk.HFLdduaf.js +0 -1
  148. package/assets/devtools/chunk.HFLdduaf.js.br +0 -0
  149. package/assets/devtools/chunk.HFLdduaf.js.gz +0 -0
  150. package/assets/devtools/chunk.J-htqECs.js +0 -1
  151. package/assets/devtools/chunk.J-htqECs.js.br +0 -2
  152. package/assets/devtools/chunk.J-htqECs.js.gz +0 -0
  153. package/assets/devtools/chunk.JjTGVewZ.js +0 -2
  154. package/assets/devtools/chunk.JjTGVewZ.js.br +0 -0
  155. package/assets/devtools/chunk.JjTGVewZ.js.gz +0 -0
  156. package/assets/devtools/chunk.OV_89czZ.js +0 -1
  157. package/assets/devtools/chunk.OV_89czZ.js.br +0 -0
  158. package/assets/devtools/chunk.OV_89czZ.js.gz +0 -0
  159. package/assets/devtools/chunk.YFkMUqFM.js +0 -1
  160. package/assets/devtools/chunk.YFkMUqFM.js.br +0 -0
  161. package/assets/devtools/chunk.YFkMUqFM.js.gz +0 -0
  162. package/assets/devtools/chunk._KdUFIrt.js +0 -1
  163. package/assets/devtools/chunk._KdUFIrt.js.br +0 -0
  164. package/assets/devtools/chunk._KdUFIrt.js.gz +0 -0
  165. package/assets/devtools/chunk.pjP6xqG8.js +0 -1
  166. package/assets/devtools/chunk.pjP6xqG8.js.br +0 -0
  167. package/assets/devtools/chunk.pjP6xqG8.js.gz +0 -0
  168. package/assets/devtools/chunk.uTFtY0ae.js +0 -2
  169. package/assets/devtools/chunk.uTFtY0ae.js.br +0 -0
  170. package/assets/devtools/chunk.uTFtY0ae.js.gz +0 -0
  171. package/assets/devtools/chunk.uyVen0u2.js +0 -1
  172. package/assets/devtools/chunk.uyVen0u2.js.br +0 -0
  173. package/assets/devtools/chunk.uyVen0u2.js.gz +0 -0
  174. package/assets/devtools/chunk.vHjNjQS8.js +0 -1
  175. package/assets/devtools/chunk.vHjNjQS8.js.br +0 -0
  176. package/assets/devtools/chunk.vHjNjQS8.js.gz +0 -0
  177. package/assets/devtools/db.html +0 -21
  178. package/assets/devtools/db.html.br +0 -0
  179. package/assets/devtools/db.html.gz +0 -0
  180. package/assets/devtools/entry.DhzNl8q_.js +0 -79
  181. package/assets/devtools/entry.DhzNl8q_.js.br +0 -0
  182. package/assets/devtools/entry.DhzNl8q_.js.gz +0 -0
  183. package/assets/devtools/env.html +0 -21
  184. package/assets/devtools/env.html.br +0 -0
  185. package/assets/devtools/env.html.gz +0 -0
  186. package/assets/devtools/graph.html +0 -22
  187. package/assets/devtools/graph.html.br +0 -0
  188. package/assets/devtools/graph.html.gz +0 -0
  189. package/assets/devtools/index.html +0 -21
  190. package/assets/devtools/index.html.br +0 -0
  191. package/assets/devtools/index.html.gz +0 -0
  192. package/assets/devtools/logs.html +0 -21
  193. package/assets/devtools/logs.html.br +0 -0
  194. package/assets/devtools/logs.html.gz +0 -0
  195. package/assets/devtools/queues.html +0 -21
  196. package/assets/devtools/queues.html.br +0 -0
  197. package/assets/devtools/queues.html.gz +0 -0
  198. package/assets/devtools/topics.html +0 -21
  199. package/assets/devtools/topics.html.br +0 -0
  200. package/assets/devtools/topics.html.gz +0 -0
  201. package/src/api/DevToolsProvider.ts +0 -157
  202. package/src/api/providers/DevToolsDatabaseProvider.ts +0 -27
  203. package/src/api/repositories/LogRepository.ts +0 -8
  204. package/src/api/schemas/DevCommandMetadata.ts +0 -9
  205. package/src/ui/components/DevAtomsViewer.tsx +0 -637
  206. package/src/ui/components/DevCacheInspector.tsx +0 -423
  207. package/src/ui/components/DevDashboard.tsx +0 -38
  208. package/src/ui/components/DevEnvExplorer.tsx +0 -462
  209. package/src/ui/components/DevLogViewer.tsx +0 -252
  210. package/src/ui/components/DevQueueMonitor.tsx +0 -51
  211. package/src/ui/components/DevTopicsViewer.tsx +0 -686
  212. package/src/ui/components/actions/ActionGroup.tsx +0 -37
  213. package/src/ui/components/actions/ActionItem.tsx +0 -138
  214. package/src/ui/components/actions/DevActionsExplorer.tsx +0 -132
  215. package/src/ui/components/actions/MethodBadge.tsx +0 -18
  216. package/src/ui/components/actions/SchemaViewer.tsx +0 -21
  217. package/src/ui/components/actions/TryItPanel.tsx +0 -140
  218. package/src/ui/components/actions/constants.ts +0 -7
  219. package/src/ui/components/actions/helpers.ts +0 -18
  220. package/src/ui/components/actions/index.ts +0 -8
  221. package/src/ui/components/db/ColumnBadge.tsx +0 -55
  222. package/src/ui/components/db/DevDbStudio.tsx +0 -485
  223. package/src/ui/components/db/constants.ts +0 -11
  224. package/src/ui/components/db/index.ts +0 -4
  225. package/src/ui/components/db/types.ts +0 -7
  226. package/src/ui/styles.css +0 -1
  227. /package/src/{api/schemas → schemas}/DevAtomMetadata.ts +0 -0
  228. /package/src/{api/schemas → schemas}/DevBucketMetadata.ts +0 -0
  229. /package/src/{api/schemas → schemas}/DevCacheMetadata.ts +0 -0
  230. /package/src/{api/schemas → schemas}/DevEnvMetadata.ts +0 -0
  231. /package/src/{api/schemas → schemas}/DevModuleMetadata.ts +0 -0
  232. /package/src/{api/schemas → schemas}/DevProviderMetadata.ts +0 -0
  233. /package/src/{api/schemas → schemas}/DevQueueMetadata.ts +0 -0
  234. /package/src/{api/schemas → schemas}/DevRealmMetadata.ts +0 -0
  235. /package/src/{api/schemas → schemas}/DevRouteMetadata.ts +0 -0
  236. /package/src/{api/schemas → schemas}/DevSchedulerMetadata.ts +0 -0
@@ -1,462 +0,0 @@
1
- import { ui } from "@alepha/ui";
2
- import {
3
- Badge,
4
- Box,
5
- Code,
6
- CopyButton,
7
- Flex,
8
- ScrollArea,
9
- Stack,
10
- Table,
11
- Text,
12
- TextInput,
13
- Tooltip,
14
- } from "@mantine/core";
15
- import {
16
- IconCheck,
17
- IconCopy,
18
- IconKey,
19
- IconSearch,
20
- IconSettings,
21
- IconVariable,
22
- } from "@tabler/icons-react";
23
- import { useInject } from "alepha/react";
24
- import { HttpClient } from "alepha/server";
25
- import { useEffect, useMemo, useState } from "react";
26
- import type { DevEnvMetadata } from "../../api/schemas/DevEnvMetadata.ts";
27
- import { devMetadataSchema } from "../../api/schemas/DevMetadata.ts";
28
-
29
- interface EnvVariable {
30
- name: string;
31
- value: any;
32
- type: string;
33
- description?: string;
34
- format?: string;
35
- }
36
-
37
- const parseEnvVariables = (envs: DevEnvMetadata[]): EnvVariable[] => {
38
- const variableMap = new Map<string, EnvVariable>();
39
-
40
- for (const env of envs) {
41
- const schema = env.schema;
42
- if (!schema?.properties) continue;
43
-
44
- for (const [name, propSchema] of Object.entries(schema.properties)) {
45
- // Skip if already added (deduplication)
46
- if (variableMap.has(name)) continue;
47
-
48
- const prop = propSchema as any;
49
- const value = env.values[name];
50
-
51
- // Get the actual type from schema
52
- let type = prop.type ?? "unknown";
53
- let format = prop.format;
54
-
55
- // Handle union types (optional)
56
- if (prop.anyOf) {
57
- const nonNull = prop.anyOf.find((t: any) => t.type !== "null");
58
- if (nonNull) {
59
- type = nonNull.type ?? "unknown";
60
- format = nonNull.format;
61
- }
62
- }
63
-
64
- variableMap.set(name, {
65
- name,
66
- value,
67
- type,
68
- description: prop.description,
69
- format,
70
- });
71
- }
72
- }
73
-
74
- return Array.from(variableMap.values()).sort((a, b) =>
75
- a.name.localeCompare(b.name),
76
- );
77
- };
78
-
79
- const EnvSidebar = ({
80
- variables,
81
- selectedVar,
82
- onSelectVar,
83
- search,
84
- onSearchChange,
85
- }: {
86
- variables: EnvVariable[];
87
- selectedVar: EnvVariable | null;
88
- onSelectVar: (v: EnvVariable) => void;
89
- search: string;
90
- onSearchChange: (s: string) => void;
91
- }) => {
92
- return (
93
- <Stack gap={0} h="100%">
94
- <Box p="sm" style={{ borderBottom: `1px solid ${ui.colors.border}` }}>
95
- <TextInput
96
- placeholder="Search variables..."
97
- leftSection={<IconSearch size={14} />}
98
- size="xs"
99
- value={search}
100
- onChange={(e) => onSearchChange(e.target.value)}
101
- />
102
- </Box>
103
- <ScrollArea style={{ flex: 1 }}>
104
- <Box>
105
- <Text
106
- size="xs"
107
- fw={600}
108
- c="dimmed"
109
- px="sm"
110
- py="xs"
111
- style={{
112
- backgroundColor: ui.colors.background,
113
- borderBottom: `1px solid ${ui.colors.border}`,
114
- textTransform: "uppercase",
115
- letterSpacing: "0.05em",
116
- }}
117
- >
118
- Variables ({variables.length})
119
- </Text>
120
- {variables.map((v) => {
121
- const isSelected = selectedVar?.name === v.name;
122
- return (
123
- <Flex
124
- key={v.name}
125
- align="center"
126
- gap="xs"
127
- px="sm"
128
- py={6}
129
- onClick={() => onSelectVar(v)}
130
- style={{
131
- cursor: "pointer",
132
- backgroundColor: isSelected ? "#228be615" : undefined,
133
- borderLeft: isSelected
134
- ? "2px solid #228be6"
135
- : "2px solid transparent",
136
- borderBottom: `1px solid ${ui.colors.border}`,
137
- }}
138
- >
139
- <IconVariable size={14} opacity={0.5} />
140
- <Text size="sm" style={{ flex: 1 }} truncate ff="monospace">
141
- {v.name}
142
- </Text>
143
- </Flex>
144
- );
145
- })}
146
- </Box>
147
- </ScrollArea>
148
- </Stack>
149
- );
150
- };
151
-
152
- const EnvPanel = ({ variable }: { variable: EnvVariable }) => {
153
- const hasValue = variable.value !== undefined && variable.value !== "";
154
- const displayValue = hasValue ? String(variable.value) : "(not set)";
155
- const isSensitive =
156
- variable.name.toLowerCase().includes("secret") ||
157
- variable.name.toLowerCase().includes("password") ||
158
- variable.name.toLowerCase().includes("key") ||
159
- variable.name.toLowerCase().includes("token");
160
-
161
- return (
162
- <Flex direction="column" h="100%">
163
- {/* Header */}
164
- <Box
165
- px="md"
166
- py="sm"
167
- style={{
168
- borderBottom: `1px solid ${ui.colors.border}`,
169
- backgroundColor: "#228be608",
170
- }}
171
- >
172
- <Flex align="center" gap="sm">
173
- <IconVariable size={18} opacity={0.7} />
174
- <Text size="md" fw={600} ff="monospace">
175
- {variable.name}
176
- </Text>
177
- <Badge size="xs" variant="outline" color="blue">
178
- {variable.format || variable.type}
179
- </Badge>
180
- </Flex>
181
- </Box>
182
-
183
- <ScrollArea style={{ flex: 1 }} p="md">
184
- <Stack gap="lg">
185
- {/* Description */}
186
- {variable.description && (
187
- <Box>
188
- <Text size="sm" fw={600} mb="xs">
189
- Description
190
- </Text>
191
- <Text size="sm" c="dimmed">
192
- {variable.description}
193
- </Text>
194
- </Box>
195
- )}
196
-
197
- {/* Current Value */}
198
- <Box>
199
- <Text size="sm" fw={600} mb="xs">
200
- Current Value
201
- </Text>
202
- <Flex align="center" gap="sm">
203
- <Code
204
- block
205
- style={{
206
- flex: 1,
207
- filter: isSensitive && hasValue ? "blur(4px)" : undefined,
208
- transition: "filter 0.2s",
209
- }}
210
- onMouseEnter={(e) => {
211
- if (isSensitive) e.currentTarget.style.filter = "none";
212
- }}
213
- onMouseLeave={(e) => {
214
- if (isSensitive && hasValue)
215
- e.currentTarget.style.filter = "blur(4px)";
216
- }}
217
- >
218
- {displayValue}
219
- </Code>
220
- {hasValue && (
221
- <CopyButton value={String(variable.value)}>
222
- {({ copied, copy }) => (
223
- <Tooltip label={copied ? "Copied!" : "Copy value"}>
224
- <Box
225
- onClick={copy}
226
- style={{ cursor: "pointer" }}
227
- c={copied ? "teal" : "dimmed"}
228
- >
229
- {copied ? (
230
- <IconCheck size={16} />
231
- ) : (
232
- <IconCopy size={16} />
233
- )}
234
- </Box>
235
- </Tooltip>
236
- )}
237
- </CopyButton>
238
- )}
239
- </Flex>
240
- {isSensitive && (
241
- <Text size="xs" c="dimmed" mt="xs">
242
- Hover to reveal sensitive value
243
- </Text>
244
- )}
245
- </Box>
246
-
247
- {/* Schema Info */}
248
- <Box>
249
- <Text size="sm" fw={600} mb="xs">
250
- Schema
251
- </Text>
252
- <Table striped highlightOnHover withTableBorder>
253
- <Table.Tbody>
254
- <Table.Tr>
255
- <Table.Td w={120}>
256
- <Text size="sm" c="dimmed">
257
- Type
258
- </Text>
259
- </Table.Td>
260
- <Table.Td>
261
- <Text size="sm" ff="monospace">
262
- {variable.type}
263
- </Text>
264
- </Table.Td>
265
- </Table.Tr>
266
- {variable.format && (
267
- <Table.Tr>
268
- <Table.Td>
269
- <Text size="sm" c="dimmed">
270
- Format
271
- </Text>
272
- </Table.Td>
273
- <Table.Td>
274
- <Text size="sm" ff="monospace">
275
- {variable.format}
276
- </Text>
277
- </Table.Td>
278
- </Table.Tr>
279
- )}
280
- </Table.Tbody>
281
- </Table>
282
- </Box>
283
-
284
- {/* Usage hint */}
285
- <Box>
286
- <Text size="sm" fw={600} mb="xs">
287
- Usage
288
- </Text>
289
- <Code block>
290
- {`# Set in .env file or environment
291
- ${variable.name}=${hasValue ? variable.value : "<value>"}`}
292
- </Code>
293
- </Box>
294
- </Stack>
295
- </ScrollArea>
296
- </Flex>
297
- );
298
- };
299
-
300
- const EmptyState = () => (
301
- <Flex align="center" justify="center" h="100%" c="dimmed">
302
- <Stack align="center" gap="xs">
303
- <IconVariable size={48} opacity={0.3} />
304
- <Text size="sm">Select a variable to view details</Text>
305
- </Stack>
306
- </Flex>
307
- );
308
-
309
- const NoEnvsState = () => (
310
- <Flex align="center" justify="center" h="100%" c="dimmed">
311
- <Stack align="center" gap="xs">
312
- <IconSettings size={48} opacity={0.3} />
313
- <Text>No environment variables found</Text>
314
- <Text size="sm" c="dimmed">
315
- Use $env primitive to define expected environment variables
316
- </Text>
317
- </Stack>
318
- </Flex>
319
- );
320
-
321
- const AllEnvsTable = ({ variables }: { variables: EnvVariable[] }) => {
322
- return (
323
- <ScrollArea h="100%" p="md">
324
- <Stack gap="lg">
325
- <Box>
326
- <Flex align="center" gap="xs" mb="sm">
327
- <IconKey size={18} opacity={0.7} />
328
- <Text size="md" fw={600}>
329
- All Environment Variables
330
- </Text>
331
- <Badge size="sm" variant="light">
332
- {variables.length} variables
333
- </Badge>
334
- </Flex>
335
- <Table striped highlightOnHover withTableBorder>
336
- <Table.Thead>
337
- <Table.Tr>
338
- <Table.Th>Name</Table.Th>
339
- <Table.Th>Type</Table.Th>
340
- <Table.Th>Value</Table.Th>
341
- </Table.Tr>
342
- </Table.Thead>
343
- <Table.Tbody>
344
- {variables.map((v) => {
345
- const hasValue = v.value !== undefined && v.value !== "";
346
- const isSensitive =
347
- v.name.toLowerCase().includes("secret") ||
348
- v.name.toLowerCase().includes("password") ||
349
- v.name.toLowerCase().includes("key") ||
350
- v.name.toLowerCase().includes("token");
351
- return (
352
- <Table.Tr key={v.name}>
353
- <Table.Td>
354
- <Text size="sm" ff="monospace" fw={500}>
355
- {v.name}
356
- </Text>
357
- </Table.Td>
358
- <Table.Td>
359
- <Badge size="xs" variant="outline" color="blue">
360
- {v.format || v.type}
361
- </Badge>
362
- </Table.Td>
363
- <Table.Td>
364
- {hasValue ? (
365
- <Text
366
- size="sm"
367
- ff="monospace"
368
- c="dimmed"
369
- style={{
370
- filter: isSensitive ? "blur(4px)" : undefined,
371
- }}
372
- >
373
- {String(v.value).slice(0, 30)}
374
- {String(v.value).length > 30 ? "..." : ""}
375
- </Text>
376
- ) : (
377
- <Text size="sm" c="dimmed" fs="italic">
378
- (not set)
379
- </Text>
380
- )}
381
- </Table.Td>
382
- </Table.Tr>
383
- );
384
- })}
385
- </Table.Tbody>
386
- </Table>
387
- </Box>
388
- </Stack>
389
- </ScrollArea>
390
- );
391
- };
392
-
393
- export const DevEnvExplorer = () => {
394
- const http = useInject(HttpClient);
395
- const [envs, setEnvs] = useState<DevEnvMetadata[]>([]);
396
- const [loading, setLoading] = useState(true);
397
- const [selectedVar, setSelectedVar] = useState<EnvVariable | null>(null);
398
- const [search, setSearch] = useState("");
399
-
400
- useEffect(() => {
401
- http
402
- .fetch("/devtools/api/metadata", {
403
- schema: { response: devMetadataSchema },
404
- })
405
- .then((res) => {
406
- setEnvs(res.data.envs);
407
- setLoading(false);
408
- });
409
- }, []);
410
-
411
- const variables = useMemo(() => parseEnvVariables(envs), [envs]);
412
-
413
- const filteredVariables = useMemo(() => {
414
- if (!search) return variables;
415
- const searchLower = search.toLowerCase();
416
- return variables.filter((v) => v.name.toLowerCase().includes(searchLower));
417
- }, [variables, search]);
418
-
419
- if (loading) {
420
- return (
421
- <Flex align="center" justify="center" h="100%">
422
- <Text c="dimmed">Loading...</Text>
423
- </Flex>
424
- );
425
- }
426
-
427
- if (variables.length === 0) {
428
- return <NoEnvsState />;
429
- }
430
-
431
- return (
432
- <Flex h="100%" style={{ overflow: "hidden" }}>
433
- {/* Sidebar */}
434
- <Box
435
- w={280}
436
- style={{
437
- borderRight: `1px solid ${ui.colors.border}`,
438
- backgroundColor: ui.colors.surface,
439
- }}
440
- >
441
- <EnvSidebar
442
- variables={filteredVariables}
443
- selectedVar={selectedVar}
444
- onSelectVar={setSelectedVar}
445
- search={search}
446
- onSearchChange={setSearch}
447
- />
448
- </Box>
449
-
450
- {/* Main content */}
451
- <Box style={{ flex: 1, overflow: "hidden" }}>
452
- {selectedVar ? (
453
- <EnvPanel variable={selectedVar} />
454
- ) : (
455
- <AllEnvsTable variables={variables} />
456
- )}
457
- </Box>
458
- </Flex>
459
- );
460
- };
461
-
462
- export default DevEnvExplorer;
@@ -1,252 +0,0 @@
1
- import { ActionButton, DataTable, DialogService, Flex, Text } from "@alepha/ui";
2
- import { type Page, t } from "alepha";
3
- import { dayjs } from "alepha/datetime";
4
- import { type LogEntry, logEntrySchema } from "alepha/logger";
5
- import { useInject } from "alepha/react";
6
- import { useI18n } from "alepha/react/i18n";
7
- import { HttpClient } from "alepha/server";
8
- import { logs } from "../../api/entities/logs.ts";
9
-
10
- const DevLogViewer = () => {
11
- const http = useInject(HttpClient);
12
- const { l } = useI18n();
13
-
14
- const renderLevel = (level: string) => {
15
- switch (level.toLowerCase()) {
16
- case "error":
17
- return (
18
- <Text ff={"monospace"} c="red">
19
- {level.slice(0, 5)}
20
- </Text>
21
- );
22
- case "warn":
23
- return (
24
- <Text ff={"monospace"} c="orange">
25
- {level.slice(0, 5)}
26
- </Text>
27
- );
28
- case "info":
29
- return (
30
- <Text ff={"monospace"} c="green">
31
- {level.slice(0, 5)}
32
- </Text>
33
- );
34
- case "debug":
35
- return (
36
- <Text ff={"monospace"} c="grape">
37
- {level.slice(0, 5)}
38
- </Text>
39
- );
40
- case "trace":
41
- return (
42
- <Text ff={"monospace"} c="dimmed">
43
- {level.slice(0, 5)}
44
- </Text>
45
- );
46
- default:
47
- return <Text>{level}</Text>;
48
- }
49
- };
50
-
51
- const filters = t.object({
52
- search: t.optional(
53
- t.string({
54
- $control: {
55
- query: t.omit(logs.schema, ["id"]),
56
- },
57
- }),
58
- ),
59
- level: t.optional(
60
- t.array(
61
- t.enum(["TRACE", "DEBUG", "INFO", "WARN", "ERROR"], {
62
- $control: {},
63
- }),
64
- ),
65
- ),
66
- });
67
-
68
- return (
69
- <DataTable<LogEntry, typeof filters>
70
- submitOnInit
71
- infinityScroll
72
- submitEvery={[10, "seconds"]}
73
- defaultSize={20}
74
- typeFormProps={{
75
- skipSubmitButton: true,
76
- columns: 2,
77
- }}
78
- tableProps={{
79
- horizontalSpacing: "xs",
80
- verticalSpacing: 0,
81
- }}
82
- onFilterChange={(key, value, form) => {
83
- if (key === "search" || key === "level") {
84
- return form.submit();
85
- }
86
- }}
87
- filters={filters}
88
- tableTrProps={(item) => {
89
- if (item.level.toLowerCase() === "error") {
90
- return {
91
- bg: "rgba(255,0,0,0.1)",
92
- };
93
- }
94
- if (item.level.toLowerCase() === "warn") {
95
- return {
96
- bg: "rgba(255,153,0,0.1)",
97
- };
98
- }
99
- return {};
100
- }}
101
- items={async (filters, ctx) => {
102
- if (filters.search) {
103
- filters.search = filters.search.replace(
104
- "now()",
105
- new Date().toISOString(),
106
- );
107
- }
108
-
109
- if (filters.page && filters.page > 0) {
110
- const next = `timestamp < ${ctx.items[0].timestamp}`;
111
- if (filters.search) {
112
- filters.search += `& ${next}`;
113
- } else {
114
- filters.search = next;
115
- }
116
- }
117
-
118
- if (filters.level) {
119
- const levelFilter = filters.level
120
- .map((it) => `level = ${it}`)
121
- .join(" | ");
122
-
123
- if (filters.search) {
124
- filters.search += `& (${levelFilter})`;
125
- } else {
126
- filters.search = levelFilter;
127
- }
128
- }
129
-
130
- const queryParams = new URLSearchParams(
131
- filters as Record<string, any>,
132
- ).toString();
133
-
134
- const response = await http.fetch(`/devtools/api/logs?${queryParams}`, {
135
- schema: {
136
- response: t.page(logEntrySchema),
137
- },
138
- });
139
-
140
- return response.data as Page<LogEntry>;
141
- }}
142
- columns={{
143
- timestamp: {
144
- label: "timestamp",
145
- value: (item, { form }) => (
146
- <ActionButton
147
- intent={"none"}
148
- variant={"transparent"}
149
- h={20}
150
- size={"xs"}
151
- tooltip={l(item.timestamp, {
152
- date: "DD MMM YYYY HH:mm:ss.SSS",
153
- })}
154
- onClick={() => {
155
- const before = dayjs(item.timestamp).subtract(1, "minute");
156
- const after = dayjs(item.timestamp).add(1, "minute");
157
- form.input.search.set(
158
- `timestamp >= ${before.toISOString()} & timestamp <= ${after.toISOString()}`,
159
- );
160
- }}
161
- >
162
- {l(item.timestamp, {
163
- date: "HH:mm:ss.SSS",
164
- })}
165
- </ActionButton>
166
- ),
167
- },
168
- level: {
169
- label: "level",
170
- value: (item) => renderLevel(item.level),
171
- },
172
- app: {
173
- label: "app",
174
- value: (item) => item.app,
175
- },
176
- context: {
177
- fit: true,
178
- label: "context",
179
- value: (item, { form }) =>
180
- item.context && (
181
- <ActionButton
182
- intent={"none"}
183
- variant={"transparent"}
184
- h={20}
185
- size={"xs"}
186
- onClick={() => {
187
- form.input.search.set(`context = ${item.context}`);
188
- }}
189
- >
190
- <Text ff={"monospace"} size={"xs"}>
191
- {item.context.replaceAll("-", "").slice(0, 10)}
192
- </Text>
193
- </ActionButton>
194
- ),
195
- },
196
- service: {
197
- fit: true,
198
- label: "service",
199
- value: (item) => (
200
- <Flex align={"center"} justify={"end"}>
201
- {item.module && (
202
- <Text c="dimmed" size={"xs"}>
203
- {item.module}.
204
- </Text>
205
- )}
206
- <Text size={"sm"}>{item.service}</Text>
207
- </Flex>
208
- ),
209
- },
210
- message: {
211
- label: "message",
212
- value: (item) => {
213
- if (item.data?.ms && item.data.status) {
214
- return `${item.message} - ${item.data.status} [${item.data.ms}ms]`;
215
- }
216
- if (item.data?.path && item.data.method) {
217
- return `${item.message} - ${item.data.method} ${item.data.path}`;
218
- }
219
- return item.message;
220
- },
221
- },
222
- data: {
223
- label: "more",
224
- value: (item, { alepha }) => {
225
- if (!item.data) {
226
- return;
227
- }
228
-
229
- if (Object.keys(item.data).length === 0) {
230
- return;
231
- }
232
-
233
- return (
234
- <ActionButton
235
- opacity={0.5}
236
- h={20}
237
- px={4}
238
- size={"xs"}
239
- fw={"bold"}
240
- onClick={() => alepha.inject(DialogService).json(item.data)}
241
- >
242
- {"{ ... }"}
243
- </ActionButton>
244
- );
245
- },
246
- },
247
- }}
248
- />
249
- );
250
- };
251
-
252
- export default DevLogViewer;