proteum 2.1.0 → 2.1.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 (83) hide show
  1. package/AGENTS.md +44 -98
  2. package/README.md +121 -7
  3. package/agents/framework/AGENTS.md +133 -886
  4. package/agents/project/AGENTS.md +70 -127
  5. package/agents/project/client/AGENTS.md +22 -93
  6. package/agents/project/client/pages/AGENTS.md +24 -26
  7. package/agents/project/server/routes/AGENTS.md +10 -8
  8. package/agents/project/server/services/AGENTS.md +22 -159
  9. package/agents/project/tests/AGENTS.md +11 -8
  10. package/cli/app/config.ts +7 -20
  11. package/cli/bin.js +8 -0
  12. package/cli/commands/command.ts +243 -0
  13. package/cli/commands/commandLocalRunner.js +198 -0
  14. package/cli/commands/deploy/web.ts +1 -2
  15. package/cli/commands/dev.ts +96 -1
  16. package/cli/commands/doctor.ts +8 -74
  17. package/cli/commands/explain.ts +8 -186
  18. package/cli/commands/trace.ts +228 -0
  19. package/cli/compiler/artifacts/commands.ts +217 -0
  20. package/cli/compiler/artifacts/manifest.ts +35 -21
  21. package/cli/compiler/artifacts/services.ts +300 -1
  22. package/cli/compiler/client/index.ts +43 -8
  23. package/cli/compiler/common/commands.ts +175 -0
  24. package/cli/compiler/common/index.ts +1 -1
  25. package/cli/compiler/common/proteumManifest.ts +15 -114
  26. package/cli/compiler/index.ts +25 -2
  27. package/cli/compiler/server/index.ts +31 -6
  28. package/cli/paths.ts +16 -1
  29. package/cli/presentation/commands.ts +59 -5
  30. package/cli/presentation/devSession.ts +5 -0
  31. package/cli/runtime/commands.ts +60 -1
  32. package/cli/tsconfig.json +4 -1
  33. package/cli/utils/check.ts +1 -1
  34. package/client/app/component.tsx +13 -9
  35. package/client/dev/profiler/index.tsx +1511 -0
  36. package/client/dev/profiler/noop.tsx +5 -0
  37. package/client/dev/profiler/runtime.noop.ts +116 -0
  38. package/client/dev/profiler/runtime.ts +840 -0
  39. package/client/services/router/components/router.tsx +30 -2
  40. package/client/services/router/index.tsx +27 -3
  41. package/client/services/router/request/api.ts +133 -17
  42. package/commands/proteum/diagnostics.ts +11 -0
  43. package/common/dev/commands.ts +50 -0
  44. package/common/dev/diagnostics.ts +298 -0
  45. package/common/dev/profiler.ts +91 -0
  46. package/common/dev/proteumManifest.ts +135 -0
  47. package/common/dev/requestTrace.ts +109 -0
  48. package/common/env/proteumEnv.ts +284 -0
  49. package/common/router/index.ts +4 -22
  50. package/docs/dev-commands.md +86 -0
  51. package/docs/request-tracing.md +122 -0
  52. package/package.json +1 -2
  53. package/server/app/commands.ts +35 -370
  54. package/server/app/commandsManager.ts +393 -0
  55. package/server/app/container/config.ts +11 -49
  56. package/server/app/container/console/index.ts +2 -3
  57. package/server/app/container/index.ts +5 -2
  58. package/server/app/container/trace/index.ts +364 -0
  59. package/server/app/devCommands.ts +192 -0
  60. package/server/app/devDiagnostics.ts +53 -0
  61. package/server/app/index.ts +27 -4
  62. package/server/services/cron/CronTask.ts +73 -5
  63. package/server/services/cron/index.ts +34 -11
  64. package/server/services/fetch/index.ts +3 -10
  65. package/server/services/prisma/index.ts +66 -4
  66. package/server/services/router/http/index.ts +151 -0
  67. package/server/services/router/index.ts +200 -12
  68. package/server/services/router/request/api.ts +30 -1
  69. package/server/services/router/response/index.ts +83 -10
  70. package/server/services/router/response/page/document.tsx +16 -0
  71. package/server/services/router/response/page/index.tsx +27 -1
  72. package/skills/clean-project-code/SKILL.md +7 -2
  73. package/test-results/.last-run.json +4 -0
  74. package/types/aliases.d.ts +6 -0
  75. package/types/global/utils.d.ts +7 -14
  76. package/Rte.zip +0 -0
  77. package/agents/project/agents.md.zip +0 -0
  78. package/doc/TODO.md +0 -71
  79. package/doc/front/router.md +0 -27
  80. package/doc/workspace/workspace.png +0 -0
  81. package/doc/workspace/workspace2.png +0 -0
  82. package/doc/workspace/workspace_26.01.22.png +0 -0
  83. package/server/services/router/http/session.ts.old +0 -40
