proteum 2.1.0-2 → 2.1.0-4

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 (38) hide show
  1. package/AGENTS.md +51 -93
  2. package/README.md +46 -1
  3. package/agents/framework/AGENTS.md +167 -788
  4. package/agents/project/AGENTS.md +87 -110
  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/bin.js +8 -0
  11. package/cli/commands/dev.ts +1 -0
  12. package/cli/commands/trace.ts +210 -0
  13. package/cli/compiler/client/index.ts +30 -8
  14. package/cli/compiler/server/index.ts +28 -6
  15. package/cli/paths.ts +16 -1
  16. package/cli/presentation/commands.ts +23 -1
  17. package/cli/presentation/devSession.ts +5 -0
  18. package/cli/runtime/commands.ts +31 -0
  19. package/common/dev/requestTrace.ts +81 -0
  20. package/docs/request-tracing.md +121 -0
  21. package/package.json +1 -1
  22. package/server/app/container/config.ts +15 -0
  23. package/server/app/container/index.ts +3 -0
  24. package/server/app/container/trace/index.ts +284 -0
  25. package/server/services/prisma/index.ts +61 -5
  26. package/server/services/router/http/index.ts +40 -0
  27. package/server/services/router/index.ts +159 -6
  28. package/server/services/router/response/index.ts +80 -7
  29. package/server/services/router/response/page/document.tsx +16 -0
  30. package/server/services/router/response/page/index.tsx +27 -1
  31. package/Rte.zip +0 -0
  32. package/agents/project/agents.md.zip +0 -0
  33. package/doc/TODO.md +0 -71
  34. package/doc/front/router.md +0 -27
  35. package/doc/workspace/workspace.png +0 -0
  36. package/doc/workspace/workspace2.png +0 -0
  37. package/doc/workspace/workspace_26.01.22.png +0 -0
  38. package/server/services/router/http/session.ts.old +0 -40
@@ -1,487 +1,119 @@
1
- # Proteum Framework Guide
1
+ # Proteum App Contract
2
2
 
3
- This document is the framework-level contract for building and maintaining a Proteum project.
3
+ This is the canonical framework contract for Proteum-based projects.
4
4
 
5
- It is based on the current Proteum core plus the two real apps used as the reference surface:
5
+ Local project `AGENTS.md` files should only add project-specific deltas. They should not restate the framework contract.
6
6
 
7
- - `crosspath/platform`
8
- - `unique.domains/website`
7
+ ## Fast Start
9
8
 
10
- Use this guide for new work. Treat older patterns in the apps as legacy unless they are explicitly described here as still-supported.
9
+ When you enter a Proteum app, inspect it in this order:
11
10
 
12
- # What Proteum is
11
+ 1. Run `npx proteum explain --json` or read `./.proteum/manifest.json`.
12
+ 2. Inspect `./server/index.ts` and `./server/config/*.ts`.
13
+ 3. Inspect the touched `./server/controllers/**/*.ts`, `./server/services/**`, `./server/routes/**`, and `./client/pages/**` files.
14
+ 4. Run `npx proteum doctor` if routing or generation looks suspicious.
15
+ 5. If you need to diagnose or test against a running app, check the default port in `./env.yaml` first.
16
+ 6. If a server is already running on that port, use `npx proteum trace` to inspect past requests, errors, and their context before reproducing the issue or adding temporary logs.
13
17
 
14
- Proteum is a server-first SSR framework with:
18
+ ## Non-Negotiable Rules
15
19
 
16
- - explicit `Router.page(...)` client page registration
17
- - explicit `server/controllers/**/*.ts` server API entrypoints
18
- - service classes for business logic
19
- - generated controller trees and generated route modules
20
- - request-scoped server context
21
- - strong bias toward SEO-friendly SSR HTML and minimal client runtime assumptions
22
-
23
- The main rule for new work is:
24
-
25
- - keep routing, data loading, validation, request access, and service boundaries explicit
26
-
27
- # Non-negotiable rules
28
-
29
- - Client pages live in `client/pages/**` and register routes with `Router.page(...)` or `Router.error(...)`.
20
+ - Client pages live in `client/pages/**` and register routes with top-level `Router.page(...)` or `Router.error(...)` calls.
30
21
  - Page URLs come from the explicit `Router.page('/path', ...)` call, not from the file path.
31
- - Server business logic lives in service classes that extend `Service`.
32
- - Callable API entrypoints live only in `server/controllers/**/*.ts` files that extend `Controller`.
22
+ - Callable app APIs live only in `server/controllers/**/*.ts` files that extend `Controller`.
23
+ - Manual HTTP endpoints live in `server/routes/**`.
33
24
  - Controllers validate input with `this.input(schema)` inside the method body.
34
25
  - Call `this.input(...)` at most once per controller method.
35
- - Controller methods are exposed to the client under `/api/...` and are always called as `POST` fetchers.
36
- - Manual server routes belong in `server/routes/**` and use `Router.get/post/put/patch/delete(...)`.
37
- - Import Prisma types from `@models/types`.
26
+ - Request-scoped state exists only on `this.request` and router/manual-route context objects.
27
+ - SSR page data belongs in the page `setup` return object, not in `api.fetch(...)`.
28
+ - Normal service methods do not read request state directly.
38
29
  - Do not import runtime values from `@models`.
39
30
  - Do not use `@request` runtime globals.
40
31
  - Do not use `@app` on the client.
41
- - Do not use `api.fetch(...)` inside page route files for SSR data loading.
42
32
  - Do not edit generated files under `.proteum` by hand.
33
+ - Prefer type inference rooted in the explicit application graph in `server/index.ts`.
43
34
 
