webspresso 0.0.97 → 0.0.99

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 (136) hide show
  1. package/.agents/ADMIN.md +163 -5
  2. package/.agents/AGENTS.md +1 -1
  3. package/.agents/ARCHITECTURE.md +10 -0
  4. package/.agents/AUTH.md +169 -0
  5. package/.agents/CLI.md +195 -1
  6. package/.agents/ORM.md +88 -4
  7. package/.agents/PLUGINS.md +221 -9
  8. package/.agents/QUEUE.md +127 -0
  9. package/.agents/ROUTING.md +127 -7
  10. package/AGENTS.md +1 -0
  11. package/bin/commands/add-esbuild.js +80 -0
  12. package/bin/commands/add-tailwind.js +76 -31
  13. package/bin/commands/analytics-prune.js +57 -0
  14. package/bin/commands/api.js +207 -15
  15. package/bin/commands/build.js +99 -0
  16. package/bin/commands/dev.js +64 -35
  17. package/bin/commands/doctor.js +26 -0
  18. package/bin/commands/email-prune.js +47 -0
  19. package/bin/commands/make-job.js +86 -0
  20. package/bin/commands/make-model.js +250 -0
  21. package/bin/commands/new.js +45 -28
  22. package/bin/commands/openapi.js +102 -0
  23. package/bin/commands/page.js +6 -3
  24. package/bin/commands/queue-prune.js +59 -0
  25. package/bin/commands/routes.js +52 -0
  26. package/bin/commands/service.js +6 -3
  27. package/bin/commands/typegen.js +106 -0
  28. package/bin/commands/upload-prune.js +79 -0
  29. package/bin/webspresso.js +22 -0
  30. package/core/applySchema.js +13 -6
  31. package/core/auth/hash.js +21 -12
  32. package/core/auth/index.js +60 -0
  33. package/core/auth/manager.js +134 -0
  34. package/core/auth/middleware.js +88 -0
  35. package/core/auth/oauth.js +517 -0
  36. package/core/auth/qrcode.js +499 -0
  37. package/core/auth/totp.js +504 -0
  38. package/core/compression/middleware.js +23 -17
  39. package/core/openapi/build-from-api-routes.js +160 -29
  40. package/core/orm/cache/fingerprint.js +15 -1
  41. package/core/orm/cache/memory-provider.js +27 -2
  42. package/core/orm/eager-loader.js +144 -10
  43. package/core/orm/index.js +20 -7
  44. package/core/orm/model.js +53 -7
  45. package/core/orm/query-builder.js +90 -4
  46. package/core/orm/repository.js +44 -3
  47. package/core/orm/search.js +213 -0
  48. package/core/queue/adapters/database.js +94 -0
  49. package/core/queue/adapters/memory.js +62 -0
  50. package/core/queue/adapters/redis.js +80 -0
  51. package/core/queue/cron.js +331 -0
  52. package/core/queue/index.js +8 -1
  53. package/core/queue/manager.js +205 -1
  54. package/core/queue/scheduler.js +359 -0
  55. package/core/sse/index.js +19 -0
  56. package/core/sse/stream.js +430 -0
  57. package/core/ssr/stream.js +11 -0
  58. package/index.d.ts +1188 -10
  59. package/index.js +88 -96
  60. package/package.json +1 -1
  61. package/plugins/admin-panel/api.js +279 -17
  62. package/plugins/admin-panel/app.js +114 -22
  63. package/plugins/admin-panel/client/parts/01-state-api-breadcrumb.js +314 -9
  64. package/plugins/admin-panel/client/parts/04-field-renderers.js +264 -67
  65. package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +333 -36
  66. package/plugins/admin-panel/client/parts/06-login-setup-forms.js +119 -28
  67. package/plugins/admin-panel/client/parts/07-model-list.js +49 -8
  68. package/plugins/admin-panel/client/parts/08-record-list.js +248 -23
  69. package/plugins/admin-panel/client/parts/09-record-form.js +59 -12
  70. package/plugins/admin-panel/client/parts/10-export-registry.js +23 -0
  71. package/plugins/admin-panel/client/verify-spa-parts.js +2 -0
  72. package/plugins/admin-panel/core/admin-module.js +13 -7
  73. package/plugins/admin-panel/core/api-extensions.js +30 -2
  74. package/plugins/admin-panel/core/registry.js +96 -16
  75. package/plugins/admin-panel/field-renderers/file-upload.js +187 -104
  76. package/plugins/admin-panel/field-renderers/index.js +2 -0
  77. package/plugins/admin-panel/index.js +96 -0
  78. package/plugins/admin-panel/lib/login-rate-limiter.js +119 -0
  79. package/plugins/admin-panel/modules/admin-users.js +16 -1
  80. package/plugins/admin-panel/modules/bulk-actions.js +13 -5
  81. package/plugins/admin-panel/modules/custom-pages.js +492 -3
  82. package/plugins/admin-panel/modules/dashboard.js +283 -0
  83. package/plugins/admin-panel/modules/menu.js +56 -18
  84. package/plugins/admin-panel/modules/user-management.js +24 -6
  85. package/plugins/api-key/crypto.js +101 -0
  86. package/plugins/api-key/index.js +288 -0
  87. package/plugins/api-key/middleware.js +461 -0
  88. package/plugins/api-key/migration-template.js +48 -0
  89. package/plugins/api-key/model.js +50 -0
  90. package/plugins/api-key/scopes.js +125 -0
  91. package/plugins/audit-log/admin-component.js +487 -59
  92. package/plugins/audit-log/api-handlers.js +148 -0
  93. package/plugins/audit-log/index.js +153 -3
  94. package/plugins/audit-log/purge.js +60 -10
  95. package/plugins/dashboard/app.js +152 -27
  96. package/plugins/dashboard/index.js +143 -39
  97. package/plugins/dashboard/styles.js +78 -0
  98. package/plugins/data-exchange/export-xlsx.js +9 -2
  99. package/plugins/data-exchange/import.js +9 -2
  100. package/plugins/data-exchange/index.js +6 -3
  101. package/plugins/email/index.js +132 -2
  102. package/plugins/email/purge.js +60 -10
  103. package/plugins/file-manager/admin-component.js +20 -4
  104. package/plugins/file-manager/index.js +21 -4
  105. package/plugins/index.js +10 -0
  106. package/plugins/oauth/index.js +338 -0
  107. package/plugins/orm-cache-admin/index.js +4 -2
  108. package/plugins/queue/index.js +15 -0
  109. package/plugins/schema-explorer/admin-component.js +714 -0
  110. package/plugins/schema-explorer/relations-graph.js +205 -0
  111. package/plugins/schema-explorer.js +248 -69
  112. package/plugins/site-analytics/admin-component.js +23 -0
  113. package/plugins/site-analytics/api-handlers.js +42 -0
  114. package/plugins/site-analytics/index.js +147 -2
  115. package/plugins/site-analytics/purge.js +168 -0
  116. package/plugins/swagger.js +261 -13
  117. package/plugins/upload/index.js +26 -0
  118. package/plugins/upload/orphan-pruner.js +292 -0
  119. package/src/api/api-loader.js +4 -1
  120. package/src/bundle/index.js +517 -0
  121. package/src/bundle/manifest.js +165 -0
  122. package/src/dev/banner.js +296 -0
  123. package/src/dev/colors.js +70 -0
  124. package/src/dev/index.js +62 -0
  125. package/src/dev/profiler.js +354 -0
  126. package/src/dev/typegen.js +497 -0
  127. package/src/dev-reload/index.js +611 -0
  128. package/src/discovery/file-route-parser.js +44 -1
  129. package/src/discovery/scan-routes.js +2 -2
  130. package/src/file-router.js +347 -162
  131. package/src/helpers.js +967 -98
  132. package/src/njk-frontmatter.js +12 -0
  133. package/src/pages/page-loader.js +61 -11
  134. package/src/routing/cascading.js +378 -0
  135. package/src/routing/middleware-resolver.js +92 -0
  136. package/src/server.js +635 -63
