webspresso 0.0.95 → 0.0.97

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 (94) 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/.agents/skills/webspresso-admin-and-plugins/SKILL.md +122 -0
  23. package/.agents/skills/webspresso-framework/SKILL.md +56 -0
  24. package/.agents/skills/webspresso-orm-and-database/SKILL.md +119 -0
  25. package/.agents/skills/webspresso-routing-and-api/SKILL.md +114 -0
  26. package/.agents/skills/webspresso-services/SKILL.md +101 -0
  27. package/.cursorrules +14 -0
  28. package/AGENTS.md +175 -0
  29. package/CLAUDE.md +54 -0
  30. package/README.md +18 -14
  31. package/bin/commands/admin-password.js +2 -1
  32. package/bin/commands/admin-setup.js +1 -1
  33. package/bin/commands/agents-init.js +209 -0
  34. package/bin/commands/api.js +114 -48
  35. package/bin/commands/audit-prune.js +1 -1
  36. package/bin/commands/doctor.js +35 -0
  37. package/bin/commands/favicon-generate.js +1 -1
  38. package/bin/commands/new.js +13 -2
  39. package/bin/commands/orm-map.js +5 -4
  40. package/bin/commands/page.js +75 -89
  41. package/bin/commands/polar-migrate.js +1 -1
  42. package/bin/commands/seed.js +1 -1
  43. package/bin/commands/service.js +77 -0
  44. package/bin/webspresso.js +2 -2
  45. package/core/auth/hash.js +10 -5
  46. package/core/compileSchema.js +1 -1
  47. package/core/errors/middleware.js +22 -2
  48. package/core/errors/normalize.js +29 -2
  49. package/{utils/schemaCache.js → core/schema-cache.js} +2 -3
  50. package/index.d.ts +306 -0
  51. package/index.js +35 -1
  52. package/package.json +36 -14
  53. package/plugins/admin-panel/app.js +10 -9
  54. package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +22 -1
  55. package/plugins/admin-panel/core/api-extensions.js +4 -0
  56. package/plugins/basic-auth/index.js +3 -1
  57. package/plugins/content/client/inline-edit.js +12 -12
  58. package/plugins/cors.js +6 -7
  59. package/plugins/csrf/index.js +3 -2
  60. package/plugins/csv/engine.js +447 -0
  61. package/plugins/csv/index.js +94 -0
  62. package/plugins/csv/middleware.js +80 -0
  63. package/plugins/csv/services.js +138 -0
  64. package/plugins/file-manager/admin-component.js +613 -0
  65. package/plugins/file-manager/api-handlers.js +261 -0
  66. package/plugins/file-manager/core.js +601 -0
  67. package/plugins/file-manager/index.js +139 -0
  68. package/plugins/file-manager/security.js +217 -0
  69. package/plugins/index.js +45 -79
  70. package/plugins/mcp/transports/sse.js +4 -2
  71. package/plugins/site-analytics/admin-component.js +7 -8
  72. package/plugins/xlsx/engine.js +397 -0
  73. package/plugins/xlsx/index.js +91 -0
  74. package/plugins/xlsx/middleware.js +87 -0
  75. package/plugins/xlsx/services.js +118 -0
  76. package/src/api/api-loader.js +233 -0
  77. package/src/api/define-api.js +35 -0
  78. package/src/discovery/file-route-parser.js +201 -0
  79. package/src/discovery/scan-routes.js +265 -0
  80. package/src/file-router.js +9 -1
  81. package/src/modules/define-module.js +99 -0
  82. package/src/modules/module-discovery.js +117 -0
  83. package/src/pages/define-page.js +36 -0
  84. package/src/pages/page-loader.js +214 -0
  85. package/src/routing/mount-discovered-routes.js +78 -0
  86. package/src/routing/route-table.js +191 -0
  87. package/src/server.js +112 -15
  88. package/src/services/builtins/file-manager.js +178 -0
  89. package/src/services/discovery.js +6 -1
  90. package/src/services/index.js +6 -11
  91. package/bin/commands/skill.js +0 -187
  92. package/templates/skills/webspresso-usage/REFERENCE-framework.md +0 -289
  93. package/templates/skills/webspresso-usage/REFERENCE-kernel.md +0 -93
  94. package/templates/skills/webspresso-usage/SKILL.md +0 -48