44
- # Source Of Truth Files
35
+ ## Source Of Truth
45
36
 
46
- The framework actually reads these files and folders:
37
+ Proteum reads these source files directly:
47
38
 
48
- - `package.json`: CLI scripts and dependency entrypoint
49
- - `identity.yaml`: app identity and web metadata
50
- - `env.yaml`: runtime environment config that Proteum loads directly
51
- - `server/config/*.ts`: plain typed config exports consumed by the explicit app bootstrap
52
- - `server/index.ts`: default-exported `Application` subclass that instantiates root services and router plugins
53
- - `server/services/**/service.json`: root service or router-plugin metadata
54
- - `server/controllers/**/*.ts`: generated API surface
55
- - `server/routes/**/*.ts`: manual server routes
56
- - `client/pages/**/*.ts(x)`: page and error registration
57
- - `client/pages/**/_layout/index.tsx`: generated layouts
58
- - `public/**`: copied or symlinked to dev/build output
39
+ - `package.json`
40
+ - `identity.yaml`
41
+ - `env.yaml`
42
+ - `server/config/*.ts`
43
+ - `server/index.ts`
44
+ - `server/services/**/service.json`
45
+ - `server/controllers/**/*.ts`
46
+ - `server/routes/**/*.ts`
47
+ - `client/pages/**/*.ts(x)`
48
+ - `client/pages/**/_layout/index.tsx`
49
+ - `public/**`
59
50
 
60
- Files Proteum generates and owns:
51
+ Proteum generates and owns:
61
52
 
62
53
  - `.proteum/manifest.json`
63
- - `.proteum/client/routes*`
64
- - `.proteum/client/layouts*`
65
- - `.proteum/client/context.ts`
66
- - `.proteum/client/models.ts`
67
- - `.proteum/client/services.d.ts`
68
- - `.proteum/common/controllers.ts`
69
- - `.proteum/common/models.ts`
70
- - `.proteum/server/routes*`
71
- - `.proteum/server/models.ts`
72
-
73
- The `.proteum` directory should be ignored by git.
74
-
75
- Generated files now live physically under `.proteum/client/*`, `.proteum/common/*`, and `.proteum/server/*`.
76
- Project code should reference the generated surface through `@generated/client/*`, `@generated/common/*`, and `@generated/server/*`.
77
- Keep `@/client/context` mapped to `.proteum/client/context.ts`.
78
- The legacy `@/client/.generated/*`, `@/common/.generated/*`, and `@/server/.generated/*` aliases may exist temporarily as migration shims, but new code should not use them.
79
-
80
- # Project Structure
81
-
82
- Use this shape for real work:
83
-
84
- - `client/pages`: route files and page-local UI
85
- - `client/components`: reusable UI
86
- - `client/hooks` or local hooks near the feature
87
- - `common`: shared types, catalogs, utilities safe across client/server
88
- - `server/config`: typed service config exports used by `server/index.ts`
89
- - `server/index.ts`: explicit app bootstrap and root service graph
90
- - `server/services`: business services
91
- - `server/controllers`: callable API entrypoints
92
- - `server/routes`: manual HTTP routes that should not be generated from controllers
93
- - `public`: static assets
94
- - `prisma`: schema and Prisma assets
95
- - `tests/e2e`: end-to-end tests
96
-
97
- The two reference apps both follow this split even when their internal feature folders differ.
98
-
99
- # Project Creation Today
100
-
101
- What the code says today:
102
-
103
- - Proteum exposes `proteum init`.
104
- - The current `init` command expects a `cli/skeleton` directory.
105
- - This repository currently does not contain that `cli/skeleton` directory.
106
-
107
- Practical consequence:
108
-
109
- - In this checkout, the reliable way to start a new app is to copy a working Proteum project structure from an existing app, then replace the identity, env, config, services, pages, and Prisma schema.
110
-
111
- If `proteum init` is restored later, re-check the skeleton before relying on it.
112
-
113
- # Required Root Files
114
-
115
- ## `package.json`
116
-
117
- The reference apps use these Proteum commands:
118
-
119
- - `proteum dev`
120
- - `npx proteum build prod`
121
- - `npx proteum refresh`
122
- - `npx proteum doctor`
123
- - `npx proteum explain`
124
- - `npx proteum typecheck`
125
- - `npx proteum lint`
126
- - `npx proteum check`
127
-
128
- ## `proteum explain`
129
-
130
- Proteum now generates a machine-readable manifest at `.proteum/manifest.json` during refresh/build/dev/explain flows.
131
-
132
- Use `proteum explain` as the first inspection command when an agent needs to understand a project without re-deriving framework conventions from source.
54
+ - `.proteum/client/*`
55
+ - `.proteum/common/*`
56
+ - `.proteum/server/*`
133
57
 
134
- Useful commands:
58
+ Project code should use:
135
59
 
136
- - `npx proteum explain`
137
- - `npx proteum explain --json`
138
- - `npx proteum explain routes --json`
139
- - `npx proteum explain services`
140
- - `npx proteum explain controllers`
141
- - `npx proteum explain env`
60
+ - `@generated/client/*`
61
+ - `@generated/common/*`
62
+ - `@generated/server/*`
63
+ - `@/client/context` for the generated client context entrypoint
142
64
 
143
- What the manifest exposes:
65
+ Use the structured CLI surfaces instead of re-deriving framework facts from source whenever possible:
144
66
 
