@modular-rest/server 1.18.1 → 1.20.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.
Files changed (41) hide show
  1. package/.nvmrc +1 -1
  2. package/dist/application.js +5 -4
  3. package/dist/class/collection_definition.d.ts +28 -3
  4. package/dist/class/collection_definition.js +72 -2
  5. package/dist/class/combinator.js +7 -3
  6. package/dist/class/directory.d.ts +2 -4
  7. package/dist/class/directory.js +42 -64
  8. package/dist/helper/data_insertion.js +93 -26
  9. package/dist/index.d.ts +12 -1
  10. package/dist/index.js +13 -1
  11. package/dist/services/data_provider/model_registry.d.ts +72 -0
  12. package/dist/services/data_provider/model_registry.js +214 -0
  13. package/dist/services/data_provider/service.js +73 -14
  14. package/dist/services/file/service.d.ts +47 -78
  15. package/dist/services/file/service.js +114 -155
  16. package/dist/services/functions/service.js +4 -4
  17. package/dist/services/jwt/router.js +2 -1
  18. package/dist/services/user_manager/router.js +1 -1
  19. package/dist/services/user_manager/service.js +48 -17
  20. package/jest.config.ts +18 -0
  21. package/package.json +11 -2
  22. package/src/application.ts +5 -4
  23. package/src/class/collection_definition.ts +94 -4
  24. package/src/class/combinator.ts +10 -3
  25. package/src/class/directory.ts +40 -58
  26. package/src/helper/data_insertion.ts +101 -27
  27. package/src/index.ts +13 -1
  28. package/src/services/data_provider/model_registry.ts +243 -0
  29. package/src/services/data_provider/service.ts +74 -14
  30. package/src/services/file/service.ts +136 -178
  31. package/src/services/functions/service.ts +4 -4
  32. package/src/services/jwt/router.ts +2 -1
  33. package/src/services/user_manager/router.ts +1 -1
  34. package/src/services/user_manager/service.ts +49 -20
  35. package/tests/helpers/test-app.ts +182 -0
  36. package/tests/router/data-provider.router.int.test.ts +192 -0
  37. package/tests/router/file.router.int.test.ts +104 -0
  38. package/tests/router/functions.router.int.test.ts +91 -0
  39. package/tests/router/jwt.router.int.test.ts +69 -0
  40. package/tests/router/user-manager.router.int.test.ts +85 -0
  41. package/tests/setup/jest.setup.ts +5 -0
package/.nvmrc CHANGED
@@ -1 +1 @@
1
- v21.5.0
1
+ v22.12.0
@@ -206,16 +206,17 @@ async function createRest(options) {
206
206
  extension: '.js',
207
207
  },
208
208
  });
209
- // 5. Plug in additional defined functions
210
- if (config_1.config.functions) {
211
- combinator_1.default.addFunctionsByArray(config_1.config.functions);
212
- }
209
+ }
210
+ // 5. Plug in additional defined functions
211
+ if (config_1.config.functions) {
212
+ combinator_1.default.addFunctionsByArray(config_1.config.functions);
213
213
  }
214
214
  // 4. Setting up default services
215
215
  try {
216
216
  await require('./helper/presetup_services').setup(config_1.config);
217
217
  }
218
218
  catch (e) {
219
+ console.error(`[createRest] Error in setup:`, e);
219
220
  return Promise.reject(e);
220
221
  }
221
222
  /**
@@ -1,6 +1,7 @@
1
- import { Schema } from 'mongoose';
1
+ import { Schema, Model } from 'mongoose';
2
2
  import { Permission } from './security';
3
3
  import { DatabaseTrigger } from './database_trigger';
4
+ import { MongoOption } from '../services/data_provider/service';
4
5
  /**
5
6
  * Configuration options for creating a collection definition.
6
7
  * This interface defines the structure for configuring MongoDB collections with their associated
@@ -24,6 +25,11 @@ interface CollectionDefinitionOptions {
24
25
  * @see https://mongoosejs.com/docs/5.x/docs/guide.html
25
26
  */
26
27
  schema: Schema<any>;
28
+ /**
29
+ * Optional MongoDB connection options. If not provided, will use config.mongo if available.
30
+ * This is used to pre-create the model before server startup.
31
+ */
32
+ mongoOption?: MongoOption;
27
33
  }
