@kollors/deep-json-server 1.0.0-alpha.1 → 1.0.0-alpha.2

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 (63) hide show
  1. package/README.md +83 -49
  2. package/README.ru.md +81 -45
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.js +1 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/src/cli.js +88 -46
  7. package/dist/src/cli.js.map +1 -1
  8. package/dist/src/config.d.ts +2 -0
  9. package/dist/src/config.js +17 -4
  10. package/dist/src/config.js.map +1 -1
  11. package/dist/src/constants.d.ts +1 -0
  12. package/dist/src/constants.js +1 -0
  13. package/dist/src/constants.js.map +1 -1
  14. package/dist/src/engine.d.ts +4 -4
  15. package/dist/src/engine.js +54 -121
  16. package/dist/src/engine.js.map +1 -1
  17. package/dist/src/errors.d.ts +6 -0
  18. package/dist/src/errors.js +9 -0
  19. package/dist/src/errors.js.map +1 -0
  20. package/dist/src/features.d.ts +8 -0
  21. package/dist/src/features.js +18 -0
  22. package/dist/src/features.js.map +1 -0
  23. package/dist/src/files/disk-store.js +4 -4
  24. package/dist/src/files/disk-store.js.map +1 -1
  25. package/dist/src/graphql/preflight.d.ts +3 -0
  26. package/dist/src/graphql/preflight.js +28 -0
  27. package/dist/src/graphql/preflight.js.map +1 -0
  28. package/dist/src/graphql/routes.d.ts +4 -0
  29. package/dist/src/graphql/routes.js +28 -0
  30. package/dist/src/graphql/routes.js.map +1 -0
  31. package/dist/src/graphql.d.ts +1 -1
  32. package/dist/src/graphql.js +32 -28
  33. package/dist/src/graphql.js.map +1 -1
  34. package/dist/src/model.d.ts +1 -3
  35. package/dist/src/model.js +35 -9
  36. package/dist/src/model.js.map +1 -1
  37. package/dist/src/openapi/document.js +25 -22
  38. package/dist/src/openapi/document.js.map +1 -1
  39. package/dist/src/query/contract.d.ts +3 -0
  40. package/dist/src/query/contract.js +19 -0
  41. package/dist/src/query/contract.js.map +1 -0
  42. package/dist/src/query/filter.d.ts +4 -1
  43. package/dist/src/query/filter.js +76 -76
  44. package/dist/src/query/filter.js.map +1 -1
  45. package/dist/src/query/options.d.ts +1 -14
  46. package/dist/src/query/options.js +2 -107
  47. package/dist/src/query/options.js.map +1 -1
  48. package/dist/src/rest/options.d.ts +14 -0
  49. package/dist/src/rest/options.js +108 -0
  50. package/dist/src/rest/options.js.map +1 -0
  51. package/dist/src/rest/projection.d.ts +6 -0
  52. package/dist/src/rest/projection.js +37 -0
  53. package/dist/src/rest/projection.js.map +1 -0
  54. package/dist/src/rest/routes.d.ts +3 -0
  55. package/dist/src/rest/routes.js +48 -0
  56. package/dist/src/rest/routes.js.map +1 -0
  57. package/dist/src/schema.d.ts +17 -0
  58. package/dist/src/schema.js +23 -0
  59. package/dist/src/schema.js.map +1 -0
  60. package/dist/src/server.d.ts +2 -5
  61. package/dist/src/server.js +68 -132
  62. package/dist/src/server.js.map +1 -1
  63. package/package.json +1 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  A JSON-backed mock server with REST, GraphQL, nested queries, binary files and schema exports. Requires Node.js 22 or newer.
6
6
 
7
- **1.0.0-alpha.1 is a breaking prerelease.** The old `$schema`/`$info` model format and `_where`, `_sort`, `_embed`, `_page`, `_perPage` query parameters are no longer supported.
7
+ **1.0.0-alpha.2 is a prerelease.** When upgrading from 0.x, update your model schema and query parameters using the examples below.
8
8
 
