@kollors/deep-json-server 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +19 -16
  2. package/README.ru.md +20 -17
  3. package/dist/bin/deep-json-server.d.ts +2 -0
  4. package/dist/bin/deep-json-server.js +11 -0
  5. package/dist/bin/deep-json-server.js.map +1 -0
  6. package/dist/index.d.ts +5 -0
  7. package/dist/index.js +2 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/src/cli.d.ts +4 -0
  10. package/dist/src/cli.js +80 -0
  11. package/dist/src/cli.js.map +1 -0
  12. package/dist/src/config.d.ts +54 -0
  13. package/dist/src/config.js +145 -0
  14. package/dist/src/config.js.map +1 -0
  15. package/{src → dist/src}/constants.js +1 -0
  16. package/dist/src/constants.js.map +1 -0
  17. package/dist/src/database.d.ts +20 -0
  18. package/dist/src/database.js +136 -0
  19. package/dist/src/database.js.map +1 -0
  20. package/dist/src/files/contract.d.ts +67 -0
  21. package/dist/src/files/contract.js +106 -0
  22. package/dist/src/files/contract.js.map +1 -0
  23. package/dist/src/files/disk-store.d.ts +5 -0
  24. package/dist/src/files/disk-store.js +313 -0
  25. package/dist/src/files/disk-store.js.map +1 -0
  26. package/dist/src/files/index.d.ts +5 -0
  27. package/dist/src/files/index.js +6 -0
  28. package/dist/src/files/index.js.map +1 -0
  29. package/dist/src/files/memory-store.d.ts +3 -0
  30. package/dist/src/files/memory-store.js +80 -0
  31. package/dist/src/files/memory-store.js.map +1 -0
  32. package/dist/src/files/routes.d.ts +6 -0
  33. package/dist/src/files/routes.js +77 -0
  34. package/dist/src/files/routes.js.map +1 -0
  35. package/dist/src/openapi/config.d.ts +11 -0
  36. package/dist/src/openapi/config.js +204 -0
  37. package/dist/src/openapi/config.js.map +1 -0
  38. package/dist/src/openapi/document.d.ts +9 -0
  39. package/dist/src/openapi/document.js +338 -0
  40. package/dist/src/openapi/document.js.map +1 -0
  41. package/dist/src/openapi/index.d.ts +10 -0
  42. package/dist/src/openapi/index.js +27 -0
  43. package/dist/src/openapi/index.js.map +1 -0
  44. package/dist/src/openapi/inference.d.ts +14 -0
  45. package/dist/src/openapi/inference.js +145 -0
  46. package/dist/src/openapi/inference.js.map +1 -0
  47. package/dist/src/query/filter.d.ts +6 -0
  48. package/dist/src/query/filter.js +248 -0
  49. package/dist/src/query/filter.js.map +1 -0
  50. package/{src → dist/src}/query/index.js +1 -0
  51. package/dist/src/query/index.js.map +1 -0
  52. package/dist/src/query/pagination.d.ts +11 -0
  53. package/dist/src/query/pagination.js +28 -0
  54. package/dist/src/query/pagination.js.map +1 -0
  55. package/dist/src/query/sort.d.ts +1 -0
  56. package/dist/src/query/sort.js +54 -0
  57. package/dist/src/query/sort.js.map +1 -0
  58. package/dist/src/relation-metadata.d.ts +10 -0
  59. package/dist/src/relation-metadata.js +32 -0
  60. package/dist/src/relation-metadata.js.map +1 -0
  61. package/dist/src/relations.d.ts +12 -0
  62. package/dist/src/relations.js +155 -0
  63. package/dist/src/relations.js.map +1 -0
  64. package/dist/src/server.d.ts +13 -0
  65. package/dist/src/server.js +188 -0
  66. package/dist/src/server.js.map +1 -0
  67. package/dist/src/types.d.ts +52 -0
  68. package/dist/src/types.js +2 -0
  69. package/dist/src/types.js.map +1 -0
  70. package/dist/src/utils.d.ts +20 -0
  71. package/dist/src/utils.js +63 -0
  72. package/dist/src/utils.js.map +1 -0
  73. package/package.json +17 -16
  74. package/bin/deep-json-server.js +0 -11
  75. package/index.js +0 -12
  76. package/src/cli.js +0 -91
  77. package/src/config.js +0 -203
  78. package/src/database.js +0 -148
  79. package/src/files.js +0 -633
  80. package/src/openapi/config.js +0 -124
  81. package/src/openapi/document.js +0 -394
  82. package/src/openapi/index.js +0 -36
  83. package/src/openapi/inference.js +0 -187
  84. package/src/query/filter.js +0 -283
  85. package/src/query/pagination.js +0 -44
  86. package/src/query/sort.js +0 -63
  87. package/src/relation-metadata.js +0 -41
  88. package/src/relations.js +0 -173
  89. package/src/server.js +0 -250
  90. package/src/utils.js +0 -84
  91. package/types/index.d.ts +0 -21
  92. package/types/src/config.d.ts +0 -73
  93. package/types/src/database.d.ts +0 -33
  94. package/types/src/files.d.ts +0 -64
  95. package/types/src/openapi/config.d.ts +0 -3
  96. package/types/src/openapi/document.d.ts +0 -11
  97. package/types/src/openapi/index.d.ts +0 -9
  98. package/types/src/openapi/inference.d.ts +0 -9
  99. package/types/src/query/filter.d.ts +0 -3
  100. package/types/src/query/pagination.d.ts +0 -8
  101. package/types/src/query/sort.d.ts +0 -1
  102. package/types/src/relation-metadata.d.ts +0 -9
  103. package/types/src/relations.d.ts +0 -3
  104. package/types/src/server.d.ts +0 -14
  105. package/types/src/utils.d.ts +0 -18
  106. /package/{types → dist}/src/constants.d.ts +0 -0
  107. /package/{types → dist}/src/query/index.d.ts +0 -0
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [GitHub](https://github.com/kollors/deep-json-server) | [npm](https://www.npmjs.com/package/@kollors/deep-json-server)
6
6
 
7
- A small REST mock server with CRUD, pagination, deep filtering, recursive relationship embedding, binary files, and OpenAPI generation. Data can be stored in JSON files or memory, and relations are inferred from conventional keys such as `countryId`, `genreIds`, and `publisherIds`.
7
+ A small REST API mock server with CRUD, pagination, nested data filtering, relation embedding through `_embed`, binary files, and OpenAPI generation. Data can be stored in JSON files or memory, and relations are inferred from field names such as `countryId`, `genreIds`, and `publisherIds`.
8
8
 
9
9
  ## Installation
10
10
 
@@ -53,7 +53,7 @@ The API is available at `http://127.0.0.1:4001` by default. For example, `GET ht
53
53
 
54
54
  ## Configuration and startup
55
55
 
56
- Create the ESM module `server.config.js`. The example below enables every feature:
56
+ Create the ESM module `server.config.js`. The example below shows settings for every feature:
57
57
 
58
58
  ```js
59
59
  import process from 'node:process';
@@ -71,6 +71,7 @@ export default {
71
71
  path: 'mock/openapi-schema.yaml',
72
72
  },
73
73
  server: {
74
+ cors: true,
74
75
  host: '127.0.0.1',
75
76
  logger: true,
76
77
  maxFileSize: 100 * 1024 * 1024,
@@ -91,6 +92,7 @@ Configuration keys:
91
92
  | `files.metadata` | Together with `files.directory` | JSON file containing file metadata on disk |
92
93
  | `files.data` | Instead of the `directory` and `metadata` pair | In-memory files with `Uint8Array` contents |
93
94
  | `openapi.path` | Required by the `--openapi` and `--openapi-only` CLI flags | Generated OpenAPI YAML file; the programmatic API can return a document without this path |
95
+ | `server.cors` | No | Enables permissive CORS headers and `OPTIONS` routes; defaults to `true` |
94
96
  | `server.host` | No | Host used by the CLI, `server.openapi()`, and argument-less `server.fastify().listen()`; defaults to `127.0.0.1` |
95
97
  | `server.logger` | No | Fastify logger settings; defaults to `true` |
96
98
  | `server.maxFileSize` | No | Maximum uploaded-file size in bytes when file routes are enabled; defaults to 100 MiB |
@@ -219,7 +221,7 @@ DELETE /movies/:id
219
221
 
220
222
  `POST` generates a string ID. `PUT` completely replaces the selected record, while `PATCH` updates only supplied fields; both preserve the existing ID and its type. An `id` supplied in any request body cannot override the server-controlled ID. All write operations — `POST`, `PUT`, `PATCH` and `DELETE` — are serialized; disk storage persists them in JSON, while memory storage retains them until the process exits.
221
223
 
222
- The database file must exist before startup. Resource names may contain Latin letters, numbers, `_` and `-`, and must start with a letter. Every resource is an array of JSON objects. Every record must have a non-empty string or finite numeric `id`; IDs must be unique within a resource when compared as strings, so `1` and `"1"` cannot coexist. The server rereads the file before every GET and write operation, so valid external edits become visible without a restart.
224
+ The database file must exist before startup. Resource names may contain Latin letters, numbers, `_` and `-`, and must start with a letter. Every resource is an array of JSON objects. Every record must have a non-empty string or finite numeric `id`; IDs must be unique within a resource when compared as strings, so `1` and `"1"` cannot coexist. All nested values must be JSON-compatible: finite numbers, strings, booleans, `null`, arrays, and plain objects. The server rereads the file before every resource GET and write operation, so valid edits to existing resources become visible immediately. Resource names and routes are discovered at startup; restart the server after adding, removing, or renaming a top-level resource.
223
225
 
224
226
  Successful writes return the created, replaced, updated or deleted record. Errors use an appropriate HTTP status and this JSON shape:
225
227
 
@@ -301,7 +303,7 @@ GET /movies?title:contains=ardenia
301
303
 
302
304
  Simple filter values recognize JSON primitives: numbers, `true`, `false` and `null`. Values with leading zeroes, such as `001`, remain strings. Unknown operators, invalid logical conditions and filter paths that do not exist in a non-empty resource return `400`.
303
305
 
304
- Multiple simple query filters are combined with `AND`. For an `in` filter, separate values with commas: `GET /movies?id:in=1,2`. On an array field, `in` means that at least one field element matches at least one supplied value. `every` returns `true` for an empty array, while `some` returns `false`.
306
+ Different simple query filters are combined with `AND`. Repeating the same equality filter selects any of its values, so `GET /movies?id=1&id=2` is equivalent to `GET /movies?id:in=1,2`. For an `in` filter, values are separated with commas. On an array field, `in` means that at least one field element matches at least one supplied value. `every` returns `true` for an empty array, while `some` returns `false`.
305
307
 
306
308
  If `_where` is present, it is the complete filter and other simple filter parameters are ignored. The examples show readable JSON; an HTTP client must URL-encode `_where` when constructing the URL manually, for example with `encodeURIComponent(JSON.stringify(where))`.
307
309
 
@@ -340,9 +342,9 @@ Relations are inferred by convention. A field named `<relation>Id` creates a sin
340
342
  - `publisherIds` points to `publishers`;
341
343
  - `parentIds` points back to the current resource when `_embed=parents` is requested; `_embed=children` resolves the reverse self-relation.
342
344
 
343
- Reverse relations use the source resource name. For example, `_embed=users` on a country finds users whose nested data contains the corresponding `countryId`. They are soft references: the server resolves them when requested but does not enforce referential integrity when data is written.
345
+ Reverse relations use the source resource name. For example, `_embed=users` on a country finds users whose nested data contains the corresponding `countryId`. Relations are resolved when requested, but referential integrity is not enforced when data is written.
344
346
 
345
- An explicit `...Id` or `...Ids` field is the source of truth. If a record also contains an outdated embedded value, `_embed` replaces that response property with the current related record. A missing target becomes `null` for a single relation or is omitted from the resulting array for a collection relation. Relationship lookups use lazy per-request ID indexes, so each referenced resource is indexed only when needed.
347
+ Explicit `...Id` and `...Ids` fields determine relations. If a record also contains an outdated embedded value, `_embed` replaces that response property with the current related record. A missing related record becomes `null` for a single relation or is omitted from the resulting array for a collection relation. Per-request ID indexes are created only for resources used by the current relation lookup.
346
348
 
347
349
  ## Files
348
350
 
@@ -403,9 +405,9 @@ Content-Type: application/json
403
405
  }
404
406
  ```
405
407
 
406
- `PATCH` returns the updated metadata with status `200`; a conflicting destination returns `409`. `DELETE` returns `204` without a response body. A missing file returns `404` on every path-based operation. File paths in URLs are relative to `files.directory`, and all returned URLs are relative to the mock-server origin.
408
+ `PATCH` returns the updated metadata with status `200`; if a file already exists at the new path, the server returns `409`. `DELETE` returns `204` without a response body. A missing file returns `404` on every path-based operation. File paths in URLs are relative to `files.directory`, and all returned URLs are relative to the server origin.
407
409
 
408
- In disk mode, the binary is stored at `<files.directory>/<directory>/<name>`. The metadata file contains only `directory`, `mimeType`, and `name`; `size` is read from the actual file, while response URLs are computed. The server creates directories automatically. The metadata file may be absent initially and is created on the first upload. Do not edit stored files or metadata while the server is running. Metadata created by versions before this path-based API is not compatible with the new format.
410
+ In disk mode, the binary is stored at `<files.directory>/<directory>/<name>`. The metadata file contains only `directory`, `mimeType`, and `name`; `size` is read from the actual file, while response URLs are computed. The server creates directories automatically and keeps validated metadata in memory while running. Use a disk-backed database and file storage from only one server process at a time, and do not edit stored files or metadata until that process stops. Paths below `files.directory` may not contain symbolic links, and file names are restricted to values that are portable across supported operating systems. The metadata file may be absent initially and is created on the first upload. Metadata created by versions before this path-based API is not compatible with the new format.
409
411
 
410
412
  The upload is raw binary rather than `multipart/form-data`, so `XMLHttpRequest.upload.onprogress` can report progress while the browser sends a `File` directly with `xhr.send(file)`. The default maximum size is 100 MiB and can be changed through `server.maxFileSize`. Missing or unsafe headers and paths return `400`, an exceeded limit returns `413`, and a missing, malformed, or Fastify-unsupported `Content-Type` returns `400` or `415`, depending on which validation stage rejects it.
411
413
 
@@ -459,7 +461,7 @@ To include file routes in the document, configure the `files` section and add `-
459
461
 
460
462
  The generator infers resources and field types from all database records. Every inferred field is optional by default, while the top-level `id` is always required in response schemas and is omitted from create and update request schemas. Add other required fields to `required`. A nested required path marks that nested property as required; it does not automatically make every parent path required, so list the parent separately when necessary.
461
463
 
462
- Different value types are inferred independently and combined through `oneOf`. Configuration is validated before generation: `$info`, resource and schema names, and the structure of `properties` are validated, while paths from `required` and `formats` must exist in the resulting schema.
464
+ Different non-overlapping value types are inferred independently and combined through `oneOf`; mixed integers and decimal numbers are represented by one `number` schema. Configuration is validated before generation: `$info`, resource and component names, supported `properties` keywords and their value types are checked, while paths from `required` and `formats` must exist in the resulting schema. Explicit component names may contain ASCII letters, digits, dots, underscores, and hyphens.
463
465
 
464
466
  Use `properties` to describe fields that cannot be inferred, particularly for an empty resource. Explicit properties are merged with inferred properties:
465
467
 
@@ -493,7 +495,7 @@ Use `name` when a resource needs an explicit schema name instead of the automati
493
495
  }
