@reldens/storage 0.51.0 → 0.53.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.
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  const { PrismaSchemaGenerator } = require('../lib/prisma/prisma-schema-generator');
10
+ const { FileHandler } = require('@reldens/server-utils');
10
11
  const { Logger } = require('@reldens/utils');
11
12
 
12
13
  let args = process.argv.slice(2);
@@ -70,7 +71,10 @@ if(!connectionData.config.user || !connectionData.config.database){
70
71
  process.exit();
71
72
  }
72
73
 
73
- let generator = new PrismaSchemaGenerator({...connectionData, prismaSchemaPath: projectPath+'/prisma'});
74
+ let generator = new PrismaSchemaGenerator({
75
+ ...connectionData,
76
+ prismaSchemaPath: FileHandler.joinPaths(projectPath, 'prisma')
77
+ });
74
78
 
75
79
  generator.generate().then((success) => {
76
80
  if(!success){
@@ -47,6 +47,8 @@ class EntitiesGenerator
47
47
  this.isOverride = sc.get(props, 'isOverride', false);
48
48
  this.generatedEntities = {};
49
49
  this.existingEntities = {};
50
+ this.existingEntityFields = {};
51
+ this.existingModels = {};
50
52
  }
51
53
 
52
54
  async generateEntityFile(tableName, tableData)
@@ -57,7 +59,7 @@ class EntitiesGenerator
57
59
  return false;
58
60
  }
59
61
  let entityTemplateContent = FileHandler.fetchFileContents(entityTemplatePath);
60
- if(!entityTemplateContent) {
62
+ if(!entityTemplateContent){
61
63
  Logger.critical('Failed to read entity template file: '+entityTemplatePath);
62
64
  return false;
63
65
  }
@@ -158,7 +160,7 @@ class EntitiesGenerator
158
160
  let propertiesConfig = [];
159
161
  for(let columnName of Object.keys(columns)){
160
162
  let column = columns[columnName];
161
- let propertyKey = titleProperty && columnName === titleProperty ? '[titleProperty]' : columnName;
163
+ let propertyKey = titleProperty && titleProperty === columnName ? '[titleProperty]' : columnName;
162
164
  if('id' === columnName){
163
165
  propertiesConfig.push(columnName+': {}');
164
166
  continue;
@@ -195,7 +197,7 @@ class EntitiesGenerator
195
197
  props.push('type: \'datetime\'');
196
198
  return;
197
199
  }
198
- if('text' === type || 'longtext' === type || 'mediumtext' === type || 'tinytext' === type){
200
+ if('text' === type || 'longtext' === type || 'mediumtext' === type || 'tinytext' === type || 'json' === type){
199
201
  props.push('type: \'textarea\'');
200
202
  return;
201
203
  }
@@ -332,7 +334,7 @@ class EntitiesGenerator
332
334
  let entityPropertiesDefinition = this.getEntityPropertiesDefinition(tableData.columns, driverKey);
333
335
  let replacements = {
334
336
  modelClassName,
335
- tableName: driverKey === 'prisma' ? tableName.toLowerCase() : tableName,
337
+ tableName: 'prisma' === driverKey ? tableName.toLowerCase() : tableName,
336
338
  modelPropertiesList,
337
339
  modelPropertiesConstructor,
338
340
  modelRelations,
@@ -341,10 +343,7 @@ class EntitiesGenerator
341
343
  let modelContent = this.applyReplacements(modelTemplateContent, replacements);
342
344
  let fileName = sc.kebabCase(tableName)+'-model.js';
343
345
  let driverFolder = FileHandler.joinPaths(this.modelsFolder, driverKey);
344
- if(!FileHandler.createFolder(driverFolder)){
345
- Logger.critical('Failed to create driver folder.');
346
- return false;
347
- }
346
+ FileHandler.createFolder(driverFolder);
348
347
  let filePath = FileHandler.joinPaths(driverFolder, fileName);
349
348
  if(!FileHandler.writeFile(filePath, modelContent)){
350
349
  Logger.critical('Failed to write model file: '+fileName);
@@ -410,32 +409,166 @@ class EntitiesGenerator
410
409
  }
411
410
  let tableName = file.replace('-entity.js', '').replace(/-/g, '_');
412
411
  this.existingEntities[tableName] = {entityFileName: file, tableName};
412
+ this.detectExistingEntityFields(tableName, file);
413
413
  }
414
+ this.detectExistingModels();
414
415
  Logger.info('Detected '+Object.keys(this.existingEntities).length+' existing entities.');
415
416
  }
416
417
 
417
- filterTablesToGenerate(tables)
418
+ detectExistingModels()
418
419
  {
419
- if(this.isOverride){
420
- return tables;
420
+ if(!FileHandler.exists(this.modelsFolder)){
421
+ return;
422
+ }
423
+ let driverFolders = FileHandler.fetchSubFoldersList(this.modelsFolder);
424
+ for(let driverKey of driverFolders){
425
+ let driverFolder = FileHandler.joinPaths(this.modelsFolder, driverKey);
426
+ let modelFiles = FileHandler.getFilesInFolder(driverFolder, ['.js']);
427
+ for(let file of modelFiles){
428
+ if(!file.endsWith('-model.js')){
429
+ continue;
430
+ }
431
+ let tableName = file.replace('-model.js', '').replace(/-/g, '_');
432
+ if(!this.existingModels[tableName]){
433
+ this.existingModels[tableName] = {};
434
+ }
435
+ this.existingModels[tableName][driverKey] = {
436
+ modelFileName: file,
437
+ driverKey: driverKey
438
+ };
439
+ }
440
+ }
441
+ }
442
+
443
+ detectExistingEntityFields(tableName, fileName)
444
+ {
445
+ let filePath = FileHandler.joinPaths(this.entitiesFolder, fileName);
446
+ let fileContent = FileHandler.readFile(filePath);
447
+ if(!fileContent){
448
+ return;
449
+ }
450
+ let fieldsMatch = fileContent.match(/properties\s*=\s*\{([^}]+)\}/s);
451
+ if(!fieldsMatch){
452
+ return;
453
+ }
454
+ let propertiesContent = fieldsMatch[1];
455
+ let fieldMatches = propertiesContent.match(/(\w+):\s*\{[^}]*\}/g);
456
+ if(!fieldMatches){
457
+ return;
458
+ }
459
+ this.existingEntityFields[tableName] = [];
460
+ for(let fieldMatch of fieldMatches){
461
+ let fieldName = fieldMatch.split(':')[0].trim();
462
+ this.existingEntityFields[tableName].push(fieldName);
463
+ }
464
+ }
465
+
466
+ entityNeedsUpdate(tableName, tableData)
467
+ {
468
+ if(!this.existingEntityFields[tableName]){
469
+ return true;
421
470
  }
471
+ let existingFields = this.existingEntityFields[tableName];
472
+ let databaseFields = Object.keys(tableData.columns);
473
+ for(let dbField of databaseFields){
474
+ if(!existingFields.includes(dbField)){
475
+ return true;
476
+ }
477
+ }
478
+ for(let existingField of existingFields){
479
+ if('id' === existingField){
480
+ continue;
481
+ }
482
+ if(!databaseFields.includes(existingField)){
483
+ return true;
484
+ }
485
+ }
486
+ return false;
487
+ }
488
+
489
+ filterTablesToGenerate(tables, driverKey)
490
+ {
422
491
  let filteredTables = {};
423
492
  let newTablesCount = 0;
493
+ let updateTablesCount = 0;
494
+ let missingConfigCount = 0;
495
+ let missingModelCount = 0;
424
496
  for(let tableName of Object.keys(tables)){
497
+ let needsGeneration = false;
498
+ let reasons = [];
425
499
  if(!this.existingEntities[tableName]){
426
- filteredTables[tableName] = tables[tableName];
500
+ needsGeneration = true;
427
501
  newTablesCount++;
502
+ reasons.push('new entity');
503
+ }
504
+ if(this.existingEntities[tableName]){
505
+ if(this.entityNeedsUpdate(tableName, tables[tableName])){
506
+ needsGeneration = true;
507
+ updateTablesCount++;
508
+ reasons.push('fields mismatch');
509
+ }
510
+ if(!this.entityExistsInConfig(tableName)){
511
+ needsGeneration = true;
512
+ missingConfigCount++;
513
+ reasons.push('missing from config');
514
+ }
515
+ }
516
+ if(!this.existingModels[tableName] || !this.existingModels[tableName][driverKey]){
517
+ needsGeneration = true;
518
+ missingModelCount++;
519
+ reasons.push('missing model');
520
+ }
521
+ if(this.existingModels[tableName] && this.existingModels[tableName][driverKey]){
522
+ if(!this.modelExistsInRegistered(tableName, driverKey)){
523
+ needsGeneration = true;
524
+ reasons.push('missing from registered models');
525
+ }
526
+ }
527
+ if(needsGeneration || this.isOverride){
528
+ filteredTables[tableName] = tables[tableName];
529
+ if(0 < reasons.length){
530
+ Logger.info('Entity '+tableName+' needs generation: '+reasons.join(', '));
531
+ }
428
532
  }
429
533
  }
430
- if(0 === newTablesCount){
431
- Logger.info('No new tables found. All entities are up to date.');
534
+ if(0 === newTablesCount && 0 === updateTablesCount && 0 === missingConfigCount && 0 === missingModelCount){
535
+ Logger.info('No new tables found and all entities are properly configured.');
536
+ return filteredTables;
432
537
  }
433
538
  if(0 < newTablesCount){
434
539
  Logger.info('Found '+newTablesCount+' new tables to generate entities for.');
435
540
  }
541
+ if(0 < updateTablesCount){
542
+ Logger.info('Found '+updateTablesCount+' existing entities that need updates.');
543
+ }
544
+ if(0 < missingConfigCount){
545
+ Logger.info('Found '+missingConfigCount+' entities missing from config.');
546
+ }
547
+ if(0 < missingModelCount){
548
+ Logger.info('Found '+missingModelCount+' entities missing models.');
549
+ }
436
550
  return filteredTables;
437
551
  }
438
552
 
553
+ entityExistsInConfig(tableName)
554
+ {
555
+ if(!FileHandler.exists(this.entitiesConfigPath)){
556
+ return false;
557
+ }
558
+ let configContent = FileHandler.readFile(this.entitiesConfigPath);
559
+ return configContent && configContent.includes(sc.camelCase(tableName)+':');
560
+ }
561
+
562
+ modelExistsInRegistered(tableName, driverKey)
563
+ {
564
+ let registeredPath = FileHandler.joinPaths(this.modelsFolder, driverKey, 'registered-models-'+driverKey+'.js');
565
+ if(!FileHandler.exists(registeredPath)){
566
+ return false;
567
+ }
568
+ let registeredContent = FileHandler.readFile(registeredPath);
569
+ return registeredContent && registeredContent.includes(sc.camelCase(tableName)+':');
570
+ }
571
+
439
572
  generateEntitiesConfigFile()
440
573
  {
441
574
  if(this.isOverride){
@@ -447,7 +580,7 @@ class EntitiesGenerator
447
580
  regenerateEntitiesConfigFile()
448
581
  {
449
582
  let configTemplateContent = FileHandler.fetchFileContents(this.templates['entities-config']);
450
- if(!configTemplateContent) {
583
+ if(!configTemplateContent){
451
584
  Logger.critical('Failed to read entities config template file: '+this.templates['entities-config']);
452
585
  return false;
453
586
  }
@@ -492,37 +625,66 @@ class EntitiesGenerator
492
625
  if(!existingContent.includes(requireStatement)){
493
626
  newRequires.push(requireStatement);
494
627
  }
495
- if(!existingContent.includes(sc.camelCase(tableName)+':')){
628
+ if(!this.entityExistsInConfig(tableName)){
496
629
  newConfigs.push(configEntry);
497
630
  }
498
631
  }
499
632
  if(0 === newRequires.length && 0 === newConfigs.length){
500
633
  return true;
501
634
  }
635
+ let normalizedContent = existingContent.replace(/\s+/g, ' ');
636
+ let entitiesConfigPattern = /let\s+entitiesConfig\s*=\s*\{/;
637
+ let entitiesConfigMatch = normalizedContent.match(entitiesConfigPattern);
638
+ if(!entitiesConfigMatch){
639
+ Logger.error('Could not find entitiesConfig variable in config file.');
640
+ return false;
641
+ }
642
+ let entitiesConfigStart = existingContent.indexOf(entitiesConfigMatch[0]);
643
+ let searchStart = entitiesConfigStart + entitiesConfigMatch[0].length;
644
+ let braceCount = 1;
645
+ let entitiesConfigEnd = -1;
646
+ for(let i = searchStart; i < existingContent.length; i++){
647
+ if('{' === existingContent[i]){
648
+ braceCount++;
649
+ }
650
+ if('}' === existingContent[i]){
651
+ braceCount--;
652
+ if(0 === braceCount){
653
+ entitiesConfigEnd = i;
654
+ break;
655
+ }
656
+ }
657
+ }
658
+ if(-1 === entitiesConfigEnd){
659
+ Logger.error('Could not find end of entitiesConfig object.');
660
+ return false;
661
+ }
502
662
  let firstRequirePosition = existingContent.indexOf('const {');
503
- let entitiesConfigEnd = existingContent.indexOf('};\n\nmodule.exports');
504
- if(-1 === firstRequirePosition || -1 === entitiesConfigEnd){
505
- Logger.error('Could not find insertion points in config file.');
663
+ if(-1 === firstRequirePosition){
664
+ Logger.error('Could not find require statements in config file.');
506
665
  return false;
507
666
  }
508
667
  let beforeFirstRequire = existingContent.substring(0, firstRequirePosition);
509
668
  let afterFirstRequire = existingContent.substring(firstRequirePosition, entitiesConfigEnd);
669
+ let afterConfig = existingContent.substring(entitiesConfigEnd);
510
670
  let updatedContent = beforeFirstRequire;
511
671
  if(0 < newRequires.length){
512
672
  updatedContent += newRequires.join('\n') + '\n';
513
673
  }
514
674
  updatedContent += afterFirstRequire;
515
675
  if(0 < newConfigs.length){
516
- if(!afterFirstRequire.trim().endsWith(',')){
676
+ let trimmedContent = afterFirstRequire.trimEnd();
677
+ if(!trimmedContent.endsWith(',')){
517
678
  updatedContent = updatedContent.trimEnd() + ',';
518
679
  }
519
- updatedContent += '\n ' + newConfigs.join(',\n ') + '\n';
680
+ updatedContent += '\n ' + newConfigs.join(',\n ');
520
681
  }
521
- updatedContent += existingContent.substring(entitiesConfigEnd);
682
+ updatedContent += afterConfig;
522
683
  if(!FileHandler.writeFile(this.entitiesConfigPath, updatedContent)){
523
684
  Logger.error('Failed to append to entities config file: '+this.entitiesConfigPath);
524
685
  return false;
525
686
  }
687
+ Logger.info('Updated entities config file with '+newConfigs.length+' new entities.');
526
688
  return true;
527
689
  }
528
690
 
@@ -611,22 +773,45 @@ class EntitiesGenerator
611
773
  if(0 === newTranslations.length){
612
774
  return true;
613
775
  }
614
- let labelsEnd = existingContent.indexOf('\n }\n');
615
- if(-1 === existingContent.indexOf('labels: {') || -1 === labelsEnd){
616
- Logger.error('Could not find labels object boundaries in existing translations file.');
776
+ let normalizedContent = existingContent.replace(/\s+/g, ' ');
777
+ let labelsPattern = /labels\s*:\s*\{/;
778
+ let labelsMatch = normalizedContent.match(labelsPattern);
779
+ if(!labelsMatch){
780
+ Logger.error('Could not find labels object in translations file.');
781
+ return false;
782
+ }
783
+ let labelsStart = existingContent.indexOf(labelsMatch[0]);
784
+ let searchStart = labelsStart + labelsMatch[0].length;
785
+ let braceCount = 1;
786
+ let labelsEnd = -1;
787
+ for(let i = searchStart; i < existingContent.length; i++){
788
+ if('{' === existingContent[i]){
789
+ braceCount++;
790
+ }
791
+ if('}' === existingContent[i]){
792
+ braceCount--;
793
+ if(0 === braceCount){
794
+ labelsEnd = i;
795
+ break;
796
+ }
797
+ }
798
+ }
799
+ if(-1 === labelsEnd){
800
+ Logger.error('Could not find end of labels object.');
617
801
  return false;
618
802
  }
619
803
  let beforeLabelsEnd = existingContent.substring(0, labelsEnd);
620
- if(!beforeLabelsEnd.trim().endsWith(',')){
621
- beforeLabelsEnd = beforeLabelsEnd.trimEnd() + ',';
804
+ let contentBeforeEnd = beforeLabelsEnd.trimEnd();
805
+ let lastChar = contentBeforeEnd[contentBeforeEnd.length - 1];
806
+ if('{' !== lastChar && ',' !== lastChar){
807
+ beforeLabelsEnd = contentBeforeEnd + ',';
622
808
  }
623
- if(!FileHandler.writeFile(
624
- this.entitiesTranslationsPath,
625
- beforeLabelsEnd + '\n ' + newTranslations.join(',\n ') + existingContent.substring(labelsEnd)
626
- )){
809
+ let updatedContent = beforeLabelsEnd + '\n ' + newTranslations.join(',\n ') + existingContent.substring(labelsEnd);
810
+ if(!FileHandler.writeFile(this.entitiesTranslationsPath, updatedContent)){
627
811
  Logger.error('Failed to append to entities translations file: '+this.entitiesTranslationsPath);
628
812
  return false;
629
813
  }
814
+ Logger.info('Updated translations file with '+newTranslations.length+' new labels.');
630
815
  return true;
631
816
  }
632
817
 
@@ -677,7 +862,9 @@ class EntitiesGenerator
677
862
  for(let tableName of Object.keys(allEntities)){
678
863
  let entity = allEntities[tableName];
679
864
  if(entity.driverKey && driverKey !== entity.driverKey){
680
- continue;
865
+ if(!this.existingModels[tableName] || !this.existingModels[tableName][driverKey]){
866
+ continue;
867
+ }
681
868
  }
682
869
  let modelClassName = entity.modelClassName || sc.capitalizedCamelCase(tableName)+'Model';
683
870
  let modelFileName = entity.modelFileName || sc.kebabCase(tableName)+'-model.js';
@@ -694,7 +881,9 @@ class EntitiesGenerator
694
881
  for(let tableName of Object.keys(allEntities)){
695
882
  let entity = allEntities[tableName];
696
883
  if(entity.driverKey && driverKey !== entity.driverKey){
697
- continue;
884
+ if(!this.existingModels[tableName] || !this.existingModels[tableName][driverKey]){
885
+ continue;
886
+ }
698
887
  }
699
888
  let modelClassName = entity.modelClassName || sc.capitalizedCamelCase(tableName)+'Model';
700
889
  registeredEntitiesObject.push(sc.camelCase(tableName)+': '+modelClassName);
@@ -731,10 +920,6 @@ class EntitiesGenerator
731
920
  return false;
732
921
  }
733
922
  this.detectExistingEntities();
734
- let tablesToGenerate = this.filterTablesToGenerate(tables);
735
- if(0 === Object.keys(tablesToGenerate).length && !this.isOverride){
736
- return true;
737
- }
738
923
  let driverKey = sc.get(
739
924
  this.connectionData,
740
925
  'driver',
@@ -744,6 +929,10 @@ class EntitiesGenerator
744
929
  Logger.critical('Failed to fetch driver key.');
745
930
  return false;
746
931
  }
932
+ let tablesToGenerate = this.filterTablesToGenerate(tables, driverKey);
933
+ if(0 === Object.keys(tablesToGenerate).length && !this.isOverride){
934
+ return true;
935
+ }
747
936
  for(let tableName of Object.keys(tablesToGenerate)){
748
937
  let tableData = tablesToGenerate[tableName];
749
938
  await this.generateEntityFile(tableName, tableData);
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  const { BaseDriver } = require('../base-driver');
8
- const { ErrorManager, Logger, sc } = require('@reldens/utils');
8
+ const { Logger, sc } = require('@reldens/utils');
9
9
  const { Prisma } = require('@prisma/client');
10
10
 
11
11
  class PrismaDriver extends BaseDriver
@@ -32,6 +32,7 @@ class PrismaDriver extends BaseDriver
32
32
  this.requiredFields = [];
33
33
  this.fieldTypes = {};
34
34
  this.idFieldType = 'String';
35
+ this.referenceFields = {};
35
36
  this.initFieldMetadata();
36
37
  }
37
38
 
@@ -55,6 +56,13 @@ class PrismaDriver extends BaseDriver
55
56
  if(field.isId){
56
57
  this.idFieldType = field.type;
57
58
  }
59
+ if(field.kind === 'scalar' && field.isList === false){
60
+ let isNumericType = ('Int' === field.type || 'BigInt' === field.type || 'Float' === field.type || 'Decimal' === field.type);
61
+ let isReferenceField = field.relationFromFields && 0 < field.relationFromFields.length;
62
+ if(isNumericType || isReferenceField){
63
+ this.referenceFields[field.name] = field.type;
64
+ }
65
+ }
58
66
  }
59
67
  } catch(error) {
60
68
  Logger.warning('Could not initialize field metadata: '+error.message);
@@ -75,6 +83,26 @@ class PrismaDriver extends BaseDriver
75
83
  return String(id);
76
84
  }
77
85
 
86
+ castNumericValue(value, fieldType)
87
+ {
88
+ if(null === value || undefined === value || '' === value){
89
+ return null;
90
+ }
91
+ if('Int' === fieldType || 'BigInt' === fieldType){
92
+ let numericValue = Number(value);
93
+ if(!isNaN(numericValue)){
94
+ return Math.floor(numericValue);
95
+ }
96
+ }
97
+ if('Float' === fieldType || 'Decimal' === fieldType){
98
+ let numericValue = Number(value);
99
+ if(!isNaN(numericValue)){
100
+ return numericValue;
101
+ }
102
+ }
103
+ return value;
104
+ }
105
+
78
106
  databaseName()
79
107
  {
80
108
  return this.config.database || '';
@@ -109,6 +137,10 @@ class PrismaDriver extends BaseDriver
109
137
  }
110
138
  if(this.isDateTimeField(key)){
111
139
  data[key] = this.convertToDate(data[key], key);
140
+ continue;
141
+ }
142
+ if(sc.hasOwn(this.referenceFields, key)){
143
+ data[key] = this.castNumericValue(data[key], this.referenceFields[key]);
112
144
  }
113
145
  }
114
146
  return data;
@@ -173,6 +205,12 @@ class PrismaDriver extends BaseDriver
173
205
  if('id' === key && !sc.isArray(operatorValue)){
174
206
  operatorValue = this.castIdValue(operatorValue);
175
207
  }
208
+ if(sc.hasOwn(this.referenceFields, key) && sc.isArray(operatorValue)){
209
+ operatorValue = operatorValue.map(val => this.castNumericValue(val, this.referenceFields[key]));
210
+ }
211
+ if(sc.hasOwn(this.referenceFields, key) && !sc.isArray(operatorValue)){
212
+ operatorValue = this.castNumericValue(operatorValue, this.referenceFields[key]);
213
+ }
176
214
  processedFilters[key] = this.applyOperator(operatorValue, value.operator);
177
215
  continue;
178
216
  }
@@ -188,6 +226,14 @@ class PrismaDriver extends BaseDriver
188
226
  processedFilters[key] = this.castIdValue(value);
189
227
  continue;
190
228
  }
229
+ if(sc.hasOwn(this.referenceFields, key) && sc.isArray(value)){
230
+ processedFilters[key] = value.map(val => this.castNumericValue(val, this.referenceFields[key]));
231
+ continue;
232
+ }
233
+ if(sc.hasOwn(this.referenceFields, key)){
234
+ processedFilters[key] = this.castNumericValue(value, this.referenceFields[key]);
235
+ continue;
236
+ }
191
237
  processedFilters[key] = value;
192
238
  }
193
239
  return processedFilters;
@@ -590,11 +636,18 @@ class PrismaDriver extends BaseDriver
590
636
  createSingleFilter(field, fieldValue, operator = null)
591
637
  {
592
638
  let filter = {};
639
+ let processedValue = fieldValue;
640
+ if(sc.hasOwn(this.referenceFields, field)){
641
+ processedValue = this.castNumericValue(fieldValue, this.referenceFields[field]);
642
+ }
643
+ if('id' === field){
644
+ processedValue = this.castIdValue(fieldValue);
645
+ }
593
646
  if(null === operator){
594
- filter[field] = fieldValue;
647
+ filter[field] = processedValue;
595
648
  return filter;
596
649
  }
597
- filter[field] = this.applyOperator(fieldValue, operator);
650
+ filter[field] = this.applyOperator(processedValue, operator);
598
651
  return filter;
599
652
  }
600
653
 
@@ -4,7 +4,7 @@
4
4
  *
5
5
  */
6
6
 
7
- const { execSync } = require('child_process');
7
+ const { execSync, spawn } = require('child_process');
8
8
  const { FileHandler } = require('@reldens/server-utils');
9
9
  const { Logger, sc } = require('@reldens/utils');
10
10
 
@@ -26,24 +26,61 @@ class PrismaSchemaGenerator
26
26
  }
27
27
 
28
28
  async generate()
29
+ {
30
+ FileHandler.createFolder(this.prismaSchemaPath);
31
+ this.generateSchemaFile();
32
+ Logger.info('Running prisma introspect "npx prisma db pull"...');
33
+ execSync('npx prisma db pull', { stdio: 'inherit' });
34
+ let generateCommand = 'npx prisma generate';
35
+ if(this.dataProxy){
36
+ generateCommand += ' --data-proxy';
37
+ }
38
+ Logger.info('Running prisma generate "'+generateCommand+'"...');
39
+ await this.executeWithFallback(generateCommand);
40
+ await this.waitForSchemaGeneration();
41
+ return true;
42
+ }
43
+
44
+ async executeWithFallback(command)
29
45
  {
30
46
  try {
31
- FileHandler.createFolder(this.prismaSchemaPath);
32
- this.generateSchemaFile();
33
- Logger.info('Running prisma introspect "npx prisma db pull"...');
34
- execSync('npx prisma db pull', { stdio: 'inherit' });
35
- let generateCommand = 'npx prisma generate';
36
- if(this.dataProxy){
37
- generateCommand += ' --data-proxy';
38
- }
39
- Logger.info('Running prisma generate "'+generateCommand+'"...');
40
- execSync(generateCommand, { stdio: 'inherit' });
41
- await this.waitForSchemaGeneration();
42
- return true;
47
+ execSync(command, { stdio: 'inherit' });
43
48
  } catch(error) {
44
- Logger.critical('Failed to generate Prisma schema. '+error.message);
49
+ if(this.isWindowsPermissionError(error)){
50
+ Logger.warning('Windows permission error detected, retrying with detached process...');
51
+ await this.executeDetached(command);
52
+ return;
53
+ }
54
+ Logger.critical('Command failed: ' + error.message);
55
+ return false;
45
56
  }
46
- return false;
57
+ }
58
+
59
+ isWindowsPermissionError(error)
60
+ {
61
+ return error.message
62
+ && error.message.includes('EPERM')
63
+ && error.message.includes('rename')
64
+ && error.message.includes('query_engine-windows.dll.node');
65
+ }
66
+
67
+ executeDetached(command)
68
+ {
69
+ return new Promise((resolve, reject) => {
70
+ let child = spawn(command, [], {
71
+ stdio: 'inherit',
72
+ shell: true,
73
+ detached: true
74
+ });
75
+ child.on('close', (code) => {
76
+ if(0 === code){
77
+ resolve();
78
+ return;
79
+ }
80
+ reject(new Error('Command failed with exit code ' + code));
81
+ });
82
+ child.on('error', reject);
83
+ });
47
84
  }
48
85
 
49
86
  async waitForSchemaGeneration()
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/storage",
3
3
  "scope": "@reldens",
4
- "version": "0.51.0",
4
+ "version": "0.53.0",
5
5
  "description": "Reldens - Storage",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",
@@ -45,13 +45,13 @@
45
45
  "@mikro-orm/core": "^6.4.16",
46
46
  "@mikro-orm/mongodb": "^6.4.16",
47
47
  "@mikro-orm/mysql": "^6.4.16",
48
- "@prisma/client": "^6.8.2",
49
- "@reldens/server-utils": "^0.18.0",
48
+ "@prisma/client": "^6.9.0",
49
+ "@reldens/server-utils": "^0.19.0",
50
50
  "@reldens/utils": "^0.50.0",
51
51
  "knex": "^3.1.0",
52
52
  "mysql": "^2.18.1",
53
53
  "mysql2": "^3.14.1",
54
54
  "objection": "^3.1.5",
55
- "prisma": "^6.8.2"
55
+ "prisma": "^6.9.0"
56
56
  }
57
57
  }