9
9
  ## Installation
10
10
 
@@ -12,29 +12,35 @@ A JSON-backed mock server with REST, GraphQL, nested queries, binary files and s
12
12
  npm install @kollors/deep-json-server@alpha
13
13
  ```
14
14
 
15
- The `alpha` npm channel is separate from `latest`. Install an exact version with `@1.0.0-alpha.1`.
15
+ To install a specific version, use `@1.0.0-alpha.2`.
16
16
 
17
17
  ## Quick start
18
18
 
19
+ Create two files in the same directory.
20
+
21
+ `database.json`:
22
+
23
+ ```json
24
+ {
25
+ "users": [
26
+ { "id": "1", "fullName": "Мира Волкова" }
27
+ ]
28
+ }
29
+ ```
30
+
19
31
  `server.config.js`:
20
32
 
21
33
  ```js
22
34
  export default {
23
- database: { path: './database.json', schema: './schema.json' },
24
- graphql: { enabled: true, path: './generated/schema.graphql' },
25
- openapi: { path: './generated/openapi.yaml' },
26
- server: { host: '127.0.0.1', port: 4001, pageSize: 10, maxPageSize: 100 },
35
+ database: { path: './database.json' },
27
36
  };
28
37
  ```
29
38
 
30
39
  ```sh
31
40
  npx deep-json-server server.config.js
32
- npx deep-json-server --openapi-only --graphql-only server.config.js
33
41
  ```
34
42
 
35
- The second command exports both schemas without listening on a port. Starting the server alone does not write schema files.
36
-
37
- Complete catalog examples: [database](examples/database.json), [model schema](examples/schema.json), [configuration](examples/server.config.js).
43
+ The user list is available at `http://127.0.0.1:4001/users`.
38
44
 
39
45
  ## Configuration
40
46
 