package/.agents/ADMIN.md CHANGED
@@ -133,7 +133,59 @@ registry.registerPageDir(path.join(__dirname, 'admin-pages'));
133
133
 
134
134
  ---
135
135
 
136
- ## 5. Custom Field Renderers (`registerFieldRenderer`)
136
+ ## 5. Page & Plugin Title Management
137
+
138
+ Admin Panel supports configurable base titles, title templates, and dynamic SPA page titles:
139
+
140
+ ### Plugin Configuration
141
+ ```js
142
+ adminPanelPlugin({
143
+ db,
144
+ title: 'Acme Admin', // Base title in <title> and header (default: 'Admin Panel')
145
+ titleTemplate: '%s | %b', // Format template: %s = page title, %b = base title (default: '%s | %b')
146
+ });
147
+ ```
148
+
149
+ ### Module & Page Title Configuration
150
+ When registering modules and custom pages, titles are automatically propagated to navigation and browser document title:
151
+ ```js
152
+ adminApi.registerModule({
153
+ id: 'reports',
154
+ title: 'Reports & Analytics', // Module fallback title
155
+ pages: [
156
+ {
157
+ id: 'monthly-report',
158
+ title: 'Monthly Report', // Document title -> "Monthly Report | Acme Admin"
159
+ path: '/reports/monthly',
160
+ component: ...,
161
+ },
162
+ {
163
+ id: 'live-dashboard',
164
+ documentTitle: 'Live Monitor (Fullscreen)', // Raw document.title override
165
+ path: '/reports/live',
166
+ layout: false,
167
+ component: ...,
168
+ }
169
+ ]
170
+ });
171
+ ```
172
+
173
+ ### Dynamic Client-Side Title Helper (`setAdminPageTitle`)
174
+ From any Mithril component or client script:
175
+ ```js
176
+ // Dynamic title formatted with template (e.g. "Sales Overview | Acme Admin")
177
+ window.setAdminPageTitle('Sales Overview');
178
+
179
+ // Custom template override
180
+ window.setAdminPageTitle('Sales Overview', { template: '%s — %b' });
181
+
182
+ // Raw document title without template
183
+ window.setAdminPageTitle('Sales Overview', { raw: true });
184
+ ```
185
+
186
+ ---
187
+
188
+ ## 6. Custom Field Renderers (`registerFieldRenderer`)
137
189
 
