Demo for query storing
Change-Id: I947bcac841992c3f6cfd01ab337c265b0d01cb70
diff --git a/node_modules/tiny-lr/lib/server.js b/node_modules/tiny-lr/lib/server.js
new file mode 100644
index 0000000..2e74a55
--- /dev/null
+++ b/node_modules/tiny-lr/lib/server.js
@@ -0,0 +1,322 @@
+import fs from 'fs';
+import http from 'http';
+import https from 'https';
+import events from 'events';
+import {parse} from 'url';
+import Client from './client';
+import config from '../package.json';
+import anybody from 'body/any';
+import qs from 'qs';
+
+const debug = require('debug')('tinylr:server');
+
+const CONTENT_TYPE = 'content-type';
+const FORM_TYPE = 'application/x-www-form-urlencoded';
+
+function buildRootPath (prefix = '/') {
+ let rootUrl = prefix;
+
+ // Add trailing slash
+ if (prefix[prefix.length - 1] !== '/') {
+ rootUrl = `${rootUrl}/`;
+ }
+
+ // Add leading slash
+ if (prefix[0] !== '/') {
+ rootUrl = `/${rootUrl}`;
+ }
+
+ return rootUrl;
+}
+
+class Server extends events.EventEmitter {
+ constructor (options = {}) {
+ super();
+
+ this.options = options;
+
+ options.livereload = options.livereload || require.resolve('livereload-js/dist/livereload.js');
+
+ // todo: change falsy check to allow 0 for random port
+ options.port = parseInt(options.port || 35729, 10);
+
+ if (options.errorListener) {
+ this.errorListener = options.errorListener;
+ }
+
+ this.rootPath = buildRootPath(options.prefix);
+
+ this.clients = {};
+ this.configure(options.app);
+ this.routes(options.app);
+ }
+
+ routes () {
+ if (!this.options.dashboard) {
+ this.on(`GET ${this.rootPath}`, this.index.bind(this));
+ }
+
+ this.on(`GET ${this.rootPath}changed`, this.changed.bind(this));
+ this.on(`POST ${this.rootPath}changed`, this.changed.bind(this));
+ this.on(`POST ${this.rootPath}alert`, this.alert.bind(this));
+ this.on(`GET ${this.rootPath}livereload.js`, this.livereload.bind(this));
+ this.on(`GET ${this.rootPath}kill`, this.close.bind(this));
+ }
+
+ configure (app) {
+ debug('Configuring %s', app ? 'connect / express application' : 'HTTP server');
+
+ let handler = this.options.handler || this.handler;
+
+ if (!app) {
+ if ((this.options.key && this.options.cert) || this.options.pfx) {
+ this.server = https.createServer(this.options, handler.bind(this));
+ } else {
+ this.server = http.createServer(handler.bind(this));
+ }
+
+ this.server.on('upgrade', this.websocketify.bind(this));
+ this.server.on('error', this.error.bind(this));
+ return this;
+ }
+
+ this.app = app;
+ this.app.listen = (port, done) => {
+ done = done || function () {};
+ if (port !== this.options.port) {
+ debug('Warn: LiveReload port is not standard (%d). You are listening on %d', this.options.port, port);
+ debug('You\'ll need to rely on the LiveReload snippet');
+ debug('> http://feedback.livereload.com/knowledgebase/articles/86180-how-do-i-add-the-script-tag-manually-');
+ }
+
+ let srv = this.server = http.createServer(app);
+ srv.on('upgrade', this.websocketify.bind(this));
+ srv.on('error', this.error.bind(this));
+ srv.on('close', this.close.bind(this));
+ return srv.listen(port, done);
+ };
+
+ return this;
+ }
+
+ handler (req, res, next) {
+ let middleware = typeof next === 'function';
+ debug('LiveReload handler %s (middleware: %s)', req.url, middleware ? 'on' : 'off');
+
+ next = next || this.defaultHandler.bind(this, res);
+ req.headers[CONTENT_TYPE] = req.headers[CONTENT_TYPE] || FORM_TYPE;
+ return anybody(req, res, (err, body) => {
+ if (err) return next(err);
+ req.body = body;
+
+ if (!req.query) {
+ req.query = req.url.indexOf('?') !== -1
+ ? qs.parse(parse(req.url).query)
+ : {};
+ }
+
+ return this.handle(req, res, next);
+ });
+ }
+
+ index (req, res) {
+ res.setHeader('Content-Type', 'application/json');
+ res.write(JSON.stringify({
+ tinylr: 'Welcome',
+ version: config.version
+ }));
+
+ res.end();
+ }
+
+ handle (req, res, next) {
+ let url = parse(req.url);
+ debug('Request:', req.method, url.href);
+ let middleware = typeof next === 'function';
+
+ // do the routing
+ let route = req.method + ' ' + url.pathname;
+ let respond = this.emit(route, req, res);
+ if (respond) return;
+
+ if (middleware) return next();
+
+ // Only apply content-type on non middleware setup #70
+ return this.notFound(res);
+ }
+
+ defaultHandler (res, err) {
+ if (!err) return this.notFound(res);
+
+ this.error(err);
+ res.setHeader('Content-Type', 'text/plain');
+ res.statusCode = 500;
+ res.end('Error: ' + err.stack);
+ }
+
+ notFound (res) {
+ res.setHeader('Content-Type', 'application/json');
+ res.writeHead(404);
+ res.write(JSON.stringify({
+ error: 'not_found',
+ reason: 'no such route'
+ }));
+ res.end();
+ }
+
+ websocketify (req, socket, head) {
+ let client = new Client(req, socket, head, this.options);
+ this.clients[client.id] = client;
+
+ // handle socket error to prevent possible app crash, such as ECONNRESET
+ socket.on('error', (e) => {
+ // ignore frequent ECONNRESET error (seems inevitable when refresh)
+ if (e.code === 'ECONNRESET') return;
+ this.error(e);
+ });
+
+ client.once('info', (data) => {
+ debug('Create client %s (url: %s)', data.id, data.url);
+ this.emit('MSG /create', data.id, data.url);
+ });
+
+ client.once('end', () => {
+ debug('Destroy client %s (url: %s)', client.id, client.url);
+ this.emit('MSG /destroy', client.id, client.url);
+ delete this.clients[client.id];
+ });
+ }
+
+ listen (port, host, fn) {
+ port = port || this.options.port;
+
+ // Last used port for error display
+ this.port = port;
+
+ if (typeof host === 'function') {
+ fn = host;
+ host = undefined;
+ }
+
+ this.server.listen(port, host, fn);
+ }
+
+ close (req, res) {
+ Object.keys(this.clients).forEach(function (id) {
+ this.clients[id].close();
+ }, this);
+
+ if (this.server._handle) this.server.close(this.emit.bind(this, 'close'));
+
+ if (res) res.end();
+ }
+
+ error (e) {
+ if (this.errorListener) {
+ this.errorListener(e);
+ return;
+ }
+
+ console.error();
+ if (typeof e === 'undefined') {
+ console.error('... Uhoh. Got error %s ...', e);
+ } else {
+ console.error('... Uhoh. Got error %s ...', e.message);
+ console.error(e.stack);
+
+ if (e.code !== 'EADDRINUSE') return;
+ console.error();
+ console.error('You already have a server listening on %s', this.port);
+ console.error('You should stop it and try again.');
+ console.error();
+ }
+ }
+
+ // Routes
+
+ livereload (req, res) {
+ res.setHeader('Content-Type', 'application/javascript');
+ fs.createReadStream(this.options.livereload).pipe(res);
+ }
+
+ changed (req, res) {
+ let files = this.param('files', req);
+
+ debug('Changed event (Files: %s)', files.join(' '));
+ let clients = this.notifyClients(files);
+
+ if (!res) return;
+
+ res.setHeader('Content-Type', 'application/json');
+ res.write(JSON.stringify({
+ clients: clients,
+ files: files
+ }));
+
+ res.end();
+ }
+
+ alert (req, res) {
+ let message = this.param('message', req);
+
+ debug('Alert event (Message: %s)', message);
+ let clients = this.alertClients(message);
+
+ if (!res) return;
+
+ res.setHeader('Content-Type', 'application/json');
+ res.write(JSON.stringify({
+ clients: clients,
+ message: message
+ }));
+
+ res.end();
+ }
+
+ notifyClients (files) {
+ let clients = Object.keys(this.clients).map(function (id) {
+ let client = this.clients[id];
+ debug('Reloading client %s (url: %s)', client.id, client.url);
+ client.reload(files);
+ return {
+ id: client.id,
+ url: client.url
+ };
+ }, this);
+
+ return clients;
+ };
+
+ alertClients (message) {
+ let clients = Object.keys(this.clients).map(function (id) {
+ let client = this.clients[id];
+ debug('Alert client %s (url: %s)', client.id, client.url);
+ client.alert(message);
+ return {
+ id: client.id,
+ url: client.url
+ };
+ }, this);
+
+ return clients;
+ }
+
+ // Lookup param from body / params / query.
+ param (name, req) {
+ let param;
+ if (req.body && req.body[name]) param = req.body[name];
+ else if (req.params && req.params[name]) param = req.params[name];
+ else if (req.query && req.query[name]) param = req.query[name];
+
+ // normalize files array
+ if (name === 'files') {
+ param = Array.isArray(param) ? param
+ : typeof param === 'string' ? param.split(/[\s,]/)
+ : [];
+ }
+
+ return param;
+ }
+}
+
+export default Server;