json-server 1.0.0-beta.3 → 1.0.0-beta.4
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 +25 -19
- package/lib/app.js +9 -9
- package/lib/bin.js +48 -48
- package/lib/service.js +17 -27
- package/package.json +42 -47
- package/views/index.html +73 -75
- package/lib/app.d.ts +0 -8
- package/lib/bin.d.ts +0 -2
- package/lib/observer.d.ts +0 -11
- package/lib/service.d.ts +0 -38
package/README.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# JSON-Server
|
|
2
2
|
|
|
3
3
|
[](https://github.com/typicode/json-server/actions/workflows/node.js.yml)
|
|
4
4
|
|
|
5
5
|
> [!IMPORTANT]
|
|
6
6
|
> Viewing beta v1 documentation – usable but expect breaking changes. For stable version, see [here](https://github.com/typicode/json-server/tree/v0)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
> [!NOTE]
|
|
9
|
+
> Using React ⚛️ ? Check my new project [MistCSS](https://github.com/typicode/mistcss) to write type-safe styles (works with TailwindCSS)
|
|
9
10
|
|
|
10
11
|
## Install
|
|
11
12
|
|
|
@@ -40,15 +41,15 @@ Create a `db.json` or `db.json5` file
|
|
|
40
41
|
```json5
|
|
41
42
|
{
|
|
42
43
|
posts: [
|
|
43
|
-
{ id:
|
|
44
|
-
{ id:
|
|
44
|
+
{ id: "1", title: "a title", views: 100 },
|
|
45
|
+
{ id: "2", title: "another title", views: 200 },
|
|
45
46
|
],
|
|
46
47
|
comments: [
|
|
47
|
-
{ id:
|
|
48
|
-
{ id:
|
|
48
|
+
{ id: "1", text: "a comment about post 1", postId: "1" },
|
|
49
|
+
{ id: "2", text: "another comment about post 1", postId: "1" },
|
|
49
50
|
],
|
|
50
51
|
profile: {
|
|
51
|
-
name:
|
|
52
|
+
name: "typicode",
|
|
52
53
|
},
|
|
53
54
|
}
|
|
54
55
|
```
|
|
@@ -78,27 +79,32 @@ Run `json-server --help` for a list of options
|
|
|
78
79
|
|
|
79
80
|
## Sponsors ✨
|
|
80
81
|
|
|
81
|
-
|
|
82
|
-
| :---: |
|
|
83
|
-
| <a href="https://mockend.com/" target="_blank"><img src="https://jsonplaceholder.typicode.com/mockend.svg" height="100px"></a> |
|
|
84
|
-
| <a href="https://zuplo.link/json-server-gh"><img src="https://github.com/typicode/json-server/assets/5502029/928b7526-0fdf-46ae-80d9-27fa0ef5f430"></a> |
|
|
82
|
+
### Gold
|
|
85
83
|
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
84
|
+
| |
|
|
85
|
+
| :--------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
86
|
+
| <a href="https://mockend.com/" target="_blank"><img src="https://jsonplaceholder.typicode.com/mockend.svg" height="100px"></a> |
|
|
87
|
+
| <a href="https://zuplo.link/json-server-gh"><img src="https://github.com/user-attachments/assets/adfee31f-a8b6-4684-9a9b-af4f03ac5b75" height="100px"></a> |
|
|
88
|
+
| <a href="https://www.mintlify.com/"><img src="https://github.com/user-attachments/assets/bcc8cc48-b2d9-4577-8939-1eb4196b7cc5" height="100px"></a> |
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
|
93
|
-
|
|
|
90
|
+
### Silver
|
|
91
|
+
|
|
92
|
+
| |
|
|
93
|
+
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
94
|
+
| <a href="https://requestly.com?utm_source=githubsponsor&utm_medium=jsonserver&utm_campaign=jsonserver"><img src="https://github.com/user-attachments/assets/f7e7b3cf-97e2-46b8-81c8-cb3992662a1c" style="height:70px; width:auto;"></a> |
|
|
94
95
|
|
|
96
|
+
### Bronze
|
|
97
|
+
|
|
98
|
+
| | |
|
|
99
|
+
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
100
|
+
| <a href="https://www.storyblok.com/" target="_blank"><img src="https://github.com/typicode/json-server/assets/5502029/c6b10674-4ada-4616-91b8-59d30046b45a" height="35px"></a> | <a href="https://betterstack.com/" target="_blank"><img src="https://github.com/typicode/json-server/assets/5502029/44679f8f-9671-470d-b77e-26d90b90cbdc" height="35px"></a> |
|
|
95
101
|
|
|
96
102
|
[Become a sponsor and have your company logo here](https://github.com/users/typicode/sponsorship)
|
|
97
103
|
|
|
98
104
|
## Sponsorware
|
|
99
105
|
|
|
100
106
|
> [!NOTE]
|
|
101
|
-
> This project uses the [Fair Source License](https://fair.io/). Only organizations with 3+ users are kindly asked to contribute a small amount through sponsorship [sponsor](https://github.com/sponsors/typicode) for usage.
|
|
107
|
+
> This project uses the [Fair Source License](https://fair.io/). Only organizations with 3+ users are kindly asked to contribute a small amount through sponsorship [sponsor](https://github.com/sponsors/typicode) for usage. **This license helps keep the project sustainable and healthy, benefiting everyone.**
|
|
102
108
|
>
|
|
103
109
|
> For more information, FAQs, and the rationale behind this, visit [https://fair.io/](https://fair.io/).
|
|
104
110
|
|
package/lib/app.js
CHANGED
|
@@ -3,9 +3,10 @@ import { fileURLToPath } from 'node:url';
|
|
|
3
3
|
import { App } from '@tinyhttp/app';
|
|
4
4
|
import { cors } from '@tinyhttp/cors';
|
|
5
5
|
import { Eta } from 'eta';
|
|
6
|
+
import { Low } from 'lowdb';
|
|
6
7
|
import { json } from 'milliparsec';
|
|
7
8
|
import sirv from 'sirv';
|
|
8
|
-
import { isItem, Service } from
|
|
9
|
+
import { isItem, Service } from "./service.js";
|
|
9
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const isProduction = process.env['NODE_ENV'] === 'production';
|
|
11
12
|
const eta = new Eta({
|
|
@@ -33,22 +34,21 @@ export function createApp(db, options = {}) {
|
|
|
33
34
|
})
|
|
34
35
|
.options('*', cors());
|
|
35
36
|
// Body parser
|
|
36
|
-
// @ts-expect-error expected
|
|
37
37
|
app.use(json());
|
|
38
38
|
app.get('/', (_req, res) => res.send(eta.render('index.html', { data: db.data })));
|
|
39
39
|
app.get('/:name', (req, res, next) => {
|
|
40
40
|
const { name = '' } = req.params;
|
|
41
|
-
const query =
|
|
42
|
-
|
|
41
|
+
const query = {};
|
|
42
|
+
Object.keys(req.query).forEach((key) => {
|
|
43
|
+
let value = req.query[key];
|
|
43
44
|
if (['_start', '_end', '_limit', '_page', '_per_page'].includes(key) &&
|
|
44
45
|
typeof value === 'string') {
|
|
45
|
-
|
|
46
|
+
value = parseInt(value);
|
|
46
47
|
}
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
if (!Number.isNaN(value)) {
|
|
49
|
+
query[key] = value;
|
|
49
50
|
}
|
|
50
|
-
})
|
|
51
|
-
.filter(([, value]) => !Number.isNaN(value)));
|
|
51
|
+
});
|
|
52
52
|
res.locals['data'] = service.find(name, query);
|
|
53
53
|
next?.();
|
|
54
54
|
});
|
package/lib/bin.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from
|
|
3
|
-
import { extname } from
|
|
4
|
-
import { parseArgs } from
|
|
5
|
-
import chalk from
|
|
6
|
-
import { watch } from
|
|
7
|
-
import JSON5 from
|
|
8
|
-
import { Low } from
|
|
9
|
-
import { DataFile, JSONFile } from
|
|
10
|
-
import { fileURLToPath } from
|
|
11
|
-
import { createApp } from
|
|
12
|
-
import { Observer } from
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { extname } from "node:path";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { watch } from "chokidar";
|
|
7
|
+
import JSON5 from "json5";
|
|
8
|
+
import { Low } from "lowdb";
|
|
9
|
+
import { DataFile, JSONFile } from "lowdb/node";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { createApp } from "./app.js";
|
|
12
|
+
import { Observer } from "./observer.js";
|
|
13
13
|
function help() {
|
|
14
14
|
console.log(`Usage: json-server [options] <file>
|
|
15
15
|
|
|
@@ -27,44 +27,44 @@ function args() {
|
|
|
27
27
|
const { values, positionals } = parseArgs({
|
|
28
28
|
options: {
|
|
29
29
|
port: {
|
|
30
|
-
type:
|
|
31
|
-
short:
|
|
32
|
-
default: process.env[
|
|
30
|
+
type: "string",
|
|
31
|
+
short: "p",
|
|
32
|
+
default: process.env["PORT"] ?? "3000",
|
|
33
33
|
},
|
|
34
34
|
host: {
|
|
35
|
-
type:
|
|
36
|
-
short:
|
|
37
|
-
default: process.env[
|
|
35
|
+
type: "string",
|
|
36
|
+
short: "h",
|
|
37
|
+
default: process.env["HOST"] ?? "localhost",
|
|
38
38
|
},
|
|
39
39
|
static: {
|
|
40
|
-
type:
|
|
41
|
-
short:
|
|
40
|
+
type: "string",
|
|
41
|
+
short: "s",
|
|
42
42
|
multiple: true,
|
|
43
43
|
default: [],
|
|
44
44
|
},
|
|
45
45
|
help: {
|
|
46
|
-
type:
|
|
46
|
+
type: "boolean",
|
|
47
47
|
},
|
|
48
48
|
version: {
|
|
49
|
-
type:
|
|
49
|
+
type: "boolean",
|
|
50
50
|
},
|
|
51
51
|
// Deprecated
|
|
52
52
|
watch: {
|
|
53
|
-
type:
|
|
54
|
-
short:
|
|
53
|
+
type: "boolean",
|
|
54
|
+
short: "w",
|
|
55
55
|
},
|
|
56
56
|
},
|
|
57
57
|
allowPositionals: true,
|
|
58
58
|
});
|
|
59
59
|
// --version
|
|
60
60
|
if (values.version) {
|
|
61
|
-
const pkg = JSON.parse(readFileSync(fileURLToPath(new URL(
|
|
61
|
+
const pkg = JSON.parse(readFileSync(fileURLToPath(new URL("../package.json", import.meta.url)), "utf-8"));
|
|
62
62
|
console.log(pkg.version);
|
|
63
63
|
process.exit();
|
|
64
64
|
}
|
|
65
65
|
// Handle --watch
|
|
66
66
|
if (values.watch) {
|
|
67
|
-
console.log(chalk.yellow(
|
|
67
|
+
console.log(chalk.yellow("--watch/-w can be omitted, JSON Server 1+ watches for file changes by default"));
|
|
68
68
|
}
|
|
69
69
|
if (values.help || positionals.length === 0) {
|
|
70
70
|
help();
|
|
@@ -72,15 +72,15 @@ function args() {
|
|
|
72
72
|
}
|
|
73
73
|
// App args and options
|
|
74
74
|
return {
|
|
75
|
-
file: positionals[0] ??
|
|
75
|
+
file: positionals[0] ?? "",
|
|
76
76
|
port: parseInt(values.port),
|
|
77
77
|
host: values.host,
|
|
78
78
|
static: values.static,
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
catch (e) {
|
|
82
|
-
if (e.code ===
|
|
83
|
-
console.log(chalk.red(e.message.split(
|
|
82
|
+
if (e.code === "ERR_PARSE_ARGS_UNKNOWN_OPTION") {
|
|
83
|
+
console.log(chalk.red(e.message.split(".")[0]));
|
|
84
84
|
help();
|
|
85
85
|
process.exit(1);
|
|
86
86
|
}
|
|
@@ -95,12 +95,12 @@ if (!existsSync(file)) {
|
|
|
95
95
|
process.exit(1);
|
|
96
96
|
}
|
|
97
97
|
// Handle empty string JSON file
|
|
98
|
-
if (readFileSync(file,
|
|
99
|
-
writeFileSync(file,
|
|
98
|
+
if (readFileSync(file, "utf-8").trim() === "") {
|
|
99
|
+
writeFileSync(file, "{}");
|
|
100
100
|
}
|
|
101
101
|
// Set up database
|
|
102
102
|
let adapter;
|
|
103
|
-
if (extname(file) ===
|
|
103
|
+
if (extname(file) === ".json5") {
|
|
104
104
|
adapter = new DataFile(file, {
|
|
105
105
|
parse: JSON5.parse,
|
|
106
106
|
stringify: JSON5.stringify,
|
|
@@ -115,41 +115,41 @@ await db.read();
|
|
|
115
115
|
// Create app
|
|
116
116
|
const app = createApp(db, { logger: false, static: staticArr });
|
|
117
117
|
function logRoutes(data) {
|
|
118
|
-
console.log(chalk.bold(
|
|
118
|
+
console.log(chalk.bold("Endpoints:"));
|
|
119
119
|
if (Object.keys(data).length === 0) {
|
|
120
120
|
console.log(chalk.gray(`No endpoints found, try adding some data to ${file}`));
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
123
123
|
console.log(Object.keys(data)
|
|
124
124
|
.map((key) => `${chalk.gray(`http://${host}:${port}/`)}${chalk.blue(key)}`)
|
|
125
|
-
.join(
|
|
125
|
+
.join("\n"));
|
|
126
126
|
}
|
|
127
|
-
const kaomojis = [
|
|
127
|
+
const kaomojis = ["♡⸜(˶˃ ᵕ ˂˶)⸝♡", "♡( ◡‿◡ )", "( ˶ˆ ᗜ ˆ˵ )", "(˶ᵔ ᵕ ᵔ˶)"];
|
|
128
128
|
function randomItem(items) {
|
|
129
129
|
const index = Math.floor(Math.random() * items.length);
|
|
130
|
-
return items.at(index) ??
|
|
130
|
+
return items.at(index) ?? "";
|
|
131
131
|
}
|
|
132
132
|
app.listen(port, () => {
|
|
133
133
|
console.log([
|
|
134
134
|
chalk.bold(`JSON Server started on PORT :${port}`),
|
|
135
|
-
chalk.gray(
|
|
135
|
+
chalk.gray("Press CTRL-C to stop"),
|
|
136
136
|
chalk.gray(`Watching ${file}...`),
|
|
137
|
-
|
|
137
|
+
"",
|
|
138
138
|
chalk.magenta(randomItem(kaomojis)),
|
|
139
|
-
|
|
140
|
-
chalk.bold(
|
|
139
|
+
"",
|
|
140
|
+
chalk.bold("Index:"),
|
|
141
141
|
chalk.gray(`http://localhost:${port}/`),
|
|
142
|
-
|
|
143
|
-
chalk.bold(
|
|
144
|
-
chalk.gray(
|
|
145
|
-
|
|
146
|
-
].join(
|
|
142
|
+
"",
|
|
143
|
+
chalk.bold("Static files:"),
|
|
144
|
+
chalk.gray("Serving ./public directory if it exists"),
|
|
145
|
+
"",
|
|
146
|
+
].join("\n"));
|
|
147
147
|
logRoutes(db.data);
|
|
148
148
|
});
|
|
149
149
|
// Watch file for changes
|
|
150
|
-
if (process.env[
|
|
150
|
+
if (process.env["NODE_ENV"] !== "production") {
|
|
151
151
|
let writing = false; // true if the file is being written to by the app
|
|
152
|
-
let prevEndpoints =
|
|
152
|
+
let prevEndpoints = "";
|
|
153
153
|
observer.onWriteStart = () => {
|
|
154
154
|
writing = true;
|
|
155
155
|
};
|
|
@@ -169,12 +169,12 @@ if (process.env['NODE_ENV'] !== 'production') {
|
|
|
169
169
|
logRoutes(data);
|
|
170
170
|
}
|
|
171
171
|
};
|
|
172
|
-
watch(file).on(
|
|
172
|
+
watch(file).on("change", () => {
|
|
173
173
|
// Do no reload if the file is being written to by the app
|
|
174
174
|
if (!writing) {
|
|
175
175
|
db.read().catch((e) => {
|
|
176
176
|
if (e instanceof SyntaxError) {
|
|
177
|
-
return console.log(chalk.red([
|
|
177
|
+
return console.log(chalk.red(["", `Error parsing ${file}`, e.message].join("\n")));
|
|
178
178
|
}
|
|
179
179
|
console.log(e);
|
|
180
180
|
});
|
package/lib/service.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto';
|
|
2
2
|
import { getProperty } from 'dot-prop';
|
|
3
3
|
import inflection from 'inflection';
|
|
4
|
+
import { Low } from 'lowdb';
|
|
4
5
|
import sortOn from 'sort-on';
|
|
5
6
|
export function isItem(obj) {
|
|
6
7
|
return typeof obj === 'object' && obj !== null;
|
|
@@ -12,15 +13,14 @@ export function isData(obj) {
|
|
|
12
13
|
const data = obj;
|
|
13
14
|
return Object.values(data).every((value) => Array.isArray(value) && value.every(isItem));
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
})(Condition || (Condition = {}));
|
|
16
|
+
const Condition = {
|
|
17
|
+
lt: 'lt',
|
|
18
|
+
lte: 'lte',
|
|
19
|
+
gt: 'gt',
|
|
20
|
+
gte: 'gte',
|
|
21
|
+
ne: 'ne',
|
|
22
|
+
default: '',
|
|
23
|
+
};
|
|
24
24
|
function isCondition(value) {
|
|
25
25
|
return Object.values(Condition).includes(value);
|
|
26
26
|
}
|
|
@@ -149,15 +149,7 @@ export class Service {
|
|
|
149
149
|
conds.push([field, op, value]);
|
|
150
150
|
continue;
|
|
151
151
|
}
|
|
152
|
-
if ([
|
|
153
|
-
'_embed',
|
|
154
|
-
'_sort',
|
|
155
|
-
'_start',
|
|
156
|
-
'_end',
|
|
157
|
-
'_limit',
|
|
158
|
-
'_page',
|
|
159
|
-
'_per_page',
|
|
160
|
-
].includes(key)) {
|
|
152
|
+
if (['_embed', '_sort', '_start', '_end', '_limit', '_page', '_per_page'].includes(key)) {
|
|
161
153
|
continue;
|
|
162
154
|
}
|
|
163
155
|
conds.push([key, Condition.default, value]);
|
|
@@ -172,32 +164,28 @@ export class Service {
|
|
|
172
164
|
switch (op) {
|
|
173
165
|
// item_gt=value
|
|
174
166
|
case Condition.gt: {
|
|
175
|
-
if (!(typeof itemValue === 'number' &&
|
|
176
|
-
itemValue > parseInt(paramValue))) {
|
|
167
|
+
if (!(typeof itemValue === 'number' && itemValue > parseInt(paramValue))) {
|
|
177
168
|
return false;
|
|
178
169
|
}
|
|
179
170
|
break;
|
|
180
171
|
}
|
|
181
172
|
// item_gte=value
|
|
182
173
|
case Condition.gte: {
|
|
183
|
-
if (!(typeof itemValue === 'number' &&
|
|
184
|
-
itemValue >= parseInt(paramValue))) {
|
|
174
|
+
if (!(typeof itemValue === 'number' && itemValue >= parseInt(paramValue))) {
|
|
185
175
|
return false;
|
|
186
176
|
}
|
|
187
177
|
break;
|
|
188
178
|
}
|
|
189
179
|
// item_lt=value
|
|
190
180
|
case Condition.lt: {
|
|
191
|
-
if (!(typeof itemValue === 'number' &&
|
|
192
|
-
itemValue < parseInt(paramValue))) {
|
|
181
|
+
if (!(typeof itemValue === 'number' && itemValue < parseInt(paramValue))) {
|
|
193
182
|
return false;
|
|
194
183
|
}
|
|
195
184
|
break;
|
|
196
185
|
}
|
|
197
186
|
// item_lte=value
|
|
198
187
|
case Condition.lte: {
|
|
199
|
-
if (!(typeof itemValue === 'number' &&
|
|
200
|
-
itemValue <= parseInt(paramValue))) {
|
|
188
|
+
if (!(typeof itemValue === 'number' && itemValue <= parseInt(paramValue))) {
|
|
201
189
|
return false;
|
|
202
190
|
}
|
|
203
191
|
break;
|
|
@@ -223,6 +211,8 @@ export class Service {
|
|
|
223
211
|
return itemValue === paramValue;
|
|
224
212
|
case 'boolean':
|
|
225
213
|
return itemValue === (paramValue === 'true');
|
|
214
|
+
case 'undefined':
|
|
215
|
+
return false;
|
|
226
216
|
}
|
|
227
217
|
}
|
|
228
218
|
}
|
|
@@ -286,7 +276,7 @@ export class Service {
|
|
|
286
276
|
const item = this.#get(name);
|
|
287
277
|
if (item === undefined || Array.isArray(item))
|
|
288
278
|
return;
|
|
289
|
-
const nextItem = (this.#db.data[name] = isPatch ? { item, ...body } : body);
|
|
279
|
+
const nextItem = (this.#db.data[name] = isPatch ? { ...item, ...body } : body);
|
|
290
280
|
await this.#db.write();
|
|
291
281
|
return nextItem;
|
|
292
282
|
}
|
package/package.json
CHANGED
|
@@ -1,63 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "json-server",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.4",
|
|
4
4
|
"description": "",
|
|
5
|
-
"
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"license": "SEE LICENSE IN ./LICENSE",
|
|
7
|
+
"author": "typicode <typicode@gmail.com>",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/typicode/json-server.git"
|
|
11
|
+
},
|
|
6
12
|
"bin": {
|
|
7
13
|
"json-server": "lib/bin.js"
|
|
8
14
|
},
|
|
9
|
-
"types": "lib",
|
|
10
15
|
"files": [
|
|
11
16
|
"lib",
|
|
12
17
|
"views"
|
|
13
18
|
],
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"type": "git",
|
|
30
|
-
"url": "git+https://github.com/typicode/json-server.git"
|
|
19
|
+
"type": "module",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@tinyhttp/app": "^3.0.1",
|
|
22
|
+
"@tinyhttp/cors": "^2.0.1",
|
|
23
|
+
"@tinyhttp/logger": "^2.1.0",
|
|
24
|
+
"chalk": "^5.6.2",
|
|
25
|
+
"chokidar": "^5.0.0",
|
|
26
|
+
"dot-prop": "^10.1.0",
|
|
27
|
+
"eta": "^4.5.0",
|
|
28
|
+
"inflection": "^3.0.2",
|
|
29
|
+
"json5": "^2.2.3",
|
|
30
|
+
"lowdb": "^7.0.1",
|
|
31
|
+
"milliparsec": "^5.1.0",
|
|
32
|
+
"sirv": "^3.0.2",
|
|
33
|
+
"sort-on": "^7.0.0"
|
|
31
34
|
},
|
|
32
35
|
"devDependencies": {
|
|
33
|
-
"@
|
|
34
|
-
"
|
|
35
|
-
"@tailwindcss/typography": "^0.5.15",
|
|
36
|
-
"@types/node": "^22.5.5",
|
|
37
|
-
"concurrently": "^9.0.1",
|
|
38
|
-
"eslint": "^9.11.0",
|
|
36
|
+
"@types/node": "^25.0.8",
|
|
37
|
+
"concurrently": "^9.2.1",
|
|
39
38
|
"get-port": "^7.1.0",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
39
|
+
"husky": "^9.1.7",
|
|
40
|
+
"oxfmt": "^0.24.0",
|
|
41
|
+
"oxlint": "^1.39.0",
|
|
42
42
|
"tempy": "^3.1.0",
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"typescript": "^5.6.2",
|
|
46
|
-
"typescript-eslint": "^8.6.0"
|
|
43
|
+
"type-fest": "^5.4.0",
|
|
44
|
+
"typescript": "^5.9.3"
|
|
47
45
|
},
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"milliparsec": "^4.0.0",
|
|
60
|
-
"sirv": "^2.0.4",
|
|
61
|
-
"sort-on": "^6.1.0"
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=22.12.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"dev": "node --watch --experimental-strip-types src/bin.ts fixtures/db.json",
|
|
51
|
+
"build": "rm -rf lib && tsc",
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
|
+
"test": "node --experimental-strip-types --test src/*.test.ts",
|
|
54
|
+
"lint": "oxlint src",
|
|
55
|
+
"fmt": "oxfmt",
|
|
56
|
+
"fmt:check": "oxfmt --check"
|
|
62
57
|
}
|
|
63
|
-
}
|
|
58
|
+
}
|
package/views/index.html
CHANGED
|
@@ -1,97 +1,95 @@
|
|
|
1
|
-
<!
|
|
1
|
+
<!doctype html>
|
|
2
2
|
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<style>
|
|
7
|
+
html {
|
|
8
|
+
font-size: 16px;
|
|
9
|
+
line-height: 1.5;
|
|
10
|
+
background-color: #fff;
|
|
11
|
+
color: #000;
|
|
12
|
+
}
|
|
3
13
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
line-height: 1.5;
|
|
11
|
-
background-color: #fff;
|
|
12
|
-
color: #000;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
body {
|
|
16
|
-
margin: 0 auto;
|
|
17
|
-
max-width: 720px;
|
|
18
|
-
padding: 0 16px;
|
|
19
|
-
font-family: sans-serif;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
a {
|
|
23
|
-
color: #db2777;
|
|
24
|
-
text-decoration: none;
|
|
25
|
-
}
|
|
14
|
+
body {
|
|
15
|
+
margin: 0 auto;
|
|
16
|
+
max-width: 720px;
|
|
17
|
+
padding: 0 16px;
|
|
18
|
+
font-family: sans-serif;
|
|
19
|
+
}
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
a {
|
|
22
|
+
color: #db2777;
|
|
23
|
+
text-decoration: none;
|
|
24
|
+
}
|
|
31
25
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
header {
|
|
27
|
+
margin-bottom: 32px;
|
|
28
|
+
padding: 16px 0;
|
|
29
|
+
}
|
|
36
30
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
nav {
|
|
32
|
+
display: flex;
|
|
33
|
+
justify-content: space-between;
|
|
34
|
+
}
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
list-style: none;
|
|
45
|
-
}
|
|
36
|
+
nav div a {
|
|
37
|
+
margin-left: 16px;
|
|
38
|
+
}
|
|
46
39
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
ul {
|
|
41
|
+
margin: 0;
|
|
42
|
+
padding: 0;
|
|
43
|
+
list-style: none;
|
|
44
|
+
}
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
html {
|
|
54
|
-
background-color: #1e293b;
|
|
55
|
-
color: #fff;
|
|
46
|
+
li {
|
|
47
|
+
margin-bottom: 8px;
|
|
56
48
|
}
|
|
57
49
|
|
|
58
|
-
|
|
50
|
+
/* Dark mode styles */
|
|
51
|
+
@media (prefers-color-scheme: dark) {
|
|
52
|
+
html {
|
|
53
|
+
background-color: #1e293b;
|
|
54
|
+
color: #fff;
|
|
55
|
+
}
|
|
59
56
|
|
|
57
|
+
a {
|
|
58
|
+
}
|
|
60
59
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
</style>
|
|
64
|
-
</head>
|
|
60
|
+
</style>
|
|
61
|
+
</head>
|
|
65
62
|
|
|
66
|
-
<body>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
63
|
+
<body>
|
|
64
|
+
<header>
|
|
65
|
+
<nav>
|
|
66
|
+
<strong>JSON Server</strong>
|
|
67
|
+
<div>
|
|
68
|
+
<a href="https://github.com/typicode/json-server">Docs</a>
|
|
69
|
+
<a href="https://github.com/sponsors/typicode">♡ Sponsor</a>
|
|
70
|
+
</div>
|
|
71
|
+
</nav>
|
|
72
|
+
</header>
|
|
73
|
+
<main class="my-12">
|
|
74
|
+
<p
|
|
75
|
+
class="bg-gradient-to-r from-purple-500 via-pink-500 to-red-500 text-transparent bg-clip-text"
|
|
76
|
+
>
|
|
77
|
+
✧*。٩(ˊᗜˋ*)و✧*。
|
|
78
|
+
</p>
|
|
79
|
+
<% if (Object.keys(it.data).length===0) { %>
|
|
79
80
|
<p>No resources found in JSON file</p>
|
|
80
|
-
|
|
81
|
-
<% Object.entries(it.data).forEach(function([name]) { %>
|
|
81
|
+
<% } %> <% Object.entries(it.data).forEach(function([name]) { %>
|
|
82
82
|
<ul>
|
|
83
83
|
<li>
|
|
84
84
|
<a href="<%= name %>">/<%= name %></a>
|
|
85
85
|
<span>
|
|
86
|
-
<% if (Array.isArray(it.data[name])) { %>
|
|
87
|
-
|
|
88
|
-
<%= it.data[name].length> 1 ? 'items' : 'item' %>
|
|
86
|
+
<% if (Array.isArray(it.data[name])) { %> - <%= it.data[name].length %> <%=
|
|
87
|
+
it.data[name].length> 1 ? 'items' : 'item' %>
|
|
89
88
|
</span>
|
|
90
89
|
<% } %>
|
|
91
90
|
</li>
|
|
92
91
|
</ul>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
</body>
|
|
96
|
-
|
|
97
|
-
</html>
|
|
92
|
+
<% }) %>
|
|
93
|
+
</main>
|
|
94
|
+
</body>
|
|
95
|
+
</html>
|
package/lib/app.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { App } from '@tinyhttp/app';
|
|
2
|
-
import { Low } from 'lowdb';
|
|
3
|
-
import { Data } from './service.js';
|
|
4
|
-
export type AppOptions = {
|
|
5
|
-
logger?: boolean;
|
|
6
|
-
static?: string[];
|
|
7
|
-
};
|
|
8
|
-
export declare function createApp(db: Low<Data>, options?: AppOptions): App<import("@tinyhttp/app").Request, import("@tinyhttp/app").Response<unknown>>;
|
package/lib/bin.d.ts
DELETED
package/lib/observer.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Adapter } from 'lowdb';
|
|
2
|
-
export declare class Observer<T> {
|
|
3
|
-
#private;
|
|
4
|
-
onReadStart: () => void;
|
|
5
|
-
onReadEnd: (data: T | null) => void;
|
|
6
|
-
onWriteStart: () => void;
|
|
7
|
-
onWriteEnd: () => void;
|
|
8
|
-
constructor(adapter: Adapter<T>);
|
|
9
|
-
read(): Promise<T | null>;
|
|
10
|
-
write(arg: T): Promise<void>;
|
|
11
|
-
}
|
package/lib/service.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { Low } from 'lowdb';
|
|
2
|
-
export type Item = Record<string, unknown>;
|
|
3
|
-
export type Data = Record<string, Item[] | Item>;
|
|
4
|
-
export declare function isItem(obj: unknown): obj is Item;
|
|
5
|
-
export declare function isData(obj: unknown): obj is Record<string, Item[]>;
|
|
6
|
-
export type PaginatedItems = {
|
|
7
|
-
first: number;
|
|
8
|
-
prev: number | null;
|
|
9
|
-
next: number | null;
|
|
10
|
-
last: number;
|
|
11
|
-
pages: number;
|
|
12
|
-
items: number;
|
|
13
|
-
data: Item[];
|
|
14
|
-
};
|
|
15
|
-
export declare class Service {
|
|
16
|
-
#private;
|
|
17
|
-
constructor(db: Low<Data>);
|
|
18
|
-
has(name: string): boolean;
|
|
19
|
-
findById(name: string, id: string, query: {
|
|
20
|
-
_embed?: string[] | string;
|
|
21
|
-
}): Item | undefined;
|
|
22
|
-
find(name: string, query?: {
|
|
23
|
-
[key: string]: unknown;
|
|
24
|
-
_embed?: string | string[];
|
|
25
|
-
_sort?: string;
|
|
26
|
-
_start?: number;
|
|
27
|
-
_end?: number;
|
|
28
|
-
_limit?: number;
|
|
29
|
-
_page?: number;
|
|
30
|
-
_per_page?: number;
|
|
31
|
-
}): Item[] | PaginatedItems | Item | undefined;
|
|
32
|
-
create(name: string, data?: Omit<Item, 'id'>): Promise<Item | undefined>;
|
|
33
|
-
update(name: string, body?: Item): Promise<Item | undefined>;
|
|
34
|
-
patch(name: string, body?: Item): Promise<Item | undefined>;
|
|
35
|
-
updateById(name: string, id: string, body?: Item): Promise<Item | undefined>;
|
|
36
|
-
patchById(name: string, id: string, body?: Item): Promise<Item | undefined>;
|
|
37
|
-
destroyById(name: string, id: string, dependent?: string | string[]): Promise<Item | undefined>;
|
|
38
|
-
}
|