145
- - app root and installed Proteum root
146
- - identity file path and identity summary
147
- - env file path plus loaded and required top-level keys
148
- - top-level services and router plugins with source file ownership and scope
149
- - generated controller endpoints with client accessor and HTTP path
150
- - client pages, error pages, and manual server routes with explicit route expressions
151
- - route and controller source locations
152
- - static route-target resolution state: literal, statically-resolved expression, or dynamic expression
153
- - client layout ownership and chunk ids
154
- - manifest-backed diagnostics for duplicate routes, invalid option keys, and controller/server-route collisions
67
+ - `npx proteum explain --json`: app structure, services, controllers, routes, layouts, diagnostics
68
+ - `npx proteum doctor --json`: manifest-backed diagnostics
69
+ - `npx proteum trace ...`: live dev-only request traces
155
70
 
156
- ## `proteum doctor`
157
-
158
- Use `npx proteum doctor` to inspect manifest diagnostics in a human-readable form.
159
-
160
- Use:
161
-
162
- - `npx proteum doctor`
163
- - `npx proteum doctor --json`
164
- - `npx proteum doctor --strict`
165
-
166
- Current strict-mode behavior:
167
-
168
- - exits non-zero if any manifest warning or error exists
169
- - intended for CI and agent guardrails
170
-
171
- ## `identity.yaml`
172
-
173
- Proteum loads `identity.yaml` directly. It must define:
174
-
175
- - `name`
176
- - `identifier`
177
- - `description`
178
- - `author`
179
- - `language`
180
- - optional `locale`
181
- - `maincolor`
182
- - optional `iconsPack`
183
- - `web.title`
184
- - `web.titleSuffix`
185
- - `web.fullTitle`
186
- - `web.description`
187
- - `web.version`
188
- - optional `web.metas`
189
- - optional `web.jsonld`
190
-
191
- The generated app type uses `identifier`.
192
-
193
- ## `env.yaml`
194
-
195
- Proteum currently loads `env.yaml` directly through `ConfigParser`.
196
-
197
- The core parser expects at least:
198
-
199
- - `name`
200
- - `profile`
201
- - `router.port`
202
- - `router.domains`
203
- - `console`
204
-
205
- Observed reality:
206
-
207
- - the apps also keep files like `env.prod.yaml` and `env.testing.yaml`
208
- - the current core parser shown here does not automatically merge those files
209
-
210
- So for framework-level correctness:
211
-
212
- - treat `env.yaml` as the authoritative runtime config file unless you also own a custom deploy flow around it
213
-
214
- # App Bootstrap
215
-
216
- Bootstrap is explicit.
217
-
218
- Use `server/config/*.ts` for typed config exports and `server/index.ts` for the application class that wires services together.
219
-
220
- Example config module:
221
-
222
- ```ts
223
- import { Services, type ServiceConfig } from '@server/app';
224
- import AppContainer from '@server/app/container';
225
- import Router from '@server/services/router';
226
- import Users from '@/server/services/Users';
227
-
228
- type RouterBaseConfig = Omit<ServiceConfig<typeof Router>, 'plugins'>;
229
-
230
- export const usersConfig = Services.config(Users, {});
231
-
232
- export const routerBaseConfig = {
233
- domains: AppContainer.Environment.router.domains,
234
- http: {
235
- domain: 'example.com',
236
- port: AppContainer.Environment.router.port,
237
- ssl: true,
238
- upload: { maxSize: '10mb' },
239
- },
240
- context: () => ({}),
241
- } satisfies RouterBaseConfig;
242
- ```
243
-
244
- Example app bootstrap:
245
-
246
- ```ts
247
- import { Application } from '@server/app';
248
- import Router from '@server/services/router';
249
- import SchemaRouter from '@server/services/schema/router';
250
- import Users from '@/server/services/Users';
251
- import * as userConfig from '@/server/config/user';
252
-
253
- export default class MyApp extends Application {
254
- public Users = new Users(this, userConfig.usersConfig, this);
255
- public Router = new Router(this, {
256
- ...userConfig.routerBaseConfig,
257
- plugins: {
258
- schema: new SchemaRouter({}, this),
259
- },
260
- }, this);
261
- }
262
- ```
71
+ ## App Bootstrap And Services
263
72
 
264
- Important bootstrap rules:
265
-
266
- - `server/index.ts` must default-export the app `Application` subclass.
267
- - Each root service is a public class field instantiated with `new ServiceClass(this, config, this)`.
268
- - `server/config/*.ts` should export plain typed constants with `Services.config(ServiceClass, { ... })`.
269
- - Router base config can use `Omit<ServiceConfig<typeof Router>, 'plugins'>` so router plugins are instantiated explicitly in `server/index.ts`.
270
- - Router plugins are configured inside the `Router` config `plugins` object as explicit `new PluginClass(config, this)` instances.
271
- - Router `context(request, app)` returns SSR-safe values exposed to both page setup/render and the client runtime.
272
- - Both reference apps expose a SSR-safe `user` object through `Router.context(...)`.
273
- - Generated service typings and manifests are derived from `server/index.ts` plus `server/services/**/service.json`.
274
-
275
- # Services
276
-
277
- ## Root service contract
278
-
279
- Each root app service normally has:
280
-
281
- - `server/services/<Feature>/index.ts`
282
- - `server/services/<Feature>/service.json`
283
-
284
- Example `service.json`:
285
-
286
- ```json
287
- {
288
- "id": "UniqueDomains/Founder",
289
- "name": "UniqueDomainsFounder",
290
- "parent": "app",
291
- "dependences": []
292
- }
293
- ```
73
+ `server/index.ts` is the canonical type root and the explicit application graph.
294
74
 
295
75
  Rules:
296
76
 
