360-mock-server 1.0.1 ā 1.1.0
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/README.md +48 -41
- package/bin/cli.js +193 -72
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# š Mock
|
|
1
|
+
# š 360 Mock Server
|
|
2
2
|
|
|
3
3
|
Zero-config dynamic mock REST API server. Perfect for frontend development when backend isn't ready.
|
|
4
4
|
|
|
@@ -8,24 +8,58 @@ Zero-config dynamic mock REST API server. Perfect for frontend development when
|
|
|
8
8
|
# Global installation (recommended)
|
|
9
9
|
npm install -g 360-mock-server
|
|
10
10
|
|
|
11
|
-
# Or use npx
|
|
12
|
-
npx 360-mock-server
|
|
11
|
+
# Or use npx (no install needed)
|
|
12
|
+
npx 360-mock-server
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Quick Start
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
#
|
|
19
|
-
|
|
18
|
+
# Just run one command - that's it!
|
|
19
|
+
npx 360-mock-server
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This will:
|
|
23
|
+
1. ā
Create `mock-data.json` automatically
|
|
24
|
+
2. ā
Start the server on port 3000
|
|
25
|
+
3. ā
Open interactive mode for easy API requests
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Interactive Mode (Recommended!)
|
|
20
30
|
|
|
21
|
-
|
|
22
|
-
360-mock init
|
|
31
|
+
After running `npx 360-mock-server`, you get an interactive prompt:
|
|
23
32
|
|
|
24
|
-
# Start server
|
|
25
|
-
360-mock start
|
|
26
33
|
```
|
|
34
|
+
360-mock> POST /users {"name": "Ali", "email": "ali@test.com"}
|
|
35
|
+
š¤ POST /users
|
|
36
|
+
ā
Status: 201
|
|
37
|
+
{ "id": 1234567890, "name": "Ali", "email": "ali@test.com" }
|
|
38
|
+
|
|
39
|
+
360-mock> GET /users
|
|
40
|
+
š¤ GET /users
|
|
41
|
+
ā
Status: 200
|
|
42
|
+
[{ "id": 1234567890, "name": "Ali", "email": "ali@test.com" }]
|
|
43
|
+
|
|
44
|
+
360-mock> POST /products {"name": "Laptop", "price": 999}
|
|
45
|
+
360-mock> GET /products
|
|
46
|
+
360-mock> DELETE /users/1234567890
|
|
47
|
+
360-mock> exit
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Interactive Commands
|
|
27
51
|
|
|
28
|
-
|
|
52
|
+
| Command | Example |
|
|
53
|
+
|---------|---------|
|
|
54
|
+
| GET | `GET /users` |
|
|
55
|
+
| POST | `POST /users {"name": "Ali"}` |
|
|
56
|
+
| PUT | `PUT /users/123 {"name": "Updated"}` |
|
|
57
|
+
| PATCH | `PATCH /users/123 {"email": "new@test.com"}` |
|
|
58
|
+
| DELETE | `DELETE /users/123` |
|
|
59
|
+
| list | Show all resources |
|
|
60
|
+
| clear | Clear screen |
|
|
61
|
+
| help | Show help |
|
|
62
|
+
| exit | Exit |
|
|
29
63
|
|
|
30
64
|
---
|
|
31
65
|
|
|
@@ -33,50 +67,23 @@ Server runs at `http://localhost:3000`
|
|
|
33
67
|
|
|
34
68
|
| Command | Description |
|
|
35
69
|
|---------|-------------|
|
|
36
|
-
| `360-mock
|
|
70
|
+
| `360-mock` | Start server + interactive mode |
|
|
71
|
+
| `360-mock start` | Start server only |
|
|
37
72
|
| `360-mock init` | Create mock-data.json |
|
|
38
73
|
| `360-mock list` | Show available resources |
|
|
39
74
|
| `360-mock reset` | Clear all data |
|
|
40
|
-
| `360-mock req` | Make API requests |
|
|
41
75
|
|
|
42
76
|
### Options
|
|
43
77
|
|
|
44
78
|
```bash
|
|
45
|
-
360-mock
|
|
46
|
-
360-mock
|
|
79
|
+
360-mock --port 4000 # Custom port
|
|
80
|
+
360-mock --file db.json # Custom data file
|
|
47
81
|
360-mock --help # Show help
|
|
48
82
|
360-mock --version # Show version
|
|
49
83
|
```
|
|
50
84
|
|
|
51
85
|
---
|
|
52
86
|
|
|
53
|
-
## Making API Requests (Easy Way!)
|
|
54
|
-
|
|
55
|
-
Use the built-in `req` command instead of curl:
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
# CREATE
|
|
59
|
-
360-mock req POST /users '{"name": "Ali", "email": "ali@test.com"}'
|
|
60
|
-
360-mock req POST /products '{"name": "Laptop", "price": 999}'
|
|
61
|
-
360-mock req POST /orders '{"product": "Laptop", "qty": 2}'
|
|
62
|
-
|
|
63
|
-
# READ ALL
|
|
64
|
-
360-mock req GET /users
|
|
65
|
-
360-mock req GET /products
|
|
66
|
-
|
|
67
|
-
# READ ONE
|
|
68
|
-
360-mock req GET /users/1234567890
|
|
69
|
-
|
|
70
|
-
# UPDATE
|
|
71
|
-
360-mock req PUT /users/1234567890 '{"name": "Ali Khan"}'
|
|
72
|
-
360-mock req PATCH /users/1234567890 '{"email": "ali.khan@test.com"}'
|
|
73
|
-
|
|
74
|
-
# DELETE
|
|
75
|
-
360-mock req DELETE /users/1234567890
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
87
|
## API Endpoints
|
|
81
88
|
|
|
82
89
|
### Any endpoint works automatically!
|
package/bin/cli.js
CHANGED
|
@@ -4,22 +4,204 @@ const { program } = require("commander");
|
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const fs = require("fs");
|
|
6
6
|
const http = require("http");
|
|
7
|
+
const readline = require("readline");
|
|
7
8
|
|
|
8
9
|
const packageJson = require("../package.json");
|
|
9
10
|
|
|
11
|
+
// Helper function for making HTTP requests
|
|
12
|
+
function makeRequest(method, endpoint, data, port = "3000") {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const upperMethod = method.toUpperCase();
|
|
15
|
+
const urlPath = endpoint.startsWith("/") ? endpoint : "/" + endpoint;
|
|
16
|
+
|
|
17
|
+
let jsonData = null;
|
|
18
|
+
if (data) {
|
|
19
|
+
try {
|
|
20
|
+
jsonData = typeof data === "string" ? JSON.parse(data) : data;
|
|
21
|
+
} catch (e) {
|
|
22
|
+
console.log(`ā Invalid JSON: ${data}`);
|
|
23
|
+
return resolve();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const reqOptions = {
|
|
28
|
+
hostname: "localhost",
|
|
29
|
+
port: port,
|
|
30
|
+
path: urlPath,
|
|
31
|
+
method: upperMethod,
|
|
32
|
+
headers: { "Content-Type": "application/json" },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
console.log(`\nš¤ ${upperMethod} ${urlPath}`);
|
|
36
|
+
if (jsonData) {
|
|
37
|
+
console.log(` Body: ${JSON.stringify(jsonData)}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const req = http.request(reqOptions, (res) => {
|
|
41
|
+
let body = "";
|
|
42
|
+
res.on("data", (chunk) => { body += chunk; });
|
|
43
|
+
res.on("end", () => {
|
|
44
|
+
const emoji = res.statusCode >= 200 && res.statusCode < 300 ? "ā
" : "ā";
|
|
45
|
+
console.log(`${emoji} Status: ${res.statusCode}`);
|
|
46
|
+
try {
|
|
47
|
+
const json = JSON.parse(body);
|
|
48
|
+
console.log(JSON.stringify(json, null, 2));
|
|
49
|
+
} catch {
|
|
50
|
+
console.log(body);
|
|
51
|
+
}
|
|
52
|
+
console.log("");
|
|
53
|
+
resolve();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
req.on("error", (error) => {
|
|
58
|
+
console.error(`ā Error: ${error.message}`);
|
|
59
|
+
console.log("š” Make sure server is running in another terminal: 360-mock start\n");
|
|
60
|
+
resolve();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (jsonData) req.write(JSON.stringify(jsonData));
|
|
64
|
+
req.end();
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Interactive REPL mode
|
|
69
|
+
function startInteractiveMode(port) {
|
|
70
|
+
const rl = readline.createInterface({
|
|
71
|
+
input: process.stdin,
|
|
72
|
+
output: process.stdout,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
console.log(`
|
|
76
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
77
|
+
ā š 360 Mock Server - Interactive Mode ā
|
|
78
|
+
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā£
|
|
79
|
+
ā Server: http://localhost:${port} ā
|
|
80
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
81
|
+
|
|
82
|
+
š Commands:
|
|
83
|
+
GET /users - Get all users
|
|
84
|
+
GET /users/123 - Get user by ID
|
|
85
|
+
POST /users {"name":"Ali"} - Create user
|
|
86
|
+
PUT /users/123 {"name":"Updated"} - Replace user
|
|
87
|
+
PATCH /users/123 {"name":"New"} - Update user
|
|
88
|
+
DELETE /users/123 - Delete user
|
|
89
|
+
|
|
90
|
+
list - Show resources
|
|
91
|
+
clear - Clear screen
|
|
92
|
+
help - Show this help
|
|
93
|
+
exit - Exit
|
|
94
|
+
|
|
95
|
+
`);
|
|
96
|
+
|
|
97
|
+
const prompt = () => {
|
|
98
|
+
rl.question("360-mock> ", async (input) => {
|
|
99
|
+
const trimmed = input.trim();
|
|
100
|
+
|
|
101
|
+
if (!trimmed) {
|
|
102
|
+
prompt();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Handle special commands
|
|
107
|
+
if (trimmed === "exit" || trimmed === "quit") {
|
|
108
|
+
console.log("ļæ½ Goodbye!");
|
|
109
|
+
rl.close();
|
|
110
|
+
process.exit(0);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (trimmed === "help") {
|
|
114
|
+
console.log(`
|
|
115
|
+
š Commands:
|
|
116
|
+
GET /users - Get all users
|
|
117
|
+
GET /users/123 - Get user by ID
|
|
118
|
+
POST /users {"name":"Ali"} - Create user
|
|
119
|
+
PUT /users/123 {"name":"Updated"} - Replace user
|
|
120
|
+
PATCH /users/123 {"name":"New"} - Update user
|
|
121
|
+
DELETE /users/123 - Delete user
|
|
122
|
+
|
|
123
|
+
list - Show resources
|
|
124
|
+
clear - Clear screen
|
|
125
|
+
exit - Exit
|
|
126
|
+
`);
|
|
127
|
+
prompt();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (trimmed === "clear" || trimmed === "cls") {
|
|
132
|
+
console.clear();
|
|
133
|
+
prompt();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (trimmed === "list") {
|
|
138
|
+
await makeRequest("GET", "/", null, port);
|
|
139
|
+
prompt();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Parse API request: METHOD /endpoint {json}
|
|
144
|
+
const match = trimmed.match(/^(GET|POST|PUT|PATCH|DELETE)\s+(\/\S*)\s*(.*)$/i);
|
|
145
|
+
|
|
146
|
+
if (match) {
|
|
147
|
+
const [, method, endpoint, jsonStr] = match;
|
|
148
|
+
await makeRequest(method, endpoint, jsonStr || null, port);
|
|
149
|
+
} else {
|
|
150
|
+
console.log(`ā Invalid command. Type 'help' for usage.\n`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
prompt();
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
prompt();
|
|
158
|
+
}
|
|
159
|
+
|
|
10
160
|
program
|
|
11
161
|
.name("360-mock")
|
|
12
|
-
.description("
|
|
162
|
+
.description("ļæ½š Dynamic Mock REST API Server - Zero Config")
|
|
13
163
|
.version(packageJson.version);
|
|
14
164
|
|
|
165
|
+
// Default command - start interactive mode
|
|
166
|
+
program
|
|
167
|
+
.command("run", { isDefault: true })
|
|
168
|
+
.description("Start server + interactive mode (recommended)")
|
|
169
|
+
.option("-p, --port <port>", "Port number", "3000")
|
|
170
|
+
.option("-f, --file <filename>", "Data file name", "mock-data.json")
|
|
171
|
+
.action((options) => {
|
|
172
|
+
const dataFile = path.join(process.cwd(), options.file);
|
|
173
|
+
|
|
174
|
+
// Create data file if not exists
|
|
175
|
+
if (!fs.existsSync(dataFile)) {
|
|
176
|
+
fs.writeFileSync(dataFile, JSON.stringify({}, null, 2));
|
|
177
|
+
console.log(`ā
Created ${options.file}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
process.env.PORT = options.port;
|
|
181
|
+
process.env.DATA_FILE = dataFile;
|
|
182
|
+
|
|
183
|
+
// Start server
|
|
184
|
+
require("../lib/server");
|
|
185
|
+
|
|
186
|
+
// Start interactive mode after a short delay
|
|
187
|
+
setTimeout(() => {
|
|
188
|
+
startInteractiveMode(options.port);
|
|
189
|
+
}, 500);
|
|
190
|
+
});
|
|
191
|
+
|
|
15
192
|
program
|
|
16
193
|
.command("start")
|
|
17
|
-
.description("Start the mock server")
|
|
194
|
+
.description("Start the mock server only (no interactive mode)")
|
|
18
195
|
.option("-p, --port <port>", "Port number", "3000")
|
|
19
196
|
.option("-f, --file <filename>", "Data file name", "mock-data.json")
|
|
20
197
|
.action((options) => {
|
|
198
|
+
const dataFile = path.join(process.cwd(), options.file);
|
|
199
|
+
if (!fs.existsSync(dataFile)) {
|
|
200
|
+
fs.writeFileSync(dataFile, JSON.stringify({}, null, 2));
|
|
201
|
+
console.log(`ā
Created ${options.file}`);
|
|
202
|
+
}
|
|
21
203
|
process.env.PORT = options.port;
|
|
22
|
-
process.env.DATA_FILE =
|
|
204
|
+
process.env.DATA_FILE = dataFile;
|
|
23
205
|
require("../lib/server");
|
|
24
206
|
});
|
|
25
207
|
|
|
@@ -33,15 +215,9 @@ program
|
|
|
33
215
|
console.log(`ā ļø ${options.file} already exists!`);
|
|
34
216
|
return;
|
|
35
217
|
}
|
|
36
|
-
|
|
37
|
-
_info: "Add your mock data here. Each key becomes an API endpoint.",
|
|
38
|
-
users: [],
|
|
39
|
-
products: []
|
|
40
|
-
};
|
|
41
|
-
fs.writeFileSync(filePath, JSON.stringify(initialData, null, 2));
|
|
218
|
+
fs.writeFileSync(filePath, JSON.stringify({}, null, 2));
|
|
42
219
|
console.log(`ā
Created ${options.file}`);
|
|
43
|
-
console.log(`\n
|
|
44
|
-
console.log(`\nš Run '360-mock start' to start the server`);
|
|
220
|
+
console.log(`\n Run '360-mock' to start the server`);
|
|
45
221
|
});
|
|
46
222
|
|
|
47
223
|
program
|
|
@@ -65,13 +241,13 @@ program
|
|
|
65
241
|
.action((options) => {
|
|
66
242
|
const filePath = path.join(process.cwd(), options.file);
|
|
67
243
|
if (!fs.existsSync(filePath)) {
|
|
68
|
-
console.log(`ā ļø ${options.file} not found
|
|
244
|
+
console.log(`ā ļø ${options.file} not found!`);
|
|
69
245
|
return;
|
|
70
246
|
}
|
|
71
247
|
try {
|
|
72
248
|
const data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
73
249
|
const resources = Object.keys(data).filter(k => !k.startsWith("_"));
|
|
74
|
-
console.log(`\nš¦ Available Resources
|
|
250
|
+
console.log(`\nš¦ Available Resources:\n`);
|
|
75
251
|
if (resources.length === 0) {
|
|
76
252
|
console.log(" No resources yet. POST to any endpoint to create one!");
|
|
77
253
|
} else {
|
|
@@ -86,68 +262,13 @@ program
|
|
|
86
262
|
}
|
|
87
263
|
});
|
|
88
264
|
|
|
89
|
-
// API Request command
|
|
265
|
+
// API Request command (for non-interactive use)
|
|
90
266
|
program
|
|
91
267
|
.command("req <method> <endpoint> [data]")
|
|
92
|
-
.
|
|
93
|
-
.description("Make API request (GET, POST, PUT, PATCH, DELETE)")
|
|
268
|
+
.description("Make single API request")
|
|
94
269
|
.option("-p, --port <port>", "Server port", "3000")
|
|
95
|
-
.action((method, endpoint, data, options) => {
|
|
96
|
-
|
|
97
|
-
const path = endpoint.startsWith("/") ? endpoint : "/" + endpoint;
|
|
98
|
-
|
|
99
|
-
let jsonData = null;
|
|
100
|
-
if (data) {
|
|
101
|
-
try {
|
|
102
|
-
jsonData = JSON.parse(data);
|
|
103
|
-
} catch (e) {
|
|
104
|
-
console.log(`ā Invalid JSON: ${data}`);
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const reqOptions = {
|
|
110
|
-
hostname: "localhost",
|
|
111
|
-
port: options.port,
|
|
112
|
-
path: path,
|
|
113
|
-
method: upperMethod,
|
|
114
|
-
headers: {
|
|
115
|
-
"Content-Type": "application/json",
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
console.log(`\nš¤ ${upperMethod} ${path}`);
|
|
120
|
-
if (jsonData) {
|
|
121
|
-
console.log(` Body: ${JSON.stringify(jsonData, null, 2)}`);
|
|
122
|
-
}
|
|
123
|
-
console.log("");
|
|
124
|
-
|
|
125
|
-
const req = http.request(reqOptions, (res) => {
|
|
126
|
-
let body = "";
|
|
127
|
-
res.on("data", (chunk) => { body += chunk; });
|
|
128
|
-
res.on("end", () => {
|
|
129
|
-
const statusEmoji = res.statusCode >= 200 && res.statusCode < 300 ? "ā
" : "ā";
|
|
130
|
-
console.log(`${statusEmoji} Status: ${res.statusCode}`);
|
|
131
|
-
console.log(`š„ Response:`);
|
|
132
|
-
try {
|
|
133
|
-
const json = JSON.parse(body);
|
|
134
|
-
console.log(JSON.stringify(json, null, 2));
|
|
135
|
-
} catch {
|
|
136
|
-
console.log(body);
|
|
137
|
-
}
|
|
138
|
-
console.log("");
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
req.on("error", (error) => {
|
|
143
|
-
console.error(`ā Error: ${error.message}`);
|
|
144
|
-
console.log("\nš” Make sure the server is running: 360-mock start\n");
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
if (jsonData) {
|
|
148
|
-
req.write(JSON.stringify(jsonData));
|
|
149
|
-
}
|
|
150
|
-
req.end();
|
|
270
|
+
.action(async (method, endpoint, data, options) => {
|
|
271
|
+
await makeRequest(method, endpoint, data, options.port);
|
|
151
272
|
});
|
|
152
273
|
|
|
153
274
|
program.parse();
|