@alepha/devtools 0.13.5 → 0.13.7

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 (62) hide show
  1. package/dist/{index.d.mts → index.d.ts} +250 -32
  2. package/dist/{index.mjs → index.js} +254 -22
  3. package/dist/index.js.map +1 -0
  4. package/package.json +12 -6
  5. package/src/{DevToolsProvider.ts → api/DevToolsProvider.ts} +29 -1
  6. package/src/{providers → api/providers}/DevToolsMetadataProvider.ts +210 -2
  7. package/src/api/schemas/DevAtomMetadata.ts +26 -0
  8. package/src/api/schemas/DevCommandMetadata.ts +9 -0
  9. package/src/api/schemas/DevEntityMetadata.ts +57 -0
  10. package/src/api/schemas/DevEnvMetadata.ts +22 -0
  11. package/src/{schemas → api/schemas}/DevMetadata.ts +10 -1
  12. package/src/api/schemas/DevRouteMetadata.ts +8 -0
  13. package/src/index.ts +23 -16
  14. package/src/ui/AppRouter.tsx +85 -2
  15. package/src/ui/components/DevAtomsViewer.tsx +636 -0
  16. package/src/ui/components/DevCacheInspector.tsx +423 -0
  17. package/src/ui/components/DevDashboard.tsx +188 -0
  18. package/src/ui/components/DevEnvExplorer.tsx +462 -0
  19. package/src/ui/components/DevLayout.tsx +65 -4
  20. package/src/ui/components/DevLogViewer.tsx +161 -163
  21. package/src/ui/components/DevQueueMonitor.tsx +51 -0
  22. package/src/ui/components/DevTopicsViewer.tsx +690 -0
  23. package/src/ui/components/actions/ActionGroup.tsx +37 -0
  24. package/src/ui/components/actions/ActionItem.tsx +138 -0
  25. package/src/ui/components/actions/DevActionsExplorer.tsx +132 -0
  26. package/src/ui/components/actions/MethodBadge.tsx +18 -0
  27. package/src/ui/components/actions/SchemaViewer.tsx +21 -0
  28. package/src/ui/components/actions/TryItPanel.tsx +140 -0
  29. package/src/ui/components/actions/constants.ts +7 -0
  30. package/src/ui/components/actions/helpers.ts +18 -0
  31. package/src/ui/components/actions/index.ts +8 -0
  32. package/src/ui/components/db/ColumnBadge.tsx +55 -0
  33. package/src/ui/components/db/DevDbStudio.tsx +485 -0
  34. package/src/ui/components/db/constants.ts +11 -0
  35. package/src/ui/components/db/index.ts +4 -0
  36. package/src/ui/components/db/types.ts +7 -0
  37. package/src/ui/components/graph/DevDependencyGraph.tsx +358 -0
  38. package/src/ui/components/graph/GraphControls.tsx +162 -0
  39. package/src/ui/components/graph/NodeDetails.tsx +181 -0
  40. package/src/ui/components/graph/ProviderNode.tsx +97 -0
  41. package/src/ui/components/graph/constants.ts +35 -0
  42. package/src/ui/components/graph/helpers.ts +443 -0
  43. package/src/ui/components/graph/index.ts +7 -0
  44. package/src/ui/components/graph/types.ts +28 -0
  45. package/src/ui/styles.css +0 -6
  46. package/src/ui/resources/wotfardregular/stylesheet.css +0 -12
  47. package/src/ui/resources/wotfardregular/wotfard-regular-webfont.eot +0 -0
  48. package/src/ui/resources/wotfardregular/wotfard-regular-webfont.ttf +0 -0
  49. package/src/ui/resources/wotfardregular/wotfard-regular-webfont.woff2 +0 -0
  50. /package/src/{entities → api/entities}/logs.ts +0 -0
  51. /package/src/{providers → api/providers}/DevToolsDatabaseProvider.ts +0 -0
  52. /package/src/{repositories → api/repositories}/LogRepository.ts +0 -0
  53. /package/src/{schemas → api/schemas}/DevActionMetadata.ts +0 -0
  54. /package/src/{schemas → api/schemas}/DevBucketMetadata.ts +0 -0
  55. /package/src/{schemas → api/schemas}/DevCacheMetadata.ts +0 -0
  56. /package/src/{schemas → api/schemas}/DevModuleMetadata.ts +0 -0
  57. /package/src/{schemas → api/schemas}/DevPageMetadata.ts +0 -0
  58. /package/src/{schemas → api/schemas}/DevProviderMetadata.ts +0 -0
  59. /package/src/{schemas → api/schemas}/DevQueueMetadata.ts +0 -0
  60. /package/src/{schemas → api/schemas}/DevRealmMetadata.ts +0 -0
  61. /package/src/{schemas → api/schemas}/DevSchedulerMetadata.ts +0 -0
  62. /package/src/{schemas → api/schemas}/DevTopicMetadata.ts +0 -0
