Changes already present in the working tree at the time of the versioning
commit, committed here so nothing is left dangling:
- index.js: template PUT preserves the untouched field (?? existing) and the
preview handler tolerates a null/empty test_object
- update-deployed.sh: detect and restart a pm2-managed app process, and don't
hard-fail when no bare Node process is found
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Snapshot the code-bearing fields of routes (verb/path/handler), templates
(name/content/test_object), and db libraries (name/library) on every
content-changing save, so nothing edited is ever lost. Recorded in a new
`versions` table via recordVersion() hooked into the create/update functions
in db.js (the single write choke point), deduped against the latest snapshot
so no-op saves, error-flag-only route writes, and server-restart WS
re-bootstraps don't pile up. The live row stays current; old snapshots are the
undo trail. Read-only, no revert UI yet.
- migrations/004_add_versions.sql: versions table + indexes
- db.js: recordVersion/getVersions/getVersion + create/update hooks
- plumbing.js: /versions/:id and per-entity .../versions list endpoints
- bliss: `versions <type> <sid> <id>` and `version <id>` reads;
`update-settings` (edit route_prefix/head_injection, preserving the
untouched field); `use <url>` sticky target persisted to ~/.bliss/target
so BLISS_URL needn't be re-exported each call
- client.js: target-file precedence (BLISS_URL > ~/.bliss/target > localhost)
- SKILL.md: document the above
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Db library scripts are basically a JS module scoped to the db, and
handlers already get fetch — but runLibrary's own vm context only
bound sql/console, so any library calling fetch (e.g. the chat db's
OpenAI call) threw ReferenceError. Since library calls are often
fire-and-forget inside setTimeout (no rejection handler), that
crashed the whole process. Bind fetch in all three places a library
script runs: request-time (runLibrary), the library-editor save eval,
and repl.
Also add `bliss link` to bliss-cli for jumping straight to a
structure/route/template/db's workshop editor URL, including
resolving a db by its require('db')(alias) name.
Simplify render pipeline, sandbox context, and db helpers; fix route
precedence so the most recently saved route wins.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The app.all("*") dispatcher hand-rolled a SELECT * FROM routes while
every other read goes through the model layer. Use model.getRoute for
consistency; the handler only reads structure_id/id/verb/handler, all
of which it returns.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The `e.stack ? \`${e}\n\n${e.stack}\` : \`${e}\`` + createLog(...true)
pattern appeared in four catch blocks. Extract formatError(e) and
logError(structureId, routeId, e) and call them instead. Behavior and
the 500 response body are unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
updateRoute/updateDb/updateStruct/updateTemplate/updateScaffoldPage
were the same UPDATE-by-id builder with a different table and field
list. Extract a single update(table, fields, obj) and express each as
a one-liner. (updateScaffoldPage's two stray debug console.logs went
away with the rewrite; it keeps its changes>0 return.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove the shared-mutable-state dance where each db library was run in
the handler context via `context.sql = x; run; module.exports = null`.
Split into three single-purpose functions:
- makeConsole(structureId, routeId) — the logs-table-backed console
- runLibrary(sql, source, console) — eval one library in its own context
- makeLibs(structureId, console) — the require() targets map
Libraries are still evaluated eagerly per request (no laziness), and
still see `sql` and `console`, so behavior is preserved. Verified the
prime db library's top-level console.log still fires on a live request.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The matcher loop kept the last match instead of the first. Since
routes[verb] is ordered by updated_at DESC, that meant the OLDEST
route won when two routes matched the same path. Break on first
match so the most recently saved route wins.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the one branchy bootstrapTemplateWithHTMXetc (six positional
args, five jobs) with small pure functions: headChrome, blissAttrs,
decoratePage, decorateFragment, and a decorate() entry point. The
route/clone/copy provenance triple becomes a single `source` value
({structureId, routeId, copyUrl}) threaded through render — the same
data the inspector roadmap needs.
Collapse the four copy-pasted render sites (res.render, ws.render,
template preview, scaffold preview) onto renderTemplate + decorate,
and dedupe the per-structure route() closure into makeRoute().
Emitted HTML (head chrome + data-bliss-* attributes) is unchanged;
verified full-page and htmx-fragment output.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>