json-server 0.17.4 → 1.0.0-alpha.1

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.
@@ -1,76 +0,0 @@
1
- "use strict";
2
-
3
- const {
4
- nanoid
5
- } = require('nanoid');
6
- const pluralize = require('pluralize');
7
- module.exports = {
8
- getRemovable,
9
- createId,
10
- deepQuery
11
- };
12
-
13
- // Returns document ids that have unsatisfied relations
14
- // Example: a comment that references a post that doesn't exist
15
- function getRemovable(db, opts) {
16
- const _ = this;
17
- const removable = [];
18
- _.each(db, (coll, collName) => {
19
- _.each(coll, doc => {
20
- _.each(doc, (value, key) => {
21
- if (new RegExp(`${opts.foreignKeySuffix}$`).test(key)) {
22
- // Remove foreign key suffix and pluralize it
23
- // Example postId -> posts
24
- const refName = pluralize.plural(key.replace(new RegExp(`${opts.foreignKeySuffix}$`), ''));
25
- // Test if table exists
26
- if (db[refName]) {
27
- // Test if references is defined in table
28
- const ref = _.getById(db[refName], value);
29
- if (_.isUndefined(ref)) {
30
- removable.push({
31
- name: collName,
32
- id: doc.id
33
- });
34
- }
35
- }
36
- }
37
- });
38
- });
39
- });
40
- return removable;
41
- }
42
-
43
- // Return incremented id or uuid
44
- // Used to override lodash-id's createId with utils.createId
45
- function createId(coll) {
46
- const _ = this;
47
- const idProperty = _.__id();
48
- if (_.isEmpty(coll)) {
49
- return 1;
50
- } else {
51
- let id = _(coll).maxBy(idProperty)[idProperty];
52
-
53
- // Increment integer id or generate string id
54
- return _.isFinite(id) ? ++id : nanoid(7);
55
- }
56
- }
57
- function deepQuery(value, q) {
58
- const _ = this;
59
- if (value && q) {
60
- if (_.isArray(value)) {
61
- for (let i = 0; i < value.length; i++) {
62
- if (_.deepQuery(value[i], q)) {
63
- return true;
64
- }
65
- }
66
- } else if (_.isObject(value) && !_.isArray(value)) {
67
- for (const k in value) {
68
- if (_.deepQuery(value[k], q)) {
69
- return true;
70
- }
71
- }
72
- } else if (value.toString().toLowerCase().indexOf(q) !== -1) {
73
- return true;
74
- }
75
- }
76
- }
@@ -1,14 +0,0 @@
1
- "use strict";
2
-
3
- const express = require('express');
4
- const rewrite = require('express-urlrewrite');
5
- module.exports = routes => {
6
- const router = express.Router();
7
- router.get('/__rules', (req, res) => {
8
- res.json(routes);
9
- });
10
- Object.keys(routes).forEach(key => {
11
- router.use(rewrite(key, routes[key]));
12
- });
13
- return router;
14
- };
@@ -1,10 +0,0 @@
1
- "use strict";
2
-
3
- const pause = require('connect-pause');
4
- module.exports = function delay(req, res, next) {
5
- // NOTE: for some reason unknown to me, if the default is 0, the tests seems to add 2 seconds
6
- // NOTE: to each test, a default value of 1 does not seem to be effected by that issue
7
- const _delay = !isNaN(parseFloat(req.query._delay)) ? parseFloat(req.query._delay) : 1;
8
- delete req.query._delay;
9
- pause(_delay)(req, res, next);
10
- };
@@ -1,10 +0,0 @@
1
- "use strict";
2
-
3
- const url = require('url');
4
- module.exports = function getFullURL(req) {
5
- const root = url.format({
6
- protocol: req.protocol,
7
- host: req.get('host')
8
- });
9
- return `${root}${req.originalUrl}`;
10
- };
@@ -1,91 +0,0 @@
1
- "use strict";
2
-
3
- const express = require('express');
4
- const methodOverride = require('method-override');
5
- const _ = require('lodash');
6
- const lodashId = require('lodash-id');
7
- const low = require('lowdb');
8
- const Memory = require('lowdb/adapters/Memory');
9
- const FileSync = require('lowdb/adapters/FileSync');
10
- const bodyParser = require('../body-parser');
11
- const validateData = require('./validate-data');
12
- const plural = require('./plural');
13
- const nested = require('./nested');
14
- const singular = require('./singular');
15
- const mixins = require('../mixins');
16
- module.exports = (db, opts) => {
17
- opts = Object.assign({
18
- foreignKeySuffix: 'Id',
19
- _isFake: false
20
- }, opts);
21
- if (typeof db === 'string') {
22
- db = low(new FileSync(db));
23
- } else if (!_.has(db, '__chain__') || !_.has(db, '__wrapped__')) {
24
- db = low(new Memory()).setState(db);
25
- }
26
-
27
- // Create router
28
- const router = express.Router();
29
-
30
- // Add middlewares
31
- router.use(methodOverride());
32
- router.use(bodyParser);
33
- validateData(db.getState());
34
-
35
- // Add lodash-id methods to db
36
- db._.mixin(lodashId);
37
-
38
- // Add specific mixins
39
- db._.mixin(mixins);
40
-
41
- // Expose database
42
- router.db = db;
43
-
44
- // Expose render
45
- router.render = (req, res) => {
46
- res.jsonp(res.locals.data);
47
- };
48
-
49
- // GET /db
50
- router.get('/db', (req, res) => {
51
- res.jsonp(db.getState());
52
- });
53
-
54
- // Handle /:parent/:parentId/:resource
55
- router.use(nested(opts));
56
-
57
- // Create routes
58
- db.forEach((value, key) => {
59
- if (key === '$schema') {
60
- // ignore $schema
61
- return;
62
- }
63
- if (_.isPlainObject(value)) {
64
- router.use(`/${key}`, singular(db, key, opts));
65
- return;
66
- }
67
- if (_.isArray(value)) {
68
- router.use(`/${key}`, plural(db, key, opts));
69
- return;
70
- }
71
- const sourceMessage = '';
72
- // if (!_.isObject(source)) {
73
- // sourceMessage = `in ${source}`
74
- // }
75
-
76
- const msg = `Type of "${key}" (${typeof value}) ${sourceMessage} is not supported. ` + `Use objects or arrays of objects.`;
77
- throw new Error(msg);
78
- }).value();
79
- router.use((req, res) => {
80
- if (!res.locals.data) {
81
- res.status(404);
82
- res.locals.data = {};
83
- }
84
- router.render(req, res);
85
- });
86
- router.use((err, req, res, next) => {
87
- console.error(err.stack);
88
- res.status(500).send(err.stack);
89
- });
90
- return router;
91
- };
@@ -1,26 +0,0 @@
1
- "use strict";
2
-
3
- const express = require('express');
4
- const pluralize = require('pluralize');
5
- const delay = require('./delay');
6
- module.exports = opts => {
7
- const router = express.Router();
8
- router.use(delay);
9
-
10
- // Rewrite URL (/:resource/:id/:nested -> /:nested) and request query
11
- function get(req, res, next) {
12
- const prop = pluralize.singular(req.params.resource);
13
- req.query[`${prop}${opts.foreignKeySuffix}`] = req.params.id;
14
- req.url = `/${req.params.nested}`;
15
- next();
16
- }
17
-
18
- // Rewrite URL (/:resource/:id/:nested -> /:nested) and request body
19
- function post(req, res, next) {
20
- const prop = pluralize.singular(req.params.resource);
21
- req.body[`${prop}${opts.foreignKeySuffix}`] = req.params.id;
22
- req.url = `/${req.params.nested}`;
23
- next();
24
- }
25
- return router.get('/:resource/:id/:nested', get).post('/:resource/:id/:nested', post);
26
- };
@@ -1,273 +0,0 @@
1
- "use strict";
2
-
3
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
- function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
- function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
7
- function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
8
- const express = require('express');
9
- const _ = require('lodash');
10
- const pluralize = require('pluralize');
11
- const write = require('./write');
12
- const getFullURL = require('./get-full-url');
13
- const utils = require('../utils');
14
- const delay = require('./delay');
15
- module.exports = (db, name, opts) => {
16
- // Create router
17
- const router = express.Router();
18
- router.use(delay);
19
-
20
- // Embed function used in GET /name and GET /name/id
21
- function embed(resource, e) {
22
- e && [].concat(e).forEach(externalResource => {
23
- if (db.get(externalResource).value) {
24
- const query = {};
25
- const singularResource = pluralize.singular(name);
26
- query[`${singularResource}${opts.foreignKeySuffix}`] = resource.id;
27
- resource[externalResource] = db.get(externalResource).filter(query).value();
28
- }
29
- });
30
- }
31
-
32
- // Expand function used in GET /name and GET /name/id
33
- function expand(resource, e) {
34
- e && [].concat(e).forEach(innerResource => {
35
- const plural = pluralize(innerResource);
36
- if (db.get(plural).value()) {
37
- const prop = `${innerResource}${opts.foreignKeySuffix}`;
38
- resource[innerResource] = db.get(plural).getById(resource[prop]).value();
39
- }
40
- });
41
- }
42
-
43
- // GET /name
44
- // GET /name?q=
45
- // GET /name?attr=&attr=
46
- // GET /name?_end=&
47
- // GET /name?_start=&_end=&
48
- // GET /name?_embed=&_expand=
49
- function list(req, res, next) {
50
- // Resource chain
51
- let chain = db.get(name);
52
-
53
- // Remove q, _start, _end, ... from req.query to avoid filtering using those
54
- // parameters
55
- let q = req.query.q;
56
- let _start = req.query._start;
57
- let _end = req.query._end;
58
- let _page = req.query._page;
59
- const _sort = req.query._sort;
60
- const _order = req.query._order;
61
- let _limit = req.query._limit;
62
- const _embed = req.query._embed;
63
- const _expand = req.query._expand;
64
- delete req.query.q;
65
- delete req.query._start;
66
- delete req.query._end;
67
- delete req.query._sort;
68
- delete req.query._order;
69
- delete req.query._limit;
70
- delete req.query._embed;
71
- delete req.query._expand;
72
-
73
- // Automatically delete query parameters that can't be found
74
- // in the database
75
- Object.keys(req.query).forEach(query => {
76
- const arr = db.get(name).value();
77
- for (const i in arr) {
78
- if (_.has(arr[i], query) || query === 'callback' || query === '_' || /_lte$/.test(query) || /_gte$/.test(query) || /_ne$/.test(query) || /_like$/.test(query)) return;
79
- }
80
- delete req.query[query];
81
- });
82
- if (q) {
83
- // Full-text search
84
- if (Array.isArray(q)) {
85
- q = q[0];
86
- }
87
- q = q.toLowerCase();
88
- chain = chain.filter(obj => {
89
- for (const key in obj) {
90
- const value = obj[key];
91
- if (db._.deepQuery(value, q)) {
92
- return true;
93
- }
94
- }
95
- return false;
96
- });
97
- }
98
- Object.keys(req.query).forEach(key => {
99
- // Don't take into account JSONP query parameters
100
- // jQuery adds a '_' query parameter too
101
- if (key !== 'callback' && key !== '_') {
102
- // Always use an array, in case req.query is an array
103
- const arr = [].concat(req.query[key]);
104
- const isDifferent = /_ne$/.test(key);
105
- const isRange = /_lte$/.test(key) || /_gte$/.test(key);
106
- const isLike = /_like$/.test(key);
107
- const path = key.replace(/(_lte|_gte|_ne|_like)$/, '');
108
- chain = chain.filter(element => {
109
- return arr.map(function (value) {
110
- // get item value based on path
111
- // i.e post.title -> 'foo'
112
- const elementValue = _.get(element, path);
113
-
114
- // Prevent toString() failing on undefined or null values
115
- if (elementValue === undefined || elementValue === null) {
116
- return undefined;
117
- }
118
- if (isRange) {
119
- const isLowerThan = /_gte$/.test(key);
120
- return isLowerThan ? value <= elementValue : value >= elementValue;
121
- } else if (isDifferent) {
122
- return value !== elementValue.toString();
123
- } else if (isLike) {
124
- return new RegExp(value, 'i').test(elementValue.toString());
125
- } else {
126
- return value === elementValue.toString();
127
- }
128
- }).reduce((a, b) => isDifferent ? a && b : a || b);
129
- });
130
- }
131
- });
132
-
133
- // Sort
134
- if (_sort) {
135
- const _sortSet = _sort.split(',');
136
- const _orderSet = (_order || '').split(',').map(s => s.toLowerCase());
137
- chain = chain.orderBy(_sortSet, _orderSet);
138
- }
139
-
140
- // Slice result
141
- if (_end || _limit || _page) {
142
- res.setHeader('X-Total-Count', chain.size());
143
- res.setHeader('Access-Control-Expose-Headers', `X-Total-Count${_page ? ', Link' : ''}`);
144
- }
145
- if (_page) {
146
- _page = parseInt(_page, 10);
147
- _page = _page >= 1 ? _page : 1;
148
- _limit = parseInt(_limit, 10) || 10;
149
- const page = utils.getPage(chain.value(), _page, _limit);
150
- const links = {};
151
- const fullURL = getFullURL(req);
152
- if (page.first) {
153
- links.first = fullURL.replace(`page=${page.current}`, `page=${page.first}`);
154
- }
155
- if (page.prev) {
156
- links.prev = fullURL.replace(`page=${page.current}`, `page=${page.prev}`);
157
- }
158
- if (page.next) {
159
- links.next = fullURL.replace(`page=${page.current}`, `page=${page.next}`);
160
- }
161
- if (page.last) {
162
- links.last = fullURL.replace(`page=${page.current}`, `page=${page.last}`);
163
- }
164
- res.links(links);
165
- chain = _.chain(page.items);
166
- } else if (_end) {
167
- _start = parseInt(_start, 10) || 0;
168
- _end = parseInt(_end, 10);
169
- chain = chain.slice(_start, _end);
170
- } else if (_limit) {
171
- _start = parseInt(_start, 10) || 0;
172
- _limit = parseInt(_limit, 10);
173
- chain = chain.slice(_start, _start + _limit);
174
- }
175
-
176
- // embed and expand
177
- chain = chain.cloneDeep().forEach(function (element) {
178
- embed(element, _embed);
179
- expand(element, _expand);
180
- });
181
- res.locals.data = chain.value();
182
- next();
183
- }
184
-
185
- // GET /name/:id
186
- // GET /name/:id?_embed=&_expand
187
- function show(req, res, next) {
188
- const _embed = req.query._embed;
189
- const _expand = req.query._expand;
190
- const resource = db.get(name).getById(req.params.id).value();
191
- if (resource) {
192
- // Clone resource to avoid making changes to the underlying object
193
- const clone = _.cloneDeep(resource);
194
-
195
- // Embed other resources based on resource id
196
- // /posts/1?_embed=comments
197
- embed(clone, _embed);
198
-
199
- // Expand inner resources based on id
200
- // /posts/1?_expand=user
201
- expand(clone, _expand);
202
- res.locals.data = clone;
203
- }
204
- next();
205
- }
206
-
207
- // POST /name
208
- function create(req, res, next) {
209
- let resource;
210
- if (opts._isFake) {
211
- const id = db.get(name).createId().value();
212
- resource = _objectSpread(_objectSpread({}, req.body), {}, {
213
- id
214
- });
215
- } else {
216
- resource = db.get(name).insert(req.body).value();
217
- }
218
- res.setHeader('Access-Control-Expose-Headers', 'Location');
219
- res.location(`${getFullURL(req)}/${resource.id}`);
220
- res.status(201);
221
- res.locals.data = resource;
222
- next();
223
- }
224
-
225
- // PUT /name/:id
226
- // PATCH /name/:id
227
- function update(req, res, next) {
228
- const id = req.params.id;
229
- let resource;
230
- if (opts._isFake) {
231
- resource = db.get(name).getById(id).value();
232
- if (req.method === 'PATCH') {
233
- resource = _objectSpread(_objectSpread({}, resource), req.body);
234
- } else {
235
- resource = _objectSpread(_objectSpread({}, req.body), {}, {
236
- id: resource.id
237
- });
238
- }
239
- } else {
240
- let chain = db.get(name);
241
- chain = req.method === 'PATCH' ? chain.updateById(id, req.body) : chain.replaceById(id, req.body);
242
- resource = chain.value();
243
- }
244
- if (resource) {
245
- res.locals.data = resource;
246
- }
247
- next();
248
- }
249
-
250
- // DELETE /name/:id
251
- function destroy(req, res, next) {
252
- let resource;
253
- if (opts._isFake) {
254
- resource = db.get(name).value();
255
- } else {
256
- resource = db.get(name).removeById(req.params.id).value();
257
-
258
- // Remove dependents documents
259
- const removable = db._.getRemovable(db.getState(), opts);
260
- removable.forEach(item => {
261
- db.get(item.name).removeById(item.id).value();
262
- });
263
- }
264
- if (resource) {
265
- res.locals.data = {};
266
- }
267
- next();
268
- }
269
- const w = write(db);
270
- router.route('/').get(list).post(create, w);
271
- router.route('/:id').get(show).put(update, w).patch(update, w).delete(destroy, w);
272
- return router;
273
- };
@@ -1,52 +0,0 @@
1
- "use strict";
2
-
3
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
- function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
- function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
7
- function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
8
- const express = require('express');
9
- const write = require('./write');
10
- const getFullURL = require('./get-full-url');
11
- const delay = require('./delay');
12
- module.exports = (db, name, opts) => {
13
- const router = express.Router();
14
- router.use(delay);
15
- function show(req, res, next) {
16
- res.locals.data = db.get(name).value();
17
- next();
18
- }
19
- function create(req, res, next) {
20
- if (opts._isFake) {
21
- res.locals.data = req.body;
22
- } else {
23
- db.set(name, req.body).value();
24
- res.locals.data = db.get(name).value();
25
- }
26
- res.setHeader('Access-Control-Expose-Headers', 'Location');
27
- res.location(`${getFullURL(req)}`);
28
- res.status(201);
29
- next();
30
- }
31
- function update(req, res, next) {
32
- if (opts._isFake) {
33
- if (req.method === 'PUT') {
34
- res.locals.data = req.body;
35
- } else {
36
- const resource = db.get(name).value();
37
- res.locals.data = _objectSpread(_objectSpread({}, resource), req.body);
38
- }
39
- } else {
40
- if (req.method === 'PUT') {
41
- db.set(name, req.body).value();
42
- } else {
43
- db.get(name).assign(req.body).value();
44
- }
45
- res.locals.data = db.get(name).value();
46
- }
47
- next();
48
- }
49
- const w = write(db);
50
- router.route('/').get(show).post(create, w).put(update, w).patch(update, w);
51
- return router;
52
- };
@@ -1,17 +0,0 @@
1
- "use strict";
2
-
3
- const _ = require('lodash');
4
- function validateKey(key) {
5
- if (key.indexOf('/') !== -1) {
6
- const msg = [`Oops, found / character in database property '${key}'.`, '', "/ aren't supported, if you want to tweak default routes, see", 'https://github.com/typicode/json-server/#add-custom-routes'].join('\n');
7
- throw new Error(msg);
8
- }
9
- }
10
- module.exports = obj => {
11
- if (_.isPlainObject(obj)) {
12
- Object.keys(obj).forEach(validateKey);
13
- } else {
14
- throw new Error(`Data must be an object. Found ${Array.isArray(obj) ? 'array' : typeof obj}.
15
- 'See https://github.com/typicode/json-server for example.`);
16
- }
17
- };
@@ -1,8 +0,0 @@
1
- "use strict";
2
-
3
- module.exports = function write(db) {
4
- return (req, res, next) => {
5
- db.write();
6
- next();
7
- };
8
- };
@@ -1,26 +0,0 @@
1
- "use strict";
2
-
3
- module.exports = {
4
- getPage
5
- };
6
- function getPage(array, page, perPage) {
7
- const obj = {};
8
- const start = (page - 1) * perPage;
9
- const end = page * perPage;
10
- obj.items = array.slice(start, end);
11
- if (obj.items.length === 0) {
12
- return obj;
13
- }
14
- if (page > 1) {
15
- obj.prev = page - 1;
16
- }
17
- if (end < array.length) {
18
- obj.next = page + 1;
19
- }
20
- if (obj.items.length !== array.length) {
21
- obj.current = page;
22
- obj.first = 1;
23
- obj.last = Math.ceil(array.length / perPage);
24
- }
25
- return obj;
26
- }
Binary file