@@ -13,6 +13,8 @@ export const proteumCommandNames = [
13
13
  'check',
14
14
  'doctor',
15
15
  'explain',
16
+ 'trace',
17
+ 'command',
16
18
  ] as const;
17
19
 
18
20
  export type TProteumCommandName = (typeof proteumCommandNames)[number];
@@ -45,7 +47,7 @@ export const proteumRecommendedFlow: TRow[] = [
45
47
  export const proteumCommandGroups: Array<{ title: string; names: TProteumCommandName[] }> = [
46
48
  { title: 'Daily workflow', names: ['dev', 'refresh', 'build'] },
47
49
  { title: 'Quality gates', names: ['typecheck', 'lint', 'check'] },
48
- { title: 'Manifest and contracts', names: ['doctor', 'explain'] },
50
+ { title: 'Manifest and contracts', names: ['doctor', 'explain', 'trace', 'command'] },
49
51
  { title: 'Project scaffolding', names: ['init'] },
50
52
  ];
51
53
 
@@ -170,20 +172,72 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
170
172
  name: 'explain',
171
173
  category: 'Manifest and contracts',
172
174
  summary: 'Explain the generated Proteum manifest.',
173
- usage: 'proteum explain [--all|--app|--conventions|--env|--services|--controllers|--routes|--layouts|--diagnostics] [--json]',
175
+ usage: 'proteum explain [--all|--app|--conventions|--env|--services|--controllers|--commands|--routes|--layouts|--diagnostics] [--json]',
174
176
  bestFor:
175
- 'Inspecting how source files became generated routes, controllers, layouts, services, and diagnostics without reading compiler internals.',
177
+ 'Inspecting how source files became generated routes, controllers, commands, layouts, services, and diagnostics without reading compiler internals.',
176
178
  examples: [
177
179
  { description: 'Show the default human summary', command: 'proteum explain' },
178
180
  {
179
- description: 'Inspect generated routes and controllers together',
180
- command: 'proteum explain --routes --controllers',
181
+ description: 'Inspect generated routes, controllers, and commands together',
182
+ command: 'proteum explain --routes --controllers --commands',
181
183
  },
182
184
  { description: 'Emit the selected manifest sections as JSON', command: 'proteum explain --routes --json' },
183
185
  ],
184
186
  notes: ['Legacy positional section selection remains supported, for example `proteum explain routes services`.'],
185
187
  status: 'stable',
186
188
  },
