@reldens/storage 0.104.0 → 0.105.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.
@@ -9,6 +9,7 @@ const { Logger, sc } = require('@reldens/utils');
9
9
  const { ObjectionJsDataServer } = require('./objection-js/objection-js-data-server');
10
10
  const { MikroOrmDataServer } = require('./mikro-orm/mikro-orm-data-server');
11
11
  const { PrismaDataServer } = require('./prisma/prisma-data-server');
12
+ const { PrismaRelationsMetadataExtractor } = require('./prisma/prisma-relations-metadata-extractor');
12
13
  const { EntitiesGeneration } = require('./generators/entities-generation');
13
14
  const { ModelsGeneration } = require('./generators/models-generation');
14
15
  const { EntitiesConfigGeneration } = require('./generators/entities-config-generation');
@@ -165,125 +166,6 @@ class EntitiesGenerator
165
166
  return false;
166
167
  }
167
168
 
168
- extractPrismaRelationsMetadata()
169
- {
170
- if(!this.prismaClient){
171
- Logger.warning('Missing PrismaClient.');
172
- return;
173
- }
174
- if(!this.server || !(this.server instanceof PrismaDataServer)){
175
- Logger.warning('Server is not a PrismaDataServer instance.');
176
- return;
177
- }
178
- try {
179
- let dmmf = this.prismaClient._runtimeDataModel || this.prismaClient._dmmf?.datamodel;
180
- if(!dmmf || !dmmf.models){
181
- Logger.warning('Could not extract Prisma DMMF data for relations metadata.');
182
- return;
183
- }
184
- if(sc.isArray(dmmf.models)){
185
- this.processArrayModels(dmmf.models);
186
- return;
187
- }
188
- this.processObjectModels(dmmf.models);
189
- } catch(error) {
190
- Logger.warning('Failed to extract Prisma relations metadata: ' + error.message);
191
- }
192
- }
193
-
194
- processArrayModels(models)
195
- {
196
- for(let model of models){
197
- if(!model){
198
- Logger.error('Missing model.');
199
- continue;
200
- }
201
- if(!model.name){
202
- Logger.error('Missing model name.', model);
203
- continue;
204
- }
205
- if(!sc.isArray(model.fields)){
206
- Logger.error('Model fields not an array.', model);
207
- continue;
208
- }
209
- this.processModelForRelations(model.name, model, models);
210
- }
211
- Logger.info('Extracted relations metadata for '+Object.keys(this.prismaRelationsMetadata).length+' models.');
212
- }
213
-
214
- processObjectModels(models)
215
- {
216
- for(let modelName of Object.keys(models)){
217
- let model = models[modelName];
218
- if(!model){
219
- Logger.error('Missing model.');
220
- continue;
221
- }
222
- if(!sc.isArray(model.fields)){
223
- Logger.error('Model fields not an array.', model);
224
- continue;
225
- }
226
- this.processModelForRelations(modelName, model, models);
227
- }
228
- Logger.info('Extracted relations metadata for '+Object.keys(this.prismaRelationsMetadata).length+' models.');
229
- }
230
-
231
- processModelForRelations(modelName, model, allModels)
232
- {
233
- let tableName = modelName.toLowerCase();
234
- this.prismaRelationsMetadata[tableName] = {};
235
- for(let field of model.fields){
236
- if('object' !== field.kind){
237
- continue;
238
- }
239
- let relationType = 'many';
240
- if(!field.isList){
241
- relationType = this.inferRelationType(field, model, allModels, modelName);
242
- }
243
- this.prismaRelationsMetadata[tableName][field.name] = {
244
- type: relationType,
245
- model: field.type,
246
- isList: field.isList,
247
- isOptional: field.isOptional,
248
- relationName: field.relationName
249
- };
250
- }
251
- }
252
-
253
- inferRelationType(field, model, allModels, modelName)
254
- {
255
- if(field.isList){
256
- return 'many';
257
- }
258
- let relatedModel = this.findRelatedModel(allModels, field.type);
259
- if(!relatedModel){
260
- return 'one';
261
- }
262
- let backReference = relatedModel.fields.find(f =>
263
- 'object' === f.kind &&
264
- f.type === modelName &&
265
- f.relationName === field.relationName
266
- );
267
- if(!backReference){
268
- return 'one';
269
- }
270
- if(field.relationFromFields && 0 < field.relationFromFields.length){
271
- return 'one';
272
- }
273
- if(backReference.relationFromFields && 0 < backReference.relationFromFields.length){
274
- return 'one';
275
- }
276
- return 'one';
277
- }
278
-
279
- findRelatedModel(allModels, fieldType)
280
- {
281
- if(sc.isArray(allModels)){
282
- return allModels.find(m => m.name === fieldType);
283
- }
284
- return allModels[fieldType];
285
- }
286
-
287
169
  filterTablesToGenerate(tables, driverKey)
