I messed around with this a bit more and got below working for me. I changed your approach and set things up so I can “deploy” a new server cell from a notebook without importing anything:
const express = require('express');
const cors = require('cors');
const puppeteer = require("puppeteer");
const host = process.env.HOST || '0.0.0.0';
const port = process.env.PORT || 8080;
herokuChromeOptions = {
args: [
'--incognito',
'--no-sandbox',
'--single-process',
'--no-zygote',
],
};
const app = express();
const x = '([\-0-9@A-Z_a-z]+)';
app.get(`/api/:user${x}/:notebook${x}/:cell${x}?`, cors(), async (req, res) => {
const start = new Date();
const { method, url, params: { user, notebook, cell = 'app' } } = req;
try {
const content = getRunNoteBookScript({ user, notebook, cell });
const browser = await puppeteer.launch(herokuChromeOptions);
const page = await browser.newPage();
await page.addScriptTag({ type: 'module', content });
await page.waitForFunction(`window['${cell}']`, { timeout: 5000 });
const result = await page.evaluate(
(req, cell) => window[cell](req),
{ url, method },
cell);
// return string as html, otherwise as json
switch (typeof result) {
case 'string':
log('html');
res.send(result);
break;
default:
log('json');
res.json(result);
break;
}
} catch (error) {
log('error', error.message);
res.status(500).json({ error: error.message });
}
function log(resultType, resultData) {
const end = new Date();
const duration = ((new Date() - start) / 1000).toPrecision(3);
console.log(`(+${duration}s) ${method} [${resultType}] ${url}\n${resultData || ''}`.trim());
}
});
function getRunNoteBookScript({ user, notebook, cell } = {}) {
return `
import { Runtime } from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js";
import define from "https://api.observablehq.com/${user}/${notebook}.js?v=3";
new Runtime().module(define, name => {
if (name === '${cell}') return {
fulfilled(value) {
window['${cell}'] = value;
},
rejected(error) {
window['${cell}'] = () => { throw error; };
}
};
});`
}
app.listen(port, host);
So then I can make use of this in a new link-shared notebook:
app = async function(req) {
return { message: 'Hello world!', req }
}