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.
@@ -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, Nunjucks/fsy helpers, i18n, lifecycle hooks, Zod API validation,
6
- ORM (zdb, defineModel, repository, query builder, migrations), plugins (admin,
7
- analytics, sitemap, SEO, audit, recaptcha), CLI, env vars, and testing. Use when
8
- working in this repo or any Webspresso app—adding routes, APIs, models, plugins,
9
- or debugging routing, ctx.db, or templates.
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 `{ alpine?, swup? }` serves `/__webspresso/client-runtime/*`, template `clientRuntime`, partial `views/partials/webspresso-client-runtime.njk`, `<main id="swup">` when swup on. Env: `WEBSPRESSO_ALPINE`, `WEBSPRESSO_SWUP`. |
70
- | `auth` | Auth manager from `createAuth` (session routes) |
71
- | `setupRoutes(app, ctx)` | **Register custom Express routes here** — runs **after** file routes and plugins’ `onRoutesReady`, **before** 404. `ctx.clientRuntime` included. Do not rely on `app.get` *after* `createApp` returns unless routes are appended before the 404 middleware (see [`src/server.js`](../../../src/server.js)). |
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? }` (and related).
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 or **names** from `createApp({ middlewares })`.
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 from **`createApp({ middlewares })`**), optional **`schema`**
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**, or **server routing** — run the relevant suite.
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 **locales**.
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 **admin** integration.
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.