@kollors/deep-json-server 1.0.0-rc.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0
4
+
5
+ First stable release of the 1.0 API, based on RC9. There are no API or stored-data format changes from RC9.
6
+
7
+ - Define models, validation and paired relations in one schema for REST, GraphQL and OpenAPI.
8
+ - Select fields, filter, sort and paginate root and nested lists with REST `scope` or GraphQL.
9
+ - Enable authentication, owner and administrator permissions, persistent file-backed sessions, timestamps and soft deletion through configuration.
10
+ - Upload, download, rename and delete files using disk or memory storage. Native OS locks prevent competing servers from writing to shared disk stores and recover after crashes.
11
+ - Verify types, lint, test coverage, dependency security and installed-package behavior on Linux, Windows and macOS with Node.js 22, 24 and 26.
12
+
13
+ The configuration, CLI and REST query format differ from 0.9.0. Follow the [migration guide](MIGRATION.md) or [Russian guide](MIGRATION.ru.md), and stop old server processes before upgrading.
14
+
15
+ ## 1.0.0-rc.9
16
+
17
+ - Update vulnerable Fastify, fast-uri and brace-expansion versions within compatible ranges.
18
+ - Check runtime dependencies with `npm audit` before the full verification and publication pipeline. RC9 includes all RC8 storage, auth, query and cross-platform fixes.
19
+
3
20
  ## 1.0.0-rc.8
4
21
 
5
22
  - Persist hashed auth sessions with users in file storage so valid tokens survive restarts, with atomic logout and password-change revocation.
@@ -41,13 +58,13 @@ When upgrading from an earlier RC, stop all old server processes before starting
41
58
  ## 1.0.0-rc.3
42
59
 
43
60
  - Keep relation keys inferred from `source` in stored records while hiding them from REST, GraphQL, and OpenAPI unless declared explicitly in model `fields`.
44
- - Clarified relation key selection with and without a model schema in both READMEs.
61
+ - Clarify relation key selection with and without a model schema in both READMEs.
45
62
 
46
63
  ## 1.0.0-rc.2
47
64
 
48
- - Added root schema `api` to select REST, GraphQL, or both for database routes, and per-model `api` selection with matching formats. Authentication and file routes remain REST endpoints; OpenAPI includes only enabled REST routes.
49
- - GraphQL-only server configurations no longer require package metadata. OpenAPI keeps authentication and file routes when no database model enables REST.
50
- - Added installed-package coverage for per-model API selection and checks for code examples in both READMEs.
65
+ - Add root schema `api` to select REST, GraphQL, or both for database routes, and per-model `api` selection with matching formats. Authentication and file routes remain REST endpoints; OpenAPI includes only enabled REST routes.
66
+ - Remove the package metadata requirement for GraphQL-only server configurations. Keep authentication and file routes in OpenAPI when no database model enables REST.
67
+ - Add installed-package coverage for per-model API selection and checks for code examples in both READMEs.
51
68
 
52
69
  ## 1.0.0-rc.1
53
70
 
package/MIGRATION.md CHANGED
@@ -1,8 +1,8 @@
1
- # Migrating from 0.9.0 to 1.0.0-rc.8
1
+ # Migrating from 0.9.0 to 1.0.0
2
2
 
3
3
  [Русский](MIGRATION.ru.md) · [Current README](README.md)
4
4
 