297
- - `parent` is `"app"` for normal root services.
298
- - `id` is the service identifier declared in `service.json` and used by manifests plus `Service.use('Service/Id')`.
299
- - `name` is metadata for generated registration.
300
- - `priority` is optional and used by some services.
301
-
302
- ## Service class contract
303
-
304
- Service classes extend `Service<Config, Hooks, App, Parent>`.
305
-
306
- Use services for:
307
-
308
- - database reads and writes
309
- - orchestration
310
- - feature logic
311
- - subservice composition
312
- - startup work in `ready()`
313
-
314
- Available on a service instance:
315
-
316
- - `this.app`: application instance
317
- - `this.services`: same application instance as a service registry
318
- - `this.models`: runtime Prisma client, if `Models` is registered
319
-
320
- Example:
321
-
322
- ```ts
323
- import Service from '@server/app/service';
324
-
325
- export type Config = {
326
- pageSize: number;
327
- };
328
-
329
- export default class FounderService extends Service<Config, {}, MyApp, MyApp> {
330
- public async ListProjects(input: { userId: number }) {
331
- return this.models.project.findMany({
332
- where: { userId: input.userId },
333
- select: {
334
- id: true,
335
- name: true,
336
- },
337
- });
338
- }
339
- }
340
- ```
77
+ - `server/index.ts` must default-export the app `Application` subclass
78
+ - root services are public class fields instantiated with `new ServiceClass(this, config, this)`
79
+ - typed root-service config lives in `server/config/*.ts` via `Services.config(ServiceClass, { ... })`
80
+ - router plugins are instantiated explicitly inside the `Router` config `plugins` object
81
+ - `server/services/**/service.json` plus `server/index.ts` drive generated service typings and manifest service entries
341
82
 
342
83
  Service rules:
343
84
 
344
- - Prefer `this.services.OtherService` over hidden globals.
345
- - Prefer `this.models` or `this.app.Models.client` for Prisma runtime access.
346
- - Keep auth, input parsing, and request handling in controllers.
347
- - Pass explicit typed values into services instead of reading request state inside services.
348
- - Prefer service inputs that are easy to test without a live request context.
349
-
350
- ## Subservices
351
-
352
- Both reference apps use service-owned subservices heavily.
353
-
354
- Example:
355
-
356
- ```ts
357
- export default class DomainsService extends Service<Config, {}, UniqueDomains, UniqueDomains> {
358
- public search = new DomainsSearchService(this, this.config, this.app);
359
- public radar = new DomainsRadarService(this, null, this.app);
360
- }
361
- ```
362
-
363
- Use subservices when:
364
-
365
- - a feature has multiple coherent domains
366
- - you want controller paths like `Domains.search.*` or `Founder.projects.*`
367
- - you want smaller files and explicit ownership
368
-
369
- # Controllers
370
-
371
- ## File contract
372
-
373
- Controller files must:
374
-
375
- - live under `server/controllers/**/*.ts`
376
- - default-export a class extending `Controller`
377
-
378
- Example:
379
-
380
- ```ts
381
- import Controller, { schema } from '@server/app/controller';
382
-
383
- export default class FounderProjectsController extends Controller<MyApp> {
384
- public async createProject() {
385
- const { Founder } = this.services;
386
-
387
- const data = this.input(
388
- schema.object({
389
- name: schema.string(),
390
- }),
391
- );
392
-
393
- return Founder.projects.createProject(data);
394
- }
395
- }
396
- ```
397
-
398
- ## Validation contract
399
-
400
- Use `this.input(...)` exactly once per controller method.
401
-
402
- Supported forms:
403
-
404
- - `this.input(zodSchema)`
405
- - `this.input({ ...shape })`
406
-
407
- Do not:
408
-
409
- - validate in decorators
410
- - call `this.input(...)` twice
411
- - parse request data manually unless you are in a manual `server/routes` handler
412
-
413
- ## Request contract
414
-
415
- Controllers receive request scope through `this.request`.
416
-
417
- Typical values:
418
-
419
- - `this.request.request`: raw request object wrapper
420
- - `this.request.response`
421
- - `this.request.user`
422
- - `this.request.auth`
423
- - `this.request.schema`
424
- - `this.request.metrics`
425
- - `this.request.request.data`
426
-
427
- The exact plugin fields depend on the router plugins configured in `server/index.ts`.
428
-
429
- ## Route generation
430
-
431
- Proteum generates controller endpoints automatically.
432
-
433
- Key facts:
434
-
435
- - Only `server/controllers/**/*.ts` files are indexed.
436
- - Only class methods with bodies become routes.
437
- - The client-facing route is always prefixed with `/api/`.
438
- - Generated client calls use `POST`, even for read methods such as `Get`, `List`, or `Search`.
439
-
440
- Route path derivation:
441
-
442
- - base path comes from the controller file path
443
- - method name becomes the final route segment
444
- - `export const controllerPath = 'Custom/path'` overrides the base path
445
-
446
- Examples from the reference apps:
447
-
448
- - `server/controllers/Auth.ts#Session()` becomes `Auth.Session()` on the client and maps to `/api/Auth/Session`
449
- - `server/controllers/Domains/search.ts#Search()` becomes `Domains.search.Search()`
450
- - `server/controllers/Companies/Persons.ts` can override the base path with `controllerPath = 'Companies/Persons'`
85
+ - business logic lives in classes that extend `Service`
86
+ - use `this.services`, `this.models`, and `this.app`
87
+ - keep auth, input parsing, locale, cookies, and request-derived values in controllers, then pass explicit typed arguments into services
88
+ - use subservices when a feature has multiple coherent domains and the root class is growing
451
89
 
452
- Naming rule:
90
+ ## Controllers
453
91
 
