proteum 2.5.8 → 2.5.9

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021-2026 Gaetan Le Gac
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,286 +1,122 @@
1
- # Proteum
1
+ <!-- Logo: drop a square mark at docs/assets/logo.png and uncomment the line below. -->
2
+ <!-- <p align="center"><img src="docs/assets/logo.png" width="120" alt="Proteum" /></p> -->
2
3
 
3
- Proteum is an LLM-first SSR / SEO / TypeScript framework for full-stack web applications.
4
+ <h1 align="center">Proteum</h1>
4
5
 
5
- It is built for teams that want explicit server contracts, server-first rendering, deterministic generated artifacts, and a codebase that an AI agent can inspect without reverse-engineering hidden runtime magic.
6
+ <p align="center">
7
+ <strong>The explicit, agent-native full-stack TypeScript framework.</strong><br />
8
+ Server-first SSR &amp; SEO, zero runtime magic, and a codebase your AI agents can read without reverse-engineering.
9
+ </p>
6
10
 
7
- ## Sponsor
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/proteum"><img src="https://img.shields.io/npm/v/proteum.svg?color=2563eb&label=npm" alt="npm version" /></a>
13
+ <a href="https://www.npmjs.com/package/proteum"><img src="https://img.shields.io/npm/dm/proteum.svg?color=2563eb" alt="npm downloads" /></a>
14
+ <a href="#-requirements"><img src="https://img.shields.io/node/v/proteum.svg?color=2563eb" alt="node version" /></a>
15
+ <a href="./package.json"><img src="https://img.shields.io/badge/TypeScript-5.9-2563eb.svg?logo=typescript&logoColor=white" alt="TypeScript" /></a>
16
+ <a href="#-built-for-ai-agents"><img src="https://img.shields.io/badge/MCP-ready-7c3aed.svg" alt="MCP ready" /></a>
17
+ <a href="./LICENSE"><img src="https://img.shields.io/npm/l/proteum.svg?color=2563eb" alt="MIT license" /></a>
18
+ </p>
8
19
 
