@rws-framework/db 2.4.6 → 3.0.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 (123) hide show
  1. package/.bin/add-v.sh +9 -9
  2. package/.bin/emerge.sh +10 -10
  3. package/.eslintrc.json +53 -53
  4. package/README.md +404 -404
  5. package/dist/decorators/IdType.d.ts +8 -0
  6. package/dist/decorators/IdType.js +10 -0
  7. package/dist/decorators/InverseRelation.d.ts +2 -2
  8. package/dist/decorators/InverseRelation.js +5 -1
  9. package/dist/decorators/InverseTimeSeries.d.ts +0 -0
  10. package/dist/decorators/InverseTimeSeries.js +0 -0
  11. package/dist/decorators/RWSCollection.d.ts +7 -0
  12. package/dist/decorators/RWSCollection.js +6 -0
  13. package/dist/decorators/Relation.d.ts +8 -6
  14. package/dist/decorators/Relation.js +8 -1
  15. package/dist/decorators/TrackType.d.ts +2 -14
  16. package/dist/decorators/TrackType.js +7 -0
  17. package/dist/decorators/TypeFunctions.d.ts +44 -0
  18. package/dist/decorators/TypeFunctions.js +174 -0
  19. package/dist/decorators/index.d.ts +4 -2
  20. package/dist/decorators/index.js +3 -1
  21. package/dist/helper/DbHelper.d.ts +76 -5
  22. package/dist/helper/DbHelper.js +93 -154
  23. package/dist/helper/FieldsHelper.d.ts +0 -0
  24. package/dist/helper/FieldsHelper.js +0 -0
  25. package/dist/helper/db/index.d.ts +9 -0
  26. package/dist/helper/db/index.js +18 -0
  27. package/dist/helper/db/relation-manager.d.ts +45 -0
  28. package/dist/helper/db/relation-manager.js +105 -0
  29. package/dist/helper/db/schema-generator.d.ts +37 -0
  30. package/dist/helper/db/schema-generator.js +243 -0
  31. package/dist/helper/db/type-converter.d.ts +22 -0
  32. package/dist/helper/db/type-converter.js +106 -0
  33. package/dist/helper/db/utils.d.ts +24 -0
  34. package/dist/helper/db/utils.js +99 -0
  35. package/dist/index.d.ts +3 -3
  36. package/dist/index.js +2 -1
  37. package/dist/models/TimeSeriesModel.d.ts +7 -7
  38. package/dist/models/TimeSeriesModel.js +33 -33
  39. package/dist/models/_model.d.ts +2 -2
  40. package/dist/models/_model.js +0 -0
  41. package/dist/models/core/RWSModel.d.ts +4 -1
  42. package/dist/models/core/RWSModel.js +6 -4
  43. package/dist/models/core/TimeSeriesModel.d.ts +0 -0
  44. package/dist/models/core/TimeSeriesModel.js +0 -0
  45. package/dist/models/index.d.ts +2 -2
  46. package/dist/models/index.js +0 -0
  47. package/dist/models/interfaces/IDbOpts.d.ts +17 -0
  48. package/dist/models/interfaces/IDbOpts.js +2 -0
  49. package/dist/models/interfaces/IIdOpts.d.ts +0 -0
  50. package/dist/models/interfaces/IIdOpts.js +1 -0
  51. package/dist/models/interfaces/IIdTypeOpts.d.ts +3 -0
  52. package/dist/models/interfaces/IIdTypeOpts.js +2 -0
  53. package/dist/models/interfaces/IModel.d.ts +0 -0
  54. package/dist/models/interfaces/IModel.js +0 -0
  55. package/dist/models/interfaces/IRWSModelServices.d.ts +0 -0
  56. package/dist/models/interfaces/IRWSModelServices.js +0 -0
  57. package/dist/models/interfaces/ITrackerOpts.d.ts +12 -0
  58. package/dist/models/interfaces/ITrackerOpts.js +2 -0
  59. package/dist/models/interfaces/OpModelType.d.ts +3 -0
  60. package/dist/models/interfaces/OpModelType.js +0 -0
  61. package/dist/models/types/RelationTypes.d.ts +0 -0
  62. package/dist/models/types/RelationTypes.js +0 -0
  63. package/dist/models/utils/ModelUtils.d.ts +0 -0
  64. package/dist/models/utils/ModelUtils.js +0 -0
  65. package/dist/models/utils/PaginationUtils.d.ts +0 -0
  66. package/dist/models/utils/PaginationUtils.js +0 -0
  67. package/dist/models/utils/RelationUtils.d.ts +1 -1
  68. package/dist/models/utils/RelationUtils.js +2 -2
  69. package/dist/models/utils/TimeSeriesUtils.d.ts +0 -0
  70. package/dist/models/utils/TimeSeriesUtils.js +0 -0
  71. package/dist/services/DBService.d.ts +0 -0
  72. package/dist/services/DBService.js +0 -0
  73. package/dist/types/DbConfigHandler.d.ts +1 -1
  74. package/dist/types/DbConfigHandler.js +0 -0
  75. package/dist/types/FindParams.d.ts +0 -0
  76. package/dist/types/FindParams.js +0 -0
  77. package/dist/types/IRWSModel.d.ts +1 -1
  78. package/dist/types/IRWSModel.js +0 -0
  79. package/dist/types/ITimeSeries.d.ts +0 -0
  80. package/dist/types/ITimeSeries.js +0 -0
  81. package/exec/console.js +110 -110
  82. package/exec/db.rws.webpack.config.js +168 -168
  83. package/exec/src/cli.ts +73 -75
  84. package/exec/tsconfig.json +32 -32
  85. package/exec/webpackFilters.js +17 -17
  86. package/package.json +36 -36
  87. package/src/decorators/IdType.ts +17 -0
  88. package/src/decorators/InverseRelation.ts +41 -37
  89. package/src/decorators/InverseTimeSeries.ts +21 -21
  90. package/src/decorators/RWSCollection.ts +45 -27
  91. package/src/decorators/Relation.ts +61 -48
  92. package/src/decorators/TrackType.ts +65 -69
  93. package/src/decorators/index.ts +16 -8
  94. package/src/empty.js +0 -0
  95. package/src/helper/DbHelper.ts +133 -223
  96. package/src/helper/FieldsHelper.ts +34 -34
  97. package/src/helper/db/index.ts +10 -0
  98. package/src/helper/db/relation-manager.ts +119 -0
  99. package/src/helper/db/schema-generator.ts +302 -0
  100. package/src/helper/db/type-converter.ts +119 -0
  101. package/src/helper/db/utils.ts +120 -0
  102. package/src/index.ts +47 -38
  103. package/src/models/_model.ts +29 -29
  104. package/src/models/core/RWSModel.ts +523 -520
  105. package/src/models/core/TimeSeriesModel.ts +19 -19
  106. package/src/models/index.ts +20 -20
  107. package/src/models/interfaces/IDbOpts.ts +17 -0
  108. package/src/models/interfaces/IIdTypeOpts.ts +4 -0
  109. package/src/models/interfaces/IModel.ts +12 -12
  110. package/src/models/interfaces/IRWSModelServices.ts +7 -7
  111. package/src/models/interfaces/ITrackerOpts.ts +13 -0
  112. package/src/models/interfaces/OpModelType.ts +52 -49
  113. package/src/models/types/RelationTypes.ts +25 -25
  114. package/src/models/utils/ModelUtils.ts +65 -65
  115. package/src/models/utils/PaginationUtils.ts +42 -42
  116. package/src/models/utils/RelationUtils.ts +76 -76
  117. package/src/models/utils/TimeSeriesUtils.ts +38 -38
  118. package/src/services/DBService.ts +277 -277
  119. package/src/types/DbConfigHandler.ts +17 -17
  120. package/src/types/FindParams.ts +13 -13
  121. package/src/types/IRWSModel.ts +2 -2
  122. package/src/types/ITimeSeries.ts +5 -5
  123. package/tsconfig.json +22 -22
