proteum 2.1.3-1 → 2.1.6
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/AGENTS.md +22 -14
- package/README.md +109 -17
- package/agents/project/AGENTS.md +188 -25
- package/agents/project/CODING_STYLE.md +1 -0
- package/agents/project/client/AGENTS.md +13 -8
- package/agents/project/client/pages/AGENTS.md +17 -9
- package/agents/project/diagnostics.md +52 -0
- package/agents/project/optimizations.md +48 -0
- package/agents/project/server/routes/AGENTS.md +9 -6
- package/agents/project/server/services/AGENTS.md +10 -6
- package/agents/project/tests/AGENTS.md +11 -5
- package/cli/app/config.ts +13 -14
- package/cli/app/index.ts +58 -0
- package/cli/commands/connect.ts +45 -0
- package/cli/commands/dev.ts +26 -11
- package/cli/commands/diagnose.ts +286 -0
- package/cli/commands/doctor.ts +18 -5
- package/cli/commands/explain.ts +25 -0
- package/cli/commands/perf.ts +243 -0
- package/cli/commands/trace.ts +9 -1
- package/cli/commands/verify.ts +281 -0
- package/cli/compiler/artifacts/connectedProjects.ts +453 -0
- package/cli/compiler/artifacts/controllers.ts +198 -49
- package/cli/compiler/artifacts/discovery.ts +0 -34
- package/cli/compiler/artifacts/manifest.ts +90 -6
- package/cli/compiler/artifacts/routing.ts +2 -2
- package/cli/compiler/artifacts/services.ts +277 -130
- package/cli/compiler/client/index.ts +3 -0
- package/cli/compiler/common/files/style.ts +52 -0
- package/cli/compiler/common/generatedRouteModules.ts +34 -5
- package/cli/compiler/common/scripts.ts +11 -5
- package/cli/compiler/index.ts +2 -1
- package/cli/compiler/server/index.ts +3 -0
- package/cli/presentation/commands.ts +110 -7
- package/cli/presentation/devSession.ts +32 -7
- package/cli/runtime/commands.ts +165 -6
- package/cli/scaffold/index.ts +14 -25
- package/cli/scaffold/templates.ts +41 -27
- package/cli/utils/agents.ts +4 -2
- package/cli/utils/keyboard.ts +8 -0
- package/client/dev/profiler/ApexChart.tsx +66 -0
- package/client/dev/profiler/index.tsx +2508 -302
- package/client/dev/profiler/runtime.noop.ts +12 -0
- package/client/dev/profiler/runtime.ts +195 -4
- package/client/services/router/request/api.ts +6 -1
- package/common/applicationConfig.ts +173 -0
- package/common/applicationConfigLoader.ts +102 -0
- package/common/connectedProjects.ts +113 -0
- package/common/dev/connect.ts +267 -0
- package/common/dev/console.ts +31 -0
- package/common/dev/contractsDoctor.ts +128 -0
- package/common/dev/diagnostics.ts +59 -15
- package/common/dev/inspection.ts +491 -0
- package/common/dev/performance.ts +809 -0
- package/common/dev/profiler.ts +3 -0
- package/common/dev/proteumManifest.ts +31 -6
- package/common/dev/requestTrace.ts +52 -1
- package/common/env/proteumEnv.ts +176 -50
- package/common/router/index.ts +1 -0
- package/common/router/request/api.ts +2 -0
- package/config.ts +5 -0
- package/docs/dev-commands.md +5 -1
- package/docs/dev-sessions.md +90 -0
- package/docs/diagnostics.md +74 -11
- package/docs/request-tracing.md +50 -3
- package/package.json +1 -1
- package/server/app/container/config.ts +16 -87
- package/server/app/container/console/index.ts +42 -8
- package/server/app/container/index.ts +3 -1
- package/server/app/container/trace/index.ts +105 -0
- package/server/app/devDiagnostics.ts +138 -0
- package/server/app/index.ts +18 -8
- package/server/app/service/container.ts +0 -12
- package/server/app/service/index.ts +0 -2
- package/server/services/prisma/index.ts +121 -4
- package/server/services/router/http/index.ts +266 -0
- package/server/services/router/index.ts +50 -47
- package/server/services/router/request/api.ts +160 -19
- package/server/services/router/request/index.ts +8 -0
- package/server/services/router/response/index.ts +23 -1
- package/server/services/router/response/page/document.tsx +5 -0
- package/server/services/router/response/page/index.tsx +10 -0
- package/agents/framework/AGENTS.md +0 -177
- package/server/services/auth/router/service.json +0 -6
- package/server/services/auth/service.json +0 -6
- package/server/services/cron/service.json +0 -6
- package/server/services/disks/drivers/local/service.json +0 -6
- package/server/services/disks/drivers/s3/service.json +0 -6
- package/server/services/disks/service.json +0 -6
- package/server/services/fetch/service.json +0 -7
- package/server/services/prisma/service.json +0 -6
- package/server/services/router/service.json +0 -6
- package/server/services/schema/router/service.json +0 -6
- package/server/services/schema/service.json +0 -6
- package/server/services/security/encrypt/aes/service.json +0 -6
|
@@ -36,7 +36,7 @@ export type TIndexedRouteDefinition = {
|
|
|
36
36
|
hasSetup: boolean;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
type TGeneratedClientRouteModuleOptions = { chunkId: string
|
|
39
|
+
type TGeneratedClientRouteModuleOptions = { chunkId: string };
|
|
40
40
|
|
|
41
41
|
type TWriteGeneratedRouteModuleOptions = {
|
|
42
42
|
outputFilepath: string;
|
|
@@ -222,6 +222,9 @@ const getRouteOptionMetadata = (node: ts.ObjectLiteralExpression | undefined) =>
|
|
|
222
222
|
return { optionKeys, normalizedOptionKeys, invalidOptionKeys, reservedOptionKeys };
|
|
223
223
|
};
|
|
224
224
|
|
|
225
|
+
const buildInjectedRouteMetadata = (sourceFilepath: string, sourceLocation: TIndexedSourceLocation, extra: string[] = []) =>
|
|
226
|
+
`{ filepath: ${JSON.stringify(normalizeFilepath(sourceFilepath))}, sourceLocation: { line: ${sourceLocation.line}, column: ${sourceLocation.column} }${extra.length > 0 ? `, ${extra.join(', ')}` : ''} }`;
|
|
227
|
+
|
|
225
228
|
const routeModuleExtensions = ['.ts', '.tsx', '.js', '.jsx'];
|
|
226
229
|
|
|
227
230
|
const resolveRouteImport = (sourceFilepath: string, moduleSpecifier: string, routeSourceFilepaths?: Set<string>) => {
|
|
@@ -467,7 +470,10 @@ const buildClientRegisterArgs = (
|
|
|
467
470
|
clientRoute: TGeneratedClientRouteModuleOptions,
|
|
468
471
|
) => {
|
|
469
472
|
const { optionsArg, setupArg, renderArg } = getClientRouteSignature(sourceFile, definition);
|
|
470
|
-
const
|
|
473
|
+
const sourceLocation = getNodeLocation(sourceFile, definition.callExpression);
|
|
474
|
+
const injectedOptions = buildInjectedRouteMetadata(sourceFile.fileName, sourceLocation, [
|
|
475
|
+
`id: ${JSON.stringify(clientRoute.chunkId)}`,
|
|
476
|
+
]);
|
|
471
477
|
|
|
472
478
|
if (!optionsArg && !setupArg) {
|
|
473
479
|
return [injectedOptions, getNodeText(sourceFile, renderArg)];
|
|
@@ -475,7 +481,7 @@ const buildClientRegisterArgs = (
|
|
|
475
481
|
|
|
476
482
|
if (optionsArg && !setupArg) {
|
|
477
483
|
return [
|
|
478
|
-
`{ ...(${getNodeText(sourceFile, optionsArg)}),
|
|
484
|
+
`{ ...(${getNodeText(sourceFile, optionsArg)}), ...${injectedOptions} }`,
|
|
479
485
|
getNodeText(sourceFile, renderArg),
|
|
480
486
|
];
|
|
481
487
|
}
|
|
@@ -485,12 +491,35 @@ const buildClientRegisterArgs = (
|
|
|
485
491
|
}
|
|
486
492
|
|
|
487
493
|
return [
|
|
488
|
-
`{ ...(${getNodeText(sourceFile, optionsArg!)}),
|
|
494
|
+
`{ ...(${getNodeText(sourceFile, optionsArg!)}), ...${injectedOptions} }`,
|
|
489
495
|
getNodeText(sourceFile, setupArg!),
|
|
490
496
|
getNodeText(sourceFile, renderArg),
|
|
491
497
|
];
|
|
492
498
|
};
|
|
493
499
|
|
|
500
|
+
const buildServerRegisterArgs = (sourceFile: ts.SourceFile, definition: TRouteDefinition) => {
|
|
501
|
+
const sourceLocation = getNodeLocation(sourceFile, definition.callExpression);
|
|
502
|
+
const [targetArg, ...routeArgs] = [...definition.args];
|
|
503
|
+
const controllerArg = routeArgs[routeArgs.length - 1];
|
|
504
|
+
const optionsArg =
|
|
505
|
+
routeArgs.length >= 2 && ts.isObjectLiteralExpression(routeArgs[0]) ? routeArgs[0] : undefined;
|
|
506
|
+
const injectedOptions = buildInjectedRouteMetadata(sourceFile.fileName, sourceLocation);
|
|
507
|
+
|
|
508
|
+
if (routeArgs.length === 1) {
|
|
509
|
+
return [getNodeText(sourceFile, targetArg), injectedOptions, getNodeText(sourceFile, controllerArg)];
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (optionsArg) {
|
|
513
|
+
return [
|
|
514
|
+
getNodeText(sourceFile, targetArg),
|
|
515
|
+
`{ ...(${getNodeText(sourceFile, optionsArg)}), ...${injectedOptions} }`,
|
|
516
|
+
getNodeText(sourceFile, controllerArg),
|
|
517
|
+
];
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return [getNodeText(sourceFile, targetArg), ...routeArgs.map((arg) => getNodeText(sourceFile, arg))];
|
|
521
|
+
};
|
|
522
|
+
|
|
494
523
|
const buildRegisterStatements = (
|
|
495
524
|
sourceFile: ts.SourceFile,
|
|
496
525
|
side: TRouteSide,
|
|
@@ -519,7 +548,7 @@ const buildRegisterStatements = (
|
|
|
519
548
|
}
|
|
520
549
|
|
|
521
550
|
return definitions.map((definition) => {
|
|
522
|
-
const args =
|
|
551
|
+
const args = buildServerRegisterArgs(sourceFile, definition);
|
|
523
552
|
|
|
524
553
|
return `${definition.serviceLocalName}.${definition.methodName}(${args.join(', ')});`;
|
|
525
554
|
});
|
|
@@ -3,24 +3,30 @@ import type { RuleSetRule } from '@rspack/core';
|
|
|
3
3
|
|
|
4
4
|
type TScriptRuleOptions = { app: App; side: TAppSide; dev: boolean };
|
|
5
5
|
|
|
6
|
-
const shouldExcludeNodeModule = (filePath: string) => {
|
|
7
|
-
|
|
6
|
+
const shouldExcludeNodeModule = (app: App, filePath: string) => {
|
|
7
|
+
const normalizedFilePath = filePath.replace(/\\/g, '/');
|
|
8
|
+
if (!normalizedFilePath.includes('/node_modules/')) return false;
|
|
8
9
|
|
|
9
|
-
if (
|
|
10
|
+
if (
|
|
11
|
+
normalizedFilePath.includes('/node_modules/proteum/') &&
|
|
12
|
+
!normalizedFilePath.includes('/node_modules/proteum/node_modules/')
|
|
13
|
+
) {
|
|
10
14
|
return false;
|
|
11
15
|
}
|
|
12
16
|
|
|
17
|
+
if (app.isTranspileModuleFile(normalizedFilePath)) return false;
|
|
18
|
+
|
|
13
19
|
return true;
|
|
14
20
|
};
|
|
15
21
|
|
|
16
22
|
const getSwcTarget = (side: TAppSide) => (side === 'client' ? 'es2022' : 'es2021');
|
|
17
23
|
|
|
18
|
-
module.exports = ({ side, dev }: TScriptRuleOptions): RuleSetRule[] => {
|
|
24
|
+
module.exports = ({ app, side, dev }: TScriptRuleOptions): RuleSetRule[] => {
|
|
19
25
|
return [
|
|
20
26
|
{
|
|
21
27
|
loader: 'builtin:swc-loader',
|
|
22
28
|
type: 'javascript/auto',
|
|
23
|
-
exclude: shouldExcludeNodeModule,
|
|
29
|
+
exclude: (filePath: string) => shouldExcludeNodeModule(app, filePath),
|
|
24
30
|
options: {
|
|
25
31
|
sourceMaps: true,
|
|
26
32
|
jsc: {
|
package/cli/compiler/index.ts
CHANGED
|
@@ -141,12 +141,13 @@ export default class Compiler {
|
|
|
141
141
|
if (!this.refreshingGeneratedArtifacts) {
|
|
142
142
|
this.refreshingGeneratedArtifacts = (async () => {
|
|
143
143
|
const services = generateServiceArtifacts();
|
|
144
|
-
const controllers = generateControllerArtifacts();
|
|
144
|
+
const { connectedProjects, controllers } = await generateControllerArtifacts();
|
|
145
145
|
const commands = generateCommandArtifacts();
|
|
146
146
|
const { clientRoutes, serverRoutes, layouts } = generateRoutingArtifacts();
|
|
147
147
|
|
|
148
148
|
writeCurrentProteumManifest({
|
|
149
149
|
services,
|
|
150
|
+
connectedProjects,
|
|
150
151
|
controllers,
|
|
151
152
|
commands,
|
|
152
153
|
routes: { client: clientRoutes, server: serverRoutes },
|
|
@@ -82,6 +82,7 @@ export default function createCompiler(
|
|
|
82
82
|
const outputPath = app.outputPath(outputTarget);
|
|
83
83
|
const installedCoreRoot = app.paths.root + '/node_modules/proteum';
|
|
84
84
|
const frameworkRoots = [cli.paths.core.root, installedCoreRoot];
|
|
85
|
+
const transpileModuleDirectories = app.transpileModuleDirectories;
|
|
85
86
|
|
|
86
87
|
const commonConfig = createCommonConfig(app, 'server', mode, outputTarget);
|
|
87
88
|
const { aliases } = app.aliases.server.forWebpack({ modulesPath: app.paths.root + '/node_modules' });
|
|
@@ -140,6 +141,7 @@ export default function createCompiler(
|
|
|
140
141
|
// Aliased modules
|
|
141
142
|
app.aliases.server.containsAlias(request) ||
|
|
142
143
|
// TODO: proteum.conf: compile: include
|
|
144
|
+
app.isTranspileModuleRequest(request) ||
|
|
143
145
|
// Compile proteum modules
|
|
144
146
|
request.startsWith('proteum') ||
|
|
145
147
|
// React-based UI packages must pass through the alias layer on the server,
|
|
@@ -193,6 +195,7 @@ export default function createCompiler(
|
|
|
193
195
|
...frameworkRoots.map((rootPath) => rootPath + '/client'),
|
|
194
196
|
...frameworkRoots.map((rootPath) => rootPath + '/common'),
|
|
195
197
|
...frameworkRoots.map((rootPath) => rootPath + '/server'),
|
|
198
|
+
...transpileModuleDirectories,
|
|
196
199
|
|
|
197
200
|
// Complle 5HTP modules so they can refer to the framework instance and aliases
|
|
198
201
|
// Temp disabled because compile issue on vercel
|
|
@@ -12,11 +12,15 @@ export const proteumCommandNames = [
|
|
|
12
12
|
'typecheck',
|
|
13
13
|
'lint',
|
|
14
14
|
'check',
|
|
15
|
+
'connect',
|
|
15
16
|
'doctor',
|
|
16
17
|
'explain',
|
|
18
|
+
'diagnose',
|
|
19
|
+
'perf',
|
|
17
20
|
'trace',
|
|
18
21
|
'command',
|
|
19
22
|
'session',
|
|
23
|
+
'verify',
|
|
20
24
|
] as const;
|
|
21
25
|
|
|
22
26
|
export type TProteumCommandName = (typeof proteumCommandNames)[number];
|
|
@@ -49,7 +53,7 @@ export const proteumRecommendedFlow: TRow[] = [
|
|
|
49
53
|
export const proteumCommandGroups: Array<{ title: string; names: TProteumCommandName[] }> = [
|
|
50
54
|
{ title: 'Daily workflow', names: ['dev', 'refresh', 'build'] },
|
|
51
55
|
{ title: 'Quality gates', names: ['typecheck', 'lint', 'check'] },
|
|
52
|
-
{ title: 'Manifest and contracts', names: ['doctor', 'explain', 'trace', 'command', 'session'] },
|
|
56
|
+
{ title: 'Manifest and contracts', names: ['connect', 'doctor', 'explain', 'diagnose', 'perf', 'trace', 'command', 'session', 'verify'] },
|
|
53
57
|
{ title: 'Project scaffolding', names: ['init', 'create'] },
|
|
54
58
|
];
|
|
55
59
|
|
|
@@ -97,7 +101,7 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
|
|
|
97
101
|
],
|
|
98
102
|
notes: [
|
|
99
103
|
'Page scaffolds write `client/pages/**/index.tsx` and default the route path from the logical target path unless `--route` is provided.',
|
|
100
|
-
'Service scaffolds create `server/services/**/index.ts`,
|
|
104
|
+
'Service scaffolds create `server/services/**/index.ts`, a typed config export under `server/config/*.ts`, and then try to register the new root service in `server/index.ts`.',
|
|
101
105
|
'Use `--dry-run --json` when an agent needs a machine-readable plan before writing files.',
|
|
102
106
|
],
|
|
103
107
|
status: 'experimental',
|
|
@@ -190,26 +194,47 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
|
|
|
190
194
|
notes: ['This command executes refresh, typecheck, then lint in that order.'],
|
|
191
195
|
status: 'stable',
|
|
192
196
|
},
|
|
197
|
+
connect: {
|
|
198
|
+
name: 'connect',
|
|
199
|
+
category: 'Manifest and contracts',
|
|
200
|
+
summary: 'Inspect connected-project config, cached contracts, and imported controllers.',
|
|
201
|
+
usage: 'proteum connect [--controllers] [--json] [--strict]',
|
|
202
|
+
bestFor:
|
|
203
|
+
'Auditing the current app connect setup without manually stitching together refresh, explain, env inspection, and contract checks.',
|
|
204
|
+
examples: [
|
|
205
|
+
{ description: 'Print a human-readable connected-project summary', command: 'proteum connect' },
|
|
206
|
+
{ description: 'Include imported connected controllers', command: 'proteum connect --controllers' },
|
|
207
|
+
{ description: 'Emit machine-readable connect output', command: 'proteum connect --json' },
|
|
208
|
+
{ description: 'Fail when connect diagnostics exist', command: 'proteum connect --strict' },
|
|
209
|
+
],
|
|
210
|
+
notes: [
|
|
211
|
+
'Proteum refreshes generated typings before reading the connect manifest state.',
|
|
212
|
+
'This command inspects explicit `proteum.config.ts` connected sources and URLs, cached `.proteum/connected/*.json` files, and imported connected controllers.',
|
|
213
|
+
'`--strict` is intended for CI or framework validation when connected contracts must be present and usable.',
|
|
214
|
+
],
|
|
215
|
+
status: 'stable',
|
|
216
|
+
},
|
|
193
217
|
doctor: {
|
|
194
218
|
name: 'doctor',
|
|
195
219
|
category: 'Manifest and contracts',
|
|
196
220
|
summary: 'Inspect the generated Proteum manifest diagnostics.',
|
|
197
|
-
usage: 'proteum doctor [--json] [--strict]',
|
|
221
|
+
usage: 'proteum doctor [--contracts] [--json] [--strict]',
|
|
198
222
|
bestFor:
|
|
199
223
|
'Auditing manifest warnings and errors, especially in CI or when route/controller generation behaves unexpectedly.',
|
|
200
224
|
examples: [
|
|
201
225
|
{ description: 'Print a human-readable diagnostic summary', command: 'proteum doctor' },
|
|
226
|
+
{ description: 'Inspect missing generated contracts and source files', command: 'proteum doctor --contracts' },
|
|
202
227
|
{ description: 'Fail if any diagnostics exist', command: 'proteum doctor --strict' },
|
|
203
228
|
{ description: 'Emit machine-readable diagnostics', command: 'proteum doctor --json' },
|
|
204
229
|
],
|
|
205
|
-
notes: ['`--strict` is intended for CI and pre-release verification.'],
|
|
230
|
+
notes: ['`--strict` is intended for CI and pre-release verification.', '`--contracts` checks manifest-owned source files and expected generated artifacts on disk.'],
|
|
206
231
|
status: 'stable',
|
|
207
232
|
},
|
|
208
233
|
explain: {
|
|
209
234
|
name: 'explain',
|
|
210
235
|
category: 'Manifest and contracts',
|
|
211
236
|
summary: 'Explain the generated Proteum manifest.',
|
|
212
|
-
usage: 'proteum explain [--all|--app|--conventions|--env|--services|--controllers|--commands|--routes|--layouts|--diagnostics] [--json]',
|
|
237
|
+
usage: 'proteum explain [owner <query>] [--all|--app|--conventions|--env|--connected|--services|--controllers|--commands|--routes|--layouts|--diagnostics] [--json]',
|
|
213
238
|
bestFor:
|
|
214
239
|
'Inspecting how source files became generated routes, controllers, commands, layouts, services, and diagnostics without reading compiler internals.',
|
|
215
240
|
examples: [
|
|
@@ -218,11 +243,64 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
|
|
|
218
243
|
description: 'Inspect generated routes, controllers, and commands together',
|
|
219
244
|
command: 'proteum explain --routes --controllers --commands',
|
|
220
245
|
},
|
|
246
|
+
{
|
|
247
|
+
description: 'Inspect configured connected projects and imported controllers',
|
|
248
|
+
command: 'proteum explain --connected --controllers',
|
|
249
|
+
},
|
|
250
|
+
{ description: 'Resolve the most likely manifest owner for a path or file', command: 'proteum explain owner /api/Auth/CurrentUser' },
|
|
221
251
|
{ description: 'Emit the selected manifest sections as JSON', command: 'proteum explain --routes --json' },
|
|
222
252
|
],
|
|
223
|
-
notes: [
|
|
253
|
+
notes: [
|
|
254
|
+
'Legacy positional section selection remains supported, for example `proteum explain routes services`.',
|
|
255
|
+
'`proteum explain owner <query>` ranks matching routes, controllers, services, commands, layouts, and diagnostics from the manifest.',
|
|
256
|
+
'Connected projects are emitted from explicit `proteum.config.ts` `connect.<Namespace>.*` values plus the resolved connected contract.',
|
|
257
|
+
],
|
|
224
258
|
status: 'stable',
|
|
225
259
|
},
|
|
260
|
+
diagnose: {
|
|
261
|
+
name: 'diagnose',
|
|
262
|
+
category: 'Manifest and contracts',
|
|
263
|
+
summary: 'Combine owner lookup, doctor output, contract checks, traces, and server logs into one report.',
|
|
264
|
+
usage: 'proteum diagnose [<query>] [--hit <path>] [--method <verb>] [--data-json <json>] [--session-email <email>] [--session-role <role>] [--port <port>|--url <baseUrl>] [--json]',
|
|
265
|
+
bestFor:
|
|
266
|
+
'Collapsing the usual explain + doctor + trace + session + server log loop into one structured debugging pass.',
|
|
267
|
+
examples: [
|
|
268
|
+
{ description: 'Diagnose the latest matching route trace on the running dev server', command: 'proteum diagnose /domains' },
|
|
269
|
+
{ description: 'Arm a deep trace, mint an admin session, hit a protected page once, then diagnose it', command: 'proteum diagnose /godmode/users --hit /godmode/users --session-email god@example.com --session-role GOD' },
|
|
270
|
+
{ description: 'Diagnose an API call with a JSON payload', command: 'proteum diagnose /api/Auth/CurrentUser --hit /api/Auth/CurrentUser --method POST --data-json "{}"' },
|
|
271
|
+
],
|
|
272
|
+
notes: [
|
|
273
|
+
'This command talks to the running app over the dev-only diagnostics, trace, and session endpoints.',
|
|
274
|
+
'When `--hit` is omitted, Proteum diagnoses the latest matching request trace if one already exists.',
|
|
275
|
+
],
|
|
276
|
+
status: 'experimental',
|
|
277
|
+
},
|
|
278
|
+
perf: {
|
|
279
|
+
name: 'perf',
|
|
280
|
+
category: 'Manifest and contracts',
|
|
281
|
+
summary: 'Inspect shared performance rollups built from live request traces on a running Proteum dev server.',
|
|
282
|
+
usage: 'proteum perf [top|request <requestId|path>|compare|memory] [--since <window>] [--baseline <window>] [--target <window>] [--group-by <path|route|controller>] [--limit <n>] [--port <port>|--url <baseUrl>] [--json]',
|
|
283
|
+
bestFor:
|
|
284
|
+
'Finding the routes or controllers with the biggest response-time, CPU, SQL, render, and memory impact without manually stitching traces together.',
|
|
285
|
+
examples: [
|
|
286
|
+
{ description: 'Show the hottest paths for the current day', command: 'proteum perf top --since today' },
|
|
287
|
+
{ description: 'Inspect one traced request or the latest request for a path', command: 'proteum perf request /domains' },
|
|
288
|
+
{
|
|
289
|
+
description: 'Compare today against yesterday by route',
|
|
290
|
+
command: 'proteum perf compare --baseline yesterday --target today --group-by route',
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
description: 'Rank memory growth by controller over the last hour',
|
|
294
|
+
command: 'proteum perf memory --since 1h --group-by controller',
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
notes: [
|
|
298
|
+
'Perf data is derived from the same dev-only request trace buffer used by `proteum trace` and the profiler.',
|
|
299
|
+
'Window values accept `1h`, `6h`, `24h`, `today`, `yesterday`, or an ISO timestamp.',
|
|
300
|
+
'Older traces captured before the perf runtime metrics were added may not include CPU or memory deltas.',
|
|
301
|
+
],
|
|
302
|
+
status: 'experimental',
|
|
303
|
+
},
|
|
226
304
|
trace: {
|
|
227
305
|
name: 'trace',
|
|
228
306
|
category: 'Manifest and contracts',
|
|
@@ -300,11 +378,36 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
|
|
|
300
378
|
],
|
|
301
379
|
status: 'experimental',
|
|
302
380
|
},
|
|
381
|
+
verify: {
|
|
382
|
+
name: 'verify',
|
|
383
|
+
category: 'Manifest and contracts',
|
|
384
|
+
summary: 'Validate framework changes against CrossPath, Unique Domains Product, and Unique Domains Website.',
|
|
385
|
+
usage: 'proteum verify [framework-change] [--crosspath <path>] [--product <path>] [--website <path>] [--crosspath-port <port>] [--product-port <port>] [--website-port <port>] [--route <path>] [--json]',
|
|
386
|
+
bestFor:
|
|
387
|
+
'Framework-repo smoke validation when Proteum changes must be exercised against CrossPath and the website -> product connected-project flow before review.',
|
|
388
|
+
examples: [
|
|
389
|
+
{
|
|
390
|
+
description: 'Run the default framework smoke verification against the reference apps',
|
|
391
|
+
command: 'proteum verify framework-change',
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
description: 'Load a specific route in the website validation pass',
|
|
395
|
+
command: 'proteum verify framework-change --route /domains',
|
|
396
|
+
},
|
|
397
|
+
],
|
|
398
|
+
notes: [
|
|
399
|
+
'When a reference app is already running on the requested port, Proteum reuses it instead of spawning a new `proteum dev` process.',
|
|
400
|
+
'When Proteum spawns the website reference app, it sets the env values consumed by the website `proteum.config.ts` for the product internal and public URLs.',
|
|
401
|
+
'This command is intended for the framework repo and will be most useful where the reference app paths exist locally.',
|
|
402
|
+
],
|
|
403
|
+
status: 'experimental',
|
|
404
|
+
},
|
|
303
405
|
};
|
|
304
406
|
|
|
305
407
|
export const isLikelyProteumAppRoot = (workdir: string) =>
|
|
306
408
|
fs.existsSync(path.join(workdir, 'package.json')) &&
|
|
307
|
-
fs.existsSync(path.join(workdir, 'identity.
|
|
409
|
+
fs.existsSync(path.join(workdir, 'identity.config.ts')) &&
|
|
410
|
+
fs.existsSync(path.join(workdir, 'proteum.config.ts')) &&
|
|
308
411
|
fs.existsSync(path.join(workdir, 'client')) &&
|
|
309
412
|
fs.existsSync(path.join(workdir, 'server'));
|
|
310
413
|
|
|
@@ -18,24 +18,30 @@ export const renderDevSession = async ({
|
|
|
18
18
|
appRoot,
|
|
19
19
|
routerPort,
|
|
20
20
|
devEventPort,
|
|
21
|
+
connectedProjects,
|
|
22
|
+
proteumVersion,
|
|
21
23
|
}: {
|
|
22
24
|
appName: string;
|
|
23
25
|
appRoot: string;
|
|
24
26
|
routerPort: number;
|
|
25
27
|
devEventPort: number;
|
|
28
|
+
connectedProjects?: Array<{ namespace: string; urlInternal: string }>;
|
|
29
|
+
proteumVersion: string;
|
|
26
30
|
}) =>
|
|
27
31
|
[
|
|
28
32
|
await renderInk(({ Box, Text }) => {
|
|
29
33
|
const createElement = React.createElement;
|
|
30
34
|
const wordmark = ProteumWordmark.map((line) =>
|
|
31
|
-
createElement(Text, { key: line, bold: true, color: '
|
|
35
|
+
createElement(Text, { key: line, bold: true, color: 'blue' }, line),
|
|
32
36
|
);
|
|
37
|
+
const versionLabel = proteumVersion ? `v${proteumVersion}` : '';
|
|
33
38
|
|
|
34
39
|
return createElement(
|
|
35
40
|
Box,
|
|
36
|
-
{ borderStyle: 'round', borderColor: '
|
|
37
|
-
createElement(Text, { bold: true, backgroundColor: '
|
|
41
|
+
{ borderStyle: 'round', borderColor: 'blue', paddingX: 2, paddingY: 0, flexDirection: 'column' },
|
|
42
|
+
createElement(Text, { bold: true, backgroundColor: 'blue', color: 'white' }, ' WELCOME TO '),
|
|
38
43
|
createElement(Box, { flexDirection: 'column' }, ...wordmark),
|
|
44
|
+
versionLabel ? createElement(Text, { bold: true, color: 'blue' }, versionLabel) : null,
|
|
39
45
|
createElement(Text, { dimColor: true }, ProteumTagline),
|
|
40
46
|
);
|
|
41
47
|
}),
|
|
@@ -45,6 +51,14 @@ export const renderDevSession = async ({
|
|
|
45
51
|
{ label: 'root', value: appRoot },
|
|
46
52
|
{ label: 'router', value: `http://localhost:${routerPort}` },
|
|
47
53
|
{ label: 'hmr', value: `http://localhost:${devEventPort}/__proteum_hmr` },
|
|
54
|
+
...(connectedProjects && connectedProjects.length > 0
|
|
55
|
+
? connectedProjects.map((connectedProject) => ({
|
|
56
|
+
label: `connect ${connectedProject.namespace}`,
|
|
57
|
+
value: connectedProject.urlInternal,
|
|
58
|
+
}))
|
|
59
|
+
: []),
|
|
60
|
+
{ label: 'diagnose', value: `proteum diagnose / --port ${routerPort}` },
|
|
61
|
+
{ label: 'perf', value: `proteum perf top --port ${routerPort}` },
|
|
48
62
|
{ label: 'trace', value: `proteum trace latest --port ${routerPort}` },
|
|
49
63
|
{ label: 'trace deep', value: `proteum trace arm --capture deep --port ${routerPort}` },
|
|
50
64
|
{ label: 'hotkeys', value: 'Ctrl+R reload, Ctrl+C stop' },
|
|
@@ -57,10 +71,12 @@ export const renderServerReadyBanner = async ({
|
|
|
57
71
|
appName,
|
|
58
72
|
publicUrl,
|
|
59
73
|
routerPort,
|
|
74
|
+
connectedProjectsCount,
|
|
60
75
|
}: {
|
|
61
76
|
appName: string;
|
|
62
77
|
publicUrl: string;
|
|
63
78
|
routerPort: number;
|
|
79
|
+
connectedProjectsCount?: number;
|
|
64
80
|
}) =>
|
|
65
81
|
renderInk(({ Box, Text }) => {
|
|
66
82
|
const createElement = React.createElement;
|
|
@@ -68,10 +84,19 @@ export const renderServerReadyBanner = async ({
|
|
|
68
84
|
return createElement(
|
|
69
85
|
Box,
|
|
70
86
|
{ borderStyle: 'round', borderColor: 'green', paddingX: 2, paddingY: 0, flexDirection: 'column' },
|
|
71
|
-
createElement(Text, { bold: true, backgroundColor: 'green', color: '
|
|
87
|
+
createElement(Text, { bold: true, backgroundColor: 'green', color: 'white' }, ' SERVER READY '),
|
|
72
88
|
createElement(Text, { bold: true, color: 'green' }, appName),
|
|
73
89
|
createElement(Text, { bold: true }, publicUrl),
|
|
74
90
|
createElement(Text, { dimColor: true }, 'SSR server is listening for requests and hot reloads.'),
|
|
91
|
+
connectedProjectsCount
|
|
92
|
+
? createElement(
|
|
93
|
+
Text,
|
|
94
|
+
{ dimColor: true },
|
|
95
|
+
`Connected projects: ${connectedProjectsCount}`,
|
|
96
|
+
)
|
|
97
|
+
: null,
|
|
98
|
+
createElement(Text, { dimColor: true }, `Diagnose /: proteum diagnose / --port ${routerPort}`),
|
|
99
|
+
createElement(Text, { dimColor: true }, `Perf top: proteum perf top --port ${routerPort}`),
|
|
75
100
|
createElement(Text, { dimColor: true }, `Trace latest: proteum trace latest --port ${routerPort}`),
|
|
76
101
|
);
|
|
77
102
|
});
|
|
@@ -82,8 +107,8 @@ export const renderDevShutdownBanner = async () =>
|
|
|
82
107
|
|
|
83
108
|
return createElement(
|
|
84
109
|
Box,
|
|
85
|
-
{ borderStyle: 'round', borderColor: '
|
|
86
|
-
createElement(Text, { bold: true, backgroundColor: '
|
|
87
|
-
createElement(Text, { bold: true, color: '
|
|
110
|
+
{ borderStyle: 'round', borderColor: 'blue', paddingX: 2, paddingY: 0, flexDirection: 'column' },
|
|
111
|
+
createElement(Text, { bold: true, backgroundColor: 'blue', color: 'white' }, ' SHUTTING DOWN '),
|
|
112
|
+
createElement(Text, { bold: true, color: 'blue' }, 'Thank you for developping with Proteum'),
|
|
88
113
|
);
|
|
89
114
|
});
|