SequenceDiagram/bin/handlers/render.js

79 lines
2.0 KiB
JavaScript
Executable File

const {HttpError} = require('../server/HttpError');
const {RequestHandler} = require('../server/RequestHandler');
const {VirtualSequenceDiagram} = require('../../lib/sequence-diagram');
function beginTimer() {
return process.hrtime();
}
function endTimer(timer) {
const delay = process.hrtime(timer);
return delay[0] * 1e9 + delay[1];
}
const NUM_MATCH = '[0-9]+(?:\\.[0-9]+)?';
const MATCH_RENDER = new RegExp(
'^/render/' +
`(?:(?:w(${NUM_MATCH}))?(?:h(${NUM_MATCH}))?/|z(${NUM_MATCH})/)?` +
'(?:(uri|b64)/)?' +
'(.*?)' +
'(?:\\.(svg))?$',
'i'
);
function getNumeric(v, name) {
if(!v) {
return null;
}
const n = Number.parseFloat(v);
if(Number.isNaN(n)) {
throw new HttpError(400, 'Invalid value for ' + name);
}
return n;
}
function readEncoded(str, encoding) {
switch(encoding) {
case 'b64':
return Buffer
.from(decodeURIComponent(str), 'base64')
.toString('utf8');
case 'uri':
return str.split('/').map(decodeURIComponent).join('\n');
default:
throw new HttpError(400, 'Unknown encoding');
}
}
function handleRender(req, res, {match, pickEncoding, log, writeEncoded}) {
res.setHeader('Access-Control-Allow-Origin', '*');
const encoding = pickEncoding();
const size = {
height: getNumeric(match[2], 'height'),
width: getNumeric(match[1], 'width'),
zoom: getNumeric(match[3], 'zoom'),
};
const code = readEncoded(match[5], (match[4] || 'uri').toLowerCase());
const format = (match[6] || 'svg').toLowerCase();
const timer = beginTimer();
const svg = VirtualSequenceDiagram.render(code, {size});
const delay = endTimer(timer);
log('RENDER (' + (delay / 1e6).toFixed(3) + 'ms)');
switch(format) {
case 'svg':
res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
writeEncoded(encoding, svg);
break;
default:
throw new HttpError(400, 'Unsupported image format');
}
}
const render = new RequestHandler('GET', MATCH_RENDER, handleRender);
render.info = 'Rendering sequence diagrams at /render/';
module.exports = {render};