@@ -1,223 +1,133 @@
1
- import { rwsShell, rwsPath } from '@rws-framework/console';
2
- import chalk from 'chalk';
3
- import path from 'path';
4
- import fs from 'fs';
5
-
6
- import { IDbConfigHandler, IDbConfigParams, IdGeneratorOptions } from '../types/DbConfigHandler';
7
- import { IMetaOpts, OpModelType, RWSModel } from '../models/_model';
8
- // import TimeSeriesModel from '../models/core/TimeSeriesModel';
9
- import { DBService } from '../services/DBService';
10
- import { IRelationOpts } from '../decorators/Relation';
11
- import { InverseRelationOpts } from '../decorators/InverseRelation';
12
-
13
- const log = console.log;
14
- const workspaceRoot = rwsPath.findRootWorkspacePath();
15
- const moduleDir = path.resolve(workspaceRoot, 'node_modules', '@rws-framework', 'db');
16
-
17
- export class DbHelper {
18
- static dbUrlVarName: string = 'PRISMA_DB_URL';
19
-
20
- static async installPrisma(configService: IDbConfigHandler, dbService: DBService, leaveFile = false): Promise<void>
21
- {
22
- const dbUrl = configService.get('db_url');
23
- const dbType = configService.get('db_type') || 'mongodb';
24
-
25
- let template: string = `generator client {\n
26
- provider = "prisma-client-js"\n
27
- }\n\n`;
28
-
29
- template += `\ndatasource db {\n
30
- provider = "${dbType}"\n
31
- url = env("${this.dbUrlVarName}")\n
32
- }\n\n`;
33
-
34
- const dbModels: OpModelType<unknown>[] | null = configService.get('db_models');
35
-
36
- if(dbModels){
37
-
38
- for (const model of dbModels){
39
- const modelSection = await DbHelper.generateModelSections(model, configService);
40
-
41
- template += '\n\n' + modelSection;
42
-
43
- log(chalk.green('[RWS]'), chalk.blue('Building DB Model'), model.name);
44
-
45
- // if(RWSModel.isSubclass(model as any, TimeSeriesModel)){
46
- // dbService.collectionExists(model._collection).then((exists: boolean) => {
47
- // if (exists){
48
- // return;
49
- // }
50
-
51
- // log(chalk.green('[RWS Init]') + ` creating TimeSeries type collection from ${model} model`);
52
-
53
- // dbService.createTimeSeriesCollection(model._collection);
54
- // });
55
- // }
56
- }
57
-
58
- const schemaDir = path.join(moduleDir, 'prisma');
59
- const schemaPath = path.join(schemaDir, 'schema.prisma');
60
-
61
- if(!fs.existsSync(schemaDir)){
62
- fs.mkdirSync(schemaDir);
63
- }
64
-
65
- if(fs.existsSync(schemaPath)){
66
- fs.unlinkSync(schemaPath);
67
- }
68
-
69
- fs.writeFileSync(schemaPath, template);
70
- process.env = { ...process.env, [this.dbUrlVarName]: dbUrl }
71
-
72
- await rwsShell.runCommand(`${this.detectInstaller()} prisma generate --schema="${schemaPath}"`, process.cwd());
73
-
74
- leaveFile = false;
75
- log(chalk.green('[RWS Init]') + ' prisma schema generated from ', schemaPath);
76
-
77
- if(!leaveFile){
78
- fs.unlinkSync(schemaPath);
79
- }
80
- }
81
- }
82
-
83
- static generateId(
84
- dbType: IDbConfigParams['db_type'],
85
- options: IdGeneratorOptions = {}
86
- ): string {
87
- const { useUuid = false, customType } = options;
88
-
89
- if (customType) {
90
- return `id ${customType} @id`;
91
- }
92
-
93
- switch(dbType) {
94
- case 'mongodb':
95
- return 'id String @id @default(auto()) @map("_id") @db.ObjectId';
96
-
97
- case 'mysql':
98
- return useUuid
99
- ? 'id String @id @default(uuid())'
100
- : 'id Int @id @default(autoincrement())';
101
-
102
- case 'sqlite':
103
- return 'id Int @id @default(autoincrement())';
104
-
105
- default:
106
- throw new Error('Kurwa, nieobsługiwany typ bazy danych!');
107
- }
108
- }
109
-
110
- static detectInstaller(): string
111
- {
112
-
113
- if (fs.existsSync(path.join(workspaceRoot, 'yarn.lock'))){
114
- return 'yarn';
115
- }
116
-
117
- return 'npx';
118
- }
119
-
120
- static async pushDBModels(configService: IDbConfigHandler, dbService: DBService, leaveFile = false){
121
- process.env = { ...process.env, [this.dbUrlVarName]: configService.get('db_url') }
122
-
123
- const schemaPath = path.join(workspaceRoot, 'node_modules', '.prisma', 'client','schema.prisma');
124
-
125
- await rwsShell.runCommand(`${this.detectInstaller()} prisma db push --schema="${schemaPath}"`, process.cwd());
126
-
127
- }
128
-
129
- static async generateModelSections(model: OpModelType<any>, configService: IDbConfigHandler): Promise<string> {
130
- let section = '';
131
- const modelMetadatas: Record<string, {annotationType: string, metadata: any}> = await RWSModel.getModelAnnotations(model);
132
-
133
- const modelName: string = (model as any)._collection;
134
-
135
- section += `model ${modelName} {\n`;
136
- section += `\t${this.generateId(configService.get('db_type'))}\n`;
137
-
138
- for (const key in modelMetadatas) {
139
- const modelMetadata = modelMetadatas[key].metadata;
140
- let requiredString = modelMetadata.required ? '' : '?';
141
- const annotationType: string = modelMetadatas[key].annotationType;
142
-
143
- if(key === 'id'){
144
- continue;
145
- }
146
-
147
-
148
- if(annotationType === 'Relation'){
149
- const relationMeta = modelMetadata as IRelationOpts
150
-
151
- const relatedModel = relationMeta.relatedTo as OpModelType<any>;
152
- const isMany = relationMeta.many;
153
- const cascadeOpts = [];
154
-
155
- if (relationMeta.cascade?.onDelete) {
156
- cascadeOpts.push(`onDelete: ${relationMeta.cascade.onDelete}`);
157
- }
158
-
159
- if (relationMeta.cascade?.onUpdate) {
160
- cascadeOpts.push(`onUpdate: ${relationMeta.cascade.onUpdate}`);
161
- }
162
-
163
- if (isMany) {
164
- // Handle many-to-many or one-to-many relation
165
- section += `\t${key} ${relatedModel._collection}[] @relation("${modelName}_${relatedModel._collection}")\n`;
166
- } else {
167
- // Handle one-to-one or many-to-one relation
168
- section += `\t${key} ${relatedModel._collection}${requiredString} @relation("${modelName}_${relatedModel._collection}", fields: [${modelMetadata.relationField}], references: [${modelMetadata.relatedToField || 'id'}], ${cascadeOpts.join(', ')})\n`;
169
- section += `\t${modelMetadata.relationField} String${requiredString} @db.ObjectId\n`;
170
- }
171
- } else if (annotationType === 'InverseRelation'){
172
- const relationMeta = modelMetadata as InverseRelationOpts;
173
-
174
- // Handle inverse relation (one-to-many or one-to-one)
175
- section += `\t${key} ${relationMeta.inversionModel._collection}[] @relation("${ relationMeta.relationName ? relationMeta.relationName : `${relationMeta.inversionModel._collection}_${modelName}`}")\n`;
176
- } else if (annotationType === 'InverseTimeSeries'){
177
- section += `\t${key} String[] @db.ObjectId\n`;
178
- } else if (annotationType === 'TrackType'){
179
- const tags: string[] = modelMetadata.tags.map((item: string) => '@' + item);
180
-
181
- if(modelMetadata.isArray || modelMetadata.type.name === 'Array'){
182
- requiredString = '';
183
- }
184
- section += `\t${key} ${DbHelper.toConfigCase(modelMetadata)}${requiredString} ${tags.join(' ')}\n`;
185
- }
186
- }
187
-
188
- section += '}\n';
189
- return section;
190
- }
191
-
192
- static toConfigCase(modelType: IMetaOpts): string {
193
- const type = modelType.type;
194
- let input = type.name;
195
-
196
-
197
- if(input == 'Number'){
198
- input = 'Int';
199
- }
200
-
201
- if(input == 'Object'){
202
- input = 'Json';
203
- }
204
-
205
- if(input == 'Date'){
206
- input = 'DateTime';
207
- }
208
-
209
- if(input == 'Array'){
210
- input = 'Json[]';
211
- }
212
-
213
- const firstChar = input.charAt(0).toUpperCase();
214
- const restOfString = input.slice(1);
215
- let resultField = firstChar + restOfString;
216
-
217
- if(modelType.isArray){
218
- resultField += '[]';
219
- }
220
-
221
- return resultField;
222
- }
223
- }
1
+ import { IDbConfigHandler, IDbConfigParams, IdGeneratorOptions } from '../types/DbConfigHandler';
2
+ import { OpModelType } from '../models/_model';
3
+ import { DBService } from '../services/DBService';
4
+
5
+ import {
6
+ DbUtils,
7
+ TypeConverter,
8
+ RelationManager,
9
+ SchemaGenerator
10
+ } from './db';
11
+ import { IIdMetaOpts, IIdTypeOpts } from '../decorators/IdType';
12
+
13
+ /**
14
+ * Database helper class
15
+ *
16
+ * This class provides a facade for the database helper modules.
17
+ * It delegates to the specialized modules for specific functionality.
18
+ */
19
+ export class DbHelper {
20
+ /**
21
+ * The environment variable name for the Prisma database URL
22
+ */
23
+ static dbUrlVarName: string = SchemaGenerator.dbUrlVarName;
24
+
25
+ /**
26
+ * Install Prisma with the generated schema
27
+ * @param configService The configuration service
28
+ * @param dbService The database service
29
+ * @param leaveFile Whether to leave the schema file after generation
30
+ */
31
+ static async installPrisma(configService: IDbConfigHandler, dbService: DBService, leaveFile = false): Promise<void> {
32
+ return SchemaGenerator.installPrisma(configService, dbService, leaveFile);
33
+ }
34
+
35
+ /**
36
+ * Push database models to the database
37
+ * @param configService The configuration service
38
+ * @param dbService The database service
39
+ * @param leaveFile Whether to leave the schema file after generation
40
+ */
41
+ static async pushDBModels(configService: IDbConfigHandler, dbService: DBService, leaveFile = false): Promise<void> {
42
+ return SchemaGenerator.pushDBModels(configService, dbService, leaveFile);
43
+ }
44
+
45
+ /**
46
+ * Generate model sections for the schema
47
+ * @param model The model to generate a section for
48
+ * @param configService The configuration service
49
+ * @returns The model section
50
+ */
51
+ static async generateModelSections(
52
+ model: OpModelType<any>,
53
+ configService: IDbConfigHandler
54
+ ): Promise<string> {
55
+ return SchemaGenerator.generateModelSections(model, configService);
56
+ }
57
+
58
+ /**
59
+ * Generate the base schema for Prisma
60
+ * @param dbType The database type
61
+ * @param dbUrl The database URL
62
+ * @returns The base schema
63
+ */
64
+ static generateBaseSchema(dbType: string, dbUrl: string): string {
65
+ return SchemaGenerator.generateBaseSchema(dbType, dbUrl);
66
+ }
67
+
68
+ /**
69
+ * Get the directory and path for the Prisma schema file
70
+ */
71
+ static getSchemaDir(): [string, string] {
72
+ return DbUtils.getSchemaDir();
73
+ }
74
+
75
+ /**
76
+ * Detect the package installer (yarn or npx)
77
+ */
78
+ static detectInstaller(): string {
79
+ return DbUtils.detectInstaller();
80
+ }
81
+
82
+ /**
83
+ * Generate an ID field based on the database type
84
+ */
85
+ static generateId(
86
+ dbType: IDbConfigParams['db_type'],
87
+ modelMeta: Record<string, { annotationType: string, metadata: IIdMetaOpts }>
88
+ ): string {
89
+ return DbUtils.generateId(dbType, modelMeta);
90
+ }
91
+
92
+ /**
93
+ * Convert a JavaScript type to a Prisma schema type
94
+ */
95
+ static toConfigCase(modelType: any, dbType: string = 'mongodb'): string {
96
+ return TypeConverter.toConfigCase(modelType, dbType);
97
+ }
98
+
99
+ /**
100
+ * Process type functions metadata to extract database-specific options
101
+ */
102
+ static processTypeOptions(metadata: any, dbType: string): string[] {
103
+ return TypeConverter.processTypeOptions(metadata, dbType);
104
+ }
105
+
106
+ /**
107
+ * Mark a relation between two models
108
+ */
109
+ static markRelation(relationKey: string, inverse: boolean = false): void {
110
+ RelationManager.markRelation(relationKey, inverse);
111
+ }
112
+
113
+ /**
114
+ * Complete a relation between two models
115
+ */
116
+ static completeRelation(relationKey: string, index: number, inverse: boolean = false): void {
117
+ RelationManager.completeRelation(relationKey, index, inverse);
118
+ }
119
+
120
+ /**
121
+ * Get a unique counter for a relation between two models
122
+ */
123
+ static getRelationCounter(relationKey: string, inverse: boolean = false): number {
124
+ return RelationManager.getRelationCounter(relationKey, inverse);
125
+ }
126
+
127
+ /**
128
+ * Generate a shortened relation name to stay within database limits
129
+ */
130
+ static getShortenedRelationName(modelName: string, relatedModelName: string, index: number): string {
131
+ return RelationManager.getShortenedRelationName(modelName, relatedModelName, index);
132
+ }
133
+ }
@@ -1,35 +1,35 @@
1
- export class FieldsHelper {
2
- private constructor(){
3
- throw new Error(`Class ${this.constructor.name} cannot be instanced.`)
4
- }
5
-
6
- static getAllClassFields(target: any): string[] {
7
- // Get instance properties
8
- const instanceFields = Object.getOwnPropertyNames(target.prototype);
9
-
10
- // Get static properties
11
- const staticFields = Object.getOwnPropertyNames(target);
12
-
13
- // Get decorated properties using Reflect metadata if available
14
- const decoratedFields = Reflect.getMetadataKeys(target.prototype) || [];
15
-
16
- // Combine all fields and remove duplicates and methods
17
- const allFields = new Set([
18
- ...instanceFields,
19
- ...staticFields,
20
- ...decoratedFields
21
- ]);
22
-
23
- // Filter out constructor and methods
24
- return Array.from(allFields).filter(field => {
25
- // Remove constructor
26
- if (field === 'constructor') return false;
27
-
28
- // Remove methods
29
- const descriptor = Object.getOwnPropertyDescriptor(target.prototype, field);
30
- if (descriptor && typeof descriptor.value === 'function') return false;
31
-
32
- return true;
33
- });
34
- };
1
+ export class FieldsHelper {
2
+ private constructor(){
3
+ throw new Error(`Class ${this.constructor.name} cannot be instanced.`)
4
+ }
5
+
6
+ static getAllClassFields(target: any): string[] {
7
+ // Get instance properties
8
+ const instanceFields = Object.getOwnPropertyNames(target.prototype);
9
+
10
+ // Get static properties
11
+ const staticFields = Object.getOwnPropertyNames(target);
12
+
13
+ // Get decorated properties using Reflect metadata if available
14
+ const decoratedFields = Reflect.getMetadataKeys(target.prototype) || [];
15
+
16
+ // Combine all fields and remove duplicates and methods
17
+ const allFields = new Set([
18
+ ...instanceFields,
19
+ ...staticFields,
20
+ ...decoratedFields
21
+ ]);
22
+
23
+ // Filter out constructor and methods
24
+ return Array.from(allFields).filter(field => {
25
+ // Remove constructor
26
+ if (field === 'constructor') return false;
27
+
28
+ // Remove methods
29
+ const descriptor = Object.getOwnPropertyDescriptor(target.prototype, field);
30
+ if (descriptor && typeof descriptor.value === 'function') return false;
31
+
32
+ return true;
33
+ });
34
+ };
35
35
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Database helper modules
3
+ *
4
+ * This module exports all the database helper classes for use in the application.
5
+ */
6
+
7
+ export { DbUtils, workspaceRootPath, moduleDirPath } from './utils';
8
+ export { TypeConverter } from './type-converter';
9
+ export { RelationManager } from './relation-manager';
10
+ export { SchemaGenerator } from './schema-generator';
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Manages database relations for schema generation
3
+ */
4
+ export class RelationManager {
5
+ private static allRelations = new Map<string, {base: boolean | null, inversion: boolean | null}[]>();
6
+
7
+ /**
8
+ * Mark a relation between two models
9
+ * @param relationKey A unique key for the relation
10
+ * @param inverse Whether this is an inverse relation
11
+ */
12
+ static markRelation(relationKey: string, inverse: boolean = false): void {
13
+ if (!this.allRelations.has(relationKey)) {
14
+ this.allRelations.set(relationKey, []);
15
+ }
16
+
17
+ const modelRelations = this.allRelations.get(relationKey);
18
+
19
+ let marked = false;
20
+
21
+ for(const relationInfo of modelRelations){
22
+ if((relationInfo.base !== null && !inverse) || (relationInfo.inversion !== null && inverse)){
23
+ continue;
24
+ }
25
+
26
+ if(inverse){
27
+ relationInfo.inversion = false;
28
+ marked = true;
29
+ }else{
30
+ relationInfo.base = false;
31
+ marked = true;
32
+ }
33
+ return;
34
+ }
35
+
36
+ if(!marked){
37
+ modelRelations.push({base: inverse ? null : false, inversion: inverse ? false : null});
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Complete a relation between two models
43
+ * @param relationKey A unique key for the relation
44
+ * @param index The index of the relation
45
+ * @param inverse Whether this is an inverse relation
46
+ */
47
+ static completeRelation(relationKey: string, index: number, inverse: boolean = false): void {
48
+ const modelRelations = this.allRelations.get(relationKey);
49
+
50
+ if(inverse){
51
+ modelRelations[index].inversion = true;
52
+ }else{
53
+ modelRelations[index].base = true;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Get a unique counter for a relation between two models
59
+ * @param relationKey A unique key for the relation
60
+ * @param inverse Whether this is an inverse relation
61
+ * @returns A unique counter for this relation
62
+ */
63
+ static getRelationCounter(relationKey: string, inverse: boolean = false): number {
64
+ let counter = 0;
65
+
66
+ for(const relationInfo of this.allRelations.get(relationKey)) {
67
+ if((relationInfo.base === true && !inverse) || (relationInfo.inversion === true && inverse)) {
68
+ counter++;
69
+ }
70
+ }
71
+
72
+ return counter;
73
+ }
74
+
75
+ /**
76
+ * Generate a shortened relation name to stay within database limits
77
+ * @param modelName The name of the model
78
+ * @param relatedModelName The name of the related model
79
+ * @param index The index of the relation
80
+ * @returns A shortened relation name
81
+ */
82
+ static getShortenedRelationName(modelName: string, relatedModelName: string, index: number): string {
83
+ const fullRelationName = `${modelName}_${relatedModelName}_${index}`.toLowerCase();
84
+
85
+ if (fullRelationName.length <= 64) {
86
+ return fullRelationName;
87
+ }
88
+
89
+ const extraChars = 2 + String(index).length;
90
+ const availableChars = 64 - extraChars;
91
+
92
+ const modelNameLength = modelName.length;
93
+ const relatedModelNameLength = relatedModelName.length;
94
+ const totalLength = modelNameLength + relatedModelNameLength;
95
+
96
+ const modelNameMaxLength = Math.floor(availableChars * (modelNameLength / totalLength));
97
+ const relatedModelNameMaxLength = availableChars - modelNameMaxLength;
98
+
99
+ const shortenedModelName = modelName.substring(0, Math.max(3, modelNameMaxLength));
100
+ const shortenedRelatedModelName = relatedModelName.substring(0, Math.max(3, relatedModelNameMaxLength));
101
+
102
+ // Create the new relation name
103
+ return `${shortenedModelName}_${shortenedRelatedModelName}_${index}`.toLowerCase();
104
+ }
105
+
106
+ /**
107
+ * Reset all relations (useful for testing)
108
+ */
109
+ static resetRelations(): void {
110
+ this.allRelations.clear();
111
+ }
112
+
113
+ /**
114
+ * Get all relations (useful for debugging)
115
+ */
116
+ static getAllRelations(): Map<string, {base: boolean | null, inversion: boolean | null}[]> {
117
+ return this.allRelations;
118
+ }
119
+ }