@kollors/deep-json-server 1.0.0-alpha.1 → 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 +522 -179
- package/README.ru.md +525 -180
- 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 -6
- 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} +1 -0
- package/dist/src/{constants.js → core/constants.js} +1 -0
- 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} +36 -14
- 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/{model.js → core/model.js} +224 -33
- 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 +49 -32
- 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.js → graphql/schema.js} +57 -50
- 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 +7 -3
- package/dist/src/openapi/document.js +147 -92
- 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/{files/openapi.js → openapi/files.js} +6 -5
- 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 -10
- 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 +17 -1
- package/dist/src/cli.d.ts +0 -4
- package/dist/src/cli.js +0 -67
- package/dist/src/cli.js.map +0 -1
- package/dist/src/config.d.ts +0 -68
- package/dist/src/config.js +0 -161
- package/dist/src/config.js.map +0 -1
- package/dist/src/constants.js.map +0 -1
- package/dist/src/database.d.ts +0 -21
- package/dist/src/database.js.map +0 -1
- package/dist/src/engine.d.ts +0 -47
- package/dist/src/engine.js +0 -438
- package/dist/src/engine.js.map +0 -1
- package/dist/src/files/openapi.d.ts +0 -3
- package/dist/src/files/openapi.js.map +0 -1
- package/dist/src/graphql.d.ts +0 -4
- package/dist/src/graphql.js.map +0 -1
- package/dist/src/model.d.ts +0 -67
- package/dist/src/model.js.map +0 -1
- package/dist/src/openapi/index.d.ts +0 -7
- package/dist/src/openapi/index.js +0 -26
- package/dist/src/openapi/index.js.map +0 -1
- package/dist/src/query/filter.d.ts +0 -1
- package/dist/src/query/filter.js +0 -84
- package/dist/src/query/filter.js.map +0 -1
- package/dist/src/query/options.d.ts +0 -31
- package/dist/src/query/options.js +0 -135
- package/dist/src/query/options.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/server.d.ts +0 -14
- package/dist/src/server.js +0 -166
- package/dist/src/server.js.map +0 -1
- package/dist/src/utils.d.ts +0 -19
- package/dist/src/utils.js +0 -62
- package/dist/src/utils.js.map +0 -1
- /package/dist/src/{types.js → core/types.js} +0 -0
package/dist/src/files/index.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
1
2
|
import { createDiskFileStore } from './disk-store.js';
|
|
2
3
|
import { createMemoryFileStore } from './memory-store.js';
|
|
3
|
-
/**
|
|
4
|
-
|
|
4
|
+
/** Выбирает дисковое хранилище или хранилище в памяти по настройкам.
|
|
5
|
+
* @example createFileStore({ source: [] }) → Promise<FileStore> без создания файлов на диске.
|
|
6
|
+
*/
|
|
7
|
+
export const createFileStore = async (config, protectedPaths = []) => typeof config.source === 'string'
|
|
8
|
+
? createDiskFileStore({ directory: config.source, metadata: config.metadata ?? resolve(config.source, '.files.json'), protectedPaths })
|
|
9
|
+
: createMemoryFileStore(config.source);
|
|
5
10
|
export { registerFileRoutes } from './routes.js';
|
|
6
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/files/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/files/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,MAAmB,EAAE,cAAc,GAAa,EAAE,EAAsB,EAAE,CAC9G,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ;IAC/B,CAAC,CAAC,mBAAmB,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,cAAc,EAAE,CAAC;IACvI,CAAC,CAAC,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAE3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
import type { MemoryFile } from '
|
|
1
|
+
import type { MemoryFile } from './contract.js';
|
|
2
2
|
import { type FileStore } from './contract.js';
|
|
3
|
+
/** Копирует начальные файлы в память и создаёт операции чтения и изменения.
|
|
4
|
+
* @example createMemoryFileStore([]) → FileStore; последующая загрузка сохраняется только в памяти.
|
|
5
|
+
*/
|
|
3
6
|
export declare const createMemoryFileStore: (sourceFiles: MemoryFile[]) => FileStore;
|
|
@@ -1,32 +1,39 @@
|
|
|
1
1
|
import { Readable } from 'node:stream';
|
|
2
|
-
import {
|
|
2
|
+
import { pipeline } from 'node:stream/promises';
|
|
3
|
+
import { domainError } from '../core/errors.js';
|
|
4
|
+
import { createSerialQueue } from '../core/utils.js';
|
|
3
5
|
import { getFileKey, normalizeStoredFileMetadata } from './contract.js';
|
|
6
|
+
import { createSizeLimiter } from './streams.js';
|
|
7
|
+
/** Собирает поток в буфер, прекращая чтение при превышении лимита байтов.
|
|
8
|
+
* @example Поток из Buffer.from('abc') и лимит 3 → Buffer('abc'); лимит 2 → ошибка.
|
|
9
|
+
*/
|
|
4
10
|
const readUpload = async (stream, maxFileSize) => {
|
|
5
11
|
const chunks = [];
|
|
6
12
|
let size = 0;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
return Buffer.concat(chunks);
|
|
13
|
+
await pipeline(stream, createSizeLimiter(maxFileSize, (value) => {
|
|
14
|
+
size = value;
|
|
15
|
+
}), async (source) => {
|
|
16
|
+
for await (const chunk of source)
|
|
17
|
+
chunks.push(Buffer.from(chunk));
|
|
18
|
+
});
|
|
19
|
+
return Buffer.concat(chunks, size);
|
|
16
20
|
};
|
|
21
|
+
/** Копирует начальные файлы в память и создаёт операции чтения и изменения.
|
|
22
|
+
* @example createMemoryFileStore([]) → FileStore; последующая загрузка сохраняется только в памяти.
|
|
23
|
+
*/
|
|
17
24
|
export const createMemoryFileStore = (sourceFiles) => {
|
|
18
25
|
const storedFiles = new Map();
|
|
19
26
|
sourceFiles.forEach((sourceFile, index) => {
|
|
20
27
|
if (!(sourceFile.content instanceof Uint8Array)) {
|
|
21
|
-
throw new Error(`Некорректная запись ${index} в config.files.
|
|
28
|
+
throw new Error(`Некорректная запись ${index} в config.files.source`);
|
|
22
29
|
}
|
|
23
30
|
const file = {
|
|
24
|
-
...normalizeStoredFileMetadata(sourceFile, `Запись ${index} в config.files.
|
|
31
|
+
...normalizeStoredFileMetadata(sourceFile, `Запись ${index} в config.files.source`),
|
|
25
32
|
size: sourceFile.content.length,
|
|
26
33
|
};
|
|
27
34
|
const path = getFileKey(file);
|
|
28
35
|
if (storedFiles.has(path)) {
|
|
29
|
-
throw new Error(`config.files.
|
|
36
|
+
throw new Error(`config.files.source содержит повторяющийся путь «${path}»`);
|
|
30
37
|
}
|
|
31
38
|
storedFiles.set(path, { content: Buffer.from(sourceFile.content), file });
|
|
32
39
|
});
|
|
@@ -34,29 +41,29 @@ export const createMemoryFileStore = (sourceFiles) => {
|
|
|
34
41
|
const findStoredFile = (path) => {
|
|
35
42
|
const storedFile = storedFiles.get(path);
|
|
36
43
|
if (storedFile == null) {
|
|
37
|
-
throw
|
|
44
|
+
throw domainError('NOT_FOUND', 'Файл не найден');
|
|
38
45
|
}
|
|
39
46
|
return storedFile;
|
|
40
47
|
};
|
|
41
|
-
const metadata = async (path) => findStoredFile(path).file;
|
|
48
|
+
const metadata = async (path) => ({ ...findStoredFile(path).file });
|
|
42
49
|
const get = async (path) => {
|
|
43
50
|
const storedFile = findStoredFile(path);
|
|
44
|
-
return { file: storedFile.file, stream: Readable.from([storedFile.content]) };
|
|
51
|
+
return { file: { ...storedFile.file }, stream: Readable.from([Buffer.from(storedFile.content)]) };
|
|
45
52
|
};
|
|
46
53
|
const upload = async ({ directory, maxFileSize, mimeType, name, override, stream }) => {
|
|
47
54
|
const path = getFileKey({ directory, name });
|
|
48
55
|
if (storedFiles.has(path) && !override) {
|
|
49
|
-
throw
|
|
56
|
+
throw domainError('CONFLICT', 'Файл уже существует');
|
|
50
57
|
}
|
|
51
58
|
const content = await readUpload(stream, maxFileSize);
|
|
52
59
|
return schedule(() => {
|
|
53
60
|
const exists = storedFiles.has(path);
|
|
54
61
|
if (exists && !override) {
|
|
55
|
-
throw
|
|
62
|
+
throw domainError('CONFLICT', 'Файл уже существует');
|
|
56
63
|
}
|
|
57
64
|
const file = { directory, mimeType, name, size: content.length };
|
|
58
65
|
storedFiles.set(path, { content, file });
|
|
59
|
-
return { created: !exists, file };
|
|
66
|
+
return { created: !exists, file: { ...file } };
|
|
60
67
|
});
|
|
61
68
|
};
|
|
62
69
|
const update = (sourcePath, updates) => schedule(() => {
|
|
@@ -64,15 +71,15 @@ export const createMemoryFileStore = (sourceFiles) => {
|
|
|
64
71
|
const file = { ...storedFile.file, ...updates };
|
|
65
72
|
const targetPath = getFileKey(file);
|
|
66
73
|
if (sourcePath !== targetPath && storedFiles.has(targetPath)) {
|
|
67
|
-
throw
|
|
74
|
+
throw domainError('CONFLICT', 'Файл с таким путём уже существует');
|
|
68
75
|
}
|
|
69
76
|
storedFiles.delete(sourcePath);
|
|
70
77
|
storedFiles.set(targetPath, { ...storedFile, file });
|
|
71
|
-
return file;
|
|
78
|
+
return { ...file };
|
|
72
79
|
});
|
|
73
80
|
const remove = (path) => schedule(() => {
|
|
74
81
|
if (!storedFiles.delete(path)) {
|
|
75
|
-
throw
|
|
82
|
+
throw domainError('NOT_FOUND', 'Файл не найден');
|
|
76
83
|
}
|
|
77
84
|
});
|
|
78
85
|
return { get, metadata, remove, update, upload };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-store.js","sourceRoot":"","sources":["../../../src/files/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-store.js","sourceRoot":"","sources":["../../../src/files/memory-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { FastifyInstance } from 'fastify';
|
|
2
2
|
import { type FileStore } from './contract.js';
|
|
3
|
+
/** Регистрирует обработчики загрузки, чтения, перемещения и удаления файлов.
|
|
4
|
+
* @example После регистрации GET по существующему пути → поток файла; функция возвращает undefined.
|
|
5
|
+
*/
|
|
3
6
|
export declare const registerFileRoutes: (fastify: FastifyInstance, { getStore, maxFileSize }: {
|
|
4
7
|
getStore: () => Promise<FileStore>;
|
|
5
8
|
maxFileSize: number;
|
package/dist/src/files/routes.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { createHttpError } from '../
|
|
2
|
-
import {
|
|
1
|
+
import { createHttpError } from '../core/http-errors.js';
|
|
2
|
+
import { getFileKey, getPathLocation, normalizeMimeType, validateDirectory, validateName } from './contract.js';
|
|
3
|
+
import { createFileMetadata, FILE_HEADERS, FILE_ROUTES, FILE_UPDATE_SCHEMA, getDownloadName, PATCH_BODY_LIMIT } from './http.js';
|
|
4
|
+
/** Декодирует строку из URL-кодировки; отсутствие или повреждённая кодировка дают ошибку 400.
|
|
5
|
+
* @example decodeHeader('a%20b', 'Name') → 'a b'; decodeHeader('%', 'Name') → ошибка.
|
|
6
|
+
*/
|
|
3
7
|
const decodeHeader = (value, name) => {
|
|
4
8
|
if (typeof value !== 'string') {
|
|
5
9
|
throw createHttpError(400, `Заголовок ${name} обязателен`);
|
|
@@ -11,8 +15,17 @@ const decodeHeader = (value, name) => {
|
|
|
11
15
|
throw createHttpError(400, `Заголовок ${name} содержит некорректное значение`);
|
|
12
16
|
}
|
|
13
17
|
};
|
|
18
|
+
/** Декодирует и проверяет имя файла из заголовка.
|
|
19
|
+
* @example getContentName('a%20b.txt') → 'a b.txt'.
|
|
20
|
+
*/
|
|
14
21
|
const getContentName = (value) => validateName(decodeHeader(value, FILE_HEADERS.name.name), `Заголовок ${FILE_HEADERS.name.name}`);
|
|
22
|
+
/** Декодирует относительный каталог; отсутствующее значение заменяет пустой строкой.
|
|
23
|
+
* @example getContentDirectory(undefined) → ''; getContentDirectory('my%20photos') → 'my photos'.
|
|
24
|
+
*/
|
|
15
25
|
const getContentDirectory = (value) => (value == null ? '' : validateDirectory(decodeHeader(value, FILE_HEADERS.directory.name), `Заголовок ${FILE_HEADERS.directory.name}`));
|
|
26
|
+
/** Читает строковый логический флаг перезаписи; отсутствие означает false.
|
|
27
|
+
* @example getContentOverride('true') → true; getContentOverride(undefined) → false; 'yes' → ошибка.
|
|
28
|
+
*/
|
|
16
29
|
const getContentOverride = (value) => {
|
|
17
30
|
if (value == null || value === 'false') {
|
|
18
31
|
return false;
|
|
@@ -22,17 +35,29 @@ const getContentOverride = (value) => {
|
|
|
22
35
|
}
|
|
23
36
|
throw createHttpError(400, `Заголовок ${FILE_HEADERS.override.name} должен содержать true или false`);
|
|
24
37
|
};
|
|
38
|
+
/** Извлекает и проверяет путь из параметра маршрута.
|
|
39
|
+
* @example При request.params = { '*': 'photos/a.jpg' } → 'photos/a.jpg'.
|
|
40
|
+
*/
|
|
25
41
|
const getRequestPath = (request) => getFileKey(getPathLocation(request.params['*']));
|
|
42
|
+
/** Проверяет переданные имя и каталог и возвращает только заданные значения.
|
|
43
|
+
* @example normalizeUpdate({ name: 'a.txt' }) → { name: 'a.txt' }.
|
|
44
|
+
*/
|
|
26
45
|
const normalizeUpdate = (body) => ({
|
|
27
46
|
...(body.directory != null && { directory: validateDirectory(body.directory, 'Ключ body.directory') }),
|
|
28
47
|
...(body.name != null && { name: validateName(body.name, 'Ключ body.name') }),
|
|
29
48
|
});
|
|
49
|
+
/** Читает поток из хранилища и отправляет его с MIME-типом и заголовком скачивания или просмотра.
|
|
50
|
+
* @example При disposition = 'inline' ответ содержит Content-Disposition: inline и байты файла.
|
|
51
|
+
*/
|
|
30
52
|
const sendFile = async (store, path, reply, disposition) => {
|
|
31
53
|
const { file, stream } = await store.get(path);
|
|
32
54
|
reply.header('Content-Disposition', `${disposition}; filename*=UTF-8''${getDownloadName(file.name)}`);
|
|
33
55
|
reply.type(file.mimeType);
|
|
34
56
|
return reply.send(stream);
|
|
35
57
|
};
|
|
58
|
+
/** Регистрирует обработчики загрузки, чтения, перемещения и удаления файлов.
|
|
59
|
+
* @example После регистрации GET по существующему пути → поток файла; функция возвращает undefined.
|
|
60
|
+
*/
|
|
36
61
|
export const registerFileRoutes = (fastify, { getStore, maxFileSize }) => {
|
|
37
62
|
fastify.register(async (fileServer) => {
|
|
38
63
|
const store = await getStore();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/files/routes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/files/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
/** Создаёт преобразующий поток, который считает байты и отклоняет превышение лимита.
|
|
3
|
+
* @example Лимит 3 и поток 'abcd' → ошибка; поток 'abc' проходит без изменения.
|
|
4
|
+
*/
|
|
5
|
+
export declare const createSizeLimiter: (maxFileSize: number, onSize: (size: number) => void) => Transform;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
import { domainError } from '../core/errors.js';
|
|
3
|
+
/** Создаёт преобразующий поток, который считает байты и отклоняет превышение лимита.
|
|
4
|
+
* @example Лимит 3 и поток 'abcd' → ошибка; поток 'abc' проходит без изменения.
|
|
5
|
+
*/
|
|
6
|
+
export const createSizeLimiter = (maxFileSize, onSize) => {
|
|
7
|
+
let size = 0;
|
|
8
|
+
return new Transform({
|
|
9
|
+
transform(chunk, _encoding, callback) {
|
|
10
|
+
size += chunk.length;
|
|
11
|
+
if (size > maxFileSize) {
|
|
12
|
+
callback(domainError('PAYLOAD_TOO_LARGE', `Размер файла не должен превышать ${maxFileSize} байт`));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
onSize(size);
|
|
16
|
+
callback(null, chunk);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=streams.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streams.js","sourceRoot":"","sources":["../../../src/files/streams.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,WAAmB,EAAE,MAA8B,EAAa,EAAE;IAClG,IAAI,IAAI,GAAG,CAAC,CAAC;IAEb,OAAO,IAAI,SAAS,CAAC;QACnB,SAAS,CAAC,KAAa,EAAE,SAAyB,EAAE,QAA2B;YAC7E,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC;YAErB,IAAI,IAAI,GAAG,WAAW,EAAE,CAAC;gBACvB,QAAQ,CAAC,WAAW,CAAC,mBAAmB,EAAE,oCAAoC,WAAW,OAAO,CAAC,CAAC,CAAC;gBACnG,OAAO;YACT,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,CAAC;YACb,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACxB,CAAC;KACF,CAAC,CAAC;AACL,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type { EntityDefinition, Field, ModelSchema } from '../core/model.js';
|
|
2
|
+
import type { ModelSchema } from '../core/model.js';
|
|
3
|
+
import type { GraphqlOptions } from './generate.js';
|
|
4
|
+
/** Загружает генератор при первом обращении и передаёт ему описание и параметры.
|
|
5
|
+
* @example Описание и параметры → Promise со схемой; при импорте этой обёртки генератор ещё не загружается.
|
|
6
|
+
*/
|
|
7
|
+
export declare function generateGraphql(schema: ModelSchema | string, options?: GraphqlOptions): Promise<string>;
|
|
8
|
+
/** Загружает модуль записи по требованию и сохраняет схему в указанный файл.
|
|
9
|
+
* @example Схема и путь → Promise<void>; файл появляется после успешного завершения.
|
|
10
|
+
*/
|
|
11
|
+
export declare function writeGraphql(sdl: string, path: string): Promise<void>;
|
|
12
|
+
export type { GraphqlOptions } from './generate.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Загружает генератор при первом обращении и передаёт ему описание и параметры.
|
|
2
|
+
* @example Описание и параметры → Promise со схемой; при импорте этой обёртки генератор ещё не загружается.
|
|
3
|
+
*/
|
|
4
|
+
export async function generateGraphql(schema, options) {
|
|
5
|
+
return (await import('./generate.js')).generateGraphql(schema, options);
|
|
6
|
+
}
|
|
7
|
+
/** Загружает модуль записи по требованию и сохраняет схему в указанный файл.
|
|
8
|
+
* @example Схема и путь → Promise<void>; файл появляется после успешного завершения.
|
|
9
|
+
*/
|
|
10
|
+
export async function writeGraphql(sdl, path) {
|
|
11
|
+
return (await import('./write.js')).writeGraphql(sdl, path);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../src/graphql/entry.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAA4B,EAAE,OAAwB;IAC1F,OAAO,CAAC,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC1E,CAAC;AACD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,IAAY;IAC1D,OAAO,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface GraphqlOptions {
|
|
2
|
+
auth?: boolean;
|
|
3
|
+
}
|
|
4
|
+
import { type Model, type ModelSchema } from '../core/model.js';
|
|
5
|
+
/** Проверяет доступность формата и возвращает текст схемы из подготовленной модели.
|
|
6
|
+
* @example Подготовленная модель с сущностью Note → SDL с типом Note и операциями note, noteList.
|
|
7
|
+
*/
|
|
8
|
+
export declare function graphqlFromModel(model: Model | undefined): string;
|
|
9
|
+
/** Загружает описание из объекта или файла и возвращает текст схемы.
|
|
10
|
+
* @example Корректное описание сущностей → Promise<string> с SDL; некорректное → ошибка.
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateGraphql(schema: ModelSchema | string, options?: GraphqlOptions): Promise<string>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { assertKnownKeys } from '../core/utils.js';
|
|
2
|
+
import { printSchema } from 'graphql';
|
|
3
|
+
import { assertApi, loadModel } from '../core/model.js';
|
|
4
|
+
import { buildGraphql } from './schema.js';
|
|
5
|
+
/** Проверяет доступность формата и возвращает текст схемы из подготовленной модели.
|
|
6
|
+
* @example Подготовленная модель с сущностью Note → SDL с типом Note и операциями note, noteList.
|
|
7
|
+
*/
|
|
8
|
+
export function graphqlFromModel(model) {
|
|
9
|
+
assertApi(model, 'graphql');
|
|
10
|
+
return printSchema(buildGraphql(model));
|
|
11
|
+
}
|
|
12
|
+
/** Загружает описание из объекта или файла и возвращает текст схемы.
|
|
13
|
+
* @example Корректное описание сущностей → Promise<string> с SDL; некорректное → ошибка.
|
|
14
|
+
*/
|
|
15
|
+
export async function generateGraphql(schema, options = {}) {
|
|
16
|
+
assertKnownKeys(options, new Set(['auth']), 'options');
|
|
17
|
+
return graphqlFromModel(await loadModel(schema, { auth: options.auth, api: ['graphql'] }));
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/graphql/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAKnD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAgC,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAwB;IACvD,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAC5B,OAAO,WAAW,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1C,CAAC;AACD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAA4B,EAAE,OAAO,GAAmB,EAAE;IAC9F,eAAe,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACvD,OAAO,gBAAgB,CAAC,MAAM,SAAS,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7F,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type FieldNode, type GraphQLResolveInfo } from 'graphql';
|
|
2
|
+
import type { Engine, PreparedList } from '../core/engine.js';
|
|
3
|
+
/** Обходит выбранные поля, фрагменты и директивы и подготавливает аргументы списков до изменения данных.
|
|
4
|
+
* @example Запрос без списков → пустая Map; выбранный список с pageSize: 0 → ошибка.
|
|
5
|
+
*/
|
|
6
|
+
export declare function preflight(info: GraphQLResolveInfo, engine: Engine): Map<FieldNode, PreparedList>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { GraphQLIncludeDirective, GraphQLSkipDirective, getArgumentValues, getDirectiveValues, getNamedType, isObjectType, } from 'graphql';
|
|
2
|
+
/** Обходит выбранные поля, фрагменты и директивы и подготавливает аргументы списков до изменения данных.
|
|
3
|
+
* @example Запрос без списков → пустая Map; выбранный список с pageSize: 0 → ошибка.
|
|
4
|
+
*/
|
|
5
|
+
export function preflight(info, engine) {
|
|
6
|
+
const prepared = new Map();
|
|
7
|
+
const root = info.operation.operation === 'mutation' ? info.schema.getMutationType() : info.schema.getQueryType();
|
|
8
|
+
const visited = new Map();
|
|
9
|
+
const walk = (selectionSet, parent) => {
|
|
10
|
+
let parents = visited.get(selectionSet);
|
|
11
|
+
if (!parents) {
|
|
12
|
+
parents = new Set();
|
|
13
|
+
visited.set(selectionSet, parents);
|
|
14
|
+
}
|
|
15
|
+
if (parents.has(parent))
|
|
16
|
+
return;
|
|
17
|
+
parents.add(parent);
|
|
18
|
+
for (const selection of selectionSet.selections) {
|
|
19
|
+
if (getDirectiveValues(GraphQLSkipDirective, selection, info.variableValues)?.if === true || getDirectiveValues(GraphQLIncludeDirective, selection, info.variableValues)?.if === false)
|
|
20
|
+
continue;
|
|
21
|
+
if (selection.kind === 'FragmentSpread') {
|
|
22
|
+
walk(info.fragments[selection.name.value].selectionSet, parent);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (selection.kind === 'InlineFragment') {
|
|
26
|
+
walk(selection.selectionSet, parent);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const field = parent.getFields()[selection.name.value];
|
|
30
|
+
if (!field)
|
|
31
|
+
continue;
|
|
32
|
+
if (field.extensions.listNode && !prepared.has(selection))
|
|
33
|
+
prepared.set(selection, engine.prepareOptions(field.extensions.listNode, getArgumentValues(field, selection, info.variableValues)));
|
|
34
|
+
const type = getNamedType(field.type);
|
|
35
|
+
if (selection.selectionSet && isObjectType(type))
|
|
36
|
+
walk(selection.selectionSet, type);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
walk(info.operation.selectionSet, root);
|
|
40
|
+
return prepared;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../../src/graphql/preflight.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,uBAAuB,EAGvB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,YAAY,GAEb,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,IAAwB,EAAE,MAAc;IAChE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA2B,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,EAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAG,CAAC;IACpH,MAAM,OAAO,GAAG,IAAI,GAAG,EAA4C,CAAC;IACpE,MAAM,IAAI,GAAG,CAAC,YAA8B,EAAE,MAAyB,EAAQ,EAAE;QAC/E,IAAI,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACrC,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO;QAChC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpB,KAAK,MAAM,SAAS,IAAI,YAAY,CAAC,UAAU,EAAE,CAAC;YAChD,IAAI,kBAAkB,CAAC,oBAAoB,EAAE,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,EAAE,KAAK,IAAI,IAAI,kBAAkB,CAAC,uBAAuB,EAAE,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,EAAE,KAAK,KAAK;gBAAE,SAAS;YACjM,IAAI,SAAS,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;gBACxC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;gBAChE,SAAS;YACX,CAAC;YACD,IAAI,SAAS,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;gBACxC,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;gBACrC,SAAS;YACX,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvD,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,IAAI,KAAK,CAAC,UAAU,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC;gBACvD,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,UAAU,CAAC,QAAgB,EAAE,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;YAC9I,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACtC,IAAI,SAAS,CAAC,YAAY,IAAI,YAAY,CAAC,IAAI,CAAC;gBAAE,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QACvF,CAAC;IACH,CAAC,CAAC;IACF,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IACxC,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type FieldNode, type GraphQLResolveInfo, type GraphQLSchema } from 'graphql';
|
|
2
|
+
import type { Engine, PreparedList } from '../core/engine.js';
|
|
3
|
+
import type { Actor } from '../core/lifecycle/options.js';
|
|
4
|
+
import { type Context } from '../core/records.js';
|
|
5
|
+
export interface GraphqlContext {
|
|
6
|
+
actor?: () => Actor;
|
|
7
|
+
snapshot(): Promise<Context>;
|
|
8
|
+
prepare(info: GraphQLResolveInfo): Promise<Map<FieldNode, PreparedList>>;
|
|
9
|
+
}
|
|
10
|
+
/** Создаёт кеш снимка данных и проверки аргументов на время одного запроса.
|
|
11
|
+
* @example Повторные вызовы context.snapshot() возвращают тот же Promise<Context>.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createGraphqlContext(engine: Engine): GraphqlContext;
|
|
14
|
+
/** Назначает полям схемы обработчики чтения и изменения записей; изменяет переданную схему.
|
|
15
|
+
* @example После вызова поле запроса имеет resolve; возвращаемое значение → undefined.
|
|
16
|
+
*/
|
|
17
|
+
export declare function attachResolvers(schema: GraphQLSchema, engine: Engine): void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { isObjectType } from 'graphql';
|
|
2
|
+
import { resolveField } from '../core/records.js';
|
|
3
|
+
import { preflight } from './preflight.js';
|
|
4
|
+
/** Создаёт кеш снимка данных и проверки аргументов на время одного запроса.
|
|
5
|
+
* @example Повторные вызовы context.snapshot() возвращают тот же Promise<Context>.
|
|
6
|
+
*/
|
|
7
|
+
export function createGraphqlContext(engine) {
|
|
8
|
+
let snapshot;
|
|
9
|
+
let prepared;
|
|
10
|
+
return {
|
|
11
|
+
snapshot: () => (snapshot ??= engine.context()),
|
|
12
|
+
prepare: (info) => (prepared ??= Promise.resolve().then(() => preflight(info, engine))),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** Назначает полям схемы обработчики чтения и изменения записей; изменяет переданную схему.
|
|
16
|
+
* @example После вызова поле запроса имеет resolve; возвращаемое значение → undefined.
|
|
17
|
+
*/
|
|
18
|
+
export function attachResolvers(schema, engine) {
|
|
19
|
+
for (const type of Object.values(schema.getTypeMap())) {
|
|
20
|
+
if (!isObjectType(type))
|
|
21
|
+
continue;
|
|
22
|
+
for (const field of Object.values(type.getFields())) {
|
|
23
|
+
const { entity, operation, node, listNode } = field.extensions;
|
|
24
|
+
if (entity && operation)
|
|
25
|
+
field.resolve = async (_root, args, context, info) => {
|
|
26
|
+
const plans = await context.prepare(info);
|
|
27
|
+
if (operation === 'find')
|
|
28
|
+
return engine.find(await context.snapshot(), entity, args[entity.primary]) ?? null;
|
|
29
|
+
if (operation === 'list')
|
|
30
|
+
return engine.list(engine.records(await context.snapshot(), entity), entity.root, args, plans.get(info.fieldNodes[0]));
|
|
31
|
+
return engine.mutate(entity, operation, args[entity.primary], args.data ?? {}, undefined, context.actor);
|
|
32
|
+
};
|
|
33
|
+
else if (node)
|
|
34
|
+
field.resolve = async (ref, args, context, info) => {
|
|
35
|
+
const value = resolveField(ref, node);
|
|
36
|
+
if (!listNode || value == null)
|
|
37
|
+
return value;
|
|
38
|
+
const plans = await context.prepare(info);
|
|
39
|
+
return engine.list(value, listNode, args, plans.get(info.fieldNodes[0]));
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=resolvers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolvers.js","sourceRoot":"","sources":["../../../src/graphql/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+D,YAAY,EAAE,MAAM,SAAS,CAAC;AAMpG,OAAO,EAA0B,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAO3C;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,IAAI,QAAsC,CAAC;IAC3C,IAAI,QAA2D,CAAC;IAChE,OAAO;QACL,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC;QAC/C,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;KACxF,CAAC;AACJ,CAAC;AACD;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAqB,EAAE,MAAc;IACnE,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC;QACtD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC;YACpD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,UAA2G,CAAC;YAChK,IAAI,MAAM,IAAI,SAAS;gBACrB,KAAK,CAAC,OAAO,GAAG,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAuB,EAAE,IAAI,EAAE,EAAE;oBACnE,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;oBAC1C,IAAI,SAAS,KAAK,MAAM;wBAAE,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC;oBAC7G,IAAI,SAAS,KAAK,MAAM;wBAAE,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,OAAO,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBACjJ,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC3G,CAAC,CAAC;iBACC,IAAI,IAAI;gBACX,KAAK,CAAC,OAAO,GAAG,KAAK,EAAE,GAAQ,EAAE,IAAiB,EAAE,OAAuB,EAAE,IAAI,EAAE,EAAE;oBACnF,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;oBACtC,IAAI,CAAC,QAAQ,IAAI,KAAK,IAAI,IAAI;wBAAE,OAAO,KAAK,CAAC;oBAC7C,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;oBAC1C,OAAO,MAAM,CAAC,IAAI,CAAC,KAAc,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACpF,CAAC,CAAC;QACN,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type { GraphQLSchema } from 'graphql';
|
|
3
|
+
import type { Engine } from '../core/engine.js';
|
|
4
|
+
import type { Authenticate } from '../core/lifecycle/options.js';
|
|
5
|
+
/** Регистрирует обработчик GraphQL, контекст запроса и преобразование ошибок.
|
|
6
|
+
* @example После регистрации POST на заданный path выполняет запрос; прикладная ошибка попадает в errors[].extensions.code.
|
|
7
|
+
*/
|
|
8
|
+
export declare function registerGraphqlRoutes(server: FastifyInstance, schema: GraphQLSchema, engine: Engine, path: string, authenticate?: Authenticate): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import mercurius from 'mercurius';
|
|
2
|
+
import { DomainError } from '../core/errors.js';
|
|
3
|
+
import { attachResolvers, createGraphqlContext } from './resolvers.js';
|
|
4
|
+
/** Регистрирует обработчик GraphQL, контекст запроса и преобразование ошибок.
|
|
5
|
+
* @example После регистрации POST на заданный path выполняет запрос; прикладная ошибка попадает в errors[].extensions.code.
|
|
6
|
+
*/
|
|
7
|
+
export function registerGraphqlRoutes(server, schema, engine, path, authenticate) {
|
|
8
|
+
attachResolvers(schema, engine);
|
|
9
|
+
server.register(mercurius, {
|
|
10
|
+
schema,
|
|
11
|
+
path,
|
|
12
|
+
queryDepth: 32,
|
|
13
|
+
context: (request) => ({ ...createGraphqlContext(engine), ...(authenticate ? { actor: () => authenticate(request.headers.authorization) } : {}) }),
|
|
14
|
+
errorFormatter: (execution, context) => {
|
|
15
|
+
const formatted = mercurius.defaultErrorFormatter(execution, context);
|
|
16
|
+
formatted.response.errors = execution.errors.map((error) => {
|
|
17
|
+
const original = error.originalError;
|
|
18
|
+
if (original instanceof DomainError)
|
|
19
|
+
return { ...error.toJSON(), extensions: { ...error.extensions, code: original.code } };
|
|
20
|
+
if (original && !('errors' in original) && !(original instanceof Error && original.name === 'GraphQLError')) {
|
|
21
|
+
context.reply?.request.log.error(original);
|
|
22
|
+
return { message: 'Internal server error', locations: error.locations, path: error.path, extensions: { code: 'INTERNAL_ERROR' } };
|
|
23
|
+
}
|
|
24
|
+
return error.toJSON();
|
|
25
|
+
});
|
|
26
|
+
return formatted;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/graphql/routes.ts"],"names":[],"mappings":"AAEA,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACvE;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAuB,EAAE,MAAqB,EAAE,MAAc,EAAE,IAAY,EAAE,YAA2B;IAC7I,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE;QACzB,MAAM;QACN,IAAI;QACJ,UAAU,EAAE,EAAE;QACd,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,oBAAoB,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAClJ,cAAc,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE;YACrC,MAAM,SAAS,GAAG,SAAS,CAAC,qBAAqB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YACtE,SAAS,CAAC,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;gBACzD,MAAM,QAAQ,GAAG,KAAK,CAAC,aAAa,CAAC;gBACrC,IAAI,QAAQ,YAAY,WAAW;oBAAE,OAAO,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,EAAE,GAAG,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC5H,IAAI,QAAQ,IAAI,CAAC,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,YAAY,KAAK,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,EAAE,CAAC;oBAC5G,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBAC3C,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,CAAC;gBACpI,CAAC;gBACD,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC;YACxB,CAAC,CAAC,CAAC;YACH,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { GraphQLSchema } from 'graphql';
|
|
2
|
+
import { type Model } from '../core/model.js';
|
|
3
|
+
/** Строит типы, фильтры, аргументы и операции по модели, проверяя конфликты имён.
|
|
4
|
+
* @example Модель с сущностью Note → GraphQLSchema с типом Note; повторяющееся имя типа → ошибка.
|
|
5
|
+
*/
|
|
6
|
+
export declare function buildGraphql(model: Model): GraphQLSchema;
|