494
496
  ```
495
497
 
496
- The generated document describes CRUD endpoints, pagination, sorting, deep filters, `_embed`, and both direct and reverse response relations inferred from `...Id` and `...Ids` fields. When `--files` is present, it also describes raw binary upload, download and deletion endpoints. A numeric database ID is described as `integer | string`, because a later `POST` creates a string ID in the same resource. The document can be used as input for tools such as RTK Query OpenAPI Codegen. OpenAPI is generated only with `--openapi` or `--openapi-only`; normal server startup does not rewrite the file.
498
+ The generated document describes CRUD endpoints, pagination, sorting, nested data filters, `_embed`, and both direct and reverse response relations inferred from `...Id` and `...Ids` fields. When `--files` is present, it also describes raw binary upload, download and deletion endpoints for arbitrary media types. A numeric database ID is described as `integer | string`, because a later `POST` creates a string ID in the same resource. Generation rejects duplicate schema names, operation IDs, and invalid schema overrides before producing a document. The document can be used as input for tools such as RTK Query OpenAPI Codegen. OpenAPI is generated only with `--openapi` or `--openapi-only`; normal server startup does not rewrite the file.
497
499
 
498
500
  During normal startup, request bodies are validated against the same inferred and configured schemas. `POST` and `PUT` enforce configured required fields; `PATCH` validates only fields that are actually supplied. `formats` and `properties` apply to all three methods. Unlisted additional object fields remain allowed. Invalid bodies return `400`.
499
501
 
@@ -515,6 +517,7 @@ const config = {
515
517
  path: 'mock/openapi-schema.yaml',
516
518
  },
517
519
  server: {
520
+ cors: true,
518
521
  host: '127.0.0.1',
519
522
  logger: false,
520
523
  maxFileSize: 100 * 1024 * 1024,
@@ -563,17 +566,17 @@ console.log(memoryResponse.json());
563
566
  await memoryFastify.close();
564
567
  ```
