// KaTeX rendering server // Listens on unix socket, path is provided as first argument // Expects JSON lines, each line is a query with the following schema: // { // formulas: [ // { // tex: string, // options?: object // } // ], // options?: object // } // see https://katex.org/docs/options.html for list of available options // If options formulas[].options field is used, the global options field is ignored. // For each line, returns one JSON line with the following schema: // { // results: [ // { html?: string } | { error?: string } // ] // } | { error?: string } // If one formula is invalid, the error in results is used // If the entire query is invalid (couldn't parse JSON, for example), the outer error field is used import katex from 'katex' import net from 'net' import * as readline from 'readline' const myArgs = process.argv.slice(2) const unixSocketPath = myArgs[0] if (!unixSocketPath) { console.error('you must specify socket path') process.exit(1) } // This server listens on a Unix socket at /var/run/mysocket var unixServer = net.createServer(handleClient); unixServer.listen(unixSocketPath); function handleExit(signal) { // unixServer.emit('close') unixServer.close(function () { }); process.exit(0); // put this into the callback to avoid closing open connections } process.on('SIGINT', handleExit); process.on('SIGQUIT', handleExit); process.on('SIGTERM', handleExit); process.on('exit', handleExit); const defaultOptions = {} /** * @param {net.Socket} socket * @returns {Promise} * */ function socketWrite(socket, data) { return new Promise((resolve, reject) => { socket.write(data, (err) => { if (err) { reject(err) } else { resolve() } }) }) } /** * @param {net.Socket} client * */ async function handleClient(client) { const rl = readline.createInterface({ input: client }) for await (const line of rl) { try { const query = JSON.parse(line) const results = [] for (const input of query.formulas) { const options = input.options ?? query.options ?? defaultOptions try { const html = katex.renderToString(input.tex, options) results.push({ html }) } catch (e) { results.push({ error: String(e) }) } } await socketWrite(client, JSON.stringify({ results }, null, query.debug ? ' ' : undefined)) await socketWrite(client, '\n') } catch (e) { console.error(e) await socketWrite(client, JSON.stringify({ error: String(e) })) await socketWrite(client, '\n') } } }