454
- - method names become public API names
455
- - choose method names deliberately because client code will call them directly
92
+ Controller rules:
456
93
 
457
- # Client Pages
94
+ - files live under `server/controllers/**/*.ts`
95
+ - each file default-exports a class extending `Controller`
96
+ - methods with bodies become generated client-callable endpoints
97
+ - route path comes from the controller file path plus the method name
98
+ - `export const controllerPath = 'Custom/path'` can override the base path when needed
99
+ - generated client calls use `POST`
458
100
 
459
- ## File contract
101
+ Controller workflow:
460
102
 
461
- Client pages live in `client/pages/**`.
103
+ 1. destructure the service or router helper you need
104
+ 2. validate once with `this.input(schema)`
105
+ 3. resolve auth and other request-derived values from `this.request`
106
+ 4. pass explicit typed values into a service method
462
107
 
463
- Proteum scans page files for top-level `Router.page(...)` and `Router.error(...)` calls.
108
+ ## Client Pages
464
109
 
465
- Important compiler rule:
110
+ Compiler rules:
466
111
 
112
+ - Proteum scans page files for top-level `Router.page(...)` and `Router.error(...)` calls
467
113
  - the file path controls chunk identity and layout discovery
468
- - the URL comes from the explicit route path string in `Router.page(...)`
114
+ - the route path comes from the explicit string in `Router.page(...)`
469
115
 
470
- Do not hide route registration inside helper abstractions that remove the direct top-level `Router.page(...)` call.
471
-
472
- ## Import contract
473
-
474
- Use:
475
-
476
- ```ts
477
- import Router from '@/client/router';
478
- ```
479
-
480
- Do not use `@app` on the client.
481
-
482
- ## Supported signatures
483
-
484
- Proteum supports these `Router.page(...)` signatures:
116
+ Supported signatures:
485
117
 
486
118
  ```ts
487
119
  Router.page('/path', render);
@@ -490,262 +122,75 @@ Router.page('/path', options, render);
490
122
  Router.page('/path', options, setup, render);
491
123
  ```
492
124
 
493
- New work should usually prefer:
125
+ For new work, prefer:
494
126
 
495
127
  ```ts
496
128
  Router.page('/path', setup, render);
497
- ```
498
-
499
- or:
500
-
501
- ```ts
502
129
  Router.page('/path', options, setup, render);
503
130
  ```
504
131
 
505
- ## Setup function
506
-
507
- `setup` is the SSR contract. It receives:
508
-
509
- - router context
510
- - generated controller tree
511
- - custom router context values like `user`
512
- - request query/path params in `data`
513
-
514
- Return one flat object.
515
-
516
- Proteum splits that object into:
517
-
518
- - route options
519
- - SSR data providers
520
-
521
- Supported route option keys are:
522
-
523
- - `_priority`
524
- - `_preload`
525
- - `_domain`
526
- - `_accept`
527
- - `_raw`
528
- - `_auth`
529
- - `_redirectLogged`
530
- - `_static`
531
- - `_whenStatic`
532
- - `_canonicalParams`
533
- - `_layout`
534
- - `_TESTING`
535
- - `_logging`
536
-
537
- The underscore is optional in the framework code, but both reference apps use the underscore form and new work should do the same.
538
-
539
- Everything else returned from `setup` is treated as page data.
540
-
541
- Example:
542
-
543
- ```ts
544
- Router.page('/pricing', ({ Plans }) => ({
545
- _auth: false,
546
- _layout: false,
547
- plans: Plans.getPlans(),
548
- }), ({ plans }) => <PricingPage plans={plans} />);
549
- ```
550
-
551
- ## Data loading rules
552
-
553
- Use page `setup` for SSR data.
554
-
555
- Good:
556
-
557
- ```ts
558
- Router.page('/app/projects/:projectId', ({ Founder }) => ({
559
- _auth: 'USER',
560
- projectsResponse: Founder.projects.getProjects(),
561
- }), ({ projectsResponse }) => <ProjectsPage projects={projectsResponse.projects} />);
562
- ```
563
-
564
- Bad:
565
-
566
- - calling `api.fetch(...)` inside the page file to preload SSR data
567
- - moving SSR data fetching into random effects when the page can know it up front
568
-
569
- How setup data works:
132
+ `setup` rules:
570
133
 
134
+ - return one flat object
135
+ - keys like `_auth`, `_layout`, `_static`, `_redirectLogged`, and other reserved setup keys are route options
136
+ - every other key is SSR data
571
137
  - controller fetchers and promises are resolved before render
572
- - SSR fetchers are batched through a single `/api` request internally
573
- - plain values can also be returned from `setup`
574
-
575
- ## Render function
576
-
577
- `render` receives:
578
-
579
- - the same router context
580
- - resolved setup data
581
- - the generated controller tree
582
- - `page`
583
- - `request`
584
- - `api`
585
- - custom router context like `user`
586
-
587
- Use it for:
588
-
589
- - page-local React/Preact state
590
- - calling controller methods on interaction
591
- - assigning page metadata on `page`
592
-
593
- Example:
594
-
595
- ```ts
596
- ({ request, page, Founder }) => {
597
- page.metas.robots = 'noindex';
598
-
599
- return <Page />;
600
- }
601
- ```
602
-
603
- ## Error pages
138
+ - plain values may also be returned
604
139
 
605
- Use `Router.error(code, options, render)` in `client/pages/_messages/**`.
140
+ `render` rules:
606
141
 
607
- Example:
142
+ - consume resolved setup data there
143
+ - use generated controller methods from the render args or `@/client/context`
144
+ - use `api.reload(...)` or `api.set(...)` only when intentionally mutating active page setup state
608
145
 