5
- This guide compares `v0.9.0` with `1.0.0-rc.8` and also covers the relation changes since RC6. Update configuration and clients together. Node.js 22 or newer is still required. Back up the database, config, schema, uploaded files and metadata before migrating.
5
+ This guide covers the move from `0.9.0` to `1.0.0`. Update configuration and clients together. Node.js 22 or newer is required. Back up the database, configuration, schema, uploaded files and metadata before migrating. If you already use a 1.0 release candidate, see [upgrading from a release candidate](#upgrading-from-a-release-candidate).
6
6
 
7
7
  ## 1. Choose whether you need a schema
8
8
 
@@ -14,7 +14,7 @@ export default { database: { path: './database.json' } };
14
14
  ```
15
15
 
16
16
  ```js
17
- // 1.0.0-rc.8
17
+ // 1.0.0
18
18
  export default { storage: 'file', database: { source: './database.json' } };
19
19
  ```
20
20
 
@@ -22,7 +22,7 @@ This keeps schemaless REST and relations inferred from stored `...Id` / `...Ids`
22
22
 
23
23
  ## 2. Convert configuration
24
24
 
25
- | 0.9.0 | 1.0.0-rc.8 |
25
+ | 0.9.0 | 1.0.0 |
26
26
  |---|---|
27
27
  | `database.path` | `storage: 'file'` and `database.source` |
28
28
  | `database.data` | `storage: 'memory'` and `database.source` |
@@ -56,7 +56,7 @@ Relative paths still resolve from the config directory in the CLI, and from the
56
56
 
57
57
  ## 3. Replace schema overrides with complete models
58
58
 
59
- In 0.9.0, `$schema` supplemented field types inferred from data. It was not the current model/relation format. Each collection now needs a model with `collection`, `fields`, and exactly one top-level primary key of type `string` or `number`.
59
+ In 0.9.0, `$schema` supplemented field types inferred from data. In 1.0.0, each collection needs a model with `collection`, `fields`, and exactly one top-level primary key of type `string` or `number`.
60
60
 
61
61
  | Old override | New definition |
62
62
  |---|---|
@@ -106,7 +106,7 @@ Every explicit relation needs both declarations and a `keyOn` value. `current` s
106
106
  - Self-relations pair `Genre.parents: { type: 'Genre[]', keyOn: 'current' }` with `Genre.children: { type: 'Genre[]', keyOn: 'related' }`; both use stored `Genre.parentIds`.
107
107
  - Multiple relations between the same models require disambiguation: inverse `Country.birthUsers` uses `target: 'birthCountryId'`, and `Country.residenceUsers` uses `target: 'residenceCountryId'`. Ambiguous or missing pairs fail schema loading.
108
108
  - Declare existing keys explicitly, as above, if clients read, filter, sort or write them. Inferred keys remain in storage but are hidden from both APIs and OpenAPI, even when selected by name.
109
- - RC7 also infers source arrays for lists targeting **non-primary** fields. For example, `publishers` with `keyOn: 'current'` and `target: 'code'` infers `publisherCodes: { type: 'string[]' }` when `Publisher.code` is a string. Explicitly declare the key if it must be exposed in the API. If RC6 stored this automatically inferred key as a scalar, convert existing values to arrays, for example `"A"` to `["A"]`, before upgrading. An explicitly declared scalar key retains its type.
109
+ - List relations targeting **non-primary** fields also infer arrays of storage keys. For example, `publishers` with `keyOn: 'current'` and `target: 'code'` infers `publisherCodes: { type: 'string[]' }` when `Publisher.code` is a string. Declare the key in `fields` if clients need direct access. Explicitly declared scalar keys retain their type.
110
110
 
111
111
  Repair dangling direct references before startup. Writes now validate references; deleting a referenced target is restricted by default. Choose `onDelete: 'cascade'` deliberately where deleting a target should delete referring records. An inverse with no explicit `onDelete` does not add its own restriction.
112
112
 
@@ -116,13 +116,13 @@ Do not regenerate IDs while converting the schema. Existing string IDs remain va
116
116
 
117
117
  `generated` is not implied by `primary`. Without it, clients must supply the primary key on create. With it, clients must omit generated fields; sending them is rejected instead of ignored as in 0.9.0.
118
118
 
119
- Old application fields must be declared or deliberately removed from copied data. A custom archive flag does not automatically become soft deletion. Enabling lifecycle or auth features reserves their system field names, so rename conflicting application fields first.
119
+ Declare existing application fields in the new schema, or remove them from the copy you are migrating. A custom archive flag does not automatically become soft deletion. Auth, timestamps and soft deletion reserve their system field names; rename conflicting application fields before enabling them.
120
120
 
121
121
  ## 4. Select APIs and update startup commands
122
122
 
123
123
  If root `api` is omitted, the server uses `['rest']`, or `['rest', 'graphql']` when `graphql` is configured. To expose the database only through GraphQL, set `api: ['graphql']` and add `graphql: {}`. A model inherits root `api` or narrows it; model `api: []` hides that model. Root `api: []` is invalid. Related models must enable the same APIs.
124
124
 
125
- The `graphql` section and root GraphQL flag must be enabled together. Auth and files remain REST services and appear only in OpenAPI. OpenAPI can describe these services on a GraphQL-only database server.
125
+ If you set root `api` explicitly, include `graphql` when the configuration has a `graphql` section and omit it otherwise. Auth and files remain REST services and appear only in OpenAPI. OpenAPI can describe these services even when the database is available only through GraphQL.
126
126
 
127
127
  | Old CLI | New CLI |
128
128
  |---|---|
@@ -170,15 +170,13 @@ Update response readers as well:
170
170
 
171
171
  `PATCH` merges top-level fields; a supplied nested plain object replaces that object while preserving protected fields. `PUT` replaces writable fields, preserving primary, generated, read-only and system values. Required fields must be supplied for creation/replacement unless they have defaults.
172
172
 
173
- Relation values are objects, for example `country: { id: '1' }`, not scalar IDs. **Starting with RC7, an object containing only the primary key is a reference in POST, PUT and PATCH.** It preserves the target's fields, dates and author values. Adding other fields invokes an update: PUT replaces the target and requires its mandatory fields; POST/PATCH update supplied fields. Changing an inverse relation still requires permission for every record whose stored key changes. Declared storage keys, such as `countryId`, can also change links directly. Do not send the relation and its storage key in the same object.
174
-
175
- **When upgrading from RC6:** key-only objects no longer update the target, change its audit values or require ownership of an unchanged target. If an existing client intends to update or replace the target, include the fields to change. Declared storage keys remain available for link-only writes.
173
+ Relation values are objects, for example `country: { id: '1' }`, not scalar IDs. An object containing only the primary key is a reference in POST, PUT and PATCH. It preserves the target's fields, timestamps and ownership. Adding other fields updates the target: PUT replaces it and requires its mandatory fields without defaults; POST/PATCH update supplied fields. Changing an inverse relation requires permission for every record whose stored key changes. Declared storage keys, such as `countryId`, can also change links directly. Do not send the relation and its storage key in the same object.
176
174
 
177
175
  Omitted inverse (`keyOn: 'related'`) relations survive PUT. Explicit `users: []` changes users' foreign keys to detach them; it can fail if those keys are required or the caller lacks permission. Nested changes are atomic, and modifying a shared target affects every record referring to it.
178
176
 
179
- Auth, timestamps and soft deletion are new, optional features. To first preserve unauthenticated writes and physical deletion, omit `auth` and leave `timestamps` / `softDelete` disabled (their defaults).
177
+ Auth, timestamps and soft deletion are optional. To keep writes available without login and retain physical deletion during the first migration, omit `auth` and leave `timestamps` and `softDelete` disabled, as they are by default.
180
178
 
181
- If enabling auth, create a separate auth user store and migrate ownership deliberately. Old records without `createdById` can be changed only by administrators. The ownership field is server-managed through the API; prepare ownership in the copied database before startup if ordinary users must edit existing records. Auth does not close read endpoints or file operations. In RC8, file storage persists hashed sessions in the auth user file and restores unexpired sessions after a restart. Existing users without `sessions` remain valid. When manually changing a password hash, also clear that user's `sessions`.
179
+ If enabling auth, create a separate user store and set ownership for existing records. Only administrators can change records without `createdById`. The server manages ownership through the API, so set it in the copied database before startup if ordinary users must edit those records. Reads and file operations remain public. File storage saves hashed sessions in the user file and restores unexpired sessions after a restart. Existing users without `sessions` remain valid. Stop the server before manually changing a password hash, and clear that user's `sessions` at the same time.
182
180
 
183
181
  With `softDelete`, DELETE retains records, lists hide deleted records by default, direct primary-key reads can retrieve them, and successful PUT/PATCH restores them. There is no automatic conversion of an old application archive flag. See the [lifecycle rules](README.md#record-dates-deletion-and-ownership) before enabling this feature.
184
182
 
@@ -186,12 +184,12 @@ With `softDelete`, DELETE retains records, lists hide deleted records by default
186
184
 
187
185
  `createServer(config)` now takes exactly one argument. Remove the old second `{ files: false }` argument and omit `files` from the config to disable the feature.
188
186
 
189
- `facade.openapi()` and `facade.graphql()` return documents without writing `target`. They require their respective config sections; OpenAPI also needs package metadata and a schema. Use CLI generation flags or explicit writers:
187
+ `facade.openapi()` and `facade.graphql()` return documents without writing `target`. Both methods require `database.schema` and their respective configuration sections. OpenAPI also requires `package.source`. Use CLI generation flags or explicit writers:
190
188
 
191
189
  ```js
192
190
  import { createServer } from '@kollors/deep-json-server';
193
191
  import { writeOpenapi } from '@kollors/deep-json-server/openapi';
194
- import config from './server.config.js';
192
+ import config from './server.config.mjs';
195
193
 
196
194
  const facade = await createServer(config);
197
195
  await writeOpenapi(await facade.openapi(), './generated/openapi.yaml');
@@ -202,10 +200,20 @@ await app.close();
202
200
 
203
201
  Schema generation alone does not validate existing database records. Call `ready()`, `inject()` or `listen()` to initialize HTTP and check data. Regenerate clients from the new OpenAPI/GraphQL documents after changing models and queries.
204
202
 
205
- ## 8. Validate the cutover and keep rollback data
203
+ ## 8. Check the migration and prepare rollback
206
204
 
207
- 1. Pin the chosen RC version and migrate a **copy** of the 0.9.0 files. Run generation, then initialize the HTTP server to validate data too.
205
+ 1. Pin `@kollors/deep-json-server@1.0.0` and migrate a **copy** of the 0.9.0 files. Generate the schemas, then initialize the HTTP server to validate the stored data.
208
206
  2. Check representative reads: defaults, explicit foreign keys, nested lists, filters and pagination. Check create, PATCH, PUT, link changes and deletion using disposable copied records.
209
207
  3. If enabling auth, test an owner, an administrator and an ordinary non-owner against old records. Confirm uploaded files remain accessible through the retained metadata path.
210
- 4. Stop every old server process before switching: RC8 replaces the old lock directory with native OS locks and readable `<source-name>-lock.json` files for the database, auth, upload root and file metadata. Old and new lock protocols do not coordinate. Run one process per writable store and stop it before manual edits.
208
+ 4. Stop all old server processes before switching. Version 1.0.0 locks the database, auth users, upload root and file metadata with native OS locks and readable `<source-name>-lock.json` files. Use one process per writable store and stop it before manual edits.
211
209
  5. Keep the original package version, config, schema, data and file metadata together for rollback. Restore them together; do not point 0.9.0 at data already modified by the new server. Include new increment counters (`<database>.counters.json`) and soft-delete restoration metadata in subsequent backups when used.
210
+
211
+ ## Upgrading from a release candidate
212
+
213
+ RC9 and 1.0.0 use the same API and stored-data formats. If you use an earlier RC, check the following changes and compare your configuration and schema with the current README:
214
+
215
+ - **Before RC6:** explicit relations now require declarations on both models, with `keyOn: 'current'` on the side that stores the key and `keyOn: 'related'` on its inverse. Follow the paired-model example in section 3.
216
+ - **Before RC7:** nested objects containing only a primary key now link an existing record without updating its fields, dates or owner. Include the fields to change when you intend to update the target. Automatically inferred list keys targeting non-primary fields are now arrays; convert scalar values from earlier versions to arrays, for example `"A"` to `["A"]`. Explicitly declared key types are unchanged.
217
+ - **Before RC8:** stop all old processes before upgrading. Native locks and `<source-name>-lock.json` files replace the earlier lock protocol; old and new versions cannot coordinate their locks. File-backed auth sessions are now saved with users, and users without `sessions` remain valid. For a primary key named `data`, GraphQL update/replace mutations use `key` for its value and `data` for the input object; regenerate affected clients.
218
+
219
+ See [the changelog](CHANGELOG.md) for the full RC history.
package/MIGRATION.ru.md CHANGED
@@ -1,8 +1,8 @@
1
- # Переход с 0.9.0 на 1.0.0-rc.8
1
+ # Переход с 0.9.0 на 1.0.0
2
2
 
3
3
  [English](MIGRATION.md) · [Актуальный README](README.ru.md)
4
4
 
5
- Руководство сравнивает релиз `v0.9.0` с `1.0.0-rc.8` и также описывает изменения связей после RC6. Конфигурацию и клиентов нужно обновлять вместе. Требование Node.js 22 или новее сохраняется. Перед переходом скопируйте базу, конфигурацию, схему, загруженные файлы и их метаданные.
5
+ Руководство описывает переход с `0.9.0` на `1.0.0`. Конфигурацию и клиентов нужно обновлять вместе. Требуется Node.js 22 или новее. Перед переходом сделайте резервную копию базы, конфигурации, схемы, загруженных файлов и метаданных. Если уже используете предварительную версию 1.0, см. [переход с релиз-кандидата](#переход-с-релиз-кандидата).
6
6
 
7
7
  ## 1. Выберите режим со схемой или без неё
8
8
 
@@ -14,7 +14,7 @@ export default { database: { path: './database.json' } };
14
14
  ```
15
15
 
16
16
  ```js
17
- // 1.0.0-rc.8
17
+ // 1.0.0
18
18
  export default { storage: 'file', database: { source: './database.json' } };
19
19
  ```
20
20
 
@@ -22,7 +22,7 @@ REST без схемы и связи по именам хранимых поле
22
22
 
23
23
  ## 2. Обновите конфигурацию
24
24
 
25
- | В 0.9.0 | В 1.0.0-rc.8 |
25
+ | В 0.9.0 | В 1.0.0 |
26
26
  |---|---|
27
27
  | `database.path` | `storage: 'file'` и `database.source` |
28
28
  | `database.data` | `storage: 'memory'` и `database.source` |
@@ -33,7 +33,7 @@ REST без схемы и связи по именам хранимых поле
33
33
  | `$info` в схеме | `package.source`: `name`, `version`, необязательный `description` |
34
34
  | `server.maxPageSize` по умолчанию `1000` | Теперь `100`; для прежнего ограничения задайте `1000` явно |
35
35
 
36
- Обязательный `storage` действует на все источники, включая схему и метаданные пакета. В режиме `file` нужны пути, в `memory` — объекты и массивы. Смешивать дисковые источники и данные в памяти больше нельзя. Пути экспорта `target` остаются путями к файлам в обоих режимах.
36
+ Обязательный `storage` задаёт режим хранения всех источников, включая схему и метаданные пакета. В режиме `file` указывайте пути, в `memory` — объекты и массивы с данными. Смешивать дисковые источники и данные в памяти больше нельзя. Пути экспорта `target` остаются путями к файлам в обоих режимах.
37
37
 
38
38
  Пример конфигурации с дисковой базой и экспортом OpenAPI:
39
39
 
@@ -48,15 +48,15 @@ export default {
48
48
  };
49
49
  ```
50
50
 
51
- Создайте `schema.json` по следующему разделу. При настроенном `openapi` обязателен `package.source`: `name` пакета становится `info.title`, также используются `version` и необязательный `description`. Остальные свойства старого `$info` не имеют аналога в конфигурации. В памяти передавайте `package: { source: { name: 'example-api', version: '1.0.0' } }`.
51
+ Создайте `schema.json` по примеру в следующем разделе. При наличии секции `openapi` обязателен `package.source`: имя пакета становится `info.title`, также используются `version` и необязательный `description`. Для остальных свойств старого `$info` аналога в конфигурации нет. В режиме `memory` передавайте `package: { source: { name: 'example-api', version: '1.0.0' } }`.
52
52
 
53
53
  **Сохраните прежний `files.metadata` явно.** Без него сервер выберет `<files.source>/.files.json` и не импортирует старый `_database.json`. Формат метаданных из 0.9.0 и маршруты `/_files/*` совместимы; заголовки загрузки бинарного тела и ответ `204` на удаление файла сохраняются.
54
54
 
55
55
  Относительные пути по-прежнему отсчитываются от каталога конфигурации в CLI и от рабочего каталога при прямом вызове `createServer(config)`.
56
56
 
57
- ## 3. Замените дополнения к схеме полными моделями
57
+ ## 3. Опишите модели вместо прежних настроек схемы
58
58
 
59
- В 0.9.0 `$schema` дополнял поля, определённые по данным. Это ещё не был нынешний формат моделей и связей. Теперь каждой коллекции нужна модель с `collection`, `fields` и одним первичным ключом верхнего уровня типа `string` или `number`.
59
+ В 0.9.0 `$schema` дополнял типы полей, определённые по данным. В 1.0.0 каждой коллекции нужна модель с `collection`, `fields` и одним первичным ключом верхнего уровня типа `string` или `number`.
60
60
 
61
61
  | Старое описание | Новое описание |
62
62
  |---|---|
@@ -66,9 +66,9 @@ export default {
66
66
  | Вложенные `properties` и `items` | Поля с типами, `[]` для массивов, пути через точку для дочерних полей |
67
67
  | Автоматический строковый `id` | Явный `{ type: 'string', primary: true, generated: 'uuid' }` |
68
68
 
69
- Опишите все хранимые поля, а не только прежние дополнения. Например, `actors` получает тип `object[]`, а `actors.fullName` — `string`. При явной схеме неизвестные поля и коллекции запрещены; исключение — автоматически выведенные ключи связей. Существующие записи проверяются при инициализации HTTP-сервера.
69
+ Опишите все хранимые поля, а не только те, для которых раньше задавали настройки. Например, `actors` получает тип `object[]`, а `actors.fullName` — `string`. При явной схеме неизвестные поля и коллекции запрещены; исключение — автоматически определённые ключи связей. Существующие записи проверяются при инициализации HTTP-сервера.
70
70
 
71
- У полей со смешанными типами `oneOf` и ограничения «только целые числа» через `integer` нет общего прямого аналога. Приведите данные к поддерживаемому типу или оставьте REST без схемы. Имена моделей должны быть идентификаторами: старые имена компонентов с точками и дефисами нужно заменить.
71
+ Для полей с несколькими типами через `oneOf` и ограничения «только целые числа» через `integer` прямого аналога нет. Приведите данные к поддерживаемому типу или оставьте REST без схемы. Имена моделей должны быть допустимыми идентификаторами: старые имена компонентов с точками и дефисами нужно заменить.
72
72
 
73
73
  ### Полный пример с двусторонней связью
74
74
 
@@ -99,14 +99,14 @@ export default {
99
99
  }
100
100
  ```
101
101
 
102
- Каждая явная связь описывается с обеих сторон, везде обязателен `keyOn`. Сторона `current` хранит внешний ключ; `related` использует этот ключ в другой модели. Второй экземпляр ключа или хранимый массив `users` не нужен.
102
+ Объявите каждую связь с обеих сторон и укажите `keyOn` у обоих полей. Сторона `current` хранит внешний ключ; `related` использует этот ключ в другой модели. Второй экземпляр ключа или хранимый массив `users` не нужен.
103
103
 
104
104
  - У прямой связи `target` по умолчанию равен первичному ключу целевой модели. `source` выводится из имени поля связи и целевого ключа: `country` → `countryId`, `publishers` → `publisherIds`. При первичном ключе `ref` получится `publisherRefs`.
105
105
  - У обратной связи пути меняются местами: для `Country.users` это `source: 'id'` и `target: 'countryId'`.
106
106
  - Связь модели с собой: `Genre.parents: { type: 'Genre[]', keyOn: 'current' }` и `Genre.children: { type: 'Genre[]', keyOn: 'related' }` используют один хранимый массив `Genre.parentIds`.
107
107
  - Если между моделями несколько связей, укажите, какие поля образуют пары: у обратной `Country.birthUsers` задайте `target: 'birthCountryId'`, у `Country.residenceUsers` — `target: 'residenceCountryId'`. Неоднозначные и отсутствующие пары отклоняются при загрузке.
108
108
  - Объявляйте существующие ключи в `fields`, как в примере, если клиент их читает, фильтрует, сортирует или записывает. Автоматически выведенные ключи остаются в базе, но скрыты из обоих API и OpenAPI; выбрать их даже по имени нельзя.
109
- - RC7 выводит массив источника и для списка с **непервичным** целевым полем. Например, `publishers` с `keyOn: 'current'` и `target: 'code'` получает `publisherCodes: { type: 'string[]' }`, если `Publisher.code` — строка. Объявите ключ явно, если его нужно открыть в API. Если RC6 сохранил такой автоматически выведенный ключ скаляром, перед обновлением преобразуйте значения в массивы, например `"A"` в `["A"]`. Явно объявленный скалярный ключ сохраняет свой тип.
109
+ - Для списков с **непервичным** целевым полем сервер тоже определяет массив ключей. Например, `publishers` с `keyOn: 'current'` и `target: 'code'` получает `publisherCodes: { type: 'string[]' }`, если `Publisher.code` — строка. Объявите ключ в `fields`, если клиенту нужен прямой доступ к нему. Тип явно объявленного скалярного ключа сохраняется.
110
110
 
111
111
  До запуска исправьте ссылки на отсутствующие записи. Теперь запись проверяет целостность связей, а удаление целевой записи по умолчанию запрещено, пока на неё ссылаются. Задавайте `onDelete: 'cascade'` только там, где удаление цели должно удалять ссылающиеся записи. Обратная связь без явно указанного `onDelete` не добавляет собственного запрета.
112
112
 
@@ -116,13 +116,13 @@ export default {
116
116
 
117
117
  `primary` не включает `generated` автоматически. Без `generated` клиент сам передаёт ключ при создании. С `generated` клиент должен пропускать генерируемые поля: теперь их передача отклоняется, а не игнорируется, как в 0.9.0.
118
118
 
119
- Старые поля приложения нужно объявить или осознанно удалить из копии базы. Пользовательский флаг архивирования не превращается в мягкое удаление автоматически. При включении дат, мягкого удаления или auth соответствующие служебные имена резервируются; переименуйте конфликтующие поля приложения заранее.
119
+ Объявите прежние поля приложения в новой схеме или удалите их из копии, которую переносите. Пользовательский флаг архивирования не превращается в мягкое удаление автоматически. Аутентификация, даты записей и мягкое удаление резервируют свои служебные имена; переименуйте конфликтующие поля до включения этих функций.
120
120
 
121
121
  ## 4. Выберите API и обновите команды запуска
122
122
 
123
- Без корневого `api` сервер использует `['rest']`, а с секцией `graphql` — `['rest', 'graphql']`. Для доступа к базе только через GraphQL задайте `api: ['graphql']` и добавьте `graphql: {}`. Модель наследует корневой выбор или сужает его; `api: []` у модели скрывает её. Корневой `api: []` недопустим. Связанные модели должны разрешать одинаковые API.
123
+ Если корневой `api` не задан, сервер использует `['rest']`, а при наличии секции `graphql` — `['rest', 'graphql']`. Для доступа к базе только через GraphQL задайте `api: ['graphql']` и добавьте `graphql: {}`. Модель наследует значение из корня или сужает список API; `api: []` у модели скрывает её. Корневой `api: []` недопустим. Связанные модели должны разрешать одинаковые API.
124
124
 
125
- Секцию `graphql` и GraphQL в корневом `api` нужно включать вместе. Auth и файлы остаются REST-сервисами и описываются только в OpenAPI. OpenAPI может описывать их и при базе, доступной только через GraphQL.
125
+ Если задаёте корневой `api` явно, включите в него `graphql` при наличии секции `graphql` в конфигурации и исключите в остальных случаях. Маршруты аутентификации и файлов доступны через REST и описываются только в OpenAPI. OpenAPI может описывать их и тогда, когда база доступна только через GraphQL.
126
126
 
127
127
  | Старый флаг CLI | Замена |
128
128
  |---|---|
@@ -166,32 +166,30 @@ const url = `/users?${new URLSearchParams({ scope: JSON.stringify(scope) })}`;
166
166
  - Строковые сравнения `gt/gte/lt/lte` используют порядок без учёта регистра, с учётом числовых частей. Проверьте клиентов, полагавшихся на прежнее лексикографическое сравнение.
167
167
  - JSON scope ограничен 10 000 символов и 32 уровнями выбора. Неизвестные поля и операторы отклоняются даже на пустых данных.
168
168
 
169
- ## 6. Проверьте запись и включение auth
169
+ ## 6. Проверьте запись и включение аутентификации
170
170
 
171
171
  `PATCH` объединяет поля верхнего уровня; переданный вложенный обычный объект заменяет прежний с сохранением защищённых полей. `PUT` заменяет доступные для записи поля, сохраняя первичный ключ, генерируемые, служебные и `readOnly` значения. При создании и замене нужны все обязательные поля без значений по умолчанию.
172
172
 
173
- Поле связи принимает объекты, например `country: { id: '1' }`, а не скалярные ID. **Начиная с RC7 объект только с первичным ключом считается ссылкой в POST, PUT и PATCH.** Поля, даты и авторство целевой записи сохраняются. Дополнительные поля превращают его в обновление: PUT заменяет цель и требует её обязательные поля, POST/PATCH обновляют переданные поля. При изменении обратной связи нужны права на каждую запись, чей хранимый ключ меняется. Объявленные хранимые ключи, например `countryId`, тоже позволяют менять ссылки напрямую. Не передавайте одновременно связь и её хранимый ключ.
174
-
175
- **При переходе с RC6:** объект только с ключом больше не обновляет цель, не меняет её даты/авторство и не требует владения неизменяемой целевой записью. Если клиент намерен обновить или заменить цель, передавайте поля для изменения. Объявленные хранимые ключи по-прежнему позволяют менять только ссылки.
173
+ Поле связи принимает объекты, например `country: { id: '1' }`, а не скалярные ID. Объект только с первичным ключом считается ссылкой в POST, PUT и PATCH. Поля, даты и сведения о владельце целевой записи сохраняются. Если передать другие поля, сервер изменит целевую запись: PUT заменяет её и требует обязательные поля без значений по умолчанию, POST/PATCH обновляют переданные поля. При изменении обратной связи нужны права на каждую запись, чей хранимый ключ меняется. Объявленные хранимые ключи, например `countryId`, тоже позволяют менять ссылки напрямую. Не передавайте одновременно связь и её хранимый ключ.
176
174
 
177
175
  Пропущенные обратные связи (`keyOn: 'related'`) сохраняются при PUT. Явное `users: []` меняет ключи пользователей, чтобы отсоединить их; операция может завершиться ошибкой при обязательных ключах или отсутствии прав. Вложенные изменения атомарны, а изменение общей целевой записи видно всем, кто на неё ссылается.
178
176
 
179
- Auth, даты и мягкое удаление — новые необязательные возможности. Для первого перехода с сохранением записи без входа и физического удаления не добавляйте `auth`, оставьте `timestamps` / `softDelete` выключенными — это их значения по умолчанию.
177
+ Аутентификация, даты записей и мягкое удаление необязательны. Чтобы на первом этапе сохранить возможность записи без входа и физическое удаление, не добавляйте `auth` и оставьте `timestamps` и `softDelete` выключенными, как по умолчанию.
180
178
 
181
- Если включаете auth, создайте отдельное хранилище учётных записей и перенесите владельцев осознанно. Старые записи без `createdById` может менять только администратор. Через API владелец управляется сервером; подготовьте его в копии базы до запуска, если старые записи должны редактировать обычные пользователи. Auth не закрывает чтение и файловые операции. В RC8 дисковый режим сохраняет хеши сессий в файле пользователей и восстанавливает действующие сессии после перезапуска. Старые пользователи без `sessions` остаются допустимыми. При ручной замене хеша пароля также очистите `sessions` этого пользователя.
179
+ Если включаете аутентификацию, создайте отдельное хранилище пользователей и задайте владельцев существующих записей. Записи без `createdById` может менять только администратор. Через API владельца назначает сервер, поэтому задайте его в копии базы до запуска, если прежние записи должны редактировать обычные пользователи. Чтение и операции с файлами остаются открытыми. Дисковый режим сохраняет хеши сессий в файле пользователей и восстанавливает действующие сессии после перезапуска. Старые пользователи без `sessions` остаются допустимыми. Для ручной замены хеша пароля остановите сервер и одновременно очистите `sessions` этого пользователя.
182
180
 
183
- При `softDelete` DELETE сохраняет запись, списки по умолчанию скрывают удалённые записи, чтение по первичному ключу может вернуть их, а успешный PUT/PATCH восстанавливает. Старый пользовательский флаг архивирования не преобразуется автоматически. Перед включением прочитайте [правила дат, удаления и владения](README.ru.md#даты-записей-удаление-и-владельцы).
181
+ При `softDelete` DELETE сохраняет запись в базе. Списки по умолчанию скрывают удалённые записи, запрос по первичному ключу может вернуть их, а успешный PUT/PATCH восстанавливает. Прежний пользовательский флаг архивирования не преобразуется автоматически. Перед включением прочитайте [правила дат, удаления и владения](README.ru.md#даты-записей-удаление-и-владельцы).
184
182
 
185
183
  ## 7. Обновите программное использование
186
184
 
187
185
  `createServer(config)` принимает ровно один аргумент. Уберите прежний второй аргумент `{ files: false }`; для отключения файлов уберите `files` из конфигурации.
188
186
 
189
- `facade.openapi()` и `facade.graphql()` возвращают документы без записи в `target`. Нужны соответствующие секции конфигурации; для OpenAPI — также метаданные пакета и схема. Используйте флаги генерации CLI или явную запись:
187
+ `facade.openapi()` и `facade.graphql()` возвращают документы без записи в `target`. Для обоих методов нужны `database.schema` и соответствующие секции конфигурации. OpenAPI также требует `package.source`. Используйте флаги генерации CLI или явную запись:
190
188
 
191
189
  ```js
192
190
  import { createServer } from '@kollors/deep-json-server';
193
191
  import { writeOpenapi } from '@kollors/deep-json-server/openapi';
194
- import config from './server.config.js';
192
+ import config from './server.config.mjs';
195
193
 
196
194
  const facade = await createServer(config);
197
195
  await writeOpenapi(await facade.openapi(), './generated/openapi.yaml');
@@ -200,12 +198,22 @@ await app.ready(); // Инициализация и проверка базы б
200
198
  await app.close();
201
199
  ```
202
200
 
203
- Одна генерация схем не проверяет существующие записи базы. Для инициализации HTTP и проверки данных вызовите `ready()`, `inject()` или `listen()`. После изменения моделей и запросов пересоздайте клиентов из новых OpenAPI/GraphQL-документов.
201
+ Генерация схем не проверяет существующие записи базы. Для инициализации HTTP-сервера и проверки данных вызовите `ready()`, `inject()` или `listen()`. После изменения моделей и запросов пересоздайте клиентов из новых OpenAPI/GraphQL-документов.
204
202
 
205
203
  ## 8. Проверьте переход и подготовьте откат
206
204
 
207
- 1. Зафиксируйте выбранную RC-версию и переведите **копию** файлов 0.9.0. Выполните генерацию, затем инициализацию HTTP-сервера для проверки данных.
205
+ 1. Установите `@kollors/deep-json-server@1.0.0` и перенесите **копию** файлов 0.9.0. Выполните генерацию схем, затем инициализируйте HTTP-сервер для проверки сохранённых данных.
208
206
  2. Проверьте чтение по умолчанию, явные ключи, вложенные списки, фильтры и пагинацию. На одноразовых копиях записей проверьте создание, PATCH, PUT, изменение связей и удаление.
209
- 3. При включении auth проверьте владельца, администратора и обычного пользователя без владения на старых записях. Убедитесь, что загруженные файлы доступны через прежний путь к метаданным.
210
- 4. Перед переключением остановите все старые процессы сервера: RC8 заменяет каталог блокировки системными блокировками и читаемыми файлами `<имя-источника>-lock.json` для базы, auth, каталога загрузок и метаданных файлов. Старый и новый протоколы блокировки не взаимодействуют. Используйте один процесс на записываемое хранилище и останавливайте его перед ручными правками.
207
+ 3. При включении аутентификации проверьте владельца, администратора и обычного пользователя без владения на старых записях. Убедитесь, что загруженные файлы доступны через прежний путь к метаданным.
208
+ 4. Перед переключением остановите все старые процессы сервера. Версия 1.0.0 блокирует базу, файл пользователей, каталог загрузок и метаданные файлов системными блокировками и читаемыми файлами `<имя-источника>-lock.json`. Используйте один процесс на записываемое хранилище и останавливайте его перед ручными правками.
211
209
  5. Для отката храните вместе старую версию пакета, конфигурацию, схему, базу и метаданные файлов. Восстанавливайте их вместе; не направляйте 0.9.0 на данные, уже изменённые новым сервером. В последующие резервные копии включайте новые счётчики (`<database>.counters.json`) и служебные данные восстановления мягкого удаления, если используете эти функции.
210
+
211
+ ## Переход с релиз-кандидата
212
+
213
+ RC9 и 1.0.0 используют одинаковый API и формат хранимых данных. Если у вас более ранняя RC, проверьте следующие изменения и сравните конфигурацию и схему с актуальным README:
214
+
215
+ - **До RC6:** явную связь теперь нужно объявлять в обеих моделях. У стороны, где хранится ключ, укажите `keyOn: 'current'`, у обратной — `keyOn: 'related'`. Используйте пример двух моделей из раздела 3.
216
+ - **До RC7:** вложенный объект только с первичным ключом теперь связывает существующую запись без изменения её полей, дат и владельца. Если хотите изменить цель, передайте поля для изменения. Автоматически определённые ключи списков с непервичным целевым полем теперь имеют тип массива; преобразуйте прежние скалярные значения в массивы, например `"A"` в `["A"]`. Типы явно объявленных ключей не меняются.
217
+ - **До RC8:** перед обновлением остановите все старые процессы. Системные блокировки и файлы `<имя-источника>-lock.json` заменяют прежний протокол; старые и новые версии не могут согласовать блокировки. Сессии в дисковом режиме теперь сохраняются вместе с пользователями; пользователи без `sessions` остаются допустимыми. Если первичный ключ называется `data`, мутации GraphQL update/replace принимают его значение в `key`, а объект изменений — в `data`; пересоздайте затронутые клиенты.
218
+
219
+ Полная история RC — в [changelog](CHANGELOG.md).