webspresso 0.0.94 → 0.0.96

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/.agents/ADMIN.md +225 -0
  2. package/.agents/AGENTS.md +72 -0
  3. package/.agents/ARCHITECTURE.md +119 -0
  4. package/.agents/AUTH.md +103 -0
  5. package/.agents/CLI.md +115 -0
  6. package/.agents/COMPRESSION.md +59 -0
  7. package/.agents/CONVENTIONS.md +46 -0
  8. package/.agents/EMAIL.md +141 -0
  9. package/.agents/ERRORS.md +201 -0
  10. package/.agents/MCP.md +103 -0
  11. package/.agents/ORM.md +230 -0
  12. package/.agents/PLUGINS.md +419 -0
  13. package/.agents/QUEUE.md +113 -0
  14. package/.agents/REALTIME.md +213 -0
  15. package/.agents/ROUTING.md +292 -0
  16. package/.agents/SERVICES.md +341 -0
  17. package/.agents/SHUTDOWN.md +67 -0
  18. package/.agents/STREAMING.md +73 -0
  19. package/.agents/WORKFLOW.md +27 -0
  20. package/.agents/internal/DOCUMENTATION_REVIEW.md +485 -0
  21. package/.agents/internal/TECHNICAL_REVIEW.md +428 -0
  22. package/.cursorrules +14 -0
  23. package/AGENTS.md +175 -0
  24. package/CLAUDE.md +54 -0
  25. package/README.md +18 -14
  26. package/bin/commands/agents-init.js +169 -0
  27. package/bin/commands/api.js +114 -48
  28. package/bin/commands/doctor.js +35 -0
  29. package/bin/commands/new.js +13 -2
  30. package/bin/commands/page.js +75 -89
  31. package/bin/commands/service.js +77 -0
  32. package/bin/webspresso.js +2 -2
  33. package/core/compileSchema.js +1 -1
  34. package/core/errors/middleware.js +22 -2
  35. package/core/errors/normalize.js +29 -2
  36. package/{utils/schemaCache.js → core/schema-cache.js} +2 -3
  37. package/index.d.ts +306 -0
  38. package/index.js +35 -1
  39. package/package.json +19 -6
  40. package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +22 -1
  41. package/plugins/csrf/index.js +3 -2
  42. package/plugins/csv/engine.js +447 -0
  43. package/plugins/csv/index.js +94 -0
  44. package/plugins/csv/middleware.js +80 -0
  45. package/plugins/csv/services.js +138 -0
  46. package/plugins/file-manager/admin-component.js +613 -0
  47. package/plugins/file-manager/api-handlers.js +261 -0
  48. package/plugins/file-manager/core.js +601 -0
  49. package/plugins/file-manager/index.js +139 -0
  50. package/plugins/file-manager/security.js +207 -0
  51. package/plugins/index.js +8 -0
  52. package/plugins/polar/CHANGELOG.md +18 -0
  53. package/plugins/polar/README.md +30 -7
  54. package/plugins/polar/index.js +6 -1
  55. package/plugins/polar/src/billing-hooks.js +51 -0
  56. package/plugins/polar/src/config.js +2 -0
  57. package/plugins/polar/src/rate-limit.js +96 -28
  58. package/plugins/polar/src/routes.js +3 -2
  59. package/plugins/polar/src/urls.js +26 -2
  60. package/plugins/polar/src/user-resolver.js +5 -1
  61. package/plugins/rate-limit/index.js +5 -1
  62. package/plugins/xlsx/engine.js +397 -0
  63. package/plugins/xlsx/index.js +91 -0
  64. package/plugins/xlsx/middleware.js +87 -0
  65. package/plugins/xlsx/services.js +118 -0
  66. package/src/api/api-loader.js +233 -0
  67. package/src/api/define-api.js +35 -0
  68. package/src/discovery/file-route-parser.js +201 -0
  69. package/src/discovery/scan-routes.js +265 -0
  70. package/src/file-router.js +6 -0
  71. package/src/modules/define-module.js +99 -0
  72. package/src/modules/module-discovery.js +117 -0
  73. package/src/pages/define-page.js +36 -0
  74. package/src/pages/page-loader.js +214 -0
  75. package/src/routing/mount-discovered-routes.js +78 -0
  76. package/src/routing/route-table.js +191 -0
  77. package/src/server.js +112 -15
  78. package/src/services/builtins/file-manager.js +178 -0
  79. package/src/services/index.js +2 -0
  80. package/bin/commands/skill.js +0 -187
  81. package/templates/skills/webspresso-usage/REFERENCE-framework.md +0 -289
  82. package/templates/skills/webspresso-usage/REFERENCE-kernel.md +0 -93
  83. package/templates/skills/webspresso-usage/SKILL.md +0 -48