565
568
 
566
- `createServer()` accepts exactly the same config shape as `server.config.js`. It loads and clones the configured sources, then returns a facade with two operations:
569
+ `createServer()` accepts exactly the same config shape as `server.config.js`. It loads and clones the database, schema, and file storage, then returns an object with two methods:
567
570
 
568
- | Member | Meaning |
569
- | --- | --- | --- |
571
+ | Method | Purpose |
572
+ | --- | --- |
570
573
  | `server.fastify()` | Lazily creates and caches the real Fastify instance; every native method remains available, and argument-less `listen()` uses `server.host` and `server.port` |
571
574
  | `server.openapi()` | Returns an OpenAPI document and also writes it when `openapi.path` is configured |
572
575
 
573
- File routes are enabled programmatically when a `files` section is present. The second argument has the shape `{ files?: boolean }`: pass `{ files: false }` to keep a configured store disabled, or `{ files: true }` to require a `files` section and enable the routes. `server.openapi()` uses the same feature state as `server.fastify()`.
576
+ File routes are enabled programmatically when a `files` section is present. The second argument has the shape `{ files?: boolean }`: pass `{ files: false }` to keep a configured store disabled, or `{ files: true }` to require a `files` section and enable the routes. `server.openapi()` uses the same file-route setting as `server.fastify()`.
574
577
 
