json-server 0.12.2 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +8 -6
- package/.eslintrc.js +2 -2
- package/CHANGELOG.md +18 -0
- package/README.md +26 -21
- package/{lib/server/public → dist}/favicon.ico +0 -0
- package/{lib/server/public → dist}/index.html +3 -11
- package/dist/main.css +675 -0
- package/dist/main.js +1 -0
- package/lib/cli/bin.js +4 -1
- package/lib/cli/index.js +13 -8
- package/lib/cli/run.js +98 -109
- package/lib/cli/utils/is.js +3 -4
- package/lib/cli/utils/load.js +71 -38
- package/lib/front/index.js +67 -0
- package/lib/server/body-parser.js +8 -3
- package/lib/server/defaults.js +44 -37
- package/lib/server/index.js +3 -5
- package/lib/server/mixins.js +37 -26
- package/lib/server/rewriter.js +7 -9
- package/lib/server/router/delay.js +4 -3
- package/lib/server/router/get-full-url.js +3 -4
- package/lib/server/router/index.js +59 -59
- package/lib/server/router/nested.js +13 -12
- package/lib/server/router/plural.js +134 -110
- package/lib/server/router/singular.js +35 -17
- package/lib/server/router/validate-data.js +4 -4
- package/lib/server/router/write.js +1 -1
- package/lib/server/utils.js +1 -1
- package/package.json +79 -51
- package/webpack.config.js +21 -0
- package/bin/index.js +0 -3
- package/lib/cli/example.json +0 -9
- package/lib/server/public/images/json.png +0 -0
- package/lib/server/public/main.js +0 -39
- package/lib/server/public/style.css +0 -73
package/lib/server/defaults.js
CHANGED
|
@@ -1,72 +1,79 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var fs = require('fs');
|
|
4
|
-
var path = require('path');
|
|
5
|
-
var express = require('express');
|
|
6
|
-
var logger = require('morgan');
|
|
7
|
-
var cors = require('cors');
|
|
8
|
-
var compression = require('compression');
|
|
9
|
-
var errorhandler = require('errorhandler');
|
|
10
|
-
var objectAssign = require('object-assign');
|
|
11
|
-
var bodyParser = require('./body-parser');
|
|
1
|
+
"use strict";
|
|
12
2
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const express = require('express');
|
|
8
|
+
|
|
9
|
+
const logger = require('morgan');
|
|
10
|
+
|
|
11
|
+
const cors = require('cors');
|
|
12
|
+
|
|
13
|
+
const compression = require('compression');
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
const errorhandler = require('errorhandler');
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
const objectAssign = require('object-assign');
|
|
18
|
+
|
|
19
|
+
const bodyParser = require('./body-parser');
|
|
20
|
+
|
|
21
|
+
module.exports = function (opts) {
|
|
22
|
+
const userDir = path.join(process.cwd(), 'public');
|
|
23
|
+
const defaultDir = path.join(__dirname, '../../dist');
|
|
24
|
+
const staticDir = fs.existsSync(userDir) ? userDir : defaultDir;
|
|
25
|
+
opts = objectAssign({
|
|
26
|
+
logger: true,
|
|
27
|
+
static: staticDir
|
|
28
|
+
}, opts);
|
|
29
|
+
const arr = []; // Compress all requests
|
|
21
30
|
|
|
22
|
-
// Compress all requests
|
|
23
31
|
if (!opts.noGzip) {
|
|
24
32
|
arr.push(compression());
|
|
25
|
-
}
|
|
33
|
+
} // Enable CORS for all the requests, including static files
|
|
34
|
+
|
|
26
35
|
|
|
27
|
-
// Enable CORS for all the requests, including static files
|
|
28
36
|
if (!opts.noCors) {
|
|
29
|
-
arr.push(cors({
|
|
37
|
+
arr.push(cors({
|
|
38
|
+
origin: true,
|
|
39
|
+
credentials: true
|
|
40
|
+
}));
|
|
30
41
|
}
|
|
31
42
|
|
|
32
43
|
if (process.env.NODE_ENV === 'development') {
|
|
33
44
|
// only use in development
|
|
34
45
|
arr.push(errorhandler());
|
|
35
|
-
}
|
|
46
|
+
} // Serve static files
|
|
47
|
+
|
|
36
48
|
|
|
37
|
-
//
|
|
38
|
-
arr.push(express.static(opts.static));
|
|
49
|
+
arr.push(express.static(opts.static)); // Logger
|
|
39
50
|
|
|
40
|
-
// Logger
|
|
41
51
|
if (opts.logger) {
|
|
42
52
|
arr.push(logger('dev', {
|
|
43
|
-
skip:
|
|
44
|
-
return process.env.NODE_ENV === 'test' || req.path === '/favicon.ico';
|
|
45
|
-
}
|
|
53
|
+
skip: req => process.env.NODE_ENV === 'test' || req.path === '/favicon.ico'
|
|
46
54
|
}));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// No cache for IE
|
|
55
|
+
} // No cache for IE
|
|
50
56
|
// https://support.microsoft.com/en-us/kb/234067
|
|
51
|
-
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
arr.push((req, res, next) => {
|
|
52
60
|
res.header('Cache-Control', 'no-cache');
|
|
53
61
|
res.header('Pragma', 'no-cache');
|
|
54
62
|
res.header('Expires', '-1');
|
|
55
63
|
next();
|
|
56
|
-
});
|
|
64
|
+
}); // Read-only
|
|
57
65
|
|
|
58
|
-
// Read-only
|
|
59
66
|
if (opts.readOnly) {
|
|
60
|
-
arr.push(
|
|
67
|
+
arr.push((req, res, next) => {
|
|
61
68
|
if (req.method === 'GET') {
|
|
62
69
|
next(); // Continue
|
|
63
70
|
} else {
|
|
64
71
|
res.sendStatus(403); // Forbidden
|
|
65
72
|
}
|
|
66
73
|
});
|
|
67
|
-
}
|
|
74
|
+
} // Add middlewares
|
|
75
|
+
|
|
68
76
|
|
|
69
|
-
// Add middlewares
|
|
70
77
|
if (opts.bodyParser) {
|
|
71
78
|
arr.push(bodyParser);
|
|
72
79
|
}
|
package/lib/server/index.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const express = require('express');
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
6
|
-
create:
|
|
7
|
-
return express().set('json spaces', 2);
|
|
8
|
-
},
|
|
6
|
+
create: () => express().set('json spaces', 2),
|
|
9
7
|
defaults: require('./defaults'),
|
|
10
8
|
router: require('./router'),
|
|
11
9
|
rewriter: require('./rewriter'),
|
package/lib/server/mixins.js
CHANGED
|
@@ -1,31 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const nanoid = require('nanoid');
|
|
4
|
+
|
|
5
|
+
const pluralize = require('pluralize');
|
|
5
6
|
|
|
6
7
|
module.exports = {
|
|
7
8
|
getRemovable,
|
|
8
9
|
createId,
|
|
9
|
-
deepQuery
|
|
10
|
-
|
|
11
|
-
// Returns document ids that have unsatisfied relations
|
|
10
|
+
deepQuery // Returns document ids that have unsatisfied relations
|
|
12
11
|
// Example: a comment that references a post that doesn't exist
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function getRemovable(db, opts) {
|
|
16
|
+
const _ = this;
|
|
17
|
+
|
|
18
|
+
const removable = [];
|
|
19
|
+
|
|
20
|
+
_.each(db, (coll, collName) => {
|
|
21
|
+
_.each(coll, doc => {
|
|
22
|
+
_.each(doc, (value, key) => {
|
|
19
23
|
if (new RegExp(`${opts.foreignKeySuffix}$`).test(key)) {
|
|
20
24
|
// Remove foreign key suffix and pluralize it
|
|
21
25
|
// Example postId -> posts
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
const refName = pluralize.plural(key.replace(new RegExp(`${opts.foreignKeySuffix}$`), '')); // Test if table exists
|
|
27
|
+
|
|
24
28
|
if (db[refName]) {
|
|
25
29
|
// Test if references is defined in table
|
|
26
|
-
|
|
30
|
+
const ref = _.getById(db[refName], value);
|
|
31
|
+
|
|
27
32
|
if (_.isUndefined(ref)) {
|
|
28
|
-
removable.push({
|
|
33
|
+
removable.push({
|
|
34
|
+
name: collName,
|
|
35
|
+
id: doc.id
|
|
36
|
+
});
|
|
29
37
|
}
|
|
30
38
|
}
|
|
31
39
|
}
|
|
@@ -34,34 +42,37 @@ module.exports = {
|
|
|
34
42
|
});
|
|
35
43
|
|
|
36
44
|
return removable;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Return incremented id or uuid
|
|
45
|
+
} // Return incremented id or uuid
|
|
40
46
|
// Used to override lodash-id's createId with utils.createId
|
|
47
|
+
|
|
48
|
+
|
|
41
49
|
function createId(coll) {
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
const _ = this;
|
|
51
|
+
|
|
52
|
+
const idProperty = _.__id();
|
|
53
|
+
|
|
44
54
|
if (_.isEmpty(coll)) {
|
|
45
55
|
return 1;
|
|
46
56
|
} else {
|
|
47
|
-
|
|
57
|
+
let id = _(coll).maxBy(idProperty)[idProperty]; // Increment integer id or generate string id
|
|
58
|
+
|
|
48
59
|
|
|
49
|
-
// Increment integer id or generate string id
|
|
50
60
|
return _.isFinite(id) ? ++id : nanoid(7);
|
|
51
61
|
}
|
|
52
62
|
}
|
|
53
63
|
|
|
54
64
|
function deepQuery(value, q) {
|
|
55
|
-
|
|
65
|
+
const _ = this;
|
|
66
|
+
|
|
56
67
|
if (value && q) {
|
|
57
68
|
if (_.isArray(value)) {
|
|
58
|
-
for (
|
|
69
|
+
for (let i = 0; i < value.length; i++) {
|
|
59
70
|
if (_.deepQuery(value[i], q)) {
|
|
60
71
|
return true;
|
|
61
72
|
}
|
|
62
73
|
}
|
|
63
74
|
} else if (_.isObject(value) && !_.isArray(value)) {
|
|
64
|
-
for (
|
|
75
|
+
for (let k in value) {
|
|
65
76
|
if (_.deepQuery(value[k], q)) {
|
|
66
77
|
return true;
|
|
67
78
|
}
|
package/lib/server/rewriter.js
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
var rewrite = require('express-urlrewrite');
|
|
3
|
+
const express = require('express');
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
var router = express.Router();
|
|
5
|
+
const rewrite = require('express-urlrewrite');
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
module.exports = routes => {
|
|
8
|
+
const router = express.Router();
|
|
9
|
+
router.get('/__rules', (req, res) => {
|
|
10
10
|
res.json(routes);
|
|
11
11
|
});
|
|
12
|
-
|
|
13
|
-
Object.keys(routes).forEach(function (key) {
|
|
12
|
+
Object.keys(routes).forEach(key => {
|
|
14
13
|
router.use(rewrite(key, routes[key]));
|
|
15
14
|
});
|
|
16
|
-
|
|
17
15
|
return router;
|
|
18
16
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const pause = require('connect-pause');
|
|
4
4
|
|
|
5
5
|
module.exports = function delay(req, res, next) {
|
|
6
6
|
// NOTE: for some reason unknown to me, if the default is 0, the tests seems to add 2 seconds
|
|
7
7
|
// NOTE: to each test, a default value of 1 does not seem to be effected by that issue
|
|
8
|
-
|
|
8
|
+
const _delay = !isNaN(parseFloat(req.query._delay)) ? parseFloat(req.query._delay) : 1;
|
|
9
|
+
|
|
9
10
|
delete req.query._delay;
|
|
10
11
|
pause(_delay)(req, res, next);
|
|
11
12
|
};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const url = require('url');
|
|
4
4
|
|
|
5
5
|
module.exports = function getFullURL(req) {
|
|
6
|
-
|
|
6
|
+
const root = url.format({
|
|
7
7
|
protocol: req.protocol,
|
|
8
8
|
host: req.get('host')
|
|
9
9
|
});
|
|
10
|
-
|
|
11
10
|
return `${root}${req.originalUrl}`;
|
|
12
11
|
};
|
|
@@ -1,65 +1,70 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const express = require('express');
|
|
4
|
+
|
|
5
|
+
const methodOverride = require('method-override');
|
|
6
|
+
|
|
7
|
+
const _ = require('lodash');
|
|
8
|
+
|
|
9
|
+
const lodashId = require('lodash-id');
|
|
10
|
+
|
|
11
|
+
const low = require('lowdb');
|
|
12
|
+
|
|
13
|
+
const Memory = require('lowdb/adapters/Memory');
|
|
14
|
+
|
|
15
|
+
const FileSync = require('lowdb/adapters/FileSync');
|
|
16
|
+
|
|
17
|
+
const bodyParser = require('../body-parser');
|
|
18
|
+
|
|
19
|
+
const validateData = require('./validate-data');
|
|
20
|
+
|
|
21
|
+
const plural = require('./plural');
|
|
22
|
+
|
|
23
|
+
const nested = require('./nested');
|
|
24
|
+
|
|
25
|
+
const singular = require('./singular');
|
|
26
|
+
|
|
27
|
+
const mixins = require('../mixins');
|
|
28
|
+
|
|
29
|
+
module.exports = (db, opts = {
|
|
30
|
+
foreignKeySuffix: 'Id',
|
|
31
|
+
_isFake: false
|
|
32
|
+
}) => {
|
|
33
|
+
if (typeof db === 'string') {
|
|
34
|
+
db = low(new FileSync(db));
|
|
35
|
+
} else if (!_.has(db, '__chain__') || !_.has(db, '__wrapped__')) {
|
|
36
|
+
db = low(new Memory()).setState(db);
|
|
37
|
+
} // Create router
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
const router = express.Router(); // Add middlewares
|
|
41
|
+
|
|
23
42
|
router.use(methodOverride());
|
|
24
43
|
router.use(bodyParser);
|
|
44
|
+
validateData(db.getState()); // Add lodash-id methods to db
|
|
25
45
|
|
|
26
|
-
//
|
|
27
|
-
var db = void 0;
|
|
28
|
-
if (_.isObject(source)) {
|
|
29
|
-
db = low();
|
|
30
|
-
db.setState(source);
|
|
31
|
-
} else {
|
|
32
|
-
db = low(source, { storage: fileAsync });
|
|
33
|
-
}
|
|
46
|
+
db._.mixin(lodashId); // Add specific mixins
|
|
34
47
|
|
|
35
|
-
validateData(db.getState());
|
|
36
48
|
|
|
37
|
-
//
|
|
38
|
-
db._.mixin(lodashId);
|
|
49
|
+
db._.mixin(mixins); // Expose database
|
|
39
50
|
|
|
40
|
-
// Add specific mixins
|
|
41
|
-
db._.mixin(mixins);
|
|
42
51
|
|
|
43
|
-
// Expose
|
|
44
|
-
router.db = db;
|
|
52
|
+
router.db = db; // Expose render
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
router.render = function (req, res) {
|
|
54
|
+
router.render = (req, res) => {
|
|
48
55
|
res.jsonp(res.locals.data);
|
|
49
|
-
};
|
|
56
|
+
}; // GET /db
|
|
57
|
+
|
|
50
58
|
|
|
51
|
-
|
|
52
|
-
router.get('/db', function (req, res) {
|
|
59
|
+
router.get('/db', (req, res) => {
|
|
53
60
|
res.jsonp(db.getState());
|
|
54
|
-
});
|
|
61
|
+
}); // Handle /:parent/:parentId/:resource
|
|
55
62
|
|
|
56
|
-
//
|
|
57
|
-
router.use(nested(opts));
|
|
63
|
+
router.use(nested(opts)); // Create routes
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
db.forEach(function (value, key) {
|
|
65
|
+
db.forEach((value, key) => {
|
|
61
66
|
if (_.isPlainObject(value)) {
|
|
62
|
-
router.use(`/${key}`, singular(db, key));
|
|
67
|
+
router.use(`/${key}`, singular(db, key, opts));
|
|
63
68
|
return;
|
|
64
69
|
}
|
|
65
70
|
|
|
@@ -68,17 +73,14 @@ module.exports = function (source) {
|
|
|
68
73
|
return;
|
|
69
74
|
}
|
|
70
75
|
|
|
71
|
-
var sourceMessage = '';
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
var msg = `Type of "${key}" (${typeof value}) ${sourceMessage} is not supported. ` + `Use objects or arrays of objects.`;
|
|
76
|
+
var sourceMessage = ''; // if (!_.isObject(source)) {
|
|
77
|
+
// sourceMessage = `in ${source}`
|
|
78
|
+
// }
|
|
77
79
|
|
|
80
|
+
const msg = `Type of "${key}" (${typeof value}) ${sourceMessage} is not supported. ` + `Use objects or arrays of objects.`;
|
|
78
81
|
throw new Error(msg);
|
|
79
82
|
}).value();
|
|
80
|
-
|
|
81
|
-
router.use(function (req, res) {
|
|
83
|
+
router.use((req, res) => {
|
|
82
84
|
if (!res.locals.data) {
|
|
83
85
|
res.status(404);
|
|
84
86
|
res.locals.data = {};
|
|
@@ -86,11 +88,9 @@ module.exports = function (source) {
|
|
|
86
88
|
|
|
87
89
|
router.render(req, res);
|
|
88
90
|
});
|
|
89
|
-
|
|
90
|
-
router.use(function (err, req, res, next) {
|
|
91
|
+
router.use((err, req, res, next) => {
|
|
91
92
|
console.error(err.stack);
|
|
92
93
|
res.status(500).send(err.stack);
|
|
93
94
|
});
|
|
94
|
-
|
|
95
95
|
return router;
|
|
96
96
|
};
|
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
var pluralize = require('pluralize');
|
|
5
|
-
var delay = require('./delay');
|
|
3
|
+
const express = require('express');
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const pluralize = require('pluralize');
|
|
6
|
+
|
|
7
|
+
const delay = require('./delay');
|
|
8
|
+
|
|
9
|
+
module.exports = opts => {
|
|
10
|
+
const router = express.Router();
|
|
11
|
+
router.use(delay); // Rewrite URL (/:resource/:id/:nested -> /:nested) and request query
|
|
10
12
|
|
|
11
|
-
// Rewrite URL (/:resource/:id/:nested -> /:nested) and request query
|
|
12
13
|
function get(req, res, next) {
|
|
13
|
-
|
|
14
|
+
const prop = pluralize.singular(req.params.resource);
|
|
14
15
|
req.query[`${prop}${opts.foreignKeySuffix}`] = req.params.id;
|
|
15
16
|
req.url = `/${req.params.nested}`;
|
|
16
17
|
next();
|
|
17
|
-
}
|
|
18
|
+
} // Rewrite URL (/:resource/:id/:nested -> /:nested) and request body
|
|
19
|
+
|
|
18
20
|
|
|
19
|
-
// Rewrite URL (/:resource/:id/:nested -> /:nested) and request body
|
|
20
21
|
function post(req, res, next) {
|
|
21
|
-
|
|
22
|
+
const prop = pluralize.singular(req.params.resource);
|
|
22
23
|
req.body[`${prop}${opts.foreignKeySuffix}`] = req.params.id;
|
|
23
24
|
req.url = `/${req.params.nested}`;
|
|
24
25
|
next();
|