@@ -5,7 +5,7 @@ import { type Page, t } from "alepha";
5
5
  import { dayjs } from "alepha/datetime";
6
6
  import { type LogEntry, logEntrySchema } from "alepha/logger";
7
7
  import { HttpClient } from "alepha/server";
8
- import { logs } from "../../entities/logs.ts";
8
+ import { logs } from "../../api/entities/logs.ts";
9
9
 
10
10
  const DevLogViewer = () => {
11
11
  const http = useInject(HttpClient);
@@ -66,188 +66,186 @@ const DevLogViewer = () => {
66
66
  });
67
67
 
68
68
  return (
69
- <Flex flex={1}>
70
- <DataTable<LogEntry, typeof filters>
71
- submitOnInit
72
- infinityScroll
73
- submitEvery={[10, "seconds"]}
74
- defaultSize={20}
75
- typeFormProps={{
76
- skipSubmitButton: true,
77
- columns: 2,
78
- }}
79
- tableProps={{
80
- horizontalSpacing: "xs",
81
- verticalSpacing: 0,
82
- }}
83
- onFilterChange={(key, value, form) => {
84
- if (key === "search" || key === "level") {
85
- return form.submit();
86
- }
87
- }}
88
- filters={filters}
89
- tableTrProps={(item) => {
90
- if (item.level.toLowerCase() === "error") {
91
- return {
92
- bg: "rgba(255,0,0,0.1)",
93
- };
94
- }
95
- if (item.level.toLowerCase() === "warn") {
96
- return {
97
- bg: "rgba(255,153,0,0.1)",
98
- };
99
- }
100
- return {};
101
- }}
102
- items={async (filters, ctx) => {
103
- if (filters.search) {
104
- filters.search = filters.search.replace(
105
- "now()",
106
- new Date().toISOString(),
107
- );
108
- }
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
+ }
109
108
 
110
- if (filters.page && filters.page > 0) {
111
- const next = `timestamp < ${ctx.items[0].timestamp}`;
112
- if (filters.search) {
113
- filters.search += `& ${next}`;
114
- } else {
115
- filters.search = next;
116
- }
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;
117
115
  }
116
+ }
118
117
 
119
- if (filters.level) {
120
- const levelFilter = filters.level
121
- .map((it) => `level = ${it}`)
122
- .join(" | ");
118
+ if (filters.level) {
119
+ const levelFilter = filters.level
120
+ .map((it) => `level = ${it}`)
121
+ .join(" | ");
123
122
 
124
- if (filters.search) {
125
- filters.search += `& (${levelFilter})`;
126
- } else {
127
- filters.search = levelFilter;
128
- }
123
+ if (filters.search) {
124
+ filters.search += `& (${levelFilter})`;
125
+ } else {
126
+ filters.search = levelFilter;
129
127
  }
128
+ }
130
129
 
131
- const queryParams = new URLSearchParams(
132
- filters as Record<string, any>,
133
- ).toString();
130
+ const queryParams = new URLSearchParams(
131
+ filters as Record<string, any>,
132
+ ).toString();
134
133
 
135
- const response = await http.fetch(
136
- `/devtools/api/logs?${queryParams}`,
137
- {
138
- schema: {
139
- response: t.page(logEntrySchema),
140
- },
141
- },
142
- );
134
+ const response = await http.fetch(`/devtools/api/logs?${queryParams}`, {
135
+ schema: {
136
+ response: t.page(logEntrySchema),
137
+ },
138
+ });
143
139
 
144
- return response.data as Page<LogEntry>;
145
- }}
146
- columns={{
147
- timestamp: {
148
- label: "timestamp",
149
- value: (item, { form }) => (
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 && (
150
181
  <ActionButton
182
+ intent={"none"}
183
+ variant={"transparent"}
151
184
  h={20}
152
- c={"dimmed"}
153
185
  size={"xs"}
154
- tooltip={l(item.timestamp, {
155
- date: "DD MMM YYYY HH:mm:ss.SSS",
156
- })}
157
186
  onClick={() => {
158
- const before = dayjs(item.timestamp).subtract(1, "minute");
159
- const after = dayjs(item.timestamp).add(1, "minute");
160
- form.input.search.set(
161
- `timestamp >= ${before.toISOString()} & timestamp <= ${after.toISOString()}`,
162
- );
187
+ form.input.search.set(`context = ${item.context}`);
163
188
  }}
164
189
  >
165
- {l(item.timestamp, {
166
- date: "HH:mm:ss.SSS",
167
- })}
190
+ <Text ff={"monospace"} size={"xs"}>
191
+ {item.context.replaceAll("-", "").slice(0, 10)}
192
+ </Text>
168
193
  </ActionButton>
169
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;
170
220
  },
171
- level: {
172
- label: "level",
173
- value: (item) => renderLevel(item.level),
174
- },
175
- app: {
176
- label: "app",
177
- value: (item) => item.app,
178
- },
179
- context: {
180
- fit: true,
181
- label: "context",
182
- value: (item, { form }) =>
183
- item.context && (
184
- <ActionButton
185
- h={20}
186
- size={"xs"}
187
- onClick={() => {
188
- form.input.search.set(`context = ${item.context}`);
189
- }}
190
- >
191
- <Text ff={"monospace"} size={"xs"} c="dimmed">
192
- {item.context.replaceAll("-", "").slice(0, 10)}
193
- </Text>
194
- </ActionButton>
195
- ),
196
- },
197
- service: {
198
- fit: true,
199
- label: "service",
200
- value: (item) => (
201
- <Flex align={"center"} justify={"end"}>
202
- {item.module && (
203
- <Text c="dimmed" size={"xs"}>
204
- {item.module}.
205
- </Text>
206
- )}
207
- <Text size={"sm"}>{item.service}</Text>
208
- </Flex>
209
- ),
210
- },
211
- message: {
212
- label: "message",
213
- value: (item) => {
214
- if (item.data?.ms && item.data.status) {
215
- return `${item.message} - ${item.data.status} [${item.data.ms}ms]`;
216
- }
217
- if (item.data?.path && item.data.method) {
218
- return `${item.message} - ${item.data.method} ${item.data.path}`;
219
- }
220
- return item.message;
221
- },
222
- },
223
- data: {
224
- label: "more",
225
- value: (item, { alepha }) => {
226
- if (!item.data) {
227
- return;
228
- }
221
+ },
222
+ data: {
223
+ label: "more",
224
+ value: (item, { alepha }) => {
225
+ if (!item.data) {
226
+ return;
227
+ }
229
228
 
230
- if (Object.keys(item.data).length === 0) {
231
- return;
232
- }
229
+ if (Object.keys(item.data).length === 0) {
230
+ return;
231
+ }
233
232
 
234
- return (
235
- <ActionButton
236
- opacity={0.5}
237
- h={20}
238
- px={4}
239
- size={"xs"}
240
- fw={"bold"}
241
- onClick={() => alepha.inject(DialogService).json(item.data)}
242
- >
243
- {"{ ... }"}
244
- </ActionButton>
245
- );
246
- },
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
+ );
247
245
  },
248
- }}
249
- />
250
- </Flex>
246
+ },
247
+ }}
248
+ />
251
249
  );