575
- An argument-less `server.fastify().listen()` uses `server.host` and `server.port`, falling back to `127.0.0.1:4001`. Explicit `listen(options)` values take precedence. Relative paths passed directly to `createServer()` resolve from the current working directory; paths loaded from `server.config.js` resolve from the config directory. The package includes generated TypeScript declarations for the facade and every config variant.
578
+ An argument-less `server.fastify().listen()` uses `server.host` and `server.port`, falling back to `127.0.0.1:4001`. Explicit `listen(options)` values take precedence. Relative paths passed directly to `createServer()` resolve from the current working directory; paths loaded from `server.config.js` resolve from the config directory. The package includes generated TypeScript declarations for the returned object and every config variant.
576
579
 
577
580
  ## Scope and security
578
581
 
579
- Deep JSON Server is intended for local development and automated tests. It has no authentication or authorization, allows CORS from every origin, persists accepted writes when disk storage is configured and does not enforce referential integrity. Keep the default loopback host unless the surrounding environment provides its own access controls; do not expose the server or file routes to an untrusted network.
582
+ Deep JSON Server is intended for local development and automated tests. It has no authentication or authorization, allows CORS from every origin by default, persists accepted writes when disk storage is configured and does not enforce referential integrity. Set `server.cors` to `false` to disable the built-in CORS headers and `OPTIONS` routes. Keep the default loopback host unless the surrounding environment provides its own access controls; do not expose the server or file routes to an untrusted network.
package/README.ru.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [GitHub](https://github.com/kollors/deep-json-server) | [npm](https://www.npmjs.com/package/@kollors/deep-json-server)
6
6
 
7
- Небольшой моковый REST-сервер с CRUD, пагинацией, глубокими фильтрами, рекурсивной загрузкой связей, бинарными файлами и генерацией OpenAPI. Данные могут храниться в JSON-файлах или памяти, а связи определяются по соглашениям о нейминге ключей: `countryId`, `genreIds`, `publisherIds` и так далее.
7
+ Небольшой сервер для имитации REST API с CRUD, пагинацией, фильтрацией вложенных данных, подстановкой связанных записей через `_embed`, бинарными файлами и генерацией OpenAPI. Данные могут храниться в JSON-файлах или памяти, а связи определяются по именам полей: `countryId`, `genreIds`, `publisherIds` и так далее.
8
8
 
9
9
  ## Установка
10
10
 
@@ -53,7 +53,7 @@ npx deep-json-server server.config.js
53
53
 
54
54
  ## Конфигурация и запуск
55
55
 
56
- Создайте ESM-модуль `server.config.js`. В примере ниже включены настройки всех возможностей:
56
+ Создайте ESM-модуль `server.config.js`. В примере ниже показаны настройки всех возможностей:
57
57
 
58
58
  ```js
59
59
  import process from 'node:process';
@@ -71,6 +71,7 @@ export default {
71
71
  path: 'mock/openapi-schema.yaml',
72
72
  },
73
73
  server: {
74
+ cors: true,
74
75
  host: '127.0.0.1',
75
76
  logger: true,
76
77
  maxFileSize: 100 * 1024 * 1024,
@@ -91,6 +92,7 @@ export default {
91
92
  | `files.metadata` | Вместе с `files.directory` | JSON-файл с метаданными файлов на диске |
92
93
  | `files.data` | Вместо пары `directory` и `metadata` | Файлы в памяти с содержимым в `Uint8Array` |
93
94
  | `openapi.path` | Обязателен для CLI-флагов `--openapi` и `--openapi-only` | Генерируемый YAML-файл OpenAPI; программный API может вернуть документ без этого пути |
95
+ | `server.cors` | Необязателен | Включает разрешающие CORS-заголовки и маршруты `OPTIONS`; по умолчанию `true` |
94
96
  | `server.host` | Необязателен | Адрес для CLI, `server.openapi()` и вызова `server.fastify().listen()` без аргументов; по умолчанию `127.0.0.1` |
95
97
  | `server.logger` | Необязателен | Настройки логгера Fastify; по умолчанию `true` |
96
98
  | `server.maxFileSize` | Необязателен | Максимальный размер загружаемого файла в байтах при включённых файловых маршрутах; по умолчанию 100 МиБ |
@@ -219,7 +221,7 @@ DELETE /movies/:id
219
221
 
220
222
  `POST` генерирует строковый ID. `PUT` полностью заменяет выбранную запись, а `PATCH` изменяет только переданные поля; обе операции сохраняют существующий ID и его тип. Поле `id` в теле любого запроса не может переопределить ID, которым управляет сервер. Все операции записи — `POST`, `PUT`, `PATCH` и `DELETE` — выполняются последовательно; дисковое хранилище записывает их в JSON, а хранилище в памяти сохраняет до завершения процесса.
221
223
 
222
- Файл базы должен существовать до запуска. Имена ресурсов могут содержать латинские буквы, цифры, `_` и `-` и должны начинаться с буквы. Каждый ресурс является массивом JSON-объектов. У каждой записи должен быть непустой строковый или конечный числовой `id`; ID должны быть уникальны внутри ресурса при сравнении как строки, поэтому `1` и `"1"` не могут существовать одновременно. Перед каждым GET-запросом и изменением сервер заново читает файл, поэтому корректные внешние правки становятся видны без перезапуска.
224
+ Файл базы должен существовать до запуска. Имена ресурсов могут содержать латинские буквы, цифры, `_` и `-` и должны начинаться с буквы. Каждый ресурс является массивом JSON-объектов. У каждой записи должен быть непустой строковый или конечный числовой `id`; ID должны быть уникальны внутри ресурса при сравнении как строки, поэтому `1` и `"1"` не могут существовать одновременно. Все вложенные значения должны быть совместимы с JSON: конечные числа, строки, логические значения, `null`, массивы и обычные объекты. Перед каждым GET-запросом к ресурсу и изменением сервер заново читает файл, поэтому корректные правки существующих ресурсов становятся видны сразу. Имена ресурсов и маршруты определяются при запуске; после добавления, удаления или переименования массива верхнего уровня перезапустите сервер.
223
225
 
224
226
  Успешная операция записи возвращает созданную, заменённую, обновлённую или удалённую запись. Ошибки используют подходящий HTTP-статус и следующий JSON-формат:
225
227
 
@@ -301,7 +303,7 @@ GET /movies?title:contains=тени
301
303
 
302
304
  В простых фильтрах распознаются JSON-примитивы: числа, `true`, `false` и `null`. Значения с ведущими нулями, например `001`, остаются строками. Неизвестные операторы, некорректные логические условия и отсутствующие в непустом ресурсе пути фильтра возвращают `400`.
303
305
 
304
- Несколько простых query-фильтров объединяются через `AND`. Для оператора `in` перечислите значения через запятую: `GET /movies?id:in=1,2`. Для поля-массива `in` означает, что хотя бы один элемент поля совпадает хотя бы с одним переданным значением. `every` для пустого массива возвращает `true`, а `some` — `false`.
306
+ Разные простые query-фильтры объединяются через `AND`. Повтор одного фильтра равенства выбирает любое из его значений, поэтому `GET /movies?id=1&id=2` равнозначен `GET /movies?id:in=1,2`. Для оператора `in` значения перечисляются через запятую. Для поля-массива `in` означает, что хотя бы один элемент поля совпадает хотя бы с одним переданным значением. `every` для пустого массива возвращает `true`, а `some` — `false`.
305
307
 
306
308
  Если передан `_where`, он становится полным фильтром, а остальные простые параметры фильтрации игнорируются. В примерах JSON оставлен читаемым; при ручном формировании URL HTTP-клиент должен закодировать значение `_where`, например через `encodeURIComponent(JSON.stringify(where))`.
307
309
 
@@ -332,7 +334,7 @@ GET /genres/2?_embed=parents.parents
332
334
  GET /countries/1?_embed=users
333
335
  ```
334
336
 
335
- Связи определяются по неймингу. Поле `<relation>Id` создаёт одиночную связь, а `<relation>Ids` — связь с коллекцией. Имя связи сопоставляется с ресурсом верхнего уровня напрямую или через его форму в единственном числе. Например:
337
+ Связи определяются по именам полей. Поле `<relation>Id` создаёт одиночную связь, а `<relation>Ids` — связь с коллекцией. Имя связи сопоставляется с ресурсом верхнего уровня напрямую или через его форму в единственном числе. Например:
336
338
 
337
339
  - `countryId` ссылается на `countries`;
338
340
  - `userId` ссылается на `users`, если запрошена связь `user`;
@@ -340,9 +342,9 @@ GET /countries/1?_embed=users
340
342
  - `publisherIds` ссылается на `publishers`;
341
343
  - `parentIds` ссылается на тот же ресурс, если запрошена связь `_embed=parents`; `_embed=children` загружает обратную связь с дочерними записями.
342
344
 
343
- Имя обратной связи совпадает с именем исходного ресурса. Например, `_embed=users` у страны находит пользователей, во вложенных данных которых указан соответствующий `countryId`. Это мягкие ссылки: сервер загружает их по запросу, но не проверяет ссылочную целостность при записи данных.
345
+ Имя обратной связи совпадает с именем исходного ресурса. Например, `_embed=users` у страны находит пользователей, во вложенных данных которых указан соответствующий `countryId`. Сервер получает связи по запросу, но не проверяет ссылочную целостность при записи данных.
344
346
 
345
- Явное поле `...Id` или `...Ids` считается источником истины. Если в записи также сохранено устаревшее вложенное значение, `_embed` заменяет это свойство ответа актуальной связанной записью. Если цель одиночной связи не найдена, результатом будет `null`; отсутствующие цели связи с коллекцией не попадут в итоговый массив. Для поиска связей лениво создаются ID-индексы только используемых в текущем запросе ресурсов.
347
+ Явные поля `...Id` и `...Ids` определяют связи. Если в записи также сохранено устаревшее вложенное значение, `_embed` заменяет это свойство ответа актуальной связанной записью. Если связанная запись для одиночной связи не найдена, результатом будет `null`; отсутствующие связанные записи коллекции не попадут в итоговый массив. ID-индексы создаются только для ресурсов, используемых при поиске связей в текущем запросе.
346
348
 
347
349
  ## Файлы
348
350
 
@@ -403,9 +405,9 @@ Content-Type: application/json
403
405
  }
