json-server 0.17.4 → 1.0.0-alpha.3

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/lib/cli/bin.js DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
-
4
- require('please-upgrade-node')(require('../../package.json'));
5
- require('./')();
package/lib/cli/index.js DELETED
@@ -1,77 +0,0 @@
1
- "use strict";
2
-
3
- const yargs = require('yargs');
4
- const run = require('./run');
5
- const pkg = require('../../package.json');
6
- module.exports = function () {
7
- const argv = yargs.config('config').usage('$0 [options] <source>').options({
8
- port: {
9
- alias: 'p',
10
- description: 'Set port',
11
- default: 3000
12
- },
13
- host: {
14
- alias: 'H',
15
- description: 'Set host',
16
- default: 'localhost'
17
- },
18
- watch: {
19
- alias: 'w',
20
- description: 'Watch file(s)'
21
- },
22
- routes: {
23
- alias: 'r',
24
- description: 'Path to routes file'
25
- },
26
- middlewares: {
27
- alias: 'm',
28
- array: true,
29
- description: 'Paths to middleware files'
30
- },
31
- static: {
32
- alias: 's',
33
- description: 'Set static files directory'
34
- },
35
- 'read-only': {
36
- alias: 'ro',
37
- description: 'Allow only GET requests'
38
- },
39
- 'no-cors': {
40
- alias: 'nc',
41
- description: 'Disable Cross-Origin Resource Sharing'
42
- },
43
- 'no-gzip': {
44
- alias: 'ng',
45
- description: 'Disable GZIP Content-Encoding'
46
- },
47
- snapshots: {
48
- alias: 'S',
49
- description: 'Set snapshots directory',
50
- default: '.'
51
- },
52
- delay: {
53
- alias: 'd',
54
- description: 'Add delay to responses (ms)'
55
- },
56
- id: {
57
- alias: 'i',
58
- description: 'Set database id property (e.g. _id)',
59
- default: 'id'
60
- },
61
- foreignKeySuffix: {
62
- alias: 'fks',
63
- description: 'Set foreign key suffix (e.g. _id as in post_id)',
64
- default: 'Id'
65
- },
66
- quiet: {
67
- alias: 'q',
68
- description: 'Suppress log messages from output'
69
- },
70
- config: {
71
- alias: 'c',
72
- description: 'Path to config file',
73
- default: 'json-server.json'
74
- }
75
- }).boolean('watch').boolean('read-only').boolean('quiet').boolean('no-cors').boolean('no-gzip').help('help').alias('help', 'h').version(pkg.version).alias('version', 'v').example('$0 db.json', '').example('$0 file.js', '').example('$0 http://example.com/db.json', '').epilog('https://github.com/typicode/json-server').require(1, 'Missing <source> argument').argv;
76
- run(argv);
77
- };
package/lib/cli/run.js DELETED
@@ -1,213 +0,0 @@
1
- "use strict";
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const jph = require('json-parse-helpfulerror');
6
- const _ = require('lodash');
7
- const chalk = require('chalk');
8
- const enableDestroy = require('server-destroy');
9
- const pause = require('connect-pause');
10
- const is = require('./utils/is');
11
- const load = require('./utils/load');
12
- const jsonServer = require('../server');
13
- function prettyPrint(argv, object, rules) {
14
- const root = `http://${argv.host}:${argv.port}`;
15
- console.log();
16
- console.log(chalk.bold(' Resources'));
17
- for (const prop in object) {
18
- // skip printing $schema nodes
19
- if (prop === '$schema') continue;
20
- console.log(` ${root}/${prop}`);
21
- }
22
- if (rules) {
23
- console.log();
24
- console.log(chalk.bold(' Other routes'));
25
- for (const rule in rules) {
26
- console.log(` ${rule} -> ${rules[rule]}`);
27
- }
28
- }
29
- console.log();
30
- console.log(chalk.bold(' Home'));
31
- console.log(` ${root}`);
32
- console.log();
33
- }
34
- function createApp(db, routes, middlewares, argv) {
35
- const app = jsonServer.create();
36
- const {
37
- foreignKeySuffix
38
- } = argv;
39
- const router = jsonServer.router(db, foreignKeySuffix ? {
40
- foreignKeySuffix
41
- } : undefined);
42
- const defaultsOpts = {
43
- logger: !argv.quiet,
44
- readOnly: argv.readOnly,
45
- noCors: argv.noCors,
46
- noGzip: argv.noGzip,
47
- bodyParser: true
48
- };
49
- if (argv.static) {
50
- defaultsOpts.static = path.join(process.cwd(), argv.static);
51
- }
52
- const defaults = jsonServer.defaults(defaultsOpts);
53
- app.use(defaults);
54
- if (routes) {
55
- const rewriter = jsonServer.rewriter(routes);
56
- app.use(rewriter);
57
- }
58
- if (middlewares) {
59
- app.use(middlewares);
60
- }
61
- if (argv.delay) {
62
- app.use(pause(argv.delay));
63
- }
64
- router.db._.id = argv.id;
65
- app.db = router.db;
66
- app.use(router);
67
- return app;
68
- }
69
- module.exports = function (argv) {
70
- const source = argv._[0];
71
- let app;
72
- let server;
73
- if (!fs.existsSync(argv.snapshots)) {
74
- console.log(`Error: snapshots directory ${argv.snapshots} doesn't exist`);
75
- process.exit(1);
76
- }
77
-
78
- // noop log fn
79
- if (argv.quiet) {
80
- console.log = () => {};
81
- }
82
- console.log();
83
- console.log(chalk.cyan(' \\{^_^}/ hi!'));
84
- function start(cb) {
85
- console.log();
86
- console.log(chalk.gray(' Loading', source));
87
- server = undefined;
88
-
89
- // create db and load object, JSON file, JS or HTTP database
90
- return load(source).then(db => {
91
- // Load additional routes
92
- let routes;
93
- if (argv.routes) {
94
- console.log(chalk.gray(' Loading', argv.routes));
95
- routes = JSON.parse(fs.readFileSync(argv.routes));
96
- }
97
-
98
- // Load middlewares
99
- let middlewares;
100
- if (argv.middlewares) {
101
- middlewares = argv.middlewares.map(function (m) {
102
- console.log(chalk.gray(' Loading', m));
103
- return require(path.resolve(m));
104
- });
105
- }
106
-
107
- // Done
108
- console.log(chalk.gray(' Done'));
109
-
110
- // Create app and server
111
- app = createApp(db, routes, middlewares, argv);
112
- server = app.listen(argv.port, argv.host);
113
-
114
- // Enhance with a destroy function
115
- enableDestroy(server);
116
-
117
- // Display server informations
118
- prettyPrint(argv, db.getState(), routes);
119
-
120
- // Catch and handle any error occurring in the server process
121
- process.on('uncaughtException', error => {
122
- if (error.errno === 'EADDRINUSE') console.log(chalk.red(`Cannot bind to the port ${error.port}. Please specify another port number either through --port argument or through the json-server.json configuration file`));else console.log('Some error occurred', error);
123
- process.exit(1);
124
- });
125
- });
126
- }
127
-
128
- // Start server
129
- start().then(() => {
130
- // Snapshot
131
- console.log(chalk.gray(' Type s + enter at any time to create a snapshot of the database'));
132
-
133
- // Support nohup
134
- // https://github.com/typicode/json-server/issues/221
135
- process.stdin.on('error', () => {
136
- console.log(` Error, can't read from stdin`);
137
- console.log(` Creating a snapshot from the CLI won't be possible`);
138
- });
139
- process.stdin.setEncoding('utf8');
140
- process.stdin.on('data', chunk => {
141
- if (chunk.trim().toLowerCase() === 's') {
142
- const filename = `db-${Date.now()}.json`;
143
- const file = path.join(argv.snapshots, filename);
144
- const state = app.db.getState();
145
- fs.writeFileSync(file, JSON.stringify(state, null, 2), 'utf-8');
146
- console.log(` Saved snapshot to ${path.relative(process.cwd(), file)}\n`);
147
- }
148
- });
149
-
150
- // Watch files
151
- if (argv.watch) {
152
- console.log(chalk.gray(' Watching...'));
153
- console.log();
154
- const source = argv._[0];
155
-
156
- // Can't watch URL
157
- if (is.URL(source)) throw new Error("Can't watch URL");
158
-
159
- // Watch .js or .json file
160
- // Since lowdb uses atomic writing, directory is watched instead of file
161
- const watchedDir = path.dirname(source);
162
- let readError = false;
163
- fs.watch(watchedDir, (event, file) => {
164
- // https://github.com/typicode/json-server/issues/420
165
- // file can be null
166
- if (file) {
167
- const watchedFile = path.resolve(watchedDir, file);
168
- if (watchedFile === path.resolve(source)) {
169
- if (is.FILE(watchedFile)) {
170
- let obj;
171
- try {
172
- obj = jph.parse(fs.readFileSync(watchedFile));
173
- if (readError) {
174
- console.log(chalk.green(` Read error has been fixed :)`));
175
- readError = false;
176
- }
177
- } catch (e) {
178
- readError = true;
179
- console.log(chalk.red(` Error reading ${watchedFile}`));
180
- console.error(e.message);
181
- return;
182
- }
183
-
184
- // Compare .json file content with in memory database
185
- const isDatabaseDifferent = !_.isEqual(obj, app.db.getState());
186
- if (isDatabaseDifferent) {
187
- console.log(chalk.gray(` ${source} has changed, reloading...`));
188
- server && server.destroy(() => start());
189
- }
190
- }
191
- }
192
- }
193
- });
194
-
195
- // Watch routes
196
- if (argv.routes) {
197
- const watchedDir = path.dirname(argv.routes);
198
- fs.watch(watchedDir, (event, file) => {
199
- if (file) {
200
- const watchedFile = path.resolve(watchedDir, file);
201
- if (watchedFile === path.resolve(argv.routes)) {
202
- console.log(chalk.gray(` ${argv.routes} has changed, reloading...`));
203
- server && server.destroy(() => start());
204
- }
205
- }
206
- });
207
- }
208
- }
209
- }).catch(err => {
210
- console.log(err);
211
- process.exit(1);
212
- });
213
- };
@@ -1,16 +0,0 @@
1
- "use strict";
2
-
3
- module.exports = {
4
- FILE,
5
- JS,
6
- URL
7
- };
8
- function FILE(s) {
9
- return !URL(s) && /\.json$/.test(s);
10
- }
11
- function JS(s) {
12
- return !URL(s) && /\.c?js$/.test(s);
13
- }
14
- function URL(s) {
15
- return /^(http|https):/.test(s);
16
- }
@@ -1,69 +0,0 @@
1
- "use strict";
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const http = require('http');
6
- const https = require('https');
7
- const low = require('lowdb');
8
- const FileAsync = require('lowdb/adapters/FileAsync');
9
- const Memory = require('lowdb/adapters/Memory');
10
- const is = require('./is');
11
- const chalk = require('chalk');
12
- const example = {
13
- posts: [{
14
- id: 1,
15
- title: 'json-server',
16
- author: 'typicode'
17
- }],
18
- comments: [{
19
- id: 1,
20
- body: 'some comment',
21
- postId: 1
22
- }],
23
- profile: {
24
- name: 'typicode'
25
- }
26
- };
27
- module.exports = function (source) {
28
- return new Promise((resolve, reject) => {
29
- if (is.FILE(source)) {
30
- if (!fs.existsSync(source)) {
31
- console.log(chalk.yellow(` Oops, ${source} doesn't seem to exist`));
32
- console.log(chalk.yellow(` Creating ${source} with some default data`));
33
- console.log();
34
- fs.writeFileSync(source, JSON.stringify(example, null, 2));
35
- }
36
- resolve(low(new FileAsync(source)));
37
- } else if (is.URL(source)) {
38
- // Normalize the source into a URL object.
39
- const sourceUrl = new URL(source);
40
- // Pick the client based on the protocol scheme
41
- const client = sourceUrl.protocol === 'https:' ? https : http;
42
- client.get(sourceUrl, res => {
43
- let dbData = '';
44
- res.on('data', data => {
45
- dbData += data;
46
- });
47
- res.on('end', () => {
48
- resolve(low(new Memory()).setState(JSON.parse(dbData)));
49
- });
50
- }).on('error', error => {
51
- return reject(error);
52
- });
53
- } else if (is.JS(source)) {
54
- // Clear cache
55
- const filename = path.resolve(source);
56
- delete require.cache[filename];
57
- const dataFn = require(filename);
58
- if (typeof dataFn !== 'function') {
59
- throw new Error('The database is a JavaScript file but the export is not a function.');
60
- }
61
-
62
- // Run dataFn to generate data
63
- const data = dataFn();
64
- resolve(low(new Memory()).setState(data));
65
- } else {
66
- throw new Error(`Unsupported source ${source}`);
67
- }
68
- });
69
- };
@@ -1,9 +0,0 @@
1
- "use strict";
2
-
3
- const bodyParser = require('body-parser');
4
- module.exports = [bodyParser.json({
5
- limit: '10mb',
6
- extended: false
7
- }), bodyParser.urlencoded({
8
- extended: false
9
- })];
@@ -1,73 +0,0 @@
1
- "use strict";
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const express = require('express');
6
- const logger = require('morgan');
7
- const cors = require('cors');
8
- const compression = require('compression');
9
- const errorhandler = require('errorhandler');
10
- const bodyParser = require('./body-parser');
11
- module.exports = function (opts) {
12
- const userDir = path.join(process.cwd(), 'public');
13
- const defaultDir = path.join(__dirname, '../../public');
14
- const staticDir = fs.existsSync(userDir) ? userDir : defaultDir;
15
- opts = Object.assign({
16
- logger: true,
17
- static: staticDir
18
- }, opts);
19
- const arr = [];
20
-
21
- // Compress all requests
22
- if (!opts.noGzip) {
23
- arr.push(compression());
24
- }
25
-
26
- // Enable CORS for all the requests, including static files
27
- if (!opts.noCors) {
28
- arr.push(cors({
29
- origin: true,
30
- credentials: true
31
- }));
32
- }
33
- if (process.env.NODE_ENV === 'development') {
34
- // only use in development
35
- arr.push(errorhandler());
36
- }
37
-
38
- // Serve static files
39
- arr.push(express.static(opts.static));
40
-
41
- // Logger
42
- if (opts.logger) {
43
- arr.push(logger('dev', {
44
- skip: req => process.env.NODE_ENV === 'test' || req.path === '/favicon.ico'
45
- }));
46
- }
47
-
48
- // No cache for IE
49
- // https://support.microsoft.com/en-us/kb/234067
50
- arr.push((req, res, next) => {
51
- res.header('Cache-Control', 'no-cache');
52
- res.header('Pragma', 'no-cache');
53
- res.header('Expires', '-1');
54
- next();
55
- });
56
-
57
- // Read-only
58
- if (opts.readOnly) {
59
- arr.push((req, res, next) => {
60
- if (req.method === 'GET') {
61
- next(); // Continue
62
- } else {
63
- res.sendStatus(403); // Forbidden
64
- }
65
- });
66
- }
67
-
68
- // Add middlewares
69
- if (opts.bodyParser) {
70
- arr.push(bodyParser);
71
- }
72
- return arr;
73
- };
@@ -1,10 +0,0 @@
1
- "use strict";
2
-
3
- const express = require('express');
4
- module.exports = {
5
- create: () => express().set('json spaces', 2),
6
- defaults: require('./defaults'),
7
- router: require('./router'),
8
- rewriter: require('./rewriter'),
9
- bodyParser: require('./body-parser')
10
- };
@@ -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
- };