@@ -0,0 +1,225 @@
1
+ # Mithril.js Admin Panel Customization & Extension Guide
2
+
3
+ Webspresso includes a customizable, zero-build Mithril.js Single Page Application (SPA) Admin Panel (`plugins/admin-panel`). It provides automatic CRUD interfaces for ORM models and supports extension points for custom pages, widgets, custom field renderers, actions, and client-side component injections.
4
+
5
+ ---
6
+
7
+ ## 1. Architecture & Session Isolation
8
+
9
+ The Admin Panel operates as a self-contained Mithril.js SPA mounted at `/_admin`.
10
+
11
+ - **Isolated Auth Session**: Admin staff authentication uses a dedicated session key (`req.session.adminUser`), isolated from public site user sessions (`req.user`).
12
+ - **Auth Endpoints**:
13
+ - `POST /_admin/api/auth/setup` — Initial staff account setup (disabled once first admin exists).
14
+ - `POST /_admin/api/auth/login` — Staff authentication.
15
+ - `POST /_admin/api/auth/logout` — Terminate admin session.
16
+ - `GET /_admin/api/auth/me` — Current authenticated staff user details.
17
+ - `GET /_admin/api/auth/check` — Check if admin setup is required.
18
+
19
+ ---
20
+
21
+ ## 2. Model Admin Metadata (`defineModel`)
22
+
23
+ Configure model behavior in the Admin Panel using the `admin` property when calling `defineModel`:
24
+
25
+ ```js
26
+ import { defineModel, zdb } from 'webspresso';
27
+
28
+ export default defineModel({
29
+ name: 'Product',
30
+ table: 'products',
31
+ schema: zdb.schema({
32
+ id: zdb.id(),
33
+ name: zdb.string(),
34
+ slug: zdb.string(),
35
+ price: zdb.number(),
36
+ status: zdb.string({ default: 'active' }),
37
+ }),
38
+ admin: {
39
+ enabled: true, // Expose in Admin navigation
40
+ label: 'Products', // Display label
41
+ icon: '📦', // Navigation icon
42
+ displayField: 'name', // Field used for title/representation
43
+ searchFields: ['name', 'slug'], // Fields searched by table search input
44
+ filterable: ['status'], // Column filters enabled in table header
45
+ defaultSort: { column: 'id', order: 'desc' },
46
+ sortableColumns: ['name', 'price'], // Optional whitelist of sortable columns
47
+ sortable: true, // Set false to disable table sorting completely
48
+ readOnly: false, // Disable create/edit/delete actions
49
+ },
50
+ });
51
+ ```
52
+
53
+ ### 3-State Table Column Sorting
54
+ Table column headers support interactive 3-state cycling:
55
+ 1. `Unsorted` (default, `↕` icon)
56
+ 2. `ASC` (1st click, `▲` icon)
57
+ 3. `DESC` (2nd click, `▼` icon)
58
+ 4. `Unsorted` (3rd click, reverts to default primary key order)
59
+
60
+ Column sortability can be configured per-column:
61
+ - `zdb.string({ sortable: true })` / `zdb.text({ sortable: false })`
62
+ - `zdb.string().config({ sortable: false })`
63
+
64
+ ---
65
+
66
+ ## 3. Admin REST API (`/_admin/api/*`)
67
+
68
+ The Admin SPA communicates with the backend via JSON REST endpoints:
69
+
70
+ - `GET /_admin/api/models` — List all models exposed to admin with schemas & metadata.
71
+ - `GET /_admin/api/models/:modelName/records` — List paginated records (query params: `page`, `limit`, `search`, `filter`, `sort`).
72
+ - `GET /_admin/api/models/:modelName/records/:id` — Retrieve single record details.
73
+ - `POST /_admin/api/models/:modelName/records` — Create new record.
74
+ - `PUT /_admin/api/models/:modelName/records/:id` — Update existing record.
75
+ - `DELETE /_admin/api/models/:modelName/records/:id` — Delete record (soft or hard delete).
76
+ - `POST /_admin/api/models/:modelName/records/:id/restore` — Restore soft-deleted record.
77
+
78
+ ---
79
+
80
+ ## 4. Custom Pages (`registerModule` / `registerPageDir`)
81
+
82
+ Custom Admin pages support:
83
+ 1. **Clean Mithril.js Component Files** (`componentFile` / `component`)
84
+ 2. **Native Custom HTML Pages** (`html` / `htmlFile`) — e.g. EUIX Engine, Vue, Alpine apps rendered directly in the DOM with full Tailwind CSS styling.
85
+ 3. **External URL Pages** (`url` / `iframeUrl`) — Embedded in clean `<iframe>` containers.
86
+
87
+ ```js
88
+ const { registry } = adminPanelPlugin({ db });
89
+
90
+ // Example 1: Mithril Component
91
+ registry.registerModule('analytics-report', {
92
+ label: 'Analytics Report',
93
+ icon: '📊',
94
+ category: 'Reports',
95
+ componentFile: path.join(__dirname, 'admin-pages/analytics/component.js'),
96
+ layout: true, // Auto-wrap inside Admin layout header & sidebar (set false for full-screen view)
97
+ });
98
+
99
+ // Example 2: EUIX Engine / Native HTML Page
100
+ registry.registerModule('euix-module', {
101
+ id: 'euix-module',
102
+ scripts: ['https://unpkg.com/euixjs@latest/dist/EUIXEngine.umd.js'],
103
+ pages: [
104
+ {
105
+ id: 'euix-counter',
106
+ title: 'EUIX Counter',
107
+ path: '/euix-counter',
108
+ htmlFile: path.join(__dirname, 'admin-pages/euix-counter.html') // Rendered directly into DOM with Tailwind & scripts
109
+ }
110
+ ],
111
+ menu: [
112
+ { id: 'euix-counter', label: 'EUIX Counter', path: '/euix-counter', icon: 'zap' }
113
+ ]
114
+ });
115
+ ```
116
+
117
+ ### Automatic Directory Discovery (`registerPageDir`)
118
+ Directories containing page folders with `page.json` and `component.js` (or `index.html`) are auto-discovered:
119
+
120
+ ```text
121
+ admin-pages/
122
+ ├── analytics/
123
+ │ ├── page.json # { "id": "analytics", "label": "Analytics", "icon": "📊", "layout": true }
124
+ │ └── component.js # clean Mithril component (export default { view() { return m('div', 'Content'); } })
125
+ └── custom-tool/
126
+ ├── page.json # { "id": "custom-tool", "label": "Custom Tool", "icon": "⚡" }
127
+ └── index.html # Native HTML file
128
+ ```
129
+
130
+ ```js
131
+ registry.registerPageDir(path.join(__dirname, 'admin-pages'));
132
+ ```
133
+
134
+ ---
135
+
136
+ ## 5. Custom Field Renderers (`registerFieldRenderer`)
137
+
138
+ Register custom display and edit renderers for specific column types:
139
+
140
+ ```js
141
+ registry.registerFieldRenderer('color-picker', {
142
+ // 1. Table & detail view display
143
+ display: (value) => m('span.color-badge', { style: { backgroundColor: value } }, value),
144
+
145
+ // 2. Form input editor
146
+ edit: (value, onChange, fieldConfig) => m('input[type=color]', {
147
+ value: value || '#000000',
148
+ oninput: (e) => onChange(e.target.value),
149
+ }),
150
+ });
151
+ ```
152
+
153
+ ---
154
+
155
+ ## 6. Dashboard Widgets (`registerWidget`)
156
+
157
+ Register dashboard widgets for the Admin overview screen:
158
+
159
+ ```js
160
+ registry.registerWidget('recent-orders', {
161
+ title: 'Recent Orders',
162
+ width: 'half', // 'full' | 'half' | 'third'
163
+ componentFile: path.join(__dirname, 'widgets/recent-orders.js'),
164
+ });
165
+ ```
166
+
167
+ ---
168
+
169
+ ## 7. Single Record & Bulk Table Actions
170
+
171
+ Add custom action buttons to model table views:
172
+
173
+ ```js
174
+ // Single record button action
175
+ registry.registerAction('Product', {
176
+ id: 'duplicate',
177
+ label: 'Duplicate',
178
+ icon: '📋',
179
+ handler: async (record, { request, reload }) => {
180
+ await request.post(`/api/products/${record.id}/duplicate`);
181
+ reload();
182
+ },
183
+ });
184
+
185
+ // Bulk multi-select table action
186
+ registry.registerBulkAction('Product', {
187
+ id: 'bulk-archive',
188
+ label: 'Archive Selected',
189
+ icon: '📦',
190
+ handler: async (selectedIds, { request, reload }) => {
191
+ await request.post('/api/products/bulk-archive', { ids: selectedIds });
192
+ reload();
193
+ },
194
+ });
195
+ ```
196
+
197
+ ---
198
+
199
+ ## 8. Client Components & Custom Client Script Injections
200
+
201
+ Inject custom client-side JavaScript / Mithril components into the Admin SPA runtime:
202
+
203
+ ```js
204
+ registry.registerClientComponent('custom-banner', `
205
+ window.AdminRegistry.registerWidget('custom-banner', {
206
+ title: 'Custom Banner',
207
+ view: function() {
208
+ return m('.banner', 'Welcome to Custom Admin!');
209
+ }
210
+ });
211
+ `);
212
+ ```
213
+
214
+ ---
215
+
216
+ ## 9. Offline & Zero-CDN Architecture
217
+
218
+ The Admin Panel is 100% self-contained and operates completely offline without external CDN dependencies (`unpkg.com`, `cdn.jsdelivr.net`, or `cdn.tailwindcss.com`):
219
+ - **Vendored Static Assets (`plugins/admin-panel/vendor/`)**:
220
+ - `mithril.min.js` — Client SPA runtime (~28 KB).
221
+ - `tailwind.min.js` — Client utility compiler & style runtime (~412 KB).
222
+ - `quill.min.js` & `quill.snow.min.css` — Rich-text WYSIWYG editor.
223
+ - **Local Asset Serving**: Served locally at `GET /_admin/vendor/:file` with proper MIME types, path traversal protections, and `Cache-Control: public, max-age=86400` caching.
224
+ - **Hardened CSP**: Works in air-gapped / intranet environments with strict `'self'` Content Security Policy without leaking third-party network requests.
225
+
@@ -0,0 +1,72 @@
1
+ # Webspresso Project Guidelines & Rules
2
+
3
+ Webspresso is a modern, lightweight, zero-dependency-sprawl full-stack Node.js SSR & API framework featuring Next.js/Nuxt-style file-based routing, an intuitive Knex-based ORM, a modular plugin ecosystem, zero-dependency Dual Authentication (Session + JWT), Graceful Shutdown & Force Close, HTTP Response Compression, Centralized Framework Exceptions, and a customizable Mithril.js SPA Admin Panel.
4
+
5
+ ---
6
+
7
+ ## 🎯 Agent Intent Triage Matrix (Quick Guide Selection)
8
+
9
+ When tasked with a feature or bugfix, **immediately consult the corresponding topic guide** before writing code:
10
+
11
+ | Intent / Task | Canonical Topic Guide | Primary Directory / Ground Truth |
12
+ | :--- | :--- | :--- |
13
+ | **New API Route / Endpoint** | [`.agents/ROUTING.md`](ROUTING.md) | `pages/api/...` |
14
+ | **SSR HTML Page / Layout / Loader** | [`.agents/ROUTING.md`](ROUTING.md) & [`.agents/STREAMING.md`](STREAMING.md) | `pages/*.njk`, `pages/*.js`, `views/` |
15
+ | **Database Model / Migration / Query** | [`.agents/ORM.md`](ORM.md) | `models/*.js`, `core/orm`, `migrations/` |
16
+ | **Business Logic / Transaction / Service** | [`.agents/SERVICES.md`](SERVICES.md) | `services/` |
17
+ | **Admin Panel (Page, Widget, Action, UI)** | [`.agents/ADMIN.md`](ADMIN.md) | `plugins/admin-panel/` |
18
+ | **Authentication (Session / JWT / Guard)** | [`.agents/AUTH.md`](AUTH.md) | `core/auth/` |
19
+ | **Background Jobs & Worker Queue** | [`.agents/QUEUE.md`](QUEUE.md) | `jobs/` |
20
+ | **API Signatures & Types Verification** | [`index.d.ts`](../index.d.ts) | **Canonical Ground Truth** |
21
+
22
+ ---
23
+
24
+ ## 🛡️ 4-Step Mandatory Agent Workflow Protocol
25
+
26
+ Every coding agent working on Webspresso **MUST** follow this 4-step execution discipline:
27
+
28
+ 1. **Discover & Triage**: Identify the relevant topic guide above and inspect existing similar files in the workspace (e.g. `pages/api/`, `models/`, `services/`).
29
+ 2. **Contract Adherence**: Follow the exact module contract exported by the framework (`{ schema, middleware, handler }` for routes, `defineModel` for ORM, `defineService` for services).
30
+ 3. **Zero External Dependencies**: Use native Node.js standard modules (`crypto`, `path`, `fs`, `zlib`, `events`) and built-in framework utilities. NEVER install ad-hoc npm packages for auth, jwt, routing, or templating.
31
+ 4. **Targeted Verification**: Run the specific Vitest test file (`npx vitest run tests/...`) to confirm zero regressions before marking the task complete.
32
+
33
+ ---
34
+
35
+ ## Agent Guidebook Index
36
+
37
+ This `.agents/` customization root is organized into modular topic guides:
38
+
39
+ 1. **[File-Based Routing & API Routes](.agents/ROUTING.md)** — File-based page routes (`pages/`), method-suffixed API routes (`pages/api/*.get.js`, `*.post.js`), parameter brackets (`[id]`), Zod request schemas, and the strict prohibition of manual `routes/` directories or `app.get()`.
40
+ 2. **[Architecture & Structure](.agents/ARCHITECTURE.md)** — File-based routing (`pages/`), Asset Management & Versioning (`AssetManager`, `fsy.asset`), ORM models & `zdb` schemas (`core/orm`), Mithril.js Admin Panel SPA (`plugins/admin-panel`), and Plugin Ecosystem.
41
+ 3. **[Services Layer](.agents/SERVICES.md)** — File-based auto-discovery (`services/`), Zod validation, declarative `auth` & RBAC guards, automatic ACID transaction propagation (`transaction: true`), timeouts, and memoization.
42
+ 4. **[Exceptions & Error Handling](.agents/ERRORS.md)** — Django-inspired framework exception hierarchy (`WebspressoError`, `HttpError`, `ValidationError`, `SecurityError`, `RequestAbortedError`), central error boundary, `app.setErrorHandler()`, and production error masking.
43
+ 5. **[Graceful Shutdown & Lifecycle](.agents/SHUTDOWN.md)** — `ShutdownManager`, `NodeHttpAdapter`, connection draining, force close mode, and reverse-order plugin disposer cleanup.
44
+ 6. **[HTTP Response Compression](.agents/COMPRESSION.md)** — Native streaming zlib compression (`server.compression`), Brotli/Gzip/Deflate negotiation, threshold handling, and `res.compress(false)` route opt-out.
45
+ 7. **[Dual Authentication System](.agents/AUTH.md)** — Stateful Session + Cookie & Stateless HS256 JWT + Refresh Token Rotation, `req.auth` helpers, and `middleware: ['jwt']` guard integrations.
46
+ 8. **[ORM & Database Layer](.agents/ORM.md)** — Model definitions with `defineModel`, `zdb` schemas, Repositories API, Query Caching, Relations, Soft Delete, Scopes & Migrations.
47
+ 9. **[Plugin Ecosystem Guide](.agents/PLUGINS.md)** — Lifecycle hooks (`register`, `onRoutesReady`), CSP headers, and detailed reference for all 18 built-in official plugins (including `xlsxPlugin`, `csvPlugin`, `fileManagerPlugin`, `realtimePlugin`, and `basicAuthPlugin`).
48
+ 10. **[Realtime Layer & Adapters](.agents/REALTIME.md)** — Framework-agnostic realtime layer (`core/realtime`), generic WebSocket, SSE, and Socket.IO adapters, subscription identity, backoff reconnect, and auth lifecycle.
49
+ 11. **[Email Plugin Guide](.agents/EMAIL.md)** — MJML template compilation, Nodemailer transport, DB delivery logs, Auth Email bridge, and Admin UI.
50
+ 12. **[Admin Panel Customization](.agents/ADMIN.md)** — Extending Mithril.js Admin Panel SPA (Custom pages `component.js`, custom field renderers, widgets, single & bulk actions).
51
+ 13. **[CLI Tooling & Commands](.agents/CLI.md)** — `webspresso dev`, `build`, `doctor`, `db:migrate`, `db:seed`, `polar:migrate`, `favicon:generate`, `add:tailwind`, `agents:init`.
52
+ 14. **[Development & Testing Workflow](.agents/WORKFLOW.md)** — CLI commands, Vitest unit/integration testing, Playwright E2E tests, TypeScript type checking, and zero regression policy.
53
+ 15. **[Coding Standards & Conventions](.agents/CONVENTIONS.md)** — Zero external dependencies policy, strict function signature & schema verification, clean component separation, and log traceback inspection rules.
54
+ 16. **[Background Job Queue](.agents/QUEUE.md)** — In-Memory, Knex Database, and Distributed Redis queue adapters, job auto-discovery (`jobs/`), retries, and graceful draining.
55
+ 17. **[SSR Streaming & Chunked Transfer](.agents/STREAMING.md)** — Native HTTP chunked streaming (`res.renderStream()`), deferred data loader slots (`defer: { key: promise }`), and TTFB optimization.
56
+ 18. **Polar billing production checklist** — [docs/guides/polar-production-checklist.md](../docs/guides/polar-production-checklist.md) (env, webhooks, rate limits, external tier hooks, CSP). Plugin reference: [PLUGINS.md § polarPlugin](PLUGINS.md).
57
+
58
+ ---
59
+
60
+ ## Essential Rules Summary
61
+
62
+ - **Strict File-Based Routing (NO `routes/` or `app.get()`)**: NEVER create a `routes/` or `src/routes/` directory. NEVER call `app.get()`, `app.post()`, or `express.Router()`. ALL application endpoints MUST be defined inside `pages/` and `pages/api/...` with HTTP method extensions (`.get.js`, `.post.js`, `.delete.js`, `.patch.js`, `.put.js`).
63
+ - **Strict Repository Pattern (NO Raw `db('table')`)**: NEVER write raw Knex table queries in application code. ALWAYS access database models through repositories via `db.getRepository('ModelName')` or `req.db.getRepository('ModelName')` to ensure schema validation, hooks, scopes, and query caching are applied.
64
+ - **Strict Services Layer (NO `controllers/`)**: NEVER create a `controllers/` folder or controller classes. Encapsulate multi-step business logic in `services/domain/action.js` and invoke via `ctx.service('domain.action', input)` to leverage automatic transaction propagation (`transaction: true`) and Zod validation.
65
+ - **Native Dual Auth (NO External JWT/Passport Packages)**: NEVER install `jsonwebtoken`, `passport`, or `express-session`. ALWAYS use native `req.auth` and `webspresso/core/auth` (`middleware: ['jwt']` / `middleware: ['auth']`).
66
+ - **Page Data Loaders (NO Manual `res.render()`)**: NEVER call `res.render()` inside custom handlers for SSR pages. Use companion loaders `pages/*.js` exporting `async function load({ req, res, db, ctx })`.
67
+ - **Semantic Exceptions (NO Generic Errors / Manual Responses)**: NEVER return manual `res.status(404).json(...)` inside services or loaders. ALWAYS throw semantic errors: `NotFoundError`, `ValidationError`, `UnauthorizedError`, `ForbiddenError`.
68
+ - **Background Jobs (NO BullMQ / `setTimeout`)**: NEVER use unmanaged `setTimeout` or external queues. Place job workers in `jobs/` and dispatch via `req.queue.dispatch()`.
69
+ - **Zero External Dependencies**: Core framework functions (Auth, JWT, CORS, Routing, Errors, Compression, Shutdown, Nunjucks helpers) must rely strictly on native Node.js modules (`crypto`, `path`, `fs`, `events`, `zlib`, `async_hooks`).
70
+ - **Zero Regression Policy**: Always run Vitest tests (`npm test` or `npx vitest run`) before declaring any task complete.
71
+ - **Strict Verification**: Inspect authoritative source files for function signatures and schema definitions before writing consuming code.
72
+ - **Log & Traceback Inspection**: Base all bug fixes strictly on exact runtime error log tracebacks and empirical test output.
@@ -0,0 +1,119 @@
1
+ # Webspresso Codebase Architecture & Structure
2
+
3
+ [← Back to AGENTS.md](.agents/AGENTS.md)
4
+
5
+ ---
6
+
7
+ ## 1. Core SSR & Routing (`pages/`, `views/`, `src/`)
8
+
9
+ - **File-based Routing**: Pages located in `pages/` automatically map to URL routes:
10
+ - `pages/index.njk` → `/`
11
+ - `pages/products/[id].njk` → `/products/:id`
12
+ - `pages/api/posts.post.js` → `POST /api/posts`
13
+ - ⚠️ **CRITICAL ANTI-PATTERN: NEVER create a `routes/` or `src/routes/` directory. NEVER use `express.Router()`, `app.get()`, or `app.post()`. All application endpoints must live under `pages/` and `pages/api/`. See [ROUTING.md](ROUTING.md).**
14
+ - **Data Loaders (`load()`)**: Page routes export `async function load({ req, res, db, ctx })` to fetch data server-side before Nunjucks template rendering.
15
+ - **API Endpoints**: Defined as file route modules in `pages/api/...` exporting `{ schema, middleware, handler }`. Input validation via Zod (`schema: ({ z }) => ({ body, query, params })`) assigns validated data to `req.input.body` / `req.input.query`.
16
+ - **Modern Page & API Helpers (`definePage`, `defineApi`)**:
17
+ - `definePage({ middleware, load, head, render, layout })`: Pure JS or Nunjucks-backed page routes with unified execution context (`ctx: { req, res, params, query, service, db, redirect, error, fsy, locale, t }`).
18
+ - `defineApi({ description, tags, schema, middleware, handler })`: REST API endpoints with declarative Zod validation (`req.input`), middleware chain, and automatic JSON response formatting.
19
+ - **Feature Modules (`src/modules/` or `modules/`, `defineModule`)**:
20
+ - Domain-oriented modular architecture bundling pages, APIs, services, and scoped middlewares into standalone units (`defineModule({ name, pages, api, services, middlewares, onInit, onDestroy })`).
21
+ - Automatic service prefixing (`'auth.login'`) and scoped route prefixing (`/auth/*`, `/api/auth/*`).
22
+ - **Deterministic Route Compilation & Introspection (`RouteTable`, `app.routes.list()`)**:
23
+ - Discovered routes are compiled and sorted in deterministic priority order: Static $\to$ Deep Dynamic $\to$ Shallow Dynamic $\to$ Catch-All (`*`).
24
+ - Detects `METHOD + PATH` collision conflicts at startup and throws descriptive errors.
25
+ - Inspectable via `app.routes.list()` for CLI tools, documentation generation, and debugging.
26
+ - **Templating & Helpers**: Nunjucks (`.njk`) templates rendered with layouts (e.g. `views/layout.njk`). Access helpers via `fsy` object in templates.
27
+ - **Asset Management & Cache-Busting (`assets`, `AssetManager`, `fsy`)**:
28
+ - Configured via `createApp({ assets: { version, manifestPath, prefix, publicDir } })` or `configureAssets()`.
29
+ - **Static Query Versioning (`version`)**: Appends `?v=<version>` (or `&v=...`) to asset URLs (e.g. `version: '1.2.3'` or `process.env.APP_VERSION`) for cache busting.
30
+ - **Build Manifests (`manifestPath`)**: Automatically resolves hashed output files from Vite (`.vite/manifest.json` mapping `{ "css/style.css": { "file": "assets/style-abc.css" } }`) or Webpack manifests (`{ "/css/style.css": "/dist/style.abc.css" }`). Falls back gracefully if an asset is not in the manifest.
31
+ - **URL Prefix (`prefix`)**: Prepends CDN or mount path prefixes (e.g. `prefix: 'https://cdn.example.com'` or `'/static'`).
32
+ - **Template Helpers**:
33
+ - `{{ fsy.asset('/css/style.css') }}` → Resolves URL with manifest/versioning.
34
+ - `{{ fsy.css('/css/style.css', { media: 'print' }) | safe }}` → Generates `<link rel="stylesheet" ...>`.
35
+ - `{{ fsy.js('/js/app.js', { defer: true, type: 'module' }) | safe }}` → Generates `<script ...></script>`.
36
+ - `{{ fsy.img('/images/logo.png', 'Logo', { loading: 'lazy' }) | safe }}` → Generates `<img ...>` with escaped attributes.
37
+ - **Page-Level Assets (`pageAssets`)**: Route `load()` can return `stylesheets` and `scripts` arrays, which are promoted to `pageHead` for per-route asset loading.
38
+ - **Framework Exceptions & Central Error Boundary (`core/errors`, `app.setErrorHandler`)**:
39
+ - Django-inspired hierarchy: `WebspressoError`, `HttpError` (400-429), `ValidationError` (422), `SecurityError`, `RequestAbortedError`, `ConfigurationError`, `PluginError`, `RouteNotFoundError`.
40
+ - Automatic async error wrapping: all rejected promises in route handlers pass automatically to `next(err)`.
41
+ - Safe production masking: 500 errors stripped of stack traces and internal messages; exposed strictly in development.
42
+ - **Graceful Shutdown & Lifecycle Management (`core/shutdown`, `ShutdownManager`)**:
43
+ - Configured via `createApp({ server: { shutdown: { enabled: true, mode: 'graceful'|'force', timeout: 10000 } } })`.
44
+ - `NodeHttpAdapter` single-listener socket tracking, request draining with `Connection: close`, and reverse-order plugin disposer cleanup.
45
+ - **HTTP Response Compression (`core/compression`)**:
46
+ - Zero-dependency streaming zlib compression configured via `createApp({ server: { compression: true } })`.
47
+ - Content negotiation for Brotli (`br`), Gzip (`gzip`), Deflate (`deflate`), threshold filtering (1 KB), and `res.compress(false)` route opt-out.
48
+ - **Custom Error & 404 Pages (`errorPages`)**:
49
+ - Configured via `createApp({ errorPages: { notFound, serverError, timeout } })`.
50
+ - **404 Not Found (`notFound`)**: Template path (`'404.njk'`) or handler `(req, res, ctx) => ...`.
51
+ - **500 Server Error (`serverError`)**: Template path (`'errors/500.njk'`) or handler `(err, req, res, ctx) => ...`. Context: `{ fsy, locale, isDev, url, method, error, status }`.
52
+ - **503 Timeout (`timeout`)**: Template path (`'errors/503.njk'`) or handler `(req, res, ctx) => ...` when `timeout` (e.g. `'30s'`) is configured.
53
+ - **i18n & Localization Engine (`loadI18n`, `createTranslator`, `detectLocale`)**:
54
+ - Zero-dependency internationalization built on native Node.js `Intl` APIs (`Intl.PluralRules`, `Intl.NumberFormat`, `Intl.DateTimeFormat`, `Intl.RelativeTimeFormat`).
55
+ - **Hierarchical Dictionaries**: Global `pages/locales/[locale].json` merged with route-specific overrides `pages/[route]/locales/[locale].json` using `mtime` cache.
56
+ - **Pluralization**: Automatic CLDR plural selection (`zero`, `one`, `two`, `few`, `many`, `other`) or exact number mapping (`0`, `1`) via `t('key', { count })` or `t.plural(count, forms)`.
57
+ - **Fallback Chain**: Missing keys fall back to `DEFAULT_LOCALE` (`en`) automatically. Key check via `t.has('key')` or `t.exists('key')`.
58
+ - **Native Formatters on `t`**: `t.number(1234.5)`, `t.currency(199.99, 'TRY')`, `t.date(d)`, `t.relativeTime(-2, 'day')`.
59
+ - **Template Helpers & Filter**: `{{ fsy.localeUrl('tr') }}`, `{{ 'nav.home' | t }}`, `{{ t('key') }}`.
60
+ - **Services Layer (`services/`, `ServiceRegistry`, `defineService`, `ctx.service`, `req.service`)**:
61
+ - Auto-discovered file-based service modules: `services/user/get.js` → `user.get`, `services/post/publish.js` → `post.publish`.
62
+ - **Shared Request Context**: Service-to-service composition (`ctx.service()`) shares the exact same `ctx` (`ctx.db`, `ctx.auth`, `ctx.session`, `ctx.user`).
63
+ - **Circular Call Protection**: Automatically tracks service call stack per request and aborts cycles (`user.get -> profile.get -> user.get`).
64
+ - **Optional Schema Validation**: Validates inputs with simple descriptors (`'number'`, `'string'`, `'email'`, etc.), custom functions, or Zod schemas before handler execution.
65
+ - **HTTP Independence**: Services can be executed without starting an HTTP server via `createServiceRegistry().call(name, input, mockCtx)`.
66
+ - **Middleware & Hooks**: Route lifecycle hooks can be declared per-page (`module.exports = { middleware: ['auth', 'jwt'] }`) or globally via `pages/_hooks.js`.
67
+
68
+ ---
69
+
70
+ ## 2. ORM & Database Layer (`core/orm`, `models/`)
71
+
72
+ - **Model Definition**: Defined in `models/*.js` using `defineModel({ name, table, schema, relations, scopes, hidden, admin, cache })`.
73
+ - **Schema & Types**: Built with `zdb` schema builder:
74
+ - `zdb.id()`, `zdb.uuid()`, `zdb.nanoid()`
75
+ - `zdb.string()`, `zdb.integer()`, `zdb.boolean()`
76
+ - `zdb.file({ maxLength, nullable })` (URL/path string for uploaded assets)
77
+ - `zdb.json()`, `zdb.timestamp()`
78
+ - **Repositories**: Accessed via `db.getRepository(modelName)` or `ctx.db`. Supports `find`, `findById`, `findOne`, `create`, `update`, `delete`, and `query()`.
79
+ - **Query Caching**: Memory/provider query cache configured per database instance or model (`cache: 'auto'|'smart'|true`).
80
+ - **Migrations**: Database schema changes managed via Knex migrations in `migrations/` (`webspresso db:migrate`).
81
+
82
+ ---
83
+
84
+ ## 3. Admin Panel Architecture (`plugins/admin-panel`)
85
+
86
+ - **SPA CRUD Engine**: Modular Mithril.js SPA mounted at `/_admin` powered by `adminPanelPlugin({ db })`.
87
+ - **Admin Module Registration**: Registered using `adminApi.registerModule` or `adminApi.registerPageDir`.
88
+ - **Custom Admin Pages**:
89
+ - Supports clean Mithril component `.js` files (`componentFile`), external URLs (`url` / `iframeUrl`), or custom HTML documents (`html` / `htmlFile`).
90
+ - **Auto Layout Wrapping**: Custom page views are automatically wrapped inside Admin `Layout` and `Breadcrumb` containers unless `layout: false` is configured for full-screen pages.
91
+ - **SSR Scripts & Styles**: `registerScript(url)` and `registerStyle(url)` inject external CSS/JS dependencies into Admin SPA markup.
92
+ - **Session Isolation**: Admin staff authentication uses a separate session (`req.session.adminUser` / `/_admin/api/auth/*`), isolated from public site auth (`req.user`).
93
+
94
+ ---
95
+
96
+ ## 4. Plugin Ecosystem (`plugins/`)
97
+
98
+ Plugins expose `name`, `version`, `dependencies`, and lifecycle hooks (`register(ctx)`, `onRoutesReady(ctx)`):
99
+
100
+ | Plugin | Feature set | One-line purpose |
101
+ |--------|-------------|------------------|
102
+ | `basicAuthPlugin` | Security / HTTP | Zero-dependency RFC 7617 HTTP Basic Authentication with timing-safe comparison |
103
+ | `realtimePlugin` | Realtime | Framework-agnostic realtime layer (`core/realtime`) with WebSocket, SSE, Socket.IO, and Redis adapters |
104
+ | `adminPanelPlugin` | Admin / SPA | ORM-backed CRUD SPA with user management & custom pages |
105
+ | `fileManagerPlugin` | Admin / Media | Media library, directory tree, file preview, and drag-and-drop uploads |
106
+ | `contentPlugin` | Admin / CMS | Schema-driven CMS with inline editing and public REST API |
107
+ | `uploadPlugin` | HTTP / Admin | Multipart upload endpoint + pluggable storage providers |
108
+ | `redirectPlugin` | HTTP | Configurable HTTP 301–308 redirects before file routes |
109
+ | `rateLimitPlugin` | HTTP | Named rate-limit middleware + optional global limiter |
110
+ | `siteAnalyticsPlugin` | Analytics | Self-hosted page view analytics and client JS error tracking |
111
+ | `emailPlugin` | Email | MJML template compilation & Nodemailer integration |
112
+ | `csrfPlugin` | Security | Session & cookie double-submit CSRF protection + helpers |
113
+ | `auditLogPlugin` | Security | Admin mutation audit logging |
114
+ | `ormCacheAdminPlugin` | Admin / ORM | Cache inspection and invalidation dashboard |
115
+ | `restResourcePlugin` | ORM / REST API | Auto RESTful CRUD endpoints from models with eager loading & sanitization |
116
+ | `xlsxPlugin` | Excel / Export | Excel spreadsheet generation, parsing, ORM streaming export (`res.xlsx`), and services |
117
+ | `csvPlugin` | CSV / Export | RFC 4180 CSV generator, parser, Excel UTF-8 BOM, streaming writer (`res.csv`), and services |
118
+
119
+
@@ -0,0 +1,103 @@
1
+ # Webspresso Dual Authentication Architecture
2
+
3
+ [← Back to AGENTS.md](.agents/AGENTS.md)
4
+
5
+ ---
6
+
7
+ ## 1. Overview
8
+
9
+ Webspresso provides a zero-dependency **Dual Authentication** system (`webspresso/core/auth` — resolved via package exports, no `/index.js` suffix required):
10
+ - **Stateful Session + Cookie**: Used for browser SSR routes and Admin Panel (`/`, `/_admin`).
11
+ - **Stateless HS256 JWT + Refresh Tokens**: Used for REST APIs, Mobile Apps, and SPAs (`/api/*`).
12
+
13
+ ---
14
+
15
+ ## ⚠️ STRICT RULE: Use Native Dual Auth (NO External JWT/Passport Packages)
16
+
17
+ - **NEVER** install `jsonwebtoken`, `bcryptjs`, `passport`, or `express-session`.
18
+ - **ALWAYS** use Webspresso's native `req.auth` and `webspresso/core/auth`:
19
+ - Session login: `await req.auth.attempt(email, password, { remember: true })`
20
+ - Stateless JWT generation: `req.auth.generateUserToken(user)`
21
+ - Refresh Tokens: `req.auth.generateRefreshToken(user)` / `req.auth.refreshAccessToken(token)`
22
+ - Route Guards: `middleware: ['auth']` (web session) or `middleware: ['jwt']` (API Bearer token)
23
+
24
+ ---
25
+
26
+ ## 2. Token Generation & Rotation
27
+
28
+ - **Access Token**: Short-lived HS256 JWT token generated via `req.auth.generateUserToken(user, options)`. Default duration is `15m` (configurable).
29
+ - **Refresh Token**: Long-lived HS256 JWT token generated via `req.auth.generateRefreshToken(user, options)` with cryptographically unique `jti` nonces. Default duration is `30d` (configurable).
30
+ - **Token Rotation**: `req.auth.refreshAccessToken(refreshToken, { rotate: true })` verifies the refresh token and generates a new Access Token alongside a rotated Refresh Token pair.
31
+
32
+ ---
33
+
34
+ ## 3. Request-Bound Auth Helper (`req.auth`)
35
+
36
+ Attached to Express request during auth middleware execution:
37
+
38
+ | Method | Description |
39
+ |--------|-------------|
40
+ | `req.auth.attempt(identifier, password, { remember })` | Authenticate user with credentials and login |
41
+ | `req.auth.login(user, { remember })` | Log in user and establish session |
42
+ | `req.auth.logout({ everywhere })` | Log out user and destroy session |
43
+ | `req.auth.generateUserToken(user, options)` | Generate short-lived JWT Access Token |
44
+ | `req.auth.generateRefreshToken(user, options)` | Generate long-lived JWT Refresh Token |
45
+ | `req.auth.refreshAccessToken(refreshToken, options)` | Refresh access token & rotate refresh token |
46
+ | `req.auth.check()` / `req.auth.guest()` | Check if user is authenticated or guest |
47
+ | `req.auth.user()` / `req.auth.id()` | Get current user object or ID |
48
+
49
+ ---
50
+
51
+ ## 4. File-Based API Route Examples (`pages/api/*`)
52
+
53
+ ### 4.1 Login & Token Generation (`pages/api/auth/login.post.js`)
54
+ ```javascript
55
+ module.exports = {
56
+ schema: ({ z }) => ({
57
+ body: z.object({
58
+ email: z.string().email(),
59
+ password: z.string().min(1),
60
+ }),
61
+ }),
62
+ async handler(req, res) {
63
+ const { email, password } = req.input.body;
64
+ const user = await req.auth.attempt(email, password);
65
+ if (!user) return res.status(401).json({ error: 'Invalid credentials' });
66
+
67
+ const accessToken = req.auth.generateUserToken(user);
68
+ const refreshToken = req.auth.generateRefreshToken(user);
69
+
70
+ return res.json({ accessToken, refreshToken, user: { id: user.id, email: user.email } });
71
+ },
72
+ };
73
+ ```
74
+
75
+ ### 4.2 Token Refresh Endpoint (`pages/api/auth/refresh.post.js`)
76
+ ```javascript
77
+ module.exports = {
78
+ schema: ({ z }) => ({
79
+ body: z.object({
80
+ refreshToken: z.string(),
81
+ }),
82
+ }),
83
+ async handler(req, res) {
84
+ const { refreshToken } = req.input.body;
85
+ try {
86
+ const result = await req.auth.refreshAccessToken(refreshToken, { rotate: true });
87
+ return res.json({ accessToken: result.accessToken, refreshToken: result.refreshToken });
88
+ } catch (err) {
89
+ return res.status(401).json({ error: err.message });
90
+ }
91
+ },
92
+ };
93
+ ```
94
+
95
+ ### 4.3 Protected API Route (`pages/api/profile.get.js`)
96
+ ```javascript
97
+ module.exports = {
98
+ middleware: ['jwt'],
99
+ async handler(req, res) {
100
+ return res.json({ user: req.user, token: req.token });
101
+ },
102
+ };
103
+ ```
package/.agents/CLI.md ADDED
@@ -0,0 +1,115 @@
1
+ # Webspresso CLI Tooling & Commands Reference
2
+
3
+ The `webspresso` CLI utility (`bin/cli.js`) provides development commands for running local SSR servers, executing database migrations, analyzing project health, and generating favicons.
4
+
5
+ ---
6
+
7
+ ## 1. Development & Build Commands
8
+
9
+ ```bash
10
+ # Start local development server with hot reloading
11
+ npx webspresso dev
12
+ # Flags:
13
+ # --port, -p <number> (default: 3001)
14
+ # --host <string> (default: localhost)
15
+
16
+ # Build production bundle / verify assets
17
+ npx webspresso build
18
+ ```
19
+
20
+ ---
21
+
22
+ ## 2. Project Health & Sanity Check (`doctor`)
23
+
24
+ Scans routing structure, model definitions, and dynamic sibling collisions:
25
+
26
+ ```bash
27
+ npx webspresso doctor
28
+
29
+ # Strict mode: exits with status 1 if warnings are found
30
+ npx webspresso doctor --strict
31
+ ```
32
+
33
+ Checks performed:
34
+ - Dynamic sibling collisions (e.g. `pages/[id].njk` vs `pages/[slug].njk`).
35
+ - Naming conventions and lowercase file-based route paths.
36
+ - Missing model schemas or invalid `zdb` column definitions.
37
+
38
+ ---
39
+
40
+ ## 3. Database Commands (`db:migrate`, `db:seed`)
41
+
42
+ ```bash
43
+ # Run pending Knex database migrations
44
+ npx webspresso db:migrate
45
+
46
+ # Seed database records
47
+ npx webspresso db:seed
48
+
49
+ # Scaffold initial seed files
50
+ npx webspresso db:seed --setup
51
+ ```
52
+
53
+ ---
54
+
55
+ ## 4. Favicon Generator (`favicon:generate`)
56
+
57
+ Generates multi-resolution browser favicons, `manifest.json`, and `favicons.njk` partial from a PNG logo:
58
+
59
+ ```bash
60
+ npx webspresso favicon:generate logo.png
61
+
62
+ # Flags:
63
+ # --theme-color <hex> (default: #ffffff)
64
+ # --output-dir <path> (default: public)
65
+ # --no-layout (do not modify layout.njk auto-include)
66
+ ```
67
+
68
+ ---
69
+
70
+ ## 5. Polar Billing Migration (`polar:migrate`)
71
+
72
+ Generates an idempotent Knex migration adding Polar billing columns to the user table:
73
+
74
+ ```bash
75
+ npx webspresso polar:migrate
76
+
77
+ # Flags:
78
+ # -c, --config <path> Database config file
79
+ # -t, --table <name> User table (default: users)
80
+ # -o, --output <path> Custom migration output path
81
+ ```
82
+
83
+ Then run `npx webspresso db:migrate`. See [docs/guides/polar-production-checklist.md](../docs/guides/polar-production-checklist.md).
84
+
85
+ ---
86
+
87
+ ## 6. Interactive Service Runner (`service`, `service:run`)
88
+
89
+ Test and run services interactively without starting the HTTP server:
90
+
91
+ ```bash
92
+ # Interactive selection & prompt mode
93
+ npx webspresso service
94
+
95
+ # Run specific service with JSON payload
96
+ npx webspresso service user.get -i '{"id": 42}'
97
+
98
+ # Run with key=value query string
99
+ npx webspresso service payment.refund -i 'transactionId=tx_123 amount=49.99'
100
+ ```
101
+
102
+ ---
103
+
104
+ ## 7. AI Agent Guidelines Scaffolder (`agents:init`)
105
+
106
+ Scaffolds and synchronizes AI coding agent rules into any Webspresso project:
107
+
108
+ ```bash
109
+ # Scaffold full guidelines bundle (.agents/, AGENTS.md, CLAUDE.md, .cursorrules)
110
+ npx webspresso agents:init
111
+
112
+ # Overwrite existing guidelines
113
+ npx webspresso agents:init --force
114
+ ```
115
+