Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
315
gaia/apps/verticalhome/test/marionette/server/child.js
Executable file
315
gaia/apps/verticalhome/test/marionette/server/child.js
Executable file
|
|
@ -0,0 +1,315 @@
|
|||
/* global Buffer, process */
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
App server for fixtures. Serves up packaged apps.
|
||||
*/
|
||||
|
||||
|
||||
var debug = require('debug')('appserver:child');
|
||||
var http = require('http');
|
||||
var fs = require('fs');
|
||||
var fsPath = require('path');
|
||||
var mime = require('mime');
|
||||
var archiver = require('archiver');
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
function readdirRecursive(dir) {
|
||||
dir = fsPath.join(dir, '/');
|
||||
var results = [];
|
||||
var files = fs.readdirSync(dir);
|
||||
|
||||
var file;
|
||||
while((file = files.shift())) {
|
||||
file = fsPath.join(dir, file);
|
||||
var stat = fs.statSync(file);
|
||||
if (stat.isDirectory()) {
|
||||
results = results.concat(readdirRecursive(file));
|
||||
} else {
|
||||
results.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function writeJSON(res, object) {
|
||||
var json = JSON.stringify(object);
|
||||
res.writeHead(200, {
|
||||
'Content-Length': Buffer.byteLength(json),
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
res.end(json);
|
||||
}
|
||||
|
||||
function decorateHandlerForJSON(method) {
|
||||
return function(req, res) {
|
||||
// if it is not json just skip it
|
||||
var type = req.headers['content-type'];
|
||||
if (!type || type.indexOf('json') === -1) {
|
||||
return method.call(this, req, res);
|
||||
}
|
||||
|
||||
var buffer = '';
|
||||
req.on('data', function(data) {
|
||||
buffer += data;
|
||||
});
|
||||
|
||||
req.once('end', function() {
|
||||
req.body = JSON.parse(buffer);
|
||||
method.call(this, req, res);
|
||||
}.bind(this));
|
||||
};
|
||||
}
|
||||
|
||||
function defaultSettings() {
|
||||
return {
|
||||
corked: false,
|
||||
fail: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} root directory where content is.@
|
||||
* @param {Object} routes for the app server.
|
||||
* @constructor
|
||||
*/
|
||||
function AppServer(root, routes) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
// ensure we always have the slash suffix
|
||||
if (root[root.length - 1] !== '/') {
|
||||
root += '/';
|
||||
}
|
||||
|
||||
this.root = root;
|
||||
this.routes = routes;
|
||||
this._settings = {};
|
||||
}
|
||||
|
||||
AppServer.prototype = {
|
||||
__proto__: EventEmitter.prototype,
|
||||
|
||||
/**
|
||||
* @return {Object} default settings for a given url.
|
||||
*/
|
||||
settings: function(url) {
|
||||
return (this._settings[url] = this._settings[url] || defaultSettings());
|
||||
},
|
||||
|
||||
requestHandler: function() {
|
||||
var routes = this.routes;
|
||||
return function(req, res) {
|
||||
debug('request', req.method, req.url);
|
||||
var handler = routes[req.url] || routes['*'];
|
||||
var settings = this.settings(req.url);
|
||||
|
||||
// If this url has a http error on it just return without calling the
|
||||
// handler.
|
||||
if (settings.response) {
|
||||
res.writeHead(settings.response.status, settings.response.headers);
|
||||
res.end(settings.response.body);
|
||||
return;
|
||||
}
|
||||
|
||||
// routes are always invoked with the context of the app server.
|
||||
return handler.call(this, req, res);
|
||||
}.bind(this);
|
||||
},
|
||||
};
|
||||
|
||||
var routes = {
|
||||
|
||||
/**
|
||||
Change the root serving directory for this server.
|
||||
*/
|
||||
'/settings/set_root': decorateHandlerForJSON(function(req, res) {
|
||||
this.root = req.body;
|
||||
writeJSON(res, req.body);
|
||||
}),
|
||||
|
||||
/**
|
||||
Override or set the response of a particular url.
|
||||
*/
|
||||
'/settings/set_response': decorateHandlerForJSON(function(req, res) {
|
||||
var options = req.body;
|
||||
var settings = this.settings(options.path);
|
||||
|
||||
settings.response = options;
|
||||
writeJSON(res, options.path);
|
||||
}),
|
||||
|
||||
/**
|
||||
Clear the response override.
|
||||
*/
|
||||
'/settings/clear_response': decorateHandlerForJSON(function(req, res) {
|
||||
var url = req.body;
|
||||
var settings = this.settings(url);
|
||||
delete settings.response;
|
||||
writeJSON(res, url);
|
||||
}),
|
||||
|
||||
/**
|
||||
A 'corked' request will send headers but no body until 'uncork' is used.
|
||||
*/
|
||||
'/settings/cork': decorateHandlerForJSON(function(req, res) {
|
||||
var url = req.body;
|
||||
var settings = this.settings(url);
|
||||
settings.corked = true;
|
||||
|
||||
// issue a cork to the particular endpoint
|
||||
var event = 'corked ' + url;
|
||||
debug('cork event', event);
|
||||
this.emit('corked ' + url);
|
||||
writeJSON(res, url);
|
||||
}),
|
||||
|
||||
/**
|
||||
The inverse of 'cork' will uncork a uri and send the body through.
|
||||
*/
|
||||
'/settings/uncork': decorateHandlerForJSON(function(req, res) {
|
||||
var url = req.body;
|
||||
var settings = this.settings(url);
|
||||
settings.corked = false;
|
||||
|
||||
var event = 'uncorked ' + url;
|
||||
debug('uncork event', event);
|
||||
this.emit(event);
|
||||
writeJSON(res, url);
|
||||
}),
|
||||
|
||||
/**
|
||||
Fail is a combination of 'cork' and a flag which will cause the incoming http
|
||||
request to be closed after a short delay.
|
||||
*/
|
||||
'/settings/fail': decorateHandlerForJSON(function(req, res) {
|
||||
var url = req.body;
|
||||
var settings = this.settings(url);
|
||||
|
||||
// must be corked to ensure we don't send the body
|
||||
settings.corked = true;
|
||||
settings.fail = true;
|
||||
writeJSON(res, url);
|
||||
}),
|
||||
|
||||
/**
|
||||
The inverse to fail but unlike uncork this cannot be called in the middle of
|
||||
a request (for obvious reasons).
|
||||
*/
|
||||
'/settings/unfail': decorateHandlerForJSON(function(req, res) {
|
||||
var url = req.body;
|
||||
var settings = this.settings(url);
|
||||
|
||||
settings.fail = settings.corked = false;
|
||||
writeJSON(res, url);
|
||||
}),
|
||||
|
||||
'/package.manifest': function(req, res) {
|
||||
var json = JSON.parse(fs.readFileSync(this.root + '/manifest.webapp'));
|
||||
json.package_path = 'http://' + req.headers.host + '/app.zip';
|
||||
// TODO: Remove this once we add the ability to override requests
|
||||
delete json.role;
|
||||
|
||||
var body = JSON.stringify(json, null, 2);
|
||||
res.writeHead(200, {
|
||||
'Content-Length': Buffer.byteLength(body),
|
||||
'Content-Type': 'application/x-web-app-manifest+json'
|
||||
});
|
||||
res.end(body);
|
||||
},
|
||||
|
||||
'/app.zip': function(req, res) {
|
||||
var url = req.url;
|
||||
var settings = this.settings(url);
|
||||
// always write the head we need it in all cases
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/zip'
|
||||
});
|
||||
|
||||
var root = this.root;
|
||||
function writeZip() {
|
||||
// read the entire app directory
|
||||
var files = readdirRecursive(root);
|
||||
var zip = archiver.create('zip');
|
||||
|
||||
files.forEach(function(file) {
|
||||
debug('packaged app zip file', file);
|
||||
var zipPath = file.replace(root, '');
|
||||
zip.append(fs.createReadStream(file), { name: zipPath });
|
||||
});
|
||||
zip.finalize();
|
||||
zip.pipe(res);
|
||||
}
|
||||
|
||||
if (settings.fail) {
|
||||
process.nextTick(function() {
|
||||
req.socket.destroy();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// if we are not corked just write the zip and we are done
|
||||
if (!settings.corked) {
|
||||
return writeZip();
|
||||
} else {
|
||||
this.once('uncorked ' + url, writeZip);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
Catch all handler which serves up static assets.
|
||||
*/
|
||||
'*': function(req, res) {
|
||||
var url = req.url;
|
||||
var settings = this.settings(url);
|
||||
var filePath = fsPath.join(this.root, url);
|
||||
|
||||
// XXX: This is not a production quality server don't use sync methods in
|
||||
// in servers in your user facing node code.
|
||||
if (!fs.existsSync(filePath)) {
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
var contentType = mime.lookup(filePath);
|
||||
var contentLength = fs.statSync(filePath).size;
|
||||
debug('static asset', filePath, contentType, contentLength);
|
||||
|
||||
// write out the head
|
||||
res.writeHead(200, {
|
||||
'Content-Type': contentType,
|
||||
'Content-Length': contentLength
|
||||
});
|
||||
|
||||
function writeContent() {
|
||||
fs.createReadStream(filePath).pipe(res);
|
||||
}
|
||||
|
||||
if (settings.fail) {
|
||||
process.nextTick(function() {
|
||||
req.socket.destroy();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!settings.corked) {
|
||||
// if we are not corked just return the stream
|
||||
return writeContent();
|
||||
} else {
|
||||
// otherwise wait for the uncork
|
||||
this.once('uncorked ' + url, writeContent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var port = parseInt((process.argv[3] || 0), 10);
|
||||
var appServer = new AppServer(process.argv[2], routes);
|
||||
var server = http.createServer(appServer.requestHandler());
|
||||
|
||||
server.listen(port, function() {
|
||||
if (process.send) {
|
||||
process.send({ type: 'started', port: server.address().port });
|
||||
}
|
||||
});
|
||||
74
gaia/apps/verticalhome/test/marionette/server/cli.js
Executable file
74
gaia/apps/verticalhome/test/marionette/server/cli.js
Executable file
|
|
@ -0,0 +1,74 @@
|
|||
/* global process, __dirname */
|
||||
'use strict';
|
||||
|
||||
// server cli stuff
|
||||
var fork = require('child_process').fork;
|
||||
var fs = require('fs');
|
||||
var fsPath = require('path');
|
||||
|
||||
function exit() {
|
||||
console.error();
|
||||
console.error.apply(console, arguments);
|
||||
commandHelp(console.error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function commandHelp(log) {
|
||||
log = log || console.log;
|
||||
|
||||
log();
|
||||
log(' App server cli');
|
||||
log();
|
||||
log(' Comamnds: ');
|
||||
log();
|
||||
log(' help - Your looking at it');
|
||||
log(' interactive <directory> [port] - ' +
|
||||
'Begin an interactive session with the app server');
|
||||
log();
|
||||
}
|
||||
|
||||
function commandInteractive(directory, port) {
|
||||
port = parseInt((port || 60234), 10);
|
||||
|
||||
if (!directory) {
|
||||
return exit('Directory must be given');
|
||||
}
|
||||
|
||||
directory = fsPath.resolve(directory);
|
||||
|
||||
if (!fs.existsSync(directory)) {
|
||||
return exit('Given app directory "%s" does not exist', directory);
|
||||
}
|
||||
|
||||
// start the server up
|
||||
console.log('Starting server...');
|
||||
|
||||
function onError(e) {
|
||||
exit('server process failed to start: ', e);
|
||||
}
|
||||
|
||||
var proc = fork(__dirname + '/child.js', [directory, port]);
|
||||
proc.once('error', onError);
|
||||
proc.on('message', function(msg) {
|
||||
if (msg.type !== 'started') {
|
||||
return;
|
||||
}
|
||||
proc.removeListener('error', onError);
|
||||
|
||||
var interactive = require('./cli/interactive');
|
||||
interactive(proc, 'http://localhost:' + msg.port + '/');
|
||||
});
|
||||
}
|
||||
|
||||
switch (process.argv[2]) {
|
||||
case 'interactive':
|
||||
commandInteractive(process.argv[3]);
|
||||
break;
|
||||
|
||||
case 'help':
|
||||
commandHelp();
|
||||
break;
|
||||
|
||||
default:
|
||||
commandHelp();
|
||||
}
|
||||
175
gaia/apps/verticalhome/test/marionette/server/cli/interactive.js
Executable file
175
gaia/apps/verticalhome/test/marionette/server/cli/interactive.js
Executable file
|
|
@ -0,0 +1,175 @@
|
|||
/* global process, Buffer, module */
|
||||
'use strict';
|
||||
var http = require('http');
|
||||
var url = require('url');
|
||||
|
||||
function Interactive(serverProc, url, commands) {
|
||||
this.server = serverProc;
|
||||
this.url = url;
|
||||
this.stream = process.stdin;
|
||||
this.commands = commands;
|
||||
}
|
||||
|
||||
Interactive.prototype = {
|
||||
|
||||
help: function() {
|
||||
console.log(' Commands: ');
|
||||
console.log();
|
||||
Object.keys(this.commands).forEach(function(command) {
|
||||
var desc = this.commands[command].description;
|
||||
console.log(' %s - %s', command, desc);
|
||||
}, this);
|
||||
console.log();
|
||||
},
|
||||
|
||||
get: function(uri) {
|
||||
var req = http.get(this.url + uri);
|
||||
console.log('>> GET ' + uri);
|
||||
|
||||
req.once('response', function(res) {
|
||||
console.log(' response %s [%s]', uri, res.statusCode);
|
||||
for (var key in res.headers) {
|
||||
console.log(' response header %s : %s', key, res.headers[key]);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
post: function(uri, body, callback) {
|
||||
var json = JSON.stringify(body);
|
||||
|
||||
var options = url.parse(this.url + uri);
|
||||
options.method = 'POST';
|
||||
var req = http.request(options);
|
||||
req.setHeader('Content-Length', Buffer.byteLength(json));
|
||||
req.setHeader('Content-Type', 'application/json');
|
||||
|
||||
req.once('response', function(res) {
|
||||
var data = '';
|
||||
res.on('data', function(buffer) {
|
||||
data += buffer;
|
||||
});
|
||||
|
||||
res.once('end', function() {
|
||||
callback(null, JSON.parse(data));
|
||||
});
|
||||
});
|
||||
|
||||
req.end(json);
|
||||
},
|
||||
|
||||
onReadable: function() {
|
||||
var stream = this.stream;
|
||||
|
||||
var content = stream.read();
|
||||
// noting to read return...
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
|
||||
// read until we have a newline
|
||||
if (content.indexOf('\n') === -1) {
|
||||
stream.unshift(content);
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore blank lines
|
||||
if (content.trim() === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
// the first word is the command parse it out...
|
||||
var commandParts = content.split(' ').map(function(part) {
|
||||
return part.trim();
|
||||
});
|
||||
|
||||
var commandName = commandParts.shift();
|
||||
var command = this.commands[commandName];
|
||||
|
||||
if (!command) {
|
||||
console.error(commandName, ' is not a recognized command');
|
||||
return this.help();
|
||||
}
|
||||
|
||||
command.handler.apply(this, commandParts);
|
||||
},
|
||||
|
||||
start: function() {
|
||||
// don't stop reading from stdin
|
||||
this.stream.setEncoding('utf8');
|
||||
this.stream.on('readable', this.onReadable.bind(this));
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
var commands = {
|
||||
help: {
|
||||
description: 'Shows all commands',
|
||||
handler: function() { this.help(); }
|
||||
},
|
||||
|
||||
cork: {
|
||||
description: '<uri> Cork a particular url',
|
||||
handler: function(uri) {
|
||||
// leading slash for consistency with get
|
||||
this.post('settings/cork', '/' + uri, function(err, body) {
|
||||
console.log('corked : %s', body);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
uncork: {
|
||||
description: '<uri> uncork a particular uri',
|
||||
handler: function(uri) {
|
||||
// leading slash for consistency with get
|
||||
this.post('settings/uncork', '/' + uri, function(err, body) {
|
||||
console.log('uncorked : %s', body);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
fail: {
|
||||
description: '<uri> Cause a particular url to fail',
|
||||
handler: function(uri) {
|
||||
// leading slash for consistency with get
|
||||
this.post('settings/fail', '/' + uri, function(err, body) {
|
||||
console.log('failed: %s', body);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
unfail: {
|
||||
description: '<uri> remove failng status from particular url',
|
||||
handler: function(uri) {
|
||||
// leading slash for consistency with get
|
||||
this.post('settings/unfail', '/' + uri, function(err, body) {
|
||||
console.log('unfail: %s', body);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
get: {
|
||||
description: '<uri> Issue a http get an show the body',
|
||||
handler: function(uri) {
|
||||
this.get(uri);
|
||||
}
|
||||
},
|
||||
|
||||
url: {
|
||||
description: 'display the server url',
|
||||
handler: function() {
|
||||
console.log(this.url);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
function main(process, url) {
|
||||
var handler = new Interactive(process, url, commands);
|
||||
handler.start();
|
||||
|
||||
process.once('SIGINT', function() {
|
||||
process.stderr.write('Shutting down...\n');
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = main;
|
||||
187
gaia/apps/verticalhome/test/marionette/server/parent.js
Executable file
187
gaia/apps/verticalhome/test/marionette/server/parent.js
Executable file
|
|
@ -0,0 +1,187 @@
|
|||
'use strict';
|
||||
/* global module, __dirname */
|
||||
var fork = require('child_process').fork;
|
||||
|
||||
/**
|
||||
* Issue a POST request via marionette.
|
||||
*/
|
||||
function post(client, url, json) {
|
||||
// must run in chrome so we can do cross domain xhr
|
||||
client = client.scope({ context: 'chrome' });
|
||||
return client.executeAsyncScript(function(url, json) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', url, true);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.onload = function() {
|
||||
marionetteScriptFinished(xhr.response);
|
||||
};
|
||||
xhr.send(json);
|
||||
}, [url, JSON.stringify(json)]);
|
||||
}
|
||||
|
||||
function AppServer(root, marionette, port, proc) {
|
||||
this.root = root;
|
||||
this.marionette = marionette;
|
||||
this.url = 'http://localhost:' + port;
|
||||
this.process = proc;
|
||||
}
|
||||
|
||||
AppServer.prototype = {
|
||||
|
||||
/**
|
||||
issues a GET request to the server and returns the parsed body.
|
||||
*/
|
||||
get: function(uri) {
|
||||
// must run in chrome so we can do cross domain xhr
|
||||
var client = this.marionette.scope({ context: 'chrome' });
|
||||
|
||||
return client.executeAsyncScript(function(url) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.onload = function() {
|
||||
marionetteScriptFinished(xhr.response);
|
||||
};
|
||||
xhr.send();
|
||||
}, [this.url + uri]);
|
||||
},
|
||||
|
||||
/**
|
||||
Changes the root of the server.
|
||||
|
||||
@param {String} path relative to the file system.
|
||||
*/
|
||||
setRoot: function(path) {
|
||||
return post(this.marionette, this.url + '/settings/set_root', path);
|
||||
},
|
||||
|
||||
/**
|
||||
Set the http response of a particular url.
|
||||
|
||||
@param {String} path to set the response of.
|
||||
@param {Number} status code to set.
|
||||
@param {Object} headers to override.
|
||||
@param {String} body of the request.
|
||||
*/
|
||||
setResponse: function(path, status, headers, body) {
|
||||
var options = {
|
||||
path: path,
|
||||
status: status,
|
||||
headers: headers || {},
|
||||
body: body
|
||||
};
|
||||
|
||||
return post(this.marionette, this.url + '/settings/set_response', options);
|
||||
},
|
||||
|
||||
/**
|
||||
Clear the response override set by `setResponse`
|
||||
*/
|
||||
clearResponse: function(url) {
|
||||
return post(
|
||||
this.marionette, this.url + '/settings/clear_response', url
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
Trigger a 500 error for a particular url and return an empty body.
|
||||
|
||||
@param {String} url to fail (/index.html).
|
||||
*/
|
||||
serverError: function(url) {
|
||||
return this.setResponse(url, 500, {}, '');
|
||||
},
|
||||
|
||||
/**
|
||||
Clears the server error for a particular url.
|
||||
|
||||
@param {String} url to fail (/index.html).
|
||||
*/
|
||||
clearServerError: function(url) {
|
||||
return this.clearResponse();
|
||||
},
|
||||
|
||||
/**
|
||||
Indicate to the server that all requests to the given url should be
|
||||
given a response with headers but then the socket should be closed shortly
|
||||
after that time.
|
||||
|
||||
@param {String} url to ban (/index.html).
|
||||
*/
|
||||
fail: function(url) {
|
||||
return post(this.marionette, this.url + '/settings/fail', url);
|
||||
},
|
||||
|
||||
/**
|
||||
Allow requests to the given url to proceed without failure after calling
|
||||
`.fail`.
|
||||
|
||||
@param {String} url to unban.
|
||||
*/
|
||||
unfail: function(url) {
|
||||
return post(this.marionette, this.url + '/settings/unfail', url);
|
||||
},
|
||||
|
||||
/**
|
||||
Cork the response body of the given url while allowing headers.
|
||||
|
||||
@param {String} url to cork.
|
||||
*/
|
||||
cork: function(url) {
|
||||
return post(this.marionette, this.url + '/settings/cork', url);
|
||||
},
|
||||
|
||||
/**
|
||||
Allow the body to be sent after calling `.cork`.
|
||||
|
||||
@param {String} url to uncork.
|
||||
*/
|
||||
uncork: function(url) {
|
||||
return post(this.marionette, this.url + '/settings/uncork', url);
|
||||
},
|
||||
|
||||
close: function(callback) {
|
||||
this.process.kill();
|
||||
this.process.once('exit', callback.bind(this, null));
|
||||
},
|
||||
|
||||
get manifest() {
|
||||
return JSON.parse(this.get('/manifest.webapp'));
|
||||
},
|
||||
|
||||
/**
|
||||
URI where the application zip lives this defined in child.js
|
||||
*/
|
||||
get applicationZipUri() {
|
||||
return '/app.zip';
|
||||
},
|
||||
|
||||
get manifestURL() {
|
||||
return this.url + '/manifest.webapp';
|
||||
},
|
||||
|
||||
get packageManifestURL() {
|
||||
return this.url + '/package.manifest';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a app server for use in marionette tests.
|
||||
*
|
||||
* @param {String} appRoot path to the root of the app.
|
||||
* @param {Marionette.Client} client for marionette.
|
||||
* @param {Function} callback [Error]
|
||||
*/
|
||||
module.exports = function create(appRoot, client, callback) {
|
||||
var proc = fork(__dirname + '/child.js', [appRoot]);
|
||||
|
||||
proc.once('error', callback);
|
||||
proc.on('message', function(msg) {
|
||||
if (msg.type !== 'started') {
|
||||
return;
|
||||
}
|
||||
proc.removeListener('error', callback);
|
||||
callback(null, new AppServer(appRoot, client, msg.port, proc));
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.AppServer;
|
||||
Loading…
Reference in a new issue