189
+ trace: {
190
+ name: 'trace',
191
+ category: 'Manifest and contracts',
192
+ summary: 'Inspect live in-memory request traces from a running Proteum dev server.',
193
+ usage: 'proteum trace [latest|show <requestId>|requests|arm|export <requestId>] [--port <port>|--url <baseUrl>] [--json]',
194
+ bestFor:
195
+ 'Debugging route resolution, context creation, SSR payloads, renders, and runtime errors without attaching a debugger.',
196
+ examples: [
197
+ { description: 'Show the latest request trace', command: 'proteum trace latest' },
198
+ { description: 'List recent trace summaries', command: 'proteum trace requests' },
199
+ { description: 'Arm the next request for deep capture', command: 'proteum trace arm --capture deep' },
200
+ { description: 'Export a request trace to disk', command: 'proteum trace export <requestId>' },
201
+ { description: 'Target a custom dev base URL directly', command: 'proteum trace latest --url http://127.0.0.1:3010' },
202
+ ],
203
+ notes: [
204
+ 'This command talks to the running app over the dev-only `__proteum/trace` HTTP endpoints.',
205
+ 'Traces are stored in a bounded in-memory buffer with payload summarization and sensitive-field redaction.',
206
+ 'Use `--port` when the app is not running on the router port declared in `PORT`, or `--url` when the host itself is non-standard.',
207
+ ],
208
+ status: 'experimental',
209
+ },
210
+ command: {
211
+ name: 'command',
212
+ category: 'Manifest and contracts',
213
+ summary: 'Run a dev-only app command from /commands or against an existing dev instance.',
214
+ usage: 'proteum command <path> [--port <port>|--url <baseUrl>] [--json]',
215
+ bestFor:
216
+ 'Internal testing, debugging, and one-off service execution that should not be exposed as a normal controller or route.',
217
+ examples: [
218
+ {
219
+ description: 'Run a local command through a temporary bundled dev server',
220
+ command: 'proteum command proteum/diagnostics/ping',
221
+ },
222
+ {
223
+ description: 'Run a command against an existing dev server',
224
+ command: 'proteum command proteum/diagnostics/ping --port 3101',
225
+ },
226
+ {
227
+ description: 'Emit the command execution as JSON',
228
+ command: 'proteum command proteum/diagnostics/ping --json',
229
+ },
230
+ ],
231
+ notes: [
232
+ 'Commands live under `./commands/**/*.ts` and default-export a class that extends `{ Commands }` from `@server/app/commands`.',
233
+ 'Methods are addressed by file path plus method name, mirroring controller path generation.',
234
+ 'Proteum creates `./commands/tsconfig.json` and `.proteum/server/commands.d.ts` so `/commands` gets a command-specific alias/type project.',
235
+ 'Prefer `extends Commands` directly inside `/commands`; importing the app class is still supported through a generated command-only `@/server/index` type alias.',
236
+ 'Without `--port` or `--url`, Proteum refreshes generated artifacts, builds the dev output, starts a temporary local dev server, runs the command, and exits.',
237
+ 'With `--port` or `--url`, Proteum talks to the running app over the dev-only `__proteum/commands` HTTP endpoints.',
238
+ ],
239
+ status: 'experimental',
240
+ },
187
241
  };
188
242
 
189
243
  export const isLikelyProteumAppRoot = (workdir: string) =>