404
406
  ```
405
407
 
406
- `PATCH` возвращает обновлённые метаданные со статусом `200`; занятый конечный путь возвращает `409`. `DELETE` отвечает статусом `204` без тела. Если файл не найден, любая операция по пути возвращает `404`. Пути в URL задаются относительно `files.directory`, а все возвращаемые URL — относительно адреса mock-сервера.
408
+ `PATCH` возвращает обновлённые метаданные со статусом `200`; если по новому пути уже существует файл, сервер возвращает `409`. `DELETE` отвечает статусом `204` без тела. Если файл не найден, любая операция по пути возвращает `404`. Пути в URL задаются относительно `files.directory`, а все возвращаемые URL — относительно адреса сервера.
407
409
 
408
- При хранении на диске бинарный файл находится по пути `<files.directory>/<directory>/<name>`. Файл метаданных содержит только `directory`, `mimeType` и `name`; `size` считывается у фактического файла, а URL вычисляются. Сервер автоматически создаёт директории. Изначально файл метаданных может отсутствовать: он создаётся при первой загрузке. Не редактируйте сохранённые файлы или метаданные во время работы сервера. Метаданные, созданные версиями до перехода на адресацию по пути, несовместимы с новым форматом.
410
+ При хранении на диске бинарный файл находится по пути `<files.directory>/<directory>/<name>`. Файл метаданных содержит только `directory`, `mimeType` и `name`; `size` считывается у фактического файла, а URL вычисляются. Используйте дисковую базу и файловое хранилище только из одного процесса сервера одновременно и не редактируйте сохранённые файлы или метаданные до его остановки. Сервер автоматически создаёт директории, пути внутри `files.directory` не могут содержать символические ссылки, а имена файлов ограничены переносимыми между поддерживаемыми операционными системами значениями. Изначально файл метаданных может отсутствовать: он создаётся при первой загрузке. Метаданные, созданные версиями до перехода на адресацию по пути, несовместимы с новым форматом.
409
411
 
410
412
  Используется бинарное тело запроса, а не `multipart/form-data`, поэтому `XMLHttpRequest.upload.onprogress` может показывать прогресс при непосредственной отправке `File` через `xhr.send(file)`. Максимальный размер по умолчанию равен 100 МиБ и настраивается через `server.maxFileSize`. Отсутствующие или небезопасные заголовки и пути возвращают `400`, превышение лимита — `413`, а отсутствующий, некорректный или не поддерживаемый Fastify `Content-Type` — `400` либо `415` в зависимости от этапа проверки.
411
413
 
@@ -459,7 +461,7 @@ deep-json-server --openapi-only server.config.js
459
461
 
460
462
  Генератор определяет ресурсы и типы полей по всем записям базы. По умолчанию все найденные поля необязательные, а поле `id` верхнего уровня всегда обязательное в схемах ответа и исключается из схем создания и обновления. Остальные обязательные поля перечисляются в `required`. Вложенный обязательный путь делает обязательным само вложенное свойство, но не все его родительские пути; при необходимости родителя нужно перечислить отдельно.
461
463
 
462
- Разные типы значений определяются независимо и объединяются через `oneOf`. Перед генерацией проверяются `$info`, имена ресурсов и схем, а также структура `properties`; пути из `required` и `formats` должны существовать в итоговой схеме.
464
+ Разные непересекающиеся типы значений определяются независимо и объединяются через `oneOf`; сочетание целых и дробных чисел описывается одной схемой `number`. Перед генерацией проверяются `$info`, имена ресурсов и компонентов, поддерживаемые ключи `properties` и типы их значений; пути из `required` и `formats` должны существовать в итоговой схеме. Явные имена компонентов могут содержать латинские буквы, цифры, точки, подчёркивания и дефисы.
463
465
 
464
466
  Используйте `properties`, чтобы описать поля, которые невозможно определить автоматически, особенно у пустого ресурса. Явно заданные свойства объединяются с найденными автоматически:
465
467
 
@@ -493,7 +495,7 @@ deep-json-server --openapi-only server.config.js
493
495
  }
494
496
  ```
