webspresso 0.0.67 → 0.0.69
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 +58 -0
- package/bin/commands/doctor.js +23 -0
- package/bin/commands/new.js +253 -79
- package/core/orm/migrations/scaffold.js +6 -0
- package/core/orm/schema-helpers.js +19 -0
- package/core/orm/types.js +1 -1
- package/index.d.ts +34 -1
- package/index.js +15 -1
- package/package.json +6 -1
- package/plugins/admin-panel/api.js +5 -5
- package/plugins/admin-panel/components.js +184 -0
- package/plugins/admin-panel/core/registry.js +1 -0
- package/plugins/admin-panel/field-renderers/file-upload.js +135 -66
- package/plugins/admin-panel/field-renderers/index.js +1 -0
- package/plugins/admin-panel/index.js +8 -0
- package/plugins/index.js +3 -0
- package/plugins/upload/index.js +188 -0
- package/plugins/upload/local-file-provider.js +122 -0
- package/src/file-router.js +95 -34
- package/templates/skills/webspresso-usage/SKILL.md +62 -22
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
name: webspresso-usage
|
|
3
3
|
description: >-
|
|
4
4
|
Comprehensive Webspresso framework reference: file-based SSR and API routing,
|
|
5
|
-
createApp options
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
createApp options including optional clientRuntime (Alpine.js, swup v4, no HTMX),
|
|
6
|
+
session auth (createAuth, quickAuth, webspresso/core/auth), Nunjucks/fsy helpers,
|
|
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.
|
|
10
12
|
---
|
|
11
13
|
|
|
12
14
|
# Webspresso — agent reference
|
|
@@ -19,7 +21,7 @@ description: >-
|
|
|
19
21
|
- **ORM**: Knex-backed layer in `core/orm` — `defineModel`, `zdb` schema helpers, repositories, query builder, migrations.
|
|
20
22
|
- **Plugins**: Register in `createApp({ plugins })`; optional `db` passed as `ctx.db`.
|
|
21
23
|
|
|
22
|
-
Public API surface: `require('webspresso')` / [`index.js`](../../../index.js) — `createApp`, file-router utilities, `createHelpers`, plugin manager, ORM exports, built-in plugins.
|
|
24
|
+
Public API surface: `require('webspresso')` / [`index.js`](../../../index.js) — `createApp`, **`resolveClientRuntime`**, **`CLIENT_RUNTIME_BASE`**, file-router utilities, `createHelpers`, plugin manager, ORM exports, built-in plugins. **Session auth** lives in [`core/auth`](../../../core/auth) — import **`require('webspresso/core/auth')`** (`createAuth`, `quickAuth`, `hash`, `verify`, `setupAuthMiddleware`, `createRememberTokensTable`, policy helpers); wire with **`createApp({ auth })`**.
|
|
23
25
|
|
|
24
26
|
---
|
|
25
27
|
|
|
@@ -66,11 +68,31 @@ project/
|
|
|
66
68
|
| `timeout` | e.g. `'30s'` or `false` |
|
|
67
69
|
| `helmet` | `true` / `false` / object |
|
|
68
70
|
| `assets` | `{ version, manifestPath, prefix }` for `fsy.asset` / `fsy.css` / `fsy.js` |
|
|
69
|
-
| `clientRuntime` | Opt-in
|
|
70
|
-
| `auth` |
|
|
71
|
-
| `setupRoutes(app, ctx)` | **Register custom Express routes here** — runs **after** file routes and plugins’ `onRoutesReady`, **before** 404.
|
|
71
|
+
| `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. |
|
|
72
|
+
| `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). |
|
|
73
|
+
| `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)). |
|
|
72
74
|
|
|
73
|
-
**Returns:** `{ app, nunjucksEnv, pluginManager, authMiddleware
|
|
75
|
+
**Returns:** `{ app, nunjucksEnv, pluginManager, authMiddleware }` — `authMiddleware` is **`null`** when `auth` was not configured.
|
|
76
|
+
|
|
77
|
+
### Client runtime — implementation notes
|
|
78
|
+
|
|
79
|
+
- **Package helpers:** `resolveClientRuntime(options)` merges **`createApp({ clientRuntime })`** with env; **`CLIENT_RUNTIME_BASE`** is **`/__webspresso/client-runtime`** (script URLs under that path).
|
|
80
|
+
- **After swup navigation:** bootstrap runs **`Alpine.initTree`** on **`#swup`** on swup’s **`content:replace`** so new SSR markup gets Alpine bindings.
|
|
81
|
+
- **Default `ignoreVisit` (bootstrap):** links under **`/_admin`**, **`/_webspresso`**, elements with **`data-no-swup`**, plus swup’s usual rules (e.g. `target="_blank"`, other origin).
|
|
82
|
+
- **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.
|
|
83
|
+
- **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**.
|
|
84
|
+
|
|
85
|
+
### Session authentication — essentials
|
|
86
|
+
|
|
87
|
+
- **Import:** `const { createAuth, quickAuth, hash, verify, createRememberTokensTable } = require('webspresso/core/auth')` (published `core/` tree on npm; **not** re-exported from package root).
|
|
88
|
+
- **`createAuth({ findUserById, findUserByCredentials, session: { secret }, rememberTokens?, ... })`** — adapter pattern; optional **remember-me** via `rememberTokens: { create, find, delete, deleteAllForUser }` + **`createRememberTokensTable(knex)`** for the default table shape.
|
|
89
|
+
- **`quickAuth({ db, userModel, identifierField, passwordField, rememberMe })`** — wires **`getRepository`** + bcrypt **`verify`**; optional Knex **`remember_tokens`** when `rememberMe: true`.
|
|
90
|
+
- **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`**).
|
|
91
|
+
- **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.
|
|
92
|
+
- **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).
|
|
93
|
+
- **Admin panel** uses a **separate** session (`req.session.adminUser`, `/_admin/api/auth/*`); it does **not** replace **`createApp({ auth })`** for site users.
|
|
94
|
+
|
|
95
|
+
Longer narrative: **[`doc/index.html#authentication`](../../../doc/index.html#authentication)** · README **Authentication (session)**.
|
|
74
96
|
|
|
75
97
|
---
|
|
76
98
|
|
|
@@ -85,7 +107,7 @@ project/
|
|
|
85
107
|
|
|
86
108
|
**Route config** — sibling `.js` file (e.g. `pages/tools/index.js`):
|
|
87
109
|
|
|
88
|
-
- `middleware` — array of functions
|
|
110
|
+
- `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.
|
|
89
111
|
- `load(req, ctx)` — async; return object merged into template context; use **`ctx.db`** when `createApp({ db })` is set.
|
|
90
112
|
- `meta(req, ctx)` — title, description, etc.
|
|
91
113
|
- `hooks` — `beforeLoad`, `afterRender`, etc. (see hook order below).
|
|
@@ -103,7 +125,7 @@ project/
|
|
|
103
125
|
**Shapes**
|
|
104
126
|
|
|
105
127
|
1. **Function** — `module.exports = async (req, res) => { ... }`
|
|
106
|
-
2. **Object** — **`handler`**, optional **`middleware`** (names
|
|
128
|
+
2. **Object** — **`handler`**, optional **`middleware`** (names, functions, or **`['name', options]`** tuples with factory registry entries), optional **`schema`**
|
|
107
129
|
|
|
108
130
|
**Order:** `req.db` (if any) → **Zod** `schema` → **`middleware`** → **`handler`**.
|
|
109
131
|
|
|
@@ -146,14 +168,14 @@ Analytics plugin adds `fsy.analyticsHead`, `fsy.verificationTags`, etc., when co
|
|
|
146
168
|
|
|
147
169
|
## 9. ORM overview
|
|
148
170
|
|
|
149
|
-
**Define schema** with **`zdb`** (`zdb.id()`, `zdb.uuid()`, `zdb.nanoid()`, `zdb.string({...})`, `zdb.foreignKey`, `zdb.foreignUuid`, `zdb.foreignNanoid`, `zdb.timestamp`, `zdb.json`, …).
|
|
171
|
+
**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`, …).
|
|
150
172
|
|
|
151
173
|
**Define model** with **`defineModel({ name, table, schema, relations, scopes, hidden, admin })`**.
|
|
152
174
|
|
|
153
175
|
- **Relations:** `belongsTo`, `hasMany`, `hasOne` with `model: () => OtherModel`.
|
|
154
176
|
- **Scopes:** `softDelete`, `timestamps`, optional `tenant` column.
|
|
155
177
|
- **`hidden`:** columns never exposed in admin/API (e.g. `password_hash`).
|
|
156
|
-
- **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.
|
|
178
|
+
- **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.
|
|
157
179
|
|
|
158
180
|
**Database:** `createDatabase({ client, connection, models: './models' })` — auto-loads `models/*.js` (ignore `_prefix`).
|
|
159
181
|
|
|
@@ -178,13 +200,17 @@ Pass **`db`** into **`createApp({ db })`** so **`ctx.db`** works in pages and pl
|
|
|
178
200
|
| `dashboardPlugin` | Dev route `/_webspresso` — route list |
|
|
179
201
|
| `sitemapPlugin` | `/sitemap.xml`, robots; optional DB-driven URLs |
|
|
180
202
|
| `analyticsPlugin` | GA / GTM / Yandex / Bing / Facebook — `fsy` helpers |
|
|
181
|
-
| `adminPanelPlugin` | SPA admin CRUD — needs `db` |
|
|
203
|
+
| `adminPanelPlugin` | SPA admin CRUD — needs `db`; optional `uploadUrl` or infer from `uploadPlugin` order |
|
|
204
|
+
| `uploadPlugin` | `POST` multipart (`multer`), `createLocalFileProvider` or custom `provider`; set **`mimeAllowlist`** / **`maxBytes`** in production |
|
|
182
205
|
| `siteAnalyticsPlugin` | Self-hosted page views + admin charts |
|
|
183
206
|
| `auditLogPlugin` | Admin mutation audit trail |
|
|
184
207
|
| `recaptchaPlugin` | v2/v3 + middleware |
|
|
185
208
|
| `seoCheckerPlugin` | Dev SEO panel |
|
|
209
|
+
| `restResourcePlugin` | Opt-in REST CRUD from models; `?include=` uses ORM eager load (single-level relations only) |
|
|
186
210
|
| `ormCacheAdminPlugin` | Admin page for ORM cache metrics / purge / invalidate (`db.cache` required) |
|
|
187
211
|
|
|
212
|
+
**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`**.
|
|
213
|
+
|
|
188
214
|
**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.
|
|
189
215
|
|
|
190
216
|
---
|
|
@@ -193,12 +219,14 @@ Pass **`db`** into **`createApp({ db })`** so **`ctx.db`** works in pages and pl
|
|
|
193
219
|
|
|
194
220
|
| Command | Role |
|
|
195
221
|
|---------|------|
|
|
196
|
-
| `webspresso new` | Scaffold project |
|
|
222
|
+
| `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`** |
|
|
223
|
+
| `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). |
|
|
224
|
+
| `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. |
|
|
197
225
|
| `webspresso dev` / `start` | Servers |
|
|
198
226
|
| `webspresso page` / `api` | Interactive scaffolding |
|
|
199
227
|
| `webspresso db:*` | migrate, rollback, status, make |
|
|
200
228
|
| `webspresso seed` | Seed data |
|
|
201
|
-
| `webspresso doctor` | Env / layout / optional `--db` check |
|
|
229
|
+
| `webspresso doctor` | Env / layout / `.env` vs `.env.example` / `config/load-env.js` / optional `--db` check |
|
|
202
230
|
| `webspresso skill` | Cursor `SKILL.md` scaffold |
|
|
203
231
|
| `webspresso skill --preset webspresso` | Copy bundled **Webspresso agent reference** skill |
|
|
204
232
|
| `webspresso add tailwind` | Tailwind setup |
|
|
@@ -206,6 +234,12 @@ Pass **`db`** into **`createApp({ db })`** so **`ctx.db`** works in pages and pl
|
|
|
206
234
|
| `webspresso admin:setup` / `admin:password` | Admin users |
|
|
207
235
|
| `webspresso audit:prune` | Audit log retention |
|
|
208
236
|
|
|
237
|
+
**`webspresso new` — current dir & automation**
|
|
238
|
+
|
|
239
|
+
- **`new .`** does **not** treat the cwd as “directory already exists”; it scaffolds **in place** next to any existing non-dot files.
|
|
240
|
+
- **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.
|
|
241
|
+
- **Typical agent / vibe-coding one-liners:** `webspresso new . --yes --no-tailwind` · `webspresso new . --yes -i` (scaffold + install, no dev server prompt).
|
|
242
|
+
|
|
209
243
|
---
|
|
210
244
|
|
|
211
245
|
## 12. Environment variables (common)
|
|
@@ -217,15 +251,18 @@ Pass **`db`** into **`createApp({ db })`** so **`ctx.db`** works in pages and pl
|
|
|
217
251
|
| `SUPPORTED_LOCALES` | Comma-separated |
|
|
218
252
|
| `BASE_URL` | Canonical / links |
|
|
219
253
|
| `DATABASE_URL` | DB connection |
|
|
254
|
+
| `SESSION_SECRET` | Session cookie signing — set on auth config **`session.secret`** or read from env in app code |
|
|
255
|
+
| `WEBSPRESSO_ALPINE` | If set to **`1`** or **`true`**, forces **`clientRuntime.alpine`** on (overrides `createApp` for that flag). |
|
|
256
|
+
| `WEBSPRESSO_SWUP` | If set to **`1`** or **`true`**, forces **`clientRuntime.swup`** on (overrides `createApp` for that flag). |
|
|
220
257
|
|
|
221
258
|
---
|
|
222
259
|
|
|
223
260
|
## 13. Testing
|
|
224
261
|
|
|
225
262
|
- **Unit / integration:** `npm test` (Vitest).
|
|
226
|
-
- **E2E:** `npm run test:e2e` (Playwright).
|
|
263
|
+
- **E2E:** `npm run test:e2e` (Playwright), including **`tests/e2e/swup.spec.js`** for **`clientRuntime`** (Alpine + swup).
|
|
227
264
|
|
|
228
|
-
Touching **CLI**, **ORM**,
|
|
265
|
+
Touching **CLI**, **ORM**, **server routing**, or **client runtime** — run the relevant suite.
|
|
229
266
|
|
|
230
267
|
---
|
|
231
268
|
|
|
@@ -236,13 +273,16 @@ Touching **CLI**, **ORM**, or **server routing** — run the relevant suite.
|
|
|
236
273
|
3. **`ctx.db` is undefined** unless `createApp({ db })` receives a `createDatabase()` instance.
|
|
237
274
|
4. **ORM hidden fields** — never return `hidden` columns to clients; use explicit selects if needed.
|
|
238
275
|
5. **Zod on API** — invalid input surfaces as validation errors; handlers should assume **`req.input`** is validated when schema is set.
|
|
276
|
+
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.
|
|
277
|
+
7. **Login route vs file router** — **`pages/login.njk`** can shadow custom login handlers; align with **`setupRoutes`** + **`views/`** pattern above.
|
|
278
|
+
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.
|
|
239
279
|
|
|
240
280
|
---
|
|
241
281
|
|
|
242
282
|
## 15. When to load this skill
|
|
243
283
|
|
|
244
|
-
- Adding or changing **pages**, **API routes**, **models**, **migrations**, **plugins**, or **
|
|
284
|
+
- Adding or changing **pages**, **API routes**, **models**, **migrations**, **plugins**, **locales**, **session auth** (`createAuth`, `createApp({ auth })`, policies), or **client runtime** (`createApp({ clientRuntime })`, Alpine, swup, layout partials).
|
|
245
285
|
- Explaining **Webspresso** behavior vs plain Express.
|
|
246
|
-
- Debugging **404 order**, **i18n**, **session/auth**, **ORM**, or **
|
|
286
|
+
- Debugging **404 order**, **i18n**, **session/auth**, **ORM**, **admin** integration, or **swup / Alpine** (e.g. missing **`#swup`**, CSP blocking scripts).
|
|
247
287
|
|
|
248
|
-
For authoritative long-form detail, see **[README.md](../../../README.md)** in the repo.
|
|
288
|
+
For authoritative long-form detail, see **[README.md](../../../README.md)** and **[`doc/index.html`](../../../doc/index.html)** (e.g. **Authentication**, **[Client runtime](../../../doc/index.html#client-runtime)**) in the repo.
|