28
34
  /**
29
35
  * To have define any collection in your database you haveto use below method in your `db.[js|ts]` file and export an array of CollectionDefinition instances.
@@ -31,7 +37,7 @@ interface CollectionDefinitionOptions {
31
37
  * @param {CollectionDefinitionOptions} options - The options for the collection
32
38
  * @expandType CollectionDefinitionOptions
33
39
  *
34
- * @returns A new instance of CollectionDefinition
40
+ * @returns A CollectionDefinition instance with a model property that returns the mongoose model
35
41
  *
36
42
  * @public
37
43
  *
@@ -48,9 +54,16 @@ interface CollectionDefinitionOptions {
48
54
  * // trigger: DatabaseTrigger[]
49
55
  * })
50
56
  * ]
57
+ *
58
+ * // Access the model directly:
59
+ * const userCollection = defineCollection({...});
60
+ * const UserModel = userCollection.model;
61
+ * const users = await UserModel.find();
51
62
  * ```
52
63
  */
53
- export declare function defineCollection(options: CollectionDefinitionOptions): CollectionDefinition;
64
+ export declare function defineCollection(options: CollectionDefinitionOptions): CollectionDefinition & {
65
+ model: Model<any>;
66
+ };
54
67
  /**
55
68
  * A class that represents a MongoDB collection configuration. Provides full support for schema validation, access control through permissions,
56
69
  * and custom triggers for various database operations.
@@ -99,6 +112,8 @@ export declare class CollectionDefinition {
99
112
  permissions: Permission[];
100
113
  /** @readonly Optional database triggers */
101
114
  triggers?: DatabaseTrigger[];
115
+ /** Optional mongoose model for this collection */
116
+ private _model;
102
117
  /**
103
118
  * Creates a new CollectionDefinition instance
104
119
  *
@@ -108,5 +123,15 @@ export declare class CollectionDefinition {
108
123
  * @beta
109
124
  */
110
125
  constructor({ database, collection, schema, permissions, triggers, }: CollectionDefinitionOptions);
126
+ /**
127
+ * Get the mongoose model for this collection
128
+ * @returns {Model<any> | null} The mongoose model or null if not set
129
+ */
130
+ getModel(): Model<any> | null;
131
+ /**
132
+ * Set the mongoose model for this collection
133
+ * @param {Model<any>} model - The mongoose model
134
+ */
135
+ setModel(model: Model<any>): void;
111
136
  }
112
137
  export {};
@@ -1,14 +1,19 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.CollectionDefinition = void 0;
4
7
  exports.defineCollection = defineCollection;
8
+ const model_registry_1 = __importDefault(require("../services/data_provider/model_registry"));
9
+ const config_1 = require("../config");
5
10
  /**
6
11
  * To have define any collection in your database you haveto use below method in your `db.[js|ts]` file and export an array of CollectionDefinition instances.
7
12
  *
8
13
  * @param {CollectionDefinitionOptions} options - The options for the collection
9
14
  * @expandType CollectionDefinitionOptions
10
15
  *
11
- * @returns A new instance of CollectionDefinition
16
+ * @returns A CollectionDefinition instance with a model property that returns the mongoose model
12
17
  *
13
18
  * @public
14
19
  *
@@ -25,10 +30,59 @@ exports.defineCollection = defineCollection;
25
30
  * // trigger: DatabaseTrigger[]
26
31
  * })
27
32
  * ]
33
+ *
34
+ * // Access the model directly:
35
+ * const userCollection = defineCollection({...});
36
+ * const UserModel = userCollection.model;
37
+ * const users = await UserModel.find();
28
38
  * ```
29
39
  */