609
- ```ts
610
- Router.error(404, { layout: false }, ({ data }) => <ErrorScreen code={404} data={data} />);
611
- ```
146
+ Error pages:
612
147
 
613
- # Client Context And Controller Calls
148
+ - use `Router.error(code, options, render)` in `client/pages/_messages/**`
614
149
 
615
- Proteum generates a client context and controller tree.
150
+ ## Client Context And Controller Calls
616
151
 
617
- Use:
152
+ Use the generated client context entrypoint:
618
153
 
619
154
  ```ts
620
155
  import useContext from '@/client/context';
621
-
622
- const { Founder, user, Router, api } = useContext();
623
156
  ```
624
157
 
625
- Generated controller methods are promise-like fetchers:
626
-
627
- - `then`
628
- - `catch`
629
- - `finally`
630
- - `run()`
631
-
632
- So all of these are valid:
158
+ Then call generated controllers directly:
633
159
 
634
160
  ```ts
635
- Founder.projects.getProjects().then(...);
161
+ const { Founder } = useContext();
636
162
  await Founder.projects.updateProject(payload);
637
- await Founder.projects.updateProject(payload).run();
638
163
  ```
639
164
 
640
- Modern usage in both apps is mostly direct `await` or `.then(...)`.
641
-
642
- Use `api.reload(...)` and `api.set(...)` only when you intentionally want to refresh or mutate page setup data that already belongs to the active page response.
643
-
644
- # Layouts
645
-
646
- Layouts come from `client/pages/**/_layout/index.tsx`.
647
-
648
- How Proteum resolves them:
649
-
650
- - if `_layout: false`, no layout is used
651
- - if `_layout: 'convert'`, a named generated layout with id `convert` is used
652
- - otherwise Proteum picks the nearest matching `_layout` folder by file chunk identity
653
- - if no generated layout matches, the internal root layout is used
654
-
655
- Observed patterns:
656
-
657
- - CrossPath has root, `convert`, and `employer` layouts
658
- - Unique Domains mostly uses the internal/root layout and sets `_layout: false` for public landing pages
165
+ Use direct controller calls for interactions. Do not recreate fake runtime imports or client-side `@app` access.
659
166
 
660
- Practical rule:
167
+ ## Manual Server Routes
661
168
 
662
- - use `_layout: false` for standalone landing or embed-like pages
663
- - use a named layout only when a matching `_layout` folder exists
169
+ Use `server/routes/**` only for explicit HTTP behavior that should not be a generated controller action.
664
170
 
665
- # Manual Server Routes
666
-
667
- Use `server/routes/**` for routes that should stay explicit HTTP endpoints rather than generated controller actions.
668
-
669
- Typical uses from the reference apps:
171
+ Good fits:
670
172
 
671
173
  - redirects
672
- - webhook-like endpoints
673
- - sitemap and RSS
674
- - landing-page tracking
675
- - public API endpoints with custom semantics
174
+ - sitemap or RSS
676
175
  - OAuth callbacks
176
+ - webhooks
177
+ - public resources with custom semantics
677
178
 
678
- Example:
679
-
680
- ```ts
681
- import { Router, Navigation } from '@app';
682
-
683
- Router.get('/sitemap.xml', async ({ response }) => {
684
- return response.xml(await Navigation.Sitemap());
685
- });
686
- ```
687
-
688
- Manual route rules:
689
-
690
- - import server services from `@app`
691
- - use route handler context for request/response and router-plugin services
692
- - validate with `schema.validate(...)` when the schema router plugin is installed
693
- - return `response.redirect(...)`, `response.json(...)`, `response.xml(...)`, `response.html(...)`, `response.file(...)`, or raw serializable data
694
-
695
- Route handler context includes:
696
-
697
- - `request`
698
- - `response`
699
- - `Router`
700
- - app services
701
- - generated controller tree
702
- - router plugin request services such as `auth`, `schema`, `metrics`
703
- - custom router context values from `Router.context(...)`
704
-
705
- # Router Plugins
706
-
707
- Router plugins are special services attached under `Router.config.plugins`.
708
-
709
- They extend `RouterService`.
710
-
711
- Use them for:
712
-
713
- - authentication
714
- - validation helpers
715
- - metrics/tracking
716
- - other request-scoped helpers
717
-
718
- A router plugin usually has:
719
-
720
- - `server/services/<Feature>/router/index.ts`
721
- - optional `server/services/<Feature>/router/request.ts`
722
- - `service.json` with `"parent": "router"`
723
-
724
- Example `service.json`:
725
-
726
- ```json
727
- {
728
- "id": "UniqueDomains/Users/Metrics/Router",
729
- "name": "Metrics",
730
- "parent": "router",
731
- "dependences": []
732
- }
733
- ```
734
-
735
- Router plugin rules:
179
+ Rules:
736
180
 
737
- - implement `requestService(request)` to expose a request-scoped helper to route/controller context
738
- - use `this.parent.on('request' | 'resolved' | 'render', ...)` inside `ready()` when you need router lifecycle hooks
181
+ - import server-side app services from `@app`
182
+ - use route handler context for `request`, `response`, router plugins, and custom router context
183
+ - if the route is just a normal app API, prefer a controller instead
739
184
 
740
- # Models And Prisma
185
+ ## Models And Aliases
741
186
 
742
- For typings:
187
+ Use Prisma typings from:
743
188
 
744
189
  ```ts
745
190
  import type * as Models from '@models/types';
746
191
  ```
747
192
 
748
- For runtime access:
193
+ Use runtime models through:
749
194
 
750
195
  - `this.models`
751
196
  - `this.app.Models.client`