495
497
 
496
- В сгенерированном документе описаны CRUD, пагинация, сортировка, глубокие фильтры, `_embed`, а также прямые и обратные связи в ответах, определённые по полям `...Id` и `...Ids`. При наличии `--files` в него также добавляются маршруты бинарной загрузки, получения и удаления файлов. Числовой ID базы описывается как `integer | string`, поскольку последующий `POST` создаст строковый ID в том же ресурсе. Файл можно передать, например, в RTK Query OpenAPI Codegen. OpenAPI создаётся только с параметром `--openapi` или `--openapi-only`; обычный запуск сервера файл не перезаписывает.
498
+ В сгенерированном документе описаны CRUD-маршруты, пагинация, сортировка, фильтрация вложенных данных, `_embed`, а также прямые и обратные связи в ответах, определённые по полям `...Id` и `...Ids`. При наличии `--files` в него также добавляются маршруты бинарной загрузки, получения и удаления файлов с поддержкой произвольных MIME-типов. Числовой ID базы описывается как `integer | string`, поскольку последующий `POST` создаст строковый ID в том же ресурсе. До создания документа генератор отклоняет повторяющиеся имена схем и `operationId`, а также некорректные переопределения схем. Файл можно передать, например, в RTK Query OpenAPI Codegen. OpenAPI создаётся только с параметром `--openapi` или `--openapi-only`; обычный запуск сервера файл не перезаписывает.
497
499
 