9
- Proteum is sponsored by [Unique Domains](https://unique.domains/?utm_source=github&utm_medium=referral&utm_campaign=repo_proteum&utm_content=top_sponsor).
20
+ <p align="center">
21
+ <a href="#-quick-start">Quick Start</a> ·
22
+ <a href="#-core-concepts">Concepts</a> ·
23
+ <a href="#-built-for-ai-agents">AI Agents</a> ·
24
+ <a href="#-the-cli">CLI</a> ·
25
+ <a href="#-documentation">Docs</a> ·
26
+ <a href="#-philosophy">Philosophy</a>
27
+ </p>
10
28
 
11
- [![Unique Domains](docs/assets/unique-domains-chip.png)](https://unique.domains/?utm_source=github&utm_medium=referral&utm_campaign=repo_proteum&utm_content=top_sponsor)
29
+ ---
12
30
 
13
- ## Why Proteum
31
+ ## What is Proteum?
14
32
 
15
- Most full-stack frameworks optimize first for human convenience.
33
+ Most full-stack frameworks optimize first for human convenience and lean on ambient runtime magic to get there. **Proteum optimizes for explicitness** — typed, machine-readable contracts that stay legible to humans *and* to the AI agents that increasingly maintain real codebases.
16
34
 
17
- Proteum optimizes first for:
35
+ Every route, controller, service, and layout is an exported **definition object**. The compiler reads them and emits deterministic contracts into `.proteum/`, so the framework can *tell you* what it discovered instead of asking you to guess. That same manifest powers the CLI, the dev profiler, and a built-in **Model Context Protocol** server — so an agent can answer "which controller handles this request, and why is it slow?" in a single low-token call.
18
36
 
19
- - explicit, typed, machine-readable contracts
20
- - SSR and SEO as framework primitives
21
- - server-first architecture with minimal client runtime
22
- - deterministic generation instead of ambient magic
23
- - codebases that stay explainable to humans and LLMs at the same time
24
-
25
- Proteum combines:
26
-
27
- - page-first SSR workflows similar to modern React meta-frameworks
28
- - explicit controller and service layers inspired by backend frameworks
29
- - generated manifests and contracts that make routes, services, layouts, and diagnostics easy to inspect
30
-
31
- ## Core Principles
32
-
33
- - **Server-first by default.** Put data loading in the page data function and keep client code focused on UI.
34
- - **Explicit request entrypoints.** Routes and controllers are exported definition objects.
35
- - **Local validation.** Declare controller input on `defineAction({ input, handler })`; handlers receive parsed `input`.
36
- - **Deterministic generation.** Proteum owns `.proteum/` and regenerates it from source.
37
- - **Explainability matters.** `proteum explain`, `proteum doctor`, `proteum diagnose`, `proteum perf`, and `proteum trace` expose the framework view of your app and its live requests, and the profiler renders the same diagnostics and perf surfaces for humans in dev.
38
- - **SEO is not an afterthought.** Identity, routes, layouts, and SSR data are part of the app contract.
39
-
40
- ## What a Proteum App Looks Like
41
-
42
- ```text
43
- my-app/
44
- identity.config.ts
45
- proteum.config.ts
46
- .env # optional file for required local env vars
47
- package.json
48
- commands/
49
- client/
50
- pages/
51
- _layout/
52
- components/
53
- islands/
54
- services/
55
- server/
56
- config/
57
- index.ts
58
- controllers/
59
- services/
60
- common/
61
- models/
62
- router/
63
- errors/
64
- .proteum/
65
- manifest.json
66
- client/
67
- common/
68
- server/
37
+ ```bash
38
+ npx proteum init my-app --name "My App"
69
39
  ```
70
40
 
71
- Important files:
72
-
73
- - `identity.config.ts`: typed app identity, naming, locale, and SEO-facing metadata defaults via `Application.identity({ ... })`
74
- - `proteum.config.ts`: typed Proteum compiler and connection settings such as `transpile` and `connect` via `Application.setup({ ... })`
75
- - `process.env` / optional `.env`: `PORT`, `ENV_*`, `URL`, `URL_INTERNAL`, any app-chosen variables referenced by `proteum.config.ts`, and `TRACE_*` environment variables loaded by the app
76
- - `server/config/*.ts`: plain typed config exports consumed by the explicit app bootstrap
77
- - `server/index.ts`: default-exported `defineApplication({ services, router, models, commands })` application graph
78
- - `client/pages/**`: SSR page entrypoints that default-export `definePageRoute({ path, options, data, render })`
79
- - `server/controllers/**`: generated API definitions that default-export `defineController({ path, actions })`
80
- - `commands/**`: dev-only internal commands that extend `Commands`
81
- - `server/services/**`: business logic that extends `Service`
82
- - `.proteum/**`: framework-owned generated contracts and manifests
83
-
84
- Required Proteum env vars:
85
-
86
- - `ENV_NAME`: `local` or `server`
87
- - `ENV_PROFILE`: `dev`, `testing`, or `prod`
88
- - `PORT`: default router port
89
- - `URL`: canonical absolute base URL for `Router.url(..., true)`
90
- - `URL_INTERNAL`: internal absolute base URL used by SSR and connected-project server calls
91
-
92
- If `proteum.config.ts` declares `connect`, Proteum also requires:
93
-
94
- - one explicit `connect.<Namespace>.source` value in `proteum.config.ts`
95
- - one explicit `connect.<Namespace>.urlInternal` value in `proteum.config.ts`
96
-
97
- Proteum does not provide defaults for required env vars. They must be defined explicitly in `process.env` or `.env`.
98
-
99
- Use `proteum explain env` to see the required env vars, their allowed values, and whether each one is currently provided.
100
-
101
- Optional trace env vars:
102
-
103
- - `TRACE_ENABLE`
104
- - `TRACE_REQUESTS_LIMIT`
105
- - `TRACE_EVENTS_LIMIT`
106
- - `TRACE_CAPTURE`
107
- - `TRACE_PERSIST_ON_ERROR`
108
- - `ENABLE_PROFILER`
109
-
110
- Optional `proteum.config.ts` fields:
41
+ ## ✨ Highlights
111
42
 
112
- - `transpile`: array of package names that Proteum should compile from `node_modules/` instead of treating as prebuilt vendor code
113
- - `connect`: connected project namespaces that should be merged into generated controller helpers
43
+ - **🧩 Explicit by design** routes, controllers, services, and the app graph are typed definition objects, not decorators or filename conventions hiding behind a compiler.
44
+ - **⚡ Server-first SSR** fast Preact / React 19 server rendering with a minimal client runtime and an islands model for interactivity exactly where you need it.
45
+ - **🔎 SEO as a primitive** — typed app identity, metadata, JSON-LD, and canonical URLs are part of the application contract, not a plugin you bolt on later.
46
+ - **🧠 Built for AI agents** — a machine-scope MCP router, compact JSON diagnostics, and generated manifests give LLMs a reliable map of your app. [Jump to details ↓](#-built-for-ai-agents)
47
+ - **📈 Live observability** — built-in request tracing, performance roll-ups, and an interactive dev profiler with charts over the same contracts the CLI reads.
48
+ - **🛡️ Validation at the edge** — declare action input once with `defineAction({ input, handler })`; handlers receive parsed, typed input.
49
+ - **🗄️ Prisma-first data layer** — typed Prisma 7 models (MySQL / MariaDB / Postgres) on `this.models`, with tagged-template SQL as an escape hatch when you need it.
50
+ - **🔗 Monorepo & connected apps** — compose multiple apps with typed cross-app controller contracts.
51
+ - **🚀 Production builds** — an `rspack` + Tailwind 4 pipeline with bundle analysis baked in.
114
52
 
115
- Example:
53
+ ## 📋 Requirements
116
54
 
117
- ```ts
118
- import { Application } from 'proteum/config';
119
-
120
- const PRODUCT_CONNECTED_SOURCE = process.env.PRODUCT_CONNECTED_SOURCE;
121
- const PRODUCT_URL_INTERNAL = process.env.PRODUCT_URL_INTERNAL;
122
-
123
- export default Application.setup({
124
- transpile: ['@acme/components'],
125
- connect: {
126
- Product: {
127
- source: PRODUCT_CONNECTED_SOURCE,
128
- urlInternal: PRODUCT_URL_INTERNAL,
129
- },
130
- },
131
- });
132
- ```
133
-
134
- Connected contract sources are provided explicitly through `proteum.config.ts` instead of being inferred from the namespace:
55
+ | Runtime | Version |
56
+ | ------- | ------------ |
57
+ | Node.js | `>= 20.19.0` |
58
+ | npm | `>= 3.10.10` |
135
59
 
136
- - local typed source value: `file:../product`
137
- - remote runtime-only source value: `github:owner/repo?ref=<sha-or-branch>&path=proteum.connected.json`
60
+ ## 🚀 Quick Start
138
61
 
139
- Use this for linked or workspace-local TypeScript packages that ship source files and must flow through Proteum's alias and SSR compilation pipeline.
140
-
141
- ## Example: Server Bootstrap
62
+ ```bash
63
+ # Scaffold a new app from deterministic built-in templates
64
+ npx proteum init my-app --name "My App"
65
+ cd my-app
142
66
 
143
- Proteum app services and router plugins are declared explicitly through typed config exports plus a default-exported `defineApplication(...)` definition object.
67
+ # Install and wire up agent instructions (AGENTS.md / CLAUDE.md)
68
+ npm install
69
+ npx proteum configure agents
144
70
 
145
- ```ts
146
- // server/config/user.ts
147
- import { Services, type ServiceConfig } from '@server/app';
148
- import AppContainer from '@server/app/container';
149
- import Router from '@server/services/router';
150
- import Users from '@/server/services/Users';
151
-
152
- type RouterBaseConfig = Omit<ServiceConfig<typeof Router>, 'plugins'>;
153
-
154
- export const usersConfig = Services.config(Users, {});
155
-
156
- export const routerBaseConfig = {
157
- currentDomain: AppContainer.Environment.router.currentDomain,
158
- http: {
159
- domain: 'example.com',
160
- port: AppContainer.Environment.router.port,
161
- ssl: true,
162
- upload: { maxSize: '10mb' },
163
- },
164
- context: () => ({}),
165
- } satisfies RouterBaseConfig;
71
+ # Start the compiler, SSR server, and hot-reload loop
72
+ npx proteum dev
166
73
  ```
167
74
 
168
- ```ts
169
- // server/index.ts
170
- import { defineApplication, type Application } from '@server/app';
171
- import Router from '@server/services/router';
172
- import SchemaRouter from '@server/services/schema/router';
173
- import Users from '@/server/services/Users';
174
- import * as userConfig from '@/server/config/user';
175
-
176
- type MyAppServices = {
177
- Users: Users;
178
- };
179
-
180
- type MyRouterPlugins = {
181
- schema: SchemaRouter;
182
- };
183
-
184
- export type MyRouter = Router<MyApp, MyRouterPlugins>;
185
- export interface MyApp extends Application, MyAppServices {
186
- Router: MyRouter;
187
- }
188
-
189
- const createRouter = (app: MyApp): MyRouter =>
190
- new Router<MyApp, MyRouterPlugins>(
191
- app,
192
- {
193
- ...userConfig.routerBaseConfig,
194
- plugins: {
195
- schema: new SchemaRouter({}, app),
196
- },
197
- },
198
- app
199
- );
200
-
201
- const createServices = (app: MyApp): MyAppServices => ({
202
- Users: new Users(app, userConfig.usersConfig, app),
203
- });
75
+ Then the everyday loop:
204
76
 
205
- const MyApplication = defineApplication({
206
- services: createServices,
207
- router: createRouter,
208
- });
209
-
210
- export default MyApplication;
77
+ ```bash
78
+ npx proteum check # refresh contracts, typecheck, and lint in one pass
79
+ npx proteum build --prod # production server + client bundles into bin/
80
+ node ./bin/server.js # run it
211
81
  ```
212
82
 
213
- Proteum reads `server/index.ts` as the source of truth for installed root services and router plugins, and reads `server/config/*.ts` `Services.config(...)` exports for typed config such as service priority overrides.
83
+ A typical app `package.json`:
214
84
 
215
- ## Router Cache Policy
216
-
217
- Browser cache headers are configurable per app through the optional `routerBaseConfig.http.cache` object. Omit it to keep Proteum's defaults.
218
-
219
- ```ts
220
- export const routerBaseConfig = {
221
- currentDomain: AppContainer.Environment.router.currentDomain,
222
- http: {
223
- domain: 'example.com',
224
- port: AppContainer.Environment.router.port,
225
- ssl: true,
226
- upload: { maxSize: '10mb' },
227
- cache: {
228
- html: {
229
- dynamic: {
230
- cacheControl: 'no-store, no-cache, must-revalidate, proxy-revalidate',
231
- surrogateControl: 'no-store',
232
- },
233
- static: {
234
- cacheControl: 'public, max-age=0, must-revalidate',
235
- surrogateControl: false,
236
- },
237
- },
238
- publicAssets: {
239
- dev: 'no-store',
240
- versioned: 'public, max-age=31536000, immutable',
241
- unversioned: 'public, max-age=0, must-revalidate',
242
- },
243
- },
244
- },
245
- context: () => ({}),
246
- } satisfies RouterBaseConfig;
85
+ ```json
86
+ {
87
+ "scripts": {
88
+ "dev": "proteum dev",
89
+ "check": "proteum check",
90
+ "build": "proteum build --prod",
91
+ "start": "node ./bin/server.js"
92
+ }
93
+ }
247
94
  ```
248
95
 
249
- Default public asset validators depend on the environment: dev disables `ETag` and `Last-Modified`, while non-dev enables them. Use `etag: false` and `lastModified: false` when an app needs to fully disable browser cache for `/public` assets.
96
+ ## 🧱 Core Concepts
250
97
 
251
- ## Example: Page
98
+ Everything you author is an explicit, typed definition object. Here is the whole surface in four snippets.
252
99
 
253
- Proteum pages are explicit SSR entrypoints.
100
+ ### Pages server-first SSR
254
101
 
255
102
  ```tsx
256
103
  import { definePageRoute } from '@common/router/definitions';
257
104
 
258
105
  export default definePageRoute({
259
106
  path: '/',
260
- options: {
261
- auth: false,
262
- layout: false,
263
- },
107
+ options: { auth: false, layout: false },
108
+ // Runs on the server. Every returned key becomes page data.
264
109
  data: ({ Plans, Stats }) => ({
265
110
  plans: Plans.getPlans(),
266
111
  stats: Stats.general(),
267
112
  }),
268
- render: ({ plans, stats }) => {
269
- return <LandingPage plans={plans} stats={stats} />;
270
- },
113
+ render: ({ plans, stats }) => <LandingPage plans={plans} stats={stats} />,
271
114
  });
272
115
  ```
273
116
 
274
- What happens here:
117
+ `path` and `options` (`auth`, `layout`, `static`, `redirectLogged`, …) are static and compiler-readable. Runtime references are allowed only inside `data` and `render`.
275
118
 
276
- - `path`, `options`, and error `code` metadata are static and compiler-readable
277
- - route behavior such as `auth`, `layout`, `static`, or `redirectLogged` lives in the options object
278
- - every key returned from `data` becomes page data
279
- - runtime app/client references are allowed only inside `data` and `render`
280
-
281
- ## Example: Controller
282
-
283
- Proteum controllers are explicit request entrypoints.
119
+ ### Controllers typed request entrypoints
284
120
 
285
121
  ```ts
286
122
  import { defineAction, defineController, schema } from '@generated/server/controller';
@@ -293,53 +129,16 @@ export default defineController({
293
129
  email: schema.string().email(),
294
130
  password: schema.string().min(8),
295
131
  }),
296
- handler: ({ input, services, request }) => {
297
- return services.Auth.loginWithPassword(input, request);
298
- },
132
+ handler: ({ input, services, request }) =>
133
+ services.Auth.loginWithPassword(input, request),
299
134
  }),
300
135
  },
301
136
  });
302
137
  ```
303
138
 
304
- Controller rules:
305
-
306
- - read request-scoped values from action context
307
- - declare validation once with `defineAction({ input, handler })`
308
- - call business logic through `services`, `models`, or `app`
309
- - return explicit values instead of relying on ambient globals
310
-
311
- ## Example: Command
312
-
313
- Proteum commands are explicit dev-only internal entrypoints.
314
-
315
- ```ts
316
- import { Commands } from '@server/app/commands';
317
-
318
- export default class DiagnosticsCommands extends Commands {
319
- public async ping() {
320
- const { Stats } = this.services;
321
-
322
- return {
323
- app: this.app.identity.identifier,
324
- domains: await Stats.general(),
325
- };
326
- }
327
- }
328
- ```
329
-
330
- Command rules:
139
+ Validation lives next to the handler. Business logic is reached through `services`, `models`, or `app` — never ambient globals.
331
140
 
332
- - files live under `commands/**/*.ts`
333
- - each file default-exports a class extending `Commands` from `@server/app/commands`
334
- - methods with bodies become generated dev commands
335
- - command path comes from the file path plus the method name
336
- - `export const commandPath = 'Custom/path'` can override the base path when needed
337
- - `commands/tsconfig.json` and `.proteum/server/commands.d.ts` give `/commands` its own dev-only alias and app typing surface
338
- - commands run only in dev contexts: `proteum command ...`, the dev profiler, or dev-only `__proteum/commands` endpoints
339
-
340
- ## Example: Service
341
-
342
- Proteum services keep business logic out of request handlers.
141
+ ### Services business logic, request-free
343
142
 
344
143
  ```ts
345
144
  import Service from '@server/app/service';
@@ -347,361 +146,176 @@ import Service from '@server/app/service';
347
146
  export default class StatsService extends Service<Config, {}, MyApp, MyApp> {
348
147
  public async general() {
349
148
  return {
350
- totalDomains: await this.models.SQL`SELECT COUNT(*) FROM domains`.value(),
149
+ // Prisma first: typed model access on this.models
150
+ totalDomains: await this.models.domain.count(),
351
151
  tlds: Object.keys(this.app.Domains.tlds).length,
152
+ // Need raw SQL? It's right there as an escape hatch:
153
+ // await this.models.SQL`SELECT COUNT(*) FROM domains`.value()
352
154
  };
353
155
  }
354
156
  }
355
157
  ```
356
158
 
357
- Service rules:
358
-
359
- - services extend `Service`
360
- - request context should be resolved in controllers, then passed into services as explicit values
361
- - services can use `this.services`, `this.models`, and `this.app`
362
-
363
- ## Framework-Owned Generated Contracts
364
-
365
- Proteum generates a machine-readable app description in `.proteum/`.
366
-
367
- Typical generated artifacts:
368
-
369
- - `.proteum/manifest.json`
370
- - `.proteum/client/routes.ts`
371
- - `.proteum/client/controllers.ts`
372
- - `.proteum/client/layouts.ts`
373
- - `.proteum/common/controllers.ts`
374
- - `.proteum/server/commands.ts`
375
- - `.proteum/server/routes.ts`
376
- - `.proteum/server/controllers.ts`
377
-
378
- These files are not hand-written application code. They are deterministic outputs derived from your app source and used by the runtime, the compiler, and tooling.
379
-
380
- This is one of Proteum's most important properties: the framework can explain what it discovered instead of asking you to guess.
381
-
382
- ## CLI
383
-
384
- Proteum ships with a compact CLI focused on the real app lifecycle:
385
-
386
- | Command | Purpose |
387
- | --- | --- |
388
- | `proteum dev` | Start the compiler, SSR server, and hot reload loop |
389
- | `proteum refresh` | Regenerate `.proteum` contracts and typings |
390
- | `proteum typecheck` | Refresh generated typings, then run TypeScript |
391
- | `proteum lint` | Run ESLint for the current app |
392
- | `proteum check` | Refresh, typecheck, and lint in one command |
393
- | `proteum build --prod` | Produce the production server and client bundles into `bin/`, with optional static or served bundle analysis |
394
- | `proteum connect` | Inspect connected-project sources, env, cached contracts, and imported controllers |
395
- | `proteum doctor` | Inspect manifest diagnostics |
396
- | `proteum explain` | Explain routes, controllers, services, layouts, conventions, env, and connected projects |
397
- | `proteum diagnose` | Combine owner lookup, diagnostics, trace data, and server logs for one concrete route or request target |
398
- | `proteum perf` | Aggregate request-trace performance into hot paths, one-request waterfalls, regressions, and memory drift views |
399
- | `proteum trace` | Inspect live dev-only request traces from the running SSR server |
400
- | `proteum mcp` | Start, inspect, or attach to the machine-scope MCP router that routes live app reads by `projectId` |
401
- | `proteum command` | Run a dev-only internal command locally or against a running dev server |
402
- | `proteum session` | Mint a dev-only auth session token and Playwright-ready cookie payload |
403
- | `proteum e2e` | Run Playwright with Proteum-managed `E2E_*` values instead of shell-leading env assignments |
404
- | `proteum verify` | Validate targeted changed-file checks, focused owner/request/browser workflows, or the full framework reference-app pass |
405
- | `proteum init` | Scaffold a new Proteum app with built-in deterministic templates |
406
- | `proteum configure agents` | Interactively configure tracked Proteum instruction files and Claude aliases |
407
- | `proteum create` | Scaffold a page, controller, command, route, or root service inside an app |
408
- | `proteum worktree` | Create or initialize Codex worktrees with a machine-readable bootstrap marker |
409
-
410
- Recommended daily workflow:
411
-
412
- ```bash
413
- proteum dev
414
- proteum refresh
415
- proteum check
416
- proteum verify changed --dry-run
417
- proteum build --prod
418
- proteum build --prod --analyze
419
- proteum build --prod --analyze --analyze-serve --analyze-port auto
420
- ```
421
-
422
- Only the bare `proteum build` and bare `proteum dev` commands print the welcome banner and include the active Proteum installation method. Any extra argument or option skips the banner. `proteum dev` is the only command that clears the interactive terminal before rendering its live session UI, exposes `CTRL+R` reload plus `CTRL+C` shutdown hotkeys, and prints connected app names plus successful connected `/ping` checks in the server-ready banner. Every `proteum dev` start ensures tracked Proteum instruction files contain the current managed `# Proteum Instructions` section and `CLAUDE.md` symlinks point to sibling `AGENTS.md` files before the dev loop begins.
423
-
424
- Useful inspection commands:
425
-
426
- ```bash
427
- proteum doctor
428
- proteum doctor --contracts
429
- proteum doctor --json
430
- proteum connect
431
- proteum connect --controllers
432
- proteum connect --strict
433
- proteum explain
434
- proteum explain owner /api/Auth/CurrentUser
435
- proteum explain --routes --controllers --commands
436
- proteum explain --routes --controllers --commands --full
437
- proteum explain --connected --controllers
438
- proteum explain --all --full
439
- proteum diagnose /
440
- proteum diagnose /dashboard --port 3101
441
- proteum perf top --since today
442
- proteum perf request /dashboard --port 3101
443
- proteum perf compare --baseline yesterday --target today --group-by route
444
- proteum perf memory --since 1h --group-by controller
445
- proteum mcp
446
- proteum mcp status
447
- proteum command proteum/diagnostics/ping
448
- proteum command proteum/diagnostics/ping --port 3101
449
- proteum session admin@example.com --role ADMIN --port 3101
450
- proteum session god@example.com --role GOD --json
451
- proteum e2e --port 3101 --session-email admin@example.com --session-role ADMIN tests/e2e/features/admin.spec.ts
452
- proteum trace requests
453
- proteum trace arm --capture deep
454
- proteum trace latest
455
- ```
159
+ ### Application — the explicit composition root
456
160
 
457
- Useful scaffolding commands:
161
+ ```ts
162
+ // server/index.ts — the canonical type root for services, router, models, and commands
163
+ import { defineApplication } from '@server/app';
458
164
 
459
- ```bash
460
- proteum init my-app --name "My App"
461
- proteum init my-app --name "My App" --dry-run --json
462
- proteum configure agents
463
- proteum worktree init --source /path/to/main-app
464
- proteum worktree create /path/to/.codex/worktrees/feature --source /path/to/main-app --branch feature/name
465
- proteum create page marketing/faq --route /faq
466
- proteum create controller Founder/projects --method list
467
- proteum create service Conversion/Plans
165
+ export default defineApplication({
166
+ services: createServices, // (app) => ({ Users: new Users(...) })
167
+ router: createRouter, // (app) => new Router(...)
168
+ });
468
169
  ```
469
170
 
470
- `proteum configure agents` writes a compact managed `# Proteum Instructions` router plus the task-specific instruction files that router points to. Standalone mode writes root documents into the app root; monorepo mode writes shared root documents such as `AGENTS.md`, `DOCUMENTATION.md`, `CODING_STYLE.md`, `diagnostics.md`, and `optimizations.md` into the chosen monorepo root and keeps only app-local instruction files in the Proteum app root. For each generated `AGENTS.md`, it creates a sibling `CLAUDE.md` symlink pointing to `AGENTS.md`. It preserves content outside managed sections and asks before replacing directories, foreign symlinks, or unrelated files. If you decline, that path is left untouched.
471
-
472
- Every `proteum dev` start runs the same idempotent instruction check. It updates missing or stale managed sections automatically and prompts only when a blocked path would need to be replaced.
473
-
474
- `proteum worktree init` writes `.proteum/worktree-bootstrap.json` for app roots under `/.codex/worktrees/`. The marker records `.env` copy status, refresh and dependency results, runtime status, key file hashes, and the active Proteum version. In monorepos with root tooling such as `prisma.config.ts` or npm workspaces, bootstrap also ensures the workspace-root `.env` exists, copying the source root `.env` when available or falling back to the source app `.env`. `proteum dev`, `proteum refresh`, `proteum runtime status`, `proteum verify`, and MCP `workflow_start` block inside Codex worktrees until the marker is fresh. Run `npx proteum worktree init --source <source-app-root>` for a new worktree, or add `--refresh` when stale state is reported. `PROTEUM_ALLOW_UNBOOTSTRAPPED_WORKTREE=1` bypasses the block but remains visible in runtime status, doctor, and MCP output.
475
-
476
- `proteum connect`, `proteum explain`, `proteum doctor`, and `proteum diagnose` share the same generated manifest and contract state. `proteum perf` uses the same dev request-trace store as the profiler `Perf` tab. `proteum runtime status` also inspects the configured router/HMR ports and returns an exact Start Dev action, so agents do not need to `curl` page routes to identify port owners. `proteum dev` exposes the app-root MCP contract at `/__proteum/mcp` and ensures one managed machine MCP daemon is running; `proteum mcp` is the machine-scope router agents register once. Agents should start with MCP `workflow_start`, use offline candidates and `data.readiness` to choose and prepare the correct app root when no dev server is live, then route repeated reads by the returned live `projectId`. For the full diagnostics and tracing model, see [docs/diagnostics.md](docs/diagnostics.md), [docs/mcp.md](docs/mcp.md), and [docs/request-tracing.md](docs/request-tracing.md).
477
-
478
- ## Dev Commands
479
-
480
- Proteum includes a dev-only command surface for internal testing, debugging, and one-off execution that should not become a normal controller or route.
481
-
482
- - commands live under `./commands/**/*.ts`
483
- - each file default-exports a class extending `Commands` from `@server/app/commands`
484
- - each method is addressed by `file/path/methodName`
485
- - Proteum creates `commands/tsconfig.json` when the folder exists so command files inherit the server alias/type project
486
- - `proteum command foo/bar` refreshes generated artifacts, builds the dev output, starts a temporary local dev server, runs the command, prints the result, and exits
487
- - `proteum command foo/bar --port 3101` runs the same command against an existing `proteum dev` instance
488
- - the dev profiler exposes the same command list and run action through the `Commands` tab
489
- - the same profiler also exposes `Explain`, `Doctor`, and `Diagnose` tabs backed by the same diagnostics contract as the CLI
490
-
491
- Proteum itself also ships a small built-in diagnostic command at `proteum/diagnostics/ping`, so the command surface is never empty in dev.
492
-
493
- ## Dev Sessions
494
-
495
- Proteum includes a dev-only auth bootstrap command for browser automation, API probes, and protected-route debugging without driving the login UI.
496
-
497
- - `proteum session <email>` mints a session for a known user
498
- - `--role <role>` asserts that the resolved user has the expected role before returning the session
499
- - `--port <port>` or `--url <baseUrl>` targets an existing `proteum dev` server
500
- - without `--port` or `--url`, Proteum starts a temporary local dev server, creates the session, prints the payload, and exits
501
- - output includes the raw token, a `Cookie:` header, and a Playwright-ready `cookies` payload
502
- - prefer this command when an LLM or test runner needs an authenticated dev context
503
- - do not use it when the login flow itself is what you are testing
504
-
505
- Typical usage:
506
-
507
- ```bash
508
- proteum session admin@example.com --role ADMIN --port 3101
509
- proteum session god@example.com --role GOD --json
171
+ Proteum reads `server/index.ts` as the single source of truth for installed root services and router plugins there is no hidden registry.
172
+
173
+ ## 🧠 Built for AI Agents
174
+
175
+ This is where Proteum is different. The compiler emits a machine-readable description of your app into `.proteum/`, and **every tool reads the same snapshot** the CLI, the dev-only HTTP endpoints, the profiler, and a Model Context Protocol server.
176
+
177
+ ```mermaid
178
+ flowchart LR
179
+ subgraph src["Your source"]
180
+ P["pages/**"]
181
+ C["controllers/**"]
182
+ S["services/**"]
183
+ A["server/index.ts"]
184
+ end
185
+ src --> CMP["Proteum compiler"]
186
+ CMP --> GEN[".proteum/<br/>manifest + contracts"]
187
+ GEN --> RT["SSR runtime"]
188
+ GEN --> CLI["CLI<br/>explain · doctor · diagnose"]
189
+ GEN --> PROF["Dev profiler"]
190
+ GEN --> MCP["MCP server<br/>/__proteum/mcp"]
191
+ MCP -.->|projectId-routed reads| AGENT(("AI agent"))
192
+ CLI -.->|compact JSON| AGENT
510
193
  ```
511
194
 
512
- The CLI talks to the running app over the dev-only `__proteum/session/start` endpoint and uses the auth service registered on the current app router. For the full guide, see [docs/dev-sessions.md](docs/dev-sessions.md).
513
-
514
- ## Request Tracing
195
+ An agent or you can ask the framework directly:
515
196
 
516
- Proteum includes a dev-only in-memory request trace buffer for auth, routing, controller, context, SSR, API, Prisma SQL, and render debugging.
517
-
518
- This is separate from `proteum explain` and `proteum doctor`: tracing is live request-time data, while explain/doctor are manifest-backed structure and diagnostics. `proteum perf` aggregates the same trace buffer into hot-path, waterfall, compare, and memory views. When you already know the failing path and want the fastest suspect list, start with `proteum diagnose`; when the issue is performance, start with `proteum perf`; then drop into raw trace output only if needed. When an agent needs repeated trace, perf, diagnose, status, owner, or instruction-routing reads from the same running app, use machine MCP `workflow_start`, then pass the returned `projectId` to follow-up app-bound MCP tools.
197
+ | Question | Command |
198
+ | --- | --- |
199
+ | Which controller owns this request? | `proteum explain owner /api/Auth/CurrentUser` |
200
+ | What did the framework detect? | `proteum doctor --json` |
201
+ | Why is this route failing? | `proteum diagnose /dashboard` |
202
+ | Where is the time going? | `proteum perf request /dashboard` |
203
+ | What happened in the last request? | `proteum trace latest` |
519
204
 
520
- When diagnosing or testing against an app, first read the default port from `PORT` or `./.proteum/manifest.json` and check whether a server is already running there. If it is, inspect the existing traces before reproducing the issue so you can collect past errors and their context.
205
+ **Why agents work well here:**
521
206
 
522
- - `proteum trace requests`: list the most recent request summaries
523
- - `proteum trace latest`: show the latest captured request
524
- - `proteum trace show <requestId>`: inspect one trace in detail
525
- - `proteum trace arm --capture deep`: force the next request into deep capture mode
526
- - `proteum trace export <requestId>`: write one trace to disk
527
- - `proteum trace latest --url http://127.0.0.1:3010`: target a non-standard dev base URL directly
528
- - `proteum diagnose /dashboard --port 3101`: combine owner lookup, diagnostics, trace summary, and buffered logs for one concrete path
529
- - `proteum perf top --since today`: rank the hottest traced paths in the selected window
530
- - `proteum perf request /dashboard --port 3101`: inspect one traced request with stage timings, CPU, SQL, render, and memory deltas
531
- - `proteum perf compare --baseline yesterday --target today --group-by route`: compare regression deltas between two windows
532
- - `proteum perf memory --since 1h --group-by controller`: rank recent heap and RSS drift
207
+ - **One MCP entry point.** `proteum mcp` runs a machine-scope router; `proteum dev` exposes each app at `/__proteum/mcp`. An agent calls `workflow_start`, gets a stable `projectId`, and routes every follow-up read to the right app.
208
+ - **Token-efficient output.** Diagnostics default to compact `proteum-agent-v1` JSON decision-ready summaries first, raw detail only behind `--full`, `--manifest`, or `--events`.
209
+ - **Generated instruction files.** `proteum configure agents` writes managed `AGENTS.md` / `CLAUDE.md` instruction routers, kept in sync on every `proteum dev` start.
210
+ - **Auth without UI automation.** `proteum session <email> --role ADMIN` mints a dev session (token + Playwright-ready cookie) so agents and E2E suites skip the login flow.
533
211
 
534
- Trace summaries include `sql=<count>`. Detailed trace output includes `Calls` and `SQL` sections so API/fetcher activity and Prisma queries can be inspected together.
212
+ > Full agent contract: [docs/mcp.md](docs/mcp.md), [docs/diagnostics.md](docs/diagnostics.md), and [docs/agent-routing.md](docs/agent-routing.md).
535
213
 
536
- Default behavior:
214
+ ## 📊 Diagnostics & Observability
537
215
 
538
- - tracing is enabled only in `profile: dev`
539
- - traces live in memory and are bounded by `TRACE_REQUESTS_LIMIT` and `TRACE_EVENTS_LIMIT`
540
- - payloads are summarized, long strings are truncated, and sensitive fields such as cookies, passwords, and tokens are redacted
541
- - `TRACE_PERSIST_ON_ERROR` can export crashing requests under `var/traces/`
542
- - `proteum dev` removes auto-persisted crash traces from `var/traces/` when the dev session stops
543
- - `ENABLE_PROFILER=true` reuses the same instrumentation path to populate `request.profiling` and the router `request.finished` hook with a reduced request/API/SQL snapshot in any environment, without retaining finished requests in the global trace buffer unless dev trace is also enabled
216
+ Proteum ships one request-instrumentation system with two shapes: a retained **dev trace** buffer and a reduced request-local **profiling** snapshot.
544
217
 
545
- Trace env example:
218
+ - **`proteum trace`** — live, in-memory traces for auth, routing, controller, context, SSR, API, Prisma SQL, and render, with sensitive fields redacted and payloads summarized.
219
+ - **`proteum perf`** — aggregates those same traces into hot paths, one-request waterfalls, regression comparisons, and memory-drift views.
220
+ - **Dev profiler** — the panel during `proteum dev` renders `Summary`, `Auth`, `Routing`, `Controller`, `SSR`, `API`, `SQL`, `Errors`, `Perf`, and more as visual charts over the same live contracts.
546
221
 
547
222
  ```bash
548
- export TRACE_ENABLE=true
549
- export TRACE_REQUESTS_LIMIT=200
550
- export TRACE_EVENTS_LIMIT=800
551
- export TRACE_CAPTURE=resolve
552
- export TRACE_PERSIST_ON_ERROR=true
553
- export ENABLE_PROFILER=true
223
+ proteum trace arm --capture deep # force the next request into deep capture
224
+ proteum perf top --since today # rank the hottest traced paths
225
+ proteum perf compare --baseline yesterday --target today --group-by route
554
226
  ```
555
227
 
556
- Capture modes:
557
-
558
- - `summary`: request lifecycle plus high-signal events
559
- - `resolve`: adds auth, route resolution, and controller/context steps
560
- - `deep`: adds route skip reasons and deeper payload summaries for one request investigation
561
-
562
- In the dev profiler, the request-trace tabs are now visual as well as textual: `Summary`, `Auth`, `Routing`, `Controller`, `SSR`, `API`, `SQL`, `Errors`, `Diagnose`, `Explain`, `Doctor`, `Commands`, and `Cron` all add focused charts over the same live contracts, while `Perf` remains the aggregated hot-path, breakdown, regression, and memory surface exposed by `proteum perf`.
563
-
564
- The trace and perf CLIs talk to the running dev server over the dev-only `__proteum/trace` and `__proteum/perf` HTTP endpoints. Use `--port` for a different local port or `--url` when the host itself is non-standard. For the full guide, see [docs/request-tracing.md](docs/request-tracing.md).
565
-
566
- ## LLM-Friendly By Design
228
+ > Full guide: [docs/request-tracing.md](docs/request-tracing.md).
567
229
 
568
- Proteum is built so an agent can answer these questions quickly and reliably:
230
+ ## 🛠️ The CLI
569
231
 
570
- - What is this app called, and what are its SEO defaults?
571
- - Which routes exist?
572
- - Which controller handles a request?
573
- - Which services are installed?
574
- - Which layouts exist?
575
- - Which diagnostics did the framework detect?
232
+ A compact CLI focused on the real app lifecycle.
576
233
 
577
- Proteum answers those questions with explicit artifacts:
578
-
579
- - `identity.config.ts` for app identity
580
- - `proteum.config.ts` for compiler and connected-project setup
581
- - `PORT`, `ENV_*`, `URL`, `URL_INTERNAL`, app-chosen connected-project config values, `TRACE_*`, and `ENABLE_PROFILER` env vars for the environment surface
582
- - `server/index.ts` for the explicit root service graph
583
- - `.proteum/manifest.json` for machine-readable app structure
584
- - `proteum explain` for compact framework introspection, and `proteum explain --manifest` when the full manifest is required
585
- - `proteum doctor --json` for structured diagnostics
586
- - `proteum doctor --contracts --json` for generated-artifact and manifest-owned file checks
587
- - `proteum explain owner <query>` for fast ownership lookup over routes, controllers, files, and generated artifacts
588
- - `proteum diagnose <path>` for a one-shot request diagnosis surface
589
- - `proteum perf top|request|compare|memory` for request-trace performance rollups
590
- - `proteum mcp` for one managed machine-scope MCP router that starts with `workflow_start` and routes repeated low-token agent reads by `projectId`
591
- - `/__proteum/mcp` from a running `proteum dev` server as the app-root runtime endpoint behind that router
592
- - the profiler `Explain`, `Doctor`, `Diagnose`, and `Perf` tabs for a human-readable view over the same diagnostics and trace-derived perf contracts
593
- - `proteum command ...` plus the profiler `Commands` tab for dev-only internal execution
594
- - `proteum session ...` for explicit authenticated dev browser or API bootstrapping without login UI automation
595
- - `proteum e2e ...` for Playwright runs that need `E2E_BASE_URL`, `E2E_PORT`, or `E2E_AUTH_TOKEN` without shell-leading env assignments
596
-
597
- If you are an LLM or automation agent, start here:
598
-
599
- 1. Use `proteum mcp` as the one registered MCP server; `proteum dev` ensures the managed machine daemon is running.
600
- 2. Call MCP `workflow_start` with `cwd` or a known `projectId`; if it is ambiguous or returns offline app candidates, use `project_resolve { cwd }`, choose the intended app root, follow its fresh-copy readiness and port-inspected next actions when needed, then retry `workflow_start`.
601
- 3. If `workflow_start` returns `data.readiness.state="blocked"`, resolve the returned setup actions first. The read-only readiness preflight covers app/root `.env`, dependency install root, generated manifest state, local connected producer apps, Prisma/client readiness, redacted database URL shape, local database TCP reachability, and exact safe setup commands.
602
- 4. If the app root is inside `/.codex/worktrees/` and `workflow_start` or a guarded CLI command reports missing/stale bootstrap, run the returned `npx proteum worktree init --source <source-app-root>` command before runtime reads.
603
- 5. Use the returned live `projectId` with MCP `runtime_status`, `orient`, `instructions_resolve`, `route_candidates`, `explain_summary`, `diagnose`, `trace_show`, `perf_request`, and `logs_tail` before CLI equivalents for repeated read-only app state.
604
- 6. Treat returned instruction previews as the allowed scope for read-only discovery and diagnostics. Read full file contents only before edits or git writes, when `fullRead`/`fullReadPolicy` requires it, or when compact previews are insufficient.
605
- 7. Use compact CLI commands for fallback, `dev`, `build`, `check`, `verify`, migrations, E2E, and final reproducible terminal evidence.
606
- 8. Use `proteum diagnose`, `proteum perf`, and compact `proteum trace` for reproducible command evidence when MCP is unavailable or the terminal output itself is needed.
607
- 9. If machine MCP routing fails, run `proteum mcp status` and `proteum runtime status` from the intended app root; if no live session exists, use the exact MCP offline or runtime-status next action. If the same app already responds on the configured port without live tracking, use or repair that runtime instead of starting another server. If a live session exists but runtime/MCP is unreachable, stop the listed session file first, then start dev again and retry `workflow_start`. Do not run diagnose, trace, or perf reads while runtime health is unreachable, and do not `curl` normal page routes to identify port ownership.
608
- 10. Inspect `server/index.ts`, controllers, services, or pages only after the routing/diagnostic surfaces identify the relevant owner. Do not run broad owner searches after MCP already returned the route/page/controller owner.
609
- 11. If the task touches a protected route or controller in dev and login UX is not the feature under test, use `proteum e2e --session-email <email> --session-role <role>` for Playwright suites or `proteum session <email> --role <role>` before direct HTTP calls.
610
-
611
- For implementation rules in a real Proteum app, treat the routed local `AGENTS.md` files plus `proteum orient`, compact CLI diagnostics, and MCP repeated-read surfaces as the task contract. This README is the framework overview, not the project-local instruction layer.
612
-
613
- ## What Proteum Avoids
614
-
615
- Proteum intentionally avoids several patterns that make frameworks harder to inspect and harder to trust:
616
-
617
- - hidden runtime globals
618
- - implicit service registration hidden behind bootstrap helpers
619
- - implicit request state inside business services
620
- - controller validation defined far away from the handler
621
- - route systems that cannot be explained without reading the compiler
622
- - generated code that hides where it came from
623
-
624
- ## Real-World Shape
625
-
626
- Proteum is already used on large application surfaces with:
234
+ | Command | Purpose |
235
+ | --- | --- |
236
+ | `proteum dev` | Compiler + SSR server + hot-reload loop, with a live profiler |
237
+ | `proteum build --prod` | Production server & client bundles into `bin/` (`--analyze` for bundle reports) |
238
+ | `proteum refresh` | Regenerate `.proteum` contracts and typings |
239
+ | `proteum check` | Refresh, typecheck, and lint in one command |
240
+ | `proteum create` | Scaffold a page, controller, command, route, or service |
241
+ | `proteum init` | Scaffold a new app from deterministic templates |
242
+ | `proteum explain` | Inspect routes, controllers, services, layouts, env, and connected projects |
243
+ | `proteum doctor` | Inspect manifest diagnostics |
244
+ | `proteum diagnose` | Owner + diagnostics + traces + logs for one route or request |
245
+ | `proteum perf` / `proteum trace` | Performance roll-ups and live request traces |
246
+ | `proteum mcp` | Machine-scope MCP router for live app reads |
247
+ | `proteum connect` | Inspect connected-project sources, env, and imported controllers |
248
+ | `proteum session` / `proteum e2e` | Dev auth bootstrap and Playwright runs without shell env juggling |
249
+ | `proteum verify` | Targeted change checks or the full reference-app pass |
627
250
 
628
- - many controllers and services
629
- - SSR landing pages and authenticated app pages
630
- - generated controller accessors injected into page context
631
- - build, typecheck, lint, and diagnostic workflows run from the CLI
251
+ Run `proteum --help` or `proteum help <command>` for the full reference.
632
252
 
633
- In real apps, the common `package.json` scripts look like this:
253
+ ## 🏗️ Project Structure
634
254
 
635
- ```json
636
- {
637
- "scripts": {
638
- "dev": "proteum dev",
639
- "refresh": "proteum refresh",
640
- "typecheck": "proteum typecheck",
641
- "check": "proteum check",
642
- "build": "proteum build --prod",
643
- "start": "node ./bin/server.js"
644
- }
645
- }
255
+ ```text
256
+ my-app/
257
+ ├─ identity.config.ts # typed app identity, locale, and SEO defaults
258
+ ├─ proteum.config.ts # compiler + connected-project settings
259
+ ├─ client/
260
+ │ ├─ pages/ # SSR page entrypoints (definePageRoute)
261
+ │ ├─ islands/ # interactive client islands
262
+ │ ├─ components/
263
+ │ └─ services/
264
+ ├─ server/
265
+ │ ├─ index.ts # defineApplication — the app graph
266
+ │ ├─ controllers/ # defineController + defineAction
267
+ │ ├─ services/ # business logic (extends Service)
268
+ │ └─ config/
269
+ ├─ common/ # shared router contracts, models, errors
270
+ ├─ commands/ # dev-only internal commands
271
+ └─ .proteum/ # framework-owned generated contracts (do not edit)
646
272
  ```
647
273
 
648
- ## Installation
649
-
650
- Proteum currently targets:
651
-
652
- - Node.js `>=20.19.0`
653
- - npm `>=3.10.10`
654
-
655
- Install in an app:
656
-
657
- ```bash
658
- npm install proteum
659
- ```
274
+ ## 📚 Documentation
660
275
 
661
- You can bootstrap a new app with:
276
+ | Topic | Guide |
277
+ | --- | --- |
278
+ | Diagnostics & explainability | [docs/diagnostics.md](docs/diagnostics.md) |
279
+ | Model Context Protocol (MCP) | [docs/mcp.md](docs/mcp.md) |
280
+ | Request tracing & perf | [docs/request-tracing.md](docs/request-tracing.md) |
281
+ | Agent routing & token efficiency | [docs/agent-routing.md](docs/agent-routing.md) |
282
+ | Dev commands | [docs/dev-commands.md](docs/dev-commands.md) |
283
+ | Dev sessions | [docs/dev-sessions.md](docs/dev-sessions.md) |
284
+ | Migrating to 2.5 | [docs/migration-2.5.md](docs/migration-2.5.md) |
662
285
 
663
- ```bash
664
- npx proteum init my-app --name "My App"
665
- npx proteum init my-app --name "My App" --dry-run --json
666
- ```
286
+ ## 🧭 Philosophy
667
287
 
668
- Then use the normal workflow:
288
+ Proteum is opinionated on purpose. It intentionally **avoids** the patterns that make frameworks hard to inspect and hard to trust:
669
289
 
670
- ```bash
671
- npm install
672
- npx proteum configure agents
673
- npx proteum dev
674
- npx proteum check
675
- npx proteum build --prod
676
- ```
290
+ - ❌ hidden runtime globals
291
+ - ❌ implicit service registration behind bootstrap helpers
292
+ - request state smuggled into business services
293
+ - validation defined far from its handler
294
+ - routing you cannot explain without reading the compiler
295
+ - generated code that hides where it came from
677
296
 
678
- ## Migrating To 2.5
297
+ In their place: explicit definition objects, a single canonical app graph, validation at the edge, and deterministic generation you can read, diff, and trace back to source.
679
298
 
680
- Proteum 2.5 removes the old contextual route/controller magic. Apps migrate by replacing ambient `@app` imports, top-level `Router.*(...)` route calls, controller classes, and `Application` subclasses with explicit definition objects and typed runtime callback parameters.
299
+ ## 🧰 Built With
681
300
 
682
- Use [the 2.5 migration guide](docs/migration-2.5.md) for the full checklist.
301
+ [TypeScript](https://www.typescriptlang.org/) · [Preact](https://preactjs.com/) / [React 19](https://react.dev/) · [Express](https://expressjs.com/) · [Prisma 7](https://www.prisma.io/) · [rspack](https://rspack.dev/) · [Tailwind CSS 4](https://tailwindcss.com/) · [Zod](https://zod.dev/) · [Ink](https://github.com/vadimdemedes/ink) · [Model Context Protocol](https://modelcontextprotocol.io/)
683
302
 
684
- ## Repository Structure
303
+ ## 🤝 Contributing
685
304
 
686
- This repository is organized around the same explicit framework surface it exposes:
305
+ Issues and pull requests are welcome. Proteum is actively hardening its explicit model, and the direction is deliberate: fewer ways to do the same thing, more contracts the framework can explain on its own.
687
306
 
688
- - `cli/`: compiler, commands, diagnostics, and developer workflow
689
- - `client/`: client runtime, page registration, islands, and router behavior
690
- - `server/`: controller base classes, services, runtime, and SSR server behavior
691
- - `common/`: shared router contracts, models, request/response types, and utilities
692
- - `docs/`: focused design notes and internal documentation
693
- - `agents/`: agent-specific conventions and scaffolding used in Proteum-based projects
307
+ When proposing a change, start from a concrete mismatch or risk visible in a real app, show the target API with realistic client/server usage, and keep generated code deterministic and auditable.
694
308
 
695
- ## Status
309
+ ## 💜 Sponsors
696
310
 
697
- Proteum is actively hardening its explicit model.
311
+ Proteum is proudly sponsored by **[Unique Domains](https://unique.domains/?utm_source=github&utm_medium=referral&utm_campaign=repo_proteum&utm_content=top_sponsor)**.
698
312
 
699
- The direction is deliberate:
313
+ <p>
314
+ <a href="https://unique.domains/?utm_source=github&utm_medium=referral&utm_campaign=repo_proteum&utm_content=top_sponsor">
315
+ <img src="docs/assets/unique-domains-chip.png" alt="Unique Domains" />
316
+ </a>
317
+ </p>
700
318
 
701
- - less runtime magic
702
- - more generated and auditable contracts
703
- - clearer controller and service boundaries
704
- - better SSR, SEO, and explainability defaults
705
- - better ergonomics for both humans and AI agents
319
+ ## 📄 License
706
320
 
707
- If you want a framework that treats machine-readable architecture as a first-class feature, Proteum is what this repository is building.
321
+ [MIT](./LICENSE) © [Gaetan Le Gac](https://github.com/gaetanlegac)
@@ -0,0 +1,35 @@
1
+ type TResolveRequestOptions = { preferApp: boolean };
2
+
3
+ type TResolveServerExternalRequestOptions = {
4
+ context?: string;
5
+ frameworkRoots: string[];
6
+ request: string;
7
+ resolveRequest: (request: string, options: TResolveRequestOptions) => string;
8
+ };
9
+
10
+ const normalizeModulePath = (value?: string) => (value || '').replace(/\\/g, '/');
11
+
12
+ export const isFrameworkSourceContext = (context: string | undefined, frameworkRoots: string[]) => {
13
+ const normalizedContext = normalizeModulePath(context);
14
+
15
+ return frameworkRoots.some((rootPath) => {
16
+ const normalizedRootPath = normalizeModulePath(rootPath);
17
+
18
+ return normalizedContext === normalizedRootPath || normalizedContext.startsWith(normalizedRootPath + '/');
19
+ });
20
+ };
21
+
22
+ export const resolveServerExternalRequest = ({
23
+ context,
24
+ frameworkRoots,
25
+ request,
26
+ resolveRequest,
27
+ }: TResolveServerExternalRequestOptions) => {
28
+ try {
29
+ return resolveRequest(request, {
30
+ preferApp: !isFrameworkSourceContext(context, frameworkRoots),
31
+ });
32
+ } catch {
33
+ return request;
34
+ }
35
+ };
@@ -10,6 +10,7 @@ import { type Configuration } from '@rspack/core';
10
10
  import cli from '@cli';
11
11
  import createCommonConfig, { TCompileMode, TCompileOutputTarget, regex } from '../common';
12
12
  import { toRspackAliases } from '../common/rspackAliases';
13
+ import { resolveServerExternalRequest } from './externals';
13
14
 
14
15
  // Type
15
16
  import type { App } from '../../app';
@@ -158,7 +159,7 @@ export default function createCompiler(
158
159
  './client-manifest.json',
159
160
 
160
161
  // node_modules
161
- function ({ request }, callback) {
162
+ function ({ context, request }, callback) {
162
163
  const shouldCompile =
163
164
  request !== undefined &&
164
165
  // Local files
@@ -177,8 +178,16 @@ export default function createCompiler(
177
178
  //console.log('isNodeModule', request, isNodeModule);
178
179
 
179
180
  if (!shouldCompile) {
180
- // Externalize to a commonjs module using the request path
181
- return callback(undefined, 'commonjs ' + request);
181
+ // Resolve server externals from their source owner. Bare runtime requires from
182
+ // the dev output can otherwise hit the framework node_modules symlink first.
183
+ const resolvedRequest = resolveServerExternalRequest({
184
+ context,
185
+ frameworkRoots,
186
+ request,
187
+ resolveRequest: (externalRequest, options) => cli.paths.resolveRequest(externalRequest, options),
188
+ });
189
+
190
+ return callback(undefined, 'commonjs ' + resolvedRequest);
182
191
  }
183
192
 
184
193
  // Continue without externalizing the import
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "proteum",
3
3
  "description": "LLM-first Opinionated Typescript Framework for web applications.",
4
- "version": "2.5.8",
4
+ "version": "2.5.9",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/proteum.git",
7
7
  "license": "MIT",
@@ -0,0 +1,67 @@
1
+ const assert = require('node:assert/strict');
2
+ const path = require('node:path');
3
+
4
+ const coreRoot = path.resolve(__dirname, '..');
5
+ process.env.TS_NODE_PROJECT = path.join(coreRoot, 'cli', 'tsconfig.json');
6
+ process.env.TS_NODE_TRANSPILE_ONLY = '1';
7
+
8
+ require('ts-node/register/transpile-only');
9
+
10
+ const {
11
+ isFrameworkSourceContext,
12
+ resolveServerExternalRequest,
13
+ } = require('../cli/compiler/server/externals.ts');
14
+
15
+ test('server external resolution prefers app dependencies outside framework source', () => {
16
+ const optionsSeen = [];
17
+ const resolved = resolveServerExternalRequest({
18
+ context: '/repo/node_modules/@klair/usecases/src/mcp/groups/workers',
19
+ frameworkRoots: ['/framework/core'],
20
+ request: '@prisma/client',
21
+ resolveRequest: (request, options) => {
22
+ optionsSeen.push(options);
23
+ assert.equal(request, '@prisma/client');
24
+ return options.preferApp
25
+ ? '/repo/node_modules/@prisma/client/default.js'
26
+ : '/framework/core/node_modules/@prisma/client/default.js';
27
+ },
28
+ });
29
+
30
+ assert.equal(resolved, '/repo/node_modules/@prisma/client/default.js');
31
+ assert.deepEqual(optionsSeen, [{ preferApp: true }]);
32
+ });
33
+
34
+ test('server external resolution prefers framework dependencies for framework source', () => {
35
+ const resolved = resolveServerExternalRequest({
36
+ context: '/framework/core/server',
37
+ frameworkRoots: ['/framework/core'],
38
+ request: 'express',
39
+ resolveRequest: (request, options) => {
40
+ assert.equal(request, 'express');
41
+ return options.preferApp
42
+ ? '/repo/node_modules/express/index.js'
43
+ : '/framework/core/node_modules/express/index.js';
44
+ },
45
+ });
46
+
47
+ assert.equal(resolved, '/framework/core/node_modules/express/index.js');
48
+ });
49
+
50
+ test('server external resolution falls back to the bare request when resolution fails', () => {
51
+ const resolved = resolveServerExternalRequest({
52
+ context: '/repo/server',
53
+ frameworkRoots: ['/framework/core'],
54
+ request: 'optional-peer',
55
+ resolveRequest: () => {
56
+ throw new Error('missing');
57
+ },
58
+ });
59
+
60
+ assert.equal(resolved, 'optional-peer');
61
+ });
62
+
63
+ test('framework source context matching handles exact and nested roots', () => {
64
+ assert.equal(isFrameworkSourceContext('/framework/core', ['/framework/core']), true);
65
+ assert.equal(isFrameworkSourceContext('/framework/core/server', ['/framework/core']), true);
66
+ assert.equal(isFrameworkSourceContext('/repo/node_modules/@klair/usecases', ['/framework/core']), false);
67
+ });