252
250
  };
253
251
 
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Queue Monitor
3
+ *
4
+ * Real-time view of background job queues.
5
+ *
6
+ * Features to implement:
7
+ * - List all queues with their provider (redis, memory)
8
+ * - For each queue show:
9
+ * - Pending count
10
+ * - Active count (currently processing)
11
+ * - Completed count (last hour/day)
12
+ * - Failed count with error preview
13
+ * - Click queue to see recent jobs:
14
+ * - Job ID, status, created, started, completed, duration
15
+ * - Payload (JSON viewer)
16
+ * - Error message and stack trace for failed jobs
17
+ * - Actions:
18
+ * - Retry single failed job
19
+ * - Retry all failed jobs
20
+ * - Purge completed jobs
21
+ * - Pause/resume queue
22
+ * - Auto-refresh toggle (poll every 5s)
23
+ * - Filter by status (pending, active, completed, failed)
24
+ *
25
+ * Data source: Need new endpoints in DevToolsProvider
26
+ * - GET /devtools/api/queues -> queue stats
27
+ * - GET /devtools/api/queues/:name/jobs -> job list
28
+ * - POST /devtools/api/queues/:name/jobs/:id/retry
29
+ */
30
+
31
+ import { Flex, Text, ThemeIcon } from "@mantine/core";
32
+ import { IconStack2 } from "@tabler/icons-react";
33
+
34
+ export const DevQueueMonitor = () => {
35
+ return (
36
+ <Flex direction="column" gap="xl" w="100%" p={"xl"}>
37
+ <Flex align="center" gap="sm">
38
+ <ThemeIcon size={32} variant="light" color="orange">
39
+ <IconStack2 size={20} />
40
+ </ThemeIcon>
41
+ <Text size="xl" fw={600}>
42
+ Queue Monitor
43
+ </Text>
44
+ </Flex>
45
+
46
+ <Text c="dimmed">Background job queues - coming soon</Text>
47
+ </Flex>
48
+ );
49
+ };
50
+
51
+ export default DevQueueMonitor;