@reldens/storage 0.31.0 → 0.33.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/README.md CHANGED
@@ -3,56 +3,103 @@
3
3
  # Reldens - Storage
4
4
 
5
5
  ## About this package
6
- This package is designed to provide a standard drivers support for managing Reldens project data.
6
+ This package provides standardized database drivers for Reldens projects.
7
+ It ensures consistent data access methods across different database types and ORM implementations.
7
8
 
8
- This way in any Reldens project we can trust that any driver implementation will have the exact same methods available to fetch and manage the project entities.
9
+ ## Features
9
10
 
10
- Every time you need to load, create, update or delete data in Reldens, the drivers from this package will be used.
11
+ ### ORM Support
12
+ - **Objection JS** (via Knex) - For SQL databases (recommended)
13
+ - **Mikro-ORM** - For MongoDB/NoSQL support
11
14
 
12
- ## Current features
13
- The package currently has drivers to support two ORMs:
14
- - Objection JS (with Knex), for everything that's SQL related (this is our base and recommended package).
15
+ ### Entity Management
16
+ - Standardized CRUD operations
17
+ - Automatic entity generation from database schemas
18
+ - Type mapping between database and JavaScript
19
+ - Foreign key relationship handling
20
+ - ENUM field support with formatted values
21
+
22
+ ### CLI Tools
23
+ Generate entity files directly from your database structure:
24
+ ```bash
25
+ npx reldens-storage generateEntities --user=dbuser --pass=dbpass --database=dbname --driver=objection-js
26
+ ```
27
+
28
+ Options:
29
+ - `--user` - Database username
30
+ - `--pass` - Database password
31
+ - `--host` - Database host (default: localhost)
32
+ - `--port` - Database port (default: 3306)
33
+ - `--database` - Database name
34
+ - `--driver` - ORM driver (objection-js|mikro-orm)
35
+ - `--client` - Database client (mysql|mysql2|mongodb)
36
+ - `--path` - Project path for output files
37
+
38
+ ## Usage Examples
39
+
40
+ ### SQL with Objection JS
15
41
  ```javascript
16
- let server = new ObjectionJsDataServer({
17
- client: 'mysql',
42
+ const { ObjectionJsDataServer } = require('@reldens/storage');
43
+
44
+ const server = new ObjectionJsDataServer({
45
+ client: 'mysql2',
18
46
  config: {
19
47
  user: 'reldens',
20
48
  password: 'reldens',
21
49
  database: 'reldens',
22
- port : 3306,
50
+ host: 'localhost',
51
+ port: 3306
23
52
  }
24
53
  });
54
+
55
+ await server.connect();
56
+ const entities = server.generateEntities();
25
57
  ```
26
- - Mikro-ORM, to offer support for nonSQL/MongoDB (this package is not finished, if you like to use it please contact us).
58
+
59
+ ### MongoDB with Mikro-ORM
27
60
  ```javascript
28
- let server = new MikroOrmDataServer({
61
+ const { MikroOrmDataServer } = require('@reldens/storage');
62
+
63
+ const server = new MikroOrmDataServer({
29
64
  client: 'mongodb',
30
65
  config: {
31
66
  user: 'reldens',
32
67
  password: 'reldens',
33
- database: 'test',
34
- port : 27017,
68
+ database: 'reldens',
69
+ host: 'localhost',
70
+ port: 27017
35
71
  },
36
- connectStringOptions: 'authSource=reldens&readPreference=primary&appname=MongoDB%20Compass&ssl=false',
37
- autoGenerateEntities: false,
38
- rawEntities: Object.values(rawRegisteredEntities)
72
+ connectStringOptions: 'authSource=reldens&readPreference=primary&ssl=false',
73
+ rawEntities: yourEntities
39
74
  });
75
+
76
+ await server.connect();
77
+ const entities = server.generateEntities();
40
78
  ```
41
79
 
42
- ## Custom drivers
43
- Through this package you can also create your own drivers (to support your own storage system) and later implement it on any Reldens project.
80
+ ## Custom Drivers
44
81
 
45
- In order to do this, just extend the BaseDataServer and BaseDriver classes and fill all the required methods.
82
+ You can create custom storage drivers by extending the base classes:
46
83
 
