@kollors/deep-json-server 0.9.0 → 1.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +566 -347
- package/README.ru.md +560 -341
- package/dist/bin/deep-json-server.js +1 -1
- package/dist/bin/deep-json-server.js.map +1 -1
- package/dist/index.d.ts +13 -5
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/src/auth/contract.d.ts +31 -0
- package/dist/src/auth/contract.js +12 -0
- package/dist/src/auth/contract.js.map +1 -0
- package/dist/src/auth/input.d.ts +18 -0
- package/dist/src/auth/input.js +34 -0
- package/dist/src/auth/input.js.map +1 -0
- package/dist/src/auth/password.d.ts +17 -0
- package/dist/src/auth/password.js +41 -0
- package/dist/src/auth/password.js.map +1 -0
- package/dist/src/auth/public.d.ts +3 -0
- package/dist/src/auth/public.js +5 -0
- package/dist/src/auth/public.js.map +1 -0
- package/dist/src/auth/routes.d.ts +6 -0
- package/dist/src/auth/routes.js +18 -0
- package/dist/src/auth/routes.js.map +1 -0
- package/dist/src/auth/service.d.ts +78 -0
- package/dist/src/auth/service.js +218 -0
- package/dist/src/auth/service.js.map +1 -0
- package/dist/src/auth/store.d.ts +26 -0
- package/dist/src/auth/store.js +82 -0
- package/dist/src/auth/store.js.map +1 -0
- package/dist/src/cli/index.d.ts +7 -0
- package/dist/src/cli/index.js +122 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/core/config-values.d.ts +28 -0
- package/dist/src/core/config-values.js +53 -0
- package/dist/src/core/config-values.js.map +1 -0
- package/dist/src/{constants.d.ts → core/constants.d.ts} +2 -1
- package/dist/src/{constants.js → core/constants.js} +2 -1
- package/dist/src/core/constants.js.map +1 -0
- package/dist/src/core/database.d.ts +48 -0
- package/dist/src/{database.js → core/database.js} +64 -29
- package/dist/src/core/database.js.map +1 -0
- package/dist/src/core/engine.d.ts +65 -0
- package/dist/src/core/engine.js +312 -0
- package/dist/src/core/engine.js.map +1 -0
- package/dist/src/core/errors.d.ts +9 -0
- package/dist/src/core/errors.js +12 -0
- package/dist/src/core/errors.js.map +1 -0
- package/dist/src/core/http-errors.d.ts +9 -0
- package/dist/src/core/http-errors.js +15 -0
- package/dist/src/core/http-errors.js.map +1 -0
- package/dist/src/core/lifecycle/mutation.d.ts +49 -0
- package/dist/src/core/lifecycle/mutation.js +170 -0
- package/dist/src/core/lifecycle/mutation.js.map +1 -0
- package/dist/src/core/lifecycle/options.d.ts +21 -0
- package/dist/src/core/lifecycle/options.js +19 -0
- package/dist/src/core/lifecycle/options.js.map +1 -0
- package/dist/src/core/model.d.ts +145 -0
- package/dist/src/core/model.js +510 -0
- package/dist/src/core/model.js.map +1 -0
- package/dist/src/core/mutations/write.d.ts +26 -0
- package/dist/src/core/mutations/write.js +284 -0
- package/dist/src/core/mutations/write.js.map +1 -0
- package/dist/src/core/operations.d.ts +33 -0
- package/dist/src/core/operations.js +10 -0
- package/dist/src/core/operations.js.map +1 -0
- package/dist/src/core/pagination.d.ts +10 -0
- package/dist/src/core/pagination.js +13 -0
- package/dist/src/core/pagination.js.map +1 -0
- package/dist/src/core/paths.d.ts +16 -0
- package/dist/src/core/paths.js +64 -0
- package/dist/src/core/paths.js.map +1 -0
- package/dist/src/core/query/contract.d.ts +6 -0
- package/dist/src/core/query/contract.js +22 -0
- package/dist/src/core/query/contract.js.map +1 -0
- package/dist/src/core/query/filter.d.ts +6 -0
- package/dist/src/core/query/filter.js +100 -0
- package/dist/src/core/query/filter.js.map +1 -0
- package/dist/src/core/query/options.d.ts +30 -0
- package/dist/src/core/query/options.js +44 -0
- package/dist/src/core/query/options.js.map +1 -0
- package/dist/src/core/records.d.ts +46 -0
- package/dist/src/core/records.js +69 -0
- package/dist/src/core/records.js.map +1 -0
- package/dist/src/core/relation-metadata.d.ts +15 -0
- package/dist/src/{relation-metadata.js → core/relation-metadata.js} +7 -2
- package/dist/src/core/relation-metadata.js.map +1 -0
- package/dist/src/core/storage.d.ts +2 -0
- package/dist/src/core/storage.js +2 -0
- package/dist/src/core/storage.js.map +1 -0
- package/dist/src/core/types.d.ts +10 -0
- package/dist/src/{types.js.map → core/types.js.map} +1 -1
- package/dist/src/core/utils.d.ts +56 -0
- package/dist/src/core/utils.js +102 -0
- package/dist/src/core/utils.js.map +1 -0
- package/dist/src/files/contract.d.ts +32 -30
- package/dist/src/files/contract.js +29 -48
- package/dist/src/files/contract.js.map +1 -1
- package/dist/src/files/disk-store.d.ts +5 -1
- package/dist/src/files/disk-store.js +61 -33
- package/dist/src/files/disk-store.js.map +1 -1
- package/dist/src/files/http.d.ts +36 -0
- package/dist/src/files/http.js +53 -0
- package/dist/src/files/http.js.map +1 -0
- package/dist/src/files/index.d.ts +5 -4
- package/dist/src/files/index.js +7 -2
- package/dist/src/files/index.js.map +1 -1
- package/dist/src/files/memory-store.d.ts +4 -1
- package/dist/src/files/memory-store.js +29 -22
- package/dist/src/files/memory-store.js.map +1 -1
- package/dist/src/files/routes.d.ts +3 -0
- package/dist/src/files/routes.js +27 -2
- package/dist/src/files/routes.js.map +1 -1
- package/dist/src/files/streams.d.ts +5 -0
- package/dist/src/files/streams.js +20 -0
- package/dist/src/files/streams.js.map +1 -0
- package/dist/src/graphql/entry.d.ts +12 -0
- package/dist/src/graphql/entry.js +13 -0
- package/dist/src/graphql/entry.js.map +1 -0
- package/dist/src/graphql/generate.d.ts +12 -0
- package/dist/src/graphql/generate.js +19 -0
- package/dist/src/graphql/generate.js.map +1 -0
- package/dist/src/graphql/preflight.d.ts +6 -0
- package/dist/src/graphql/preflight.js +42 -0
- package/dist/src/graphql/preflight.js.map +1 -0
- package/dist/src/graphql/resolvers.d.ts +17 -0
- package/dist/src/graphql/resolvers.js +44 -0
- package/dist/src/graphql/resolvers.js.map +1 -0
- package/dist/src/graphql/routes.d.ts +8 -0
- package/dist/src/graphql/routes.js +30 -0
- package/dist/src/graphql/routes.js.map +1 -0
- package/dist/src/graphql/schema.d.ts +6 -0
- package/dist/src/graphql/schema.js +230 -0
- package/dist/src/graphql/schema.js.map +1 -0
- package/dist/src/graphql/write.d.ts +4 -0
- package/dist/src/graphql/write.js +10 -0
- package/dist/src/graphql/write.js.map +1 -0
- package/dist/src/openapi/auth.d.ts +13 -0
- package/dist/src/openapi/auth.js +139 -0
- package/dist/src/openapi/auth.js.map +1 -0
- package/dist/src/openapi/document.d.ts +11 -7
- package/dist/src/openapi/document.js +287 -318
- package/dist/src/openapi/document.js.map +1 -1
- package/dist/src/openapi/entry.d.ts +14 -0
- package/dist/src/openapi/entry.js +13 -0
- package/dist/src/openapi/entry.js.map +1 -0
- package/dist/src/openapi/files.d.ts +6 -0
- package/dist/src/openapi/files.js +87 -0
- package/dist/src/openapi/files.js.map +1 -0
- package/dist/src/openapi/generate.d.ts +19 -0
- package/dist/src/openapi/generate.js +41 -0
- package/dist/src/openapi/generate.js.map +1 -0
- package/dist/src/openapi/helpers.d.ts +26 -0
- package/dist/src/openapi/helpers.js +13 -0
- package/dist/src/openapi/helpers.js.map +1 -0
- package/dist/src/openapi/options.d.ts +18 -0
- package/dist/src/openapi/options.js +13 -0
- package/dist/src/openapi/options.js.map +1 -0
- package/dist/src/{types.d.ts → openapi/types.d.ts} +2 -12
- package/dist/src/openapi/types.js +2 -0
- package/dist/src/openapi/types.js.map +1 -0
- package/dist/src/openapi/write.d.ts +5 -0
- package/dist/src/openapi/write.js +12 -0
- package/dist/src/openapi/write.js.map +1 -0
- package/dist/src/rest/options.d.ts +22 -0
- package/dist/src/rest/options.js +65 -0
- package/dist/src/rest/options.js.map +1 -0
- package/dist/src/rest/projection.d.ts +13 -0
- package/dist/src/rest/projection.js +61 -0
- package/dist/src/rest/projection.js.map +1 -0
- package/dist/src/rest/routes.d.ts +7 -0
- package/dist/src/rest/routes.js +49 -0
- package/dist/src/rest/routes.js.map +1 -0
- package/dist/src/server/config.d.ts +74 -0
- package/dist/src/server/config.js +142 -0
- package/dist/src/server/config.js.map +1 -0
- package/dist/src/server/create.d.ts +16 -0
- package/dist/src/server/create.js +133 -0
- package/dist/src/server/create.js.map +1 -0
- package/dist/src/server/features.d.ts +4 -0
- package/dist/src/server/features.js +11 -0
- package/dist/src/server/features.js.map +1 -0
- package/dist/src/server/model.d.ts +6 -0
- package/dist/src/server/model.js +14 -0
- package/dist/src/server/model.js.map +1 -0
- package/dist/src/server/public.d.ts +2 -0
- package/dist/src/server/public.js +2 -0
- package/dist/src/server/public.js.map +1 -0
- package/package.json +25 -4
- package/dist/src/cli.d.ts +0 -4
- package/dist/src/cli.js +0 -80
- package/dist/src/cli.js.map +0 -1
- package/dist/src/config.d.ts +0 -54
- package/dist/src/config.js +0 -145
- package/dist/src/config.js.map +0 -1
- package/dist/src/constants.js.map +0 -1
- package/dist/src/database.d.ts +0 -20
- package/dist/src/database.js.map +0 -1
- package/dist/src/openapi/config.d.ts +0 -11
- package/dist/src/openapi/config.js +0 -204
- package/dist/src/openapi/config.js.map +0 -1
- package/dist/src/openapi/index.d.ts +0 -10
- package/dist/src/openapi/index.js +0 -27
- package/dist/src/openapi/index.js.map +0 -1
- package/dist/src/openapi/inference.d.ts +0 -14
- package/dist/src/openapi/inference.js +0 -145
- package/dist/src/openapi/inference.js.map +0 -1
- package/dist/src/query/filter.d.ts +0 -6
- package/dist/src/query/filter.js +0 -248
- package/dist/src/query/filter.js.map +0 -1
- package/dist/src/query/index.d.ts +0 -3
- package/dist/src/query/index.js +0 -4
- package/dist/src/query/index.js.map +0 -1
- package/dist/src/query/pagination.d.ts +0 -11
- package/dist/src/query/pagination.js +0 -28
- package/dist/src/query/pagination.js.map +0 -1
- package/dist/src/query/sort.d.ts +0 -1
- package/dist/src/query/sort.js +0 -54
- package/dist/src/query/sort.js.map +0 -1
- package/dist/src/relation-metadata.d.ts +0 -10
- package/dist/src/relation-metadata.js.map +0 -1
- package/dist/src/relations.d.ts +0 -12
- package/dist/src/relations.js +0 -155
- package/dist/src/relations.js.map +0 -1
- package/dist/src/server.d.ts +0 -13
- package/dist/src/server.js +0 -188
- package/dist/src/server.js.map +0 -1
- package/dist/src/utils.d.ts +0 -20
- package/dist/src/utils.js +0 -63
- package/dist/src/utils.js.map +0 -1
- /package/dist/src/{types.js → core/types.js} +0 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { JSONFile } from 'lowdb/node';
|
|
2
|
+
import { domainError } from '../core/errors.js';
|
|
3
|
+
import { createSerialQueue, hasOnlyKeys, isObject } from '../core/utils.js';
|
|
4
|
+
import { MAX_USERNAME_LENGTH } from './contract.js';
|
|
5
|
+
import { validPasswordHash } from './password.js';
|
|
6
|
+
/** Проверяет записи и строит индексы по двум уникальным строковым ключам.
|
|
7
|
+
* @example Две записи с разными id и username → два индекса; повторяющийся ключ → ошибка.
|
|
8
|
+
*/
|
|
9
|
+
function indexUsers(records) {
|
|
10
|
+
if (!Array.isArray(records))
|
|
11
|
+
throw new Error('Auth users must be an array');
|
|
12
|
+
const byId = new Map();
|
|
13
|
+
const byUsername = new Map();
|
|
14
|
+
for (const record of records) {
|
|
15
|
+
if (!isObject(record) ||
|
|
16
|
+
!hasOnlyKeys(record, ['id', 'username', 'passwordHash', 'isAdmin']) ||
|
|
17
|
+
typeof record.id !== 'string' ||
|
|
18
|
+
!record.id.trim() ||
|
|
19
|
+
typeof record.username !== 'string' ||
|
|
20
|
+
!record.username.trim() ||
|
|
21
|
+
record.username.length > MAX_USERNAME_LENGTH ||
|
|
22
|
+
!validPasswordHash(record.passwordHash) ||
|
|
23
|
+
(record.isAdmin !== undefined && typeof record.isAdmin !== 'boolean'))
|
|
24
|
+
throw new Error('Auth users require id, username and a valid passwordHash');
|
|
25
|
+
if (byId.has(record.id) || byUsername.has(record.username))
|
|
26
|
+
throw new Error('Auth user ids and usernames must be unique');
|
|
27
|
+
const user = Object.freeze({ id: record.id, username: record.username, passwordHash: record.passwordHash, isAdmin: record.isAdmin ?? false });
|
|
28
|
+
byId.set(user.id, user);
|
|
29
|
+
byUsername.set(user.username, user);
|
|
30
|
+
}
|
|
31
|
+
return { byId, byUsername };
|
|
32
|
+
}
|
|
33
|
+
/** Хранит подтверждённые записи и последовательно сохраняет изменения одного пользователя. */
|
|
34
|
+
export class AuthStore {
|
|
35
|
+
users;
|
|
36
|
+
file;
|
|
37
|
+
schedule = createSerialQueue();
|
|
38
|
+
constructor(users, file) {
|
|
39
|
+
this.users = users;
|
|
40
|
+
this.file = file;
|
|
41
|
+
}
|
|
42
|
+
/** Возвращает индексы последнего успешно сохранённого состояния.
|
|
43
|
+
* @example Во время записи нового пароля read() ещё возвращает запись с прежним хешем.
|
|
44
|
+
*/
|
|
45
|
+
read() {
|
|
46
|
+
return this.users;
|
|
47
|
+
}
|
|
48
|
+
/** Проверяет изменение внутри очереди и публикует его после записи; обработчик подтверждения вызывается синхронно.
|
|
49
|
+
* @example Успешная запись → новый пользователь; ошибка файла → прежние индексы, обработчик не вызван.
|
|
50
|
+
*/
|
|
51
|
+
update(operation, committed) {
|
|
52
|
+
return this.schedule(async () => {
|
|
53
|
+
const user = Object.freeze(operation(this.users));
|
|
54
|
+
const owner = this.users.byUsername.get(user.username);
|
|
55
|
+
if (owner && owner.id !== user.id)
|
|
56
|
+
throw domainError('CONFLICT', 'Username already exists');
|
|
57
|
+
const byId = new Map(this.users.byId);
|
|
58
|
+
const byUsername = new Map(this.users.byUsername);
|
|
59
|
+
const previous = byId.get(user.id);
|
|
60
|
+
if (previous)
|
|
61
|
+
byUsername.delete(previous.username);
|
|
62
|
+
byId.set(user.id, user);
|
|
63
|
+
byUsername.set(user.username, user);
|
|
64
|
+
if (this.file)
|
|
65
|
+
await this.file.write([...byId.values()]);
|
|
66
|
+
this.users = { byId, byUsername };
|
|
67
|
+
committed?.();
|
|
68
|
+
return user;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Загружает массив из файла или памяти и подключает запись в тот же источник.
|
|
73
|
+
* @example Массив → хранилище в памяти; 'users.json' → чтение и сохранение JSON-массива через JSONFile.
|
|
74
|
+
*/
|
|
75
|
+
export async function createAuthStore(source) {
|
|
76
|
+
const file = typeof source === 'string' ? new JSONFile(source) : undefined;
|
|
77
|
+
const records = file ? await file.read() : source;
|
|
78
|
+
if (file && records === null)
|
|
79
|
+
throw new Error(`Auth users file not found: ${source}`);
|
|
80
|
+
return new AuthStore(indexUsers(records), file);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/auth/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createConfiguredServer } from '../server/create.js';
|
|
2
|
+
/** Разбирает флаги, выполняет экспорт по секциям конфигурации и при необходимости запускает сервер.
|
|
3
|
+
* @example runCli(['--generate-only', 'config.mjs']) → файлы схем без открытия порта.
|
|
4
|
+
*/
|
|
5
|
+
export declare function runCli(args?: string[], services?: {
|
|
6
|
+
createServer: typeof createConfiguredServer;
|
|
7
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import { DEFAULT_HOST, DEFAULT_PORT, VERSION } from '../core/constants.js';
|
|
3
|
+
import { inputPaths, validateExportPaths } from '../core/paths.js';
|
|
4
|
+
import { isObject } from '../core/utils.js';
|
|
5
|
+
import { writeGraphql } from '../graphql/entry.js';
|
|
6
|
+
import { writeOpenapi } from '../openapi/entry.js';
|
|
7
|
+
import { configure, readConfigModule } from '../server/config.js';
|
|
8
|
+
import { createConfiguredServer } from '../server/create.js';
|
|
9
|
+
import { configuredModel } from '../server/model.js';
|
|
10
|
+
const HELP_TEXT = `Deep JSON Server
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
deep-json-server [options] <server.config.js>
|
|
14
|
+
|
|
15
|
+
--generate Export configured schemas, then start the server
|
|
16
|
+
--generate-only Export configured schemas and exit
|
|
17
|
+
--host <host> Server address
|
|
18
|
+
--port <port> Server port
|
|
19
|
+
--help, -h Show help
|
|
20
|
+
--version, -v Show version
|
|
21
|
+
|
|
22
|
+
Modules and export formats are selected by configuration sections.
|
|
23
|
+
Generate flags are mutually exclusive and require output targets.`;
|
|
24
|
+
/** Проверяет все назначения, строит обе схемы и только затем сохраняет файлы.
|
|
25
|
+
* @example Секции openapi и graphql с target → два файла; нет target → ошибка до записи.
|
|
26
|
+
*/
|
|
27
|
+
async function generate(config, source, directory, sourcePath) {
|
|
28
|
+
if (!config.openapi && !config.graphql)
|
|
29
|
+
throw new Error('Generation requires an openapi or graphql section');
|
|
30
|
+
const outputs = [];
|
|
31
|
+
for (const format of ['openapi', 'graphql']) {
|
|
32
|
+
if (!config[format])
|
|
33
|
+
continue;
|
|
34
|
+
const target = config[format].target;
|
|
35
|
+
if (!target)
|
|
36
|
+
throw new Error(`Укажите config.${format}.target`);
|
|
37
|
+
outputs.push(target);
|
|
38
|
+
}
|
|
39
|
+
await validateExportPaths(outputs, inputPaths(source, directory, sourcePath));
|
|
40
|
+
const model = await configuredModel(config);
|
|
41
|
+
const openapi = config.openapi
|
|
42
|
+
? (await import('../openapi/generate.js')).openapiFromModel(model, {
|
|
43
|
+
files: config.files !== undefined,
|
|
44
|
+
auth: config.auth !== undefined,
|
|
45
|
+
host: config.server.host,
|
|
46
|
+
port: config.server.port,
|
|
47
|
+
pageSize: config.server.pageSize,
|
|
48
|
+
maxPageSize: config.server.maxPageSize,
|
|
49
|
+
info: config.openapi.info,
|
|
50
|
+
})
|
|
51
|
+
: undefined;
|
|
52
|
+
const graphql = config.graphql ? (await import('../graphql/generate.js')).graphqlFromModel(model) : undefined;
|
|
53
|
+
if (openapi && config.openapi?.target) {
|
|
54
|
+
await writeOpenapi(openapi, config.openapi.target);
|
|
55
|
+
process.stdout.write(`OpenAPI: ${config.openapi.target}\n`);
|
|
56
|
+
}
|
|
57
|
+
if (graphql && config.graphql?.target) {
|
|
58
|
+
await writeGraphql(graphql, config.graphql.target);
|
|
59
|
+
process.stdout.write(`GraphQL: ${config.graphql.target}\n`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Разбирает флаги, выполняет экспорт по секциям конфигурации и при необходимости запускает сервер.
|
|
63
|
+
* @example runCli(['--generate-only', 'config.mjs']) → файлы схем без открытия порта.
|
|
64
|
+
*/
|
|
65
|
+
export async function runCli(args = process.argv.slice(2), services = { createServer: createConfiguredServer }) {
|
|
66
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
67
|
+
process.stdout.write(`${HELP_TEXT}\n`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
71
|
+
process.stdout.write(`${VERSION}\n`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const positional = [];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
let host;
|
|
77
|
+
let port;
|
|
78
|
+
for (let index = 0; index < args.length; index++) {
|
|
79
|
+
const arg = args[index];
|
|
80
|
+
if (!arg.startsWith('-')) {
|
|
81
|
+
positional.push(arg);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!['--generate', '--generate-only', '--host', '--port'].includes(arg) || seen.has(arg))
|
|
85
|
+
throw new Error(`Неизвестный параметр или повтор: ${arg}`);
|
|
86
|
+
seen.add(arg);
|
|
87
|
+
if (arg === '--host' || arg === '--port') {
|
|
88
|
+
const value = args[++index];
|
|
89
|
+
if (!value || value.startsWith('-'))
|
|
90
|
+
throw new Error(`Укажите значение ${arg}`);
|
|
91
|
+
if (arg === '--host')
|
|
92
|
+
host = value;
|
|
93
|
+
else {
|
|
94
|
+
if (!/^\d+$/.test(value))
|
|
95
|
+
throw new Error('Invalid --port');
|
|
96
|
+
port = Number(value);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (seen.has('--generate') && seen.has('--generate-only'))
|
|
101
|
+
throw new Error('--generate and --generate-only are mutually exclusive');
|
|
102
|
+
if (!positional.length)
|
|
103
|
+
throw new Error('Укажите путь к файлу конфигурации');
|
|
104
|
+
if (positional.length !== 1)
|
|
105
|
+
throw new Error('Можно указать только один файл конфигурации');
|
|
106
|
+
const source = await readConfigModule(positional[0]);
|
|
107
|
+
const server = source.config.server ?? {};
|
|
108
|
+
if (!isObject(server))
|
|
109
|
+
throw new Error('config.server must be an object');
|
|
110
|
+
const config = configure({
|
|
111
|
+
...source.config,
|
|
112
|
+
server: { ...server, host: host ?? server.host ?? process.env.HOST ?? DEFAULT_HOST, port: port ?? server.port ?? Number(process.env.PORT ?? DEFAULT_PORT) },
|
|
113
|
+
}, source.directory, source.path);
|
|
114
|
+
if (seen.has('--generate') || seen.has('--generate-only'))
|
|
115
|
+
await generate(config, source.config, source.directory, source.path);
|
|
116
|
+
if (seen.has('--generate-only'))
|
|
117
|
+
return;
|
|
118
|
+
const app = (await services.createServer(config)).fastify();
|
|
119
|
+
await app.listen();
|
|
120
|
+
app.log.info('Deep JSON Server started');
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Проверяет объект и обязательность значения; необязательные null и undefined пропускает.
|
|
2
|
+
* @example getObject(undefined, 'data') → undefined; getObject([], 'data', true) → ошибка.
|
|
3
|
+
*/
|
|
4
|
+
export declare function getObject(value: unknown, path: string, required: true): Record<string, unknown>;
|
|
5
|
+
export declare function getObject(value: unknown, path: string, required?: false): Record<string, unknown> | undefined;
|
|
6
|
+
/** Проверяет непустую строку, не обрезая её; необязательные null и undefined пропускает.
|
|
7
|
+
* @example getString(' a ', 'name') → ' a '; getString('', 'name') → ошибка.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getString(value: unknown, path: string, required: true): string;
|
|
10
|
+
export declare function getString(value: unknown, path: string, required?: false): string | undefined;
|
|
11
|
+
/** Принимает положительное безопасное целое число или отсутствие значения.
|
|
12
|
+
* @example getPositiveInteger(2, 'size') → 2; getPositiveInteger(0, 'size') → ошибка.
|
|
13
|
+
*/
|
|
14
|
+
export declare const getPositiveInteger: (value: unknown, path: string) => number | undefined;
|
|
15
|
+
/** Проверяет необязательный логический флаг, не преобразуя строки в boolean.
|
|
16
|
+
* @example getBoolean(false, 'flag') → false; getBoolean('false', 'flag') → ошибка.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getBoolean(value: unknown, path: string): boolean | undefined;
|
|
19
|
+
/** Проверяет адрес и порт и подставляет значения по умолчанию.
|
|
20
|
+
* @example normalizeAddress({ port: 0 }) → { host: '127.0.0.1', port: 0 }.
|
|
21
|
+
*/
|
|
22
|
+
export declare function normalizeAddress(options: {
|
|
23
|
+
host?: unknown;
|
|
24
|
+
port?: unknown;
|
|
25
|
+
}): {
|
|
26
|
+
host: string;
|
|
27
|
+
port: number;
|
|
28
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { DEFAULT_HOST, DEFAULT_PORT } from './constants.js';
|
|
2
|
+
import { isObject, isPortNumber } from './utils.js';
|
|
3
|
+
export function getObject(value, path, required = false) {
|
|
4
|
+
if (value == null && !required) {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
if (!isObject(value)) {
|
|
8
|
+
throw new Error(`Ключ ${path} должен быть JSON-объектом`);
|
|
9
|
+
}
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
export function getString(value, path, required = false) {
|
|
13
|
+
if (value == null && !required) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
17
|
+
throw new Error(`Ключ ${path} должен содержать непустую строку`);
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
/** Принимает положительное безопасное целое число или отсутствие значения.
|
|
22
|
+
* @example getPositiveInteger(2, 'size') → 2; getPositiveInteger(0, 'size') → ошибка.
|
|
23
|
+
*/
|
|
24
|
+
export const getPositiveInteger = (value, path) => {
|
|
25
|
+
if (value == null) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 1) {
|
|
29
|
+
throw new Error(`Ключ ${path} должен быть положительным целым числом`);
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
};
|
|
33
|
+
/** Проверяет необязательный логический флаг, не преобразуя строки в boolean.
|
|
34
|
+
* @example getBoolean(false, 'flag') → false; getBoolean('false', 'flag') → ошибка.
|
|
35
|
+
*/
|
|
36
|
+
export function getBoolean(value, path) {
|
|
37
|
+
if (value === undefined)
|
|
38
|
+
return undefined;
|
|
39
|
+
if (typeof value !== 'boolean')
|
|
40
|
+
throw new Error(`${path} must be boolean`);
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
/** Проверяет адрес и порт и подставляет значения по умолчанию.
|
|
44
|
+
* @example normalizeAddress({ port: 0 }) → { host: '127.0.0.1', port: 0 }.
|
|
45
|
+
*/
|
|
46
|
+
export function normalizeAddress(options) {
|
|
47
|
+
const host = getString(options.host, 'config.server.host') ?? DEFAULT_HOST;
|
|
48
|
+
const port = options.port ?? DEFAULT_PORT;
|
|
49
|
+
if (!isPortNumber(port))
|
|
50
|
+
throw new Error('Ключ config.server.port должен быть целым числом от 0 до 65535');
|
|
51
|
+
return { host, port };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=config-values.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-values.js","sourceRoot":"","sources":["../../../src/core/config-values.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAOpD,MAAM,UAAU,SAAS,CAAC,KAAc,EAAE,IAAY,EAAE,QAAQ,GAAG,KAAK;IACtE,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,4BAA4B,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAOD,MAAM,UAAU,SAAS,CAAC,KAAc,EAAE,IAAY,EAAE,QAAQ,GAAG,KAAK;IACtE,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,mCAAmC,CAAC,CAAC;IACnE,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAAc,EAAE,IAAY,EAAsB,EAAE;IACrF,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;QAClB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,yCAAyC,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc,EAAE,IAAY;IACrD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,kBAAkB,CAAC,CAAC;IAC3E,OAAO,KAAK,CAAC;AACf,CAAC;AACD;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAA2C;IAC1E,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,oBAAoB,CAAC,IAAI,YAAY,CAAC;IAC3E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,YAAY,CAAC;IAC1C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;IAC3G,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const DEFAULT_HOST = "127.0.0.1";
|
|
2
2
|
export declare const DEFAULT_MAX_FILE_SIZE: number;
|
|
3
|
-
export declare const DEFAULT_MAX_PAGE_SIZE =
|
|
3
|
+
export declare const DEFAULT_MAX_PAGE_SIZE = 100;
|
|
4
4
|
export declare const DEFAULT_PAGE_SIZE = 10;
|
|
5
5
|
export declare const DEFAULT_PORT = 4001;
|
|
6
|
+
export declare const VERSION = "1.0.0-alpha.10";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const DEFAULT_HOST = '127.0.0.1';
|
|
2
2
|
export const DEFAULT_MAX_FILE_SIZE = 100 * 1024 * 1024;
|
|
3
|
-
export const DEFAULT_MAX_PAGE_SIZE =
|
|
3
|
+
export const DEFAULT_MAX_PAGE_SIZE = 100;
|
|
4
4
|
export const DEFAULT_PAGE_SIZE = 10;
|
|
5
5
|
export const DEFAULT_PORT = 4001;
|
|
6
|
+
export const VERSION = '1.0.0-alpha.10';
|
|
6
7
|
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/core/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC;AACxC,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AACvD,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AACzC,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AACpC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC;AACjC,MAAM,CAAC,MAAM,OAAO,GAAG,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ModelSchema } from './model.js';
|
|
2
|
+
import type { Source, Storage } from './storage.js';
|
|
3
|
+
import type { DatabaseData, DatabaseRecord } from './types.js';
|
|
4
|
+
export interface DatabaseConfig<S extends Storage = Storage> {
|
|
5
|
+
source: Source<S, DatabaseData>;
|
|
6
|
+
schema?: Source<S, ModelSchema>;
|
|
7
|
+
}
|
|
8
|
+
export interface DatabaseContainer {
|
|
9
|
+
data: DatabaseData;
|
|
10
|
+
counters?: Record<string, number>;
|
|
11
|
+
}
|
|
12
|
+
export interface DatabaseStore {
|
|
13
|
+
database: DatabaseContainer;
|
|
14
|
+
path?: string;
|
|
15
|
+
read(): Promise<DatabaseData>;
|
|
16
|
+
/** Передаёт изменяемый черновик и исходные данные только для чтения; сохраняет черновик после проверки.
|
|
17
|
+
* @example update((draft, before) => { draft.data.notes = []; return before.notes.length; }) → прежнее число записей.
|
|
18
|
+
*/
|
|
19
|
+
update<T>(operation: (database: DatabaseContainer, before: Readonly<DatabaseData>) => T): Promise<T>;
|
|
20
|
+
}
|
|
21
|
+
/** Проверяет сериализуемое JSON-значение; отвергает циклы, разреженные массивы, NaN и бесконечности.
|
|
22
|
+
* @example validateJsonValue({ a: [1, null] }, 'data') → undefined; NaN → ошибка.
|
|
23
|
+
*/
|
|
24
|
+
export declare const validateJsonValue: (value: unknown, path: string, ancestors?: WeakSet<object>) => void;
|
|
25
|
+
/** Проверяет объект коллекций, записи и уникальность ключей; возвращает исходный объект.
|
|
26
|
+
* @example validateDatabase({ notes: [{ id: '1' }] }) → тот же объект; повторный id → ошибка.
|
|
27
|
+
*/
|
|
28
|
+
export declare const validateDatabase: (data: unknown, primaryKeys?: Map<string, string>) => DatabaseData;
|
|
29
|
+
/** Читает UTF-8 JSON-файл и проверяет, что верхний уровень является объектом.
|
|
30
|
+
* @example Файл с {"a":1} → Promise<{ a: 1 }>; файл с [] → ошибка.
|
|
31
|
+
*/
|
|
32
|
+
export declare const readJsonObjectFile: (path: string, label: string) => Promise<Record<string, unknown>>;
|
|
33
|
+
/** Читает файл и проверяет коллекции и первичные ключи.
|
|
34
|
+
* @example Файл с {"notes":[]} → Promise<{ notes: [] }>.
|
|
35
|
+
*/
|
|
36
|
+
export declare const readDatabaseFile: (databasePath: string, keys?: Map<string, string>) => Promise<DatabaseData>;
|
|
37
|
+
/** Создаёт дисковое хранилище или хранилище в памяти по настройкам.
|
|
38
|
+
* @example createDatabaseStore({ source: { notes: [] } }) → Promise<DatabaseStore>.
|
|
39
|
+
*/
|
|
40
|
+
export declare const createDatabaseStore: (config: DatabaseConfig, keys?: Map<string, string>) => Promise<DatabaseStore>;
|
|
41
|
+
/** Ищет индекс записи, сравнивая строковые представления id.
|
|
42
|
+
* @example findItemIndex([{ id: 1 }], '1') → 0; пустой массив → -1.
|
|
43
|
+
*/
|
|
44
|
+
export declare const findItemIndex: (collection: DatabaseRecord[], id: unknown) => number;
|
|
45
|
+
/** Генерирует случайный строковый id, отсутствующий в переданной коллекции.
|
|
46
|
+
* @example createId([]) → строка из 11 символов; конкретное значение зависит от генератора случайных чисел.
|
|
47
|
+
*/
|
|
48
|
+
export declare const createId: (collection: DatabaseRecord[]) => string;
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { Low } from 'lowdb';
|
|
4
|
+
import { JSONFile } from 'lowdb/node';
|
|
5
|
+
import { domainError } from './errors.js';
|
|
6
|
+
import { createSerialQueue, createUniqueId, isObject, isSafeKey, isSystemError, resolveDatabasePath } from './utils.js';
|
|
5
7
|
const RESOURCE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
8
|
+
/** Проверяет сериализуемое JSON-значение; отвергает циклы, разреженные массивы, NaN и бесконечности.
|
|
9
|
+
* @example validateJsonValue({ a: [1, null] }, 'data') → undefined; NaN → ошибка.
|
|
10
|
+
*/
|
|
6
11
|
export const validateJsonValue = (value, path, ancestors = new WeakSet()) => {
|
|
7
12
|
if (value === null || typeof value === 'string' || typeof value === 'boolean') {
|
|
8
13
|
return;
|
|
@@ -39,7 +44,10 @@ export const validateJsonValue = (value, path, ancestors = new WeakSet()) => {
|
|
|
39
44
|
}
|
|
40
45
|
ancestors.delete(value);
|
|
41
46
|
};
|
|
42
|
-
|
|
47
|
+
/** Проверяет объект коллекций, записи и уникальность ключей; возвращает исходный объект.
|
|
48
|
+
* @example validateDatabase({ notes: [{ id: '1' }] }) → тот же объект; повторный id → ошибка.
|
|
49
|
+
*/
|
|
50
|
+
export const validateDatabase = (data, primaryKeys) => {
|
|
43
51
|
if (!isObject(data)) {
|
|
44
52
|
throw new Error('База данных должна содержать JSON-объект');
|
|
45
53
|
}
|
|
@@ -51,18 +59,19 @@ export const validateDatabase = (data) => {
|
|
|
51
59
|
if (!Array.isArray(records)) {
|
|
52
60
|
throw new Error(`Ресурс «${resource}» должен содержать JSON-массив`);
|
|
53
61
|
}
|
|
62
|
+
const primary = primaryKeys?.get(resource) ?? 'id';
|
|
54
63
|
const ids = new Set();
|
|
55
64
|
records.forEach((record, index) => {
|
|
56
65
|
if (!isObject(record)) {
|
|
57
66
|
throw new Error(`Запись ${index} ресурса «${resource}» должна содержать JSON-объект`);
|
|
58
67
|
}
|
|
59
|
-
if (typeof record
|
|
68
|
+
if (typeof record[primary] !== 'string' && !(typeof record[primary] === 'number' && Number.isFinite(record[primary]))) {
|
|
60
69
|
throw new Error(`Запись ${index} ресурса «${resource}» должна содержать строковый или числовой id`);
|
|
61
70
|
}
|
|
62
|
-
if (String(record
|
|
71
|
+
if (String(record[primary]) === '') {
|
|
63
72
|
throw new Error(`Запись ${index} ресурса «${resource}» должна содержать непустой id`);
|
|
64
73
|
}
|
|
65
|
-
const id = String(record
|
|
74
|
+
const id = String(record[primary]);
|
|
66
75
|
if (ids.has(id)) {
|
|
67
76
|
throw new Error(`Ресурс «${resource}» содержит повторяющийся id «${id}»`);
|
|
68
77
|
}
|
|
@@ -71,6 +80,9 @@ export const validateDatabase = (data) => {
|
|
|
71
80
|
});
|
|
72
81
|
return data;
|
|
73
82
|
};
|
|
83
|
+
/** Читает UTF-8 JSON-файл и проверяет, что верхний уровень является объектом.
|
|
84
|
+
* @example Файл с {"a":1} → Promise<{ a: 1 }>; файл с [] → ошибка.
|
|
85
|
+
*/
|
|
74
86
|
export const readJsonObjectFile = async (path, label) => {
|
|
75
87
|
const resolvedPath = resolve(path);
|
|
76
88
|
let source;
|
|
@@ -89,48 +101,71 @@ export const readJsonObjectFile = async (path, label) => {
|
|
|
89
101
|
}
|
|
90
102
|
return value;
|
|
91
103
|
};
|
|
92
|
-
|
|
93
|
-
|
|
104
|
+
/** Читает файл и проверяет коллекции и первичные ключи.
|
|
105
|
+
* @example Файл с {"notes":[]} → Promise<{ notes: [] }>.
|
|
106
|
+
*/
|
|
107
|
+
export const readDatabaseFile = async (databasePath, keys) => validateDatabase(await readJsonObjectFile(databasePath, 'Файл базы данных'), keys);
|
|
108
|
+
const validateDraft = (data, keys) => {
|
|
109
|
+
try {
|
|
110
|
+
validateDatabase(data, keys);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
throw domainError('INVALID_INPUT', error.message);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const createDiskDatabaseStore = async (databasePath, keys) => {
|
|
94
117
|
const resolvedDatabasePath = resolveDatabasePath(databasePath);
|
|
95
|
-
const initialData = await readDatabaseFile(resolvedDatabasePath);
|
|
96
|
-
const database =
|
|
118
|
+
const initialData = await readDatabaseFile(resolvedDatabasePath, keys);
|
|
119
|
+
const database = new Low(new JSONFile(resolvedDatabasePath), initialData);
|
|
97
120
|
const schedule = createSerialQueue();
|
|
121
|
+
const counterStore = new Low(new JSONFile(`${resolvedDatabasePath}.counters.json`), {});
|
|
122
|
+
await counterStore.read();
|
|
98
123
|
const read = async () => {
|
|
99
|
-
database.data = await readDatabaseFile(resolvedDatabasePath);
|
|
124
|
+
database.data = await readDatabaseFile(resolvedDatabasePath, keys);
|
|
100
125
|
return database.data;
|
|
101
126
|
};
|
|
102
127
|
const update = (operation) => schedule(async () => {
|
|
103
128
|
await read();
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
129
|
+
await counterStore.read();
|
|
130
|
+
const draft = { data: structuredClone(database.data), counters: structuredClone(counterStore.data) };
|
|
131
|
+
const result = operation(draft, database.data);
|
|
132
|
+
validateDraft(draft.data, keys);
|
|
133
|
+
// Reserve generated numbers first: failed data writes may leave gaps, never reused IDs.
|
|
134
|
+
if (JSON.stringify(draft.counters) !== JSON.stringify(counterStore.data)) {
|
|
135
|
+
counterStore.data = draft.counters;
|
|
136
|
+
await counterStore.write();
|
|
137
|
+
}
|
|
138
|
+
await database.adapter.write(draft.data);
|
|
139
|
+
database.data = draft.data;
|
|
107
140
|
return result;
|
|
108
141
|
});
|
|
109
142
|
return { database, path: resolvedDatabasePath, read, update };
|
|
110
143
|
};
|
|
111
|
-
const createMemoryDatabaseStore = (sourceData) => {
|
|
112
|
-
validateDatabase(sourceData);
|
|
113
|
-
const database = { data: structuredClone(sourceData) };
|
|
144
|
+
const createMemoryDatabaseStore = (sourceData, keys) => {
|
|
145
|
+
validateDatabase(sourceData, keys);
|
|
146
|
+
const database = { data: structuredClone(sourceData), counters: {} };
|
|
114
147
|
const schedule = createSerialQueue();
|
|
115
148
|
const read = async () => database.data;
|
|
116
149
|
const update = (operation) => schedule(() => {
|
|
117
|
-
const draft =
|
|
118
|
-
const result = operation(draft);
|
|
119
|
-
|
|
150
|
+
const draft = structuredClone(database);
|
|
151
|
+
const result = operation(draft, database.data);
|
|
152
|
+
validateDraft(draft.data, keys);
|
|
120
153
|
database.data = draft.data;
|
|
154
|
+
database.counters = draft.counters;
|
|
121
155
|
return result;
|
|
122
156
|
});
|
|
123
157
|
return { database, read, update };
|
|
124
158
|
};
|
|
125
|
-
/**
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return collection;
|
|
133
|
-
};
|
|
159
|
+
/** Создаёт дисковое хранилище или хранилище в памяти по настройкам.
|
|
160
|
+
* @example createDatabaseStore({ source: { notes: [] } }) → Promise<DatabaseStore>.
|
|
161
|
+
*/
|
|
162
|
+
export const createDatabaseStore = async (config, keys) => typeof config.source === 'string' ? createDiskDatabaseStore(config.source, keys) : createMemoryDatabaseStore(config.source, keys);
|
|
163
|
+
/** Ищет индекс записи, сравнивая строковые представления id.
|
|
164
|
+
* @example findItemIndex([{ id: 1 }], '1') → 0; пустой массив → -1.
|
|
165
|
+
*/
|
|
134
166
|
export const findItemIndex = (collection, id) => collection.findIndex((item) => String(item.id) === String(id));
|
|
167
|
+
/** Генерирует случайный строковый id, отсутствующий в переданной коллекции.
|
|
168
|
+
* @example createId([]) → строка из 11 символов; конкретное значение зависит от генератора случайных чисел.
|
|
169
|
+
*/
|
|
135
170
|
export const createId = (collection) => createUniqueId((id) => findItemIndex(collection, id) !== -1);
|
|
136
171
|
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../../src/core/database.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { DatabaseStore } from './database.js';
|
|
2
|
+
import type { ActorSource } from './lifecycle/options.js';
|
|
3
|
+
import { type Entity, type Model, type Node } from './model.js';
|
|
4
|
+
import type { MutationMode } from './operations.js';
|
|
5
|
+
import { type Predicate } from './query/filter.js';
|
|
6
|
+
import { type ListOptions } from './query/options.js';
|
|
7
|
+
import { type Context, type Ref } from './records.js';
|
|
8
|
+
import type { DatabaseData } from './types.js';
|
|
9
|
+
export interface PreparedList {
|
|
10
|
+
page: number;
|
|
11
|
+
pageSize: number;
|
|
12
|
+
predicate?: Predicate;
|
|
13
|
+
rules: Array<{
|
|
14
|
+
direction: 'ASC' | 'DESC';
|
|
15
|
+
keys: string[];
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
export interface Page {
|
|
19
|
+
data: Ref[];
|
|
20
|
+
total: number;
|
|
21
|
+
}
|
|
22
|
+
export declare class Engine {
|
|
23
|
+
readonly store: DatabaseStore;
|
|
24
|
+
model: Model;
|
|
25
|
+
readonly pageSize: number;
|
|
26
|
+
readonly maxPageSize: number;
|
|
27
|
+
private inferredModels;
|
|
28
|
+
constructor(store: DatabaseStore, model: Model, pageSize?: number, maxPageSize?: number);
|
|
29
|
+
/** Возвращает явную модель или кешированное описание, выведенное из переданного снимка данных.
|
|
30
|
+
* @example Повторный вызов с тем же объектом data → тот же объект Model.
|
|
31
|
+
*/
|
|
32
|
+
private modelFor;
|
|
33
|
+
/** Читает актуальный снимок хранилища и создаёт контекст с пустым кешем индексов.
|
|
34
|
+
* @example context() → Promise<Context>; при ошибке чтения хранилища → отклонённый Promise.
|
|
35
|
+
*/
|
|
36
|
+
context(): Promise<Context>;
|
|
37
|
+
/** Оборачивает все записи выбранной коллекции ссылками с контекстом.
|
|
38
|
+
* @example Пустая коллекция → []; две записи → две ссылки Ref.
|
|
39
|
+
*/
|
|
40
|
+
records(context: Context, entity: Entity): Ref[];
|
|
41
|
+
/** Ищет запись по строковому представлению первичного ключа, включая удалённые записи.
|
|
42
|
+
* @example Ключ '1' находит запись с числовым ключом 1; отсутствующий ключ → undefined.
|
|
43
|
+
*/
|
|
44
|
+
find(context: Context, entity: Entity, key: unknown): Ref | undefined;
|
|
45
|
+
/** Проверяет аргументы списка и подготавливает предикат, пути сортировки и размеры страницы.
|
|
46
|
+
* @example pager: { page: 2, pageSize: 5 } → план с page: 2 и pageSize: 5; page: 0 → ошибка.
|
|
47
|
+
*/
|
|
48
|
+
prepareOptions(node: Node, options?: ListOptions): PreparedList;
|
|
49
|
+
/** Фильтрует, сортирует и возвращает страницу, не меняя порядок исходного массива.
|
|
50
|
+
* @example Три подходящие записи, page: 2, pageSize: 2 → { data: [третья запись], total: 3 }.
|
|
51
|
+
*/
|
|
52
|
+
list(records: Ref[], node: Node, options?: ListOptions, prepared?: PreparedList): Page;
|
|
53
|
+
/** Проверяет коллекции, значения полей и целостность активных связей.
|
|
54
|
+
* @example Согласованные записи → undefined; обязательная связь без цели → исключение.
|
|
55
|
+
*/
|
|
56
|
+
validateData(data: DatabaseData): void;
|
|
57
|
+
/** Выполняет одну операцию записи в транзакции, включая связи, права и подготовку ответа.
|
|
58
|
+
* @example Режим update с { name: 'Анна' } → обновлённая запись; ошибка проверки → прежние данные.
|
|
59
|
+
*/
|
|
60
|
+
mutate<T = Ref>(entity: Entity, mode: MutationMode, key?: unknown, body?: unknown, prepare?: (ref: Ref) => T, actor?: ActorSource): Promise<T>;
|
|
61
|
+
/** Вычисляет каскад удаления, проверяет запрещающие связи и меняет только черновик данных.
|
|
62
|
+
* @example Удаление родителя при cascade → удалённые зависимые записи; restrict → исключение.
|
|
63
|
+
*/
|
|
64
|
+
private cascade;
|
|
65
|
+
}
|