Merge remote-tracking branch 'origin/master' into merge-account-profile

Conflicts:
	plugins/c9.account.react/components/Footer.js
	plugins/c9.account.react/components/Footer.jsx
	plugins/c9.account.react/style-server.cli.js
	plugins/c9.account.react/style-server.js
	plugins/c9.account.react/styles/theme.less
pull/85/head
Matthijs van Henten 2015-04-16 07:42:02 +00:00
commit 0acad29850
10 zmienionych plików z 597 dodań i 313 usunięć

1
.gitignore vendored
Wyświetl plik

@ -13,6 +13,7 @@ plugins/c9.ide.language.javascript.tern/util/sigs_ts
plugins/c9.account.billing/node_modules/
plugins/c9.account/node_modules/
plugins/c9.api.project/coverage/
plugins/c9.proxy.apps/coverage/
plugins/c9.db.redis/coverage/
scripts/build
build/output

Wyświetl plik

@ -10,83 +10,69 @@ var http = require("http");
var frontdoor = require("../frontdoor");
var createClient = require("./api-client");
module.exports = {
"test client/server integration": function(next) {
this.getUsers = function(params, callback) {
console.log(params)
callback(null, [{
id: 1,
name: "fjakobs",
first: params.first
}]);
};
this.addUser = sinon.stub();
this.getUser = function(params, callback) {
console.log(params)
callback(null, {
id: params.uid,
name: "fjakobs"
});
};
require("c9/inline-mocha")(module);
require("amd-loader");
var api = frontdoor();
api.section("users")
.get("/", {
name: "getAll",
params: {
first: {
type: "int",
source: "query",
optional: true
}
it("test client/server integration", function(next){
this.getUsers = function(params, callback) {
callback(null, [{
id: 1,
name: "fjakobs",
first: params.first
}]);
};
this.addUser = sinon.stub();
this.getUser = function(params, callback) {
callback(null, {
id: params.uid,
name: "fjakobs"
});
};
var api = frontdoor();
api.section("users")
.get("/", {
name: "getAll",
params: {
first: {
type: "int",
source: "query",
optional: true
}
}, this.getUsers)
.get("/:uid", this.getUser)
.post("/:uid", {
params: {
name: {
source: "body"
}
}
}, this.getUsers)
.get("/:uid", this.getUser)
.post("/:uid", {
params: {
name: {
source: "body"
}
}, this.addUser);
}
}, this.addUser);
api.get("/describe.json", {
name: "describe"
}, frontdoor.middleware.describeApi(api));
var port = process.env.PORT || 8383;
this.server = http.createServer(api.handle).listen(port, function() {
createClient("http://localhost:" + port + "/describe.json", function(err, client) {
assert.equal(err, null);
api.get("/describe.json", {
name: "describe"
}, frontdoor.middleware.describeApi(api));
var port = process.env.PORT || 8383;
this.server = http.createServer(api.handle).listen(port, function() {
createClient("http://localhost:" + port + "/describe.json", function(err, client) {
client.users.get({ uid: 123}, function(err, user) {
assert.equal(user.id, 123);
assert.equal(err, null);
client.users.get({ uid: 123}, function(err, user) {
assert.equal(user.id, 123);
client.users.getAll({ first: 100}, function(err, users) {
assert.equal(err, null);
assert.equal(users[0].first, 100);
client.users.getAll({ first: 100}, function(err, users) {
assert.equal(err, null);
assert.equal(users[0].first, 100);
next();
});
next();
});
});
});
},
// ">test real API server": function(next) {
// createClient("http://127.0.0.1:8081/api.json", function(err, client) {
// assert.equal(err, null);
// console.log(err, Object.keys(client), client.vfs);
// client.vfs.get(function(err, res) {
// console.log(err, res);
// assert.equal(err, null);
// next();
// })
// });
// }
};
});
});
!module.parent && require("asyncjs").test.testcase(module.exports).exec();

54
node_modules/frontdoor/lib/api_test.js wygenerowano vendored
Wyświetl plik

@ -2,7 +2,10 @@
"use server";
var assert = require("assert");
require("amd-loader");
var assert = require("assert-diff");
var sinon = require("sinon");
var Api = require("./api");
@ -284,14 +287,18 @@ module.exports = {
root.handle({
method: "PUT",
url: "/post/fab?age=34"
}, this.res, assert.fail);
sinon.assert.calledWith(this.res.writeHead, 422);
var errors = JSON.parse(this.res.end.args[0][0]).errors;
assert.equal(errors.length, 1);
assert.equal(errors[0].resource, "root");
assert.equal(errors[0].field, "name");
assert.equal(errors[0].code, "missing_field");
}, this.res, function(err){
assert.ok( err );
var errors = err.errors;
assert.equal(errors.length, 1);
assert.equal(errors[0].resource, "root");
assert.equal(errors[0].field, "name");
assert.equal(errors[0].code, "missing_field");
});
this.res.writeHead.reset();
this.res.end.reset();
@ -299,14 +306,19 @@ module.exports = {
method: "PUT",
url: "/post/fab?age=juhu",
body: { name: "Fabian"}
}, this.res, assert.fail);
sinon.assert.calledWith(this.res.writeHead, 422);
var errors = JSON.parse(this.res.end.args[0][0]).errors;
assert.equal(errors.length, 1);
assert.equal(errors[0].resource, "root");
assert.equal(errors[0].field, "age");
assert.equal(errors[0].type_expected, "int");
assert.equal(errors[0].code, "invalid");
}, this.res, function(err){
assert.ok( err );
var errors = err.errors;
assert.equal(errors.length, 1);
assert.equal(errors[0].resource, "root");
assert.equal(errors[0].field, "age");
assert.equal(errors[0].type_expected, "int");
assert.equal(errors[0].code, "invalid");
});
},
"test custom type with register": function() {
@ -393,16 +405,19 @@ module.exports = {
"method": "put",
"params": {
"id": {
"name": "id",
"type": "/\\d{4}/",
"source": "url",
"optional": false
},
"name": {
"name": "name",
"type": "string",
"source": "body",
"optional": false
},
"age": {
"name": "age",
"type": "int",
"source": "body",
"optional": true
@ -414,6 +429,7 @@ module.exports = {
"method": "delete",
"params": {
"id": {
"name": "id",
"type": "string",
"source": "url",
"optional": false
@ -424,8 +440,8 @@ module.exports = {
}
]
};
assert.equal(JSON.stringify(description), JSON.stringify(expected));
assert.equal(JSON.stringify(description), JSON.stringify(expected));
},
"test handler with two arguments should be treated as function(params, callback) {}": function() {
@ -457,8 +473,6 @@ module.exports = {
assert(called);
}
// test checks can be async
// fromString, fromJson
};
!module.parent && require("asyncjs").test.testcase(module.exports).exec();

93
node_modules/frontdoor/lib/params.js wygenerowano vendored 100644
Wyświetl plik

@ -0,0 +1,93 @@
define(function(require, exports, module) {
"use strict";
/**
* Handles parameter definitions. A full parameter definition is a name => value
* object, where each value is an object with a type, source, optional and name parameter.
*/
var Types = require("./types").Types;
var RegExpType = require("./types").RegExp;
var BuiltinTypes = new Types();
var Params = {
/**
* Batch normalize params, and creates Type Objects for each param.
*/
normalize: function(params, types, source) {
if ( ! params ) return {};
types = types || BuiltinTypes;
for (var name in params) {
var param = Params.param(params[name], name, source);
param.type = types.get(param.type);
params[name] = param;
}
return params;
},
/**
* Create/normalize a parameter definition from one of the following
* api's:
*
* 1. A URL parameter defined as part of a path definition:
* - This is a single string, defaulting to a required, *string* type url param
*
* 2. A url parameter following the { key: value } convention
* - The key is the name of the path/body/query part, value is a type
* - Values that are strings must be one of the builtin types
* - Values may be a RegExp, that will be converted to RegExpType
*
* 3. Fully defined param spec with valid values for type and source.
*
* @param String|Object def A param object or type string, or name.
* @param String name (Optional) param name.
* When omitted, the first argument will be the name
* @param String source (Optional) param source. Must be url, body or query
*
* @return Object param Full param object
*/
param: function(def, name, source) {
var param = def;
// Singe edge case for implicit param generation from the url pathparts,
// where the pathpart is not defined in params definition.
if (typeof def === 'string' && !name) {
return {
source: 'url',
optional: false,
type: BuiltinTypes.get('string'),
};
}
if (typeof def === 'string' || def instanceof RegExp) {
param = {
name: name,
type: def
};
}
param.optional = !!param.optional;
param.source = param.source || source || "body";
param.type = param.type || "string";
// allow regular expressions as types
if (param.type instanceof RegExp)
param.type = new RegExpType(param.type);
if ( !/^body|url|query$/.test(param.source)) {
throw new Error("parameter source muste be 'url', 'query' or 'body'");
}
return param;
}
};
module.exports = Params;
});

104
node_modules/frontdoor/lib/route.js wygenerowano vendored
Wyświetl plik

@ -1,12 +1,25 @@
define(function(require, exports, module) {
"use strict";
var RegExpType = require("./types").RegExp;
var Types = require("./types").Types;
var Params = require("./params");
var flatten = require("./utils").flatten;
var error = require("http-error");
module.exports = function Route(route, options, handler, types) {
function prepareParams( options, types, parent ){
var params = Params.normalize(options.params, types );
if ( parent ){
for ( var key in parent.params ){
if ( params[key] ) continue;
params[key] = parent.params[key];
}
}
return params;
}
module.exports = function Route(route, options, handler, types, parent ) {
// options is optional
if (typeof options == "function" || Array.isArray(options)) {
@ -14,9 +27,9 @@ module.exports = function Route(route, options, handler, types) {
handler = options;
options = {};
}
options.route = route;
types = types || new Types();
this.middlewares = flatten(handler);
@ -28,14 +41,13 @@ module.exports = function Route(route, options, handler, types) {
});
this.middlewares.unshift(decodeParams);
this.method = (options.method || "GET").toLowerCase();
var self = this;
var keys = [];
var params = options.params || {};
var params = prepareParams( options, types, parent );
var routeRe = normalizePath(options.route, keys, params);
params = normalizeParams(params);
function wrapHandler(handler) {
return function(req, res, next) {
@ -53,30 +65,23 @@ module.exports = function Route(route, options, handler, types) {
* the default values for url parameters.
*/
function normalizePath(path, keys, params) {
for (var name in params) {
var param = params[name];
if (typeof param == "string" || param instanceof RegExp)
params[name] = { type: param};
}
path = path
.concat("/?")
.replace(/\/:([\w\.\-\_]+)(\*?)/g, function(match, key, wildcard) {
keys.push(key);
if (!params[key]) {
params[key] = {};
}
// url params default to type string and optional=false
// Implicit params: part of path definition but not defined as
// { params }, created here on the fly.
if (!params[key])
params[key] = Params.param( key );
var param = params[key];
param.type = param.type || "string";
param.optional = false;
if (!param.source)
param.source = "url";
// override the default source for params
// that are created by parsing the url
param.source = 'url';
if (param.source !== "url")
throw new Error("Url parameters must have 'url' as source but found '" + param.source + "'");
if (wildcard)
return "(/*)";
else
@ -84,44 +89,11 @@ module.exports = function Route(route, options, handler, types) {
})
.replace(/([\/.])/g, '\\$1')
.replace(/\*/g, '(.*)');
return new RegExp('^' + path + '$');
}
function normalizeParams(params) {
for (var name in params) {
var param = params[name];
if (param.source == "query") {
// query params default to string
param.type = param.type || "string";
}
else if (!param.source || param.source == "body") {
// body params default to json
param.type = param.type || "json";
param.source = "body";
}
else if (param.source === "url") {
param.type = param.type || "string";
}
else {
throw new Error("parameter source muste be 'url', 'query' or 'body'");
}
// optional defaults to false
param.optional = !!param.optional;
// allow regular expressions as types
if (param.type instanceof RegExp)
param.type = new RegExpType(param.type);
// convert all types to type objects
param.type = types.get(param.type);
}
return params;
}
/**
* Check if the given path matched the route regular expression. If
* the regular expression doesn't match or parsing fails `match` will
@ -129,8 +101,9 @@ module.exports = function Route(route, options, handler, types) {
**/
this.match = function(req, path) {
var m = path.match(routeRe);
if (!m) return false;
var match = {};
for (var i = 0; i < keys.length; i++) {
var value = m[i+1];
@ -147,6 +120,7 @@ module.exports = function Route(route, options, handler, types) {
}
match[key] = value;
}
req.match = match;
return true;
};
@ -159,6 +133,7 @@ module.exports = function Route(route, options, handler, types) {
*/
function decodeParams(req, res, next) {
var urlParams = req.match;
if (!urlParams) return;
var body = req.body || {};
@ -169,10 +144,14 @@ module.exports = function Route(route, options, handler, types) {
// marker object
var EMPTY = {};
// 1. check if all required params are there
for (var key in params) {
var param = params[key];
if ( keys.indexOf(key) === -1 && param.source == 'url' )
continue;
if (
(!param.optional) && (
(param.source == "body" && !(key in body)) ||
@ -191,7 +170,8 @@ module.exports = function Route(route, options, handler, types) {
var type = param.type;
var value = EMPTY;
var isValid = true;
switch(param.source) {
switch (param.source) {
case "body":
if (param.optional && !(key in body))
break;
@ -261,7 +241,7 @@ module.exports = function Route(route, options, handler, types) {
for (var name in params) {
var param = params[name];
route.params[name] = {
name: param.name,
name: name,
type: param.type.toString(),
source: param.source,
optional: param.optional

268
node_modules/frontdoor/lib/route_test.js wygenerowano vendored
Wyświetl plik

@ -2,136 +2,152 @@
"use server";
var assert = require("assert");
require("amd-loader");
require("c9/inline-mocha")(module);
var sinon = require("sinon");
var frontdoor = require('../frontdoor');
var Route = frontdoor.Route;
var assert = require('assert');
var Route = require("./route");
it("test router: simple route with argument", function(done) {
var route = new Route("/user/:name", sinon.stub());
module.exports = {
"test router: simple route with argument": function() {
var route = new Route("/user/:name", sinon.stub());
var req = {};
assert.equal(route.match(req, "/juhu"), false);
assert.equal(route.match(req, "/juhu/12"), false);
assert.equal(route.match(req, "/user/fabian"), true);
assert.equal(req.match.name, "fabian");
},
"test router: simple route with number argument": function() {
var route = new Route("/user/:id", {
params: {
id: {
type: "int"
}
var req = {};
assert.equal(route.match(req, "/juhu"), false);
assert.equal(route.match(req, "/juhu/12"), false);
assert.equal(route.match(req, "/user/fabian"), true);
assert.equal(req.match.name, "fabian");
done();
});
it("test router: simple route with number argument", function(done) {
var route = new Route("/user/:id", {
params: {
id: {
type: "int"
}
}, sinon.stub());
var req = {};
assert.equal(route.match(req, "/user/fabian"), false);
assert.equal(route.match(req, "/user/123"), true);
assert.equal(req.match.id, 123);
},
"test router: for params if the value is a string it is treated as the type": function() {
var route = new Route("/user/:id", {
params: {
id: "int"
}
}, sinon.stub());
var req = {};
assert.equal(route.match(req, "/user/123"), true);
assert.equal(req.match.id, 123);
},
"test router: complex route with wildcard arguments": function() {
var route = new Route("/user/:name/:rest*", {
params: {
id: {
type: "int"
},
rest: {
type: "string"
}
}
}, sinon.stub());
var req = {};
assert.equal(route.match(req, "/user/fabian"), false);
assert.equal(route.match(req, "/user/fabian/"), true);
assert.equal(req.match.name, "fabian");
assert.equal(req.match.rest, "/");
assert.equal(route.match(req, "/user/fabian/abc"), true);
assert.equal(req.match.name, "fabian");
assert.equal(req.match.rest, "/abc");
assert.equal(route.match(req, "/user/fabian/abc/123"), true);
assert.equal(req.match.name, "fabian");
assert.equal(req.match.rest, "/abc/123");
},
"test router: complex route with multiple arguments": function() {
var route = new Route("/user/:name/:id", {
params: {
id: {
type: "int"
}
}
}, sinon.stub());
var req = {};
assert.equal(route.match(req, "/user/fabian"), false);
assert.equal(route.match(req, "/user/123"), false);
assert.equal(route.match(req, "/user/fabian/123"), true);
assert.equal(req.match.id, 123);
assert.equal(req.match.name, "fabian");
},
"test regexp types": function() {
var route = new Route("/users/:uid", {
params: {
uid: /u\d+/
}
}, sinon.stub());
var req = {};
assert.ok(route.match(req, "/users/u123"));
assert.ok(!route.match(req, "/users/_u123"));
},
"test custom type without register": function() {
var DateType = {
parse: function(string) {
if (!/\d{13}/.test(string))
throw new Error("not a timestamp");
return new Date(parseInt(string, 10));
}
}, sinon.stub());
var req = {};
assert.equal(route.match(req, "/user/fabian"), false);
assert.equal(route.match(req, "/user/123"), true);
assert.equal(req.match.id, 123);
done();
});
it("test router: for params if the value is a string it is treated as the type", function(done) {
var route = new Route("/user/:id", {
params: {
id: "int"
}
}, sinon.stub());
var req = {};
assert.equal(route.match(req, "/user/123"), true);
assert.equal(req.match.id, 123);
done();
});
it("test router: complex route with wildcard arguments", function(done) {
var route = new Route("/user/:name/:rest*", {
params: {
id: {
type: "int"
},
check: function(value) {
return value instanceof Date;
rest: {
type: "string"
}
};
var route = new Route("/ts/:ts", {
params: {
ts: {
type: DateType
}
}
}, sinon.stub());
var req = {};
assert.ok(route.match(req, "/ts/1353676299181"));
assert.ok(req.match.ts instanceof Date);
assert.ok(!route.match(req, "/ts/353676299181"));
assert.ok(!route.match(req, "/ts/abc"));
}
};
}
}, sinon.stub());
!module.parent && require("asyncjs").test.testcase(module.exports).exec();
var req = {};
assert.equal(route.match(req, "/user/fabian"), false);
assert.equal(route.match(req, "/user/fabian/"), true);
assert.equal(req.match.name, "fabian");
assert.equal(req.match.rest, "/");
assert.equal(route.match(req, "/user/fabian/abc"), true);
assert.equal(req.match.name, "fabian");
assert.equal(req.match.rest, "/abc");
assert.equal(route.match(req, "/user/fabian/abc/123"), true);
assert.equal(req.match.name, "fabian");
assert.equal(req.match.rest, "/abc/123");
done();
});
it("test router: complex route with multiple arguments", function(done) {
var route = new Route("/user/:name/:id", {
params: {
id: {
type: "int"
}
}
}, sinon.stub());
var req = {};
assert.equal(route.match(req, "/user/fabian"), false);
assert.equal(route.match(req, "/user/123"), false);
assert.equal(route.match(req, "/user/fabian/123"), true);
assert.equal(req.match.id, 123);
assert.equal(req.match.name, "fabian");
done();
});
it("test regexp types", function(done) {
var route = new Route("/users/:uid", {
params: {
uid: /u\d+/
}
}, sinon.stub());
var req = {};
assert.ok(route.match(req, "/users/u123"));
assert.ok(!route.match(req, "/users/_u123"));
done();
});
it("test custom type without register", function(done) {
var DateType = {
parse: function(string) {
if (!/\d{13}/.test(string))
throw new Error("not a timestamp");
return new Date(parseInt(string, 10));
},
check: function(value) {
return value instanceof Date;
}
};
var route = new Route("/ts/:ts", {
params: {
ts: {
type: DateType
}
}
}, sinon.stub());
var req = {};
assert.ok(route.match(req, "/ts/1353676299181"));
assert.ok(req.match.ts instanceof Date);
assert.ok(!route.match(req, "/ts/353676299181"));
assert.ok(!route.match(req, "/ts/abc"));
done();
});

23
node_modules/frontdoor/lib/section.js wygenerowano vendored
Wyświetl plik

@ -4,6 +4,7 @@ define(function(require, exports, module) {
var url = require("url");
var Types = require("./types").Types;
var Params = require("./params");
var Route = require("./route");
var flatten = require("./utils").flatten;
@ -32,10 +33,11 @@ module.exports = function Section(name, description, types) {
return self._route(route, options, handler);
}).bind(self, method);
});
this.del = this["delete"];
this.registerType = types.register.bind(types);
this.all = function(method, route, options, handler) {
var self = this;
var args = arguments;
@ -54,7 +56,7 @@ module.exports = function Section(name, description, types) {
};
this._route = function(route, options, handler) {
route = new Route(route, options, handler, types);
route = new Route(route, options, handler, types, this);
route.parent = this;
routes[route.method].push(route);
return this;
@ -160,7 +162,9 @@ module.exports = function Section(name, description, types) {
if (section && section.length) {
var subPath = "/" + splitPath.slice(1).join("/");
for (var i = 0; i < section.length; i++) {
var handler = section[i].match(req, subPath, method);
if (handler)
return handler;
}
@ -204,6 +208,21 @@ module.exports = function Section(name, description, types) {
return api;
};
var params;
Object.defineProperty( this, 'params', {
set: function( def, types ){
params = Params.normalize( def );
},
get: function(){
return params;
}
});
};
});

169
node_modules/frontdoor/lib/section_test.js wygenerowano vendored 100644
Wyświetl plik

@ -0,0 +1,169 @@
"use strict";
"use server";
require("c9/inline-mocha")(module);
require("amd-loader");
var assert = require('assert-diff');
var Section = require('../frontdoor').Section;
var Url = require('url');
var mock = {
req: function( method, uri) {
var parsedUrl = Url.parse(uri||'', true);
return {
method: method || 'get',
parsedUrl: parsedUrl,
pathname: parsedUrl.pathname,
}
}
};
it('Defines params on section level', function(done) {
var testParams = {
int: {
type: 'int',
source: 'url'
},
string: 'string',
alphanum: {
type: /[a-z0-9]+/,
source: 'url'
},
};
var cases = [
{
label: 'Match a simple string param',
path: '/test/:string',
url: '/test/foo',
params: {
string: 'foo',
}
},
{
label: 'Match a simple number param',
path: '/test/:int',
url: '/test/123',
params: {
int: 123,
}
},
{
label: 'Match multiple params',
path: '/test/:int/:string',
url: '/test/123/hello',
params: {
string: 'hello',
int: 123,
}
},
{
label: 'Match multiple params 3x',
path: '/test/:string/:int/:alphanum',
url: '/test/hello/123/baz123',
params: {
string: 'hello',
int: 123,
alphanum: 'baz123'
}
},
{
label: 'Check ordered params',
path: '/test/:string/:int/:alphanum',
url: '/test/123/hello/baz123',
err: true,
},
{
label: 'Must match type int param',
path: '/test/:int',
url: '/test/test',
err: true,
},
{
label: 'Must match optinal type int',
path: '/test/:int',
url: '/test',
err: true,
},
{
label: 'Match an optional param',
path: '/test/:optional',
url: '/test',
err: true,
},
{
label: 'Match an implied url param',
path: '/test/:implied',
url: '/test/ok',
params: {
implied: 'ok',
},
},
{
label: 'Query params can be passed along',
path: '/test/:string/:int/:alphanum',
url: '/test/hello/123/baz123?q=123',
options: {
params: {
q: {
type: 'int',
optional: false,
source: 'query',
}
}
},
params: {
string: 'hello',
int: 123,
alphanum: 'baz123',
q: 123
}
},
{
label: 'Required query params must be passed',
path: '/test/:string/:int/:alphanum',
url: '/test/hello/123/baz123',
err: true,
options: {
params: {
q: {
type: 'int',
optional: false,
source: 'query',
}
}
},
},
];
cases.forEach(function(testCase) {
var req = mock.req('get', testCase.url),
api = new Section('test');
api.params = testParams;
var handled = false;
api.get( testCase.path, testCase.options || {}, function(req, res, next){
handled = true;
assert.deepEqual( req.params, testCase.params, testCase.label );
});
api.handle( req.pathname, req, {}, function(err) {
if ( testCase.err ) {
assert.ok( 'OK: route not matched: ' + testCase.label );
return;
}
assert.ok(handled);
assert.fail( 'route not matched: ' + testCase.label );
});
});
done();
});

Wyświetl plik

@ -2,6 +2,8 @@
"use server";
require("amd-loader");
var assert = require("assert");
var types = require("./types");

74
node_modules/frontdoor/package.json wygenerowano vendored
Wyświetl plik

@ -1,37 +1,41 @@
{
"author": "Ajax.org B.V. <info@ajax.org>",
"contributors": [{
"name": "Fabian Jakobs",
"email": "fabian@c9.io"
}],
"name": "frontdoor",
"description": "Frontdoor is a libarary for creating RESTful API servers.",
"version": "0.0.1",
"scripts": {
"test": "find lib | grep '_test.js$' | xargs -n 1 node"
},
"licenses": [{
"type": "MIT",
"url": "http://github.com/frontdoor/smith/raw/master/LICENSE"
}],
"repository": {
"type": "git",
"url": "git://github.com/c9/frontdoor.git"
},
"main": "frontdoor.js",
"engines": {
"node": ">=0.6.0"
},
"dependencies": {
"http-error": "~0.0.1",
"request": "~2.12.0",
"amd-loader": "~0.0.5"
},
"devDependencies": {
"asyncjs": "~0.0.9",
"sinon": "~1.3.0",
"express": "3.0.3",
"ejs": "*"
"author": "Ajax.org B.V. <info@ajax.org>",
"contributors": [
{
"name": "Fabian Jakobs",
"email": "fabian@c9.io"
}
}
],
"name": "frontdoor",
"description": "Frontdoor is a libarary for creating RESTful API servers.",
"version": "0.0.1",
"scripts": {
"test": "find lib | grep '_test.js$' | xargs -n 1 node"
},
"licenses": [
{
"type": "MIT",
"url": "http://github.com/frontdoor/smith/raw/master/LICENSE"
}
],
"repository": {
"type": "git",
"url": "git://github.com/c9/frontdoor.git"
},
"main": "frontdoor.js",
"engines": {
"node": ">=0.6.0"
},
"dependencies": {
"http-error": "~0.0.1",
"request": "~2.12.0",
"amd-loader": "~0.0.5"
},
"devDependencies": {
"asyncjs": "~0.0.9",
"ejs": "*",
"express": "3.0.3",
"sinon": "~1.3.0",
"tape": "^3.5.0"
}
}