@@ -43,6 +43,8 @@ export const renderDevSession = async ({
43
43
  { label: 'root', value: appRoot },
44
44
  { label: 'router', value: `http://localhost:${routerPort}` },
45
45
  { label: 'hmr', value: `http://localhost:${devEventPort}/__proteum_hmr` },
46
+ { label: 'trace', value: `proteum trace latest --port ${routerPort}` },
47
+ { label: 'trace deep', value: `proteum trace arm --capture deep --port ${routerPort}` },
46
48
  { label: 'hotkeys', value: 'Ctrl+R reload, Ctrl+C stop' },
47
49
  ],
48
50
  { minLabelWidth: 12, maxLabelWidth: 12 },
@@ -52,9 +54,11 @@ export const renderDevSession = async ({
52
54
  export const renderServerReadyBanner = async ({
53
55
  appName,
54
56
  publicUrl,
57
+ routerPort,
55
58
  }: {
56
59
  appName: string;
57
60
  publicUrl: string;
61
+ routerPort: number;
58
62
  }) =>
59
63
  renderInk(({ Box, Text }) => {
60
64
  const createElement = React.createElement;
@@ -66,5 +70,6 @@ export const renderServerReadyBanner = async ({
66
70
  createElement(Text, { bold: true, color: 'green' }, appName),
67
71
  createElement(Text, { bold: true }, publicUrl),
68
72
  createElement(Text, { dimColor: true }, 'SSR server is listening for requests and hot reloads.'),
73
+ createElement(Text, { dimColor: true }, `Trace latest: proteum trace latest --port ${routerPort}`),
69
74
  );
70
75
  });
@@ -153,6 +153,7 @@ class ExplainCommand extends ProteumCommand {
153
153
  public env = Option.Boolean('--env', false, { description: 'Include the env section.' });
154
154
  public services = Option.Boolean('--services', false, { description: 'Include the services section.' });
155
155
  public controllers = Option.Boolean('--controllers', false, { description: 'Include the controllers section.' });
156
+ public commands = Option.Boolean('--commands', false, { description: 'Include the commands section.' });
156
157
  public routes = Option.Boolean('--routes', false, { description: 'Include the routes section.' });
157
158
  public layouts = Option.Boolean('--layouts', false, { description: 'Include the layouts section.' });
158
159
  public diagnostics = Option.Boolean('--diagnostics', false, {
@@ -169,6 +170,7 @@ class ExplainCommand extends ProteumCommand {
169
170
  env: this.env,
170
171
  services: this.services,
171
172
  controllers: this.controllers,
173
+ commands: this.commands,
172
174
  routes: this.routes,
173
175
  layouts: this.layouts,
174
176
  diagnostics: this.diagnostics,
@@ -177,7 +179,7 @@ class ExplainCommand extends ProteumCommand {
177
179
  applyLegacyBooleanArgs(
178
180
  'explain',
179
181
  this.legacyArgs,
180
- ['json', 'all', 'app', 'conventions', 'env', 'services', 'controllers', 'routes', 'layouts', 'diagnostics'],
182
+ ['json', 'all', 'app', 'conventions', 'env', 'services', 'controllers', 'commands', 'routes', 'layouts', 'diagnostics'],
181
183
  args,
182
184
  );
183
185
  this.setCliArgs(args);
@@ -185,6 +187,59 @@ class ExplainCommand extends ProteumCommand {
185
187
  }
186
188
  }
187
189
 
190
+ class TraceCommand extends ProteumCommand {
191
+ public static paths = [['trace']];
192
+
193
+ public static usage = buildUsage('trace');
194
+
195
+ public port = Option.String('--port', { description: 'Override the router port used to query the running dev server.' });
196
+ public url = Option.String('--url', { description: 'Override the full base URL used to query the running dev server.' });
197
+ public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
198
+ public capture = Option.String('--capture', { description: 'Capture mode used by `proteum trace arm`.' });
199
+ public output = Option.String('--output', { description: 'Output filepath used by `proteum trace export`.' });
200
+ public args = Option.Rest();
201
+
202
+ public async execute() {
203
+ const [action = 'latest', id = ''] = this.args;
204
+
205
+ this.setCliArgs({
206
+ action,
207
+ id,
208
+ port: this.port ?? '',
209
+ url: this.url ?? '',
210
+ json: this.json,
211
+ capture: this.capture ?? '',
212
+ output: this.output ?? '',
213
+ });
214
+
215
+ await runCommandModule(() => import('../commands/trace'));
216
+ }
217
+ }
218
+
219
+ class CommandCommand extends ProteumCommand {
220
+ public static paths = [['command']];
221
+
222
+ public static usage = buildUsage('command');
223
+
224
+ public port = Option.String('--port', { description: 'Target an existing dev server on the given port.' });
225
+ public url = Option.String('--url', { description: 'Target an existing dev server at the given base URL.' });
226
+ public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
227
+ public args = Option.Rest();
228
+
229
+ public async execute() {
230
+ const [path = ''] = this.args;
231
+
232
+ this.setCliArgs({
233
+ path,
234
+ port: this.port ?? '',
235
+ url: this.url ?? '',
236
+ json: this.json,
237
+ });
238
+
239
+ await runCommandModule(() => import('../commands/command'));
240
+ }
241
+ }
242
+
188
243
  export const registeredCommands = {
189
244
  init: InitCommand,
190
245
  dev: DevCommand,
@@ -195,6 +250,8 @@ export const registeredCommands = {
195
250
  check: CheckCommand,
196
251
  doctor: DoctorCommand,
197
252
  explain: ExplainCommand,
253
+ trace: TraceCommand,
254
+ command: CommandCommand,
198
255
  } as const;
199
256
 
200
257
  export const createCli = (version: string) => {
@@ -216,6 +273,8 @@ export const createCli = (version: string) => {
216
273
  clipanion.register(CheckCommand);
217
274
  clipanion.register(DoctorCommand);
218
275
  clipanion.register(ExplainCommand);
276
+ clipanion.register(TraceCommand);
277
+ clipanion.register(CommandCommand);
219
278
 
220
279
  return clipanion;
221
280
  };
package/cli/tsconfig.json CHANGED
@@ -29,9 +29,12 @@
29
29
  "outDir": "./bin",
30
30
 
31
31
  "paths": {
32
+ "@client/*": [ "../client/*" ],
32
33
  "@cli/*": [ "./*" ],
33
34
  "@cli/app": [ "./app" ],
34
- "@cli": [ "./" ]
35
+ "@cli": [ "./" ],
36
+ "@common/*": [ "../common/*" ],
37
+ "@server/*": [ "../server/*" ]
35
38
  },
36
39
  },
37
40
 
@@ -5,7 +5,7 @@ import cli from '..';
5
5
  import Compiler from '../compiler';
6
6
  import { runProcess } from './runProcess';
7
7
 
8
- const tsconfigPaths = ['client/tsconfig.json', 'server/tsconfig.json'];
8
+ const tsconfigPaths = ['client/tsconfig.json', 'server/tsconfig.json', 'commands/tsconfig.json'];
9
9
  const eslintConfigPaths = ['eslint.config.mjs', 'eslint.config.js', 'eslint.config.cjs'];
10
10
 
11
11
  const resolveInstalledBinary = (name: string) => {
@@ -21,10 +21,17 @@ export default function App({ context }: { context: ClientContext }) {
21
21
  const curLayout = context.page?.layout;
22
22
  const [layout, setLayout] = React.useState<Layout | false | undefined>(curLayout);
23
23
  const [apiData, setApiData] = React.useState<{ [k: string]: any } | null>(context.page?.data || {});
24
+ const shouldEnableDevProfiler = __DEV__ && typeof window !== 'undefined' && window.dev;
25
+ const [isDevProfilerMounted, setDevProfilerMounted] = React.useState(false);
24
26
 
25
27
  // TODO: context.page is always provided in the context on the client side
26
28
  if (context.app.side === 'client') context.app.setLayout = setLayout;
27
29
 
30
+ React.useEffect(() => {
31
+ if (!shouldEnableDevProfiler) return;
32
+ setDevProfilerMounted(true);
33
+ }, [shouldEnableDevProfiler]);
34
+
28
35
  const layoutProps: LayoutProps = {
29
36
  ...context,
30
37
  context,
@@ -32,22 +39,19 @@ export default function App({ context }: { context: ClientContext }) {
32
39
  menu: undefined,
33
40
  children: undefined,
34
41
  };
42
+ const DevProfiler = shouldEnableDevProfiler
43
+ ? ((require('@client/dev/profiler') as typeof import('@client/dev/profiler')).default as React.ComponentType)
44
+ : null;
35
45
 
36
46
  return (
37
47
  <ReactClientContext.Provider value={context}>
38
48
  <DialogManager />
49
+ {DevProfiler && isDevProfilerMounted ? <DevProfiler /> : null}
39
50
 
40
51
  {!layout ? (
41
- <>
42
- {/* TODO: move to app, because here, we're not aware that the router service has been defined */}
43
- <RouterComponent service={context.Router} />
44
- </>
52
+ <RouterComponent service={context.Router} />
45
53
  ) : (
46
- <>
47
- {' '}
48
- {/* Same as router/components/Page.tsx */}
49
- <layout.Component {...layoutProps} />
50
- </>
54
+ <layout.Component {...layoutProps} />
51
55
  )}
52
56
  </ReactClientContext.Provider>
53
57
  );