webspresso 0.0.74 → 0.0.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -3
- package/bin/commands/orm-map.js +139 -0
- package/bin/commands/skill.js +22 -8
- package/bin/utils/orm-map-html.js +689 -0
- package/bin/utils/orm-map-load.js +85 -0
- package/bin/utils/orm-map-snapshot.js +179 -0
- package/bin/utils/resolve-webspresso-orm.js +23 -0
- package/bin/webspresso.js +2 -0
- package/core/auth/manager.js +14 -1
- package/core/kernel/app.js +96 -0
- package/core/kernel/base-repository.js +143 -0
- package/core/kernel/events.js +101 -0
- package/core/kernel/flow.js +22 -0
- package/core/kernel/index.js +17 -0
- package/core/kernel/plugin.js +23 -0
- package/core/kernel/plugins/sample-seo.js +26 -0
- package/core/kernel/run-demo.js +58 -0
- package/core/kernel/view.js +167 -0
- package/core/openapi/build-from-api-routes.js +8 -2
- package/core/orm/model.js +3 -1
- package/core/url-path-normalize.js +30 -0
- package/index.d.ts +168 -1
- package/index.js +20 -2
- package/package.json +11 -1
- package/plugins/admin-panel/api.js +43 -15
- package/plugins/admin-panel/client/README.md +39 -0
- package/plugins/admin-panel/client/load-parts.js +74 -0
- package/plugins/admin-panel/client/manifest.parts.json +12 -0
- package/plugins/admin-panel/client/parts/01-state-api-breadcrumb.js +150 -0
- package/plugins/admin-panel/client/parts/02-filter-components.js +554 -0
- package/plugins/admin-panel/client/parts/03-pagination-intro.js +70 -0
- package/plugins/admin-panel/client/parts/04-field-renderers.js +287 -0
- package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +335 -0
- package/plugins/admin-panel/client/parts/06-login-setup-forms.js +125 -0
- package/plugins/admin-panel/client/parts/07-model-list.js +596 -0
- package/plugins/admin-panel/client/parts/08-record-list.js +536 -0
- package/plugins/admin-panel/client/parts/09-record-form.js +170 -0
- package/plugins/admin-panel/client/parts/10-export-registry.js +11 -0
- package/plugins/admin-panel/client/verify-spa-parts.js +32 -0
- package/plugins/admin-panel/client/vite.config.example.mjs +22 -0
- package/plugins/admin-panel/components.js +4 -2640
- package/plugins/admin-panel/core/api-extensions.js +100 -10
- package/plugins/admin-panel/index.js +3 -0
- package/plugins/admin-panel/lib/is-rich-text-empty.js +23 -0
- package/plugins/admin-panel/lib/sanitize-rich-html.js +106 -0
- package/plugins/admin-panel/modules/dashboard.js +3 -2
- package/plugins/admin-panel/modules/user-management.js +90 -20
- package/plugins/index.js +4 -0
- package/plugins/rate-limit/index.js +178 -0
- package/plugins/redirect/index.js +204 -0
- package/plugins/rest-resources/index.js +2 -1
- package/plugins/swagger.js +2 -1
- package/plugins/upload/local-file-provider.js +6 -2
- package/src/file-router.js +191 -50
- package/src/njk-frontmatter.js +156 -0
- package/src/plugin-manager.js +4 -2
- package/src/server.js +26 -9
- package/templates/skills/webspresso-usage/REFERENCE-framework.md +276 -0
- package/templates/skills/webspresso-usage/REFERENCE-kernel.md +93 -0
- package/templates/skills/webspresso-usage/SKILL.md +29 -278
|
@@ -1,291 +1,42 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: webspresso-usage
|
|
3
3
|
description: >-
|
|
4
|
-
|
|
5
|
-
createApp
|
|
6
|
-
|
|
7
|
-
i18n, lifecycle hooks, Zod API validation, ORM (zdb, defineModel, repository,
|
|
8
|
-
query builder, migrations), plugins (admin, analytics, sitemap, SEO, audit,
|
|
9
|
-
recaptcha, rest resources, file upload), CLI (`new .`, `--yes`, `-i`), env vars, and testing. Use when working in this
|
|
10
|
-
repo or any Webspresso app—adding routes, APIs, models, plugins, auth, client-side
|
|
11
|
-
sprinkles or page transitions, or debugging routing, ctx.db, session, or templates.
|
|
4
|
+
Webspresso: SSR file routes (createApp), ORM, auth, plugins, CLI; plus optional in-process
|
|
5
|
+
kernel (kernel.createApp: event bus, flows, view resolver). Read REFERENCE-framework.md for
|
|
6
|
+
pages/api/ORM/auth; REFERENCE-kernel.md for kernel. Use in this repo or Webspresso apps.
|
|
12
7
|
---
|
|
13
8
|
|
|
14
|
-
# Webspresso — agent
|
|
9
|
+
# Webspresso — agent skill (overview)
|
|
15
10
|
|
|
16
|
-
|
|
11
|
+
There are **three** files in this folder; open the reference that matches the task.
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
- **API**: File-based handlers under `pages/api/` with method suffixes and optional **Zod** validation (`req.input`).
|
|
20
|
-
- **i18n**: JSON locales; `t('key')` in templates.
|
|
21
|
-
- **ORM**: Knex-backed layer in `core/orm` — `defineModel`, `zdb` schema helpers, repositories, query builder, migrations.
|
|
22
|
-
- **Plugins**: Register in `createApp({ plugins })`; optional `db` passed as `ctx.db`.
|
|
13
|
+
## Which file should I read?
|
|
23
14
|
|
|
24
|
-
|
|
15
|
+
| Need | File | Notes |
|
|
16
|
+
|------|------|-------|
|
|
17
|
+
| `pages/`, `views/`, API, `load()`, hooks, i18n, `fsy` | [`REFERENCE-framework.md`](./REFERENCE-framework.md) | SSR **`createApp`** — the main framework. |
|
|
18
|
+
| `createApp({ db })`, `ctx.db`, `defineModel`, migrations | [`REFERENCE-framework.md`](./REFERENCE-framework.md) § ORM | Knex ORM in `core/orm`. |
|
|
19
|
+
| `createAuth`, `quickAuth`, `middleware: ['auth']` | [`REFERENCE-framework.md`](./REFERENCE-framework.md) § Auth | **`webspresso/core/auth`**. |
|
|
20
|
+
| Admin, sitemap, upload, redirect, data-exchange plugins | [`REFERENCE-framework.md`](./REFERENCE-framework.md) § Plugins | Framework plugin API (`createApp({ plugins })`). |
|
|
21
|
+
| Greenfield project, `webspresso new`, `dev`, `db:migrate`, `doctor` | [`REFERENCE-framework.md`](./REFERENCE-framework.md) § CLI | Full command table there. |
|
|
22
|
+
| Event bus, `kernel.createApp`, `defineFlow`, `definePlugin`, simulated `BaseRepository`, namespaced minimal views | [`REFERENCE-kernel.md`](./REFERENCE-kernel.md) | **Not** SSR **`createApp`** — [`doc/index.html#application-kernel`](../../../doc/index.html#application-kernel). |
|
|
23
|
+
| Long-form narrative, all options | [`README.md`](../../../README.md), [`doc/index.html`](../../../doc/index.html) | Single-page HTML docs. |
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
## 2. Typical project layout
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
project/
|
|
32
|
-
├── server.js # listen(); uses createApp()
|
|
33
|
-
├── pages/
|
|
34
|
-
│ ├── _hooks.js # global lifecycle hooks (optional)
|
|
35
|
-
│ ├── locales/en.json # global i18n
|
|
36
|
-
│ ├── index.njk # GET /
|
|
37
|
-
│ ├── about/index.njk # GET /about
|
|
38
|
-
│ ├── blog/[slug].njk # dynamic
|
|
39
|
-
│ └── api/
|
|
40
|
-
│ ├── health.get.js
|
|
41
|
-
│ └── posts.post.js
|
|
42
|
-
├── views/ # layouts (e.g. layout.njk)
|
|
43
|
-
├── public/ # static assets
|
|
44
|
-
├── models/ # ORM models (defineModel)
|
|
45
|
-
├── migrations/
|
|
46
|
-
├── webspresso.db.js # or knexfile.js
|
|
47
|
-
└── plugins/ # app-specific plugins (optional)
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## 3. `createApp(options)` — essentials
|
|
53
|
-
|
|
54
|
-
**Required**
|
|
55
|
-
|
|
56
|
-
- `pagesDir` — path to `pages/`.
|
|
57
|
-
|
|
58
|
-
**Common optional**
|
|
59
|
-
|
|
60
|
-
| Option | Role |
|
|
61
|
-
|--------|------|
|
|
62
|
-
| `viewsDir` | Layouts / partials (Nunjucks paths) |
|
|
63
|
-
| `publicDir` | Static files (`express.static`) |
|
|
64
|
-
| `db` | DB instance from `createDatabase()` → **`ctx.db`** in `load`, `meta`, plugins |
|
|
65
|
-
| `middlewares` | Named map; reference by string in route/API config (`middleware: ['auth']`) |
|
|
66
|
-
| `plugins` | Array of plugin factories/objects |
|
|
67
|
-
| `errorPages` | `{ notFound, serverError, timeout }` — function or template path |
|
|
68
|
-
| `timeout` | e.g. `'30s'` or `false` |
|
|
69
|
-
| `helmet` | `true` / `false` / object |
|
|
70
|
-
| `assets` | `{ version, manifestPath, prefix }` for `fsy.asset` / `fsy.css` / `fsy.js` |
|
|
71
|
-
| `pageAssets` | Opt-in **`true`** or **`{ enabled?, stylesheets?, scripts? }`**. When on, route **`load()`** may return reserved keys **`stylesheets`** (string or list; also `{ href, media? }` objects) and **`scripts`** (string, `{ src, defer?, async?, type? }`, or list). They are removed from the root Nunjucks context and passed as **`pageHead`** with **`pageAssets: true`**. The app layout must print them (see [`views/layout.njk`](../../../views/layout.njk) in the package). Default **off** — `stylesheets` / `scripts` in **`load()`** behave as normal data keys. |
|
|
72
|
-
| `clientRuntime` | Opt-in **`{ alpine?: boolean \| object, swup?: boolean \| object }`**. Serves **`/__webspresso/client-runtime/*`** (Alpine 3, swup 4 + Head + Scripts plugins + bootstrap). Template context **`clientRuntime`**; include [`views/partials/webspresso-client-runtime.njk`](../../../views/partials/webspresso-client-runtime.njk) and set **`<main id="swup">`** when swup is on. Env overrides: **`WEBSPRESSO_ALPINE`**, **`WEBSPRESSO_SWUP`** (`1` or `true`). Admin / dev dashboard HTML is unchanged (Mithril). Use **`data-no-swup`** on links for full page loads. HTMX is not used. |
|
|
73
|
-
| `auth` | `AuthManager` from **`createAuth()`** / **`quickAuth()`** (`webspresso/core/auth`). Mounts cookie parser + **`express-session`** + per-request **`authenticate`**; sets **`req.user`**, **`req.auth`**. Injects named route middleware **`auth`** and **`guest`** (overwrites same keys in `middlewares` if you passed both — avoid reusing those names for custom handlers). |
|
|
74
|
-
| `setupRoutes(app, ctx)` | **Register custom Express routes here** — runs **after** file routes and plugins’ `onRoutesReady`, **before** 404. **`ctx.clientRuntime`** is the resolved flags. **`ctx.authMiddleware`** is set when `auth` was passed (guards: `requireAuth`, `requireGuest`, `requireCan`, `requireVerified`, …). Do not rely on `app.get` *after* `createApp` returns unless routes are appended before the 404 middleware (see [`src/server.js`](../../../src/server.js)). |
|
|
75
|
-
|
|
76
|
-
**Returns:** `{ app, nunjucksEnv, pluginManager, authMiddleware }` — `authMiddleware` is **`null`** when `auth` was not configured.
|
|
77
|
-
|
|
78
|
-
### Client runtime — implementation notes
|
|
79
|
-
|
|
80
|
-
- **Package helpers:** `resolveClientRuntime(options)` merges **`createApp({ clientRuntime })`** with env; **`CLIENT_RUNTIME_BASE`** is **`/__webspresso/client-runtime`** (script URLs under that path).
|
|
81
|
-
- **After swup navigation:** bootstrap runs **`Alpine.initTree`** on **`#swup`** on swup’s **`content:replace`** so new SSR markup gets Alpine bindings.
|
|
82
|
-
- **Default `ignoreVisit` (bootstrap):** links under **`/_admin`**, **`/_webspresso`**, elements with **`data-no-swup`**, plus swup’s usual rules (e.g. `target="_blank"`, other origin).
|
|
83
|
-
- **CSP / Helmet:** production **`script-src 'self'`** works for **`/__webspresso/client-runtime/`**; some Alpine builds may need **`unsafe-eval`** — validate for your version or use a stricter build.
|
|
84
|
-
- **Demo:** repo **[`examples/alpine-swup-demo/`](../../../examples/alpine-swup-demo/)**. Longer doc: **[`doc/index.html#client-runtime`](../../../doc/index.html#client-runtime)** · README **Client runtime**.
|
|
85
|
-
|
|
86
|
-
### Session authentication — essentials
|
|
87
|
-
|
|
88
|
-
- **Import:** `const { createAuth, quickAuth, hash, verify, createRememberTokensTable } = require('webspresso/core/auth')` (published `core/` tree on npm; **not** re-exported from package root).
|
|
89
|
-
- **`createAuth({ findUserById, findUserByCredentials, session: { secret }, rememberTokens?, ... })`** — adapter pattern; optional **remember-me** via `rememberTokens: { create, find, delete, deleteAllForUser }` + **`createRememberTokensTable(knex)`** for the default table shape.
|
|
90
|
-
- **`quickAuth({ db, userModel, identifierField, passwordField, rememberMe })`** — wires **`getRepository`** + bcrypt **`verify`**; optional Knex **`remember_tokens`** when `rememberMe: true`.
|
|
91
|
-
- **Request API** (after global authenticate): **`req.auth.attempt(id, password, { remember })`**, **`login`**, **`logout`**, **`check`**, **`guest`**, **`user`**, **`id`**, **`can` / `cannot` / `authorize`** (policies: **`auth.definePolicy`**, **`defineGate`**, **`beforePolicy`**).
|
|
92
|
-
- **Route config:** `middleware: ['auth']` (must be logged in) or `['guest']` (logged-out only). For JSON APIs mounted in **`setupRoutes`**, use **`ctx.authMiddleware.requireAuth({ api: true })`** for 401 JSON instead of redirect.
|
|
93
|
-
- **Login page pitfall:** a **`pages/login.njk`** can register **before** `setupRoutes` and bypass **`requireGuest`**. Prefer login GET/POST in **`setupRoutes`** with templates under **`views/`** only, or omit **`pages/login.njk`** — see [`tests/e2e/auth.spec.js`](../../../tests/e2e/auth.spec.js).
|
|
94
|
-
- **Admin panel** uses a **separate** session (`req.session.adminUser`, `/_admin/api/auth/*`); it does **not** replace **`createApp({ auth })`** for site users.
|
|
95
|
-
- **Site users in the admin UI (`userManagement`):** Opt-in on **`adminPanelPlugin`**. Set **`userManagement: { enabled: true, model: 'User', fields?: { ... } }`** so the SPA shows **Users** (routes like **`/_admin/users`**, **`/_admin/users/new`**, **`/_admin/users/:id/edit`** — same Mithril shell as the rest of the panel). The **`model`** must be the ORM model your site auth uses (e.g. **`quickAuth({ userModel: 'User', ... })`** / **`createAuth`** adapters reading the same table). Pass **`auth: authManager`** with the **same** **`AuthManager`** instance as **`createApp({ auth: authManager })`** when you want **Active Sessions** / revoke APIs (**`rememberTokens`** / **`remember_me`**); without **`auth`**, list/create/update/delete users still work via **`db.getRepository(model)`**, but session endpoints return empty or “not enabled”.
|
|
96
|
-
- **Wiring:** `plugins: [ adminPanelPlugin({ db, auth: authManager, userManagement: { enabled: true, model: 'User' } }) ]` alongside `createApp({ ..., auth: authManager })`. Admin staff log in at **`/_admin`**; end users use your normal site login — two different cookies/sessions.
|
|
97
|
-
|
|
98
|
-
Longer narrative: **[`doc/index.html#authentication`](../../../doc/index.html#authentication)** · **[`#admin-user-management`](../../../doc/index.html#admin-user-management)** · README **Authentication (session)** and **Admin Panel Plugin**.
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## 4. File-based routing (SSR)
|
|
103
|
-
|
|
104
|
-
| Path pattern | URL |
|
|
105
|
-
|--------------|-----|
|
|
106
|
-
| `pages/index.njk` | `/` |
|
|
107
|
-
| `pages/about/index.njk` | `/about` |
|
|
108
|
-
| `pages/tools/[slug].njk` | `/tools/:slug` |
|
|
109
|
-
| `pages/docs/[...rest].njk` | `/docs/*` (catch-all) |
|
|
110
|
-
|
|
111
|
-
**Route config** — sibling `.js` file (e.g. `pages/tools/index.js`):
|
|
112
|
-
|
|
113
|
-
- `middleware` — array of Express functions, **string names** from `createApp({ middlewares })`, or **`['name', options]` tuples** when the registry entry is a **factory** `(options) => (req, res, next) => …` (bare string calls the factory with `{}`). Built-in **`auth`** / **`guest`** (with `createApp({ auth })`) are factories — e.g. **`['auth', { api: true }]`** for JSON 401 on APIs.
|
|
114
|
-
- `load(req, ctx)` — async; return object merged into template context; use **`ctx.db`** when `createApp({ db })` is set.
|
|
115
|
-
- `meta(req, ctx)` — title, description, etc.
|
|
116
|
-
- `hooks` — `beforeLoad`, `afterRender`, etc. (see hook order below).
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
## 5. File-based API (`pages/api/`)
|
|
121
|
-
|
|
122
|
-
| File | Route |
|
|
123
|
-
|------|-------|
|
|
124
|
-
| `api/health.get.js` | `GET /api/health` |
|
|
125
|
-
| `api/items.post.js` | `POST /api/items` |
|
|
126
|
-
| `api/users/[id].get.js` | `GET /api/users/:id` |
|
|
127
|
-
|
|
128
|
-
**Shapes**
|
|
129
|
-
|
|
130
|
-
1. **Function** — `module.exports = async (req, res) => { ... }`
|
|
131
|
-
2. **Object** — **`handler`**, optional **`middleware`** (names, functions, or **`['name', options]`** tuples with factory registry entries), optional **`schema`**
|
|
132
|
-
|
|
133
|
-
**Order:** `req.db` (if any) → **Zod** `schema` → **`middleware`** → **`handler`**.
|
|
134
|
-
|
|
135
|
-
**Zod** — `schema: ({ z }) => ({ body, query, params, response })` → **`req.input`**; invalid → **400** `{ error: 'Validation Error', issues }`.
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
## 6. Global and route hooks
|
|
140
|
-
|
|
141
|
-
**Global:** `pages/_hooks.js` exports `onRequest`, `beforeLoad`, `afterLoad`, `beforeRender`, `afterRender`, `onError`, etc.
|
|
142
|
-
|
|
143
|
-
**Rough order:** global `onRequest` → route middleware chain → `load` → render → `afterRender`. (See README “Hook Execution Order” for full list.)
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
## 7. i18n
|
|
148
|
-
|
|
149
|
-
- **Global:** `pages/locales/<locale>.json` (nested keys).
|
|
150
|
-
- **Route-specific:** `pages/<route>/locales/<locale>.json` overrides global.
|
|
151
|
-
- Templates: `{{ t('nav.home') }}`.
|
|
152
|
-
- Env: `DEFAULT_LOCALE`, `SUPPORTED_LOCALES` (comma-separated).
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
## 8. Template helpers (`fsy`)
|
|
25
|
+
## CLI cheat sheet
|
|
157
26
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
-
|
|
167
|
-
|
|
168
|
-
Analytics plugin adds `fsy.analyticsHead`, `fsy.verificationTags`, etc., when configured.
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
## 9. ORM overview
|
|
173
|
-
|
|
174
|
-
**Define schema** with **`zdb`** (`zdb.id()`, `zdb.uuid()`, `zdb.nanoid()`, `zdb.string({...})`, **`zdb.file({ maxLength, nullable })`** — URL/path string for uploaded assets, `zdb.foreignKey`, `zdb.foreignUuid`, `zdb.foreignNanoid`, `zdb.timestamp`, `zdb.json`, …).
|
|
175
|
-
|
|
176
|
-
**Define model** with **`defineModel({ name, table, schema, relations, scopes, hidden, admin })`**.
|
|
177
|
-
|
|
178
|
-
- **Relations:** `belongsTo`, `hasMany`, `hasOne` with `model: () => OtherModel`.
|
|
179
|
-
- **Scopes:** `softDelete`, `timestamps`, optional `tenant` column.
|
|
180
|
-
- **`hidden`:** columns never exposed in admin/API (e.g. `password_hash`).
|
|
181
|
-
- **Nanoid PK:** `zdb.nanoid()` / `zdb.nanoid({ maxLength: 12 })` — string primary key; migrations use `string(length)`. On **`create()`**, omitting the PK auto-fills a URL-safe id (built-in generator, same alphabet as `nanoid`). Use **`zdb.foreignNanoid('table', { maxLength })`** when the parent uses nanoid PKs; **`generateNanoid`** is exported from `webspresso` for manual ids. In API **`schema`**, use **`z.nanoid()`** / **`z.nanoid(12)`** / **`z.nanoid({ maxLength })`** (the `z` from `schema: ({ z })` is extended by Webspresso). **`zodNanoid`** / **`extendZ`** are also exported for non-route use.
|
|
182
|
-
|
|
183
|
-
**Database:** `createDatabase({ client, connection, models: './models' })` — auto-loads `models/*.js` (ignore `_prefix`).
|
|
184
|
-
|
|
185
|
-
**Repository:** `db.getRepository('User')` → `findById`, `findOne`, `findAll`, `create`, `update`, `delete`, `query()`, …
|
|
186
|
-
|
|
187
|
-
**Query builder:** `UserRepo.query().where(...).with('relation').orderBy(...).list()` / `.first()` / `.paginate()` / `.count()`. **`with()`** eager-loads relations; **`count()`** ignores builder `.limit`/`.offset` for total; see ORM docs for edge cases.
|
|
188
|
-
|
|
189
|
-
**Migrations:** `webspresso db:migrate`, `db:rollback`, `db:status`, `db:make`.
|
|
190
|
-
|
|
191
|
-
**Transactions:** `db.transaction(async (trx) => { trx.getRepository('User') })`.
|
|
192
|
-
|
|
193
|
-
**Query cache (optional):** `createDatabase({ ..., cache: true })` or `cache: { defaultStrategy: 'auto'|'smart', memory: { maxEntries, defaultTtlMs }, provider?: custom }`. Opt-in per model: `defineModel({ ..., cache: 'auto'|'smart'|true })`. API: `db.cache` → `getMetrics()`, `purge()`, `invalidateModel(name)`, `invalidateTags(tags[])`, `resetMetrics()`. Reads bypass cache when using a transaction knex. Admin UI: `ormCacheAdminPlugin({ db })` (needs `admin-panel` and `cache` enabled).
|
|
194
|
-
|
|
195
|
-
Pass **`db`** into **`createApp({ db })`** so **`ctx.db`** works in pages and plugins. **`pages/api/`** handlers receive **`req.db`** (and route **`middleware`** runs after it). Outside requests, use **`getDb()`** / **`hasDb()`**; for **`setupRoutes`**-only routes, use **`attachDbMiddleware`**.
|
|
196
|
-
|
|
197
|
-
---
|
|
198
|
-
|
|
199
|
-
## 10. Plugins (built-in — concise)
|
|
200
|
-
|
|
201
|
-
| Plugin | Purpose |
|
|
202
|
-
|--------|---------|
|
|
203
|
-
| `dashboardPlugin` | Dev route `/_webspresso` — route list |
|
|
204
|
-
| `sitemapPlugin` | `/sitemap.xml`, robots; optional DB-driven URLs |
|
|
205
|
-
| `analyticsPlugin` | GA / GTM / Yandex / Bing / Facebook — `fsy` helpers |
|
|
206
|
-
| `adminPanelPlugin` | SPA admin CRUD — needs **`db`**; optional **`uploadUrl`** (or infer from **`uploadPlugin`**); optional **`userManagement: { enabled, model, fields }`** + **`auth`** (same **`AuthManager`** as **`createApp({ auth })`**) for site-user CRUD + remember-me session UI — see **Session authentication** above |
|
|
207
|
-
| `uploadPlugin` | `POST` multipart (`multer`), `createLocalFileProvider` or custom `provider`; set **`mimeAllowlist`** / **`maxBytes`** in production |
|
|
208
|
-
| `siteAnalyticsPlugin` | Self-hosted page views + admin charts |
|
|
209
|
-
| `auditLogPlugin` | Admin mutation audit trail |
|
|
210
|
-
| `recaptchaPlugin` | v2/v3 + middleware |
|
|
211
|
-
| `seoCheckerPlugin` | Dev SEO panel |
|
|
212
|
-
| `restResourcePlugin` | Opt-in REST CRUD from models; `?include=` uses ORM eager load (single-level relations only) |
|
|
213
|
-
| `ormCacheAdminPlugin` | Admin page for ORM cache metrics / purge / invalidate (`db.cache` required) |
|
|
214
|
-
|
|
215
|
-
**File uploads:** `require('webspresso').uploadPlugin` / `createLocalFileProvider` — response `{ url, publicUrl, key? }`; admin reads **`settings.uploadUrl`** when `uploadPlugin` is registered before `adminPanelPlugin` (or pass **`adminPanelPlugin({ uploadUrl })`**). Docs: README **File upload plugin**, **`doc/index.html#plugins`**.
|
|
216
|
-
|
|
217
|
-
**Custom plugin:** `name`, `version`, `register(ctx)`, `onRoutesReady(ctx)` — use `ctx.app`, `ctx.db`, `ctx.addHelper`, `ctx.addRoute`, `ctx.usePlugin('other')`. Plugin failures **warn**; app keeps running.
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
|
-
## 11. CLI (project directory)
|
|
222
|
-
|
|
223
|
-
| Command | Role |
|
|
224
|
-
|---------|------|
|
|
225
|
-
| `webspresso new` | Scaffold project — includes **`config/load-env.js`** (`.env` chain), **`config/env.schema.js`** (Zod), **`config/app.js`** (`createApp` paths + optional `db` if `webspresso.db.js` exists), thin **`server.js`** |
|
|
226
|
-
| `webspresso new .` / `new ./` | Scaffold **into the current directory** (same as interactive “install here”). `package.json` **`name`** = folder basename, or **`webspresso-app`** if basename is not npm-safe. **Aborts** if **`server.js`** or **`pages/`** already exists (already a Webspresso layout). |
|
|
227
|
-
| `webspresso new … --yes` | **Non-interactive:** skips DB/seed prompts (no DB), skips “install now?” unless you pass **`-i` / `--install`**, skips “start dev server?” after install. Use for CI and agent tools. |
|
|
228
|
-
| `webspresso dev` / `start` | Servers |
|
|
229
|
-
| `webspresso page` / `api` | Interactive scaffolding |
|
|
230
|
-
| `webspresso db:*` | migrate, rollback, status, make |
|
|
231
|
-
| `webspresso seed` | Seed data |
|
|
232
|
-
| `webspresso doctor` | Env / layout / `.env` vs `.env.example` / `config/load-env.js` / optional `--db` check |
|
|
233
|
-
| `webspresso skill` | Cursor `SKILL.md` scaffold |
|
|
234
|
-
| `webspresso skill --preset webspresso` | Copy bundled **Webspresso agent reference** skill |
|
|
235
|
-
| `webspresso add tailwind` | Tailwind setup |
|
|
236
|
-
| `webspresso favicon:generate` | Favicons + manifest |
|
|
237
|
-
| `webspresso admin:setup` / `admin:password` | Admin users |
|
|
238
|
-
| `webspresso audit:prune` | Audit log retention |
|
|
239
|
-
|
|
240
|
-
**`webspresso new` — current dir & automation**
|
|
241
|
-
|
|
242
|
-
- **`new .`** does **not** treat the cwd as “directory already exists”; it scaffolds **in place** next to any existing non-dot files.
|
|
243
|
-
- **Non-empty cwd:** interactive prompt defaults to **continue**; if **stdin is not a TTY** (piped/agent) or you pass **`--yes`**, the “continue?” step is skipped and a short **info** line is printed instead.
|
|
244
|
-
- **Typical agent / vibe-coding one-liners:** `webspresso new . --yes --no-tailwind` · `webspresso new . --yes -i` (scaffold + install, no dev server prompt).
|
|
245
|
-
|
|
246
|
-
---
|
|
247
|
-
|
|
248
|
-
## 12. Environment variables (common)
|
|
249
|
-
|
|
250
|
-
| Var | Notes |
|
|
251
|
-
|-----|-------|
|
|
252
|
-
| `NODE_ENV` | `development` / `production` |
|
|
253
|
-
| `DEFAULT_LOCALE` | Default locale |
|
|
254
|
-
| `SUPPORTED_LOCALES` | Comma-separated |
|
|
255
|
-
| `BASE_URL` | Canonical / links |
|
|
256
|
-
| `DATABASE_URL` | DB connection |
|
|
257
|
-
| `SESSION_SECRET` | Session cookie signing — set on auth config **`session.secret`** or read from env in app code |
|
|
258
|
-
| `WEBSPRESSO_ALPINE` | If set to **`1`** or **`true`**, forces **`clientRuntime.alpine`** on (overrides `createApp` for that flag). |
|
|
259
|
-
| `WEBSPRESSO_SWUP` | If set to **`1`** or **`true`**, forces **`clientRuntime.swup`** on (overrides `createApp` for that flag). |
|
|
260
|
-
|
|
261
|
-
---
|
|
262
|
-
|
|
263
|
-
## 13. Testing
|
|
264
|
-
|
|
265
|
-
- **Unit / integration:** `npm test` (Vitest).
|
|
266
|
-
- **E2E:** `npm run test:e2e` (Playwright), including **`tests/e2e/swup.spec.js`** for **`clientRuntime`** (Alpine + swup).
|
|
267
|
-
|
|
268
|
-
Touching **CLI**, **ORM**, **server routing**, or **client runtime** — run the relevant suite.
|
|
269
|
-
|
|
270
|
-
---
|
|
271
|
-
|
|
272
|
-
## 14. Pitfalls (for agents)
|
|
273
|
-
|
|
274
|
-
1. **Custom Express routes** must be registered in a way that runs **before** the catch-all 404 inside `createApp` — use **`setupRoutes`** (see [`src/server.js`](../../../src/server.js)), not only `app.use` after `createApp` returns.
|
|
275
|
-
2. **File-router wins first:** If both `pages/foo.njk` and a manual route exist, understand **order** — prefer file-based structure for SSR.
|
|
276
|
-
3. **`ctx.db` is undefined** unless `createApp({ db })` receives a `createDatabase()` instance.
|
|
277
|
-
4. **ORM hidden fields** — never return `hidden` columns to clients; use explicit selects if needed.
|
|
278
|
-
5. **Zod on API** — invalid input surfaces as validation errors; handlers should assume **`req.input`** is validated when schema is set.
|
|
279
|
-
6. **Built-in `auth` option** — if you pass **`createApp({ auth })`**, do not expect a custom **`middlewares.auth`** to apply; the framework assigns **`auth`** / **`guest`** to the session guards.
|
|
280
|
-
7. **Login route vs file router** — **`pages/login.njk`** can shadow custom login handlers; align with **`setupRoutes`** + **`views/`** pattern above.
|
|
281
|
-
8. **Client runtime + swup** — without **`<main id="swup">`** (or matching **`containers`**), transitions will not replace the intended region. Include **`partials/webspresso-client-runtime.njk`** in the layout when flags are on. Tailwind CDN / third-party scripts may require **`helmet: false`** in dev or relaxed **`script-src`** in production.
|
|
282
|
-
|
|
283
|
-
---
|
|
27
|
+
| Command | Purpose |
|
|
28
|
+
|---------|---------|
|
|
29
|
+
| `webspresso new [name]` or `webspresso new .` | Scaffold a new app (Tailwind, i18n, optional DB). |
|
|
30
|
+
| `webspresso new . --yes` | Non-interactive scaffold (CI / agents). |
|
|
31
|
+
| `webspresso new . --yes -i` | Scaffold + install deps / CSS build. |
|
|
32
|
+
| `webspresso dev` | Dev server (watch). |
|
|
33
|
+
| `webspresso db:migrate` / `db:rollback` / `db:status` | ORM migrations. |
|
|
34
|
+
| `webspresso doctor` | Environment / project sanity checks. |
|
|
35
|
+
| `webspresso skill --preset webspresso` | Copies this skill bundle under `.cursor/skills/webspresso-usage/` (`SKILL.md` + `REFERENCE-*.md`). |
|
|
284
36
|
|
|
285
|
-
##
|
|
37
|
+
## When to use this skill
|
|
286
38
|
|
|
287
|
-
-
|
|
288
|
-
-
|
|
289
|
-
- Debugging **404 order**, **i18n**, **session/auth**, **ORM**, **admin** integration, or **swup / Alpine** (e.g. missing **`#swup`**, CSP blocking scripts).
|
|
39
|
+
- **`REFERENCE-framework.md`:** Routes, APIs, ORM, auth, plugins, i18n, hooks, client runtime (Alpine/swup), `setupRoutes`, 404 ordering.
|
|
40
|
+
- **`REFERENCE-kernel.md`:** `kernel`, domain events, `dispatch` / `publish`, `registerFlow`, `definePlugin` inside the kernel.
|
|
290
41
|
|
|
291
|
-
|
|
42
|
+
They are independent: most apps only need SSR **`createApp`**; the kernel is optional.
|