added doga
This commit is contained in:
44
25_02_24/node_modules/json-server/LICENSE
generated
vendored
Normal file
44
25_02_24/node_modules/json-server/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
Fair Source License, version 0.9
|
||||
|
||||
Copyright (C) 2023-present typicode
|
||||
|
||||
Licensor: typicode
|
||||
|
||||
Software: json-server
|
||||
|
||||
Use Limitation: 2 users
|
||||
|
||||
License Grant. Licensor hereby grants to each recipient of the
|
||||
Software ("you") a non-exclusive, non-transferable, royalty-free and
|
||||
fully-paid-up license, under all of the Licensor's copyright and
|
||||
patent rights, to use, copy, distribute, prepare derivative works of,
|
||||
publicly perform and display the Software, subject to the Use
|
||||
Limitation and the conditions set forth below.
|
||||
|
||||
Use Limitation. The license granted above allows use by up to the
|
||||
number of users per entity set forth above (the "Use Limitation"). For
|
||||
determining the number of users, "you" includes all affiliates,
|
||||
meaning legal entities controlling, controlled by, or under common
|
||||
control with you. If you exceed the Use Limitation, your use is
|
||||
subject to payment of Licensor's then-current list price for licenses.
|
||||
|
||||
Conditions. Redistribution in source code or other forms must include
|
||||
a copy of this license document to be provided in a reasonable
|
||||
manner. Any redistribution of the Software is only allowed subject to
|
||||
this license.
|
||||
|
||||
Trademarks. This license does not grant you any right in the
|
||||
trademarks, service marks, brand names or logos of Licensor.
|
||||
|
||||
DISCLAIMER. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OR
|
||||
CONDITION, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. LICENSORS HEREBY DISCLAIM ALL LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE.
|
||||
|
||||
Termination. If you violate the terms of this license, your rights
|
||||
will terminate automatically and will not be reinstated without the
|
||||
prior written consent of Licensor. Any such termination will not
|
||||
affect the right of others who may have received copies of the
|
||||
Software from you.
|
||||
209
25_02_24/node_modules/json-server/README.md
generated
vendored
Normal file
209
25_02_24/node_modules/json-server/README.md
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
# json-server
|
||||
|
||||
[](https://github.com/typicode/json-server/actions/workflows/node.js.yml)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Viewing beta v1 documentation – usable but expect breaking changes. For stable version, see [here](https://github.com/typicode/json-server/tree/v0)
|
||||
|
||||
👋 _Hey! Using React, Vue or Astro? Check my new project [MistCSS](https://github.com/typicode/mistcss) to write 50% less code._
|
||||
|
||||
## Install
|
||||
|
||||
```shell
|
||||
npm install json-server
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Create a `db.json` or `db.json5` file
|
||||
|
||||
```json
|
||||
{
|
||||
"posts": [
|
||||
{ "id": "1", "title": "a title", "views": 100 },
|
||||
{ "id": "2", "title": "another title", "views": 200 }
|
||||
],
|
||||
"comments": [
|
||||
{ "id": "1", "text": "a comment about post 1", "postId": "1" },
|
||||
{ "id": "2", "text": "another comment about post 1", "postId": "1" }
|
||||
],
|
||||
"profile": {
|
||||
"name": "typicode"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
<summary>View db.json5 example</summary>
|
||||
|
||||
```json5
|
||||
{
|
||||
posts: [
|
||||
{ id: '1', title: 'a title', views: 100 },
|
||||
{ id: '2', title: 'another title', views: 200 },
|
||||
],
|
||||
comments: [
|
||||
{ id: '1', text: 'a comment about post 1', postId: '1' },
|
||||
{ id: '2', text: 'another comment about post 1', postId: '1' },
|
||||
],
|
||||
profile: {
|
||||
name: 'typicode',
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
You can read more about JSON5 format [here](https://github.com/json5/json5).
|
||||
|
||||
</details>
|
||||
|
||||
Pass it to JSON Server CLI
|
||||
|
||||
```shell
|
||||
$ npx json-server db.json
|
||||
```
|
||||
|
||||
Get a REST API
|
||||
|
||||
```shell
|
||||
$ curl http://localhost:3000/posts/1
|
||||
{
|
||||
"id": "1",
|
||||
"title": "a title",
|
||||
"views": 100
|
||||
}
|
||||
```
|
||||
|
||||
Run `json-server --help` for a list of options
|
||||
|
||||
## Sponsors ✨
|
||||
|
||||
| Sponsors |
|
||||
| :---: |
|
||||
| <a href="https://mockend.com/" target="_blank"><img src="https://jsonplaceholder.typicode.com/mockend.svg" height="100px"></a> |
|
||||
| <a href="https://zuplo.link/json-server-gh"><img src="https://github.com/typicode/json-server/assets/5502029/928b7526-0fdf-46ae-80d9-27fa0ef5f430"></a> |
|
||||
|
||||
| Sponsors |
|
||||
| :---: |
|
||||
| <a href="https://konghq.com/products/kong-konnect?utm_medium=referral&utm_source=github&utm_campaign=platform&utm_content=json-server"><img src="https://github.com/typicode/json-server/assets/5502029/e8d8ecb2-3c45-4f60-92d0-a060b820fa7f" height="75px"></a> |
|
||||
|
||||
| Sponsors | |
|
||||
| :---: | :---: |
|
||||
| <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> |
|
||||
| <a href="https://route4me.com"><img src="https://github.com/user-attachments/assets/4eab0bac-119e-4b27-8183-8b136190b776" height="35px" alt="Delivery Routing Software and Route Optimization Software"></a> | <a href="https://www.speechanddebate.org"><img src="https://github.com/user-attachments/assets/cc7980e4-2147-4499-8de4-4d0c265d0c07" height="35px"></a> |
|
||||
|
||||
|
||||
[Become a sponsor and have your company logo here](https://github.com/users/typicode/sponsorship)
|
||||
|
||||
## Sponsorware
|
||||
|
||||
> [!NOTE]
|
||||
> 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.__
|
||||
>
|
||||
> For more information, FAQs, and the rationale behind this, visit [https://fair.io/](https://fair.io/).
|
||||
|
||||
## Routes
|
||||
|
||||
Based on the example `db.json`, you'll get the following routes:
|
||||
|
||||
```
|
||||
GET /posts
|
||||
GET /posts/:id
|
||||
POST /posts
|
||||
PUT /posts/:id
|
||||
PATCH /posts/:id
|
||||
DELETE /posts/:id
|
||||
|
||||
# Same for comments
|
||||
```
|
||||
|
||||
```
|
||||
GET /profile
|
||||
PUT /profile
|
||||
PATCH /profile
|
||||
```
|
||||
|
||||
## Params
|
||||
|
||||
### Conditions
|
||||
|
||||
- ` ` → `==`
|
||||
- `lt` → `<`
|
||||
- `lte` → `<=`
|
||||
- `gt` → `>`
|
||||
- `gte` → `>=`
|
||||
- `ne` → `!=`
|
||||
|
||||
```
|
||||
GET /posts?views_gt=9000
|
||||
```
|
||||
|
||||
### Range
|
||||
|
||||
- `start`
|
||||
- `end`
|
||||
- `limit`
|
||||
|
||||
```
|
||||
GET /posts?_start=10&_end=20
|
||||
GET /posts?_start=10&_limit=10
|
||||
```
|
||||
|
||||
### Paginate
|
||||
|
||||
- `page`
|
||||
- `per_page` (default = 10)
|
||||
|
||||
```
|
||||
GET /posts?_page=1&_per_page=25
|
||||
```
|
||||
|
||||
### Sort
|
||||
|
||||
- `_sort=f1,f2`
|
||||
|
||||
```
|
||||
GET /posts?_sort=id,-views
|
||||
```
|
||||
|
||||
### Nested and array fields
|
||||
|
||||
- `x.y.z...`
|
||||
- `x.y.z[i]...`
|
||||
|
||||
```
|
||||
GET /foo?a.b=bar
|
||||
GET /foo?x.y_lt=100
|
||||
GET /foo?arr[0]=bar
|
||||
```
|
||||
|
||||
### Embed
|
||||
|
||||
```
|
||||
GET /posts?_embed=comments
|
||||
GET /comments?_embed=post
|
||||
```
|
||||
|
||||
## Delete
|
||||
|
||||
```
|
||||
DELETE /posts/1
|
||||
DELETE /posts/1?_dependent=comments
|
||||
```
|
||||
|
||||
## Serving static files
|
||||
|
||||
If you create a `./public` directory, JSON Server will serve its content in addition to the REST API.
|
||||
|
||||
You can also add custom directories using `-s/--static` option.
|
||||
|
||||
```sh
|
||||
json-server -s ./static
|
||||
json-server -s ./static -s ./node_modules
|
||||
```
|
||||
|
||||
## Notable differences with v0.17
|
||||
|
||||
- `id` is always a string and will be generated for you if missing
|
||||
- use `_per_page` with `_page` instead of `_limit`for pagination
|
||||
- use Chrome's `Network tab > throtling` to delay requests instead of `--delay` CLI option
|
||||
8
25_02_24/node_modules/json-server/lib/app.d.ts
generated
vendored
Normal file
8
25_02_24/node_modules/json-server/lib/app.d.ts
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { App } from '@tinyhttp/app';
|
||||
import { Low } from 'lowdb';
|
||||
import { Data } from './service.js';
|
||||
export type AppOptions = {
|
||||
logger?: boolean;
|
||||
static?: string[];
|
||||
};
|
||||
export declare function createApp(db: Low<Data>, options?: AppOptions): App<import("@tinyhttp/app").Request, import("@tinyhttp/app").Response<unknown>>;
|
||||
112
25_02_24/node_modules/json-server/lib/app.js
generated
vendored
Normal file
112
25_02_24/node_modules/json-server/lib/app.js
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
import { dirname, isAbsolute, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { App } from '@tinyhttp/app';
|
||||
import { cors } from '@tinyhttp/cors';
|
||||
import { Eta } from 'eta';
|
||||
import { json } from 'milliparsec';
|
||||
import sirv from 'sirv';
|
||||
import { isItem, Service } from './service.js';
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const isProduction = process.env['NODE_ENV'] === 'production';
|
||||
const eta = new Eta({
|
||||
views: join(__dirname, '../views'),
|
||||
cache: isProduction,
|
||||
});
|
||||
export function createApp(db, options = {}) {
|
||||
// Create service
|
||||
const service = new Service(db);
|
||||
// Create app
|
||||
const app = new App();
|
||||
// Static files
|
||||
app.use(sirv('public', { dev: !isProduction }));
|
||||
options.static
|
||||
?.map((path) => (isAbsolute(path) ? path : join(process.cwd(), path)))
|
||||
.forEach((dir) => app.use(sirv(dir, { dev: !isProduction })));
|
||||
// CORS
|
||||
app
|
||||
.use((req, res, next) => {
|
||||
return cors({
|
||||
allowedHeaders: req.headers['access-control-request-headers']
|
||||
?.split(',')
|
||||
.map((h) => h.trim()),
|
||||
})(req, res, next);
|
||||
})
|
||||
.options('*', cors());
|
||||
// Body parser
|
||||
// @ts-expect-error expected
|
||||
app.use(json());
|
||||
app.get('/', (_req, res) => res.send(eta.render('index.html', { data: db.data })));
|
||||
app.get('/:name', (req, res, next) => {
|
||||
const { name = '' } = req.params;
|
||||
const query = Object.fromEntries(Object.entries(req.query)
|
||||
.map(([key, value]) => {
|
||||
if (['_start', '_end', '_limit', '_page', '_per_page'].includes(key) &&
|
||||
typeof value === 'string') {
|
||||
return [key, parseInt(value)];
|
||||
}
|
||||
else {
|
||||
return [key, value];
|
||||
}
|
||||
})
|
||||
.filter(([, value]) => !Number.isNaN(value)));
|
||||
res.locals['data'] = service.find(name, query);
|
||||
next?.();
|
||||
});
|
||||
app.get('/:name/:id', (req, res, next) => {
|
||||
const { name = '', id = '' } = req.params;
|
||||
res.locals['data'] = service.findById(name, id, req.query);
|
||||
next?.();
|
||||
});
|
||||
app.post('/:name', async (req, res, next) => {
|
||||
const { name = '' } = req.params;
|
||||
if (isItem(req.body)) {
|
||||
res.locals['data'] = await service.create(name, req.body);
|
||||
}
|
||||
next?.();
|
||||
});
|
||||
app.put('/:name', async (req, res, next) => {
|
||||
const { name = '' } = req.params;
|
||||
if (isItem(req.body)) {
|
||||
res.locals['data'] = await service.update(name, req.body);
|
||||
}
|
||||
next?.();
|
||||
});
|
||||
app.put('/:name/:id', async (req, res, next) => {
|
||||
const { name = '', id = '' } = req.params;
|
||||
if (isItem(req.body)) {
|
||||
res.locals['data'] = await service.updateById(name, id, req.body);
|
||||
}
|
||||
next?.();
|
||||
});
|
||||
app.patch('/:name', async (req, res, next) => {
|
||||
const { name = '' } = req.params;
|
||||
if (isItem(req.body)) {
|
||||
res.locals['data'] = await service.patch(name, req.body);
|
||||
}
|
||||
next?.();
|
||||
});
|
||||
app.patch('/:name/:id', async (req, res, next) => {
|
||||
const { name = '', id = '' } = req.params;
|
||||
if (isItem(req.body)) {
|
||||
res.locals['data'] = await service.patchById(name, id, req.body);
|
||||
}
|
||||
next?.();
|
||||
});
|
||||
app.delete('/:name/:id', async (req, res, next) => {
|
||||
const { name = '', id = '' } = req.params;
|
||||
res.locals['data'] = await service.destroyById(name, id, req.query['_dependent']);
|
||||
next?.();
|
||||
});
|
||||
app.use('/:name', (req, res) => {
|
||||
const { data } = res.locals;
|
||||
if (data === undefined) {
|
||||
res.sendStatus(404);
|
||||
}
|
||||
else {
|
||||
if (req.method === 'POST')
|
||||
res.status(201);
|
||||
res.json(data);
|
||||
}
|
||||
});
|
||||
return app;
|
||||
}
|
||||
2
25_02_24/node_modules/json-server/lib/bin.d.ts
generated
vendored
Normal file
2
25_02_24/node_modules/json-server/lib/bin.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
export {};
|
||||
183
25_02_24/node_modules/json-server/lib/bin.js
generated
vendored
Normal file
183
25_02_24/node_modules/json-server/lib/bin.js
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env node
|
||||
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { extname } from 'node:path';
|
||||
import { parseArgs } from 'node:util';
|
||||
import chalk from 'chalk';
|
||||
import { watch } from 'chokidar';
|
||||
import JSON5 from 'json5';
|
||||
import { Low } from 'lowdb';
|
||||
import { DataFile, JSONFile } from 'lowdb/node';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createApp } from './app.js';
|
||||
import { Observer } from './observer.js';
|
||||
function help() {
|
||||
console.log(`Usage: json-server [options] <file>
|
||||
|
||||
Options:
|
||||
-p, --port <port> Port (default: 3000)
|
||||
-h, --host <host> Host (default: localhost)
|
||||
-s, --static <dir> Static files directory (multiple allowed)
|
||||
--help Show this message
|
||||
--version Show version number
|
||||
`);
|
||||
}
|
||||
// Parse args
|
||||
function args() {
|
||||
try {
|
||||
const { values, positionals } = parseArgs({
|
||||
options: {
|
||||
port: {
|
||||
type: 'string',
|
||||
short: 'p',
|
||||
default: process.env['PORT'] ?? '3000',
|
||||
},
|
||||
host: {
|
||||
type: 'string',
|
||||
short: 'h',
|
||||
default: process.env['HOST'] ?? 'localhost',
|
||||
},
|
||||
static: {
|
||||
type: 'string',
|
||||
short: 's',
|
||||
multiple: true,
|
||||
default: [],
|
||||
},
|
||||
help: {
|
||||
type: 'boolean',
|
||||
},
|
||||
version: {
|
||||
type: 'boolean',
|
||||
},
|
||||
// Deprecated
|
||||
watch: {
|
||||
type: 'boolean',
|
||||
short: 'w',
|
||||
},
|
||||
},
|
||||
allowPositionals: true,
|
||||
});
|
||||
// --version
|
||||
if (values.version) {
|
||||
const pkg = JSON.parse(readFileSync(fileURLToPath(new URL('../package.json', import.meta.url)), 'utf-8'));
|
||||
console.log(pkg.version);
|
||||
process.exit();
|
||||
}
|
||||
// Handle --watch
|
||||
if (values.watch) {
|
||||
console.log(chalk.yellow('--watch/-w can be omitted, JSON Server 1+ watches for file changes by default'));
|
||||
}
|
||||
if (values.help || positionals.length === 0) {
|
||||
help();
|
||||
process.exit();
|
||||
}
|
||||
// App args and options
|
||||
return {
|
||||
file: positionals[0] ?? '',
|
||||
port: parseInt(values.port),
|
||||
host: values.host,
|
||||
static: values.static,
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
if (e.code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION') {
|
||||
console.log(chalk.red(e.message.split('.')[0]));
|
||||
help();
|
||||
process.exit(1);
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
const { file, port, host, static: staticArr } = args();
|
||||
if (!existsSync(file)) {
|
||||
console.log(chalk.red(`File ${file} not found`));
|
||||
process.exit(1);
|
||||
}
|
||||
// Handle empty string JSON file
|
||||
if (readFileSync(file, 'utf-8').trim() === '') {
|
||||
writeFileSync(file, '{}');
|
||||
}
|
||||
// Set up database
|
||||
let adapter;
|
||||
if (extname(file) === '.json5') {
|
||||
adapter = new DataFile(file, {
|
||||
parse: JSON5.parse,
|
||||
stringify: JSON5.stringify,
|
||||
});
|
||||
}
|
||||
else {
|
||||
adapter = new JSONFile(file);
|
||||
}
|
||||
const observer = new Observer(adapter);
|
||||
const db = new Low(observer, {});
|
||||
await db.read();
|
||||
// Create app
|
||||
const app = createApp(db, { logger: false, static: staticArr });
|
||||
function logRoutes(data) {
|
||||
console.log(chalk.bold('Endpoints:'));
|
||||
if (Object.keys(data).length === 0) {
|
||||
console.log(chalk.gray(`No endpoints found, try adding some data to ${file}`));
|
||||
return;
|
||||
}
|
||||
console.log(Object.keys(data)
|
||||
.map((key) => `${chalk.gray(`http://${host}:${port}/`)}${chalk.blue(key)}`)
|
||||
.join('\n'));
|
||||
}
|
||||
const kaomojis = ['♡⸜(˶˃ ᵕ ˂˶)⸝♡', '♡( ◡‿◡ )', '( ˶ˆ ᗜ ˆ˵ )', '(˶ᵔ ᵕ ᵔ˶)'];
|
||||
function randomItem(items) {
|
||||
const index = Math.floor(Math.random() * items.length);
|
||||
return items.at(index) ?? '';
|
||||
}
|
||||
app.listen(port, () => {
|
||||
console.log([
|
||||
chalk.bold(`JSON Server started on PORT :${port}`),
|
||||
chalk.gray('Press CTRL-C to stop'),
|
||||
chalk.gray(`Watching ${file}...`),
|
||||
'',
|
||||
chalk.magenta(randomItem(kaomojis)),
|
||||
'',
|
||||
chalk.bold('Index:'),
|
||||
chalk.gray(`http://localhost:${port}/`),
|
||||
'',
|
||||
chalk.bold('Static files:'),
|
||||
chalk.gray('Serving ./public directory if it exists'),
|
||||
'',
|
||||
].join('\n'));
|
||||
logRoutes(db.data);
|
||||
});
|
||||
// Watch file for changes
|
||||
if (process.env['NODE_ENV'] !== 'production') {
|
||||
let writing = false; // true if the file is being written to by the app
|
||||
let prevEndpoints = '';
|
||||
observer.onWriteStart = () => {
|
||||
writing = true;
|
||||
};
|
||||
observer.onWriteEnd = () => {
|
||||
writing = false;
|
||||
};
|
||||
observer.onReadStart = () => {
|
||||
prevEndpoints = JSON.stringify(Object.keys(db.data).sort());
|
||||
};
|
||||
observer.onReadEnd = (data) => {
|
||||
if (data === null) {
|
||||
return;
|
||||
}
|
||||
const nextEndpoints = JSON.stringify(Object.keys(data).sort());
|
||||
if (prevEndpoints !== nextEndpoints) {
|
||||
console.log();
|
||||
logRoutes(data);
|
||||
}
|
||||
};
|
||||
watch(file).on('change', () => {
|
||||
// Do no reload if the file is being written to by the app
|
||||
if (!writing) {
|
||||
db.read().catch((e) => {
|
||||
if (e instanceof SyntaxError) {
|
||||
return console.log(chalk.red(['', `Error parsing ${file}`, e.message].join('\n')));
|
||||
}
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
11
25_02_24/node_modules/json-server/lib/observer.d.ts
generated
vendored
Normal file
11
25_02_24/node_modules/json-server/lib/observer.d.ts
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Adapter } from 'lowdb';
|
||||
export declare class Observer<T> {
|
||||
#private;
|
||||
onReadStart: () => void;
|
||||
onReadEnd: (data: T | null) => void;
|
||||
onWriteStart: () => void;
|
||||
onWriteEnd: () => void;
|
||||
constructor(adapter: Adapter<T>);
|
||||
read(): Promise<T | null>;
|
||||
write(arg: T): Promise<void>;
|
||||
}
|
||||
30
25_02_24/node_modules/json-server/lib/observer.js
generated
vendored
Normal file
30
25_02_24/node_modules/json-server/lib/observer.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Lowdb adapter to observe read/write events
|
||||
export class Observer {
|
||||
#adapter;
|
||||
onReadStart = function () {
|
||||
return;
|
||||
};
|
||||
onReadEnd = function () {
|
||||
return;
|
||||
};
|
||||
onWriteStart = function () {
|
||||
return;
|
||||
};
|
||||
onWriteEnd = function () {
|
||||
return;
|
||||
};
|
||||
constructor(adapter) {
|
||||
this.#adapter = adapter;
|
||||
}
|
||||
async read() {
|
||||
this.onReadStart();
|
||||
const data = await this.#adapter.read();
|
||||
this.onReadEnd(data);
|
||||
return data;
|
||||
}
|
||||
async write(arg) {
|
||||
this.onWriteStart();
|
||||
await this.#adapter.write(arg);
|
||||
this.onWriteEnd();
|
||||
}
|
||||
}
|
||||
38
25_02_24/node_modules/json-server/lib/service.d.ts
generated
vendored
Normal file
38
25_02_24/node_modules/json-server/lib/service.d.ts
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Low } from 'lowdb';
|
||||
export type Item = Record<string, unknown>;
|
||||
export type Data = Record<string, Item[] | Item>;
|
||||
export declare function isItem(obj: unknown): obj is Item;
|
||||
export declare function isData(obj: unknown): obj is Record<string, Item[]>;
|
||||
export type PaginatedItems = {
|
||||
first: number;
|
||||
prev: number | null;
|
||||
next: number | null;
|
||||
last: number;
|
||||
pages: number;
|
||||
items: number;
|
||||
data: Item[];
|
||||
};
|
||||
export declare class Service {
|
||||
#private;
|
||||
constructor(db: Low<Data>);
|
||||
has(name: string): boolean;
|
||||
findById(name: string, id: string, query: {
|
||||
_embed?: string[] | string;
|
||||
}): Item | undefined;
|
||||
find(name: string, query?: {
|
||||
[key: string]: unknown;
|
||||
_embed?: string | string[];
|
||||
_sort?: string;
|
||||
_start?: number;
|
||||
_end?: number;
|
||||
_limit?: number;
|
||||
_page?: number;
|
||||
_per_page?: number;
|
||||
}): Item[] | PaginatedItems | Item | undefined;
|
||||
create(name: string, data?: Omit<Item, 'id'>): Promise<Item | undefined>;
|
||||
update(name: string, body?: Item): Promise<Item | undefined>;
|
||||
patch(name: string, body?: Item): Promise<Item | undefined>;
|
||||
updateById(name: string, id: string, body?: Item): Promise<Item | undefined>;
|
||||
patchById(name: string, id: string, body?: Item): Promise<Item | undefined>;
|
||||
destroyById(name: string, id: string, dependent?: string | string[]): Promise<Item | undefined>;
|
||||
}
|
||||
333
25_02_24/node_modules/json-server/lib/service.js
generated
vendored
Normal file
333
25_02_24/node_modules/json-server/lib/service.js
generated
vendored
Normal file
@@ -0,0 +1,333 @@
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import { getProperty } from 'dot-prop';
|
||||
import inflection from 'inflection';
|
||||
import sortOn from 'sort-on';
|
||||
export function isItem(obj) {
|
||||
return typeof obj === 'object' && obj !== null;
|
||||
}
|
||||
export function isData(obj) {
|
||||
if (typeof obj !== 'object' || obj === null) {
|
||||
return false;
|
||||
}
|
||||
const data = obj;
|
||||
return Object.values(data).every((value) => Array.isArray(value) && value.every(isItem));
|
||||
}
|
||||
var Condition;
|
||||
(function (Condition) {
|
||||
Condition["lt"] = "lt";
|
||||
Condition["lte"] = "lte";
|
||||
Condition["gt"] = "gt";
|
||||
Condition["gte"] = "gte";
|
||||
Condition["ne"] = "ne";
|
||||
Condition["default"] = "";
|
||||
})(Condition || (Condition = {}));
|
||||
function isCondition(value) {
|
||||
return Object.values(Condition).includes(value);
|
||||
}
|
||||
function ensureArray(arg = []) {
|
||||
return Array.isArray(arg) ? arg : [arg];
|
||||
}
|
||||
function embed(db, name, item, related) {
|
||||
if (inflection.singularize(related) === related) {
|
||||
const relatedData = db.data[inflection.pluralize(related)];
|
||||
if (!relatedData) {
|
||||
return item;
|
||||
}
|
||||
const foreignKey = `${related}Id`;
|
||||
const relatedItem = relatedData.find((relatedItem) => {
|
||||
return relatedItem['id'] === item[foreignKey];
|
||||
});
|
||||
return { ...item, [related]: relatedItem };
|
||||
}
|
||||
const relatedData = db.data[related];
|
||||
if (!relatedData) {
|
||||
return item;
|
||||
}
|
||||
const foreignKey = `${inflection.singularize(name)}Id`;
|
||||
const relatedItems = relatedData.filter((relatedItem) => relatedItem[foreignKey] === item['id']);
|
||||
return { ...item, [related]: relatedItems };
|
||||
}
|
||||
function nullifyForeignKey(db, name, id) {
|
||||
const foreignKey = `${inflection.singularize(name)}Id`;
|
||||
Object.entries(db.data).forEach(([key, items]) => {
|
||||
// Skip
|
||||
if (key === name)
|
||||
return;
|
||||
// Nullify
|
||||
if (Array.isArray(items)) {
|
||||
items.forEach((item) => {
|
||||
if (item[foreignKey] === id) {
|
||||
item[foreignKey] = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function deleteDependents(db, name, dependents) {
|
||||
const foreignKey = `${inflection.singularize(name)}Id`;
|
||||
Object.entries(db.data).forEach(([key, items]) => {
|
||||
// Skip
|
||||
if (key === name || !dependents.includes(key))
|
||||
return;
|
||||
// Delete if foreign key is null
|
||||
if (Array.isArray(items)) {
|
||||
db.data[key] = items.filter((item) => item[foreignKey] !== null);
|
||||
}
|
||||
});
|
||||
}
|
||||
function randomId() {
|
||||
return randomBytes(2).toString('hex');
|
||||
}
|
||||
function fixItemsIds(items) {
|
||||
items.forEach((item) => {
|
||||
if (typeof item['id'] === 'number') {
|
||||
item['id'] = item['id'].toString();
|
||||
}
|
||||
if (item['id'] === undefined) {
|
||||
item['id'] = randomId();
|
||||
}
|
||||
});
|
||||
}
|
||||
// Ensure all items have an id
|
||||
function fixAllItemsIds(data) {
|
||||
Object.values(data).forEach((value) => {
|
||||
if (Array.isArray(value)) {
|
||||
fixItemsIds(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
export class Service {
|
||||
#db;
|
||||
constructor(db) {
|
||||
fixAllItemsIds(db.data);
|
||||
this.#db = db;
|
||||
}
|
||||
#get(name) {
|
||||
return this.#db.data[name];
|
||||
}
|
||||
has(name) {
|
||||
return Object.prototype.hasOwnProperty.call(this.#db?.data, name);
|
||||
}
|
||||
findById(name, id, query) {
|
||||
const value = this.#get(name);
|
||||
if (Array.isArray(value)) {
|
||||
let item = value.find((item) => item['id'] === id);
|
||||
ensureArray(query._embed).forEach((related) => {
|
||||
if (item !== undefined)
|
||||
item = embed(this.#db, name, item, related);
|
||||
});
|
||||
return item;
|
||||
}
|
||||
return;
|
||||
}
|
||||
find(name, query = {}) {
|
||||
let items = this.#get(name);
|
||||
if (!Array.isArray(items)) {
|
||||
return items;
|
||||
}
|
||||
// Include
|
||||
ensureArray(query._embed).forEach((related) => {
|
||||
if (items !== undefined && Array.isArray(items)) {
|
||||
items = items.map((item) => embed(this.#db, name, item, related));
|
||||
}
|
||||
});
|
||||
// Return list if no query params
|
||||
if (Object.keys(query).length === 0) {
|
||||
return items;
|
||||
}
|
||||
// Convert query params to conditions
|
||||
const conds = [];
|
||||
for (const [key, value] of Object.entries(query)) {
|
||||
if (value === undefined || typeof value !== 'string') {
|
||||
continue;
|
||||
}
|
||||
const re = /_(lt|lte|gt|gte|ne)$/;
|
||||
const reArr = re.exec(key);
|
||||
const op = reArr?.at(1);
|
||||
if (op && isCondition(op)) {
|
||||
const field = key.replace(re, '');
|
||||
conds.push([field, op, value]);
|
||||
continue;
|
||||
}
|
||||
if ([
|
||||
'_embed',
|
||||
'_sort',
|
||||
'_start',
|
||||
'_end',
|
||||
'_limit',
|
||||
'_page',
|
||||
'_per_page',
|
||||
].includes(key)) {
|
||||
continue;
|
||||
}
|
||||
conds.push([key, Condition.default, value]);
|
||||
}
|
||||
// Loop through conditions and filter items
|
||||
let filtered = items;
|
||||
for (const [key, op, paramValue] of conds) {
|
||||
filtered = filtered.filter((item) => {
|
||||
if (paramValue && !Array.isArray(paramValue)) {
|
||||
// https://github.com/sindresorhus/dot-prop/issues/95
|
||||
const itemValue = getProperty(item, key);
|
||||
switch (op) {
|
||||
// item_gt=value
|
||||
case Condition.gt: {
|
||||
if (!(typeof itemValue === 'number' &&
|
||||
itemValue > parseInt(paramValue))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// item_gte=value
|
||||
case Condition.gte: {
|
||||
if (!(typeof itemValue === 'number' &&
|
||||
itemValue >= parseInt(paramValue))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// item_lt=value
|
||||
case Condition.lt: {
|
||||
if (!(typeof itemValue === 'number' &&
|
||||
itemValue < parseInt(paramValue))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// item_lte=value
|
||||
case Condition.lte: {
|
||||
if (!(typeof itemValue === 'number' &&
|
||||
itemValue <= parseInt(paramValue))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// item_ne=value
|
||||
case Condition.ne: {
|
||||
switch (typeof itemValue) {
|
||||
case 'number':
|
||||
return itemValue !== parseInt(paramValue);
|
||||
case 'string':
|
||||
return itemValue !== paramValue;
|
||||
case 'boolean':
|
||||
return itemValue !== (paramValue === 'true');
|
||||
}
|
||||
break;
|
||||
}
|
||||
// item=value
|
||||
case Condition.default: {
|
||||
switch (typeof itemValue) {
|
||||
case 'number':
|
||||
return itemValue === parseInt(paramValue);
|
||||
case 'string':
|
||||
return itemValue === paramValue;
|
||||
case 'boolean':
|
||||
return itemValue === (paramValue === 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
// Sort
|
||||
const sort = query._sort || '';
|
||||
const sorted = sortOn(filtered, sort.split(','));
|
||||
// Slice
|
||||
const start = query._start;
|
||||
const end = query._end;
|
||||
const limit = query._limit;
|
||||
if (start !== undefined) {
|
||||
if (end !== undefined) {
|
||||
return sorted.slice(start, end);
|
||||
}
|
||||
return sorted.slice(start, start + (limit || 0));
|
||||
}
|
||||
if (limit !== undefined) {
|
||||
return sorted.slice(0, limit);
|
||||
}
|
||||
// Paginate
|
||||
let page = query._page;
|
||||
const perPage = query._per_page || 10;
|
||||
if (page) {
|
||||
const items = sorted.length;
|
||||
const pages = Math.ceil(items / perPage);
|
||||
// Ensure page is within the valid range
|
||||
page = Math.max(1, Math.min(page, pages));
|
||||
const first = 1;
|
||||
const prev = page > 1 ? page - 1 : null;
|
||||
const next = page < pages ? page + 1 : null;
|
||||
const last = pages;
|
||||
const start = (page - 1) * perPage;
|
||||
const end = start + perPage;
|
||||
const data = sorted.slice(start, end);
|
||||
return {
|
||||
first,
|
||||
prev,
|
||||
next,
|
||||
last,
|
||||
pages,
|
||||
items,
|
||||
data,
|
||||
};
|
||||
}
|
||||
return sorted.slice(start, end);
|
||||
}
|
||||
async create(name, data = {}) {
|
||||
const items = this.#get(name);
|
||||
if (items === undefined || !Array.isArray(items))
|
||||
return;
|
||||
const item = { id: randomId(), ...data };
|
||||
items.push(item);
|
||||
await this.#db.write();
|
||||
return item;
|
||||
}
|
||||
async #updateOrPatch(name, body = {}, isPatch) {
|
||||
const item = this.#get(name);
|
||||
if (item === undefined || Array.isArray(item))
|
||||
return;
|
||||
const nextItem = (this.#db.data[name] = isPatch ? { item, ...body } : body);
|
||||
await this.#db.write();
|
||||
return nextItem;
|
||||
}
|
||||
async #updateOrPatchById(name, id, body = {}, isPatch) {
|
||||
const items = this.#get(name);
|
||||
if (items === undefined || !Array.isArray(items))
|
||||
return;
|
||||
const item = items.find((item) => item['id'] === id);
|
||||
if (!item)
|
||||
return;
|
||||
const nextItem = isPatch ? { ...item, ...body, id } : { ...body, id };
|
||||
const index = items.indexOf(item);
|
||||
items.splice(index, 1, nextItem);
|
||||
await this.#db.write();
|
||||
return nextItem;
|
||||
}
|
||||
async update(name, body = {}) {
|
||||
return this.#updateOrPatch(name, body, false);
|
||||
}
|
||||
async patch(name, body = {}) {
|
||||
return this.#updateOrPatch(name, body, true);
|
||||
}
|
||||
async updateById(name, id, body = {}) {
|
||||
return this.#updateOrPatchById(name, id, body, false);
|
||||
}
|
||||
async patchById(name, id, body = {}) {
|
||||
return this.#updateOrPatchById(name, id, body, true);
|
||||
}
|
||||
async destroyById(name, id, dependent) {
|
||||
const items = this.#get(name);
|
||||
if (items === undefined || !Array.isArray(items))
|
||||
return;
|
||||
const item = items.find((item) => item['id'] === id);
|
||||
if (item === undefined)
|
||||
return;
|
||||
const index = items.indexOf(item);
|
||||
items.splice(index, 1);
|
||||
nullifyForeignKey(this.#db, name, id);
|
||||
const dependents = ensureArray(dependent);
|
||||
deleteDependents(this.#db, name, dependents);
|
||||
await this.#db.write();
|
||||
return item;
|
||||
}
|
||||
}
|
||||
63
25_02_24/node_modules/json-server/package.json
generated
vendored
Normal file
63
25_02_24/node_modules/json-server/package.json
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "json-server",
|
||||
"version": "1.0.0-beta.3",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"json-server": "lib/bin.js"
|
||||
},
|
||||
"types": "lib",
|
||||
"files": [
|
||||
"lib",
|
||||
"views"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18.3"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "tsx watch src/bin.ts fixtures/db.json",
|
||||
"build": "rm -rf lib && tsc",
|
||||
"test": "node --import tsx/esm --test src/*.test.ts",
|
||||
"lint": "eslint src",
|
||||
"prepare": "husky",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "typicode <typicode@gmail.com>",
|
||||
"license": "SEE LICENSE IN ./LICENSE",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/typicode/json-server.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.11.0",
|
||||
"@sindresorhus/tsconfig": "^6.0.0",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@types/node": "^22.5.5",
|
||||
"concurrently": "^9.0.1",
|
||||
"eslint": "^9.11.0",
|
||||
"get-port": "^7.1.0",
|
||||
"globals": "^15.9.0",
|
||||
"husky": "^9.1.6",
|
||||
"tempy": "^3.1.0",
|
||||
"tsx": "^4.19.1",
|
||||
"type-fest": "^4.26.1",
|
||||
"typescript": "^5.6.2",
|
||||
"typescript-eslint": "^8.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tinyhttp/app": "^2.4.0",
|
||||
"@tinyhttp/cors": "^2.0.1",
|
||||
"@tinyhttp/logger": "^2.0.0",
|
||||
"chalk": "^5.3.0",
|
||||
"chokidar": "^4.0.1",
|
||||
"dot-prop": "^9.0.0",
|
||||
"eta": "^3.5.0",
|
||||
"inflection": "^3.0.0",
|
||||
"json5": "^2.2.3",
|
||||
"lowdb": "^7.0.1",
|
||||
"milliparsec": "^4.0.0",
|
||||
"sirv": "^2.0.4",
|
||||
"sort-on": "^6.1.0"
|
||||
}
|
||||
}
|
||||
97
25_02_24/node_modules/json-server/views/index.html
generated
vendored
Normal file
97
25_02_24/node_modules/json-server/views/index.html
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<style>
|
||||
html {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0 auto;
|
||||
max-width: 720px;
|
||||
padding: 0 16px;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #db2777;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 32px;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
nav div a {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Dark mode styles */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
background-color: #1e293b;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<nav>
|
||||
<strong>JSON Server</strong>
|
||||
<div>
|
||||
<a href="https://github.com/typicode/json-server">Docs</a>
|
||||
<a href="https://github.com/sponsors/typicode">♡ Sponsor</a>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<main class="my-12">
|
||||
<p class="bg-gradient-to-r from-purple-500 via-pink-500 to-red-500 text-transparent bg-clip-text">✧*。٩(ˊᗜˋ*)و✧*。</p>
|
||||
<% if (Object.keys(it.data).length===0) { %>
|
||||
<p>No resources found in JSON file</p>
|
||||
<% } %>
|
||||
<% Object.entries(it.data).forEach(function([name]) { %>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="<%= name %>">/<%= name %></a>
|
||||
<span>
|
||||
<% if (Array.isArray(it.data[name])) { %>
|
||||
- <%= it.data[name].length %>
|
||||
<%= it.data[name].length> 1 ? 'items' : 'item' %>
|
||||
</span>
|
||||
<% } %>
|
||||
</li>
|
||||
</ul>
|
||||
<% }) %>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user