Allow fetch in db library vm contexts
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.
This commit is contained in:
parent
44f345fff0
commit
03a9d2143d
4 changed files with 62 additions and 4 deletions
|
|
@ -32,7 +32,7 @@ The Express app has two kinds of routes:
|
||||||
|
|
||||||
User route handlers run in `vm.createContext()` with a restricted API:
|
User route handlers run in `vm.createContext()` with a restricted API:
|
||||||
- `require('eta')` — Eta template instance scoped to the structure
|
- `require('eta')` — Eta template instance scoped to the structure
|
||||||
- `require('db')` — getter for named database instances. `require('db')(alias)` returns `{ library, sql }`: `sql` is the raw `better-sqlite3` instance for that db, and `library` is `module.exports` from that db's own "library" script (arbitrary JS, edited via `/workshop/:structure_id/db/:db_id/library`, run once per request with `sql` bound in its own vm context — see `bootstrapContext` in `index.js`). Handlers typically call helper functions off `library` rather than writing raw SQL inline.
|
- `require('db')` — getter for named database instances. `require('db')(alias)` returns `{ library, sql }`: `sql` is the raw `better-sqlite3` instance for that db, and `library` is `module.exports` from that db's own "library" script (arbitrary JS, edited via `/workshop/:structure_id/db/:db_id/library`, run once per request with `sql`, `console`, and `fetch` bound in its own vm context — see `runLibrary` in `index.js`). Handlers typically call helper functions off `library` rather than writing raw SQL inline.
|
||||||
- `require('push')` — web-push library
|
- `require('push')` — web-push library
|
||||||
- `require('files')` — `{ saveFile }` helper
|
- `require('files')` — `{ saveFile }` helper
|
||||||
- `console.log` — writes to the app's `logs` table (not stdout)
|
- `console.log` — writes to the app's `logs` table (not stdout)
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,23 @@ bliss preview-route <sid> <rid> # rendered HTML of a GET route
|
||||||
bliss preview-template <sid> <tid> # rendered HTML using the template's test_object
|
bliss preview-template <sid> <tid> # rendered HTML using the template's test_object
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
Jump straight into the workshop editor for something instead of constructing
|
||||||
|
the URL by hand:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bliss link structure <sid>
|
||||||
|
bliss link route <sid> <rid>
|
||||||
|
bliss link template <sid> <tid>
|
||||||
|
bliss link db <sid> <dbId> # by numeric db id
|
||||||
|
bliss link db <sid> <alias> # or by the alias used in require('db')(alias)
|
||||||
|
```
|
||||||
|
|
||||||
|
`bliss link db` resolves an alias (e.g. `chat`, as in `require('db')('chat')`)
|
||||||
|
by looking at the structure's attached dbs — handy since the same alias name
|
||||||
|
often points to a different db per structure (aliased/cloned dbs).
|
||||||
|
|
||||||
## Editing code (handlers, templates, library)
|
## Editing code (handlers, templates, library)
|
||||||
|
|
||||||
Handlers/templates/library are multiline code. Write the code to a temp file and
|
Handlers/templates/library are multiline code. Write the code to a temp file and
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,44 @@ const commands = {
|
||||||
);
|
);
|
||||||
out(await c.getText(`/workshop/${sid}/template/${tid}/preview`));
|
out(await c.getText(`/workshop/${sid}/template/${tid}/preview`));
|
||||||
},
|
},
|
||||||
|
async link({ pos }) {
|
||||||
|
const [type, sid, ref] = pos;
|
||||||
|
need(
|
||||||
|
type && sid,
|
||||||
|
"usage: bliss link structure <sid> | route <sid> <rid> | template <sid> <tid> | db <sid> <dbId|alias>",
|
||||||
|
);
|
||||||
|
switch (type) {
|
||||||
|
case "structure":
|
||||||
|
out(`${c.BASE}/workshop/${sid}`);
|
||||||
|
break;
|
||||||
|
case "route":
|
||||||
|
need(ref, "usage: bliss link route <sid> <rid>");
|
||||||
|
out(`${c.BASE}/workshop/${sid}/route/${ref}`);
|
||||||
|
break;
|
||||||
|
case "template":
|
||||||
|
need(ref, "usage: bliss link template <sid> <tid>");
|
||||||
|
out(`${c.BASE}/workshop/${sid}/template/${ref}`);
|
||||||
|
break;
|
||||||
|
case "db": {
|
||||||
|
need(ref, "usage: bliss link db <sid> <dbId|alias>");
|
||||||
|
let dbId = ref;
|
||||||
|
if (!/^\d+$/.test(ref)) {
|
||||||
|
// ref is an alias (the string passed to require('db')(alias) in
|
||||||
|
// handler code) rather than a numeric db id — resolve it.
|
||||||
|
const { dbs } = await c.getJSON(`/plumbing/structures/${sid}`);
|
||||||
|
const match = dbs.find((d) => d.alias === ref);
|
||||||
|
need(match, `no db aliased "${ref}" on structure ${sid}`);
|
||||||
|
dbId = match.id;
|
||||||
|
}
|
||||||
|
out(`${c.BASE}/workshop/${sid}/db/${dbId}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error(
|
||||||
|
`unknown link type "${type}" (want structure|route|template|db)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
async upload({ pos }) {
|
async upload({ pos }) {
|
||||||
const [sid, filepath] = pos;
|
const [sid, filepath] = pos;
|
||||||
need(sid && filepath, "usage: bliss upload <structureId> <filepath>");
|
need(sid && filepath, "usage: bliss upload <structureId> <filepath>");
|
||||||
|
|
|
||||||
9
index.js
9
index.js
|
|
@ -103,10 +103,11 @@ function makeConsole(structureId, routeId) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate a db's "library" script with `sql` and `console` bound, returning
|
// Evaluate a db's "library" script with `sql`, `console`, and `fetch` bound,
|
||||||
// its exports. Each library runs in its own context so nothing leaks between dbs.
|
// returning its exports. Each library runs in its own context so nothing
|
||||||
|
// leaks between dbs.
|
||||||
function runLibrary(sql, librarySource, console) {
|
function runLibrary(sql, librarySource, console) {
|
||||||
const libContext = vm.createContext({ sql, console, module: { exports: null } });
|
const libContext = vm.createContext({ sql, console, fetch, module: { exports: null } });
|
||||||
vm.runInContext(librarySource, libContext);
|
vm.runInContext(librarySource, libContext);
|
||||||
return libContext.module.exports;
|
return libContext.module.exports;
|
||||||
}
|
}
|
||||||
|
|
@ -548,6 +549,7 @@ app.put("/workshop/:structure_id/db/:db_id/library", (req, res) => {
|
||||||
let context = vm.createContext({
|
let context = vm.createContext({
|
||||||
module: { exports: null },
|
module: { exports: null },
|
||||||
sql: dbInstance,
|
sql: dbInstance,
|
||||||
|
fetch,
|
||||||
console: {
|
console: {
|
||||||
log: (...args) => capturedOutput.push(inspectArgs(args)),
|
log: (...args) => capturedOutput.push(inspectArgs(args)),
|
||||||
},
|
},
|
||||||
|
|
@ -569,6 +571,7 @@ app.post("/workshop/:structure_id/db/:db_id/repl", (req, res) => {
|
||||||
let context = vm.createContext({
|
let context = vm.createContext({
|
||||||
module: { exports: null },
|
module: { exports: null },
|
||||||
sql: dbInstance,
|
sql: dbInstance,
|
||||||
|
fetch,
|
||||||
console: {
|
console: {
|
||||||
log: (...args) => capturedOutput.push(inspectArgs(args)),
|
log: (...args) => capturedOutput.push(inspectArgs(args)),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue