@kollors/deep-json-server 0.5.0 → 0.7.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/src/server.js CHANGED
@@ -1,20 +1,25 @@
1
1
  import Fastify from 'fastify';
2
- import { DEFAULT_HOST, DEFAULT_MAX_FILE_SIZE, DEFAULT_PORT, MAX_PAGE_SIZE } from './constants.js';
2
+ import { normalizeServerConfig } from './config.js';
3
+ import { DEFAULT_HOST, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_PAGE_SIZE, DEFAULT_PORT } from './constants.js';
3
4
  import { createDatabaseStore, createId, findItem, getCollection } from './database.js';
4
- import { registerFileRoutes } from './files.js';
5
- import { readSchemaConfig } from './openapi/config.js';
6
- import { createOpenApiDocument } from './openapi/index.js';
5
+ import { createFileStore, registerFileRoutes } from './files.js';
6
+ import { resolveSchemaConfig } from './openapi/config.js';
7
+ import { buildOpenapiDocument } from './openapi/document.js';
8
+ import { createOpenapi, writeOpenapi } from './openapi/index.js';
7
9
  import { matchesWhere, paginateItems, parsePagination, parseWhere, sortItems, validateWhere } from './query/index.js';
8
10
  import { embedItem, parseEmbedPaths, validateEmbedPaths } from './relations.js';
9
- import { createHttpError, getResourceNames, isObject, resolveDatabasePath } from './utils.js';
11
+ import { createHttpError, getResourceNames, isObject } from './utils.js';
12
+
13
+ /** @typedef {Record<string, unknown>} OpenapiDocument */
14
+ /** @typedef {{ fastify: () => import('fastify').FastifyInstance, openapi: () => Promise<OpenapiDocument> }} ServerFacade */
10
15
 
11
16
  const CORS_HEADERS = {
12
- 'Access-Control-Allow-Headers': 'Content-Name, Content-Type',
17
+ 'Access-Control-Allow-Headers': 'Content-Directory, Content-Name, Content-Override, Content-Type',
13
18
  'Access-Control-Allow-Methods': 'DELETE, GET, OPTIONS, PATCH, POST, PUT',
14
19
  'Access-Control-Allow-Origin': '*',
15
20
  };
16
21
 