498
500
  При обычном запуске тела запросов проверяются по тем же автоматически выведенным и настроенным схемам. Для `POST` и `PUT` проверяются обязательные поля из `required`; `PATCH` проверяет только фактически переданные поля. Настройки `formats` и `properties` применяются ко всем трём методам. Не перечисленные дополнительные поля объекта остаются разрешёнными. Некорректные тела возвращают `400`.
499
501
 
@@ -515,6 +517,7 @@ const config = {
515
517
  path: 'mock/openapi-schema.yaml',
516
518
  },
517
519
  server: {
520
+ cors: true,
518
521
  host: '127.0.0.1',
519
522
  logger: false,
520
523
  maxFileSize: 100 * 1024 * 1024,
@@ -563,17 +566,17 @@ console.log(memoryResponse.json());
563
566
  await memoryFastify.close();
564
567
  ```
565
568
 
566
- `createServer()` принимает точно ту же структуру конфига, что и `server.config.js`. Функция загружает и клонирует настроенные источники, а затем возвращает фасад с двумя операциями:
569
+ `createServer()` принимает точно ту же структуру конфига, что и `server.config.js`. Функция загружает и клонирует базу данных, схему и файловое хранилище, а затем возвращает объект с двумя методами:
567
570
 
568
- | Член | Назначение |
569
- | --- | --- | --- |
571
+ | Метод | Назначение |
572
+ | --- | --- |
570
573
  | `server.fastify()` | Лениво создаёт и кэширует настоящий экземпляр Fastify; все нативные методы доступны, а `listen()` без аргументов использует `server.host` и `server.port` |
571
574
  | `server.openapi()` | Возвращает документ OpenAPI и дополнительно записывает его, если настроен `openapi.path` |
572
575
 
573
- При программном использовании файловые маршруты включаются при наличии секции `files`. Сигнатура второго аргумента — `{ files?: boolean }`: передайте `{ files: false }`, чтобы оставить настроенное хранилище выключенным, или `{ files: true }`, чтобы потребовать секцию `files` и включить маршруты. `server.openapi()` использует то же состояние возможности, что и `server.fastify()`.
576
+ При программном использовании файловые маршруты включаются при наличии секции `files`. Сигнатура второго аргумента — `{ files?: boolean }`: передайте `{ files: false }`, чтобы оставить настроенное хранилище выключенным, или `{ files: true }`, чтобы потребовать секцию `files` и включить маршруты. `server.openapi()` использует ту же настройку файловых маршрутов, что и `server.fastify()`.
574
577
 
575
- Вызов `server.fastify().listen()` без аргументов использует `server.host` и `server.port`, а при их отсутствии — `127.0.0.1:4001`. Явные параметры `listen(options)` имеют приоритет. Относительные пути, переданные напрямую в `createServer()`, вычисляются от текущей рабочей директории; пути из `server.config.js` — от директории конфига. Пакет содержит сгенерированные TypeScript-декларации фасада и всех вариантов конфигурации.
578
+ Вызов `server.fastify().listen()` без аргументов использует `server.host` и `server.port`, а при их отсутствии — `127.0.0.1:4001`. Явные параметры `listen(options)` имеют приоритет. Относительные пути, переданные напрямую в `createServer()`, вычисляются от текущей рабочей директории; пути из `server.config.js` — от директории конфига. Пакет содержит сгенерированные TypeScript-декларации возвращаемого объекта и всех вариантов конфигурации.
576
579
 
577
580
  ## Назначение и безопасность
578
581
 
579
- Deep JSON Server предназначен для локальной разработки и автоматических тестов. В нём нет аутентификации и авторизации, CORS разрешён для любого источника, при дисковом хранилище принятые изменения сохраняются в настроенные файлы, а ссылочная целостность не проверяется. Оставляйте адрес loopback по умолчанию, если внешняя среда не предоставляет собственный контроль доступа; не открывайте сервер и файловые маршруты для недоверенной сети.
582
+ Deep JSON Server предназначен для локальной разработки и автоматических тестов. В нём нет аутентификации и авторизации, CORS по умолчанию разрешён для любого источника, при дисковом хранилище принятые изменения сохраняются в настроенные файлы, а ссылочная целостность не проверяется. Установите `server.cors: false`, чтобы отключить встроенные CORS-заголовки и маршруты `OPTIONS`. Оставляйте адрес loopback по умолчанию, если внешняя среда не предоставляет собственный контроль доступа; не открывайте сервер и файловые маршруты для недоверенной сети.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import process from 'node:process';
3
+ import { runCli } from '../src/cli.js';
4
+ try {
5
+ await runCli();
6
+ }
7
+ catch (error) {
8
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
9
+ process.exitCode = 1;
10
+ }
11
+ //# sourceMappingURL=deep-json-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deep-json-server.js","sourceRoot":"","sources":["../../bin/deep-json-server.ts"],"names":[],"mappings":";AAEA,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,IAAI,CAAC;IACH,MAAM,MAAM,EAAE,CAAC;AACjB,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export type { DatabaseConfig, DeepJsonServerConfig, FilesConfig, MemoryFile, OpenapiConfig, ServerConfig } from './src/config.js';
2
+ export type { FileMetadata, FileUpdate } from './src/files/contract.js';
3
+ export type { ServerFacade } from './src/server.js';
4
+ export { createServer } from './src/server.js';
5
+ export type { OpenapiDocument } from './src/types.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createServer } from './src/server.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { createServer } from './server.js';
2
+ export declare function runCli(args?: string[], services?: {
3
+ createServer: typeof createServer;
4
+ }): Promise<void>;
@@ -0,0 +1,80 @@
1
+ import process from 'node:process';
2
+ import { readServerConfig } from './config.js';
3
+ import { DEFAULT_HOST, DEFAULT_PORT } from './constants.js';
4
+ import { createServer } from './server.js';
5
+ const HELP_TEXT = `Deep JSON Server
6
+
7
+ Использование:
8
+ deep-json-server [--files] [--openapi | --openapi-only] <server.config.js>
9
+
10
+ Параметры:
11
+ --files Добавить файловые маршруты в сервер и OpenAPI
12
+ --openapi Сгенерировать OpenAPI и запустить сервер
13
+ --openapi-only Сгенерировать OpenAPI и завершить работу
14
+ --help Показать справку`;
15
+ const parseArguments = (args) => {
16
+ const options = { files: false, openapiMode: 'none' };
17
+ let configPath;
18
+ args.forEach((argument) => {
19
+ if (argument === '--files') {
20
+ options.files = true;
21
+ }
22
+ else if (argument === '--openapi') {
23
+ if (options.openapiMode !== 'none') {
24
+ throw new Error('Параметры --openapi и --openapi-only нельзя использовать одновременно');
25
+ }
26
+ options.openapiMode = 'generate';
27
+ }
28
+ else if (argument === '--openapi-only') {
29
+ if (options.openapiMode !== 'none') {
30
+ throw new Error('Параметры --openapi и --openapi-only нельзя использовать одновременно');
31
+ }
32
+ options.openapiMode = 'only';
33
+ }
34
+ else if (argument.startsWith('-')) {
35
+ throw new Error(`Неизвестный параметр: ${argument}`);
36
+ }
37
+ else if (configPath == null) {
38
+ configPath = argument;
39
+ }
40
+ else {
41
+ throw new Error('Можно указать только один файл конфигурации');
42
+ }
43
+ });
44
+ if (configPath == null) {
45
+ throw new Error('Укажите путь к файлу конфигурации');
46
+ }
47
+ return { configPath, ...options };
48
+ };
49
+ const validateModeConfig = (config, { files, openapiMode }) => {
50
+ if (openapiMode !== 'none' && config.openapi.path == null) {
51
+ throw new Error(`Для --openapi${openapiMode === 'only' ? '-only' : ''} укажите ключ config.openapi.path`);
52
+ }
53
+ if (files && config.files == null) {
54
+ throw new Error('Для --files укажите секцию config.files');
55
+ }
56
+ };
57
+ export async function runCli(args = process.argv.slice(2), services = { createServer }) {
58
+ if (args.includes('--help')) {
59
+ process.stdout.write(`${HELP_TEXT}\n`);
60
+ return;
61
+ }
62
+ const { configPath, files, openapiMode } = parseArguments(args);
63
+ const config = await readServerConfig(configPath);
64
+ const host = config.server.host ?? process.env.HOST ?? DEFAULT_HOST;
65
+ const port = config.server.port ?? Number(process.env.PORT ?? DEFAULT_PORT);
66
+ validateModeConfig(config, { files, openapiMode });
67
+ const runtimeConfig = { ...config, server: { ...config.server, host, port } };
68
+ const serverFacade = await services.createServer(runtimeConfig, { files });
69
+ if (openapiMode !== 'none') {
70
+ await serverFacade.openapi();
71
+ process.stdout.write(`OpenAPI-схема сохранена в ${config.openapi.path}\n`);
72
+ }
73
+ if (openapiMode === 'only') {
74
+ return;
75
+ }
76
+ const fastify = serverFacade.fastify();
77
+ await fastify.listen();
78
+ fastify.log.info({ database: 'path' in config.database ? config.database.path : 'memory' }, 'Deep JSON Server запущен');
79
+ }
80
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import type { FastifyServerOptions } from 'fastify';
2
+ import type { DatabaseData, JsonObject } from './types.js';
3
+ export type DatabaseSchema = JsonObject;
4
+ export type DatabaseConfig = {
5
+ data: DatabaseData;
6
+ path?: never;
7
+ schema?: DatabaseSchema | string;
8
+ } | {
9
+ data?: never;
10
+ path: string;
11
+ schema?: DatabaseSchema | string;
12
+ };
13
+ export interface MemoryFile {
14
+ content: Uint8Array;
15
+ directory?: string;
16
+ mimeType: string;
17
+ name: string;
18
+ }
19
+ export type FilesConfig = {
20
+ data: MemoryFile[];
21
+ directory?: never;
22
+ metadata?: never;
23
+ } | {
24
+ data?: never;
25
+ directory: string;
26
+ metadata: string;
27
+ };
28
+ export interface OpenapiConfig {
29
+ path?: string;
30
+ }
31
+ export interface ServerConfig {
32
+ cors?: boolean;
33
+ host?: string;
34
+ logger?: FastifyServerOptions['logger'];
35
+ maxFileSize?: number;
36
+ maxPageSize?: number;
37
+ port?: number;
38
+ }
39
+ export interface DeepJsonServerConfig {
40
+ database: DatabaseConfig;
41
+ files?: FilesConfig;
42
+ openapi?: OpenapiConfig;
43
+ server?: ServerConfig;
44
+ }
45
+ export interface NormalizedServerConfig {
46
+ database: DatabaseConfig;
47
+ files?: FilesConfig;
48
+ openapi: OpenapiConfig;
49
+ server: ServerConfig;
50
+ }
51
+ /** Validates configuration and resolves relative paths. */
52
+ export declare const normalizeServerConfig: (config: DeepJsonServerConfig, directoryPath?: string) => NormalizedServerConfig;
53
+ /** Loads an ES module config and resolves paths from its directory. */
54
+ export declare function readServerConfig(configPath: string): Promise<NormalizedServerConfig>;