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.
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>
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>