@@ -753,165 +198,99 @@ For runtime access:
753
198
  Rules:
754
199
 
755
200
  - do not import runtime values from `@models`
756
- - keep Prisma model access inside services
201
+ - keep Prisma runtime access inside services when possible
757
202
  - prefer explicit `select` or narrow `include`
758
203
  - do not edit generated Prisma client files
759
204
 
760
- Both apps instantiate `Models` explicitly in `server/index.ts` with config imported from `server/config/*.ts`.
761
-
762
- # Aliases
205
+ Relevant aliases:
763
206
 
764
- These aliases matter in real projects:
765
-
766
- - `@/client/...`: app client code
767
- - `@/server/...`: app server code
768
- - `@/common/...`: app shared code
207
+ - `@/client/...`, `@/server/...`, `@/common/...`: app code
769
208
  - `@client/...`, `@server/...`, `@common/...`: Proteum core modules
770
- - `@app`: server-side application services for manual routes
771
- - `@models/types`: Prisma typings only
772
-
773
- Import rules:
774
-
775
- - client pages: use `@/client/router`, `@/client/context`, app-local components, and generated controller tree from context
776
- - controllers/services: use `@server/app/controller`, `@server/app/service`, app-local services, and `@models/types`
777
- - manual server routes: use `@app` plus app-local utilities
778
-
779
- # SEO And Static Output
780
-
781
- Proteum is built for SSR and crawlable HTML.
209
+ - `@app`: server-side application services for manual routes only
210
+ - `@generated/*`: generated app surfaces
782
211
 
783
- Observed patterns in the apps:
212
+ ## Task Playbooks
784
213
 
785
- - public landing pages use `Router.page(..., { _layout: false, ... })`
786
- - sitemap is produced explicitly through a service, then exposed with `Router.get('/sitemap.xml', ...)`
787
- - canonical behavior is available through `_canonicalParams`
788
- - static caching exists through `_static`
789
- - manual routes can opt into running even for static pages with `whenStatic: true`
214
+ ### Add A New App API
790
215
 
791
- Use these rules:
216
+ 1. Add or extend a root service under `server/services/<Feature>/index.ts`.
217
+ 2. Add or update `server/services/<Feature>/service.json`.
218
+ 3. Add a controller under `server/controllers/**`.
219
+ 4. Validate once with `this.input(schema)`.
220
+ 5. Resolve auth and request-derived values in the controller.
221
+ 6. Call the service from the client through the generated controller tree.
792
222
 
793
- - prefer SSR page setup for crawlable content
794
- - keep metadata and structured output on the server-rendered path
795
- - use manual routes for sitemap, RSS, redirects, and resource endpoints
223
+ ### Add A New SSR Page
796
224
 
797
- # Generated Code Mental Model
798
-
799
- Proteum is not magic, but it is generation-heavy.
800
-
801
- When you change source files, Proteum regenerates:
802
-
803
- - route wrapper modules for client pages and server routes
804
- - layout registries
805
- - controller client tree
806
- - typed server app shim
807
-
808
- Source-to-generated mapping:
809
-
810
- - `client/pages/**` -> generated route modules and layout modules
811
- - `server/routes/**` -> generated server route modules
812
- - `server/controllers/**/*.ts` -> `.proteum/common/controllers.ts` and server controller registry
813
- - `server/services/**/service.json` + `server/index.ts` -> generated service typings and manifest service entries
814
-
815
- LLM rule:
816
-
817
- - edit source files only
818
- - never patch generated output directly
819
-
820
- # Maintenance Workflow For New Features
225
+ 1. Create or update `client/pages/.../index.tsx`.
226
+ 2. Register `Router.page('/real-url', setup, render)`.
227
+ 3. Return `_auth`, `_layout`, and SSR data from `setup`.
228
+ 4. Read resolved data in `render`.
229
+ 5. Use `@/client/context` or render args only for interactive follow-up actions.
821
230
 
822
- When adding a feature, follow this order:
231
+ ### Add A New Manual Route
823
232
 
824
- 1. Add or extend a root service under `server/services/<Feature>`.
825
- 2. Add or extend subservices if the feature has distinct concerns.
826
- 3. Add `server/controllers/**/*.ts` entrypoints for callable app APIs.
827
- 4. Add or extend typed config exports in `server/config/*.ts`, then instantiate the root service in `server/index.ts`.
828
- 5. Add or update `client/pages/**` routes that consume the feature.
829
- 6. Load SSR data in page `setup`.
830
- 7. Use generated controller methods from page args or `useContext()` for interactions.
831
- 8. Add manual `server/routes/**` only if you need explicit HTTP behavior that should not be a controller endpoint.
233
+ 1. Create `server/routes/...`.
234
+ 2. Import `Router` and needed app services from `@app`.
235
+ 3. Register `Router.get/post/put/patch/delete(...)`.
236
+ 4. Return response helpers or raw serializable data.
832
237
 
833
- # Maintenance Checklist For Existing Projects
238
+ ### Diagnose A Runtime Issue
834
239
 
835
- When maintaining a Proteum app:
240
+ 1. Run `npx proteum explain --json`.
241
+ 2. Run `npx proteum doctor`.
242
+ 3. Read the default port from `./env.yaml` and check whether a server is already running there.
243
+ 4. If a server is already running on that default port, inspect existing traces first:
244
+ - `npx proteum trace requests --port <envPort>`
245
+ - `npx proteum trace latest --port <envPort>`
246
+ - `npx proteum trace show <requestId> --port <envPort>` when you need the full context for a past error
247
+ 5. If the issue is request-time behavior in dev and the existing traces are not enough, run:
248
+ - `npx proteum trace arm --capture deep --port <envPort>`
249
+ - reproduce the failing request once
250
+ - `npx proteum trace latest --port <envPort>` or `npx proteum trace show <requestId> --port <envPort>`
251
+ 6. Inspect the touched controller, service, route, or page source.
252
+ 7. Only add temporary logging if the trace is insufficient.
836
253
 