138
190
  Register custom display and edit renderers for specific column types:
139
191
 
@@ -152,7 +204,7 @@ registry.registerFieldRenderer('color-picker', {
152
204
 
153
205
  ---
154
206
 
155
- ## 6. Dashboard Widgets (`registerWidget`)
207
+ ## 7. Dashboard Widgets (`registerWidget`)
156
208
 
157
209
  Register dashboard widgets for the Admin overview screen:
158
210
 
@@ -164,9 +216,20 @@ registry.registerWidget('recent-orders', {
164
216
  });
165
217
  ```
166
218
 
219
+ ### Built-in Dashboard Widgets
220
+ - **`model-stats`**: Model overview cards displaying record counts, column counts, and last update timestamps.
221
+ - **`database-storage`**: Database Storage & Health monitor showing:
222
+ - Database engine (SQLite, PostgreSQL, MySQL) & active file sizes
223
+ - Total tables and total record counts across the schema
224
+ - Storage health status badge with high-volume alerts for unpruned logs
225
+ - Top tables by row count with visual progress percentage bars
226
+ - Direct navigation links to model CRUD screens
227
+ - **`quick-actions`**: Fast shortcuts to create new records across enabled models.
228
+ - **`recent-activity`**: Timeline of recent administrative mutations (creates, updates, deletes) powered by `audit_logs` / `admin_activity_logs` with direct links to record changes.
229
+
167
230
  ---
168
231
 
169
- ## 7. Single Record & Bulk Table Actions
232
+ ## 8. Single Record & Bulk Table Actions
170
233
 
171
234
  Add custom action buttons to model table views:
172
235
 
@@ -182,6 +245,16 @@ registry.registerAction('Product', {
182
245
  },
183
246
  });
184
247
 
248
+ // Record action with navigation redirect (e.g. audit-timeline)
249
+ registry.registerAction('Product', {
250
+ id: 'view-history',
251
+ label: 'History',
252
+ icon: 'clock',
253
+ handler: async (record, model) => {
254
+ return { redirect: `/_admin/audit-log?model=${model.name}&recordId=${record.id}` };
255
+ },
256
+ });
257
+
185
258
  // Bulk multi-select table action
186
259
  registry.registerBulkAction('Product', {
187
260
  id: 'bulk-archive',
@@ -196,7 +269,7 @@ registry.registerBulkAction('Product', {
196
269
 
197
270
  ---
198
271
 
199
- ## 8. Client Components & Custom Client Script Injections
272
+ ## 9. Client Components & Custom Client Script Injections
200
273
 
201
274
  Inject custom client-side JavaScript / Mithril components into the Admin SPA runtime:
202
275
 
@@ -213,7 +286,7 @@ registry.registerClientComponent('custom-banner', `
213
286
 
214
287
  ---
215
288
 
216
- ## 9. Offline & Zero-CDN Architecture
289
+ ## 10. Offline & Zero-CDN Architecture
217
290
 
218
291
  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
292
  - **Vendored Static Assets (`plugins/admin-panel/vendor/`)**:
@@ -223,3 +296,88 @@ The Admin Panel is 100% self-contained and operates completely offline without e
223
296
  - **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
297
  - **Hardened CSP**: Works in air-gapped / intranet environments with strict `'self'` Content Security Policy without leaking third-party network requests.
225
298
 
299
+ ---
300
+
301
+ ## 11. Toast Notifications, Modal Confirmations & Session Expiry UX
302
+
303
+ The Admin SPA includes a built-in reactive notification and modal system that replaces intrusive browser `alert()` and `confirm()` dialogs.
304
+
305
+ ### Toast Notifications (`window.notify`)
306
+ Display auto-dismissing, animated toasts in the top-right corner with pause-on-hover:
307
+
308
+ ```js
309
+ // Available on window.notify or as global helpers: notifySuccess, notifyError, notifyWarning, notifyInfo
310
+ notify.success('Record created successfully');
311
+ notify.error('Failed to save record: Validation error');
312
+ notify.warning('Please review unsaved changes');
313
+ notify.info('Export started in the background');
314
+
315
+ // Custom duration (TTL in milliseconds, default 4000ms):
316
+ notify.success('Saved!', 2000);
317
+ ```
318
+
319
+ ### Confirmation Modal (`window.confirmModal` / `confirmAction`)
320
+ Display an accessible, styled modal dialog returning a Promise resolving to `true` or `false`:
321
+
322
+ ```js
323
+ // Destructive confirmation (danger: true highlights the confirm button in red)
324
+ const confirmed = await confirmAction({
325
+ title: 'Delete User',
326
+ message: 'Are you sure you want to permanently delete this user?',
327
+ confirmText: 'Delete',
328
+ cancelText: 'Cancel',
329
+ danger: true,
330
+ });
331
+
332
+ if (confirmed) {
333
+ await api.delete(`/users/${id}`);
334
+ notify.success('User deleted');
335
+ }
336
+ ```
337
+
338
+ ### Session Expiration & Intended Return-To Redirection
339
+ When an authenticated staff user's session expires or an API call returns `401 Unauthorized`:
340
+ 1. `redirectToLogin('expired')` captures the active route and redirects to `/_admin/#!/login?returnTo=<route>&reason=expired`.
341
+ 2. The login screen displays an informational notice banner: *"Your session has expired. Please sign in again."*
342
+ 3. After authenticating successfully, the user is automatically redirected to their intended route.
343
+ 4. **Open-Redirect Hardening**: All return destinations are strictly validated by `isSafeIntendedRoute(path)` to disallow protocol-relative URLs (`//evil.com`), foreign protocols (`http:`, `javascript:`), or backslashes.
344
+
345
+ ---
346
+
347
+ ## 12. Admin Login Brute-Force Protection & Rate Limiting
348
+
349
+ The Admin Panel includes built-in in-memory rate limiting to defend staff login endpoints against credential stuffing and brute-force password guessing.
350
+
351
+ ### Configuration (`adminPanelPlugin`)
352
+
353
+ ```js
354
+ import { createApp, adminPanelPlugin } from 'webspresso';
355
+
356
+ const app = createApp({
357
+ plugins: [
358
+ adminPanelPlugin({
359
+ db,
360
+ // Configure or disable login rate limiting:
361
+ loginRateLimit: {
362
+ enabled: true, // Default: true
363
+ maxAttempts: 5, // Max failed attempts before blocking (default: 5)
364
+ windowMs: 15 * 60 * 1000, // Tracking window (default: 15 minutes)
365
+ blockDurationMs: 15 * 60 * 1000 // Temporary block duration (default: 15 minutes)
366
+ },
367
+ }),
368
+ ],
369
+ });
370
+ ```
371
+
372
+ To disable rate limiting (e.g. in test or internal automated environments):
373
+ ```js
374
+ adminPanelPlugin({ db, loginRateLimit: false });
375
+ ```
376
+
377
+ ### Protection Mechanics
378
+ - **IP Tracking**: Tracks failed attempts keyed by client IP (`req.ip` or upstream trusted proxy headers).
379
+ - **Automatic Block**: After `maxAttempts` failed logins within `windowMs`, returns HTTP 429 Too Many Requests with a `Retry-After: <seconds>` response header and JSON error payload.
380
+ - **Success Reset**: A single successful login clears the failed attempt counter for that IP.
381
+ - **Programmatic Reset**: Cleared automatically upon server restart or via `plugin.api.resetLoginAttempts(ip)`.
382
+
383
+
package/.agents/AGENTS.md CHANGED
@@ -44,7 +44,7 @@ This `.agents/` customization root is organized into modular topic guides:
44
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
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
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`).
47
+ 9. **[Plugin Ecosystem Guide](.agents/PLUGINS.md)** — Lifecycle hooks (`register`, `onRoutesReady`), CSP headers, and detailed reference for all 19 built-in official plugins (including `apiKeyPlugin`, `xlsxPlugin`, `csvPlugin`, `fileManagerPlugin`, `realtimePlugin`, and `basicAuthPlugin`).
48
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
49
  11. **[Email Plugin Guide](.agents/EMAIL.md)** — MJML template compilation, Nodemailer transport, DB delivery logs, Auth Email bridge, and Admin UI.
50
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).
@@ -116,4 +116,14 @@ Plugins expose `name`, `version`, `dependencies`, and lifecycle hooks (`register
116
116
  | `xlsxPlugin` | Excel / Export | Excel spreadsheet generation, parsing, ORM streaming export (`res.xlsx`), and services |
117
117
  | `csvPlugin` | CSV / Export | RFC 4180 CSV generator, parser, Excel UTF-8 BOM, streaming writer (`res.csv`), and services |
118
118
 
119
+ ---
120
+
121
+ ## 5. Developer Experience & Live/Hot Reload (`src/dev-reload/`)
122
+
123
+ - **Zero-Dependency SSE Reload Hub**: Enabled via `createApp({ devReload: true })` (or `devReload: { watchTemplates: true, watchCss: true, debounce: 50 }`).
124
+ - **Instant CSS Hot Swap**: Modifying `.css` files dynamically refreshes all `<link rel="stylesheet">` elements without page refresh or DOM state loss.
125
+ - **Template & Layout Live Reload**: Modifying Nunjucks templates (`.njk`, `.html`) in `views/` or pages in `pages/` automatically invalidates frontmatter/loader caches and broadcasts a debounced `'full'` reload event to active browser tabs.
126
+ - **Error Overlay Frame**: Client runtime script automatically renders terminal-free in-browser error frames for compile/build errors with line numbers and dismiss keyboard shortcuts (`Esc`).
127
+ - **DevTools Toolbar & Dashboard**: Embedded via `fsy.devToolbar()` and available at `/_webspresso` for interactive inspection of routes, models, services, cache, and profiler metrics.
128
+
119
129
 
package/.agents/AUTH.md CHANGED
@@ -101,3 +101,172 @@ module.exports = {
101
101
  },
102
102
  };
103
103
  ```
104
+
105
+ ---
106
+
107
+ ## 5. Zero-Dependency OAuth 2.0 & Social Login (`plugins/oauth`, `core/auth/oauth`)
108
+
109
+ Webspresso includes a built-in, zero-dependency OAuth 2.0 client and plugin with RFC 7636 PKCE, HMAC-SHA256 signed anti-CSRF state tokens, profile normalization, and seamless Dual Auth integration:
110
+
111
+ ### 5.1 Plugin Configuration
112
+ ```javascript
113
+ const { createApp, oauthPlugin } = require('webspresso');
114
+
115
+ const app = createApp({
116
+ plugins: [
117
+ oauthPlugin({
118
+ secret: process.env.SESSION_SECRET,
119
+ providers: {
120
+ google: {
121
+ clientId: process.env.GOOGLE_CLIENT_ID,
122
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET,
123
+ // redirectUri is optional; auto-inferred from request host if omitted
124
+ },
125
+ github: {
126
+ clientId: process.env.GITHUB_CLIENT_ID,
127
+ clientSecret: process.env.GITHUB_CLIENT_SECRET,
128
+ },
129
+ },
130
+ // Optional hook to find or create the user in your database
131
+ async findOrCreateUser({ provider, profile, tokens, db }) {
132
+ let user = await db.getRepository('User').findOne({ email: profile.email });
133
+ if (!user) {
134
+ user = await db.getRepository('User').create({
135
+ email: profile.email,
136
+ name: profile.name,
137
+ avatar_url: profile.avatarUrl,
138
+ });
139
+ }
140
+ return user;
141
+ },
142
+ defaultReturnTo: '/dashboard',
143
+ }),
144
+ ],
145
+ });
146
+ ```
147
+
148
+ ### 5.2 Automatic Routes
149
+ - `GET /auth/:provider` (e.g. `/auth/google?returnTo=/dashboard`): Generates PKCE code challenge and HMAC-signed state token with embedded return URL, redirecting the browser to the provider's authorization page.
150
+ - `GET /auth/:provider/callback`: Validates anti-CSRF state, exchanges authorization code for tokens, fetches normalized profile, calls `findOrCreateUser`, automatically establishes session via `req.auth.login(user)`, and redirects to `returnTo`. For API/mobile clients (`format=json` or `Accept: application/json`), returns `{ success: true, user, token }`.
151
+
152
+ ### 5.3 Template Helper (`fsy.oauthUrl`)
153
+ In Nunjucks templates:
154
+ ```html
155
+ <a href="{{ fsy.oauthUrl('google') }}" class="btn">Sign in with Google</a>
156
+ <a href="{{ fsy.oauthUrl('github', { returnTo: '/settings' }) }}" class="btn">Connect GitHub</a>
157
+ ```
158
+
159
+ ---
160
+
161
+ ## 6. Zero-Dependency Two-Factor Authentication (2FA / TOTP)
162
+
163
+ Webspresso provides an enterprise-grade, zero-dependency 2FA/TOTP suite adhering strictly to **RFC 6238 (TOTP)**, **RFC 4226 (HOTP)**, and **RFC 4648 (Base32)**. It requires NO external npm libraries and features offline SVG QR code generation to prevent leaking secret keys to third-party APIs.
164
+
165
+ ### 6.1 Key Capabilities
166
+ - **RFC 6238 Time-Based OTP**: Standard 30s step, 6/8 digits, HMAC-SHA1/SHA256/SHA512.
167
+ - **Time Drift Tolerance**: Built-in clock drift window (`window: 1` verifies $t \pm 30s$) and detailed delta analysis.
168
+ - **Offline QR Code Generator**: Generates crisp vector SVG strings and data URIs directly in pure Node.js.
169
+ - **Single-Use Recovery Codes**: Cryptographically secure alphanumeric codes, SHA-256 hashed storage, and timing-safe single-use burning.
170
+ - **Session & Guard Integration**: `req.auth.verify2Fa()`, `req.auth.is2FaVerified()`, and `middleware: ['2fa']`.
171
+
172
+ ### 6.2 Initializing 2FA Setup for a User
173
+ ```javascript
174
+ const { totp } = require('webspresso');
175
+
176
+ // In an API route: POST /api/user/2fa/setup
177
+ module.exports = {
178
+ middleware: ['auth'],
179
+ async handler(req, res) {
180
+ const setup = totp.createTotpSetup({
181
+ user: req.user,
182
+ issuer: 'My SaaS App',
183
+ recoveryCodeCount: 8,
184
+ });
185
+
186
+ // 1. Temporarily save secret and hashed recovery codes to DB for confirmation
187
+ await req.db.getRepository('User').update(req.user.id, {
188
+ temp_totp_secret: setup.secret,
189
+ temp_recovery_codes: JSON.stringify(setup.recoveryCodesHashed),
190
+ });
191
+
192
+ // 2. Return QR SVG and plaintext recovery codes for user to record
193
+ return res.json({
194
+ secret: setup.secret,
195
+ qrSvg: setup.qrSvg,
196
+ qrDataUri: setup.qrDataUri,
197
+ recoveryCodes: setup.recoveryCodes,
198
+ });
199
+ },
200
+ };
201
+ ```
202
+
203
+ ### 6.3 Confirming 2FA Activation
204
+ ```javascript
205
+ // In an API route: POST /api/user/2fa/confirm
206
+ module.exports = {
207
+ middleware: ['auth'],
208
+ schema: ({ z }) => ({
209
+ body: z.object({ token: z.string().min(6).max(8) }),
210
+ }),
211
+ async handler(req, res) {
212
+ const user = req.user;
213
+ const { token } = req.input.body;
214
+
215
+ const isValid = totp.verifyTotp(token, user.temp_totp_secret);
216
+ if (!isValid) {
217
+ return res.status(400).json({ error: 'Invalid verification code' });
218
+ }
219
+
220
+ // Activate 2FA on user record
221
+ await req.db.getRepository('User').update(user.id, {
222
+ two_factor_enabled: true,
223
+ totp_secret: user.temp_totp_secret,
224
+ recovery_codes: user.temp_recovery_codes,
225
+ temp_totp_secret: null,
226
+ temp_recovery_codes: null,
227
+ });
228
+
229
+ req.auth.set2FaVerified(true);
230
+ return res.json({ success: true, message: '2FA enabled successfully' });
231
+ },
232
+ };
233
+ ```
234
+
235
+ ### 6.4 2FA Login Flow & Guard Middleware
236
+ ```javascript
237
+ // Step 1: Initial password login
238
+ const user = await req.auth.findUserByCredentials(email, password);
239
+ if (user.two_factor_enabled) {
240
+ // Mark session as 2FA pending without full login
241
+ req.auth.set2FaPending(user);
242
+ return res.json({ require2Fa: true });
243
+ }
244
+ await req.auth.login(user);
245
+
246
+ // Step 2: 2FA Verification Endpoint (accepts 6-digit TOTP token OR recovery code)
247
+ const verification = req.auth.verify2Fa(tokenOrRecoveryCode, user);
248
+ if (verification.success) {
249
+ if (verification.type === 'recovery_code') {
250
+ // Burn used recovery code
251
+ await req.db.getRepository('User').update(user.id, {
252
+ recovery_codes: JSON.stringify(verification.remainingHashedCodes),
253
+ });
254
+ }
255
+ await req.auth.login(user);
256
+ req.auth.set2FaVerified(true);
257
+ return res.json({ success: true });
258
+ }
259
+ ```
260
+
261
+ ### 6.5 Route Protection with `middleware: ['2fa']`
262
+ ```javascript
263
+ // In pages/api/account/billing.get.js or pages/security/index.njk
264
+ module.exports = {
265
+ middleware: ['auth', '2fa'], // Rejects unverified users with 403 or redirects to /2fa
266
+ async handler(req, res) {
267
+ return res.json({ secureData: true });
268
+ },
269
+ };
270
+ ```
271
+
272
+
package/.agents/CLI.md CHANGED
@@ -13,8 +13,12 @@ npx webspresso dev
13
13
  # --port, -p <number> (default: 3001)
14
14
  # --host <string> (default: localhost)
15
15
 
16
- # Build production bundle / verify assets
16
+ # Build production bundle / compile assets (Tailwind + esbuild with manifest.json)
17
17
  npx webspresso build
18
+ # Flags:
19
+ # --no-css (skip CSS build)
20
+ # --no-js (skip JS bundle build)
21
+ # --no-hash (disable content hashing in filenames)
18
22
  ```
19
23
 
20
24
  ---
@@ -113,3 +117,193 @@ npx webspresso agents:init
113
117
  npx webspresso agents:init --force
114
118
  ```
115
119
 
120
+ ---
121
+
122
+ ## 8. Tailwind CSS Setup & Migration (`add:tailwind`)
123
+
124
+ Add or upgrade Tailwind CSS in a project:
125
+
126
+ ```bash
127
+ # Add Tailwind CSS v4 (Oxide engine, LightningCSS, zero-config)
128
+ npx webspresso add tailwind --v4
129
+
130
+ # Add Tailwind CSS v3 (legacy PostCSS engine)
131
+ npx webspresso add tailwind --v3
132
+
133
+ # Upgrade existing Tailwind v3 project to v4
134
+ npx webspresso add tailwind --upgrade
135
+ ```
136
+
137
+ ---
138
+
139
+ ## 9. Client Bundler Setup (`add:esbuild`)
140
+
141
+ Add client-side JS/TS bundling with esbuild incremental watching:
142
+
143
+ ```bash
144
+ # Scaffold esbuild client bundler setup
145
+ npx webspresso add esbuild
146
+ # or
147
+ npx webspresso add:esbuild
148
+ ```
149
+
150
+ ---
151
+
152
+ ## 10. Data Retention & Storage Cleanup Commands
153
+
154
+ Keep databases and storage clean by pruning historical records and unreferenced files:
155
+
156
+ ```bash
157
+ # Delete audit log entries older than N days (default: 90)
158
+ npx webspresso audit:prune --days 90
159
+
160
+ # Delete email delivery logs older than N days (default: 90)
161
+ npx webspresso email:prune --days 90
162
+
163
+ # Delete completed/failed background jobs older than N days (default: 30)
164
+ npx webspresso queue:prune --days 30
165
+
166
+ # Delete expired site analytics visits & JS errors older than N days
167
+ npx webspresso analytics:prune --days 90
168
+
169
+ # Scan and delete orphaned uploads not referenced by any database model
170
+ npx webspresso upload:prune
171
+
172
+ # Dry run mode (inspect orphaned files without deleting)
173
+ npx webspresso upload:prune --dry-run
174
+ ```
175
+
176
+ ---
177
+
178
+ ## 11. Routes Introspection (`routes`)
179
+
180
+ List and inspect all compiled file-based and API routes in terminal:
181
+
182
+ ```bash
183
+ # List all application routes in a colorized table
184
+ npx webspresso routes
185
+
186
+ # Filter to only API endpoints
187
+ npx webspresso routes --api
188
+
189
+ # Filter to only SSR page routes
190
+ npx webspresso routes --pages
191
+
192
+ # Output routes as machine-readable JSON
193
+ npx webspresso routes --json
194
+ ```
195
+
196
+ ---
197
+
198
+ ## 12. Dev Server DX & Query Profiler
199
+
200
+ When starting in development mode (`isDev: true` / `process.env.NODE_ENV !== 'production'`), Webspresso automatically:
201
+
202
+ - **Displays Startup Banner**: ASCII box with Node/Framework version, Local & Admin URLs, Database dialect, and counts of routes, models, services, and plugins.
203
+ - **Request Profiler & Logger**: Color-coded HTTP requests with status, response duration, and database metrics:
204
+ ```text
205
+ [GET 200] /api/notes — 14.2ms | 2 DB queries (2.8ms)
206
+ ```
207
+ - **N+1 Query Detection**: Flags queries executing repeatedly against the same table within a single request:
208
+ ```text
209
+ ⚠️ [webspresso:n+1] Potential N+1 query pattern detected on table 'users' (5 queries, 6.4ms). Consider eager loading with repo.query().include('users') or batching.
210
+ ```
211
+ - **Configuration (`dev` options in `createApp`)**:
212
+ ```javascript
213
+ createApp({
214
+ dev: {
215
+ banner: true, // show startup banner (default: true in dev)
216
+ routeTable: true, // show route table in banner (default: true)
217
+ profiler: true, // enable request & DB profiler (default: true)
218
+ nPlusOne: true, // detect N+1 queries (default: true)
219
+ nPlusOneThreshold: 4,// query count threshold for warning (default: 4)
220
+ slowQueryMs: 50, // flag queries slower than N ms (default: 50)
221
+ verbose: false, // log individual SQL statements
222
+ }
223
+ });
224
+ ```
225
+
226
+ ---
227
+
228
+ ## 13. Scaffolding & Code Generation (`make:*` Commands)
229
+
230
+ Quickly generate boilerplate adhering strictly to Webspresso contracts:
231
+
232
+ ```bash
233
+ # 1. Scaffold an ORM Model in models/<Name>.js with parsed zdb fields and Knex migration
234
+ npx webspresso make:model Post --fields title:string,content:text,published:boolean,views:integer --migration
235
+ # Flags:
236
+ # --fields <list> (title:string,content:text,views:integer,price:number,published:boolean,authorId:id)
237
+ # --table <name> (custom table name; defaults to pluralized lowercase)
238
+ # --migration, -m (auto-generate timestamped Knex migration in migrations/)
239
+ # --soft-deletes (include deletedAt column and default scope)
240
+
241
+ # 2. Scaffold API Endpoints in pages/api/
242
+ # Single endpoint with Zod validation and handler:
243
+ npx webspresso make:api notes/export --method POST --auth
244
+
245
+ # Full REST CRUD Suite (list, create, detail, update, delete) in pages/api/posts:
246
+ npx webspresso make:api posts --crud --model Post
247
+
248
+ # 3. Scaffold an SSR Page in pages/
249
+ npx webspresso make:page blog/[slug]
250
+ # Generates pages/blog/[slug].njk template + companion loader pages/blog/[slug].js
251
+
252
+ # 4. Scaffold a Service in services/<domain>/<action>.js
253
+ npx webspresso make:service order.process --transaction --auth admin
254
+
255
+ # 5. Scaffold a Background Queue Worker in jobs/<name>.js
256
+ npx webspresso make:job send-welcome-email --queue default --retries 3
257
+ ```
258
+
259
+ ---
260
+
261
+ ## 14. OpenAPI & API Documentation Export (`openapi` / `api:docs`)
262
+
263
+ Generate and export standard OpenAPI 3.0 JSON specification from file-based API routes and Zod schemas:
264
+
265
+ ```bash
266
+ # Print OpenAPI 3.0 specification to stdout
267
+ npx webspresso openapi
268
+
269
+ # Export directly to a JSON file
270
+ npx webspresso openapi --out openapi.json
271
+
272
+ # Export with custom title, version, and server URL
273
+ npx webspresso api:docs --out public/openapi.json --title "My API" --api-version "2.0.0" --server "https://api.myapp.com"
274
+
275
+ # Include ORM model schemas in components.schemas
276
+ npx webspresso openapi --out openapi.json --include-orm
277
+ ```
278
+
279
+ ---
280
+
281
+ ## 15. TypeScript Definitions & IDE Auto-Complete Generator (`typegen` / `types`)
282
+
283
+ Scan ORM models, business services, and routes to automatically generate typed TypeScript declaration files (`.webspresso/types.d.ts` and `webspresso-env.d.ts`) with module augmentation:
284
+
285
+ ```bash
286
+ # Generate TypeScript definitions
287
+ npx webspresso typegen
288
+
289
+ # Aliases
290
+ npx webspresso types
291
+ npx webspresso generate:types
292
+
293
+ # Watch mode (automatically regenerates on model/service/page edits)
294
+ npx webspresso typegen --watch
295
+
296
+ # Custom output destination
297
+ npx webspresso typegen --out types/webspresso.d.ts --no-env-file
298
+ ```
299
+
300
+ ### Generated Capabilities & IDE DX:
301
+ - **Typed Models (`db.getRepository('Model')`)**: Auto-completes model names and infers entity column types from Zod schemas.
302
+ - **Typed Services (`req.service('name', input)`)**: Auto-completes registered service names and enforces validated input payloads.
303
+ - **Typed Route Table (`AppRoute`, `AppRoutePath`)**: Union types for all discovered SSR and API routes.
304
+
305
+
306
+
307
+
308
+
309
+