@@ -42,6 +48,8 @@ Complete catalog examples: [database](examples/database.json), [model schema](ex
42
48
  |---|---|
43
49
  | `database.path` / `database.data` | Exactly one: JSON file or in-memory collection object |
44
50
  | `database.schema` | Model object or JSON schema-file path; optional for REST |
51
+ | `openapi.enabled` | Enable the specification endpoint; default `false` |
52
+ | `openapi.endpoint` | Specification path; default `/openapi.json` |
45
53
  | `openapi.path` | YAML export destination |
46
54
  | `openapi.info` | Optional `title`, `version`, `description` |
47
55
  | `graphql.enabled` | Enable GraphQL HTTP endpoint; default `false` |
@@ -54,24 +62,34 @@ Complete catalog examples: [database](examples/database.json), [model schema](ex
54
62
  | `files.data` | In-memory binary files |
55
63
  | `files.directory`, `files.metadata` | Disk storage directory and metadata JSON file; both required |
56
64
 
57
- Configuration-file paths resolve relative to that file. Direct `createServer()` paths resolve relative to the working directory. In-memory input is copied.
65
+ Relative paths resolve from the configuration file's directory. When passing a configuration object to `createServer()`, paths resolve from the working directory. The server works with a copy of in-memory input.
58
66
 
59
- CLI flags:
60
-
61
- | Flag | Action |
67
+ | CLI flag | Action |
62
68
  |---|---|
63
- | `--files` | Enable binary-file routes |
64
- | `--graphql` | Enable GraphQL endpoint |
65
- | `--openapi` | Export OpenAPI and start |
66
- | `--openapi-only` | Export OpenAPI without starting |
67
- | `--graphql-schema` | Export GraphQL SDL and start |
68
- | `--graphql-only` | Export GraphQL SDL without starting |
69
- | `--help` | Show usage |
69
+ | `--files` | Enable file routes |
70
+ | `--graphql` | Enable the GraphQL API |
71
+ | `--openapi` | Enable the OpenAPI endpoint |
72
+ | `--host <host>` | Server address |
73
+ | `--port <port>` | Server port |
74
+ | `--help`, `-h` | Show help |
75
+ | `--version`, `-v` | Show package version |
76
+
77
+ Setting priority: CLI → configuration → `HOST`/`PORT` → defaults. File routes are enabled when `files` is configured.
78
+
79
+ To generate schemas, specify the format and configuration file:
70
80
 
71
- Both exporters can be combined. Any `--*-only` flag prevents startup. CLI file routes require `--files`; programmatic use enables them when `files` is configured unless overridden through the second `createServer()` argument.
81
+ ```sh
82
+ npx deep-json-server generate openapi server.config.js
83
+ npx deep-json-server generate graphql server.config.js
84
+ npx deep-json-server generate openapi,graphql server.config.js
85
+ ```
86
+
87
+ The command reads `database.schema` and writes schemas to `openapi.path` and `graphql.path`. Generation requires no database contents or file store.
72
88
 
73
89
  ## Model schema
74
90
 
91
+ Examples: [database](examples/database.json), [model schema](examples/schema.json), [configuration](examples/server.config.js).
92
+
75
93
  ```json
76
94
  {
77
95
  "Country": {
@@ -104,11 +122,11 @@ Both exporters can be combined. Any `--*-only` flag prevents startup. CLI file r
104
122
  | OpenAPI 3.0.3 export | Available | Error when requested |
105
123
  | GraphQL SDL / API | Available | Error when requested |
106
124
 
107
- Explicit schemas are strict: undeclared fields and collections are rejected, except storage keys inferred from relations. Existing data is validated on startup. Schemas can be exported for empty collections or an empty database object. REST without a schema retains the standard generated `id` behavior.
125
+ Explicit schemas are strict: undeclared fields and collections are rejected, except storage keys inferred from relations. Existing data is validated on startup. Generation uses the model definitions. Schemaless REST generates an `id` and preserves arbitrary JSON fields. Filters and individual field selections use identifier-style names; other fields are returned through `scope=*`.
108
126
 
109
127
  ### Fields
110
128
 
111
- Types: `string`, `number`, `boolean`, `object`, or a model name. Append `[]` for an array. No `integer`, `relation`, `items`, or multidimensional type strings. Nested fields use full dotted paths, for example `actors.fullName`. Objects inside arrays may contain their own arrays.
129
+ The `type` property accepts `string`, `number`, `boolean`, `object`, or a model name. Append `[]` for an array: `string[]`, `object[]`, `Genre[]`. Use dotted paths for nested fields, such as `actors.fullName`.
112
130
 
113
131
  | Properties | Meaning |
114
132
  |---|---|
@@ -125,11 +143,11 @@ Types: `string`, `number`, `boolean`, `object`, or a model name. Append `[]` for
125
143
  | `minimum`, `maximum` | Inclusive numeric bounds |
126
144
  | `source`, `target`, `onDelete` | Relation metadata |
127
145
 
128
- String/numeric constraints on `string[]`/`number[]` apply to every element. `required`/`nullable` apply to the entire array, and `default`/`example` contain a complete array. Array elements are non-null. There are no item-count or uniqueness constraints. A required ordinary array may be empty.
146
+ String and numeric constraints on `string[]`/`number[]` apply to every element. `required` and `nullable` apply to the entire array; `default` and `example` contain a complete array. Elements must match the array's type and be non-null. `required` requires the field to be present; an ordinary array may still be empty.
129
147
 
130
- Primary keys can be named `username`, `code`, etc.; exactly one root string/number primary key is required. Without `generated`, the client supplies it during creation. Generated fields are root fields, absent from create/replace/update input; they cannot have `default`. Replace preserves generated and read-only root values.
148
+ Each model requires exactly one primary key of type `string` or `number`, declared at the top level. The name is arbitrary: `id`, `username`, `code`. If `generated` is omitted, the client supplies the value on creation. Generated fields must be declared at the top level, are excluded from input types and cannot have `default`. Replacing a record preserves generated values and read-only fields, including nested objects. To protect fields inside an array, mark the entire array or its containing object as `readOnly`. Objects containing only server-managed fields are output-only.
131
149
 
132
- For example, a `LocalUser` with primary `username` and `password: {"type":"string","required":true,"writeOnly":true}` has `localUser(username: ...)` and `/localUsers/{username}`. `writeOnly` excludes passwords from responses, scope, filters and ordering. It does not implement hashing or authentication.
150
+ For example, a `LocalUser` with primary key `username` and `password: {"type":"string","required":true,"writeOnly":true}` has `localUser(username: ...)` and `/localUsers/{username}`. A `writeOnly` field accepts input and is excluded from responses, `scope`, filters and ordering.
133
151
 
134
152
  ### Relations
135
153
 
@@ -141,20 +159,20 @@ For example, a `LocalUser` with primary `username` and `password: {"type":"strin
141
159
  }
142
160
  ```
143
161
 
144
- `Genre` produces an object; `Genre[]` produces a list. `source` defaults to the current model's primary key, `target` to the target model's primary key. Both paths are rooted at their respective records. Within `actors`, `actors.genreIds` reads the current actor's IDs. An omitted `source` still means the root model key, not `actors.id`.
162
+ `Genre` returns an object; `Genre[]` returns a list. `source` defaults to the current model's primary key, `target` to the target model's primary key. These defaults also apply to nested relations. Paths start at the root of their respective records: in this example, `actors.genreIds` contains the current actor's genre keys.
145
163
 
146
- Storage keys remain in the database and are included among own fields. Their types can be inferred from the target key. For an undeclared source pointing to a target primary key, a list relation implies an array of keys; a single relation implies a scalar key. Declare storage fields explicitly when the mapping is ambiguous. Generation never guesses from the first database record.
164
+ Relation keys are stored in the database and included among the record's own fields. Their types are inferred from the matched keys. A `source` field pointing to a target primary key can be omitted from the field declarations: the schema infers an array of keys for a list relation or a scalar key for a single relation. Declare the storage field explicitly when the mapping is ambiguous.
147
165
 
148
166
  Reverse example: `User.movies = {"type":"Movie[]","target":"actors.userId"}`. A movie is returned once even if several actors match. A single relation resolving to multiple targets is invalid.
149
167
 
150
- Every supplied direct reference must resolve. `required: true` on a relation requires at least one target before response filtering/pagination. Reverse views with a primary source may be empty unless required. Missing single relations return `null`.
168
+ Every supplied direct relation key must point to an existing record. `required: true` on a relation requires at least one target before response filtering/pagination. Reverse relations using the primary key as `source` may be empty unless required. Missing single relations return `null`.
151
169
 
152
170
  `onDelete` describes what happens **when a target record is deleted**:
153
171
 
154
172
  - `restrict` (default): refuse deletion while a surviving owner refers to the target.
155
173
  - `cascade`: delete the referring owner. For `User.country`, deleting the country deletes its users. For `Movie.actors.user`, deleting the user removes matching actor elements and retains the movie.
156
174
 
157
- Deletion computes the cascade closure, handles cycles, checks restrictions and validates remaining data before committing. A failure cancels the complete operation. Policies also apply to explicitly declared reverse relation views; configure both directions deliberately when both are present.
175
+ Cascading deletion runs as one operation, including cyclic relations. A validation failure cancels the entire operation. `onDelete` rules also apply to explicitly declared reverse relations; account for both rules when defining both directions.
158
176
 
159
177
  ## Queries and responses
160
178
 
@@ -164,9 +182,11 @@ Collections and lists of objects, including embedded `object[]` fields, return:
164
182
  { "data": [], "total": 0 }
165
183
  ```
166
184
 
167
- Primitive arrays remain plain arrays. Every object list accepts optional `where`, `order`, `pager`. Processing order is filter → sort → pagination. `total` is the filtered count before pagination. Page numbers start at 1; default pagination applies even when omitted. Exceeding `maxPageSize`, fractional values and nonpositive values are errors. Out-of-range pages return empty `data` with the correct `total`.
185
+ Primitive arrays are returned as plain arrays. Every object list accepts optional `where`, `order` and `pager`. Processing order is filter → sort → pagination. `total` is the filtered record count before pagination.
168
186
 
169
- `where` uses field operators `eq`, `ne`, `in`, string `contains`/`startsWith`/`endsWith`, and comparisons `gt`, `gte`, `lt`, `lte`. Logical composition uses `and`, `or`, `not`. Arrays support `some`, `every`, `none`; primitive arrays also support `contains`, `in`. String matching is case-insensitive. Field filters use operator objects, not shorthand scalar values.
187
+ Use `page` and `pageSize` in `pager`. The default is the first page with the size from `server.pageSize`. Both values must be positive integers; `pageSize` is limited by `server.maxPageSize`. Out-of-range pages return empty `data` with the total matching record count in `total`.
188
+
189
+ `where` uses field operators `eq`, `ne`, `in`, string `contains`/`startsWith`/`endsWith`, and comparisons `gt`, `gte`, `lt`, `lte`. Combine conditions with `and`, `or`, `not`. Arrays support `some`, `every`, `none`; primitive arrays also support `contains`, `in`. String matching is case-insensitive. A field condition is an object containing an operator, such as `{ "id": { "eq": "1" } }`.
170
190
 
171
191
  ```json
172
192
  {
@@ -182,9 +202,9 @@ Primitive arrays remain plain arrays. Every object list accepts optional `where`
182
202
  }
183
203
  ```
184
204
 
185
- Root filters choose parents. Filters inside a selected relation only trim that relation; they do not remove the parent. Each parent's child list is processed independently. Filtering does not require a relation to be included in the response.
205
+ Root `where` selects records from the main collection. `where` inside a relation filters its elements while retaining the parent record. Each nested list is processed independently. Filtering by a relation works independently of its inclusion in the response.
186
206
 
187
- `order` is an array of `{ "field": "fullName", "direction": "ASC" }` rules. Earlier rules have priority; complete ties retain storage order. Null and missing values compare equally. REST uses dotted field paths; GraphQL uses generated enums (`profile_name` for `profile.name`). Ambiguous enum names cause a generation error. Sorting parents by a relation or an array is unsupported; sorting inside the relation is supported.
207
+ `order` is an array of `{ "field": "fullName", "direction": "ASC" }` rules. Earlier rules have priority; equal values retain storage order. Null and missing values compare equally. REST uses dotted field paths; GraphQL uses generated enums (`profile_name` for `profile.name`). Ambiguous enum names cause a generation error. Sorting supports scalar fields of the current object, including nested fields. Related lists accept their own `order`.
188
208
 
189
209
  ### REST
190
210
 
@@ -197,7 +217,7 @@ Root filters choose parents. Filters inside a selected relation only trim that r
197
217
  | PATCH | `/users/{id}` | `userUpdate` |
198
218
  | DELETE | `/users/{id}` | `userDelete` |
199
219
 
200
- The path key name follows the primary key. POST/PUT/PATCH receive raw record objects. PUT replaces the record while retaining its key and server-owned root values. PATCH shallowly merges supplied fields; supplied nested objects are full replacements. Create/replace enforce required fields. Update validates supplied values and the final record. Missing targets return 404; conflicts return 409. DELETE returns the deleted record.
220
+ The path parameter name follows the primary key. POST, PUT and PATCH accept a JSON record object. PUT replaces the record while retaining its key and server-managed fields. PATCH merges fields at the top level; supplied nested objects are replaced while preserving their read-only fields. Creation and replacement require all mandatory fields. Updates validate supplied values and the final record. Missing records return `404`; conflicts return `409`. DELETE returns the deleted record.
201
221
 
202
222
  Query parameters `where`, `order`, `pager`, `nested` contain JSON. `scope` is a selection string. Example shown before URL encoding:
203
223
 
@@ -215,7 +235,7 @@ const params = new URLSearchParams({
215
235
  const response = await fetch(`/users?${params}`);
216
236
  ```
217
237
 
218
- `scope=*,actors(user(id,fullName),genres(*))` selects own fields and explicit relations. `*` selects only own fields of the current object, including inferred storage keys and excluding `writeOnly`. It never recursively expands relations. Without scope, own fields are selected. Wrappers `data`/`total` remain present.
238
+ `scope=*,actors(user(id,fullName),genres(*))` selects own fields and the specified relations. `*` includes the current object's own fields and stored keys, except `writeOnly` fields. Relations are listed explicitly. The default selection is own fields. Lists retain the `{ data, total }` response structure.
219
239
 
220
240
  `nested` maps full response paths to list options:
221
241
 
@@ -229,7 +249,7 @@ const response = await fetch(`/users?${params}`);
229
249
  }
230
250
  ```
231
251
 
232
- A nested path must be selected by scope and must address an object list. Single-record routes and mutations accept `scope` and `nested`; root list parameters only apply to collection GET. Invalid names and unsafe paths return 400.
252
+ A path in `nested` must be selected by `scope` and point to an object list. Single-record routes and mutations accept `scope` and `nested`; root `where`, `order` and `pager` apply to collection GET. Invalid names and unsafe paths return `400`.
233
253
 
234
254
  ### GraphQL
235
255
 
@@ -252,10 +272,9 @@ query {
252
272
  }
253
273
  ```
254
274
 
255
- Single queries are `user(id: ...)`, with no `ById`; missing records yield null. Mutation names are `userCreate(data: ...)`, `userReplace(id: ..., data: ...)`, `userUpdate(id: ..., data: ...)`, `userDelete(id: ...)`. A generated-only model creates records without a `data` argument. Mutations use the same validation and storage operations as REST. Selecting relations in mutation results shapes the response only.
256
-
257
- String primary keys use GraphQL `ID`; ordinary strings use `String`, numbers use `Float`, pagination uses `Int`. Schema enums preserve valid string labels; other values receive `VALUE_0`, `VALUE_1`, etc. Schema constraints such as string length and formats are enforced by the shared runtime validator; SDL alone cannot express all constraints. Introspection and ordinary query/mutation execution are supported; this release does not add subscriptions or bulk mutations.
275
+ The query `user(id: ...)` returns one record or `null` if it is missing. Mutations are `userCreate(data: ...)`, `userReplace(id: ..., data: ...)`, `userUpdate(id: ..., data: ...)`, `userDelete(id: ...)`. For models containing only generated fields, the create mutation takes no `data` argument. Writes and validation follow the same rules as REST. Relations selected in a mutation result determine the response contents.
258
276
 
277
+ String primary keys use GraphQL `ID`; ordinary strings use `String`, numbers use `Float`, and pagination parameters use `Int`. Schema enums preserve valid string labels; other values receive `VALUE_0`, `VALUE_1`, etc. String lengths, formats and other model constraints are validated by the server during request execution. Introspection is available for exploring the schema. Selected list arguments are validated before executing mutations. Errors include `extensions.code`: `INVALID_INPUT`, `INVALID_QUERY`, `NOT_FOUND`, `CONFLICT` or `INTERNAL_ERROR`.
259
278
 
260
279
  ## Example database
261
280
 
@@ -363,10 +382,10 @@ String primary keys use GraphQL `ID`; ordinary strings use `String`, numbers use
363
382
 
364
383
  ## Files
365
384
 
366
- Add `files.directory` and `files.metadata` to the server config, then pass `--files` to enable raw binary uploads:
385
+ Add `files.directory` and `files.metadata` to the configuration and start the server:
367
386
 
368
387
  ```bash
369
- deep-json-server --files server.config.js
388
+ deep-json-server server.config.js
370
389
  ```
371
390
 
372
391
  For temporary tests, use `files.data` instead. Each initial record contains `name`, `mimeType`, binary `content` as a `Uint8Array`, and an optional `directory`. Uploaded files then remain in memory until the process exits.
@@ -422,9 +441,9 @@ Content-Type: application/json
422
441
 
423
442
  `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.
424
443
 
425
- 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.
444
+ 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.
426
445
 
427
- 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.
446
+ Send the file as a binary request body. In a browser, use `xhr.send(file)` and track progress through `XMLHttpRequest.upload.onprogress`. 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.
428
447
 
429
448
  ## Programmatic API
430
449
 
@@ -440,19 +459,34 @@ await server.listen();
440
459
  // await server.close();
441
460
  ```
442
461
 
443
- Accessors are lazy; exporting schemas does not listen or initialize disk file storage. Override runtime features with `createServer(config, { files: false, graphql: true })`.
462
+ The `openapi()` and `graphql()` methods return schemas. `fastify()` returns the server instance for configuration and startup. The database and enabled services initialize on `ready()`, `listen()` or the first `inject()`; initialization errors stop startup. Override server features with `createServer(config, { files: false, graphql: true, openapi: true })`.
463
+
464
+ Generators can also be used independently:
465
+
466
+ ```js
467
+ import { generateOpenapi, generateGraphql, writeOpenapi, writeGraphql } from '@kollors/deep-json-server';
468
+
469
+ const document = await generateOpenapi('./schema.json', { files: true });
470
+ const sdl = await generateGraphql('./schema.json');
471
+ await writeOpenapi(document, './generated/openapi.yaml');
472
+ await writeGraphql(sdl, './generated/schema.graphql');
473
+ ```
474
+
475
+ `generateOpenapi()` also accepts `host`, `port`, `pageSize`, `maxPageSize` and `info`. Pass a schema object instead of a path if preferred. Servers and generators use their own copy of the model.
444
476
 
445
477
  ## Storage and development
446
478
 
447
- Updates are serialized within one server instance and validated on a draft before persistence. Use one writer per database file. Increment counters are stored next to the database in `<database path>.counters.json`; keep that file with the database. Numbers are reserved before the data write, so a failed write can leave gaps but cannot reuse a reserved number. UUID generation uses Node's built-in crypto API.
479
+ Updates run sequentially within one server instance and are validated on a copy of the data before saving. Use one server process per database file. `increment` counters are stored next to the database in `<database path>.counters.json`; keep that file with the database. Numbers are reserved before the data write, so a failed write can leave gaps but cannot reuse a reserved number.
448
480
 
449
- This is a mock server; there is no authentication or password hashing. File routes keep their independent storage and validation.
481
+ The server is intended for mocking APIs. Implement authentication and password hashing in your application if needed.
450
482
 
451
483
  ```sh
452
484
  npm ci
453
485
  npm run verify
454
486
  ```
455
487
 
456
- Verification runs type checking, lint, coverage gates and installation checks against the packed package. A new alpha version in `package.json` pushed to `main` creates its version tag and publishes through GitHub Actions trusted publishing to npm `alpha`. An existing tag skips automatic publication. Explicit version-tag pushes also publish; stable versions use `latest`. The publishing script rejects mismatched Git tags.
488
+ The command checks types, code style, test coverage and installation from the package archive.
489
+
490
+ To publish a new alpha, update the version in `package.json` and push to `main`. GitHub Actions creates the version tag and publishes to npm `alpha` through trusted publishing. Already published versions are skipped. If the tag exists but publication failed, a retry uses that tag and verifies that the package files match it. Pushing a version tag also triggers publication; stable versions publish to `latest`.
457
491
 
458
492
  License: MIT.