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.
- package/AGENTS.md +51 -93
- package/README.md +46 -1
- package/agents/framework/AGENTS.md +167 -788
- package/agents/project/AGENTS.md +87 -110
- package/agents/project/client/AGENTS.md +22 -93
- package/agents/project/client/pages/AGENTS.md +24 -26
- package/agents/project/server/routes/AGENTS.md +10 -8
- package/agents/project/server/services/AGENTS.md +22 -159
- package/agents/project/tests/AGENTS.md +11 -8
- package/cli/bin.js +8 -0
- package/cli/commands/dev.ts +1 -0
- package/cli/commands/trace.ts +210 -0
- package/cli/compiler/client/index.ts +30 -8
- package/cli/compiler/server/index.ts +28 -6
- package/cli/paths.ts +16 -1
- package/cli/presentation/commands.ts +23 -1
- package/cli/presentation/devSession.ts +5 -0
- package/cli/runtime/commands.ts +31 -0
- package/common/dev/requestTrace.ts +81 -0
- package/docs/request-tracing.md +121 -0
- package/package.json +1 -1
- package/server/app/container/config.ts +15 -0
- package/server/app/container/index.ts +3 -0
- package/server/app/container/trace/index.ts +284 -0
- package/server/services/prisma/index.ts +61 -5
- package/server/services/router/http/index.ts +40 -0
- package/server/services/router/index.ts +159 -6
- package/server/services/router/response/index.ts +80 -7
- package/server/services/router/response/page/document.tsx +16 -0
- package/server/services/router/response/page/index.tsx +27 -1
- package/Rte.zip +0 -0
- package/agents/project/agents.md.zip +0 -0
- package/doc/TODO.md +0 -71
- package/doc/front/router.md +0 -27
- package/doc/workspace/workspace.png +0 -0
- package/doc/workspace/workspace2.png +0 -0
- package/doc/workspace/workspace_26.01.22.png +0 -0
- package/server/services/router/http/session.ts.old +0 -40
|
@@ -1,487 +1,119 @@
|
|
|
1
|
-
# Proteum
|
|
1
|
+
# Proteum App Contract
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
This is the canonical framework contract for Proteum-based projects.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Local project `AGENTS.md` files should only add project-specific deltas. They should not restate the framework contract.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- `unique.domains/website`
|
|
7
|
+
## Fast Start
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
When you enter a Proteum app, inspect it in this order:
|
|
11
10
|
|
|
12
|
-
|
|
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
|
-
|
|
18
|
+
## Non-Negotiable Rules
|
|
15
19
|
|
|
16
|
-
-
|
|
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
|
-
-
|
|
32
|
-
-
|
|
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
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
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
|
-
|
|
35
|
+
## Source Of Truth
|
|
45
36
|
|
|
46
|
-
|
|
37
|
+
Proteum reads these source files directly:
|
|
47
38
|
|
|
48
|
-
- `package.json
|
|
49
|
-
- `identity.yaml
|
|
50
|
-
- `env.yaml
|
|
51
|
-
- `server/config/*.ts
|
|
52
|
-
- `server/index.ts
|
|
53
|
-
- `server/services/**/service.json
|
|
54
|
-
- `server/controllers/**/*.ts
|
|
55
|
-
- `server/routes/**/*.ts
|
|
56
|
-
- `client/pages/**/*.ts(x)
|
|
57
|
-
- `client/pages/**/_layout/index.tsx
|
|
58
|
-
- `public
|
|
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
|
-
|
|
51
|
+
Proteum generates and owns:
|
|
61
52
|
|
|
62
53
|
- `.proteum/manifest.json`
|
|
63
|
-
- `.proteum/client
|
|
64
|
-
- `.proteum/
|
|
65
|
-
- `.proteum/
|
|
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
|
-
|
|
58
|
+
Project code should use:
|
|
135
59
|
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
- `
|
|
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
|
-
|
|
65
|
+
Use the structured CLI surfaces instead of re-deriving framework facts from source whenever possible:
|
|
144
66
|
|
|
145
|
-
- app
|
|
146
|
-
-
|
|
147
|
-
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
- `
|
|
298
|
-
-
|
|
299
|
-
- `
|
|
300
|
-
-
|
|
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
|
-
-
|
|
345
|
-
-
|
|
346
|
-
-
|
|
347
|
-
-
|
|
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
|
-
|
|
90
|
+
## Controllers
|
|
453
91
|
|
|
454
|
-
|
|
455
|
-
- choose method names deliberately because client code will call them directly
|
|
92
|
+
Controller rules:
|
|
456
93
|
|
|
457
|
-
|
|
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
|
-
|
|
101
|
+
Controller workflow:
|
|
460
102
|
|
|
461
|
-
|
|
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
|
-
|
|
108
|
+
## Client Pages
|
|
464
109
|
|
|
465
|
-
|
|
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
|
|
114
|
+
- the route path comes from the explicit string in `Router.page(...)`
|
|
469
115
|
|
|
470
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
-
|
|
140
|
+
`render` rules:
|
|
606
141
|
|
|
607
|
-
|
|
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
|
-
|
|
610
|
-
Router.error(404, { layout: false }, ({ data }) => <ErrorScreen code={404} data={data} />);
|
|
611
|
-
```
|
|
146
|
+
Error pages:
|
|
612
147
|
|
|
613
|
-
|
|
148
|
+
- use `Router.error(code, options, render)` in `client/pages/_messages/**`
|
|
614
149
|
|
|
615
|
-
|
|
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
|
-
|
|
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
|
|
161
|
+
const { Founder } = useContext();
|
|
636
162
|
await Founder.projects.updateProject(payload);
|
|
637
|
-
await Founder.projects.updateProject(payload).run();
|
|
638
163
|
```
|
|
639
164
|
|
|
640
|
-
|
|
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
|
-
|
|
167
|
+
## Manual Server Routes
|
|
661
168
|
|
|
662
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
-
|
|
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
|
-
-
|
|
738
|
-
- use
|
|
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
|
-
|
|
185
|
+
## Models And Aliases
|
|
741
186
|
|
|
742
|
-
|
|
187
|
+
Use Prisma typings from:
|
|
743
188
|
|
|
744
189
|
```ts
|
|
745
190
|
import type * as Models from '@models/types';
|
|
746
191
|
```
|
|
747
192
|
|
|
748
|
-
|
|
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
|
|
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
|
-
|
|
761
|
-
|
|
762
|
-
# Aliases
|
|
205
|
+
Relevant aliases:
|
|
763
206
|
|
|
764
|
-
|
|
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
|
-
- `@
|
|
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
|
-
|
|
212
|
+
## Task Playbooks
|
|
784
213
|
|
|
785
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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
|
-
|
|
231
|
+
### Add A New Manual Route
|
|
823
232
|
|
|
824
|
-
1.
|
|
825
|
-
2.
|
|
826
|
-
3.
|
|
827
|
-
4.
|
|
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
|
-
|
|
238
|
+
### Diagnose A Runtime Issue
|
|
834
239
|
|
|
835
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
260
|
+
- controller-backed app APIs over ad hoc manual `/api/...` route files
|
|
849
261
|
- service classes over random server helpers with hidden dependencies
|
|
850
|
-
-
|
|
851
|
-
-
|
|
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
|
-
|
|
265
|
+
## Discouraged Patterns
|
|
858
266
|
|
|
859
|
-
-
|
|
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
|
-
|
|
274
|
+
## Verification
|
|
867
275
|
|
|
868
|
-
|
|
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
|
|
873
|
-
- router/plugin changes: verify request context
|
|
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
|
-
|
|
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
|
|
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`
|