30
40
  function defineCollection(options) {
31
- return new CollectionDefinition(options);
41
+ const definition = new CollectionDefinition(options);
42
+ // Try to get mongoOption from options or config
43
+ let mongoOption = options.mongoOption;
44
+ if (!mongoOption && config_1.config.mongo) {
45
+ mongoOption = config_1.config.mongo;
46
+ }
47
+ // If mongoOption is available, register the collection and create the model
48
+ if (mongoOption) {
49
+ const model = model_registry_1.default.registerCollection(definition, mongoOption);
50
+ definition.setModel(model);
51
+ }
52
+ // Create a proxy object that includes both CollectionDefinition properties and model
53
+ const result = definition;
54
+ // Add model property getter
55
+ Object.defineProperty(result, 'model', {
56
+ get() {
57
+ // If model was already set, return it
58
+ const existingModel = definition.getModel();
59
+ if (existingModel) {
60
+ return existingModel;
61
+ }
62
+ // Otherwise, try to get from registry
63
+ const registryModel = model_registry_1.default.getModel(definition.database, definition.collection);
64
+ if (registryModel) {
65
+ definition.setModel(registryModel);
66
+ return registryModel;
67
+ }
68
+ // If still not found, try to get mongoOption from config and register now
69
+ let currentMongoOption = mongoOption;
70
+ if (!currentMongoOption && config_1.config.mongo) {
71
+ currentMongoOption = config_1.config.mongo;
72
+ }
73
+ if (currentMongoOption) {
74
+ const newModel = model_registry_1.default.registerCollection(definition, currentMongoOption);
75
+ definition.setModel(newModel);
76
+ return newModel;
77
+ }
78
+ throw new Error(`Model for ${definition.database}.${definition.collection} is not available. ` +
79
+ `Ensure mongoOption is provided in defineCollection options, config.mongo is set, ` +
80
+ `or the collection is registered via addCollectionDefinitionByList before accessing the model.`);
81
+ },
82
+ enumerable: true,
83
+ configurable: true,
84
+ });
85
+ return result;
32
86
  }
33
87
  /**
34
88
  * A class that represents a MongoDB collection configuration. Provides full support for schema validation, access control through permissions,
@@ -77,11 +131,27 @@ class CollectionDefinition {
77
131
  * @beta
78
132
  */
79
133
  constructor({ database, collection, schema, permissions, triggers, }) {
134
+ /** Optional mongoose model for this collection */
135
+ this._model = null;
80
136
  this.database = database;
81
137
  this.collection = collection;
82
138
  this.schema = schema;
83
139
  this.permissions = permissions;
84
140
  this.triggers = triggers;
85
141
  }
142
+ /**
143
+ * Get the mongoose model for this collection
144
+ * @returns {Model<any> | null} The mongoose model or null if not set
145
+ */
146
+ getModel() {
147
+ return this._model;
148
+ }
149
+ /**
150
+ * Set the mongoose model for this collection
151
+ * @param {Model<any>} model - The mongoose model
152
+ */
153
+ setModel(model) {
154
+ this._model = model;
155
+ }
86
156
  }
87
157
  exports.CollectionDefinition = CollectionDefinition;