837
- - inspect `server/index.ts` and `server/config/*.ts` first to understand which services actually exist
838
- - inspect `service.json` before moving or renaming services
839
- - inspect `server/controllers/**/*.ts` to understand the public client API
840
- - inspect `client/pages/**` for the real route table
841
- - check `_layout` folders before changing page chrome
842
- - check router plugins before assuming `auth`, `schema`, or `metrics` behavior
843
- - trace generated controller calls back to controller files, not to ad-hoc fetch URLs
254
+ For the full trace reference, see `node_modules/proteum/docs/request-tracing.md` in installed apps or `docs/request-tracing.md` in the framework repository.
844
255
 
845
- # Preferred Patterns For New Work
256
+ ## Preferred Patterns
846
257
 
258
+ - explicit `server/index.ts` bootstrap over hidden registration
847
259
  - `Router.page(path, setup, render)` over page-local fetch hacks
848
- - controller-backed APIs over ad-hoc manual `/api/...` route files
260
+ - controller-backed app APIs over ad hoc manual `/api/...` route files
849
261
  - service classes over random server helpers with hidden dependencies
850
- - `controllerPath` only when the file path would produce the wrong public API shape
851
- - `useContext()` or page render args for controller access on the client
852
- - one clear source of truth for catalogs and shared types
853
- - when a project already includes a Shadcn-based `client/components/ui/**` layer, reuse those components for standard UI primitives before creating custom ones
854
-
855
- # Legacy Or Discouraged Patterns
262
+ - one canonical source of truth for catalogs, registries, and shared types
263
+ - project-local Shadcn-based UI primitives when the app already provides them
856
264
 
857
- These exist in the codebase but should not be the default for new work:
265
+ ## Discouraged Patterns
858
266
 
859
- - older pages that overuse `api.reload(...)` and `api.set(...)`
860
- - older pages with deeply mixed UI and data responsibilities
861
- - legacy code that leans on manual `/api/...` routes for app APIs
862
- - any attempt to reintroduce `api.fetch(...)` for SSR page loading
267
+ - `api.fetch(...)` inside page files for SSR loading
863
268
  - client-side `@app` imports
864
269
  - runtime `@models` imports
270
+ - request-scoped state inside normal service methods
271
+ - hiding route registration behind abstractions that remove the top-level `Router.page(...)` call
272
+ - editing `.proteum` directly
865
273
 
866
- # Testing And Verification
274
+ ## Verification
867
275
 
868
- For app work, verify at the correct layer:
276
+ Verify at the correct layer:
869
277
 
870
278
  - route additions: boot the app and hit the real URL
871
- - controller changes: exercise the generated client call or `/api/...` endpoint
872
- - SSR changes: load the real page and inspect the rendered HTML and browser console
873
- - router/plugin changes: verify request context behavior, auth, redirects, metrics, and validation on a running app
279
+ - controller changes: exercise the generated client call or the generated `/api/...` endpoint
280
+ - SSR changes: load the real page and inspect rendered HTML plus browser console
281
+ - router/plugin changes: verify request context, auth, redirects, metrics, and validation on a running app
874
282
 
875
- Use the real app commands already present in the reference projects when possible:
283
+ When you need to diagnose or test against an app that may already be running:
284
+
285
+ - read the default port from `env.yaml`
286
+ - check whether a server is already running on that port
287
+ - if it is, inspect `proteum trace requests`, `proteum trace latest`, and `proteum trace show <requestId>` before reproducing the issue
288
+
289
+ Useful app commands:
876
290
 
877
291
  - `proteum dev`
878
- - `npx proteum build prod`
292
+ - `npx proteum refresh`
879
293
  - `npx proteum typecheck`
880
294
  - `npx proteum lint`
881
295
  - `npx proteum check`
882
-
883
- # Minimal Recipes
884
-
885
- ## Add a new app API
886
-
887
- 1. Create or extend `server/services/Feature/index.ts`.
888
- 2. Create `server/controllers/Feature.ts`.
889
- 3. Validate input with `this.input(schema)`.
890
- 4. Resolve auth or other request-derived values in the controller and pass them into the service method.
891
- 5. Call it from the client as `Feature.MethodName(...)`.
892
-
893
- ## Add a new SSR page
894
-
895
- 1. Create `client/pages/.../index.tsx`.
896
- 2. Register `Router.page('/real-url', setup, render)`.
897
- 3. Return `_auth`, `_layout`, and SSR fetchers from `setup`.
898
- 4. Read resolved data in `render`.
899
- 5. Use `useContext()` only for interactive follow-up actions.
900
-
901
- ## Add a new manual route
902
-
903
- 1. Create `server/routes/.../file.ts`.
904
- 2. Import `Router` and needed services from `@app`.
905
- 3. Register `Router.get/post/...`.
906
- 4. Use `schema.validate(...)` if the schema plugin is installed.
907
- 5. Return a response helper or raw JSON-safe data.
908
-
909
- # Summary Rule
910
-
911
- If you are unsure where code belongs:
912
-
913
- - page URL and SSR data: `client/pages`
914
- - reusable business logic: `server/services`
915
- - client-callable app API: `server/controllers/**/*.ts`
916
- - custom HTTP endpoint: `server/routes`
917
- - request-scoped cross-cutting concern: router plugin
296
+ - `npx proteum build prod`