@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.
- package/README.md +1 -5
- package/dist/index.browser.js +224 -0
- package/dist/index.browser.js.map +1 -0
- package/dist/index.d.ts +349 -321
- package/dist/index.js +293 -186
- package/dist/index.js.map +1 -1
- package/package.json +30 -23
- package/src/assets.ts +6 -0
- package/src/{api/entities → entities}/logs.ts +2 -2
- package/src/index.browser.ts +11 -0
- package/src/index.shared.ts +15 -0
- package/src/index.ts +11 -37
- package/src/{api/providers → providers}/DevToolsMetadataProvider.ts +84 -47
- package/src/providers/DevToolsProvider.ts +280 -0
- package/src/{api/schemas → schemas}/DevActionMetadata.ts +8 -0
- package/src/{api/schemas → schemas}/DevEntityMetadata.ts +3 -0
- package/src/{api/schemas → schemas}/DevMetadata.ts +13 -2
- package/src/{api/schemas → schemas}/DevPageMetadata.ts +3 -0
- package/src/{api/schemas → schemas}/DevTopicMetadata.ts +1 -0
- package/src/ui/AppRouter.tsx +55 -59
- package/src/ui/components/DevLayout.tsx +104 -84
- package/src/ui/components/configuration/ConfigAtoms.page.tsx +5 -0
- package/src/ui/components/configuration/ConfigAtoms.tsx +511 -0
- package/src/ui/components/configuration/ConfigEnv.page.tsx +5 -0
- package/src/ui/components/configuration/ConfigEnv.tsx +230 -0
- package/src/ui/components/configuration/DevConfiguration.tsx +36 -0
- package/src/ui/components/configuration/index.ts +3 -0
- package/src/ui/components/dashboard/DevDashboard.tsx +482 -0
- package/src/ui/components/database/DatabaseEditor.page.tsx +23 -0
- package/src/ui/components/database/DatabaseEditor.tsx +399 -0
- package/src/ui/components/database/DatabaseErd.page.tsx +28 -0
- package/src/ui/components/database/DatabaseErd.tsx +107 -0
- package/src/ui/components/database/DevDatabase.tsx +36 -0
- package/src/ui/components/database/EntityNode.tsx +83 -0
- package/src/ui/components/explorer/DevExplorer.tsx +351 -0
- package/src/ui/components/explorer/ExplorerTree.tsx +178 -0
- package/src/ui/components/explorer/panels/DevPanelAction.tsx +499 -0
- package/src/ui/components/explorer/panels/DevPanelCache.tsx +73 -0
- package/src/ui/components/explorer/panels/DevPanelPage.tsx +96 -0
- package/src/ui/components/explorer/panels/DevPanelQueue.tsx +51 -0
- package/src/ui/components/explorer/panels/DevPanelTopic.tsx +56 -0
- package/src/ui/components/explorer/panels/index.ts +5 -0
- package/src/ui/components/graph/DevDependencyGraph.tsx +35 -60
- package/src/ui/components/graph/GraphControls.tsx +10 -11
- package/src/ui/components/graph/NodeDetails.tsx +22 -29
- package/src/ui/components/graph/ProviderNode.tsx +4 -4
- package/src/ui/components/graph/helpers.ts +1 -1
- package/src/ui/components/logs/DevLogs.tsx +661 -0
- package/src/ui/components/logs/index.ts +1 -0
- package/src/ui/components/shared/TreeView.tsx +189 -0
- package/src/ui/main.css +17 -0
- package/src/ui/main.ts +2 -6
- package/LICENSE +0 -21
- package/assets/devtools/actions.html +0 -21
- package/assets/devtools/actions.html.br +0 -0
- package/assets/devtools/actions.html.gz +0 -0
- package/assets/devtools/asset.BJSLFcNT.css +0 -1
- package/assets/devtools/asset.BJSLFcNT.css.br +0 -0
- package/assets/devtools/asset.BJSLFcNT.css.gz +0 -0
- package/assets/devtools/asset.BZV40eAE.css +0 -1
- package/assets/devtools/asset.BZV40eAE.css.br +0 -0
- package/assets/devtools/asset.BZV40eAE.css.gz +0 -0
- package/assets/devtools/atoms.html +0 -21
- package/assets/devtools/atoms.html.br +0 -0
- package/assets/devtools/atoms.html.gz +0 -0
- package/assets/devtools/caches.html +0 -21
- package/assets/devtools/caches.html.br +0 -0
- package/assets/devtools/caches.html.gz +0 -0
- package/assets/devtools/chunk.1h5GuATm.js +0 -1
- package/assets/devtools/chunk.1h5GuATm.js.br +0 -0
- package/assets/devtools/chunk.1h5GuATm.js.gz +0 -0
- package/assets/devtools/chunk.3PgxxOdM.js +0 -1
- package/assets/devtools/chunk.3PgxxOdM.js.br +0 -0
- package/assets/devtools/chunk.3PgxxOdM.js.gz +0 -0
- package/assets/devtools/chunk.A_W3H6Aa.js +0 -1
- package/assets/devtools/chunk.A_W3H6Aa.js.br +0 -0
- package/assets/devtools/chunk.A_W3H6Aa.js.gz +0 -0
- package/assets/devtools/chunk.B5tL0VjH.js +0 -1
- package/assets/devtools/chunk.B5tL0VjH.js.br +0 -0
- package/assets/devtools/chunk.B5tL0VjH.js.gz +0 -0
- package/assets/devtools/chunk.B8p_Szro.js +0 -1
- package/assets/devtools/chunk.B8p_Szro.js.br +0 -0
- package/assets/devtools/chunk.B8p_Szro.js.gz +0 -0
- package/assets/devtools/chunk.BANy8c2v.js +0 -1
- package/assets/devtools/chunk.BANy8c2v.js.br +0 -0
- package/assets/devtools/chunk.BANy8c2v.js.gz +0 -0
- package/assets/devtools/chunk.BKph0hv1.js +0 -1
- package/assets/devtools/chunk.BKph0hv1.js.br +0 -0
- package/assets/devtools/chunk.BKph0hv1.js.gz +0 -0
- package/assets/devtools/chunk.BUs1kuwE.js +0 -1
- package/assets/devtools/chunk.BUs1kuwE.js.br +0 -0
- package/assets/devtools/chunk.BUs1kuwE.js.gz +0 -0
- package/assets/devtools/chunk.BVIEr21R.js +0 -1
- package/assets/devtools/chunk.BVIEr21R.js.br +0 -0
- package/assets/devtools/chunk.BVIEr21R.js.gz +0 -0
- package/assets/devtools/chunk.Bb3re2d8.js +0 -1
- package/assets/devtools/chunk.Bb3re2d8.js.br +0 -2
- package/assets/devtools/chunk.Bb3re2d8.js.gz +0 -0
- package/assets/devtools/chunk.BjFrJKj1.js +0 -1
- package/assets/devtools/chunk.BjFrJKj1.js.br +0 -2
- package/assets/devtools/chunk.BjFrJKj1.js.gz +0 -0
- package/assets/devtools/chunk.BkXzz14p.js +0 -1
- package/assets/devtools/chunk.BkXzz14p.js.br +0 -0
- package/assets/devtools/chunk.BkXzz14p.js.gz +0 -0
- package/assets/devtools/chunk.BlqFPyLh.js +0 -1
- package/assets/devtools/chunk.BlqFPyLh.js.br +0 -0
- package/assets/devtools/chunk.BlqFPyLh.js.gz +0 -0
- package/assets/devtools/chunk.BymZ9jU5.js +0 -1
- package/assets/devtools/chunk.BymZ9jU5.js.br +0 -0
- package/assets/devtools/chunk.BymZ9jU5.js.gz +0 -0
- package/assets/devtools/chunk.C0BD3Ujz.js +0 -1
- package/assets/devtools/chunk.C0BD3Ujz.js.br +0 -0
- package/assets/devtools/chunk.C0BD3Ujz.js.gz +0 -0
- package/assets/devtools/chunk.C63rzhbT.js +0 -1
- package/assets/devtools/chunk.C63rzhbT.js.br +0 -0
- package/assets/devtools/chunk.C63rzhbT.js.gz +0 -0
- package/assets/devtools/chunk.CJrYVzjN.js +0 -9
- package/assets/devtools/chunk.CJrYVzjN.js.br +0 -0
- package/assets/devtools/chunk.CJrYVzjN.js.gz +0 -0
- package/assets/devtools/chunk.CPGX3Xpx.js +0 -1
- package/assets/devtools/chunk.CPGX3Xpx.js.br +0 -1
- package/assets/devtools/chunk.CPGX3Xpx.js.gz +0 -0
- package/assets/devtools/chunk.Cf-3skUw.js +0 -1
- package/assets/devtools/chunk.Cf-3skUw.js.br +0 -0
- package/assets/devtools/chunk.Cf-3skUw.js.gz +0 -0
- package/assets/devtools/chunk.D7JLxcoJ.js +0 -7
- package/assets/devtools/chunk.D7JLxcoJ.js.br +0 -0
- package/assets/devtools/chunk.D7JLxcoJ.js.gz +0 -0
- package/assets/devtools/chunk.D7e5mBY4.js +0 -1
- package/assets/devtools/chunk.D7e5mBY4.js.br +0 -0
- package/assets/devtools/chunk.D7e5mBY4.js.gz +0 -0
- package/assets/devtools/chunk.DClU9Z1_.js +0 -1
- package/assets/devtools/chunk.DClU9Z1_.js.br +0 -0
- package/assets/devtools/chunk.DClU9Z1_.js.gz +0 -0
- package/assets/devtools/chunk.DE_M8b3Z.js +0 -1
- package/assets/devtools/chunk.DE_M8b3Z.js.br +0 -0
- package/assets/devtools/chunk.DE_M8b3Z.js.gz +0 -0
- package/assets/devtools/chunk.DWASJDBE.js +0 -1
- package/assets/devtools/chunk.DWASJDBE.js.br +0 -0
- package/assets/devtools/chunk.DWASJDBE.js.gz +0 -0
- package/assets/devtools/chunk.DfzRLqwW.js +0 -1
- package/assets/devtools/chunk.DfzRLqwW.js.br +0 -0
- package/assets/devtools/chunk.DfzRLqwW.js.gz +0 -0
- package/assets/devtools/chunk.Dww1YQtc.js +0 -1
- package/assets/devtools/chunk.Dww1YQtc.js.br +0 -0
- package/assets/devtools/chunk.Dww1YQtc.js.gz +0 -0
- package/assets/devtools/chunk.HFLdduaf.js +0 -1
- package/assets/devtools/chunk.HFLdduaf.js.br +0 -0
- package/assets/devtools/chunk.HFLdduaf.js.gz +0 -0
- package/assets/devtools/chunk.J-htqECs.js +0 -1
- package/assets/devtools/chunk.J-htqECs.js.br +0 -2
- package/assets/devtools/chunk.J-htqECs.js.gz +0 -0
- package/assets/devtools/chunk.JjTGVewZ.js +0 -2
- package/assets/devtools/chunk.JjTGVewZ.js.br +0 -0
- package/assets/devtools/chunk.JjTGVewZ.js.gz +0 -0
- package/assets/devtools/chunk.OV_89czZ.js +0 -1
- package/assets/devtools/chunk.OV_89czZ.js.br +0 -0
- package/assets/devtools/chunk.OV_89czZ.js.gz +0 -0
- package/assets/devtools/chunk.YFkMUqFM.js +0 -1
- package/assets/devtools/chunk.YFkMUqFM.js.br +0 -0
- package/assets/devtools/chunk.YFkMUqFM.js.gz +0 -0
- package/assets/devtools/chunk._KdUFIrt.js +0 -1
- package/assets/devtools/chunk._KdUFIrt.js.br +0 -0
- package/assets/devtools/chunk._KdUFIrt.js.gz +0 -0
- package/assets/devtools/chunk.pjP6xqG8.js +0 -1
- package/assets/devtools/chunk.pjP6xqG8.js.br +0 -0
- package/assets/devtools/chunk.pjP6xqG8.js.gz +0 -0
- package/assets/devtools/chunk.uTFtY0ae.js +0 -2
- package/assets/devtools/chunk.uTFtY0ae.js.br +0 -0
- package/assets/devtools/chunk.uTFtY0ae.js.gz +0 -0
- package/assets/devtools/chunk.uyVen0u2.js +0 -1
- package/assets/devtools/chunk.uyVen0u2.js.br +0 -0
- package/assets/devtools/chunk.uyVen0u2.js.gz +0 -0
- package/assets/devtools/chunk.vHjNjQS8.js +0 -1
- package/assets/devtools/chunk.vHjNjQS8.js.br +0 -0
- package/assets/devtools/chunk.vHjNjQS8.js.gz +0 -0
- package/assets/devtools/db.html +0 -21
- package/assets/devtools/db.html.br +0 -0
- package/assets/devtools/db.html.gz +0 -0
- package/assets/devtools/entry.DhzNl8q_.js +0 -79
- package/assets/devtools/entry.DhzNl8q_.js.br +0 -0
- package/assets/devtools/entry.DhzNl8q_.js.gz +0 -0
- package/assets/devtools/env.html +0 -21
- package/assets/devtools/env.html.br +0 -0
- package/assets/devtools/env.html.gz +0 -0
- package/assets/devtools/graph.html +0 -22
- package/assets/devtools/graph.html.br +0 -0
- package/assets/devtools/graph.html.gz +0 -0
- package/assets/devtools/index.html +0 -21
- package/assets/devtools/index.html.br +0 -0
- package/assets/devtools/index.html.gz +0 -0
- package/assets/devtools/logs.html +0 -21
- package/assets/devtools/logs.html.br +0 -0
- package/assets/devtools/logs.html.gz +0 -0
- package/assets/devtools/queues.html +0 -21
- package/assets/devtools/queues.html.br +0 -0
- package/assets/devtools/queues.html.gz +0 -0
- package/assets/devtools/topics.html +0 -21
- package/assets/devtools/topics.html.br +0 -0
- package/assets/devtools/topics.html.gz +0 -0
- package/src/api/DevToolsProvider.ts +0 -157
- package/src/api/providers/DevToolsDatabaseProvider.ts +0 -27
- package/src/api/repositories/LogRepository.ts +0 -8
- package/src/api/schemas/DevCommandMetadata.ts +0 -9
- package/src/ui/components/DevAtomsViewer.tsx +0 -637
- package/src/ui/components/DevCacheInspector.tsx +0 -423
- package/src/ui/components/DevDashboard.tsx +0 -38
- package/src/ui/components/DevEnvExplorer.tsx +0 -462
- package/src/ui/components/DevLogViewer.tsx +0 -252
- package/src/ui/components/DevQueueMonitor.tsx +0 -51
- package/src/ui/components/DevTopicsViewer.tsx +0 -686
- package/src/ui/components/actions/ActionGroup.tsx +0 -37
- package/src/ui/components/actions/ActionItem.tsx +0 -138
- package/src/ui/components/actions/DevActionsExplorer.tsx +0 -132
- package/src/ui/components/actions/MethodBadge.tsx +0 -18
- package/src/ui/components/actions/SchemaViewer.tsx +0 -21
- package/src/ui/components/actions/TryItPanel.tsx +0 -140
- package/src/ui/components/actions/constants.ts +0 -7
- package/src/ui/components/actions/helpers.ts +0 -18
- package/src/ui/components/actions/index.ts +0 -8
- package/src/ui/components/db/ColumnBadge.tsx +0 -55
- package/src/ui/components/db/DevDbStudio.tsx +0 -485
- package/src/ui/components/db/constants.ts +0 -11
- package/src/ui/components/db/index.ts +0 -4
- package/src/ui/components/db/types.ts +0 -7
- package/src/ui/styles.css +0 -1
- /package/src/{api/schemas → schemas}/DevAtomMetadata.ts +0 -0
- /package/src/{api/schemas → schemas}/DevBucketMetadata.ts +0 -0
- /package/src/{api/schemas → schemas}/DevCacheMetadata.ts +0 -0
- /package/src/{api/schemas → schemas}/DevEnvMetadata.ts +0 -0
- /package/src/{api/schemas → schemas}/DevModuleMetadata.ts +0 -0
- /package/src/{api/schemas → schemas}/DevProviderMetadata.ts +0 -0
- /package/src/{api/schemas → schemas}/DevQueueMetadata.ts +0 -0
- /package/src/{api/schemas → schemas}/DevRealmMetadata.ts +0 -0
- /package/src/{api/schemas → schemas}/DevRouteMetadata.ts +0 -0
- /package/src/{api/schemas → schemas}/DevSchedulerMetadata.ts +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { $inject, Alepha, type TObject, type TSchema, t } from "alepha";
|
|
2
2
|
import { $bucket } from "alepha/bucket";
|
|
3
3
|
import { $cache } from "alepha/cache";
|
|
4
|
-
import {
|
|
4
|
+
import { DateTimeProvider } from "alepha/datetime";
|
|
5
5
|
import { $logger } from "alepha/logger";
|
|
6
6
|
import {
|
|
7
7
|
PG_CREATED_AT,
|
|
@@ -16,15 +16,15 @@ import {
|
|
|
16
16
|
RepositoryProvider,
|
|
17
17
|
} from "alepha/orm";
|
|
18
18
|
import { $queue } from "alepha/queue";
|
|
19
|
+
import { $page } from "alepha/react/router";
|
|
19
20
|
import { $scheduler } from "alepha/scheduler";
|
|
20
21
|
import { $issuer } from "alepha/security";
|
|
21
|
-
import { $action } from "alepha/server";
|
|
22
|
+
import { $action, ServerProvider } from "alepha/server";
|
|
22
23
|
import { $topic } from "alepha/topic";
|
|
23
24
|
import type { DevActionMetadata } from "../schemas/DevActionMetadata.ts";
|
|
24
25
|
import type { DevAtomMetadata } from "../schemas/DevAtomMetadata.ts";
|
|
25
26
|
import type { DevBucketMetadata } from "../schemas/DevBucketMetadata.ts";
|
|
26
27
|
import type { DevCacheMetadata } from "../schemas/DevCacheMetadata.ts";
|
|
27
|
-
import type { DevCommandMetadata } from "../schemas/DevCommandMetadata.ts";
|
|
28
28
|
import type {
|
|
29
29
|
DevEntityColumn,
|
|
30
30
|
DevEntityConstraint,
|
|
@@ -33,7 +33,7 @@ import type {
|
|
|
33
33
|
DevEntityMetadata,
|
|
34
34
|
} from "../schemas/DevEntityMetadata.ts";
|
|
35
35
|
import type { DevEnvMetadata } from "../schemas/DevEnvMetadata.ts";
|
|
36
|
-
import type { DevMetadata } from "../schemas/DevMetadata.ts";
|
|
36
|
+
import type { DevMetadata, DevSystem } from "../schemas/DevMetadata.ts";
|
|
37
37
|
import type { DevModuleMetadata } from "../schemas/DevModuleMetadata.ts";
|
|
38
38
|
import type { DevPageMetadata } from "../schemas/DevPageMetadata.ts";
|
|
39
39
|
import type { DevProviderMetadata } from "../schemas/DevProviderMetadata.ts";
|
|
@@ -45,7 +45,10 @@ import type { DevTopicMetadata } from "../schemas/DevTopicMetadata.ts";
|
|
|
45
45
|
|
|
46
46
|
export class DevToolsMetadataProvider {
|
|
47
47
|
protected readonly alepha = $inject(Alepha);
|
|
48
|
+
protected readonly dateTime = $inject(DateTimeProvider);
|
|
48
49
|
protected readonly log = $logger();
|
|
50
|
+
protected readonly serverProvider = $inject(ServerProvider);
|
|
51
|
+
protected readonly startedAt = this.dateTime.nowMillis();
|
|
49
52
|
|
|
50
53
|
public getActions(): DevActionMetadata[] {
|
|
51
54
|
const actionPrimitives = this.alepha.primitives($action);
|
|
@@ -64,13 +67,17 @@ export class DevToolsMetadataProvider {
|
|
|
64
67
|
description: action.options.description,
|
|
65
68
|
summary: options.summary,
|
|
66
69
|
disabled: action.options.disabled,
|
|
67
|
-
secure:
|
|
70
|
+
secure:
|
|
71
|
+
action.middlewares.some((m) => m?.name === "$secure") || undefined,
|
|
68
72
|
hide: options.hide,
|
|
69
73
|
body: schema?.body,
|
|
70
74
|
params: schema?.params,
|
|
71
75
|
query: schema?.query,
|
|
72
76
|
response: schema?.response,
|
|
73
77
|
bodyContentType: action.getBodyContentType(),
|
|
78
|
+
middlewares: action.middlewares.filter(Boolean).length
|
|
79
|
+
? action.middlewares.filter(Boolean)
|
|
80
|
+
: undefined,
|
|
74
81
|
};
|
|
75
82
|
});
|
|
76
83
|
}
|
|
@@ -101,12 +108,23 @@ export class DevToolsMetadataProvider {
|
|
|
101
108
|
public getTopics(): DevTopicMetadata[] {
|
|
102
109
|
const topicPrimitives = this.alepha.primitives($topic);
|
|
103
110
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
// Deduplicate by name, count subscribers
|
|
112
|
+
const topicMap = new Map<string, DevTopicMetadata>();
|
|
113
|
+
for (const topic of topicPrimitives) {
|
|
114
|
+
const existing = topicMap.get(topic.name);
|
|
115
|
+
if (existing) {
|
|
116
|
+
existing.subscribers++;
|
|
117
|
+
} else {
|
|
118
|
+
topicMap.set(topic.name, {
|
|
119
|
+
name: topic.name,
|
|
120
|
+
description: topic.options.description,
|
|
121
|
+
schema: topic.options.schema,
|
|
122
|
+
provider: this.getProviderName(topic.options.provider),
|
|
123
|
+
subscribers: 1,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return Array.from(topicMap.values());
|
|
110
128
|
}
|
|
111
129
|
|
|
112
130
|
public getBuckets(): DevBucketMetadata[] {
|
|
@@ -152,30 +170,42 @@ export class DevToolsMetadataProvider {
|
|
|
152
170
|
}
|
|
153
171
|
|
|
154
172
|
public getPages(): DevPageMetadata[] {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// hasChildren: !!page.options.children,
|
|
167
|
-
// hasParent: !!page.options.parent,
|
|
168
|
-
// hasErrorHandler: !!page.options.errorHandler,
|
|
169
|
-
// static:
|
|
170
|
-
// typeof page.options.static === "boolean"
|
|
171
|
-
// ? page.options.static
|
|
172
|
-
// : !!page.options.static,
|
|
173
|
-
// cache: page.options.cache,
|
|
174
|
-
// client: page.options.client,
|
|
175
|
-
// animation: page.options.animation,
|
|
176
|
-
// }));
|
|
173
|
+
const pagePrimitives = this.alepha.primitives($page);
|
|
174
|
+
|
|
175
|
+
return pagePrimitives.map((page: any) => {
|
|
176
|
+
// Resolve children (can be an array or a function returning an array)
|
|
177
|
+
const children =
|
|
178
|
+
typeof page.options.children === "function"
|
|
179
|
+
? page.options.children()
|
|
180
|
+
: page.options.children;
|
|
181
|
+
const childrenNames = Array.isArray(children)
|
|
182
|
+
? children.map((c: any) => c.name).filter(Boolean)
|
|
183
|
+
: undefined;
|
|
177
184
|
|
|
178
|
-
|
|
185
|
+
return {
|
|
186
|
+
name: page.name,
|
|
187
|
+
label: page.options.label,
|
|
188
|
+
description: page.options.description,
|
|
189
|
+
path: page.options.path,
|
|
190
|
+
parentName: page.options.parent?.name,
|
|
191
|
+
params: page.options.schema?.params,
|
|
192
|
+
query: page.options.schema?.query,
|
|
193
|
+
hasComponent: !!page.options.component,
|
|
194
|
+
hasLazy: !!page.options.lazy,
|
|
195
|
+
hasResolve: !!page.options.resolve,
|
|
196
|
+
childrenNames: childrenNames?.length ? childrenNames : undefined,
|
|
197
|
+
hasChildren: !!page.options.children,
|
|
198
|
+
hasParent: !!page.options.parent,
|
|
199
|
+
hasErrorHandler: !!page.options.errorHandler,
|
|
200
|
+
static:
|
|
201
|
+
typeof page.options.static === "boolean"
|
|
202
|
+
? page.options.static
|
|
203
|
+
: !!page.options.static,
|
|
204
|
+
cache: page.options.cache,
|
|
205
|
+
client: page.options.client,
|
|
206
|
+
animation: page.options.animation,
|
|
207
|
+
};
|
|
208
|
+
});
|
|
179
209
|
}
|
|
180
210
|
|
|
181
211
|
public getProviders(): DevProviderMetadata[] {
|
|
@@ -291,6 +321,9 @@ export class DevToolsMetadataProvider {
|
|
|
291
321
|
indexes,
|
|
292
322
|
foreignKeys,
|
|
293
323
|
constraints,
|
|
324
|
+
schema: entity.schema,
|
|
325
|
+
insertSchema: entity.insertSchema,
|
|
326
|
+
updateSchema: entity.updateSchema,
|
|
294
327
|
};
|
|
295
328
|
});
|
|
296
329
|
} catch {
|
|
@@ -347,18 +380,6 @@ export class DevToolsMetadataProvider {
|
|
|
347
380
|
return false;
|
|
348
381
|
}
|
|
349
382
|
|
|
350
|
-
public getCommands(): DevCommandMetadata[] {
|
|
351
|
-
const commandPrimitives = this.alepha.primitives($command);
|
|
352
|
-
|
|
353
|
-
return commandPrimitives
|
|
354
|
-
.filter((cmd) => !cmd.options.hide)
|
|
355
|
-
.map((command) => ({
|
|
356
|
-
name: command.name,
|
|
357
|
-
description: command.options.description,
|
|
358
|
-
hidden: command.options.hide,
|
|
359
|
-
}));
|
|
360
|
-
}
|
|
361
|
-
|
|
362
383
|
public getRoutes(): DevRouteMetadata[] {
|
|
363
384
|
// Routes are the base primitive - actions and pages are routes
|
|
364
385
|
// Showing them separately would be redundant with actions
|
|
@@ -388,8 +409,25 @@ export class DevToolsMetadataProvider {
|
|
|
388
409
|
}));
|
|
389
410
|
}
|
|
390
411
|
|
|
412
|
+
public getSystem(): DevSystem {
|
|
413
|
+
const isBun = typeof globalThis.Bun !== "undefined";
|
|
414
|
+
const port = Number(this.alepha.env.SERVER_PORT) || 3000;
|
|
415
|
+
return {
|
|
416
|
+
alephaVersion: String(this.alepha.env.npm_package_version ?? "unknown"),
|
|
417
|
+
nodeVersion: isBun
|
|
418
|
+
? (globalThis.Bun?.version ?? "unknown")
|
|
419
|
+
: process.version,
|
|
420
|
+
runtime: isBun ? "bun" : "node",
|
|
421
|
+
mode: this.alepha.isProduction() ? "production" : "development",
|
|
422
|
+
port,
|
|
423
|
+
uptime: (this.dateTime.nowMillis() - this.startedAt) / 1000,
|
|
424
|
+
memoryUsage: process.memoryUsage?.()?.rss ?? 0,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
391
428
|
public getMetadata(): DevMetadata {
|
|
392
429
|
return {
|
|
430
|
+
system: this.getSystem(),
|
|
393
431
|
actions: this.getActions(),
|
|
394
432
|
queues: this.getQueues(),
|
|
395
433
|
schedulers: this.getSchedulers(),
|
|
@@ -401,7 +439,6 @@ export class DevToolsMetadataProvider {
|
|
|
401
439
|
providers: this.getProviders(),
|
|
402
440
|
modules: this.getModules(),
|
|
403
441
|
entities: this.getEntities(),
|
|
404
|
-
commands: this.getCommands(),
|
|
405
442
|
routes: this.getRoutes(),
|
|
406
443
|
envs: this.getEnvs(),
|
|
407
444
|
atoms: this.getAtoms(),
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { $hook, $inject, Alepha, t } from "alepha";
|
|
2
|
+
import { $logger, MemoryDestinationProvider } from "alepha/logger";
|
|
3
|
+
import { RepositoryProvider } from "alepha/orm";
|
|
4
|
+
import { $route, ServerProvider } from "alepha/server";
|
|
5
|
+
import { $serve } from "alepha/server/static";
|
|
6
|
+
import { devtoolsAssets } from "../assets.ts";
|
|
7
|
+
import { devMetadataSchema } from "../schemas/DevMetadata.ts";
|
|
8
|
+
import { DevToolsMetadataProvider } from "./DevToolsMetadataProvider.ts";
|
|
9
|
+
|
|
10
|
+
export class DevToolsProvider {
|
|
11
|
+
protected readonly log = $logger();
|
|
12
|
+
protected readonly alepha = $inject(Alepha);
|
|
13
|
+
protected readonly serverProvider = $inject(ServerProvider);
|
|
14
|
+
protected readonly devCollectorProvider = $inject(DevToolsMetadataProvider);
|
|
15
|
+
protected readonly memoryDestination = $inject(MemoryDestinationProvider);
|
|
16
|
+
|
|
17
|
+
protected readonly onStart = $hook({
|
|
18
|
+
on: "start",
|
|
19
|
+
handler: () => {
|
|
20
|
+
this.log.info("Devtools OK", {
|
|
21
|
+
url: `${this.serverProvider.hostname}/__devtools/`,
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Capture all logs into memory so the devtools UI can display them.
|
|
28
|
+
* In dev mode, LogDestinationProvider is bound to ConsoleDestinationProvider,
|
|
29
|
+
* so MemoryDestinationProvider would otherwise never receive writes.
|
|
30
|
+
*/
|
|
31
|
+
protected readonly onLog = $hook({
|
|
32
|
+
on: "log",
|
|
33
|
+
handler: ({ entry }) => {
|
|
34
|
+
this.memoryDestination.write("", entry);
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
protected readonly uiRoute = $serve({
|
|
39
|
+
path: "/__devtools",
|
|
40
|
+
root: devtoolsAssets.ui,
|
|
41
|
+
historyApiFallback: true,
|
|
42
|
+
silent: true,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
protected readonly metadataRoute = $route({
|
|
46
|
+
method: "GET",
|
|
47
|
+
path: "/__devtools/api/metadata",
|
|
48
|
+
silent: true,
|
|
49
|
+
schema: {
|
|
50
|
+
response: devMetadataSchema,
|
|
51
|
+
},
|
|
52
|
+
handler: () => {
|
|
53
|
+
return this.devCollectorProvider.getMetadata();
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
protected readonly updateAtomRoute = $route({
|
|
58
|
+
method: "POST",
|
|
59
|
+
path: "/__devtools/api/atoms",
|
|
60
|
+
silent: true,
|
|
61
|
+
schema: {
|
|
62
|
+
body: t.object({
|
|
63
|
+
name: t.text(),
|
|
64
|
+
value: t.any(),
|
|
65
|
+
}),
|
|
66
|
+
response: t.object({
|
|
67
|
+
success: t.boolean(),
|
|
68
|
+
}),
|
|
69
|
+
},
|
|
70
|
+
handler: ({ body }) => {
|
|
71
|
+
const atoms = this.alepha.store.getAtoms(false);
|
|
72
|
+
const atomEntry = atoms.find((a) => a.atom.key === body.name);
|
|
73
|
+
|
|
74
|
+
if (atomEntry) {
|
|
75
|
+
this.alepha.store.set(atomEntry.atom, body.value);
|
|
76
|
+
return { success: true };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { success: false };
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
84
|
+
// Logs endpoint
|
|
85
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
protected readonly logsRoute = $route({
|
|
88
|
+
method: "GET",
|
|
89
|
+
path: "/__devtools/api/logs",
|
|
90
|
+
silent: true,
|
|
91
|
+
schema: {
|
|
92
|
+
query: t.object({
|
|
93
|
+
level: t.optional(t.text()),
|
|
94
|
+
type: t.optional(t.text()),
|
|
95
|
+
module: t.optional(t.text()),
|
|
96
|
+
search: t.optional(t.text()),
|
|
97
|
+
since: t.optional(t.text()),
|
|
98
|
+
limit: t.optional(t.text()),
|
|
99
|
+
offset: t.optional(t.text()),
|
|
100
|
+
}),
|
|
101
|
+
response: t.object({
|
|
102
|
+
logs: t.array(t.any()),
|
|
103
|
+
total: t.integer(),
|
|
104
|
+
}),
|
|
105
|
+
},
|
|
106
|
+
handler: ({ query }) => {
|
|
107
|
+
const levelOrder = ["TRACE", "DEBUG", "INFO", "WARN", "ERROR"];
|
|
108
|
+
let entries = this.memoryDestination.logs;
|
|
109
|
+
|
|
110
|
+
if (query.level) {
|
|
111
|
+
const minIndex = levelOrder.indexOf(query.level.toUpperCase());
|
|
112
|
+
if (minIndex >= 0) {
|
|
113
|
+
entries = entries.filter(
|
|
114
|
+
(e) => levelOrder.indexOf(e.level) >= minIndex,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (query.type) {
|
|
120
|
+
const types = query.type.split(",").map((t) => t.trim());
|
|
121
|
+
entries = entries.filter((e) => {
|
|
122
|
+
if (!e.data || typeof e.data !== "object") return false;
|
|
123
|
+
const d = e.data as Record<string, unknown>;
|
|
124
|
+
for (const t of types) {
|
|
125
|
+
if (t === "http" || t === "http:request") {
|
|
126
|
+
if (d.status && d.method && d.path && d.duration) return true;
|
|
127
|
+
} else if (t === "db" || t === "db:query") {
|
|
128
|
+
if (d.type === "db:query") return true;
|
|
129
|
+
} else if (d.type === t) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (query.module) {
|
|
138
|
+
entries = entries.filter((e) => e.module === query.module);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (query.search) {
|
|
142
|
+
const terms = query.search.toLowerCase().split(/\s+/);
|
|
143
|
+
entries = entries.filter((e) => {
|
|
144
|
+
const text = `${e.message} ${e.module} ${e.service}`.toLowerCase();
|
|
145
|
+
return terms.every((term) => text.includes(term));
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (query.since) {
|
|
150
|
+
const since = Number(query.since);
|
|
151
|
+
if (!Number.isNaN(since)) {
|
|
152
|
+
entries = entries.filter((e) => e.timestamp >= since);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const total = entries.length;
|
|
157
|
+
|
|
158
|
+
// Reverse so newest first
|
|
159
|
+
entries = entries.reverse();
|
|
160
|
+
|
|
161
|
+
const offset = query.offset ? Number(query.offset) : 0;
|
|
162
|
+
const limit = query.limit ? Number(query.limit) : 100;
|
|
163
|
+
entries = entries.slice(offset, offset + limit);
|
|
164
|
+
|
|
165
|
+
return { logs: entries, total };
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
170
|
+
// DB CRUD endpoints
|
|
171
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
protected readonly dbListRoute = $route({
|
|
174
|
+
method: "GET",
|
|
175
|
+
path: "/__devtools/api/db/:entity/records",
|
|
176
|
+
silent: true,
|
|
177
|
+
schema: {
|
|
178
|
+
params: t.object({ entity: t.text() }),
|
|
179
|
+
query: t.object({
|
|
180
|
+
page: t.optional(t.text()),
|
|
181
|
+
size: t.optional(t.text()),
|
|
182
|
+
sort: t.optional(t.text()),
|
|
183
|
+
}),
|
|
184
|
+
response: t.record(t.text(), t.any()),
|
|
185
|
+
},
|
|
186
|
+
handler: async ({ params, query }) => {
|
|
187
|
+
const repo = this.getRepository(params.entity);
|
|
188
|
+
if (!repo) {
|
|
189
|
+
return { content: [], page: { totalElements: 0 } };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return repo.paginate(
|
|
193
|
+
{
|
|
194
|
+
page: query.page ? Number(query.page) : 0,
|
|
195
|
+
size: query.size ? Number(query.size) : 50,
|
|
196
|
+
sort: query.sort || undefined,
|
|
197
|
+
},
|
|
198
|
+
{},
|
|
199
|
+
{ count: true },
|
|
200
|
+
) as any;
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
protected readonly dbCreateRoute = $route({
|
|
205
|
+
method: "POST",
|
|
206
|
+
path: "/__devtools/api/db/:entity/records",
|
|
207
|
+
silent: true,
|
|
208
|
+
schema: {
|
|
209
|
+
params: t.object({ entity: t.text() }),
|
|
210
|
+
body: t.record(t.text(), t.any()),
|
|
211
|
+
response: t.record(t.text(), t.any()),
|
|
212
|
+
},
|
|
213
|
+
handler: async ({ params, body }) => {
|
|
214
|
+
const repo = this.getRepository(params.entity);
|
|
215
|
+
if (!repo) {
|
|
216
|
+
return { error: "Entity not found" };
|
|
217
|
+
}
|
|
218
|
+
return repo.create(body) as any;
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
protected readonly dbUpdateRoute = $route({
|
|
223
|
+
method: "PUT",
|
|
224
|
+
path: "/__devtools/api/db/:entity/records/:id",
|
|
225
|
+
silent: true,
|
|
226
|
+
schema: {
|
|
227
|
+
params: t.object({ entity: t.text(), id: t.text() }),
|
|
228
|
+
body: t.record(t.text(), t.any()),
|
|
229
|
+
response: t.record(t.text(), t.any()),
|
|
230
|
+
},
|
|
231
|
+
handler: async ({ params, body }) => {
|
|
232
|
+
const repo = this.getRepository(params.entity);
|
|
233
|
+
if (!repo) {
|
|
234
|
+
return { error: "Entity not found" };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const idValue = this.parseId(repo, params.id);
|
|
238
|
+
return repo.updateById(idValue, body) as any;
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
protected readonly dbDeleteRoute = $route({
|
|
243
|
+
method: "DELETE",
|
|
244
|
+
path: "/__devtools/api/db/:entity/records/:id",
|
|
245
|
+
silent: true,
|
|
246
|
+
schema: {
|
|
247
|
+
params: t.object({ entity: t.text(), id: t.text() }),
|
|
248
|
+
response: t.record(t.text(), t.any()),
|
|
249
|
+
},
|
|
250
|
+
handler: async ({ params }) => {
|
|
251
|
+
const repo = this.getRepository(params.entity);
|
|
252
|
+
if (!repo) {
|
|
253
|
+
return { error: "Entity not found" };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const idValue = this.parseId(repo, params.id);
|
|
257
|
+
return repo.deleteById(idValue) as any;
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
262
|
+
|
|
263
|
+
protected getRepository(entityName: string) {
|
|
264
|
+
try {
|
|
265
|
+
const repositoryProvider = this.alepha.inject(RepositoryProvider);
|
|
266
|
+
const repos = repositoryProvider.getRepositories();
|
|
267
|
+
return repos.find((r) => r.entity.name === entityName);
|
|
268
|
+
} catch {
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
protected parseId(repo: any, rawId: string): string | number {
|
|
274
|
+
const idType = repo.id.type;
|
|
275
|
+
if (idType?.type === "integer" || idType?.type === "number") {
|
|
276
|
+
return Number(rawId);
|
|
277
|
+
}
|
|
278
|
+
return rawId;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -17,6 +17,14 @@ export const devActionMetadataSchema = t.object({
|
|
|
17
17
|
query: t.optional(t.any()),
|
|
18
18
|
response: t.optional(t.any()),
|
|
19
19
|
bodyContentType: t.optional(t.text()),
|
|
20
|
+
middlewares: t.optional(
|
|
21
|
+
t.array(
|
|
22
|
+
t.object({
|
|
23
|
+
name: t.text(),
|
|
24
|
+
options: t.optional(t.any()),
|
|
25
|
+
}),
|
|
26
|
+
),
|
|
27
|
+
),
|
|
20
28
|
});
|
|
21
29
|
|
|
22
30
|
export type DevActionMetadata = Static<typeof devActionMetadataSchema>;
|
|
@@ -48,6 +48,9 @@ export const devEntityMetadataSchema = t.object({
|
|
|
48
48
|
indexes: t.array(devEntityIndexSchema),
|
|
49
49
|
foreignKeys: t.array(devEntityForeignKeySchema),
|
|
50
50
|
constraints: t.array(devEntityConstraintSchema),
|
|
51
|
+
schema: t.optional(t.any()),
|
|
52
|
+
insertSchema: t.optional(t.any()),
|
|
53
|
+
updateSchema: t.optional(t.any()),
|
|
51
54
|
});
|
|
52
55
|
|
|
53
56
|
export type DevEntityColumn = Static<typeof devEntityColumnSchema>;
|
|
@@ -3,7 +3,6 @@ import { devActionMetadataSchema } from "./DevActionMetadata.ts";
|
|
|
3
3
|
import { devAtomMetadataSchema } from "./DevAtomMetadata.ts";
|
|
4
4
|
import { devBucketMetadataSchema } from "./DevBucketMetadata.ts";
|
|
5
5
|
import { devCacheMetadataSchema } from "./DevCacheMetadata.ts";
|
|
6
|
-
import { devCommandMetadataSchema } from "./DevCommandMetadata.ts";
|
|
7
6
|
import { devEntityMetadataSchema } from "./DevEntityMetadata.ts";
|
|
8
7
|
import { devEnvMetadataSchema } from "./DevEnvMetadata.ts";
|
|
9
8
|
import { devModuleMetadataSchema } from "./DevModuleMetadata.ts";
|
|
@@ -15,7 +14,20 @@ import { devRouteMetadataSchema } from "./DevRouteMetadata.ts";
|
|
|
15
14
|
import { devSchedulerMetadataSchema } from "./DevSchedulerMetadata.ts";
|
|
16
15
|
import { devTopicMetadataSchema } from "./DevTopicMetadata.ts";
|
|
17
16
|
|
|
17
|
+
export const devSystemSchema = t.object({
|
|
18
|
+
alephaVersion: t.text(),
|
|
19
|
+
nodeVersion: t.text(),
|
|
20
|
+
runtime: t.enum(["node", "bun"]),
|
|
21
|
+
mode: t.enum(["development", "production"]),
|
|
22
|
+
port: t.integer(),
|
|
23
|
+
uptime: t.number(),
|
|
24
|
+
memoryUsage: t.number(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export type DevSystem = Static<typeof devSystemSchema>;
|
|
28
|
+
|
|
18
29
|
export const devMetadataSchema = t.object({
|
|
30
|
+
system: devSystemSchema,
|
|
19
31
|
actions: t.array(devActionMetadataSchema),
|
|
20
32
|
queues: t.array(devQueueMetadataSchema),
|
|
21
33
|
schedulers: t.array(devSchedulerMetadataSchema),
|
|
@@ -27,7 +39,6 @@ export const devMetadataSchema = t.object({
|
|
|
27
39
|
providers: t.array(devProviderMetadataSchema),
|
|
28
40
|
modules: t.array(devModuleMetadataSchema),
|
|
29
41
|
entities: t.array(devEntityMetadataSchema),
|
|
30
|
-
commands: t.array(devCommandMetadataSchema),
|
|
31
42
|
routes: t.array(devRouteMetadataSchema),
|
|
32
43
|
envs: t.array(devEnvMetadataSchema),
|
|
33
44
|
atoms: t.array(devAtomMetadataSchema),
|
|
@@ -2,13 +2,16 @@ import { type Static, t } from "alepha";
|
|
|
2
2
|
|
|
3
3
|
export const devPageMetadataSchema = t.object({
|
|
4
4
|
name: t.text(),
|
|
5
|
+
label: t.optional(t.text()),
|
|
5
6
|
description: t.optional(t.text()),
|
|
6
7
|
path: t.optional(t.text()),
|
|
8
|
+
parentName: t.optional(t.text()),
|
|
7
9
|
params: t.optional(t.any()),
|
|
8
10
|
query: t.optional(t.any()),
|
|
9
11
|
hasComponent: t.boolean(),
|
|
10
12
|
hasLazy: t.boolean(),
|
|
11
13
|
hasResolve: t.boolean(),
|
|
14
|
+
childrenNames: t.optional(t.array(t.text())),
|
|
12
15
|
hasChildren: t.boolean(),
|
|
13
16
|
hasParent: t.boolean(),
|
|
14
17
|
hasErrorHandler: t.boolean(),
|