47
- For last, on your Reldens implementation you just need to pass an instance of your data server to the server manager and you are done:
48
- ```
49
- const appServer = new ServerManager(serverConfig, eventsManager, yourDriverInstance);
84
+ 1. Extend `BaseDataServer` and `BaseDriver`
85
+ 2. Implement all required methods
86
+ 3. Pass your custom server instance to Reldens ServerManager:
87
+
88
+ ```javascript
89
+ const { ServerManager } = require('@reldens/server');
90
+ const YourCustomDriver = require('./your-custom-driver');
91
+
92
+ const customDriver = new YourCustomDriver(options);
93
+ const appServer = new ServerManager(serverConfig, eventsManager, customDriver);
50
94
  ```
51
95
 
52
- You can also check the [skeleton implementation](https://github.com/damian-pastorini/reldens-skeleton/) to see how it works.
96
+ ## Links
97
+ - [Reldens Website](https://www.reldens.com/)
98
+ - [GitHub Repository](https://github.com/damian-pastorini/reldens/tree/master)
53
99
 
54
- For reference see Reldens GitHub: [https://github.com/damian-pastorini/reldens/tree/master](https://github.com/damian-pastorini/reldens/tree/master)
100
+ ---
55
101
 
56
102
  ### [Reldens](https://www.reldens.com/ "Reldens")
57
103
 
58
104
  ##### [By DwDeveloper](https://www.dwdeveloper.com/ "DwDeveloper")
105
+
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ *
5
+ * Reldens - Storage CLI
6
+ *
7
+ */
8
+
9
+ const { EntitiesGenerator } = require('../lib/entities-generator');
10
+ const { Logger } = require('@reldens/utils');
11
+
12
+ let args = process.argv.slice(2);
13
+ let command = args[0];
14
+
15
+ if('generateEntities' !== command){
16
+ Logger.error('Unknown command. Available command "generateEntities".');
17
+ process.exit();
18
+ }
19
+
20
+ let connectionData = {
21
+ driver: 'objection-js',
22
+ client: 'mysql2',
23
+ user: '',
24
+ password: '',
25
+ host: 'localhost',
26
+ database: '',
27
+ port: 3306
28
+ };
29
+
30
+ let projectPath = process.cwd();
31
+ for(let i = 1; i < args.length; i++){
32
+ let arg = args[i];
33
+ if(!arg.startsWith('--')){
34
+ continue;
35
+ }
36
+ let [key, value] = arg.substring(2).split('=');
37
+ if(!key || !value){
38
+ continue;
39
+ }
40
+ if('pass' === key){
41
+ connectionData.password = value;
42
+ continue;
43
+ }
44
+ if('path' === key){
45
+ projectPath = value;
46
+ continue;
47
+ }
48
+ connectionData[key] = value;
49
+ }
50
+
51
+ if('mikro-orm' === connectionData.driver && !args.find(arg => arg.startsWith('--client='))){
52
+ connectionData.client = 'mysql';
53
+ }
54
+
55
+ if(!connectionData.user || !connectionData.database){
56
+ Logger.error('Required parameters missing.');
57
+ Logger.error(
58
+ 'Usage:',
59
+ 'npx reldens-storage generateEntities --user=[db-username] --pass=[db-password] --host=[db-host] --database=[db-name] --driver=[driver-map-key] --client=[db-client] --path=[project-path]'
60
+ );
61
+ process.exit();
62
+ }
63
+
64
+ let generator = new EntitiesGenerator({connectionData, projectPath});
65
+ generator.generate().then((success) => {
66
+ if(!success){
67
+ Logger.error('Entity generation failed.');
68
+ return;
69
+ }
70
+ Logger.info('Entity generation completed successfully!');
71
+ process.exit();
72
+ }).catch((error) => {
73
+ Logger.error('Error during entity generation: '+error.message);
74
+ process.exit();
75
+ });
package/index.js CHANGED
@@ -12,17 +12,30 @@ const { Model } = require('objection');
12
12
  const { MikroOrmDriver } = require('./lib/mikro-orm/mikro-orm-driver');
13
13
  const { MikroOrmDataServer } = require('./lib/mikro-orm/mikro-orm-data-server');
14
14
  const MikroOrmCore = require('@mikro-orm/core');
15
+ const { EntitiesGenerator } = require('./lib/entities-generator');
16
+ const { EntityProperties } = require('./lib/entity-properties');
17
+ const { TypeMapper } = require('./lib/type-mapper');
18
+ const { MySQLTablesProvider } = require('./lib/mysql-tables-provider');
15
19
 
16
20
  module.exports = {
17
21
  // base:
18
- BaseDataServer: BaseDataServer,
19
- BaseDriver: BaseDriver,
22
+ BaseDataServer,
23
+ BaseDriver,
24
+ DriversMap: {
25
+ 'objection-js': ObjectionJsDataServer,
26
+ 'mikro-orm': MikroOrmDataServer
27
+ },
20
28
  // objection-js:
21
- ObjectionJsDataServer: ObjectionJsDataServer,
22
- ObjectionJsDriver: ObjectionJsDriver,
29
+ ObjectionJsDataServer,
30
+ ObjectionJsDriver,
23
31
  ObjectionJsRawModel: Model,
24
32
  // mikro-orm:
25
33
  MikroOrmCore,
26
- MikroOrmDataServer: MikroOrmDataServer,
27
- MikroOrmDriver: MikroOrmDriver
34
+ MikroOrmDataServer,
35
+ MikroOrmDriver,
36
+ // entities:
37
+ EntitiesGenerator,
38
+ EntityProperties,
39
+ TypeMapper,
40
+ MySQLTablesProvider
28
41
  };
@@ -56,6 +56,11 @@ class BaseDataServer
56
56
  ErrorManager.error('BaseDriver connect() not implemented.');
57
57
  }
58
58
 
59
+ async fetchEntitiesFromDatabase()
60
+ {
61
+ ErrorManager.error('BaseDriver fetchEntitiesFromDatabase() not implemented.');
62
+ }
63
+
59
64
  }
60
65
 
61
66
  module.exports.BaseDataServer = BaseDataServer;
@@ -0,0 +1,584 @@
1
+ /**
2
+ *
3
+ * Reldens - EntitiesGenerator
4
+ *
5
+ */
6
+
7
+ const { FileHandler } = require('@reldens/server-utils');
8
+ const { ErrorManager, Logger, sc } = require('@reldens/utils');
9
+ const { ObjectionJsDataServer } = require('./objection-js/objection-js-data-server');
10
+ const { MikroOrmDataServer } = require('./mikro-orm/mikro-orm-data-server');
11
+ const { TypeMapper } = require('./type-mapper');
12
+
13
+ class EntitiesGenerator
14
+ {
15
+
16
+ constructor(props)
17
+ {
18
+ this.templatesFolderPath = FileHandler.joinPaths(__dirname, 'entity-templates');
19
+ this.templates = {
20
+ 'objection-js': FileHandler.joinPaths(this.templatesFolderPath, 'objection-js-entity.template'),
21
+ 'mikro-orm': FileHandler.joinPaths(this.templatesFolderPath, 'mikro-orm-entity.template'),
22
+ 'entity': FileHandler.joinPaths(this.templatesFolderPath, 'entity.template'),
23
+ 'entities-config': FileHandler.joinPaths(this.templatesFolderPath, 'entities-config.template'),
24
+ 'entities-translations': FileHandler.joinPaths(this.templatesFolderPath, 'entities-translations.template'),
25
+ 'registered-models': FileHandler.joinPaths(this.templatesFolderPath, 'registered-models.template')
26
+ };
27
+ this.projectPath = sc.get(props, 'projectPath', FileHandler.joinPaths(__dirname, '..'));
28
+ this.generationFolder = FileHandler.joinPaths(this.projectPath, 'generated-entities');
29
+ this.entitiesFolder = FileHandler.joinPaths(this.generationFolder, 'entities');
30
+ this.modelsFolder = FileHandler.joinPaths(this.generationFolder, 'models');
31
+ this.entitiesConfigPath = FileHandler.joinPaths(this.generationFolder, 'entities-config.js');
32
+ this.entitiesTranslationsPath = FileHandler.joinPaths(this.generationFolder, 'entities-translations.js');
33
+ this.driverMap = {
34
+ 'objection-js': ObjectionJsDataServer,
35
+ 'mikro-orm': MikroOrmDataServer
36
+ };
37
+ this.server = sc.get(props, 'server', false);
38
+ this.connectionData = sc.get(props, 'connectionData', false);
39
+ if(!this.server && !this.connectionData){
40
+ ErrorManager.error('Either server or connectionData must be provided to EntitiesGenerator.');
41
+ }
42
+ this.generatedEntities = {};
43
+ }
44
+
45
+ ensureFoldersExist()
46
+ {
47
+ if(!FileHandler.exists(this.generationFolder)){
48
+ try {
49
+ FileHandler.createFolder(this.generationFolder, { recursive: true });
50
+ } catch (error) {
51
+ ErrorManager.error('Failed to create generation folder: '+error.message);
52
+ }
53
+ }
54
+ if(!FileHandler.exists(this.entitiesFolder)){
55
+ try {
56
+ FileHandler.createFolder(this.entitiesFolder, { recursive: true });
57
+ } catch (error) {
58
+ ErrorManager.error('Failed to create entities folder: '+error.message);
59
+ }
60
+ }
61
+ if(!FileHandler.exists(this.modelsFolder)){
62
+ try {
63
+ FileHandler.createFolder(this.modelsFolder, { recursive: true });
64
+ } catch (error) {
65
+ ErrorManager.error('Failed to create models folder: '+error.message);
66
+ }
67
+ }
68
+ for(let driverKey of Object.keys(this.driverMap)){
69
+ let driverFolder = FileHandler.joinPaths(this.modelsFolder, driverKey);
70
+ if(!FileHandler.exists(driverFolder)){
71
+ try {
72
+ FileHandler.createFolder(driverFolder, { recursive: true });
73
+ } catch (error) {
74
+ ErrorManager.error('Failed to create driver folder: '+error.message);
75
+ }
76
+ }
77
+ }
78
+ }
79
+
80
+ async generateEntityFile(tableName, tableData)
81
+ {
82
+ let entityTemplatePath = this.templates['entity'];
83
+ if(!FileHandler.exists(entityTemplatePath)){
84
+ ErrorManager.error('Entity template file not found: '+entityTemplatePath);
85
+ }
86
+ let entityTemplateContent;
87
+ try {
88
+ entityTemplateContent = FileHandler.fetchFileContents(entityTemplatePath);
89
+ } catch (error) {
90
+ ErrorManager.error('Failed to read entity template file: '+error.message);
91
+ }
92
+ let entityClassName = sc.capitalizedCamelCase(tableName)+'Entity';
93
+ let titleProperty = this.determineTitleProperty(tableData.columns);
94
+ for(let columnName in tableData.columns){
95
+ tableData.columns[columnName].tableName = tableName;
96
+ }
97
+ let propertiesConfig = this.generatePropertiesConfig(tableData.columns, titleProperty);
98
+ let titlePropertyDeclaration = titleProperty ? '\n let titleProperty = \''+titleProperty+'\';' : '';
99
+ let titlePropertyReturn = titleProperty ? '\n titleProperty,' : '';
100
+ let fieldsToRemoveFromEdit = ['id'];
101
+ if(tableData.columns['created_at']){
102
+ fieldsToRemoveFromEdit.push('created_at');
103
+ }
104
+ if(tableData.columns['updated_at']){
105
+ fieldsToRemoveFromEdit.push('updated_at');
106
+ }
107
+ let editPropertiesRemoval = this.getEditPropertiesRemoval(fieldsToRemoveFromEdit);
108
+ let listPropertiesDeclaration = this.getListPropertiesDeclaration(tableData.columns);
109
+ let needsSc = listPropertiesDeclaration.includes('sc.removeFromArray') || editPropertiesRemoval.includes('sc.removeFromArray');
110
+ let scRequire = needsSc ? '\nconst { sc } = require(\'@reldens/utils\');' : '';
111
+ let replacements = {
112
+ entityClassName,
113
+ propertiesConfig,
114
+ titlePropertyDeclaration,
115
+ titlePropertyReturn,
116
+ listPropertiesDeclaration,
117
+ editPropertiesRemoval,
118
+ scRequire
119
+ };
120
+ let entityContent = this.applyReplacements(entityTemplateContent, replacements);
121
+ let fileName = sc.kebabCase(tableName)+'-entity.js';
122
+ let filePath = FileHandler.joinPaths(this.entitiesFolder, fileName);
123
+ try {
124
+ FileHandler.writeFile(filePath, entityContent);
125
+ Logger.info('Generated entity file: '+filePath);
126
+ this.generatedEntities[tableName] = {
127
+ entityClassName,
128
+ entityFileName: fileName,
129
+ tableName,
130
+ titleProperty
131
+ };
132
+ return true;
133
+ } catch (error) {
134
+ ErrorManager.error('Failed to write entity file: '+error.message);
135
+ }
136
+ }
137
+
138
+ getEditPropertiesRemoval(fieldsToRemove)
139
+ {
140
+ if(1 === fieldsToRemove.length){
141
+ return 'let editProperties = [...showProperties];\n editProperties.splice(editProperties.indexOf(\''+fieldsToRemove[0]+'\'), 1);';
142
+ }
143
+ return 'let editProperties = sc.removeFromArray([...showProperties], [\''+fieldsToRemove.join('\', \'')+'\']);';
144
+ }
145
+
146
+ determineTitleProperty(columns)
147
+ {
148
+ if(columns['label']){
149
+ return 'label';
150
+ }
151
+ if(columns['title']){
152
+ return 'title';
153
+ }
154
+ if(columns['name']){
155
+ return 'name';
156
+ }
157
+ if(columns['key']){
158
+ return 'key';
159
+ }
160
+ return false;
161
+ }
162
+
163
+ generatePropertiesConfig(columns, titleProperty)
164
+ {
165
+ let propertiesConfig = [];
166
+ for(let columnName in columns){
167
+ let column = columns[columnName];
168
+ let propertyKey = titleProperty && columnName === titleProperty ? '[titleProperty]' : columnName;
169
+ if('id' === columnName){
170
+ propertiesConfig.push(columnName+': {}');
171
+ continue;
172
+ }
173
+ let props = this.getPropertyAttributes(column);
174
+ if(0 === props.length){
175
+ propertiesConfig.push(propertyKey+': {}');
176
+ continue;
177
+ }
178
+ propertiesConfig.push(propertyKey+': {\n '+props.join(',\n ')+'\n }');
179
+ }
180
+ return propertiesConfig.join(',\n ');
181
+ }
182
+
183
+ getPropertyAttributes(column)
184
+ {
185
+ let props = [];
186
+ let type = column.type.toLowerCase();
187
+ this.addTypeAttribute(props, column, type);
188
+ this.addRequiredAttribute(props, column);
189
+ return props;
190
+ }
191
+
192
+ addTypeAttribute(props, column, type)
193
+ {
194
+ if(this.isForeignKey(column)){
195
+ props.push('type: \'reference\'');
196
+ props.push('reference: \''+this.getReferenceTable(column)+'\'');
197
+ return;
198
+ }
199
+ if('date' === type || 'datetime' === type || 'timestamp' === type){
200
+ props.push('type: \'datetime\'');
201
+ return;
202
+ }
203
+ if('tinyint' === type){
204
+ props.push('type: \'boolean\'');
205
+ return;
206
+ }
207
+ if('enum' === type){
208
+ this.addEnumValues(props, column);
209
+ return;
210
+ }
211
+ if(type.includes('int')){
212
+ props.push('type: \'number\'');
213
+ return;
214
+ }
215
+ if('varchar' === type || 'char' === type){
216
+ return;
217
+ }
218
+ let jsType = TypeMapper.mapDbTypeToJsType(type);
219
+ if(jsType && 'string' !== jsType){
220
+ props.push('type: \''+jsType+'\'');
221
+ }
222
+ }
223
+
224
+ addEnumValues(props, column)
225
+ {
226
+ let enumValues = this.parseEnumValues(column.columnType);
227
+ if(0 === enumValues.length){
228
+ return;
229
+ }
230
+ let availableValues = [];
231
+ for(let i = 0; i < enumValues.length; i++){
232
+ availableValues.push('{value: '+(i+1)+', label: \''+enumValues[i]+'\'}');
233
+ }
234
+ props.push('availableValues: [\n '+availableValues.join(',\n ')+'\n ]');
235
+ }
236
+
237
+ parseEnumValues(columnType)
238
+ {
239
+ if(!columnType){
240
+ return [];
241
+ }
242
+ let match = columnType.match(/^enum\('(.+)'\)$/i);
243
+ if(!match){
244
+ return [];
245
+ }
246
+ let valuesString = match[1];
247
+ let values = valuesString.split('\',\'');
248
+ return values;
249
+ }
250
+
251
+ addRequiredAttribute(props, column)
252
+ {
253
+ if(!column.nullable && !column.default){
254
+ props.push('isRequired: true');
255
+ }
256
+ }
257
+
258
+ isForeignKey(column)
259
+ {
260
+ if(column.referencedTable){
261
+ return true;
262
+ }
263
+ return false;
264
+ }
265
+
266
+ getReferenceTable(column)
267
+ {
268
+ if(column.referencedTable){
269
+ return column.referencedTable;
270
+ }
271
+ let refTableName = column.name.replace('_id', '');
272
+ if(refTableName.includes('_')){
273
+ let parts = refTableName.split('_');
274
+ if(column.tableName && column.tableName === parts[0]){
275
+ refTableName = parts.slice(1).join('_');
276
+ }
277
+ }
278
+ return refTableName;
279
+ }
280
+
281
+ getListPropertiesDeclaration(columns)
282
+ {
283
+ let fieldsToRemove = this.getTextAndJsonFields(columns);
284
+ if(0 === fieldsToRemove.length){
285
+ return 'let listProperties = showProperties;';
286
+ }
287
+ if(1 === fieldsToRemove.length){
288
+ return 'let listProperties = [...showProperties];\n listProperties.splice(listProperties.indexOf(\''+fieldsToRemove[0]+'\'), 1);';
289
+ }
290
+ return 'let listProperties = sc.removeFromArray([...showProperties], [\''+fieldsToRemove.join('\', \'')+'\']);';
291
+ }
292
+
293
+ getTextAndJsonFields(columns)
294
+ {
295
+ let fieldsToRemove = [];
296
+ for(let columnName in columns){
297
+ let column = columns[columnName];
298
+ let type = column.type.toLowerCase();
299
+ if(['text', 'json', 'tinytext', 'mediumtext', 'longtext'].includes(type)){
300
+ fieldsToRemove.push(columnName);
301
+ }
302
+ }
303
+ return fieldsToRemove;
304
+ }
305
+
306
+ async generateModelFile(tableName, tableData, driverKey)
307
+ {
308
+ let modelTemplatePath = this.templates[driverKey];
309
+ if(!FileHandler.exists(modelTemplatePath)){
310
+ ErrorManager.error('Model template file not found: '+modelTemplatePath);
311
+ }
312
+ let modelTemplateContent;
313
+ try {
314
+ modelTemplateContent = FileHandler.fetchFileContents(modelTemplatePath);
315
+ } catch (error) {
316
+ ErrorManager.error('Failed to read model template file: '+error.message);
317
+ }
318
+ let modelClassName = sc.capitalizedCamelCase(tableName)+'Model';
319
+ let modelPropertiesList = Object.keys(tableData.columns).join(', ');
320
+ let modelPropertiesConstructor = Object.keys(tableData.columns)
321
+ .map(columnName => 'this.'+columnName+' = '+columnName+';')
322
+ .join('\n ');
323
+ let modelRelations = '';
324
+ let entityPropertiesDefinition = this.getEntityPropertiesDefinition(tableData.columns, driverKey);
325
+ let replacements = {
326
+ modelClassName,
327
+ tableName,
328
+ modelPropertiesList,
329
+ modelPropertiesConstructor,
330
+ modelRelations,
331
+ entityPropertiesDefinition
332
+ };
333
+ let modelContent = this.applyReplacements(modelTemplateContent, replacements);
334
+ let fileName = sc.kebabCase(tableName)+'-model.js';
335
+ let driverFolder = FileHandler.joinPaths(this.modelsFolder, driverKey);
336
+ let filePath = FileHandler.joinPaths(driverFolder, fileName);
337
+ try {
338
+ FileHandler.writeFile(filePath, modelContent);
339
+ Logger.info('Generated model file: '+filePath);
340
+ this.generatedEntities[tableName].modelClassName = modelClassName;
341
+ this.generatedEntities[tableName].modelFileName = fileName;
342
+ this.generatedEntities[tableName].driverKey = driverKey;
343
+ return true;
344
+ } catch (error) {
345
+ ErrorManager.error('Failed to write model file: '+error.message);
346
+ }
347
+ }
348
+
349
+ getEntityPropertiesDefinition(columns, driverKey)
350
+ {
351
+ if('mikro-orm' !== driverKey){
352
+ return '';
353
+ }
354
+ let entityProps = [];
355
+ for(let columnName in columns){
356
+ let column = columns[columnName];
357
+ let isPrimary = 'PRI' === column.key;
358
+ let type = TypeMapper.mapDbTypeToJsType(column.type);
359
+ let propDef = columnName+': { type: \''+type+'\'';
360
+ if(isPrimary){
361
+ propDef += ', primary: true';
362
+ }
363
+ if(column.nullable){
364
+ propDef += ', nullable: true';
365
+ }
366
+ propDef += ' }';
367
+ entityProps.push(propDef);
368
+ }
369
+ return entityProps.join(',\n ');
370
+ }
371
+
372
+ generateEntitiesConfigFile()
373
+ {
374
+ let configTemplatePath = this.templates['entities-config'];
375
+ if(!FileHandler.exists(configTemplatePath)){
376
+ ErrorManager.error('Entities config template file not found: '+configTemplatePath);
377
+ }
378
+ let configTemplateContent;
379
+ try {
380
+ configTemplateContent = FileHandler.fetchFileContents(configTemplatePath);
381
+ } catch (error) {
382
+ ErrorManager.error('Failed to read entities config template file: '+error.message);
383
+ }
384
+ if(!configTemplateContent || 'string' !== typeof configTemplateContent){
385
+ ErrorManager.error('Invalid entities config template content');
386
+ }
387
+ let requireStatements = this.getRequireStatements();
388
+ let entitiesConfigExport = this.getEntitiesConfigExport();
389
+ let replacements = {
390
+ requireStatements,
391
+ entitiesConfigExport
392
+ };
393
+ let configContent = this.applyReplacements(configTemplateContent, replacements);
394
+ try {
395
+ FileHandler.writeFile(this.entitiesConfigPath, configContent);
396
+ Logger.info('Generated entities config file: '+this.entitiesConfigPath);
397
+ return true;
398
+ } catch (error) {
399
+ ErrorManager.error('Failed to write entities config file: '+error.message);
400
+ }
401
+ }
402
+
403
+ getRequireStatements()
404
+ {
405
+ let requireStatements = [];
406
+ for(let tableName in this.generatedEntities){
407
+ let entity = this.generatedEntities[tableName];
408
+ requireStatements.push('const { '+entity.entityClassName+' } = require(\'./entities/'+entity.entityFileName.replace('.js', '')+'\');');
409
+ }
410
+ return requireStatements.join('\n');
411
+ }
412
+
413
+ getEntitiesConfigExport()
414
+ {
415
+ let entitiesConfigExport = [];
416
+ for(let tableName in this.generatedEntities){
417
+ let entity = this.generatedEntities[tableName];
418
+ let camelCaseKey = sc.camelCase(tableName);
419
+ entitiesConfigExport.push(camelCaseKey+': '+entity.entityClassName+'.propertiesConfig()');
420
+ }
421
+ return entitiesConfigExport.join(',\n ');
422
+ }
423
+
424
+ generateEntitiesTranslationsFile()
425
+ {
426
+ let translationsTemplatePath = this.templates['entities-translations'];
427
+ if(!FileHandler.exists(translationsTemplatePath)){
428
+ ErrorManager.error('Entities translations template file not found: '+translationsTemplatePath);
429
+ }
430
+ let translationsTemplateContent;
431
+ try {
432
+ translationsTemplateContent = FileHandler.fetchFileContents(translationsTemplatePath);
433
+ } catch (error) {
434
+ ErrorManager.error('Failed to read entities translations template file: '+error.message);
435
+ }
436
+ if(!translationsTemplateContent || 'string' !== typeof translationsTemplateContent){
437
+ ErrorManager.error('Invalid entities translations template content');
438
+ }
439
+ let labels = this.getTranslationLabels();
440
+ let translationsContent = translationsTemplateContent.replace(
441
+ /{{labels}}/g,
442
+ labels
443
+ );
444
+ try {
445
+ FileHandler.writeFile(this.entitiesTranslationsPath, translationsContent);
446
+ Logger.info('Generated entities translations file: '+this.entitiesTranslationsPath);
447
+ return true;
448
+ } catch (error) {
449
+ ErrorManager.error('Failed to write entities translations file: '+error.message);
450
+ }
451
+ }
452
+
453
+ getTranslationLabels()
454
+ {
455
+ let labels = [];
456
+ for(let tableName in this.generatedEntities){
457
+ let humanReadable = tableName
458
+ .split('_')
459
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
460
+ .join(' ');
461
+ labels.push('\''+tableName+'\': \''+humanReadable+'\'');
462
+ }
463
+ return labels.join(',\n ');
464
+ }
465
+
466
+ generateRegisteredModelsFile(driverKey)
467
+ {
468
+ let registeredTemplatePath = this.templates['registered-models'];
469
+ if(!FileHandler.exists(registeredTemplatePath)){
470
+ ErrorManager.error('Registered models template file not found: '+registeredTemplatePath);
471
+ }
472
+ let registeredTemplateContent;
473
+ try {
474
+ registeredTemplateContent = FileHandler.fetchFileContents(registeredTemplatePath);
475
+ } catch (error) {
476
+ ErrorManager.error('Failed to read registered models template file: '+error.message);
477
+ }
478
+ if(!registeredTemplateContent || 'string' !== typeof registeredTemplateContent){
479
+ ErrorManager.error('Invalid registered models template content');
480
+ }
481
+ let registeredModels = this.getRegisteredModels(driverKey);
482
+ let registeredEntitiesObject = this.getRegisteredEntitiesObject(driverKey);
483
+ let replacements = {
484
+ registeredModels,
485
+ registeredEntitiesObject
486
+ };
487
+ let registeredContent = this.applyReplacements(registeredTemplateContent, replacements);
488
+ let driverFolder = FileHandler.joinPaths(this.modelsFolder, driverKey);
489
+ let filePath = FileHandler.joinPaths(driverFolder, 'registered-models-'+driverKey+'.js');
490
+ try {
491
+ FileHandler.writeFile(filePath, registeredContent);
492
+ Logger.info('Generated registered models file: '+filePath);
493
+ return true;
494
+ } catch (error) {
495
+ ErrorManager.error('Failed to write registered models file: '+error.message);
496
+ }
497
+ }
498
+
499
+ getRegisteredModels(driverKey)
500
+ {
501
+ let registeredModels = [];
502
+ for(let tableName in this.generatedEntities){
503
+ let entity = this.generatedEntities[tableName];
504
+ if(entity.driverKey === driverKey){
505
+ registeredModels.push('const { '+entity.modelClassName+' } = require(\'./'+entity.modelFileName.replace('.js', '')+'\');');
506
+ }
507
+ }
508
+ return registeredModels.join('\n');
509
+ }
510
+
511
+ getRegisteredEntitiesObject(driverKey)
512
+ {
513
+ let registeredEntitiesObject = [];
514
+ for(let tableName in this.generatedEntities){
515
+ let entity = this.generatedEntities[tableName];
516
+ if(entity.driverKey === driverKey){
517
+ let camelCaseKey = sc.camelCase(tableName);
518
+ registeredEntitiesObject.push(camelCaseKey+': '+entity.modelClassName);
519
+ }
520
+ }
521
+ return registeredEntitiesObject.join(',\n ');
522
+ }
523
+
524
+ applyReplacements(content, replacements)
525
+ {
526
+ let result = content;
527
+ for(let placeholder in replacements){
528
+ result = result.replace(
529
+ new RegExp('{{'+placeholder+'}}', 'g'),
530
+ replacements[placeholder]
531
+ );
532
+ }
533
+ return result;
534
+ }
535
+
536
+ async generate()
537
+ {
538
+ if(!this.server){
539
+ this.createServer();
540
+ }
541
+ try {
542
+ await this.server.connect();
543
+ } catch (error) {
544
+ ErrorManager.error('Failed to connect to database: '+error.message);
545
+ }
546
+ let tables = await this.server.fetchEntitiesFromDatabase();
547
+ if(!tables){
548
+ ErrorManager.error('Failed to fetch tables from database.');
549
+ }
550
+ this.ensureFoldersExist();
551
+ let driverKey = sc.get(this.connectionData, 'driver', 'objection-js');
552
+ for(let tableName in tables){
553
+ let tableData = tables[tableName];
554
+ await this.generateEntityFile(tableName, tableData);
555
+ await this.generateModelFile(tableName, tableData, driverKey);
556
+ }
557
+ this.generateEntitiesConfigFile();
558
+ this.generateEntitiesTranslationsFile();
559
+ this.generateRegisteredModelsFile(driverKey);
560
+ return true;
561
+ }
562
+
563
+ createServer()
564
+ {
565
+ let driverKey = sc.get(this.connectionData, 'driver', 'objection-js');
566
+ let driverClassMapped = this.driverMap[driverKey];
567
+ if(!driverClassMapped){
568
+ ErrorManager.error('Unsupported driver: '+driverKey);
569
+ }
570
+ this.server = new driverClassMapped({
571
+ client: sc.get(this.connectionData, 'client', 'mysql2'),
572
+ config: {
573
+ user: sc.get(this.connectionData, 'user', ''),
574
+ password: sc.get(this.connectionData, 'password', ''),
575
+ database: sc.get(this.connectionData, 'database', ''),
576
+ host: sc.get(this.connectionData, 'host', 'localhost'),
577
+ port: sc.get(this.connectionData, 'port', 3306)
578
+ }
579
+ });
580
+ }
581
+
582
+ }
583
+
584
+ module.exports.EntitiesGenerator = EntitiesGenerator;
@@ -0,0 +1,26 @@
1
+ /**
2
+ *
3
+ * Reldens - EntityProperties
4
+ *
5
+ */
6
+
7
+ const { Logger } = require('@reldens/utils');
8
+
9
+ class EntityProperties
10
+ {
11
+
12
+ static propertiesDefinition()
13
+ {
14
+ Logger.alert('Method not implemented propertiesDefinition().');
15
+ return {};
16
+ }
17
+
18
+ static propertiesConfig(extraProps)
19
+ {
20
+ Logger.alert('Method not implemented propertiesConfig().', extraProps);
21
+ return {};
22
+ }
23
+
24
+ }
25
+
26
+ module.exports.EntityProperties = EntityProperties;
@@ -0,0 +1,13 @@
1
+ /**
2
+ *
3
+ * Reldens - Entities Config
4
+ *
5
+ */
6
+
7
+ {{requireStatements}}
8
+
9
+ let entitiesConfig = {
10
+ {{entitiesConfigExport}}
11
+ };
12
+
13
+ module.exports.entitiesConfig = entitiesConfig;
@@ -0,0 +1,11 @@
1
+ /**
2
+ *
3
+ * Reldens - Entities Translations
4
+ *
5
+ */
6
+
7
+ module.exports.entitiesTranslations = {
8
+ labels: {
9
+ {{labels}}
10
+ }
11
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ *
3
+ * Reldens - {{entityClassName}}
4
+ *
5
+ */
6
+
7
+ const { EntityProperties } = require('@reldens/storage');{{scRequire}}
8
+
9
+ class {{entityClassName}} extends EntityProperties
10
+ {
11
+
12
+ static propertiesConfig(extraProps)
13
+ {{{titlePropertyDeclaration}}
14
+ let properties = {
15
+ {{propertiesConfig}}
16
+ };
17
+ let showProperties = Object.keys(properties);
18
+ {{editPropertiesRemoval}}
19
+ {{listPropertiesDeclaration}}
20
+ return {
21
+ showProperties,
22
+ editProperties,
23
+ listProperties,
24
+ filterProperties: listProperties,
25
+ properties,{{titlePropertyReturn}}
26
+ ...extraProps
27
+ };
28
+ }
29
+
30
+ }
31
+
32
+ module.exports.{{entityClassName}} = {{entityClassName}};
@@ -0,0 +1,37 @@
1
+ /**
2
+ *
3
+ * Reldens - {{modelClassName}}
4
+ *
5
+ */
6
+
7
+ const { MikroOrmCore } = require('@reldens/storage');
8
+ const { EntitySchema } = MikroOrmCore;
9
+
10
+ class {{modelClassName}}
11
+ {
12
+
13
+ constructor({{modelPropertiesList}})
14
+ {
15
+ {{modelPropertiesConstructor}}
16
+ }
17
+
18
+ static createByProps(props)
19
+ {
20
+ const {{{modelPropertiesList}}} = props;
21
+ return new this({{modelPropertiesList}});
22
+ }
23
+ {{modelRelations}}
24
+ }
25
+
26
+ const schema = new EntitySchema({
27
+ class: {{modelClassName}},
28
+ properties: {
29
+ {{entityPropertiesDefinition}}
30
+ },
31
+ });
32
+
33
+ module.exports = {
34
+ {{modelClassName}},
35
+ entity: {{modelClassName}},
36
+ schema: schema
37
+ };
@@ -0,0 +1,19 @@
1
+ /**
2
+ *
3
+ * Reldens - {{modelClassName}}
4
+ *
5
+ */
6
+
7
+ const { ObjectionJsRawModel } = require('@reldens/storage');
8
+
9
+ class {{modelClassName}} extends ObjectionJsRawModel
10
+ {
11
+
12
+ static get tableName()
13
+ {
14
+ return '{{tableName}}';
15
+ }
16
+ {{modelRelations}}
17
+ }
18
+
19
+ module.exports.{{modelClassName}} = {{modelClassName}};
@@ -0,0 +1,19 @@
1
+ /**
2
+ *
3
+ * Reldens - Registered Models
4
+ *
5
+ */
6
+
7
+ {{registeredModels}}
8
+ const { entitiesConfig } = require('../../entities-config');
9
+ const { entitiesTranslations } = require('../../entities-translations');
10
+
11
+ let rawRegisteredEntities = {
12
+ {{registeredEntitiesObject}}
13
+ };
14
+
15
+ module.exports.rawRegisteredEntities = rawRegisteredEntities;
16
+
17
+ module.exports.entitiesConfig = entitiesConfig;
18
+
19
+ module.exports.entitiesTranslations = entitiesTranslations;
@@ -6,10 +6,11 @@
6
6
 
7
7
  const { MikroOrmDriver } = require('./mikro-orm-driver');
8
8
  const { BaseDataServer } = require('../base-data-server');
9
+ const { MySQLTablesProvider } = require('../mysql-tables-provider');
9
10
  const { MikroORM } = require('@mikro-orm/core');
10
11
  const { MySqlDriver } = require('@mikro-orm/mysql');
11
12
  const { MongoDriver } = require('@mikro-orm/mongodb');
12
- const { Logger, ErrorManager } = require('@reldens/utils');
13
+ const { ErrorManager, Logger, sc } = require('@reldens/utils');
13
14
 
14
15
  class MikroOrmDataServer extends BaseDataServer
15
16
  {
@@ -22,9 +23,9 @@ class MikroOrmDataServer extends BaseDataServer
22
23
 
23
24
  constructor(props)
24
25
  {
25
- // @TODO - BETA - Finish implementation.
26
26
  super(props);
27
27
  this.entitiesPath = props.entitiesPath;
28
+ this.warnWhenNoEntities = Boolean(props.warnWhenNoEntities);
28
29
  }
29
30
 
30
31
  async connect()
@@ -36,7 +37,7 @@ class MikroOrmDataServer extends BaseDataServer
36
37
  driver: this.clientsMapped[(this.client || 'mongodb')],
37
38
  dbName: this.config.database,
38
39
  clientUrl: this.connectString,
39
- allowGlobalContext: true
40
+ allowGlobalContext: true,
40
41
  };
41
42
  if(this.rawEntities){
42
43
  providedSetup.entities = Object.values(this.rawEntities);
@@ -47,6 +48,7 @@ class MikroOrmDataServer extends BaseDataServer
47
48
  if(this.entitiesPath){
48
49
  providedSetup.entities = this.entitiesPath;
49
50
  }
51
+ providedSetup.discovery = {warnWhenNoEntities: this.warnWhenNoEntities};
50
52
  this.orm = await MikroORM.init(providedSetup);
51
53
  this.initialized = Date.now();
52
54
  return this.initialized;
@@ -85,8 +87,43 @@ class MikroOrmDataServer extends BaseDataServer
85
87
 
86
88
  async rawQuery(content)
87
89
  {
88
- // @TODO - BETA - Implement.
89
- ErrorManager.error('MikroORM rawQuery() not implemented.');
90
+ try {
91
+ let queryResult = await this.orm.em.execute(content);
92
+ if(!sc.isArray(queryResult) || 0 === queryResult.length){
93
+ return false;
94
+ }
95
+ return queryResult;
96
+ } catch(error) {
97
+ Logger.error('Raw query failed: '+error.message);
98
+ return false;
99
+ }
100
+ }
101
+
102
+ async fetchEntitiesFromDatabase()
103
+ {
104
+ if(!this.initialized){
105
+ Logger.error('Connection was not initialized, please use the connect method first.');
106
+ return false;
107
+ }
108
+ try {
109
+ if('mysql' === this.client){
110
+ return await MySQLTablesProvider.fetchTables(this);
111
+ }
112
+ if('mongodb' === this.client){
113
+ let tables = {};
114
+ let collections = await this.orm.em.getDriver().getConnection().db().listCollections().toArray();
115
+ for(let collection of collections){
116
+ tables[collection.name] = {
117
+ name: collection.name,
118
+ columns: {}
119
+ };
120
+ }
121
+ return tables;
122
+ }
123
+ ErrorManager.error('Unsupported client type: '+this.client);
124
+ } catch(error) {
125
+ ErrorManager.error('Failed to fetch tables from database: ' + error.message);
126
+ }
90
127
  }
91
128
 
92
129
  }
@@ -0,0 +1,87 @@
1
+ /**
2
+ *
3
+ * Reldens - MySQLTablesProvider
4
+ *
5
+ */
6
+
7
+ const { ErrorManager, sc } = require('@reldens/utils');
8
+
9
+ class MySQLTablesProvider
10
+ {
11
+
12
+ static async fetchTables(server)
13
+ {
14
+ let tablesQuery =
15
+ 'SELECT ' +
16
+ ' TABLE_NAME, ' +
17
+ ' COLUMN_NAME, ' +
18
+ ' DATA_TYPE, ' +
19
+ ' CHARACTER_MAXIMUM_LENGTH, ' +
20
+ ' COLUMN_TYPE, ' +
21
+ ' IS_NULLABLE, ' +
22
+ ' COLUMN_KEY, ' +
23
+ ' EXTRA, ' +
24
+ ' COLUMN_DEFAULT ' +
25
+ 'FROM ' +
26
+ ' information_schema.COLUMNS ' +
27
+ 'WHERE ' +
28
+ ' TABLE_SCHEMA = \'' + server.config.database + '\' ' +
29
+ 'ORDER BY ' +
30
+ ' TABLE_NAME, ORDINAL_POSITION';
31
+ try {
32
+ let result = await server.rawQuery(tablesQuery);
33
+ if(!sc.isArray(result) || 0 === result.length){
34
+ ErrorManager.error('No tables found in the database.');
35
+ }
36
+ let tables = {};
37
+ for(let row of result){
38
+ if(!tables[row.TABLE_NAME]){
39
+ tables[row.TABLE_NAME] = {
40
+ name: row.TABLE_NAME,
41
+ columns: {}
42
+ };
43
+ }
44
+ tables[row.TABLE_NAME].columns[row.COLUMN_NAME] = {
45
+ name: row.COLUMN_NAME,
46
+ type: row.DATA_TYPE,
47
+ columnType: row.COLUMN_TYPE,
48
+ length: row.CHARACTER_MAXIMUM_LENGTH,
49
+ nullable: 'YES' === row.IS_NULLABLE,
50
+ key: row.COLUMN_KEY,
51
+ extra: row.EXTRA,
52
+ default: row.COLUMN_DEFAULT
53
+ };
54
+ }
55
+ let foreignKeysQuery =
56
+ 'SELECT ' +
57
+ ' TABLE_NAME, ' +
58
+ ' COLUMN_NAME, ' +
59
+ ' REFERENCED_TABLE_NAME, ' +
60
+ ' REFERENCED_COLUMN_NAME ' +
61
+ 'FROM ' +
62
+ ' information_schema.KEY_COLUMN_USAGE ' +
63
+ 'WHERE ' +
64
+ ' TABLE_SCHEMA = \'' + server.config.database + '\' ' +
65
+ ' AND REFERENCED_TABLE_NAME IS NOT NULL';
66
+
67
+ let foreignKeysResult = await server.rawQuery(foreignKeysQuery);
68
+ if(sc.isArray(foreignKeysResult) && 0 < foreignKeysResult.length){
69
+ for(let row of foreignKeysResult){
70
+ if(
71
+ tables[row.TABLE_NAME] &&
72
+ tables[row.TABLE_NAME].columns[row.COLUMN_NAME]
73
+ ){
74
+ tables[row.TABLE_NAME].columns[row.COLUMN_NAME].referencedTable = row.REFERENCED_TABLE_NAME;
75
+ tables[row.TABLE_NAME].columns[row.COLUMN_NAME].referencedColumn = row.REFERENCED_COLUMN_NAME;
76
+ }
77
+ }
78
+ }
79
+ return tables;
80
+ } catch(error) {
81
+ ErrorManager.error('Failed to fetch tables from database: '+error.message);
82
+ }
83
+ }
84
+
85
+ }
86
+
87
+ module.exports.MySQLTablesProvider = MySQLTablesProvider;
@@ -6,6 +6,7 @@
6
6
 
7
7
  const { BaseDataServer } = require('../base-data-server');
8
8
  const { ObjectionJsDriver } = require('./objection-js-driver');
9
+ const { MySQLTablesProvider } = require('../mysql-tables-provider');
9
10
  const { Model } = require('objection');
10
11
  const Knex = require('knex');
11
12
  const { Logger } = require('@reldens/utils');
@@ -72,7 +73,17 @@ class ObjectionJsDataServer extends BaseDataServer
72
73
 
73
74
  async rawQuery(content)
74
75
  {
75
- return await this.knex.raw(content);
76
+ let result = await this.knex.raw(content);
77
+ return result[0] || false;
78
+ }
79
+
80
+ async fetchEntitiesFromDatabase()
81
+ {
82
+ if(!this.initialized){
83
+ Logger.error('Connection was not initialized, please use the connect method first.');
84
+ return false;
85
+ }
86
+ return await MySQLTablesProvider.fetchTables(this);
76
87
  }
77
88
 
78
89
  }
@@ -86,11 +86,6 @@ class ObjectionJsDriver extends BaseDriver
86
86
  return this.appendFilters(queryBuilder, filters).delete();
87
87
  }
88
88
 
89
- deleteBy(filters)
90
- {
91
- this.delete(filters);
92
- }
93
-
94
89
  deleteById(id)
95
90
  {
96
91
  return this.queryBuilder().deleteById(id);
@@ -0,0 +1,49 @@
1
+ /**
2
+ *
3
+ * Reldens - TypeMapper
4
+ *
5
+ */
6
+
7
+ class TypeMapper
8
+ {
9
+
10
+ mapDbTypeToJsType(dbType)
11
+ {
12
+ let typeMap = {
13
+ 'int': 'number',
14
+ 'integer': 'number',
15
+ 'tinyint': 'number',
16
+ 'smallint': 'number',
17
+ 'mediumint': 'number',
18
+ 'bigint': 'number',
19
+ 'decimal': 'number',
20
+ 'float': 'number',
21
+ 'double': 'number',
22
+ 'varchar': 'string',
23
+ 'char': 'string',
24
+ 'text': 'string',
25
+ 'tinytext': 'string',
26
+ 'mediumtext': 'string',
27
+ 'longtext': 'string',
28
+ 'date': 'Date',
29
+ 'datetime': 'Date',
30
+ 'timestamp': 'Date',
31
+ 'time': 'string',
32
+ 'year': 'number',
33
+ 'boolean': 'boolean',
34
+ 'bool': 'boolean',
35
+ 'bit': 'boolean',
36
+ 'json': 'object',
37
+ 'binary': 'Buffer',
38
+ 'varbinary': 'Buffer',
39
+ 'blob': 'Buffer',
40
+ 'tinyblob': 'Buffer',
41
+ 'mediumblob': 'Buffer',
42
+ 'longblob': 'Buffer'
43
+ };
44
+ return typeMap[dbType.toLowerCase()];
45
+ }
46
+
47
+ }
48
+
49
+ module.exports.TypeMapper = new TypeMapper();
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "@reldens/storage",
3
3
  "scope": "@reldens",
4
- "version": "0.31.0",
4
+ "version": "0.33.0",
5
5
  "description": "Reldens - Storage",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",
8
8
  "homepage": "https://github.com/damian-pastorini/reldens-storage",
9
9
  "source": true,
10
10
  "main": "index.js",
11
+ "bin": {
12
+ "reldens-storage": "./bin/reldens-storage.js"
13
+ },
11
14
  "repository": {
12
15
  "type": "git",
13
16
  "url": "https://github.com/damian-pastorini/reldens-storage.git"
@@ -44,13 +47,14 @@
44
47
  "url": "https://github.com/damian-pastorini/reldens-storage/issues"
45
48
  },
46
49
  "dependencies": {
47
- "@mikro-orm/core": "^6.4.0",
48
- "@mikro-orm/mongodb": "^6.4.0",
49
- "@mikro-orm/mysql": "^6.4.0",
50
- "@reldens/utils": "^0.39.0",
50
+ "@mikro-orm/core": "^6.4.15",
51
+ "@mikro-orm/mongodb": "^6.4.15",
52
+ "@mikro-orm/mysql": "^6.4.15",
53
+ "@reldens/utils": "^0.47.0",
54
+ "@reldens/server-utils": "^0.15.0",
51
55
  "knex": "^3.1.0",
52
56
  "mysql": "^2.18.1",
53
- "mysql2": "^3.11.5",
57
+ "mysql2": "^3.14.1",
54
58
  "objection": "^3.1.5"
55
59
  }
56
60
  }