17
- const getRequestBody = (body) => {
22
+ const getJsonObjectBody = (body) => {
18
23
  if (!isObject(body)) {
19
24
  throw createHttpError(400, 'Тело запроса должно быть JSON-объектом');
20
25
  }
@@ -24,7 +29,7 @@ const getRequestBody = (body) => {
24
29
 
25
30
  const getSchemaName = (reference) => reference.split('/').at(-1);
26
31
 
27
- const addRequestSchemas = (server, document, resources) => {
32
+ const addRequestSchemas = (fastify, document, resources) => {
28
33
  const requestSchemaNames = new Set(
29
34
  resources.flatMap((resource) => {
30
35
  const resourcePath = document.paths[`/${resource}`];
@@ -35,17 +40,17 @@ const addRequestSchemas = (server, document, resources) => {
35
40
  );
36
41
 
37
42
  requestSchemaNames.forEach((schemaName) => {
38
- server.addSchema({ $id: schemaName, ...document.components.schemas[schemaName] });
43
+ fastify.addSchema({ $id: schemaName, ...document.components.schemas[schemaName] });
39
44
  });
40
45
  };
41
46
 
42
- const registerResourceRoutes = (server, store, resource, document, maxPageSize) => {
47
+ const registerResourceRoutes = (fastify, store, resource, document, maxPageSize) => {
43
48
  const resourcePath = `/${resource}`;
44
49
  const itemPath = `/${resource}/:id`;
45
50
  const createSchemaName = getSchemaName(document.paths[resourcePath].post.requestBody.content['application/json'].schema.$ref);
46
51
  const updateSchemaName = getSchemaName(document.paths[`/${resource}/{id}`].patch.requestBody.content['application/json'].schema.$ref);
47
52
 
48
- server.get(resourcePath, async (request) => {
53
+ fastify.get(resourcePath, async (request) => {
49
54
  const collection = getCollection(store.database, resource);
50
55
  const where = parseWhere(request.query);
51
56
  const embedPaths = parseEmbedPaths(request.query._embed);
@@ -64,7 +69,7 @@ const registerResourceRoutes = (server, store, resource, document, maxPageSize)
64
69
  return paginateItems(sortedItems, pagination.page, pagination.pageSize);
65
70
  });
66
71
 
67
- server.get(itemPath, async (request) => {
72
+ fastify.get(itemPath, async (request) => {
68
73
  const collection = getCollection(store.database, resource);
69
74
  const item = findItem(collection, request.params.id);
70
75
  const embedPaths = parseEmbedPaths(request.query._embed);
@@ -78,10 +83,10 @@ const registerResourceRoutes = (server, store, resource, document, maxPageSize)
78
83
  return embedItem(store.database, item, resource, embedPaths);
79
84
  });
80
85
 
81
- server.post(resourcePath, { schema: { body: { $ref: `${createSchemaName}#` } } }, async (request, reply) => {
86
+ fastify.post(resourcePath, { schema: { body: { $ref: `${createSchemaName}#` } } }, async (request, reply) => {
82
87
  const item = await store.update((database) => {
83
88
  const collection = getCollection(database, resource);
84
- const createdItem = { ...getRequestBody(request.body), id: createId(collection) };
89
+ const createdItem = { ...getJsonObjectBody(request.body), id: createId(collection) };
85
90
 
86
91
  collection.push(createdItem);
87
92
 
@@ -91,7 +96,7 @@ const registerResourceRoutes = (server, store, resource, document, maxPageSize)
91
96
  return reply.code(201).send(item);
92
97
  });
93
98
 
94
- server.put(itemPath, { schema: { body: { $ref: `${createSchemaName}#` } } }, async (request) =>
99
+ fastify.put(itemPath, { schema: { body: { $ref: `${createSchemaName}#` } } }, async (request) =>
95
100
  store.update((database) => {
96
101
  const collection = getCollection(database, resource);
97
102
  const currentItem = findItem(collection, request.params.id);
@@ -100,7 +105,7 @@ const registerResourceRoutes = (server, store, resource, document, maxPageSize)
100
105
  throw createHttpError(404, 'Запись не найдена');
101
106
  }
102
107
 
103
- const item = { ...getRequestBody(request.body), id: currentItem.id };
108
+ const item = { ...getJsonObjectBody(request.body), id: currentItem.id };
104
109
 
105
110
  collection.splice(collection.indexOf(currentItem), 1, item);
106
111
 
@@ -108,7 +113,7 @@ const registerResourceRoutes = (server, store, resource, document, maxPageSize)
108
113
  }),
109
114
  );
110
115
 
111
- server.patch(itemPath, { schema: { body: { $ref: `${updateSchemaName}#` } } }, async (request) =>
116
+ fastify.patch(itemPath, { schema: { body: { $ref: `${updateSchemaName}#` } } }, async (request) =>
112
117
  store.update((database) => {
113
118
  const collection = getCollection(database, resource);
114
119
  const currentItem = findItem(collection, request.params.id);
@@ -117,7 +122,7 @@ const registerResourceRoutes = (server, store, resource, document, maxPageSize)
117
122
  throw createHttpError(404, 'Запись не найдена');
118
123
  }
119
124
 
120
- const item = { ...currentItem, ...getRequestBody(request.body), id: currentItem.id };
125
+ const item = { ...currentItem, ...getJsonObjectBody(request.body), id: currentItem.id };
121
126
 
122
127
  collection.splice(collection.indexOf(currentItem), 1, item);
123
128
 
@@ -125,7 +130,7 @@ const registerResourceRoutes = (server, store, resource, document, maxPageSize)
125
130
  }),
126
131
  );
127
132
 
128
- server.delete(itemPath, async (request) =>
133
+ fastify.delete(itemPath, async (request) =>
129
134
  store.update((database) => {
130
135
  const collection = getCollection(database, resource);
131
136
  const currentItem = findItem(collection, request.params.id);
@@ -142,97 +147,104 @@ const registerResourceRoutes = (server, store, resource, document, maxPageSize)
142
147
  };
143
148
 
144
149
  /**
145
- * Creates a Fastify server without opening a network port.
146
- * @param {{ databasePath: string, filesDirectoryPath?: string, filesMetadataPath?: string, logger?: boolean | Record<string, unknown>, maxFileSize?: number, maxPageSize?: number, schemaPath?: string }} options Server options.
147
- * @returns {Promise<import('fastify').FastifyInstance>} Fastify server.
150
+ * Creates lazy Fastify and OpenAPI accessors from one configuration.
151
+ * @param {import('./config.js').DeepJsonServerConfig} config Server configuration.
152
+ * @param {{ files?: boolean }} [features] Optional feature switches.
153
+ * @returns {Promise<ServerFacade>} Server facade.
148
154
  */
149
- export async function createServer(options) {
150
- const { databasePath, filesDirectoryPath, filesMetadataPath, logger = true, maxFileSize = DEFAULT_MAX_FILE_SIZE, maxPageSize = MAX_PAGE_SIZE, schemaPath } = options ?? {};
151
-
152
- if (!Number.isInteger(maxPageSize) || maxPageSize < 1) {
153
- throw new Error('Максимальный размер страницы должен быть положительным целым числом');
155
+ export async function createServer(config, features = {}) {
156
+ const normalizedConfig = normalizeServerConfig(config);
157
+ const filesEnabled = features.files ?? normalizedConfig.files != null;
158
+ const { logger = true, maxFileSize = DEFAULT_MAX_FILE_SIZE, maxPageSize = DEFAULT_MAX_PAGE_SIZE } = normalizedConfig.server;
159
+ const store = await createDatabaseStore(normalizedConfig.database);
160
+ const schema = await resolveSchemaConfig(normalizedConfig.database.schema);
161
+ const fileStore = filesEnabled && normalizedConfig.files != null ? await createFileStore(normalizedConfig.files) : undefined;
162
+ let fastifyInstance;
163
+
164
+ if (filesEnabled && normalizedConfig.files == null) {
165
+ throw new Error('Для файловых маршрутов укажите секцию config.files');
154
166
  }
155
167
 
156
- if (!Number.isInteger(maxFileSize) || maxFileSize < 1) {
157
- throw new Error('Максимальный размер файла должен быть положительным целым числом');
158
- }
168
+ const buildDocument = () =>
169
+ buildOpenapiDocument({
170
+ database: store.database.data,
171
+ files: filesEnabled,
172
+ maxPageSize,
173
+ schema,
174
+ });
175
+
176
+ const getFastify = () => {
177
+ if (fastifyInstance != null) {
178
+ return fastifyInstance;
179
+ }
180
+
181
+ const document = buildDocument();
182
+ const resources = getResourceNames(store.database.data);
183
+ const fastify = Fastify({ logger });
184
+ const originalListen = fastify.listen.bind(fastify);
159
185
 
160
- const store = await createDatabaseStore(databasePath);
161
- const schemaConfig = await readSchemaConfig(schemaPath);
162
- const document = createOpenApiDocument(store.database.data, schemaConfig);
163
- const resources = getResourceNames(store.database.data);
164
- const server = Fastify({ logger });
186
+ // Calling fastify().listen() without arguments uses config defaults.
187
+ fastify.listen = (...args) => originalListen(...(args.length === 0 ? [{ host: normalizedConfig.server.host ?? DEFAULT_HOST, port: normalizedConfig.server.port ?? DEFAULT_PORT }] : args));
165
188
 
166
- addRequestSchemas(server, document, resources);
189
+ addRequestSchemas(fastify, document, resources);
167
190
 
168
- server.addHook('onRequest', async (_request, reply) => {
169
- Object.entries(CORS_HEADERS).forEach(([header, value]) => {
170
- reply.header(header, value);
191
+ fastify.addHook('onRequest', async (_request, reply) => {
192
+ Object.entries(CORS_HEADERS).forEach(([header, value]) => {
193
+ reply.header(header, value);
194
+ });
171
195
  });
172
- });
173
196
 
174
- server.addHook('preHandler', async (request) => {
175
- if (request.method === 'GET') {
176
- await store.read();
177
- }
178
- });
197
+ fastify.addHook('preHandler', async (request) => {
198
+ if (request.method === 'GET') {
199
+ await store.read();
200
+ }
201
+ });
179
202
 
180
- server.options('/', async (_request, reply) => reply.code(204).send());
181
- server.options('/*', async (_request, reply) => reply.code(204).send());
182
- server.get('/', async () => ({ resources }));
203
+ fastify.options('/', async (_request, reply) => reply.code(204).send());
204
+ fastify.options('/*', async (_request, reply) => reply.code(204).send());
205
+ fastify.get('/', async () => ({ resources }));
183
206
 
184
- resources.forEach((resource) => {
185
- registerResourceRoutes(server, store, resource, document, maxPageSize);
186
- });
207
+ resources.forEach((resource) => {
208
+ registerResourceRoutes(fastify, store, resource, document, maxPageSize);
209
+ });
187
210
 
188
- if ((filesDirectoryPath == null) !== (filesMetadataPath == null)) {
189
- throw new Error('Для файловых маршрутов укажите filesDirectoryPath и filesMetadataPath');
190
- }
211
+ if (fileStore != null) {
212
+ registerFileRoutes(fastify, { maxFileSize, store: fileStore });
213
+ }
191
214
 
192
- if (filesDirectoryPath != null) {
193
- await registerFileRoutes(server, { directoryPath: filesDirectoryPath, maxFileSize, metadataPath: filesMetadataPath });
194
- }
215
+ fastify.setErrorHandler((error, request, reply) => {
216
+ const statusCode = Number.isInteger(error.statusCode) ? error.statusCode : 500;
195
217
 
196
- server.setErrorHandler((error, request, reply) => {
197
- const statusCode = Number.isInteger(error.statusCode) ? error.statusCode : 500;
218
+ if (statusCode === 500) {
219
+ request.log.error(error);
220
+ }
198
221
 
199
- if (statusCode === 500) {
200
- request.log.error(error);
201
- }
222
+ return reply.code(statusCode).send({ error: error.message });
223
+ });
202
224
 
203
- return reply.code(statusCode).send({ error: error.message });
204
- });
225
+ fastifyInstance = fastify;
205
226
 
206
- return server;
207
- }
227
+ return fastifyInstance;
228
+ };
208
229
 
209
- /**
210
- * Creates and starts a Fastify server.
211
- * @param {{ databasePath: string, filesDirectoryPath?: string, filesMetadataPath?: string, host?: string, logger?: boolean | Record<string, unknown>, maxFileSize?: number, maxPageSize?: number, port?: number, schemaPath?: string }} options Server options.
212
- * @returns {Promise<import('fastify').FastifyInstance>} Listening Fastify server.
213
- */
214
- export async function startServer(options) {
215
- const {
216
- databasePath,
217
- filesDirectoryPath,
218
- filesMetadataPath,
219
- host = DEFAULT_HOST,
220
- logger = true,
221
- maxFileSize = DEFAULT_MAX_FILE_SIZE,
222
- maxPageSize = MAX_PAGE_SIZE,
223
- port = DEFAULT_PORT,
224
- schemaPath,
225
- } = options ?? {};
226
-
227
- if (!Number.isInteger(port) || port < 0 || port > 65_535) {
228
- throw new Error('Порт должен быть целым числом от 0 до 65535');
229
- }
230
+ const openapi = async () => {
231
+ await store.read();
230
232
 
231
- const resolvedDatabasePath = resolveDatabasePath(databasePath);
232
- const server = await createServer({ databasePath: resolvedDatabasePath, filesDirectoryPath, filesMetadataPath, logger, maxFileSize, maxPageSize, schemaPath });
233
+ const document = createOpenapi({
234
+ database: store.database.data,
235
+ files: filesEnabled,
236
+ host: normalizedConfig.server.host,
237
+ maxPageSize,
238
+ port: normalizedConfig.server.port,
239
+ schema,
240
+ });
241
+
242
+ if (normalizedConfig.openapi.path != null) {
243
+ await writeOpenapi(document, normalizedConfig.openapi.path);
244
+ }
233
245
 
234
- await server.listen({ host, port });
235
- server.log.info({ database: resolvedDatabasePath }, 'Deep JSON Server запущен');
246
+ return document;
247
+ };
236
248
 
237
- return server;
249
+ return { fastify: getFastify, openapi };
238
250
  }
package/src/utils.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { randomBytes } from 'node:crypto';
1
2
  import { resolve } from 'node:path';
2
3
  import pluralize from 'pluralize';
3
4
 
@@ -11,6 +12,35 @@ export const createHttpError = (statusCode, message) => {
11
12
  return error;
12
13
  };
13
14
 
15
+ /** @returns {<T>(operation: () => T | Promise<T>) => Promise<T>} Serialized operation scheduler. */
16
+ export const createSerialQueue = () => {
17
+ let queue = Promise.resolve();
18
+
19
+ return (operation) => {
20
+ const pendingOperation = queue.then(operation);
21
+
22
+ // Keep the queue usable after a failed operation.
23
+ queue = pendingOperation.catch(() => undefined);
24
+
25
+ return pendingOperation;
26
+ };
27
+ };
28
+
29
+ /**
30
+ * Creates a random ID that is not currently in use.
31
+ * @param {(id: string) => boolean} isUsed Checks whether an ID already exists.
32
+ * @returns {string} Unique ID.
33
+ */
34
+ export const createUniqueId = (isUsed) => {
35
+ let id;
36
+
37
+ do {
38
+ id = randomBytes(8).toString('base64url');
39
+ } while (isUsed(id));
40
+
41
+ return id;
42
+ };
43
+
14
44
  export const getResourceNames = (data) => Object.keys(data);
15
45
  export const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
16
46
  export const isSafeKey = (key) => !UNSAFE_KEYS.has(key);
package/types/index.d.ts CHANGED
@@ -1,2 +1,21 @@
1
- export { createOpenApiDocument, generateOpenApi } from './src/openapi/index.js';
2
- export { createServer, startServer } from './src/server.js';
1
+ /** @typedef {import('./src/config.js').DeepJsonServerConfig} DeepJsonServerConfig */
2
+ /** @typedef {import('./src/config.js').DatabaseConfig} DatabaseConfig */
3
+ /** @typedef {import('./src/config.js').FilesConfig} FilesConfig */
4
+ /** @typedef {import('./src/config.js').MemoryFile} MemoryFile */
5
+ /** @typedef {import('./src/config.js').OpenapiConfig} OpenapiConfig */
6
+ /** @typedef {import('./src/config.js').ServerConfig} ServerConfig */
7
+ /** @typedef {import('./src/files.js').FileMetadata} FileMetadata */
8
+ /** @typedef {import('./src/files.js').FileUpdate} FileUpdate */
9
+ /** @typedef {import('./src/server.js').OpenapiDocument} OpenapiDocument */
10
+ /** @typedef {import('./src/server.js').ServerFacade} ServerFacade */
11
+ export type DeepJsonServerConfig = import('./src/config.js').DeepJsonServerConfig;
12
+ export type DatabaseConfig = import('./src/config.js').DatabaseConfig;
13
+ export type FilesConfig = import('./src/config.js').FilesConfig;
14
+ export type MemoryFile = import('./src/config.js').MemoryFile;
15
+ export type OpenapiConfig = import('./src/config.js').OpenapiConfig;
16
+ export type ServerConfig = import('./src/config.js').ServerConfig;
17
+ export type FileMetadata = import('./src/files.js').FileMetadata;
18
+ export type FileUpdate = import('./src/files.js').FileUpdate;
19
+ export type OpenapiDocument = import('./src/server.js').OpenapiDocument;
20
+ export type ServerFacade = import('./src/server.js').ServerFacade;
21
+ export { createServer } from './src/server.js';
@@ -0,0 +1,73 @@
1
+ export type DatabaseData = Record<string, Array<Record<string, unknown>>>;
2
+ export type DatabaseSchema = Record<string, unknown>;
3
+ export type DatabaseConfig = {
4
+ data: DatabaseData;
5
+ path?: never;
6
+ schema?: DatabaseSchema | string;
7
+ } | {
8
+ data?: never;
9
+ path: string;
10
+ schema?: DatabaseSchema | string;
11
+ };
12
+ export type MemoryFile = {
13
+ content: Uint8Array;
14
+ directory?: string;
15
+ mimeType: string;
16
+ name: string;
17
+ };
18
+ export type FilesConfig = {
19
+ data: MemoryFile[];
20
+ directory?: never;
21
+ metadata?: never;
22
+ } | {
23
+ data?: never;
24
+ directory: string;
25
+ metadata: string;
26
+ };
27
+ export type OpenapiConfig = {
28
+ path?: string;
29
+ };
30
+ export type ServerConfig = {
31
+ host?: string;
32
+ logger?: boolean | Record<string, unknown>;
33
+ maxFileSize?: number;
34
+ maxPageSize?: number;
35
+ port?: number;
36
+ };
37
+ export type DeepJsonServerConfig = {
38
+ /**
39
+ * Database source and optional schema.
40
+ */
41
+ database: DatabaseConfig;
42
+ /**
43
+ * Binary-file storage.
44
+ */
45
+ files?: FilesConfig;
46
+ /**
47
+ * Generated OpenAPI file.
48
+ */
49
+ openapi?: OpenapiConfig;
50
+ /**
51
+ * Runtime settings.
52
+ */
53
+ server?: ServerConfig;
54
+ };
55
+ export type NormalizedServerConfig = {
56
+ database: DatabaseConfig;
57
+ files?: FilesConfig;
58
+ openapi: OpenapiConfig;
59
+ server: ServerConfig;
60
+ };
61
+ /**
62
+ * Validates configuration and resolves relative paths.
63
+ * @param {DeepJsonServerConfig} config Server configuration.
64
+ * @param {string} [directoryPath] Base directory for relative paths.
65
+ * @returns {NormalizedServerConfig} Normalized configuration.
66
+ */
67
+ export declare const normalizeServerConfig: (config: DeepJsonServerConfig, directoryPath?: string) => NormalizedServerConfig;
68
+ /**
69
+ * Loads an ES module config and resolves paths from its directory.
70
+ * @param {string} configPath Configuration module path.
71
+ * @returns {Promise<NormalizedServerConfig>} Normalized configuration.
72
+ */
73
+ export declare function readServerConfig(configPath: string): Promise<NormalizedServerConfig>;
@@ -1,5 +1,5 @@
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 = 1000;
3
4
  export declare const DEFAULT_PAGE_SIZE = 10;
4
5
  export declare const DEFAULT_PORT = 4001;
5
- export declare const MAX_PAGE_SIZE = 1000;
@@ -1,8 +1,33 @@
1
+ export type DatabaseContainer = {
2
+ data: import('./config.js').DatabaseData;
3
+ };
4
+ export type DatabaseStore = {
5
+ /**
6
+ * Current database container.
7
+ */
8
+ database: DatabaseContainer;
9
+ /**
10
+ * Resolved database file path.
11
+ */
12
+ path?: string;
13
+ /**
14
+ * Returns current data and reloads disk-backed sources.
15
+ */
16
+ read: () => Promise<import('./config.js').DatabaseData>;
17
+ /**
18
+ * Runs a serialized update.
19
+ */
20
+ update: <T>(operation: (database: DatabaseContainer) => T) => Promise<T>;
21
+ };
1
22
  export declare const validateDatabase: (data: any) => any;
2
23
  export declare const readJsonObject: (path: any, label: any) => Promise<any>;
3
24
  export declare const readDatabaseFile: (databasePath: any) => Promise<any>;
4
- /** @returns {Promise<any>} Internal LowDB-backed store. */
5
- export declare const createDatabaseStore: (databasePath: any) => Promise<any>;
25
+ /**
26
+ * Creates a disk- or memory-backed database with serialized updates.
27
+ * @param {import('./config.js').DatabaseConfig} config Database source.
28
+ * @returns {Promise<DatabaseStore>} Database store.
29
+ */
30
+ export declare const createDatabaseStore: (config: import('./config.js').DatabaseConfig) => Promise<DatabaseStore>;
6
31
  export declare const getCollection: (database: any, resource: any) => any[];
7
32
  export declare const findItem: (collection: any, id: any) => any;
8
- export declare const createId: (collection: any) => any;
33
+ export declare const createId: (collection: any) => string;
@@ -1,5 +1,64 @@
1
- export declare const registerFileRoutes: (server: any, { directoryPath, maxFileSize, metadataPath }: {
2
- directoryPath: any;
1
+ import { Readable } from 'node:stream';
2
+ export type StoredFileMetadata = {
3
+ directory: string;
4
+ mimeType: string;
5
+ name: string;
6
+ };
7
+ export type FileRecord = StoredFileMetadata & {
8
+ size: number;
9
+ };
10
+ export type FileMetadata = FileRecord & {
11
+ downloadUrl: string;
12
+ metadataUrl: string;
13
+ url: string;
14
+ };
15
+ export type FileUpload = {
16
+ directory: string;
17
+ maxFileSize: number;
18
+ mimeType: string;
19
+ name: string;
20
+ override: boolean;
21
+ stream: Readable;
22
+ };
23
+ export type FileUpdate = {
24
+ directory?: string;
25
+ name?: string;
26
+ };
27
+ export type FileStore = {
28
+ /**
29
+ * Returns file metadata and contents.
30
+ */
31
+ get: (path: string) => Promise<{
32
+ file: FileRecord;
33
+ stream: Readable;
34
+ }>;
35
+ /**
36
+ * Returns file metadata.
37
+ */
38
+ metadata: (path: string) => Promise<FileRecord>;
39
+ /**
40
+ * Deletes a file.
41
+ */
42
+ remove: (path: string) => Promise<void>;
43
+ /**
44
+ * Renames or moves a file.
45
+ */
46
+ update: (path: string, update: FileUpdate) => Promise<FileRecord>;
47
+ /**
48
+ * Stores a file.
49
+ */
50
+ upload: (upload: FileUpload) => Promise<{
51
+ created: boolean;
52
+ file: FileRecord;
53
+ }>;
54
+ };
55
+ /**
56
+ * Creates a disk- or memory-backed file store with serialized operations.
57
+ * @param {import('./config.js').FilesConfig} config File storage configuration.
58
+ * @returns {Promise<FileStore>} File store.
59
+ */
60
+ export declare const createFileStore: (config: import('./config.js').FilesConfig) => Promise<FileStore>;
61
+ export declare const registerFileRoutes: (fastify: any, { maxFileSize, store }: {
3
62
  maxFileSize: any;
4
- metadataPath: any;
5
- }) => Promise<void>;
63
+ store: any;
64
+ }) => void;
@@ -1,3 +1,3 @@
1
1
  export declare const validateSchemaConfig: (schemaConfig: any, resources: any) => any;
2
2
  export declare const applyConfiguredFields: (schema: any, resource: any, resourceConfig: any) => any;
3
- export declare const readSchemaConfig: (schemaPath: any) => Promise<any>;
3
+ export declare const resolveSchemaConfig: (schema: any) => Promise<any>;
@@ -1,12 +1,11 @@
1
1
  /**
2
- * Creates an OpenAPI 3.0 document from a database and optional schema configuration.
3
- * @param {Record<string, Array<Record<string, unknown>>>} database Database contents.
4
- * @param {Record<string, unknown>} [schemaConfig] Schema configuration.
5
- * @param {{ files?: boolean, host?: string, port?: number }} [serverOptions] Server address and optional features used in the generated document.
2
+ * Builds an OpenAPI document without runtime server addresses.
3
+ * @param {{ database: Record<string, Array<Record<string, unknown>>>, files?: boolean, maxPageSize?: number, schema?: Record<string, unknown> }} options Source data and schema settings.
6
4
  * @returns {Record<string, unknown>} OpenAPI document.
7
5
  */
8
- export declare function createOpenApiDocument(database: Record<string, Array<Record<string, unknown>>>, schemaConfig?: Record<string, unknown>, { files, host, port }?: {
6
+ export declare function buildOpenapiDocument(options: {
7
+ database: Record<string, Array<Record<string, unknown>>>;
9
8
  files?: boolean;
10
- host?: string;
11
- port?: number;
9
+ maxPageSize?: number;
10
+ schema?: Record<string, unknown>;
12
11
  }): Record<string, unknown>;
@@ -1,14 +1,9 @@
1
- export { createOpenApiDocument } from './document.js';
2
- /**
3
- * Generates an OpenAPI YAML file.
4
- * @param {{ databasePath: string, files?: boolean, host?: string, outputPath: string, port?: number, schemaPath?: string }} options Generation options.
5
- * @returns {Promise<Record<string, unknown>>} Generated OpenAPI document.
6
- */
7
- export declare function generateOpenApi({ databasePath, files, host, outputPath, port, schemaPath }: {
8
- databasePath: string;
9
- files?: boolean;
10
- host?: string;
11
- outputPath: string;
12
- port?: number;
13
- schemaPath?: string;
14
- }): Promise<Record<string, unknown>>;
1
+ export declare const createOpenapi: ({ database, files, host, maxPageSize, port, schema }: {
2
+ database: any;
3
+ files: any;
4
+ host?: string | undefined;
5
+ maxPageSize: any;
6
+ port?: number | undefined;
7
+ schema: any;
8
+ }) => Record<string, unknown>;
9
+ export declare const writeOpenapi: (document: any, outputPath: any) => Promise<void>;
@@ -4,10 +4,5 @@ export declare const parsePagination: (query: any, maxPageSize?: number) => {
4
4
  };
5
5
  export declare const paginateItems: (items: any, page: any, pageSize: any) => {
6
6
  data: any;
7
- first: number;
8
- items: any;
9
- last: number;
10
- next: any;
11
- pages: number;
12
- prev: number | null;
7
+ total: any;
13
8
  };