@@ -43,7 +43,7 @@ class Combinator {
43
43
  // find route paths
44
44
  const option = {
45
45
  name: 'router',
46
- filter: ['.js'],
46
+ filter: ['.js', '.ts'],
47
47
  };
48
48
  let routerPaths = [];
49
49
  try {
@@ -52,6 +52,8 @@ class Combinator {
52
52
  catch (e) {
53
53
  console.log(e);
54
54
  }
55
+ // ignore type declarations
56
+ routerPaths = routerPaths.filter(routePath => !routePath.endsWith('.d.ts'));
55
57
  // create and combine routes into the app
56
58
  for (let i = 0; i < routerPaths.length; i++) {
57
59
  const service = require(routerPaths[i]);
@@ -70,7 +72,7 @@ class Combinator {
70
72
  let rootObject_temp;
71
73
  const option = {
72
74
  name: filename.name,
73
- filter: [filename.extension],
75
+ filter: [filename.extension, '.ts'],
74
76
  };
75
77
  let modulesPath = [];
76
78
  try {
@@ -79,6 +81,7 @@ class Combinator {
79
81
  catch (e) {
80
82
  console.log(e);
81
83
  }
84
+ modulesPath = modulesPath.filter(modulePath => !modulePath.endsWith('.d.ts'));
82
85
  // create and combine routes into the app
83
86
  for (let i = 0; i < modulesPath.length; i++) {
84
87
  const moduleObject = require(modulesPath[i]);
@@ -117,7 +120,7 @@ class Combinator {
117
120
  // find route paths
118
121
  const option = {
119
122
  name: filename.name,
120
- filter: [filename.extension],
123
+ filter: [filename.extension, '.ts'],
121
124
  };
122
125
  let functionsPaths = [];
123
126
  try {
@@ -126,6 +129,7 @@ class Combinator {
126
129
  catch (e) {
127
130
  console.log(e);
128
131
  }
132
+ functionsPaths = functionsPaths.filter(functionPath => !functionPath.endsWith('.d.ts'));
129
133
  // create and combine routes into the app
130
134
  for (let i = 0; i < functionsPaths.length; i++) {
131
135
  const modularFunctions = require(functionsPaths[i]);
@@ -2,14 +2,12 @@ interface DirectorySettings {
2
2
  name?: string;
3
3
  filter?: string[];
4
4
  }
5
- type WalkCallback = (err: Error | null, results: string[]) => void;
6
5
  /**
7
- * Walk through a directory and its subdirectories
6
+ * Walk through a directory and its subdirectories efficiently
8
7
  * @param dir - Directory to walk
9
8
  * @param settings - Settings for filtering files
10
- * @param done - Callback function
11
9
  */
12
- declare function walk(dir: string, settings: DirectorySettings, done: WalkCallback): void;
10
+ declare function walk(dir: string, settings: DirectorySettings): Promise<string[]>;
13
11
  /**
14
12
  * Find files in a directory with Promise API
15
13
  * @param dir - Directory to search
@@ -8,66 +8,45 @@ exports.find = find;
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  /**
11
- * Walk through a directory and its subdirectories
11
+ * Walk through a directory and its subdirectories efficiently
12
12
  * @param dir - Directory to walk
13
13
  * @param settings - Settings for filtering files
14
- * @param done - Callback function
15
14
  */
16
- function walk(dir, settings, done) {
15
+ async function walk(dir, settings) {
17
16
  let results = [];
18
- // Read director file and folders
19
- fs_1.default.readdir(dir, function (err, list) {
20
- if (err)
21
- return done(err, results);
22
- let pending = list.length;
23
- if (!pending)
24
- return done(null, results);
25
- list.forEach(function (file) {
26
- file = path_1.default.join(dir, file);
27
- fs_1.default.stat(file, function (err, stat) {
28
- if (err) {
29
- // Handle file stat error but continue with other files
30
- console.error(`Error reading file stats for ${file}:`, err);
31
- if (!--pending)
32
- done(null, results);
33
- return;
17
+ const list = await fs_1.default.promises.readdir(dir);
18
+ for (const file of list) {
19
+ const filePath = path_1.default.join(dir, file);
20
+ const stat = await fs_1.default.promises.stat(filePath);
21
+ if (stat && stat.isDirectory()) {
22
+ // Ignore common directories that should not be scanned
23
+ if (file === 'node_modules' || file === '.git' || file === 'dist') {
24
+ continue;
25
+ }
26
+ const res = await walk(filePath, settings);
27
+ results = results.concat(res);
28
+ }
29
+ else {
30
+ const extension = path_1.default.extname(filePath);
31
+ const fileName = path_1.default.basename(filePath).split('.')[0];
32
+ let fileNameKey = true;
33
+ // name filter
34
+ if (settings.name && settings.name !== fileName) {
35
+ fileNameKey = false;
36
+ }
37
+ // extension filter
38
+ if (settings.filter && fileNameKey) {
39
+ if (settings.filter.some(ext => ext.toLowerCase() === extension.toLowerCase())) {
40
+ results.push(filePath);
34
41
  }
35
- // If directory, execute a recursive call
36
- if (stat && stat.isDirectory()) {
37
- // Add directory to array [comment if you need to remove the directories from the array]
38
- // results.push(file);
39
- walk(file, settings, function (err, res) {
40
- results = results.concat(res);
41
- if (!--pending)
42
- done(null, results);
43
- });
44
- }
45
- else {
46
- // file filter
47
- const extension = path_1.default.extname(file);
48
- const fileName = path_1.default.basename(file).split('.')[0];
49
- let fileNameKey = true;
50
- // name filter
51
- if (settings.name && settings.name === fileName)
52
- fileNameKey = true;
53
- else
54
- fileNameKey = false;
55
- // extension filter
56
- if (settings.filter && fileNameKey) {
57
- settings.filter.forEach(function (element) {
58
- if (element.toLowerCase() === extension.toLowerCase())
59
- results.push(file);
60
- });
61
- }
62
- // push any file if no option
63
- else if (fileNameKey)
64
- results.push(file);
65
- if (!--pending)
66
- done(null, results);
67
- }
68
- });
69
- });
70
- });
42
+ }
43
+ // push any file if no name filter and no extension filter
44
+ else if (fileNameKey && !settings.filter) {
45
+ results.push(filePath);
46
+ }
47
+ }
48
+ }
49
+ return results;
71
50
  }
72
51
  /**
73
52
  * Find files in a directory with Promise API
@@ -75,13 +54,12 @@ function walk(dir, settings, done) {
75
54
  * @param settings - Settings for filtering files
76
55
  * @returns Promise resolving to an array of file paths
77
56
  */
78
- function find(dir, settings) {
79
- return new Promise((resolve, reject) => {
80
- walk(dir, settings, (err, result) => {
81
- if (err)
82
- reject(err);
83
- else
84
- resolve(result);
85
- });
86
- });
57
+ async function find(dir, settings) {
58
+ try {
59
+ return await walk(dir, settings);
60
+ }
61
+ catch (err) {
62
+ console.error(`Error in directory.find for ${dir}:`, err);
63
+ return [];
64
+ }
87
65
  }
@@ -65,43 +65,110 @@ const userManager = __importStar(require("../services/user_manager/service"));
65
65
  */
66
66
  async function createAdminUser({ email, password }) {
67
67
  const authModel = DataProvider.getCollection('cms', 'auth');
68
+ // Wait for connection to be ready for queries
69
+ const connection = authModel.db;
70
+ if (connection) {
71
+ if (connection.readyState !== 1) {
72
+ await new Promise((resolve, reject) => {
73
+ const timeout = setTimeout(() => {
74
+ reject(new Error('Database connection timeout - connection not ready after 10s'));
75
+ }, 10000);
76
+ if (connection.readyState === 1) {
77
+ clearTimeout(timeout);
78
+ resolve();
79
+ }
80
+ else {
81
+ connection.once('connected', () => {
82
+ clearTimeout(timeout);
83
+ resolve();
84
+ });
85
+ connection.once('error', err => {
86
+ clearTimeout(timeout);
87
+ reject(err);
88
+ });
89
+ }
90
+ });
91
+ }
92
+ // Test if database is actually ready by doing a simple operation
93
+ // This ensures the connection is fully initialized
94
+ let retries = 5;
95
+ let lastError = null;
96
+ while (retries > 0) {
97
+ try {
98
+ await authModel.findOne({}).limit(1).maxTimeMS(3000).lean().exec();
99
+ // Additional small delay to ensure write operations will work
100
+ await new Promise(resolve => setTimeout(resolve, 100));
101
+ break; // Success, connection is ready
102
+ }
103
+ catch (err) {
104
+ lastError = err instanceof Error ? err : new Error(String(err));
105
+ retries--;
106
+ if (retries > 0) {
107
+ await new Promise(resolve => setTimeout(resolve, 500));
108
+ }
109
+ }
110
+ }
111
+ if (retries === 0 && lastError) {
112
+ throw new Error(`Database not ready for queries: ${lastError.message}`);
113
+ }
114
+ }
68
115
  try {
69
- const isAnonymousExisted = await authModel.countDocuments({ type: 'anonymous' }).exec();
116
+ // Execute queries with explicit timeout handling
117
+ const queryOptions = { maxTimeMS: 5000 }; // 5 second timeout
118
+ const isAnonymousExisted = await authModel
119
+ .countDocuments({ type: 'anonymous' })
120
+ .maxTimeMS(5000)
121
+ .exec();
70
122
  const isAdministratorExisted = await authModel
71
123
  .countDocuments({ type: 'user', email: email })
124
+ .maxTimeMS(5000)
72
125
  .exec();
73
126
  if (isAnonymousExisted === 0) {
74
- await userManager.main.registerUser({
75
- permissionGroup: (0, permissionManager_1.getDefaultAnonymousPermissionGroup)().title,
76
- email: '',
77
- phone: '',
78
- password: '',
79
- type: 'anonymous',
80
- });
81
- // await new authModel({
82
- // permission: getDefaultAnonymousPermissionGroup().title,
83
- // email: "",
84
- // phone: "",
85
- // password: "",
86
- // type: "anonymous",
87
- // }).save();
127
+ // Wrap registerUser with timeout to prevent hanging
128
+ try {
129
+ let timeoutId;
130
+ await Promise.race([
131
+ userManager.main.registerUser({
132
+ permissionGroup: (0, permissionManager_1.getDefaultAnonymousPermissionGroup)().title,
133
+ email: '',
134
+ phone: '',
135
+ password: '',
136
+ type: 'anonymous',
137
+ }),
138
+ new Promise((_, reject) => {
139
+ timeoutId = setTimeout(() => reject(new Error('registerUser timeout for anonymous user after 15s')), 15000);
140
+ }),
141
+ ]).finally(() => {
142
+ if (timeoutId)
143
+ clearTimeout(timeoutId);
144
+ });
145
+ }
146
+ catch (err) {
147
+ // If anonymous user creation fails, log but don't fail completely
148
+ // It might already exist from a previous attempt
149
+ console.warn('Failed to create anonymous user:', err instanceof Error ? err.message : String(err));
150
+ }
88
151
  }
89
152
  if (isAdministratorExisted === 0) {
90
153
  if (!email || !password) {
91
154
  return Promise.reject('Invalid email or password for admin user.');
92
155
  }
93
- await userManager.main.registerUser({
94
- permissionGroup: (0, permissionManager_1.getDefaultAdministratorPermissionGroup)().title,
95
- email: email,
96
- password: password,
97
- type: 'user',
156
+ // Wrap registerUser with timeout to prevent hanging
157
+ let timeoutId;
158
+ await Promise.race([
159
+ userManager.main.registerUser({
160
+ permissionGroup: (0, permissionManager_1.getDefaultAdministratorPermissionGroup)().title,
161
+ email: email,
162
+ password: password,
163
+ type: 'user',
164
+ }),
165
+ new Promise((_, reject) => {
166
+ timeoutId = setTimeout(() => reject(new Error('registerUser timeout for admin user after 15s')), 15000);
167
+ }),
168
+ ]).finally(() => {
169
+ if (timeoutId)
170
+ clearTimeout(timeoutId);
98
171
  });
99
- // await new authModel({
100
- // permission: getDefaultAdministratorPermissionGroup().title,
101
- // email: email,
102
- // password: password,
103
- // type: "user",
104
- // }).save();
105
172
  }
106
173
  }
107
174
  catch (e) {
package/dist/index.d.ts CHANGED
@@ -40,7 +40,7 @@ export { createRest };
40
40
  * ],
41
41
  * uploadDirectoryConfig: {
42
42
  * directory: './uploads',
43
- * urlPath: '/assets'
43
+ * urlPath: '/assets'
44
44
  * }
45
45
  * };
46
46
  * ```
@@ -118,6 +118,17 @@ export { paginator };
118
118
  * @description Database collection access utilities
119
119
  */
120
120
  export { getCollection };
121
+ /**
122
+ * @description Model registry for managing mongoose models and connections
123
+ * @example
124
+ * ```typescript
125
+ * import { modelRegistry } from '@modular-rest/server';
126
+ *
127
+ * // Get a model directly from registry
128
+ * const userModel = modelRegistry.getModel('myapp', 'users');
129
+ * ```
130
+ */
131
+ export { modelRegistry } from './services/data_provider/model_registry';
121
132
  /**
122
133
  * @description File handling utilities
123
134
  * @example
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.userManager = exports.middleware = exports.fileService = exports.getCollection = exports.paginator = exports.reply = exports.validator = exports.TypeCasters = exports.defineFunction = exports.AccessTypes = exports.PermissionGroup = exports.PermissionTypes = exports.Permission = exports.AccessDefinition = exports.CmsTrigger = exports.DatabaseTrigger = exports.Schema = exports.schemas = exports.defineCollection = exports.CollectionDefinition = exports.createRest = void 0;
39
+ exports.userManager = exports.middleware = exports.fileService = exports.modelRegistry = exports.getCollection = exports.paginator = exports.reply = exports.validator = exports.TypeCasters = exports.defineFunction = exports.AccessTypes = exports.PermissionGroup = exports.PermissionTypes = exports.Permission = exports.AccessDefinition = exports.CmsTrigger = exports.DatabaseTrigger = exports.Schema = exports.schemas = exports.defineCollection = exports.CollectionDefinition = exports.createRest = void 0;
40
40
  // Application
41
41
  const application_1 = require("./application");
42
42
  Object.defineProperty(exports, "createRest", { enumerable: true, get: function () { return application_1.createRest; } });
@@ -77,3 +77,15 @@ Object.defineProperty(exports, "PermissionGroup", { enumerable: true, get: funct
77
77
  Object.defineProperty(exports, "AccessTypes", { enumerable: true, get: function () { return security_1.AccessTypes; } });
78
78
  const middleware = __importStar(require("./middlewares"));
79
79
  exports.middleware = middleware;
80
+ /**
81
+ * @description Model registry for managing mongoose models and connections
82
+ * @example
83
+ * ```typescript
84
+ * import { modelRegistry } from '@modular-rest/server';
85
+ *
86
+ * // Get a model directly from registry
87
+ * const userModel = modelRegistry.getModel('myapp', 'users');
88
+ * ```
89
+ */
90
+ var model_registry_1 = require("./services/data_provider/model_registry");
91
+ Object.defineProperty(exports, "modelRegistry", { enumerable: true, get: function () { return model_registry_1.modelRegistry; } });
@@ -0,0 +1,72 @@
1
+ import { Connection, Model } from 'mongoose';
2
+ import { CollectionDefinition } from '../../class/collection_definition';
3
+ import { MongoOption } from './service';
4
+ /**
5
+ * ModelRegistry - Singleton class for managing mongoose models and connections
6
+ * Pre-creates all models before database connections are established
7
+ */
8
+ declare class ModelRegistry {
9
+ private static instance;
10
+ private connections;
11
+ private models;
12
+ private collectionDefinitions;
13
+ private mongoOption;
14
+ private constructor();
15
+ /**
16
+ * Get the singleton instance of ModelRegistry
17
+ * @returns {ModelRegistry} The singleton instance
18
+ */
19
+ static getInstance(): ModelRegistry;
20
+ /**
21
+ * Register a collection definition and create its model
22
+ * @param {CollectionDefinition} definition - The collection definition
23
+ * @param {MongoOption} mongoOption - MongoDB connection options
24
+ * @returns {Model<any>} The created mongoose model
25
+ */
26
+ registerCollection(definition: CollectionDefinition, mongoOption: MongoOption): Model<any>;
27
+ /**
28
+ * Get a model by database and collection name
29
+ * @param {string} database - Database name
30
+ * @param {string} collection - Collection name
31
+ * @returns {Model<any> | null} The mongoose model or null if not found
32
+ */
33
+ getModel(database: string, collection: string): Model<any> | null;
34
+ /**
35
+ * Get a connection for a database
36
+ * @param {string} database - Database name
37
+ * @returns {Connection | null} The mongoose connection or null if not found
38
+ */
39
+ getConnection(database: string): Connection | null;
40
+ /**
41
+ * Initialize all connections and connect to databases
42
+ * This should be called during server startup
43
+ * @param {MongoOption} mongoOption - MongoDB connection options
44
+ * @returns {Promise<void>} A promise that resolves when all connections are established
45
+ */
46
+ initializeConnections(mongoOption: MongoOption): Promise<void>;
47
+ /**
48
+ * Get all registered collection definitions
49
+ * @returns {CollectionDefinition[]} Array of collection definitions
50
+ */
51
+ getCollectionDefinitions(): CollectionDefinition[];
52
+ /**
53
+ * Get all models for a specific database
54
+ * @param {string} database - Database name
55
+ * @returns {Record<string, Model<any>> | null} Object mapping collection names to models, or null if database not found
56
+ */
57
+ getModelsForDatabase(database: string): Record<string, Model<any>> | null;
58
+ /**
59
+ * Check if a model exists
60
+ * @param {string} database - Database name
61
+ * @param {string} collection - Collection name
62
+ * @returns {boolean} True if model exists, false otherwise
63
+ */
64
+ hasModel(database: string, collection: string): boolean;
65
+ /**
66
+ * Clear all internal state, including connections and models.
67
+ * Useful for testing when different database prefixes are used.
68
+ */
69
+ clear(): Promise<void>;
70
+ }
71
+ export declare const modelRegistry: ModelRegistry;
72
+ export default modelRegistry;