@@ -0,0 +1,59 @@
1
+ # Webspresso HTTP Response Compression Guide
2
+
3
+ [← Back to AGENTS.md](.agents/AGENTS.md)
4
+
5
+ ---
6
+
7
+ ## 1. Overview
8
+
9
+ Webspresso provides a zero-dependency, streaming-first HTTP response compression layer built entirely with native `node:zlib`. It transparently compresses compressible responses (HTML, JSON, JS, CSS, SVG, XML) using Brotli (`br`), Gzip (`gzip`), or Deflate (`deflate`).
10
+
11
+ ---
12
+
13
+ ## 2. Configuration
14
+
15
+ Compression is disabled by default to avoid unnecessary CPU overhead. Enable via `createApp`:
16
+
17
+ ```js
18
+ const { app } = createApp({
19
+ pagesDir: './pages',
20
+ server: {
21
+ compression: true, // Enables default 1 KB threshold with Brotli + Gzip
22
+ },
23
+ });
24
+ ```
25
+
26
+ ### Advanced Options:
27
+ ```js
28
+ const { app } = createApp({
29
+ pagesDir: './pages',
30
+ server: {
31
+ compression: {
32
+ threshold: 2048, // Only compress responses >= 2 KB (default: 1024)
33
+ encodings: ['br', 'gzip'], // Algorithm preference
34
+ level: 6, // Compression level (1-9)
35
+ filter: (req, res) => {
36
+ if (req.path.startsWith('/downloads')) return false;
37
+ return true;
38
+ },
39
+ },
40
+ },
41
+ });
42
+ ```
43
+
44
+ ---
45
+
46
+ ## 3. Route-Level Opt-out
47
+
48
+ Individual routes can opt-out of compression (e.g. to mitigate BREACH attacks or for already-compressed dynamic payloads):
49
+
50
+ ```js
51
+ // pages/api/sensitive-export.get.js
52
+ module.exports = async function handler(req, res) {
53
+ res.compress(false); // Disables compression for this response
54
+
55
+ res.json({
56
+ secretToken: 'xyz-secret-token',
57
+ });
58
+ };
59
+ ```
@@ -0,0 +1,46 @@
1
+ # Webspresso Coding Standards & Conventions
2
+
3
+ [← Back to AGENTS.md](.agents/AGENTS.md)
4
+
5
+ ---
6
+
7
+ ## 1. Core Policies
8
+
9
+ 1. **Zero External Dependencies Policy**: Core framework functionality (Auth, JWT, CORS, Routing, Nunjucks helpers) must avoid unnecessary third-party npm dependencies. Rely strictly on native Node.js standard library modules (`crypto`, `path`, `fs`, `events`, `async_hooks`).
10
+ 2. **Strict Signature & Schema Verification**: Always verify function signatures and object schemas by inspecting authoritative source files before calling or extending them.
11
+ 3. **File & Component Separation**: Keep client-side components in clean standalone JS files (`component.js` / `componentFile`) rather than escaping inline template strings.
12
+ 4. **Log & Traceback Inspection**: Base all bug fixes strictly on exact runtime error log tracebacks and empirical test output. Never swallow errors or return dummy fallbacks.
13
+ 5. **No Superficial Symptom Patches**: Never resolve errors by masking symptoms or deleting failing unit tests. Always resolve the underlying contract breakage.
14
+ 6. **Strict File-Based Routing Policy (NO Express `routes/` or `app.get()`)**:
15
+ - **NEVER** create a `routes/` or `src/routes/` directory.
16
+ - **NEVER** call `express.Router()`, `app.get()`, `app.post()`, or `app.use()` to register application API endpoints.
17
+ - **ALWAYS** define API routes under `pages/api/...` with HTTP method extensions:
18
+ - `GET /api/notes` → `pages/api/notes.get.js`
19
+ - `POST /api/notes` → `pages/api/notes.post.js`
20
+ - `GET /api/notes/:id` → `pages/api/notes/[id].get.js`
21
+ - `DELETE /api/notes/:id` → `pages/api/notes/[id].delete.js`
22
+ - Every file route module must export `{ schema, middleware, handler }` or an `async (req, res)` function. See [.agents/ROUTING.md](ROUTING.md) for full patterns.
23
+ 7. **Strict Repository Pattern Policy (NO Raw `db('table')`)**:
24
+ - **NEVER** write raw Knex table queries (`db('table')`, `req.db('table')`) in application code.
25
+ - **ALWAYS** access database models through `db.getRepository('ModelName')` or `req.db.getRepository('ModelName')`.
26
+ - Raw Knex queries bypass schema validation, lifecycle hooks, soft-deletes, multi-tenant scopes, hidden fields, and query caching. See [.agents/ORM.md](ORM.md) for full patterns.
27
+ 8. **Services Layer Over Controllers (NO `controllers/`)**:
28
+ - **NEVER** create a `controllers/` directory or write class-based controller singletons.
29
+ - **ALWAYS** encapsulate multi-step business logic, domain mutations, and reusable operations inside `services/domain/action.js`.
30
+ - Invoke services via `ctx.service('domain.action', input)` or `req.service('domain.action', input)`. This ensures declarative validation (`schema`), automatic ACID transaction propagation (`transaction: true`), and role guards (`auth`). See [.agents/SERVICES.md](SERVICES.md).
31
+ 9. **Native Dual Auth Over External Packages (NO `jsonwebtoken` or `passport`)**:
32
+ - **NEVER** install third-party auth packages like `jsonwebtoken`, `bcryptjs`, `passport`, or `express-session`.
33
+ - **ALWAYS** use Webspresso's built-in zero-dependency Dual Authentication (`webspresso/core/auth` & `req.auth`). Use `req.auth.attempt()`, `req.auth.generateUserToken()`, and guards `middleware: ['auth']` (web session) or `middleware: ['jwt']` (stateless API Bearer). See [.agents/AUTH.md](AUTH.md).
34
+ 10. **Page Data Loaders Over Manual `res.render()`**:
35
+ - **NEVER** manually call `res.render('view.njk', { ... })` inside custom handlers for web pages.
36
+ - **ALWAYS** declare SSR pages inside `pages/` (e.g. `pages/notes/[id].njk`) and provide server-side data via a companion loader in `pages/notes/[id].js` exporting `async function load({ req, res, db, ctx })`.
37
+ 11. **Framework Exceptions Over Generic Errors**:
38
+ - **NEVER** return manual error payloads like `return res.status(404).json(...)` inside services or loaders, and never throw generic `throw new Error('Not found')` for client-facing issues (which masks as a 500 error in production).
39
+ - **ALWAYS** throw semantic framework exceptions: `NotFoundError` (404), `ValidationError` (422), `UnauthorizedError` (401), `ForbiddenError` (403), `BadRequestError` (400). Import them from `webspresso` or `webspresso/core/errors`. See [.agents/ERRORS.md](ERRORS.md).
40
+ 12. **Background Queue Engine Over External BullMQ or `setTimeout`**:
41
+ - **NEVER** install `bullmq` / `agenda` or use unmanaged `setTimeout()` / `setInterval()` for background or deferred tasks.
42
+ - **ALWAYS** place background job handlers in `jobs/` (e.g. `jobs/email/send.js`) and dispatch them reliably using `req.queue.dispatch('email.send', payload)` or `ctx.queue.dispatch()`. See [.agents/QUEUE.md](QUEUE.md).
43
+ 13. **Package Subpath Imports (NO manual `/index.js` suffix)**:
44
+ - **ALWAYS** use published `package.json` export paths: `require('webspresso/core/auth')`, `require('webspresso/core/orm')`, `require('webspresso/plugins/polar/src/webhooks')`.
45
+ - **NEVER** rely on deep relative paths from `node_modules/webspresso/...` or append `/index.js` unless documenting a pre-0.0.95 workaround.
46
+ - Smoke test: `tests/unit/package-exports.test.js`.
@@ -0,0 +1,141 @@
1
+ # Webspresso Email Plugin Guide (`plugins/email`)
2
+
3
+ The `emailPlugin` provides responsive email template compilation (using MJML), Nodemailer transport management, database delivery audit logs, auth email integration (password reset / email verification), and an interactive Admin Panel management UI.
4
+
5
+ ---
6
+
7
+ ## 1. Overview & Setup
8
+
9
+ ```js
10
+ import { createApp } from 'webspresso';
11
+ import { emailPlugin } from 'webspresso/plugins/email';
12
+
13
+ const email = emailPlugin({
14
+ db,
15
+ smtp: {
16
+ host: 'smtp.mailtrap.io',
17
+ port: 2525,
18
+ auth: { user: 'username', pass: 'password' },
19
+ },
20
+ defaults: {
21
+ from: 'Webspresso App <noreply@example.com>',
22
+ },
23
+ templatesDir: './emails',
24
+ logToDb: true,
25
+ includeAdminPage: true,
26
+ });
27
+
28
+ const { app } = createApp({
29
+ db,
30
+ plugins: [email],
31
+ });
32
+ ```
33
+
34
+ ---
35
+
36
+ ## 2. Configuration Options
37
+
38
+ | Option | Type | Default | Description |
39
+ |---|---|---|---|
40
+ | `db` | `Object` | `null` | Knex database instance (for logging emails to DB) |
41
+ | `transport` | `Object` | `null` | Pre-configured Nodemailer transport instance |
42
+ | `smtp` | `Object` | `null` | SMTP configuration object `{ host, port, auth }` |
43
+ | `defaults` | `Object` | `{}` | Default email headers (`from`, `replyTo`) |
44
+ | `templatesDir` | `string` | `null` | Path to directory containing `.mjml` templates |
45
+ | `templates` | `Object` | `{}` | In-memory map of MJML template strings |
46
+ | `logToDb` | `boolean` | `true` | Log sent emails to database |
47
+ | `tableName` | `string` | `'email_logs'` | Table name used for email logs |
48
+ | `includeAdminPage` | `boolean` | `true` | Mount Email management UI inside Admin Panel (`/_admin/email`) |
49
+ | `authEmails` | `Object` | `null` | Auth email bridge configuration |
50
+
51
+ ---
52
+
53
+ ## 3. Template Management & MJML Compilation
54
+
55
+ Place `.mjml` templates inside `templatesDir` (e.g. `emails/welcome.mjml`):
56
+
57
+ ```xml
58
+ <mjml>
59
+ <mj-body>
60
+ <mj-section>
61
+ <mj-column>
62
+ <mj-text font-size="20px">Welcome, {{ name }}!</mj-text>
63
+ <mj-button href="{{ activationUrl }}">Activate Account</mj-button>
64
+ </mj-column>
65
+ </mj-section>
66
+ </mj-body>
67
+ </mjml>
68
+ ```
69
+
70
+ Templates are dynamically compiled to responsive HTML with Nunjucks variable interpolation.
71
+
72
+ ---
73
+
74
+ ## 4. Plugin API (`emailPlugin().api`)
75
+
76
+ The plugin exports helper methods under `.api`:
77
+
78
+ ```js
79
+ const { api: emailApi } = email;
80
+
81
+ // 1. Send compiled MJML template
82
+ await emailApi.sendTemplate('welcome', {
83
+ to: 'user@example.com',
84
+ subject: 'Welcome to Webspresso!',
85
+ data: { name: 'Ahmet', activationUrl: 'https://example.com/activate' },
86
+ });
87
+
88
+ // 2. Direct raw email sending
89
+ await emailApi.send({
90
+ to: 'user@example.com',
91
+ subject: 'Hello',
92
+ html: '<p>Direct HTML</p>',
93
+ });
94
+
95
+ // 3. Render HTML preview without sending
96
+ const { html, subject } = await emailApi.preview('welcome', { name: 'Ahmet' });
97
+
98
+ // 4. Register in-memory MJML template string dynamically
99
+ emailApi.registerTemplate('notification', '<mjml><mj-body>...</mj-body></mjml>');
100
+
101
+ // 5. Connection test & log management
102
+ const isReady = await emailApi.verifyConnection();
103
+ const logs = await emailApi.queryLogs({ status: 'sent', limit: 20 });
104
+ await emailApi.purgeLogs({ olderThan: '30d' });
105
+ ```
106
+
107
+ ---
108
+
109
+ ## 5. Auth Email Bridge (`authEmails`)
110
+
111
+ Automatically wires password reset and email verification flows with `AuthManager`:
112
+
113
+ ```js
114
+ const email = emailPlugin({
115
+ db,
116
+ auth,
117
+ authEmails: {
118
+ enabled: true,
119
+ resetPassword: {
120
+ template: 'reset-password',
121
+ subject: 'Reset your password',
122
+ tokenExpiresIn: '1h',
123
+ },
124
+ emailVerification: {
125
+ template: 'verify-email',
126
+ subject: 'Verify your email address',
127
+ },
128
+ },
129
+ });
130
+ ```
131
+
132
+ ---
133
+
134
+ ## 6. Admin Panel UI & Database Logs
135
+
136
+ When `includeAdminPage: true` and `adminPanelPlugin` are present, an interactive Email Management module is mounted at `/_admin/email`:
137
+
138
+ - **Live Template Preview**: Select any MJML template, edit mock JSON data, and preview rendered responsive HTML.
139
+ - **Test Sender**: Send test emails directly from the Admin Panel to verify SMTP configuration.
140
+ - **Audit Logs Inspector**: Filter sent email logs by status (`sent`, `failed`), view error tracebacks, and purge historic logs.
141
+ - **Database Schema**: Log entries are stored in `email_logs` table (`id`, `to_email`, `subject`, `template_name`, `status`, `error`, `created_at`).
@@ -0,0 +1,201 @@
1
+ # Webspresso Framework Exceptions & Error Handling Guide
2
+
3
+ [← Back to AGENTS.md](.agents/AGENTS.md)
4
+
5
+ ---
6
+
7
+ ## ⚠️ STRICT RULE: Throw Semantic Errors (NO Generic `new Error()` or Manual `res.status`)
8
+
9
+ - **NEVER** return manual error payloads like `return res.status(404).json(...)` inside services or loaders.
10
+ - **NEVER** throw a generic `throw new Error('Not found')` for business or client-facing issues. In production, unmapped `Error` instances are automatically masked as `500 Internal Server Error` with stack traces hidden!
11
+ - **ALWAYS** throw semantic framework exceptions:
12
+ - `throw new NotFoundError('Item not found')` → returns HTTP 404
13
+ - `throw new ValidationError('Invalid data', { fields: { ... } })` → returns HTTP 422
14
+ - `throw new UnauthorizedError('Please log in')` → returns HTTP 401
15
+ - `throw new ForbiddenError('Access denied')` → returns HTTP 403
16
+ - `throw new BadRequestError('Invalid query')` → returns HTTP 400
17
+
18
+ ---
19
+
20
+ ## 1. Overview & Exception Hierarchy
21
+
22
+ Webspresso features a centralized, lightweight, zero-dependency exception hierarchy inspired by Django's failure domain separation. Thrown synchronous and asynchronous exceptions are automatically caught by the global error boundary, normalized, and formatted as standardized JSON responses for API routes or styled HTML pages for web clients.
23
+
24
+ ```text
25
+ WebspressoError (base class with cause, code, details)
26
+ ├── ConfigurationError (bootstrap & config errors)
27
+ ├── PluginError (plugin lifecycle & dependency errors)
28
+ ├── ValidationError (validation failures with fields, 422 default)
29
+ ├── SecurityError (suspicious requests, CSRF/cookie tampering, 400 default)
30
+ ├── RequestError
31
+ │ └── RequestAbortedError (client socket disconnection, 499 default)
32
+ ├── RouterError
33
+ │ ├── RouteNotFoundError (404 URL resolution error)
34
+ │ └── RouteGenerationError (named route generation error)
35
+ └── HttpError (base for HTTP-mapped exceptions)
36
+ ├── BadRequestError (400)
37
+ ├── UnauthorizedError (401)
38
+ ├── ForbiddenError (403)
39
+ ├── NotFoundError (404)
40
+ ├── MethodNotAllowedError (405)
41
+ ├── ConflictError (409)
42
+ ├── PayloadTooLargeError (413)
43
+ ├── UnsupportedMediaTypeError (415)
44
+ ├── UnprocessableEntityError (422)
45
+ └── TooManyRequestsError (429)
46
+ ```
47
+
48
+ ---
49
+
50
+ ## 2. Public Imports & Subpath
51
+
52
+ ```js
53
+ import {
54
+ WebspressoError,
55
+ HttpError,
56
+ BadRequestError,
57
+ UnauthorizedError,
58
+ ForbiddenError,
59
+ NotFoundError,
60
+ MethodNotAllowedError,
61
+ ConflictError,
62
+ PayloadTooLargeError,
63
+ UnsupportedMediaTypeError,
64
+ UnprocessableEntityError,
65
+ TooManyRequestsError,
66
+ ValidationError,
67
+ ConfigurationError,
68
+ PluginError,
69
+ SecurityError,
70
+ RequestAbortedError,
71
+ RouteNotFoundError,
72
+ RouteGenerationError,
73
+ } from 'webspresso';
74
+
75
+ // Or via subpath:
76
+ import { NotFoundError, ValidationError } from 'webspresso/errors';
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 3. Usage Examples
82
+
83
+ ### 3.1 HTTP Exceptions in API Handlers
84
+ ```js
85
+ // pages/api/users/[id].get.js
86
+ import { NotFoundError } from 'webspresso';
87
+
88
+ module.exports = async function handler(req, res) {
89
+ const user = await req.db.getRepository('User').findById(req.params.id);
90
+ if (!user) {
91
+ throw new NotFoundError('User not found', {
92
+ code: 'USER_NOT_FOUND',
93
+ });
94
+ }
95
+ res.json({ user });
96
+ };
97
+ ```
98
+
99
+ ### 3.2 Structured Validation Errors (422)
100
+ ```js
101
+ import { ValidationError } from 'webspresso';
102
+
103
+ throw new ValidationError('Validation failed', {
104
+ code: 'INVALID_INPUT',
105
+ fields: {
106
+ email: ['Invalid email address'],
107
+ age: ['Must be at least 18'],
108
+ },
109
+ });
110
+ ```
111
+
112
+ ### 3.3 Custom Error Classes
113
+ ```js
114
+ import { HttpError } from 'webspresso';
115
+
116
+ export class PaymentRequiredError extends HttpError {
117
+ constructor(message = 'Payment Required', options = {}) {
118
+ super(402, message, {
119
+ code: options.code || 'PAYMENT_REQUIRED',
120
+ expose: true,
121
+ ...options,
122
+ });
123
+ }
124
+ }
125
+ ```
126
+
127
+ ### 3.4 Custom Error Handler (`app.setErrorHandler`)
128
+ ```js
129
+ const { app } = createApp({ pagesDir: './pages' });
130
+
131
+ app.setErrorHandler(async (err, req, res, next) => {
132
+ if (err instanceof ValidationError) {
133
+ return res.status(422).json({
134
+ success: false,
135
+ errors: err.fields,
136
+ });
137
+ }
138
+ // Re-throwing or returning unhandled delegates to Webspresso default handler
139
+ });
140
+ ```
141
+
142
+ ---
143
+
144
+ ## 4. Response Contract & Production Masking
145
+
146
+ ### Default JSON Contract:
147
+ ```json
148
+ {
149
+ "status": 404,
150
+ "error": "Not Found",
151
+ "message": "User not found",
152
+ "code": "USER_NOT_FOUND"
153
+ }
154
+ ```
155
+
156
+ ### Development vs Production:
157
+ - **Production (`NODE_ENV === 'production'`)**: 500 errors are masked to `"Internal Server Error"`. `stack`, `cause`, and internal file paths are never exposed to clients.
158
+ - **Development**: Returns `stack`, `cause`, and a structured `trace` object identifying the exact route, module, source file, and execution phase.
159
+
160
+ ---
161
+
162
+ ## 5. Route Tracing & Request ID Observability
163
+
164
+ Webspresso automatically correlates every request and enriches thrown errors with route-level execution metadata:
165
+
166
+ ### 5.1 Request Correlation ID (`req.id` / `X-Request-Id`)
167
+ Every incoming request is assigned a unique UUID (`req.id`) available on `req.id` and returned in the `X-Request-Id` response header:
168
+ ```javascript
169
+ // Access in services, loaders, or logs:
170
+ const reqId = req.id; // e.g. "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
171
+ ```
172
+
173
+ ### 5.2 Context-Enriched Trace Object (Development Mode)
174
+ When an exception occurs during request execution in `definePage`, `defineApi`, or a feature module, the development JSON payload contains an explicit `trace` object:
175
+
176
+ ```json
177
+ {
178
+ "status": 500,
179
+ "error": "Internal Server Error",
180
+ "message": "Database query failed",
181
+ "trace": {
182
+ "route": "/api/auth/me",
183
+ "method": "GET",
184
+ "source": "src/modules/auth/api/me.get.js",
185
+ "module": "auth",
186
+ "phase": "handler",
187
+ "requestId": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
188
+ },
189
+ "stack": "Error: Database query failed\n at ...",
190
+ "cause": null
191
+ }
192
+ ```
193
+
194
+ ### 5.3 Visual Console Error Banners
195
+ In server logs, 500 errors are automatically highlighted with visual banners pinpointing the root cause:
196
+ ```txt
197
+ [webspresso] 🚨 Error at GET /api/auth/me [module: auth] (reqId: 9b1deb4d-3b7d-...)
198
+ Source: src/modules/auth/api/me.get.js
199
+ Phase: handler
200
+ ```
201
+
package/.agents/MCP.md ADDED
@@ -0,0 +1,103 @@
1
+ # Webspresso Model Context Protocol (MCP) Guide
2
+
3
+ The **Model Context Protocol (MCP)** plugin (`plugins/mcp`) connects Webspresso applications directly to AI agents, IDE assistants (Antigravity, Claude Desktop, Cursor, Zed, Windsurf), and autonomous coding tools.
4
+
5
+ ---
6
+
7
+ ## 1. Overview & Architecture
8
+
9
+ Webspresso's architecture seamlessly bridges to MCP:
10
+ - **Services (`services/`) → MCP Tools**: Every service defined in `services/` is automatically converted into an MCP tool with full JSON Schema input validation derived from its Zod `schema`.
11
+ - **ORM Models (`models/`) → MCP Tools & Resources**: Models defined with `defineModel` provide query tools (`orm_find`, `orm_findById`), mutation tools (`orm_create`, `orm_update`, `orm_delete`), metadata tools (`orm_describe_model`), and resources (`webspresso://models`, `webspresso://models/{model}`, `webspresso://schema`).
12
+ - **System Context → MCP Resources**: `webspresso://routes` (file routes, API routes, HTTP methods), `webspresso://services` (catalog of services), and `webspresso://health` (system metrics).
13
+ - **Dual Transport**:
14
+ - **Stdio (`webspresso mcp`)**: Standard input/output JSON-RPC 2.0 lines for local IDEs and CLI.
15
+ - **HTTP & SSE (`/_mcp/sse`, `/_mcp/messages`, `/_mcp`)**: Server-Sent Events stream and Streamable HTTP for remote or web-accessible agents.
16
+
17
+ ---
18
+
19
+ ## 2. Using the Plugin in Webspresso App
20
+
21
+ ```javascript
22
+ const { createApp, mcpPlugin } = require('webspresso');
23
+ const db = require('./models');
24
+
25
+ const { app } = createApp({
26
+ db,
27
+ plugins: [
28
+ mcpPlugin({
29
+ path: '/_mcp', // default: '/_mcp'
30
+ auth: {
31
+ token: process.env.MCP_SECRET_TOKEN, // Bearer token validation
32
+ localhostOnly: true, // Only allow connections from localhost
33
+ },
34
+ services: {
35
+ include: ['user.*', 'catalog.*'], // optional filter
36
+ readOnly: false, // set true to disable mutation services
37
+ },
38
+ orm: {
39
+ readOnly: false, // set true to allow only find/describe queries
40
+ },
41
+ discovery: true, // auto-discover custom mcp/ folder
42
+ }),
43
+ ],
44
+ });
45
+ ```
46
+
47
+ ---
48
+
49
+ ## 3. CLI Command: `webspresso mcp`
50
+
51
+ Start the MCP server over Stdio without starting the web server:
52
+
53
+ ```bash
54
+ # Start MCP server over stdio
55
+ npx webspresso mcp
56
+
57
+ # Restrict to read-only mode (prevent mutations)
58
+ npx webspresso mcp --read-only
59
+
60
+ # Print configuration snippet for Claude Desktop / Cursor
61
+ npx webspresso mcp --print-config
62
+ ```
63
+
64
+ ### IDE Configuration
65
+
66
+ Add to `claude_desktop_config.json` or `.cursor/mcp.json` or Antigravity IDE `mcp_config.json`:
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "my-webspresso-app": {
72
+ "command": "npx",
73
+ "args": ["webspresso", "mcp"]
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 4. Custom Tools, Resources, and Prompts (`mcp/` folder)
82
+
83
+ When `discovery: true` (default), Webspresso scans the project's `mcp/` directory:
84
+
85
+ ```
86
+ mcp/
87
+ ├── tools/
88
+ │ └── deploy_preview.js # exports { name, description, inputSchema, handler }
89
+ ├── resources/
90
+ │ └── docs.js # exports { uri, name, description, handler }
91
+ └── prompts/
92
+ └── review_code.js # exports { name, description, arguments, handler }
93
+ ```
94
+
95
+ ### Programmatic Registration via Runtime API
96
+ ```javascript
97
+ const mcp = app.usePlugin('mcp'); // or ctx.mcp inside plugins
98
+ mcp.registerTool({
99
+ name: 'custom_action',
100
+ description: 'Does something custom',
101
+ handler: async (args, ctx) => { ... },
102
+ });
103
+ ```