288
170
  {
289
171
  let filteredTables = {};
@@ -386,7 +268,11 @@ class EntitiesGenerator
386
268
  this.prismaClient = this.server.prisma;
387
269
  }
388
270
  //Logger.debug('Extract Prisma relations metadata.');
389
- this.extractPrismaRelationsMetadata();
271
+ this.prismaRelationsMetadata = new PrismaRelationsMetadataExtractor({
272
+ prismaClient: this.prismaClient,
273
+ projectRoot: this.server ? this.server.projectRoot : false,
274
+ projectPath: this.projectPath
275
+ }).extract();
390
276
  }
391
277
  let tables = await this.server.fetchEntitiesFromDatabase();
392
278
  if(!tables){
@@ -17,21 +17,10 @@ class PrismaMetadataLoader
17
17
 
18
18
  getDmmf()
19
19
  {
20
- try {
21
- if(this.prisma._dmmf){
22
- return this.prisma._dmmf.datamodel;
23
- }
24
- if(this.prisma._getDmmf && sc.isFunction(this.prisma._getDmmf)){
25
- return this.prisma._getDmmf().datamodel;
26
- }
27
- if(this.prisma._runtimeDataModel){
28
- return this.prisma._runtimeDataModel;
29
- }
30
- return null;
31
- } catch(error) {
32
- Logger.warning('Failed to access DMMF: '+error.message);
33
- return null;
20
+ if(this.prisma && this.prisma._runtimeDataModel){
21
+ return this.prisma._runtimeDataModel;
34
22
  }
23
+ return null;
35
24
  }
36
25
 
37
26
  loadModelMetadata(tableName)
@@ -0,0 +1,137 @@
1
+ /**
2
+ *
3
+ * Reldens - PrismaRelationsMetadataExtractor
4
+ *
5
+ * Builds the per-model relation metadata ({type: 'many'|'one', model, isList, isOptional, relationName})
6
+ * consumed by ModelsGeneration to emit each Prisma model's relationTypes. The DMMF access is reused from
7
+ * PrismaMetadataLoader.getDmmf(). Cardinality (list vs single) is read from schema.prisma's list markers
8
+ * ("[]") because Prisma 7's pruned _runtimeDataModel no longer exposes field.isList.
9
+ *
10
+ */
11
+
12
+ const { PrismaMetadataLoader } = require('./prisma-metadata-loader');
13
+ const { FileHandler } = require('@reldens/server-utils');
14
+ const { Logger, sc } = require('@reldens/utils');
15
+
16
+ class PrismaRelationsMetadataExtractor
17
+ {
18
+
19
+ constructor(props)
20
+ {
21
+ this.prismaClient = sc.get(props, 'prismaClient', false);
22
+ this.projectRoot = sc.get(props, 'projectRoot', false);
23
+ this.projectPath = sc.get(props, 'projectPath', false);
24
+ this.metadataLoader = new PrismaMetadataLoader({prisma: this.prismaClient});
25
+ this.relationsMetadata = {};
26
+ this.schemaListRelations = {};
27
+ }
28
+
29
+ extract()
30
+ {
31
+ if(!this.prismaClient){
32
+ Logger.warning('Missing PrismaClient for relations metadata extraction.');
33
+ return this.relationsMetadata;
34
+ }
35
+ let dmmf = this.metadataLoader.getDmmf();
36
+ if(!dmmf || !dmmf.models){
37
+ Logger.warning('Could not access Prisma DMMF for relations metadata.');
38
+ return this.relationsMetadata;
39
+ }
40
+ this.schemaListRelations = this.resolveSchemaListRelations();
41
+ this.processModels(dmmf.models);
42
+ Logger.info('Extracted relations metadata for '+Object.keys(this.relationsMetadata).length+' models.');
43
+ return this.relationsMetadata;
44
+ }
45
+
46
+ processModels(models)
47
+ {
48
+ if(sc.isArray(models)){
49
+ for(let model of models){
50
+ this.processModel(sc.get(model, 'name', ''), model);
51
+ }
52
+ return;
53
+ }
54
+ for(let modelName of Object.keys(models)){
55
+ this.processModel(modelName, models[modelName]);
56
+ }
57
+ }
58
+
59
+ processModel(modelName, model)
60
+ {
61
+ if('' === modelName || !sc.isArray(sc.get(model, 'fields', false))){
62
+ return;
63
+ }
64
+ let tableName = modelName.toLowerCase();
65
+ this.relationsMetadata[tableName] = {};
66
+ for(let field of model.fields){
67
+ if('object' !== field.kind){
68
+ continue;
69
+ }
70
+ let isListRelation = true === sc.get(sc.get(this.schemaListRelations, tableName, {}), field.name, false);
71
+ this.relationsMetadata[tableName][field.name] = {
72
+ type: isListRelation ? 'many' : 'one',
73
+ model: field.type,
74
+ isList: isListRelation,
75
+ isOptional: field.isOptional,
76
+ relationName: field.relationName
77
+ };
78
+ }
79
+ }
80
+
81
+ resolveSchemaPath()
82
+ {
83
+ let candidatePaths = [];
84
+ if(this.projectRoot){
85
+ candidatePaths.push(FileHandler.joinPaths(this.projectRoot, 'prisma', 'schema.prisma'));
86
+ }
87
+ candidatePaths.push(FileHandler.joinPaths(process.cwd(), 'prisma', 'schema.prisma'));
88
+ if(this.projectPath){
89
+ candidatePaths.push(FileHandler.joinPaths(this.projectPath, 'prisma', 'schema.prisma'));
90
+ }
91
+ for(let candidatePath of candidatePaths){
92
+ if(FileHandler.exists(candidatePath)){
93
+ return candidatePath;
94
+ }
95
+ }
96
+ return false;
97
+ }
98
+
99
+ resolveSchemaListRelations()
100
+ {
101
+ let listRelations = {};
102
+ let schemaPath = this.resolveSchemaPath();
103
+ if(!schemaPath){
104
+ Logger.warning('Prisma schema.prisma not found; relation cardinality may fall back to one.');
105
+ return listRelations;
106
+ }
107
+ let schemaContent = FileHandler.readFile(schemaPath);
108
+ if(!schemaContent){
109
+ return listRelations;
110
+ }
111
+ let currentModel = '';
112
+ for(let schemaLine of schemaContent.toString().split('\n')){
113
+ let line = schemaLine.trim();
114
+ let modelMatch = line.match(/^model\s+(\w+)\s*\{/);
115
+ if(modelMatch){
116
+ currentModel = modelMatch[1].toLowerCase();
117
+ listRelations[currentModel] = {};
118
+ continue;
119
+ }
120
+ if('' === currentModel){
121
+ continue;
122
+ }
123
+ if('}' === line){
124
+ currentModel = '';
125
+ continue;
126
+ }
127
+ let listFieldMatch = line.match(/^(\w+)\s+\w+\[\]/);
128
+ if(listFieldMatch){
129
+ listRelations[currentModel][listFieldMatch[1]] = true;
130
+ }
131
+ }
132
+ return listRelations;
133
+ }
134
+
135
+ }
136
+
137
+ module.exports.PrismaRelationsMetadataExtractor = PrismaRelationsMetadataExtractor;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/storage",
3
3
  "scope": "@reldens",
4
- "version": "0.104.0",
4
+ "version": "0.105.0",
5
5
  "description": "Reldens - Storage",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",