@memberjunction/codegen-lib 3.4.0 → 4.1.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 (94) hide show
  1. package/README.md +828 -630
  2. package/dist/Angular/angular-codegen.d.ts +10 -3
  3. package/dist/Angular/angular-codegen.d.ts.map +1 -1
  4. package/dist/Angular/angular-codegen.js +164 -199
  5. package/dist/Angular/angular-codegen.js.map +1 -1
  6. package/dist/Angular/entity-data-grid-related-entity-component.d.ts +1 -1
  7. package/dist/Angular/entity-data-grid-related-entity-component.d.ts.map +1 -1
  8. package/dist/Angular/entity-data-grid-related-entity-component.js +8 -10
  9. package/dist/Angular/entity-data-grid-related-entity-component.js.map +1 -1
  10. package/dist/Angular/join-grid-related-entity-component.d.ts +1 -1
  11. package/dist/Angular/join-grid-related-entity-component.js +8 -36
  12. package/dist/Angular/join-grid-related-entity-component.js.map +1 -1
  13. package/dist/Angular/related-entity-components.js +13 -79
  14. package/dist/Angular/related-entity-components.js.map +1 -1
  15. package/dist/Angular/timeline-related-entity-component.d.ts +1 -1
  16. package/dist/Angular/timeline-related-entity-component.js +14 -38
  17. package/dist/Angular/timeline-related-entity-component.js.map +1 -1
  18. package/dist/Config/config.d.ts.map +1 -1
  19. package/dist/Config/config.js +171 -177
  20. package/dist/Config/config.js.map +1 -1
  21. package/dist/Config/db-connection.d.ts +1 -1
  22. package/dist/Config/db-connection.d.ts.map +1 -1
  23. package/dist/Config/db-connection.js +6 -33
  24. package/dist/Config/db-connection.js.map +1 -1
  25. package/dist/Database/dbSchema.js +28 -35
  26. package/dist/Database/dbSchema.js.map +1 -1
  27. package/dist/Database/manage-metadata.d.ts +302 -6
  28. package/dist/Database/manage-metadata.d.ts.map +1 -1
  29. package/dist/Database/manage-metadata.js +1294 -445
  30. package/dist/Database/manage-metadata.js.map +1 -1
  31. package/dist/Database/reorder-columns.d.ts +1 -1
  32. package/dist/Database/reorder-columns.d.ts.map +1 -1
  33. package/dist/Database/reorder-columns.js +1 -5
  34. package/dist/Database/reorder-columns.js.map +1 -1
  35. package/dist/Database/sql.d.ts +1 -1
  36. package/dist/Database/sql.d.ts.map +1 -1
  37. package/dist/Database/sql.js +67 -98
  38. package/dist/Database/sql.js.map +1 -1
  39. package/dist/Database/sql_codegen.d.ts +15 -2
  40. package/dist/Database/sql_codegen.d.ts.map +1 -1
  41. package/dist/Database/sql_codegen.js +282 -253
  42. package/dist/Database/sql_codegen.js.map +1 -1
  43. package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts +11 -0
  44. package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts.map +1 -1
  45. package/dist/Manifest/GenerateClassRegistrationsManifest.js +43 -41
  46. package/dist/Manifest/GenerateClassRegistrationsManifest.js.map +1 -1
  47. package/dist/Misc/action_subclasses_codegen.d.ts.map +1 -1
  48. package/dist/Misc/action_subclasses_codegen.js +15 -26
  49. package/dist/Misc/action_subclasses_codegen.js.map +1 -1
  50. package/dist/Misc/advanced_generation.d.ts +69 -7
  51. package/dist/Misc/advanced_generation.d.ts.map +1 -1
  52. package/dist/Misc/advanced_generation.js +114 -75
  53. package/dist/Misc/advanced_generation.js.map +1 -1
  54. package/dist/Misc/createNewUser.d.ts +1 -1
  55. package/dist/Misc/createNewUser.js +22 -26
  56. package/dist/Misc/createNewUser.js.map +1 -1
  57. package/dist/Misc/entity_subclasses_codegen.d.ts +7 -2
  58. package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
  59. package/dist/Misc/entity_subclasses_codegen.js +56 -45
  60. package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
  61. package/dist/Misc/graphql_server_codegen.d.ts.map +1 -1
  62. package/dist/Misc/graphql_server_codegen.js +39 -42
  63. package/dist/Misc/graphql_server_codegen.js.map +1 -1
  64. package/dist/Misc/runCommand.d.ts +1 -1
  65. package/dist/Misc/runCommand.js +13 -20
  66. package/dist/Misc/runCommand.js.map +1 -1
  67. package/dist/Misc/sql_logging.d.ts +1 -1
  68. package/dist/Misc/sql_logging.d.ts.map +1 -1
  69. package/dist/Misc/sql_logging.js +21 -51
  70. package/dist/Misc/sql_logging.js.map +1 -1
  71. package/dist/Misc/status_logging.js +45 -60
  72. package/dist/Misc/status_logging.js.map +1 -1
  73. package/dist/Misc/system_integrity.d.ts +1 -1
  74. package/dist/Misc/system_integrity.d.ts.map +1 -1
  75. package/dist/Misc/system_integrity.js +12 -16
  76. package/dist/Misc/system_integrity.js.map +1 -1
  77. package/dist/Misc/temp_batch_file.js +15 -22
  78. package/dist/Misc/temp_batch_file.js.map +1 -1
  79. package/dist/Misc/util.d.ts.map +1 -1
  80. package/dist/Misc/util.js +17 -28
  81. package/dist/Misc/util.js.map +1 -1
  82. package/dist/__tests__/metadataConfig.test.d.ts +12 -0
  83. package/dist/__tests__/metadataConfig.test.d.ts.map +1 -0
  84. package/dist/__tests__/metadataConfig.test.js +604 -0
  85. package/dist/__tests__/metadataConfig.test.js.map +1 -0
  86. package/dist/index.d.ts +21 -21
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +21 -41
  89. package/dist/index.js.map +1 -1
  90. package/dist/runCodeGen.d.ts +1 -0
  91. package/dist/runCodeGen.d.ts.map +1 -1
  92. package/dist/runCodeGen.js +150 -178
  93. package/dist/runCodeGen.js.map +1 -1
  94. package/package.json +29 -23
@@ -1,45 +1,16 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.SQLCodeGenBase = exports.SPType = void 0;
30
- const core_1 = require("@memberjunction/core");
31
- const status_logging_1 = require("../Misc/status_logging");
32
- const fs = __importStar(require("fs"));
33
- const path_1 = __importDefault(require("path"));
34
- const sql_1 = require("./sql");
35
- const config_1 = require("../Config/config");
36
- const manage_metadata_1 = require("./manage-metadata");
37
- const sqlserver_dataprovider_1 = require("@memberjunction/sqlserver-dataprovider");
38
- const util_1 = require("../Misc/util");
39
- const global_1 = require("@memberjunction/global");
40
- const sql_logging_1 = require("../Misc/sql_logging");
41
- const temp_batch_file_1 = require("../Misc/temp_batch_file");
42
- exports.SPType = {
1
+ import { EntityInfo, Metadata } from '@memberjunction/core';
2
+ import { logError, logStatus, logWarning, startSpinner, updateSpinner, succeedSpinner, failSpinner } from '../Misc/status_logging.js';
3
+ import * as fs from 'fs';
4
+ import path from 'path';
5
+ import { SQLUtilityBase } from './sql.js';
6
+ import { autoIndexForeignKeys, configInfo, customSqlScripts, dbDatabase, mjCoreSchema, MAX_INDEX_NAME_LENGTH } from '../Config/config.js';
7
+ import { ManageMetadataBase } from './manage-metadata.js';
8
+ import { UserCache } from '@memberjunction/sqlserver-dataprovider';
9
+ import { combineFiles, logIf, sortBySequenceAndCreatedAt } from '../Misc/util.js';
10
+ import { MJGlobal } from '@memberjunction/global';
11
+ import { SQLLogging } from '../Misc/sql_logging.js';
12
+ import { TempBatchFile } from '../Misc/temp_batch_file.js';
13
+ export const SPType = {
43
14
  Create: 'Create',
44
15
  Update: 'Update',
45
16
  Delete: 'Delete',
@@ -49,53 +20,55 @@ exports.SPType = {
49
20
  * databases. The base class implements support for SQL Server. In future versions of MJ, we will break out an abstract base class that has the skeleton of the logic and then the SQL Server version will be a sub-class
50
21
  * of that abstract base class and other databases will be sub-classes of the abstract base class as well.
51
22
  */
52
- class SQLCodeGenBase {
53
- _sqlUtilityObject = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(sql_1.SQLUtilityBase);
23
+ export class SQLCodeGenBase {
24
+ constructor() {
25
+ this._sqlUtilityObject = MJGlobal.Instance.ClassFactory.CreateInstance(SQLUtilityBase);
26
+ /**
27
+ * Array of entity names that qualify for forced regeneration based on the whereClause filter
28
+ */
29
+ this.entitiesQualifiedForForcedRegeneration = [];
30
+ /**
31
+ * Flag indicating whether to filter entities for forced regeneration based on entityWhereClause
32
+ */
33
+ this.filterEntitiesQualifiedForRegeneration = false;
34
+ /**
35
+ * Tracks cascade delete dependencies between entities.
36
+ * Key: Entity ID whose update/delete SP is called by other entities' delete SPs
37
+ * Value: Set of Entity IDs that have CascadeDeletes=true and call this entity's update/delete SP
38
+ */
39
+ this.cascadeDeleteDependencies = new Map();
40
+ /**
41
+ * Tracks entities that need their delete stored procedures regenerated due to cascade dependencies
42
+ */
43
+ this.entitiesNeedingDeleteSPRegeneration = new Set();
44
+ /**
45
+ * Ordered list of entity IDs for delete SP regeneration (dependency order)
46
+ */
47
+ this.orderedEntitiesForDeleteSPRegeneration = [];
48
+ }
54
49
  get SQLUtilityObject() {
55
50
  return this._sqlUtilityObject;
56
51
  }
57
- /**
58
- * Array of entity names that qualify for forced regeneration based on the whereClause filter
59
- */
60
- entitiesQualifiedForForcedRegeneration = [];
61
- /**
62
- * Flag indicating whether to filter entities for forced regeneration based on entityWhereClause
63
- */
64
- filterEntitiesQualifiedForRegeneration = false;
65
- /**
66
- * Tracks cascade delete dependencies between entities.
67
- * Key: Entity ID whose update/delete SP is called by other entities' delete SPs
68
- * Value: Set of Entity IDs that have CascadeDeletes=true and call this entity's update/delete SP
69
- */
70
- cascadeDeleteDependencies = new Map();
71
- /**
72
- * Tracks entities that need their delete stored procedures regenerated due to cascade dependencies
73
- */
74
- entitiesNeedingDeleteSPRegeneration = new Set();
75
- /**
76
- * Ordered list of entity IDs for delete SP regeneration (dependency order)
77
- */
78
- orderedEntitiesForDeleteSPRegeneration = [];
79
52
  async manageSQLScriptsAndExecution(pool, entities, directory, currentUser) {
80
53
  try {
81
54
  // Build list of entities qualified for forced regeneration if entityWhereClause is provided
82
- if (config_1.configInfo.forceRegeneration?.enabled && config_1.configInfo.forceRegeneration?.entityWhereClause) {
55
+ if (configInfo.forceRegeneration?.enabled && configInfo.forceRegeneration?.entityWhereClause) {
83
56
  this.filterEntitiesQualifiedForRegeneration = true; // Enable filtering
84
57
  try {
85
- const whereClause = config_1.configInfo.forceRegeneration.entityWhereClause;
58
+ const whereClause = configInfo.forceRegeneration.entityWhereClause;
86
59
  const query = `
87
60
  SELECT Name
88
- FROM [${config_1.mjCoreSchema}].[Entity]
61
+ FROM [${mjCoreSchema}].[Entity]
89
62
  WHERE ${whereClause}
90
63
  `;
91
64
  const result = await pool.request().query(query);
92
65
  this.entitiesQualifiedForForcedRegeneration = result.recordset.map((r) => r.Name);
93
- (0, status_logging_1.logStatus)(`Force regeneration filter enabled: ${this.entitiesQualifiedForForcedRegeneration.length} entities qualified based on entityWhereClause: ${whereClause}`);
66
+ logStatus(`Force regeneration filter enabled: ${this.entitiesQualifiedForForcedRegeneration.length} entities qualified based on entityWhereClause: ${whereClause}`);
94
67
  }
95
68
  catch (error) {
96
- (0, status_logging_1.logError)(`CRITICAL ERROR: Failed to execute forceRegeneration.entityWhereClause query: ${error}`);
97
- (0, status_logging_1.logError)(`WHERE clause: ${config_1.configInfo.forceRegeneration.entityWhereClause}`);
98
- (0, status_logging_1.logError)(`Stopping execution due to invalid entityWhereClause configuration`);
69
+ logError(`CRITICAL ERROR: Failed to execute forceRegeneration.entityWhereClause query: ${error}`);
70
+ logError(`WHERE clause: ${configInfo.forceRegeneration.entityWhereClause}`);
71
+ logError(`Stopping execution due to invalid entityWhereClause configuration`);
99
72
  throw new Error(`Invalid forceRegeneration.entityWhereClause: ${error}`);
100
73
  }
101
74
  }
@@ -103,32 +76,32 @@ class SQLCodeGenBase {
103
76
  // we have custom base views, need to have them defined before we do
104
77
  // the rest as the generated stuff might use custom base views in compiled
105
78
  // objects like spCreate for a given entity might reference the vw for that entity
106
- (0, status_logging_1.startSpinner)('Running custom SQL scripts...');
79
+ startSpinner('Running custom SQL scripts...');
107
80
  const startTime = new Date();
108
81
  if (!await this.runCustomSQLScripts(pool, 'before-sql')) {
109
- (0, status_logging_1.failSpinner)('Failed to run custom SQL scripts');
82
+ failSpinner('Failed to run custom SQL scripts');
110
83
  return false;
111
84
  }
112
- (0, status_logging_1.succeedSpinner)(`Custom SQL scripts completed (${(new Date().getTime() - startTime.getTime()) / 1000}s)`);
85
+ succeedSpinner(`Custom SQL scripts completed (${(new Date().getTime() - startTime.getTime()) / 1000}s)`);
113
86
  // ALWAYS use the first filter where we only include entities that have IncludeInAPI = 1
114
87
  // Entities are already sorted by name in PostProcessEntityMetadata (see providerBase.ts)
115
88
  const baselineEntities = entities.filter(e => e.IncludeInAPI);
116
- const includedEntities = baselineEntities.filter(e => config_1.configInfo.excludeSchemas.find(s => s.toLowerCase() === e.SchemaName.toLowerCase()) === undefined); //only include entities that are NOT in the excludeSchemas list
117
- const excludedEntities = baselineEntities.filter(e => config_1.configInfo.excludeSchemas.find(s => s.toLowerCase() === e.SchemaName.toLowerCase()) !== undefined); //only include entities that ARE in the excludeSchemas list in this array
89
+ const includedEntities = baselineEntities.filter(e => configInfo.excludeSchemas.find(s => s.toLowerCase() === e.SchemaName.toLowerCase()) === undefined); //only include entities that are NOT in the excludeSchemas list
90
+ const excludedEntities = baselineEntities.filter(e => configInfo.excludeSchemas.find(s => s.toLowerCase() === e.SchemaName.toLowerCase()) !== undefined); //only include entities that ARE in the excludeSchemas list in this array
118
91
  // Initialize temp batch files for each schema
119
92
  // These will be populated as SQL is generated and will be used for actual execution
120
93
  const schemas = Array.from(new Set(baselineEntities.map(e => e.SchemaName)));
121
- temp_batch_file_1.TempBatchFile.initialize(directory, schemas);
94
+ TempBatchFile.initialize(directory, schemas);
122
95
  // STEP 1.5 - Check for cascade delete dependencies that require regeneration
123
- (0, status_logging_1.startSpinner)('Analyzing cascade delete dependencies...');
96
+ startSpinner('Analyzing cascade delete dependencies...');
124
97
  await this.markEntitiesForCascadeDeleteRegeneration(pool, includedEntities);
125
- (0, status_logging_1.succeedSpinner)('Cascade delete dependency analysis completed');
98
+ succeedSpinner('Cascade delete dependency analysis completed');
126
99
  // STEP 2(a) - clean out all *.generated.sql and *.permissions.generated.sql files from the directory
127
- (0, status_logging_1.startSpinner)('Cleaning generated files...');
100
+ startSpinner('Cleaning generated files...');
128
101
  this.deleteGeneratedEntityFiles(directory, baselineEntities);
129
- (0, status_logging_1.succeedSpinner)('Cleaned generated files');
102
+ succeedSpinner('Cleaned generated files');
130
103
  // STEP 2(b) - generate all the SQL files and execute them
131
- (0, status_logging_1.startSpinner)(`Generating SQL for ${includedEntities.length} entities...`);
104
+ startSpinner(`Generating SQL for ${includedEntities.length} entities...`);
132
105
  const step2StartTime = new Date();
133
106
  // First, separate entities that need cascade delete regeneration from others
134
107
  const entitiesWithoutCascadeRegeneration = includedEntities.filter(e => !this.entitiesNeedingDeleteSPRegeneration.has(e.ID));
@@ -147,12 +120,12 @@ class SQLCodeGenBase {
147
120
  enableSQLLoggingForNewOrModifiedEntities: true
148
121
  }); // enable sql logging for NEW entities....
149
122
  if (!genResult.Success) {
150
- (0, status_logging_1.failSpinner)('Failed to generate entity SQL files');
123
+ failSpinner('Failed to generate entity SQL files');
151
124
  return false;
152
125
  }
153
126
  // Generate SQL for cascade delete regenerations in dependency order (sequentially)
154
127
  if (entitiesForCascadeRegeneration.length > 0) {
155
- (0, status_logging_1.updateSpinner)(`Regenerating ${entitiesForCascadeRegeneration.length} delete SPs in dependency order...`);
128
+ updateSpinner(`Regenerating ${entitiesForCascadeRegeneration.length} delete SPs in dependency order...`);
156
129
  const cascadeGenResult = await this.generateAndExecuteEntitySQLToSeparateFiles({
157
130
  pool,
158
131
  entities: entitiesForCascadeRegeneration,
@@ -164,13 +137,13 @@ class SQLCodeGenBase {
164
137
  enableSQLLoggingForNewOrModifiedEntities: true
165
138
  });
166
139
  if (!cascadeGenResult.Success) {
167
- (0, status_logging_1.failSpinner)('Failed to regenerate cascade delete SPs');
140
+ failSpinner('Failed to regenerate cascade delete SPs');
168
141
  return false;
169
142
  }
170
143
  genResult.Files.push(...cascadeGenResult.Files);
171
144
  }
172
145
  // STEP 2(c) - for the excludedEntities, while we don't want to generate SQL, we do want to generate the permissions files for them
173
- (0, status_logging_1.updateSpinner)(`Generating permissions for ${excludedEntities.length} excluded entities...`);
146
+ updateSpinner(`Generating permissions for ${excludedEntities.length} excluded entities...`);
174
147
  const genResult2 = await this.generateAndExecuteEntitySQLToSeparateFiles({
175
148
  pool,
176
149
  entities: excludedEntities,
@@ -182,88 +155,88 @@ class SQLCodeGenBase {
182
155
  enableSQLLoggingForNewOrModifiedEntities: false /*don't log this stuff, it is just permissions for excluded entities*/
183
156
  });
184
157
  if (!genResult2.Success) {
185
- (0, status_logging_1.failSpinner)('Failed to generate permissions for excluded entities');
158
+ failSpinner('Failed to generate permissions for excluded entities');
186
159
  return false;
187
160
  }
188
- (0, status_logging_1.succeedSpinner)(`Entity generation completed (${(new Date().getTime() - step2StartTime.getTime()) / 1000}s)`);
161
+ succeedSpinner(`Entity generation completed (${(new Date().getTime() - step2StartTime.getTime()) / 1000}s)`);
189
162
  // STEP 2(d) now that we've generated the SQL, let's create a combined file in each schema sub-directory for convenience for a DBA
190
- (0, status_logging_1.startSpinner)('Creating combined SQL files...');
163
+ startSpinner('Creating combined SQL files...');
191
164
  const allEntityFiles = this.createCombinedEntitySQLFiles(directory, baselineEntities);
192
- (0, status_logging_1.succeedSpinner)(`Created combined SQL files for ${allEntityFiles.length} schemas`);
165
+ succeedSpinner(`Created combined SQL files for ${allEntityFiles.length} schemas`);
193
166
  // STEP 2(e) ---- FINALLY, we execute SQL in proper dependency order
194
167
  // Use temp batch files (which maintain CodeGen log order) if available, otherwise fall back to combined files
195
- (0, status_logging_1.startSpinner)('Executing entity SQL files...');
168
+ startSpinner('Executing entity SQL files...');
196
169
  const step2eStartTime = new Date();
197
170
  let executionSuccess = false;
198
- if (temp_batch_file_1.TempBatchFile.hasContent()) {
171
+ if (TempBatchFile.hasContent()) {
199
172
  // Execute temp batch files in dependency order (matches CodeGen run log)
200
- const tempFiles = temp_batch_file_1.TempBatchFile.getTempFilePaths();
201
- (0, util_1.logIf)(config_1.configInfo?.verboseOutput ?? false, `Executing ${tempFiles.length} temp batch file(s) in dependency order`);
202
- executionSuccess = await this.SQLUtilityObject.executeSQLFiles(tempFiles, config_1.configInfo?.verboseOutput ?? false);
173
+ const tempFiles = TempBatchFile.getTempFilePaths();
174
+ logIf(configInfo?.verboseOutput ?? false, `Executing ${tempFiles.length} temp batch file(s) in dependency order`);
175
+ executionSuccess = await this.SQLUtilityObject.executeSQLFiles(tempFiles, configInfo?.verboseOutput ?? false);
203
176
  // Clean up temp files after execution
204
- temp_batch_file_1.TempBatchFile.cleanup();
177
+ TempBatchFile.cleanup();
205
178
  }
206
179
  else {
207
180
  // Fall back to combined files (for backward compatibility or if temp files weren't created)
208
- (0, util_1.logIf)(config_1.configInfo?.verboseOutput ?? false, `Executing ${allEntityFiles.length} combined file(s)`);
209
- executionSuccess = await this.SQLUtilityObject.executeSQLFiles(allEntityFiles, config_1.configInfo?.verboseOutput ?? false);
181
+ logIf(configInfo?.verboseOutput ?? false, `Executing ${allEntityFiles.length} combined file(s)`);
182
+ executionSuccess = await this.SQLUtilityObject.executeSQLFiles(allEntityFiles, configInfo?.verboseOutput ?? false);
210
183
  }
211
184
  if (!executionSuccess) {
212
- (0, status_logging_1.failSpinner)('Failed to execute entity SQL files');
213
- temp_batch_file_1.TempBatchFile.cleanup(); // Cleanup on error
185
+ failSpinner('Failed to execute entity SQL files');
186
+ TempBatchFile.cleanup(); // Cleanup on error
214
187
  return false;
215
188
  }
216
189
  const step2eEndTime = new Date();
217
- (0, status_logging_1.succeedSpinner)(`SQL execution completed (${(step2eEndTime.getTime() - step2eStartTime.getTime()) / 1000}s)`);
218
- const manageMD = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(manage_metadata_1.ManageMetadataBase);
190
+ succeedSpinner(`SQL execution completed (${(step2eEndTime.getTime() - step2eStartTime.getTime()) / 1000}s)`);
191
+ const manageMD = MJGlobal.Instance.ClassFactory.CreateInstance(ManageMetadataBase);
219
192
  // STEP 3 - re-run the process to manage entity fields since the Step 1 and 2 above might have resulted in differences in base view columns compared to what we had at first
220
193
  // we CAN skip the entity field values part because that wouldn't change from the first time we ran it
221
194
  // Run advanced generation here in case new virtual fields added, so we do NOT run advanced geneartion in the main manageMetadata() call
222
- (0, status_logging_1.startSpinner)('Managing entity fields metadata...');
223
- if (!await manageMD.manageEntityFields(pool, config_1.configInfo.excludeSchemas, true, true, currentUser, false)) {
224
- (0, status_logging_1.failSpinner)('Failed to manage entity fields');
195
+ startSpinner('Managing entity fields metadata...');
196
+ if (!await manageMD.manageEntityFields(pool, configInfo.excludeSchemas, true, true, currentUser, false)) {
197
+ failSpinner('Failed to manage entity fields');
225
198
  return false;
226
199
  }
227
- (0, status_logging_1.succeedSpinner)('Entity fields metadata updated');
200
+ succeedSpinner('Entity fields metadata updated');
228
201
  // no logStatus/timer for this because manageEntityFields() has its own internal logging for this including the total, so it is redundant to log it here
229
202
  // STEP 4- Apply permissions, executing all .permissions files
230
- (0, status_logging_1.startSpinner)('Applying permissions...');
203
+ startSpinner('Applying permissions...');
231
204
  const step4StartTime = new Date();
232
205
  if (!await this.applyPermissions(pool, directory, baselineEntities)) {
233
- (0, status_logging_1.failSpinner)('Failed to apply permissions');
206
+ failSpinner('Failed to apply permissions');
234
207
  return false;
235
208
  }
236
- (0, status_logging_1.succeedSpinner)(`Permissions applied (${(new Date().getTime() - step4StartTime.getTime()) / 1000}s)`);
209
+ succeedSpinner(`Permissions applied (${(new Date().getTime() - step4StartTime.getTime()) / 1000}s)`);
237
210
  // STEP 5 - execute any custom SQL scripts that should run afterwards
238
- (0, status_logging_1.startSpinner)('Running post-generation SQL scripts...');
211
+ startSpinner('Running post-generation SQL scripts...');
239
212
  const step5StartTime = new Date();
240
213
  if (!await this.runCustomSQLScripts(pool, 'after-sql')) {
241
- (0, status_logging_1.failSpinner)('Failed to run post-generation SQL scripts');
214
+ failSpinner('Failed to run post-generation SQL scripts');
242
215
  return false;
243
216
  }
244
- (0, status_logging_1.succeedSpinner)(`Post-generation scripts completed (${(new Date().getTime() - step5StartTime.getTime()) / 1000}s)`);
245
- (0, status_logging_1.succeedSpinner)(`SQL CodeGen completed successfully (${((new Date().getTime() - startTime.getTime()) / 1000)}s total)`);
217
+ succeedSpinner(`Post-generation scripts completed (${(new Date().getTime() - step5StartTime.getTime()) / 1000}s)`);
218
+ succeedSpinner(`SQL CodeGen completed successfully (${((new Date().getTime() - startTime.getTime()) / 1000)}s total)`);
246
219
  // now - we need to tell our metadata object to refresh itself
247
- const md = new core_1.Metadata();
220
+ const md = new Metadata();
248
221
  await md.Refresh();
249
222
  return true;
250
223
  }
251
224
  catch (err) {
252
- (0, status_logging_1.logError)(err);
225
+ logError(err);
253
226
  // Clean up temp batch files on error
254
- temp_batch_file_1.TempBatchFile.cleanup();
227
+ TempBatchFile.cleanup();
255
228
  return false;
256
229
  }
257
230
  }
258
231
  async runCustomSQLScripts(pool, when) {
259
232
  try {
260
- const scripts = (0, config_1.customSqlScripts)(when);
233
+ const scripts = customSqlScripts(when);
261
234
  let bSuccess = true;
262
235
  if (scripts) {
263
236
  for (let i = 0; i < scripts.length; ++i) {
264
237
  const s = scripts[i];
265
238
  if (!await this.SQLUtilityObject.executeSQLFile(s.scriptFile)) {
266
- (0, status_logging_1.logError)(`Error executing custom '${when}' SQL script ${s.scriptFile}`);
239
+ logError(`Error executing custom '${when}' SQL script ${s.scriptFile}`);
267
240
  bSuccess = false; // keep going if we have more scripts, but make sure we return false
268
241
  }
269
242
  }
@@ -271,7 +244,7 @@ class SQLCodeGenBase {
271
244
  return bSuccess;
272
245
  }
273
246
  catch (e) {
274
- (0, status_logging_1.logError)(e);
247
+ logError(e);
275
248
  return false;
276
249
  }
277
250
  }
@@ -285,7 +258,7 @@ class SQLCodeGenBase {
285
258
  const files = this.getEntityPermissionFileNames(e);
286
259
  let innerSuccess = true;
287
260
  for (const f of files) {
288
- const fullPath = path_1.default.join(directory, f);
261
+ const fullPath = path.join(directory, f);
289
262
  if (fs.existsSync(fullPath)) {
290
263
  const fileBuffer = fs.readFileSync(fullPath);
291
264
  const fileContents = fileBuffer.toString();
@@ -293,28 +266,28 @@ class SQLCodeGenBase {
293
266
  await pool.request().query(fileContents);
294
267
  }
295
268
  catch (e) {
296
- (0, status_logging_1.logError)(`Error executing permissions file ${fullPath} for entity ${e.Name}: ${e}`);
269
+ logError(`Error executing permissions file ${fullPath} for entity ${e.Name}: ${e}`);
297
270
  innerSuccess = false;
298
271
  }
299
272
  }
300
273
  else {
301
274
  // we don't have the file, so we can't execute it, but we should log it as an error
302
275
  // and then keep going
303
- (0, status_logging_1.logError)(`Permissions file ${fullPath} does not exist for entity ${e.Name}`);
276
+ logError(`Permissions file ${fullPath} does not exist for entity ${e.Name}`);
304
277
  }
305
278
  }
306
279
  return innerSuccess;
307
280
  });
308
281
  const results = await Promise.all(promises);
309
282
  if (results.includes(false)) {
310
- (0, status_logging_1.logError)(`Error executing one or more permissions files in batch starting from index ${i}`);
283
+ logError(`Error executing one or more permissions files in batch starting from index ${i}`);
311
284
  bSuccess = false; // keep going, but will return false at the end
312
285
  }
313
286
  }
314
287
  return bSuccess;
315
288
  }
316
289
  catch (err) {
317
- (0, status_logging_1.logError)(err);
290
+ logError(err);
318
291
  return false;
319
292
  }
320
293
  }
@@ -337,7 +310,7 @@ class SQLCodeGenBase {
337
310
  const promises = batch.map(async (e) => {
338
311
  const pkeyField = e.Fields.find(f => f.IsPrimaryKey);
339
312
  if (!pkeyField) {
340
- (0, status_logging_1.logError)(`SKIPPING ENTITY: Entity ${e.Name}, because it does not have a primary key field defined. A table must have a primary key defined to quality to be a MemberJunction entity`);
313
+ logError(`SKIPPING SQL GENERATION: Entity ${e.Name} has no primary key field in metadata. If using soft primary keys, ensure metadata was refreshed after applySoftPKFKConfig().`);
341
314
  return { Success: false, Files: [] };
342
315
  }
343
316
  return this.generateAndExecuteSingleEntitySQLToSeparateFiles({
@@ -360,7 +333,7 @@ class SQLCodeGenBase {
360
333
  return { Success: !bFail, Files: files };
361
334
  }
362
335
  catch (err) {
363
- (0, status_logging_1.logError)(err);
336
+ logError(err);
364
337
  return { Success: false, Files: files };
365
338
  }
366
339
  }
@@ -369,7 +342,7 @@ class SQLCodeGenBase {
369
342
  // for the schemas associated with the specified entities, clean out all the generated files
370
343
  const schemaNames = entities.map(e => e.SchemaName).filter((value, index, self) => self.indexOf(value) === index);
371
344
  for (const s of schemaNames) {
372
- const fullPath = path_1.default.join(directory, s);
345
+ const fullPath = path.join(directory, s);
373
346
  // now, within each schema directory, clean out all the generated files
374
347
  // the generated files map this pattern: *.generated.sql or *.permissions.generated.sql
375
348
  let stats;
@@ -383,14 +356,14 @@ class SQLCodeGenBase {
383
356
  if (stats?.isDirectory()) {
384
357
  const files = fs.readdirSync(fullPath).filter(f => f.endsWith('.generated.sql') || f.endsWith('.permissions.generated.sql'));
385
358
  for (const f of files) {
386
- const filePath = path_1.default.join(fullPath, f);
359
+ const filePath = path.join(fullPath, f);
387
360
  fs.unlinkSync(filePath);
388
361
  }
389
362
  }
390
363
  }
391
364
  }
392
365
  catch (e) {
393
- (0, status_logging_1.logError)(e);
366
+ logError(e);
394
367
  }
395
368
  }
396
369
  createCombinedEntitySQLFiles(directory, entities) {
@@ -399,12 +372,12 @@ class SQLCodeGenBase {
399
372
  const schemaNames = entities.map(e => e.SchemaName).filter((value, index, self) => self.indexOf(value) === index);
400
373
  for (const s of schemaNames) {
401
374
  // generate the all-entities.sql file and all-entities.permissions.sql file in each schema folder
402
- const fullPath = path_1.default.join(directory, s);
375
+ const fullPath = path.join(directory, s);
403
376
  if (fs.statSync(fullPath).isDirectory()) {
404
- (0, util_1.combineFiles)(fullPath, '_all_entities.sql', '*.generated.sql', true);
405
- files.push(path_1.default.join(fullPath, '_all_entities.sql'));
406
- (0, util_1.combineFiles)(fullPath, '_all_entities.permissions.sql', '*.permissions.generated.sql', true);
407
- files.push(path_1.default.join(fullPath, '_all_entities.permissions.sql'));
377
+ combineFiles(fullPath, '_all_entities.sql', '*.generated.sql', true);
378
+ files.push(path.join(fullPath, '_all_entities.sql'));
379
+ combineFiles(fullPath, '_all_entities.permissions.sql', '*.permissions.generated.sql', true);
380
+ files.push(path.join(fullPath, '_all_entities.permissions.sql'));
408
381
  }
409
382
  }
410
383
  return files;
@@ -422,7 +395,7 @@ class SQLCodeGenBase {
422
395
  return { Success: true, Files: files };
423
396
  }
424
397
  catch (err) {
425
- (0, status_logging_1.logError)(err);
398
+ logError(err);
426
399
  return { Success: false, Files: [] };
427
400
  }
428
401
  }
@@ -431,20 +404,20 @@ class SQLCodeGenBase {
431
404
  let shouldLog = false;
432
405
  if (logSql) {
433
406
  // Check if entity is in new or modified lists
434
- const isNewOrModified = !!manage_metadata_1.ManageMetadataBase.newEntityList.find(e => e === entity.Name) ||
435
- !!manage_metadata_1.ManageMetadataBase.modifiedEntityList.find(e => e === entity.Name);
407
+ const isNewOrModified = !!ManageMetadataBase.newEntityList.find(e => e === entity.Name) ||
408
+ !!ManageMetadataBase.modifiedEntityList.find(e => e === entity.Name);
436
409
  // Check if entity is being regenerated due to cascade dependencies
437
410
  const isCascadeDependencyRegeneration = description.toLowerCase().includes('spdelete') &&
438
411
  this.entitiesNeedingDeleteSPRegeneration.has(entity.ID);
439
412
  // Check if force regeneration is enabled for relevant SQL types
440
- const isForceRegeneration = config_1.configInfo.forceRegeneration?.enabled && ((description.toLowerCase().includes('base view') && config_1.configInfo.forceRegeneration.baseViews) ||
441
- (description.toLowerCase().includes('root id function') && config_1.configInfo.forceRegeneration.baseViews) || // TVFs are part of base view infrastructure
442
- (description.toLowerCase().includes('spcreate') && config_1.configInfo.forceRegeneration.spCreate) ||
443
- (description.toLowerCase().includes('spupdate') && config_1.configInfo.forceRegeneration.spUpdate) ||
444
- (description.toLowerCase().includes('spdelete') && config_1.configInfo.forceRegeneration.spDelete) ||
445
- (description.toLowerCase().includes('index') && config_1.configInfo.forceRegeneration.indexes) ||
446
- (description.toLowerCase().includes('full text search') && config_1.configInfo.forceRegeneration.fullTextSearch) ||
447
- (config_1.configInfo.forceRegeneration.allStoredProcedures &&
413
+ const isForceRegeneration = configInfo.forceRegeneration?.enabled && ((description.toLowerCase().includes('base view') && configInfo.forceRegeneration.baseViews) ||
414
+ (description.toLowerCase().includes('root id function') && configInfo.forceRegeneration.baseViews) || // TVFs are part of base view infrastructure
415
+ (description.toLowerCase().includes('spcreate') && configInfo.forceRegeneration.spCreate) ||
416
+ (description.toLowerCase().includes('spupdate') && configInfo.forceRegeneration.spUpdate) ||
417
+ (description.toLowerCase().includes('spdelete') && configInfo.forceRegeneration.spDelete) ||
418
+ (description.toLowerCase().includes('index') && configInfo.forceRegeneration.indexes) ||
419
+ (description.toLowerCase().includes('full text search') && configInfo.forceRegeneration.fullTextSearch) ||
420
+ (configInfo.forceRegeneration.allStoredProcedures &&
448
421
  (description.toLowerCase().includes('spcreate') ||
449
422
  description.toLowerCase().includes('spupdate') ||
450
423
  description.toLowerCase().includes('spdelete'))));
@@ -478,10 +451,10 @@ class SQLCodeGenBase {
478
451
  }
479
452
  }
480
453
  if (shouldLog) {
481
- sql_logging_1.SQLLogging.appendToSQLLogFile(sql, description);
482
- temp_batch_file_1.TempBatchFile.appendToTempBatchFile(sql, entity.SchemaName);
454
+ SQLLogging.appendToSQLLogFile(sql, description);
455
+ TempBatchFile.appendToTempBatchFile(sql, entity.SchemaName);
483
456
  }
484
- (0, util_1.logIf)(config_1.configInfo.verboseOutput, `SQL Generated for ${entity.Name}: ${description}`);
457
+ logIf(configInfo.verboseOutput, `SQL Generated for ${entity.Name}: ${description}`);
485
458
  }
486
459
  async generateSingleEntitySQLToSeparateFiles(options) {
487
460
  const files = [];
@@ -490,18 +463,18 @@ class SQLCodeGenBase {
490
463
  if (options.writeFiles && !fs.existsSync(options.directory))
491
464
  fs.mkdirSync(options.directory, { recursive: true });
492
465
  // now do the same thing for the /schema directory within the provided directory
493
- const schemaDirectory = path_1.default.join(options.directory, options.entity.SchemaName);
466
+ const schemaDirectory = path.join(options.directory, options.entity.SchemaName);
494
467
  if (options.writeFiles && !fs.existsSync(schemaDirectory))
495
468
  fs.mkdirSync(schemaDirectory, { recursive: true }); // create the directory if it doesn't exist
496
469
  let sRet = '';
497
470
  let permissionsSQL = '';
498
- // Indexes for Fkeys for the table
499
- if (!options.onlyPermissions) {
500
- const shouldGenerateIndexes = (0, config_1.autoIndexForeignKeys)() || (config_1.configInfo.forceRegeneration?.enabled && config_1.configInfo.forceRegeneration?.indexes);
471
+ // Indexes for Fkeys for the table (skip for virtual entities — views can't have indexes)
472
+ if (!options.onlyPermissions && !options.entity.VirtualEntity) {
473
+ const shouldGenerateIndexes = autoIndexForeignKeys() || (configInfo.forceRegeneration?.enabled && configInfo.forceRegeneration?.indexes);
501
474
  const indexSQL = shouldGenerateIndexes ? this.generateIndexesForForeignKeys(options.pool, options.entity) : ''; // generate indexes if auto-indexing is on OR force regeneration is enabled
502
475
  const s = this.generateSingleEntitySQLFileHeader(options.entity, 'Index for Foreign Keys') + indexSQL;
503
476
  if (options.writeFiles) {
504
- const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('index', options.entity.SchemaName, options.entity.BaseTable, false, true));
477
+ const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('index', options.entity.SchemaName, options.entity.BaseTable, false, true));
505
478
  this.logSQLForNewOrModifiedEntity(options.entity, s, 'Index for Foreign Keys for ' + options.entity.BaseTable, options.enableSQLLoggingForNewOrModifiedEntities);
506
479
  fs.writeFileSync(filePath, s);
507
480
  files.push(filePath);
@@ -523,7 +496,7 @@ class SQLCodeGenBase {
523
496
  const functionName = `fn${options.entity.BaseTable}${field.Name}_GetRootID`;
524
497
  const s = this.generateSingleEntitySQLFileHeader(options.entity, functionName) +
525
498
  this.generateRootIDFunction(options.entity, field);
526
- const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('function', options.entity.SchemaName, functionName, false, true));
499
+ const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('function', options.entity.SchemaName, functionName, false, true));
527
500
  if (options.writeFiles) {
528
501
  this.logSQLForNewOrModifiedEntity(options.entity, s, `Root ID Function SQL for ${options.entity.Name}.${field.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
529
502
  fs.writeFileSync(filePath, s);
@@ -535,7 +508,7 @@ class SQLCodeGenBase {
535
508
  }
536
509
  // Generate the base view (which may reference the TVFs created above)
537
510
  const s = this.generateSingleEntitySQLFileHeader(options.entity, options.entity.BaseView) + await this.generateBaseView(options.pool, options.entity);
538
- const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('view', options.entity.SchemaName, options.entity.BaseView, false, true));
511
+ const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('view', options.entity.SchemaName, options.entity.BaseView, false, true));
539
512
  if (options.writeFiles) {
540
513
  this.logSQLForNewOrModifiedEntity(options.entity, s, `Base View SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
541
514
  fs.writeFileSync(filePath, s);
@@ -548,7 +521,7 @@ class SQLCodeGenBase {
548
521
  if (s.length > 0)
549
522
  permissionsSQL += s + '\nGO\n';
550
523
  if (options.writeFiles) {
551
- const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('view', options.entity.SchemaName, options.entity.BaseView, true, true));
524
+ const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('view', options.entity.SchemaName, options.entity.BaseView, true, true));
552
525
  fs.writeFileSync(filePath, s);
553
526
  this.logSQLForNewOrModifiedEntity(options.entity, s, `Base View Permissions SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
554
527
  files.push(filePath);
@@ -559,25 +532,25 @@ class SQLCodeGenBase {
559
532
  sRet += s + '\nGO\n';
560
533
  // CREATE SP
561
534
  if (options.entity.AllowCreateAPI && !options.entity.VirtualEntity) {
562
- const spName = this.getSPName(options.entity, exports.SPType.Create);
535
+ const spName = this.getSPName(options.entity, SPType.Create);
563
536
  // Only generate if spCreateGenerated is true (respects custom SPs where it's false)
564
537
  // forceRegeneration only forces regeneration of SPs where spCreateGenerated=true
565
538
  if (!options.onlyPermissions && options.entity.spCreateGenerated) {
566
539
  // generate the create SP
567
540
  const s = this.generateSingleEntitySQLFileHeader(options.entity, spName) + this.generateSPCreate(options.entity);
568
541
  if (options.writeFiles) {
569
- const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, false, true));
542
+ const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, false, true));
570
543
  this.logSQLForNewOrModifiedEntity(options.entity, s, `spCreate SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
571
544
  fs.writeFileSync(filePath, s);
572
545
  files.push(filePath);
573
546
  }
574
547
  sRet += s + '\nGO\n';
575
548
  }
576
- const s = this.generateSPPermissions(options.entity, spName, exports.SPType.Create) + '\n\n';
549
+ const s = this.generateSPPermissions(options.entity, spName, SPType.Create) + '\n\n';
577
550
  if (s.length > 0)
578
551
  permissionsSQL += s + '\nGO\n';
579
552
  if (options.writeFiles) {
580
- const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, true, true));
553
+ const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, true, true));
581
554
  this.logSQLForNewOrModifiedEntity(options.entity, s, `spCreate Permissions for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
582
555
  fs.writeFileSync(filePath, s);
583
556
  files.push(filePath);
@@ -589,25 +562,25 @@ class SQLCodeGenBase {
589
562
  }
590
563
  // UPDATE SP
591
564
  if (options.entity.AllowUpdateAPI && !options.entity.VirtualEntity) {
592
- const spName = this.getSPName(options.entity, exports.SPType.Update);
565
+ const spName = this.getSPName(options.entity, SPType.Update);
593
566
  // Only generate if spUpdateGenerated is true (respects custom SPs where it's false)
594
567
  // forceRegeneration only forces regeneration of SPs where spUpdateGenerated=true
595
568
  if (!options.onlyPermissions && options.entity.spUpdateGenerated) {
596
569
  // generate the update SP
597
570
  const s = this.generateSingleEntitySQLFileHeader(options.entity, spName) + this.generateSPUpdate(options.entity);
598
571
  if (options.writeFiles) {
599
- const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, false, true));
572
+ const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, false, true));
600
573
  fs.writeFileSync(filePath, s);
601
574
  this.logSQLForNewOrModifiedEntity(options.entity, s, `spUpdate SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
602
575
  files.push(filePath);
603
576
  }
604
577
  sRet += s + '\nGO\n';
605
578
  }
606
- const s = this.generateSPPermissions(options.entity, spName, exports.SPType.Update) + '\n\n';
579
+ const s = this.generateSPPermissions(options.entity, spName, SPType.Update) + '\n\n';
607
580
  if (s.length > 0)
608
581
  permissionsSQL += s + '\nGO\n';
609
582
  if (options.writeFiles) {
610
- const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, true, true));
583
+ const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, true, true));
611
584
  this.logSQLForNewOrModifiedEntity(options.entity, s, `spUpdate Permissions for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
612
585
  fs.writeFileSync(filePath, s);
613
586
  files.push(filePath);
@@ -619,7 +592,7 @@ class SQLCodeGenBase {
619
592
  }
620
593
  // DELETE SP
621
594
  if (options.entity.AllowDeleteAPI && !options.entity.VirtualEntity) {
622
- const spName = this.getSPName(options.entity, exports.SPType.Delete);
595
+ const spName = this.getSPName(options.entity, SPType.Delete);
623
596
  // Only generate if spDeleteGenerated is true (respects custom SPs where it's false)
624
597
  // OR if this entity has cascade delete dependencies that require regeneration
625
598
  // forceRegeneration only forces regeneration of SPs where spDeleteGenerated=true
@@ -628,22 +601,22 @@ class SQLCodeGenBase {
628
601
  this.entitiesNeedingDeleteSPRegeneration.has(options.entity.ID))) {
629
602
  // generate the delete SP
630
603
  if (this.entitiesNeedingDeleteSPRegeneration.has(options.entity.ID)) {
631
- (0, status_logging_1.logStatus)(` Regenerating ${spName} due to cascade dependency changes`);
604
+ logStatus(` Regenerating ${spName} due to cascade dependency changes`);
632
605
  }
633
606
  const s = this.generateSingleEntitySQLFileHeader(options.entity, spName) + this.generateSPDelete(options.entity);
634
607
  if (options.writeFiles) {
635
- const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, false, true));
608
+ const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, false, true));
636
609
  this.logSQLForNewOrModifiedEntity(options.entity, s, `spDelete SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
637
610
  fs.writeFileSync(filePath, s);
638
611
  files.push(filePath);
639
612
  }
640
613
  sRet += s + '\nGO\n';
641
614
  }
642
- const s = this.generateSPPermissions(options.entity, spName, exports.SPType.Delete) + '\n\n';
615
+ const s = this.generateSPPermissions(options.entity, spName, SPType.Delete) + '\n\n';
643
616
  if (s.length > 0)
644
617
  permissionsSQL += s + '\nGO\n';
645
618
  if (options.writeFiles) {
646
- const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, true, true));
619
+ const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, true, true));
647
620
  this.logSQLForNewOrModifiedEntity(options.entity, s, `spDelete Permissions for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
648
621
  fs.writeFileSync(filePath, s);
649
622
  files.push(filePath);
@@ -654,12 +627,12 @@ class SQLCodeGenBase {
654
627
  sRet += s + '\nGO\n';
655
628
  }
656
629
  // check to see if the options.entity supports full text search or not
657
- if (options.entity.FullTextSearchEnabled || (config_1.configInfo.forceRegeneration?.enabled && config_1.configInfo.forceRegeneration?.fullTextSearch)) {
630
+ if (options.entity.FullTextSearchEnabled || (configInfo.forceRegeneration?.enabled && configInfo.forceRegeneration?.fullTextSearch)) {
658
631
  // always generate the code so we can get the function name from the below function call
659
632
  const ft = await this.generateEntityFullTextSearchSQL(options.pool, options.entity);
660
633
  if (!options.onlyPermissions) {
661
634
  // only write the actual sql out if we're not only generating permissions
662
- const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('full_text_search_function', options.entity.SchemaName, options.entity.BaseTable, false, true));
635
+ const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('full_text_search_function', options.entity.SchemaName, options.entity.BaseTable, false, true));
663
636
  if (options.writeFiles) {
664
637
  this.logSQLForNewOrModifiedEntity(options.entity, ft.sql, `Full Text Search SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
665
638
  fs.writeFileSync(filePath, ft.sql);
@@ -670,7 +643,7 @@ class SQLCodeGenBase {
670
643
  const sP = this.generateFullTextSearchFunctionPermissions(options.entity, ft.functionName) + '\n\n';
671
644
  if (sP.length > 0)
672
645
  permissionsSQL += sP + '\nGO\n';
673
- const filePath = path_1.default.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('full_text_search_function', options.entity.SchemaName, options.entity.BaseTable, true, true));
646
+ const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('full_text_search_function', options.entity.SchemaName, options.entity.BaseTable, true, true));
674
647
  if (options.writeFiles) {
675
648
  this.logSQLForNewOrModifiedEntity(options.entity, sP, `Full Text Search Permissions for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities);
676
649
  fs.writeFileSync(filePath, sP);
@@ -684,17 +657,17 @@ class SQLCodeGenBase {
684
657
  return { sql: sRet, permissionsSQL: permissionsSQL, files: files };
685
658
  }
686
659
  catch (err) {
687
- (0, status_logging_1.logError)(err);
660
+ logError(err);
688
661
  return null;
689
662
  }
690
663
  }
691
664
  getSPName(entity, type) {
692
665
  switch (type) {
693
- case exports.SPType.Create:
666
+ case SPType.Create:
694
667
  return entity.spCreate && entity.spCreate.length > 0 ? entity.spCreate : 'spCreate' + entity.BaseTableCodeName;
695
- case exports.SPType.Update:
668
+ case SPType.Update:
696
669
  return entity.spUpdate && entity.spUpdate.length > 0 ? entity.spUpdate : 'spUpdate' + entity.BaseTableCodeName;
697
- case exports.SPType.Delete:
670
+ case SPType.Delete:
698
671
  return entity.spDelete && entity.spDelete.length > 0 ? entity.spDelete : 'spDelete' + entity.BaseTableCodeName;
699
672
  }
700
673
  }
@@ -706,11 +679,11 @@ class SQLCodeGenBase {
706
679
  if (!entity.VirtualEntity) {
707
680
  // only add each SP file if the Allow flags are set to true, doesn't matter if the SPs are generated or not, we always generate permissions
708
681
  if (entity.AllowCreateAPI)
709
- files.push(this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, this.getSPName(entity, exports.SPType.Create), true, true));
682
+ files.push(this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, this.getSPName(entity, SPType.Create), true, true));
710
683
  if (entity.AllowUpdateAPI)
711
- files.push(this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, this.getSPName(entity, exports.SPType.Update), true, true));
684
+ files.push(this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, this.getSPName(entity, SPType.Update), true, true));
712
685
  if (entity.AllowDeleteAPI)
713
- files.push(this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, this.getSPName(entity, exports.SPType.Delete), true, true));
686
+ files.push(this.SQLUtilityObject.getDBObjectFileName('sp', entity.SchemaName, this.getSPName(entity, SPType.Delete), true, true));
714
687
  }
715
688
  if (entity.FullTextSearchEnabled)
716
689
  files.push(this.SQLUtilityObject.getDBObjectFileName('full_text_search_function', entity.SchemaName, entity.BaseTable, true, true));
@@ -737,7 +710,7 @@ class SQLCodeGenBase {
737
710
  sOutput += this.generateSPCreate(entity) + '\n\n';
738
711
  else
739
712
  // custom SP, still generate the permissions
740
- sOutput += this.generateSPPermissions(entity, entity.spCreate, exports.SPType.Create) + '\n\n';
713
+ sOutput += this.generateSPPermissions(entity, entity.spCreate, SPType.Create) + '\n\n';
741
714
  }
742
715
  if (entity.AllowUpdateAPI && !entity.VirtualEntity) {
743
716
  if (entity.spUpdateGenerated)
@@ -745,7 +718,7 @@ class SQLCodeGenBase {
745
718
  sOutput += this.generateSPUpdate(entity) + '\n\n';
746
719
  else
747
720
  // custom SP, still generate the permissions
748
- sOutput += this.generateSPPermissions(entity, entity.spUpdate, exports.SPType.Update) + '\n\n';
721
+ sOutput += this.generateSPPermissions(entity, entity.spUpdate, SPType.Update) + '\n\n';
749
722
  }
750
723
  if (entity.AllowDeleteAPI && !entity.VirtualEntity) {
751
724
  if (entity.spDeleteGenerated)
@@ -753,7 +726,7 @@ class SQLCodeGenBase {
753
726
  sOutput += this.generateSPDelete(entity) + '\n\n';
754
727
  else
755
728
  // custom SP, still generate the permissions
756
- sOutput += this.generateSPPermissions(entity, entity.spDelete, exports.SPType.Delete) + '\n\n';
729
+ sOutput += this.generateSPPermissions(entity, entity.spDelete, SPType.Delete) + '\n\n';
757
730
  }
758
731
  // check to see if the entity supports full text search or not
759
732
  if (entity.FullTextSearchEnabled) {
@@ -763,7 +736,7 @@ class SQLCodeGenBase {
763
736
  }
764
737
  async generateEntityFullTextSearchSQL(pool, entity) {
765
738
  let sql = '';
766
- const catalogName = entity.FullTextCatalog && entity.FullTextCatalog.length > 0 ? entity.FullTextCatalog : config_1.dbDatabase + '_FullTextCatalog';
739
+ const catalogName = entity.FullTextCatalog && entity.FullTextCatalog.length > 0 ? entity.FullTextCatalog : dbDatabase + '_FullTextCatalog';
767
740
  if (entity.FullTextCatalogGenerated) {
768
741
  // this situation means we have a generated catalog and the user has provided a name specific to THIS entity
769
742
  sql += ` -- CREATE THE FULL TEXT CATALOG FOR THE ENTITY, IF NOT ALREADY CREATED
@@ -816,8 +789,8 @@ class SQLCodeGenBase {
816
789
  throw new Error(`FullTextSearchFunctionGenerated is true for entity ${entity.Name}, but no fields are marked as FullTextSearchEnabled`);
817
790
  if (!entity.FullTextSearchFunction || entity.FullTextSearchFunction.length === 0) {
818
791
  // update this in the DB
819
- const md = new core_1.Metadata();
820
- const u = sqlserver_dataprovider_1.UserCache.Instance.Users[0];
792
+ const md = new Metadata();
793
+ const u = UserCache.Instance.Users[0];
821
794
  if (!u)
822
795
  throw new Error('Could not find the first user in the cache, cant generate the full text search function without a user');
823
796
  const e = await md.GetEntityObject('Entities', u);
@@ -906,8 +879,8 @@ class SQLCodeGenBase {
906
879
  if (f.RelatedEntity && f.RelatedEntity.length > 0) {
907
880
  // we have an fkey, so generate the create index
908
881
  let indexName = `IDX_AUTO_MJ_FKEY_${entity.BaseTableCodeName}_${f.CodeName}`; // use code names in case the table and/or field names have special characters or spaces/etc
909
- if (indexName.length > config_1.MAX_INDEX_NAME_LENGTH)
910
- indexName = indexName.substring(0, config_1.MAX_INDEX_NAME_LENGTH); // truncate to max length if necessary
882
+ if (indexName.length > MAX_INDEX_NAME_LENGTH)
883
+ indexName = indexName.substring(0, MAX_INDEX_NAME_LENGTH); // truncate to max length if necessary
911
884
  if (sOutput.length > 0)
912
885
  sOutput += '\n\n'; // do this way so we don't end up with a trailing newline at end of the string/file
913
886
  sOutput += `-- Index for foreign key ${f.Name} in table ${entity.BaseTable}
@@ -1046,6 +1019,56 @@ GO
1046
1019
  generateRecursiveCTEJoins(recursiveFKs, classNameFirstChar, entity) {
1047
1020
  return this.generateRootIDJoins(recursiveFKs, classNameFirstChar, entity);
1048
1021
  }
1022
+ /**
1023
+ * Generates SELECT field expressions for IS-A parent entity columns.
1024
+ * Walks the ParentID chain upward, joining to each parent's base table, and includes
1025
+ * non-PK, non-timestamp, non-virtual fields from each parent table.
1026
+ * Returns a string starting with ',\n' if there are parent fields, or '' if none.
1027
+ */
1028
+ generateParentEntityFieldSelects(entity) {
1029
+ if (!entity.IsChildType)
1030
+ return '';
1031
+ const parentChain = entity.ParentChain;
1032
+ if (parentChain.length === 0)
1033
+ return '';
1034
+ const fieldExpressions = [];
1035
+ for (let i = 0; i < parentChain.length; i++) {
1036
+ const parent = parentChain[i];
1037
+ const alias = `__mj_isa_p${i + 1}`;
1038
+ for (const field of parent.Fields) {
1039
+ // Skip PKs (shared with child), timestamps, and virtual fields (view-computed)
1040
+ if (field.IsPrimaryKey || field.Name.startsWith('__mj_') || field.IsVirtual)
1041
+ continue;
1042
+ fieldExpressions.push(` ${alias}.[${field.Name}]`);
1043
+ }
1044
+ }
1045
+ if (fieldExpressions.length === 0)
1046
+ return '';
1047
+ return ',\n' + fieldExpressions.join(',\n');
1048
+ }
1049
+ /**
1050
+ * Generates INNER JOIN clauses for IS-A parent entity base tables.
1051
+ * Chains joins from child -> parent -> grandparent using PK-to-PK conditions.
1052
+ * Each parent is joined via its base table (not view) to avoid view dependency ordering issues.
1053
+ */
1054
+ generateParentEntityJoins(entity, classNameFirstChar) {
1055
+ if (!entity.IsChildType)
1056
+ return '';
1057
+ const parentChain = entity.ParentChain;
1058
+ if (parentChain.length === 0)
1059
+ return '';
1060
+ const joins = [];
1061
+ for (let i = 0; i < parentChain.length; i++) {
1062
+ const parent = parentChain[i];
1063
+ const parentAlias = `__mj_isa_p${i + 1}`;
1064
+ // First parent joins to child table alias; deeper parents chain to previous parent alias
1065
+ const sourceAlias = i === 0 ? classNameFirstChar : `__mj_isa_p${i}`;
1066
+ // Build PK-to-PK join condition (supports composite keys)
1067
+ const joinConditions = entity.PrimaryKeys.map(pk => `[${sourceAlias}].[${pk.Name}] = ${parentAlias}.[${pk.Name}]`).join(' AND ');
1068
+ joins.push(`INNER JOIN\n [${parent.SchemaName}].[${parent.BaseTable}] AS ${parentAlias}\n ON\n ${joinConditions}`);
1069
+ }
1070
+ return joins.join('\n');
1071
+ }
1049
1072
  async generateBaseView(pool, entity) {
1050
1073
  const viewName = entity.BaseView ? entity.BaseView : `vw${entity.CodeName}`;
1051
1074
  const classNameFirstChar = entity.ClassName.charAt(0).toLowerCase();
@@ -1053,12 +1076,15 @@ GO
1053
1076
  const relatedFieldsJoinString = this.generateBaseViewJoins(entity, entity.Fields);
1054
1077
  const permissions = this.generateViewPermissions(entity);
1055
1078
  const whereClause = entity.DeleteType === 'Soft' ? `WHERE
1056
- ${classNameFirstChar}.[${core_1.EntityInfo.DeletedAtFieldName}] IS NULL
1079
+ ${classNameFirstChar}.[${EntityInfo.DeletedAtFieldName}] IS NULL
1057
1080
  ` : '';
1058
1081
  // Detect recursive foreign keys and generate TVF joins and root field selects
1059
1082
  const recursiveFKs = this.detectRecursiveForeignKeys(entity);
1060
1083
  const rootFields = recursiveFKs.length > 0 ? this.generateRootFieldSelects(recursiveFKs, classNameFirstChar) : '';
1061
1084
  const rootJoins = recursiveFKs.length > 0 ? this.generateRootIDJoins(recursiveFKs, classNameFirstChar, entity) : '';
1085
+ // IS-A parent entity JOINs — walk ParentID chain, JOIN to each parent's base table
1086
+ const parentFieldsString = this.generateParentEntityFieldSelects(entity);
1087
+ const parentJoinsString = this.generateParentEntityJoins(entity, classNameFirstChar);
1062
1088
  return `
1063
1089
  ------------------------------------------------------------
1064
1090
  ----- BASE VIEW FOR ENTITY: ${entity.Name}
@@ -1073,9 +1099,9 @@ GO
1073
1099
  CREATE VIEW [${entity.SchemaName}].[${viewName}]
1074
1100
  AS
1075
1101
  SELECT
1076
- ${classNameFirstChar}.*${relatedFieldsString.length > 0 ? ',' : ''}${relatedFieldsString}${rootFields}
1102
+ ${classNameFirstChar}.*${parentFieldsString}${relatedFieldsString.length > 0 ? ',' : ''}${relatedFieldsString}${rootFields}
1077
1103
  FROM
1078
- [${entity.SchemaName}].[${entity.BaseTable}] AS ${classNameFirstChar}${relatedFieldsJoinString ? '\n' + relatedFieldsJoinString : ''}${rootJoins}
1104
+ [${entity.SchemaName}].[${entity.BaseTable}] AS ${classNameFirstChar}${parentJoinsString ? '\n' + parentJoinsString : ''}${relatedFieldsJoinString ? '\n' + relatedFieldsJoinString : ''}${rootJoins}
1079
1105
  ${whereClause}GO${permissions}
1080
1106
  `;
1081
1107
  }
@@ -1109,8 +1135,8 @@ ${whereClause}GO${permissions}
1109
1135
  async generateBaseViewRelatedFieldsString(pool, entityFields) {
1110
1136
  let sOutput = '';
1111
1137
  let fieldCount = 0;
1112
- const manageMD = global_1.MJGlobal.Instance.ClassFactory.CreateInstance(manage_metadata_1.ManageMetadataBase);
1113
- const md = new core_1.Metadata();
1138
+ const manageMD = MJGlobal.Instance.ClassFactory.CreateInstance(ManageMetadataBase);
1139
+ const md = new Metadata();
1114
1140
  const allGeneratedAliases = [];
1115
1141
  // Get fields that are related entities with join field configuration.
1116
1142
  //
@@ -1145,7 +1171,7 @@ ${whereClause}GO${permissions}
1145
1171
  // This happens in table-per-type inheritance where child.ID is FK to parent.ID
1146
1172
  // stripID("ID") returns "" which would generate invalid SQL: AS []
1147
1173
  if (candidateName.trim().length === 0) {
1148
- (0, status_logging_1.logStatus)(` Skipping related entity name field for ${ef.Name} in entity - stripID returned empty string (likely inheritance pattern)`);
1174
+ logStatus(` Skipping related entity name field for ${ef.Name} in entity - stripID returned empty string (likely inheritance pattern)`);
1149
1175
  }
1150
1176
  else {
1151
1177
  // check to make sure candidateName is not already a field name in the base table (other than a virtual field of course, as that is what we're creating)
@@ -1184,12 +1210,12 @@ ${whereClause}GO${permissions}
1184
1210
  const alias = fieldConfig.alias || this.generateDefaultAlias(ef.Name, fieldName);
1185
1211
  // Validate field exists on related entity
1186
1212
  if (!this.validateFieldExists(ef.RelatedEntity, fieldName)) {
1187
- (0, status_logging_1.logError)(`RelatedEntityJoinFields: Field '${fieldName}' not found on entity '${ef.RelatedEntity}' (FK: ${ef.Name})`);
1213
+ logError(`RelatedEntityJoinFields: Field '${fieldName}' not found on entity '${ef.RelatedEntity}' (FK: ${ef.Name})`);
1188
1214
  continue;
1189
1215
  }
1190
1216
  // Check for alias collisions
1191
1217
  if (currentEntity && this.hasAliasCollision(currentEntity, alias, allGeneratedAliases)) {
1192
- (0, status_logging_1.logError)(`RelatedEntityJoinFields: Alias '${alias}' for field '${fieldName}' would collide with an existing field or alias in entity '${currentEntity.Name}'`);
1218
+ logError(`RelatedEntityJoinFields: Alias '${alias}' for field '${fieldName}' would collide with an existing field or alias in entity '${currentEntity.Name}'`);
1193
1219
  continue;
1194
1220
  }
1195
1221
  // Get field metadata from related entity to check if virtual
@@ -1219,7 +1245,7 @@ ${whereClause}GO${permissions}
1219
1245
  return sOutput;
1220
1246
  }
1221
1247
  getIsNameFieldForSingleEntity(entityName) {
1222
- const md = new core_1.Metadata(); // use the full metadata entity list, not the filtered version that we receive
1248
+ const md = new Metadata(); // use the full metadata entity list, not the filtered version that we receive
1223
1249
  const e = md.Entities.find(e => e.Name === entityName);
1224
1250
  if (e) {
1225
1251
  const ef = e.NameField;
@@ -1227,7 +1253,7 @@ ${whereClause}GO${permissions}
1227
1253
  return { nameField: ef.Name, nameFieldIsVirtual: ef.IsVirtual };
1228
1254
  }
1229
1255
  else
1230
- (0, status_logging_1.logStatus)(`ERROR: Could not find entity with name ${entityName}`);
1256
+ logStatus(`ERROR: Could not find entity with name ${entityName}`);
1231
1257
  return { nameField: '', nameFieldIsVirtual: false };
1232
1258
  }
1233
1259
  stripID(name) {
@@ -1244,7 +1270,7 @@ ${whereClause}GO${permissions}
1244
1270
  return baseName + relatedFieldName;
1245
1271
  }
1246
1272
  validateFieldExists(entityName, fieldName) {
1247
- const md = new core_1.Metadata();
1273
+ const md = new Metadata();
1248
1274
  const entity = md.Entities.find(e => e.Name === entityName);
1249
1275
  if (!entity)
1250
1276
  return false;
@@ -1258,7 +1284,7 @@ ${whereClause}GO${permissions}
1258
1284
  if (generatedAliases.some(a => a.toLowerCase() === alias.toLowerCase()))
1259
1285
  return true;
1260
1286
  // Check against system fields
1261
- const systemFields = ['__mj_CreatedAt', '__mj_UpdatedAt', core_1.EntityInfo.DeletedAtFieldName];
1287
+ const systemFields = ['__mj_CreatedAt', '__mj_UpdatedAt', EntityInfo.DeletedAtFieldName];
1262
1288
  if (systemFields.some(sf => sf?.toLowerCase() === alias.toLowerCase()))
1263
1289
  return true;
1264
1290
  return false;
@@ -1267,9 +1293,9 @@ ${whereClause}GO${permissions}
1267
1293
  let sOutput = '';
1268
1294
  for (let i = 0; i < entity.Permissions.length; i++) {
1269
1295
  const ep = entity.Permissions[i];
1270
- if ((type == exports.SPType.Create && ep.CanCreate) ||
1271
- (type == exports.SPType.Update && ep.CanUpdate) ||
1272
- (type == exports.SPType.Delete && ep.CanDelete)) {
1296
+ if ((type == SPType.Create && ep.CanCreate) ||
1297
+ (type == SPType.Update && ep.CanUpdate) ||
1298
+ (type == SPType.Delete && ep.CanDelete)) {
1273
1299
  if (ep.RoleSQLName && ep.RoleSQLName.length > 0) {
1274
1300
  sOutput += (sOutput === '' ? `GRANT EXECUTE ON [${entity.SchemaName}].[${spName}] TO ` : ', ') + `[${ep.RoleSQLName}]`;
1275
1301
  }
@@ -1295,7 +1321,7 @@ ${whereClause}GO${permissions}
1295
1321
  //double exclamations used on the firstKey.DefaultValue property otherwise the type of this variable is 'number | ""';
1296
1322
  const primaryKeyAutomatic = firstKey.AutoIncrement; // Only exclude auto-increment fields, allow manual override for all other PKs including UUIDs with defaults
1297
1323
  const efString = this.createEntityFieldsParamString(entity.Fields, false); // Always pass false for isUpdate since this is generateSPCreate
1298
- const permissions = this.generateSPPermissions(entity, spName, exports.SPType.Create);
1324
+ const permissions = this.generateSPPermissions(entity, spName, SPType.Create);
1299
1325
  let preInsertCode = '';
1300
1326
  let outputCode = '';
1301
1327
  let selectInsertedRecord = '';
@@ -1398,12 +1424,12 @@ GO${permissions}
1398
1424
  `;
1399
1425
  }
1400
1426
  generateUpdatedAtTrigger(entity) {
1401
- const updatedAtField = entity.Fields.find(f => f.Name.toLowerCase().trim() === core_1.EntityInfo.UpdatedAtFieldName.toLowerCase().trim());
1427
+ const updatedAtField = entity.Fields.find(f => f.Name.toLowerCase().trim() === EntityInfo.UpdatedAtFieldName.toLowerCase().trim());
1402
1428
  if (!updatedAtField)
1403
1429
  return '';
1404
1430
  const triggerStatement = `
1405
1431
  ------------------------------------------------------------
1406
- ----- TRIGGER FOR ${core_1.EntityInfo.UpdatedAtFieldName} field for the ${entity.BaseTable} table
1432
+ ----- TRIGGER FOR ${EntityInfo.UpdatedAtFieldName} field for the ${entity.BaseTable} table
1407
1433
  ------------------------------------------------------------
1408
1434
  IF OBJECT_ID('[${entity.SchemaName}].[trgUpdate${entity.ClassName}]', 'TR') IS NOT NULL
1409
1435
  DROP TRIGGER [${entity.SchemaName}].[trgUpdate${entity.ClassName}];
@@ -1417,7 +1443,7 @@ BEGIN
1417
1443
  UPDATE
1418
1444
  [${entity.SchemaName}].[${entity.BaseTable}]
1419
1445
  SET
1420
- ${core_1.EntityInfo.UpdatedAtFieldName} = GETUTCDATE()
1446
+ ${EntityInfo.UpdatedAtFieldName} = GETUTCDATE()
1421
1447
  FROM
1422
1448
  [${entity.SchemaName}].[${entity.BaseTable}] AS _organicTable
1423
1449
  INNER JOIN
@@ -1430,8 +1456,8 @@ GO`;
1430
1456
  generateSPUpdate(entity) {
1431
1457
  const spName = entity.spUpdate ? entity.spUpdate : `spUpdate${entity.BaseTableCodeName}`;
1432
1458
  const efParamString = this.createEntityFieldsParamString(entity.Fields, true);
1433
- const permissions = this.generateSPPermissions(entity, spName, exports.SPType.Update);
1434
- const hasUpdatedAtField = entity.Fields.find(f => f.Name.toLowerCase().trim() === core_1.EntityInfo.UpdatedAtFieldName.trim().toLowerCase()) !== undefined;
1459
+ const permissions = this.generateSPPermissions(entity, spName, SPType.Update);
1460
+ const hasUpdatedAtField = entity.Fields.find(f => f.Name.toLowerCase().trim() === EntityInfo.UpdatedAtFieldName.trim().toLowerCase()) !== undefined;
1435
1461
  const updatedAtTrigger = hasUpdatedAtField ? this.generateUpdatedAtTrigger(entity) : '';
1436
1462
  let selectUpdatedRecord = `SELECT
1437
1463
  *
@@ -1625,7 +1651,7 @@ ${updatedAtTrigger}
1625
1651
  generateSPDelete(entity) {
1626
1652
  const spName = entity.spDelete ? entity.spDelete : `spDelete${entity.BaseTableCodeName}`;
1627
1653
  const sCascadeDeletes = this.generateCascadeDeletes(entity);
1628
- const permissions = this.generateSPPermissions(entity, spName, exports.SPType.Delete);
1654
+ const permissions = this.generateSPPermissions(entity, spName, SPType.Delete);
1629
1655
  let sVariables = '';
1630
1656
  let sSelect = '';
1631
1657
  for (let k of entity.PrimaryKeys) {
@@ -1650,8 +1676,8 @@ ${deleteCode}`;
1650
1676
  deleteCode = ` UPDATE
1651
1677
  [${entity.SchemaName}].[${entity.BaseTable}]
1652
1678
  SET
1653
- ${core_1.EntityInfo.DeletedAtFieldName} = GETUTCDATE()
1654
- ${deleteCode} AND ${core_1.EntityInfo.DeletedAtFieldName} IS NULL -- don't update the record if it's already been deleted via a soft delete`;
1679
+ ${EntityInfo.DeletedAtFieldName} = GETUTCDATE()
1680
+ ${deleteCode} AND ${EntityInfo.DeletedAtFieldName} IS NULL -- don't update the record if it's already been deleted via a soft delete`;
1655
1681
  }
1656
1682
  // Build the NULL select statement for when no rows are affected
1657
1683
  let sNullSelect = '';
@@ -1688,7 +1714,7 @@ GO${permissions}
1688
1714
  generateCascadeDeletes(entity) {
1689
1715
  let sOutput = '';
1690
1716
  if (entity.CascadeDeletes) {
1691
- const md = new core_1.Metadata();
1717
+ const md = new Metadata();
1692
1718
  // Find all fields in other entities that are foreign keys to this entity
1693
1719
  for (const e of md.Entities) {
1694
1720
  for (const ef of e.Fields) {
@@ -1718,7 +1744,7 @@ GO${permissions}
1718
1744
  // Nullable FK but no update API - this is a configuration error
1719
1745
  const sqlComment = `WARNING: ${relatedEntity.BaseTable} has nullable FK to ${parentEntity.BaseTable} but doesn't allow update API - cascade operation will fail`;
1720
1746
  const consoleMsg = `WARNING in spDelete${parentEntity.BaseTableCodeName} generation: ${relatedEntity.BaseTable} has nullable FK to ${parentEntity.BaseTable} but doesn't allow update API - cascade operation will fail`;
1721
- (0, status_logging_1.logWarning)(consoleMsg);
1747
+ logWarning(consoleMsg);
1722
1748
  return `
1723
1749
  -- ${sqlComment}
1724
1750
  -- This will cause the delete operation to fail due to referential integrity`;
@@ -1727,7 +1753,7 @@ GO${permissions}
1727
1753
  // Entity doesn't allow delete API, so we can't cascade delete
1728
1754
  const sqlComment = `WARNING: ${relatedEntity.BaseTable} has non-nullable FK to ${parentEntity.BaseTable} but doesn't allow delete API - cascade operation will fail`;
1729
1755
  const consoleMsg = `WARNING in spDelete${parentEntity.BaseTableCodeName} generation: ${relatedEntity.BaseTable} has non-nullable FK to ${parentEntity.BaseTable} but doesn't allow delete API - cascade operation will fail`;
1730
- (0, status_logging_1.logWarning)(consoleMsg);
1756
+ logWarning(consoleMsg);
1731
1757
  return `
1732
1758
  -- ${sqlComment}
1733
1759
  -- This will cause a referential integrity violation`;
@@ -1738,43 +1764,47 @@ GO${permissions}
1738
1764
  // Build the WHERE clause for matching foreign key(s)
1739
1765
  // TODO: Future enhancement to support composite foreign keys
1740
1766
  const whereClause = `[${fkField.CodeName}] = @${parentEntity.FirstPrimaryKey.CodeName}`;
1741
- // Generate unique cursor name using entity code names
1742
- const cursorName = `cascade_${operation}_${relatedEntity.CodeName}_cursor`;
1767
+ // Generate unique cursor name using entity code name AND FK field name
1768
+ // This ensures uniqueness when an entity has multiple FKs pointing to the same parent
1769
+ // (e.g., AIPromptRun.ParentID and AIPromptRun.RerunFromPromptRunID both reference AIPromptRun)
1770
+ const cursorName = `cascade_${operation}_${relatedEntity.CodeName}_${fkField.CodeName}_cursor`;
1771
+ // Use a combined prefix that includes both entity name and FK field name to ensure unique variable names
1772
+ const variablePrefix = `${relatedEntity.CodeName}_${fkField.CodeName}`;
1743
1773
  // Determine which SP to call
1744
- const spType = operation === 'delete' ? exports.SPType.Delete : exports.SPType.Update;
1774
+ const spType = operation === 'delete' ? SPType.Delete : SPType.Update;
1745
1775
  const spName = this.getSPName(relatedEntity, spType);
1746
1776
  if (operation === 'update') {
1747
1777
  // For update, we need to include all updateable fields
1748
- // Use the related entity's code name as prefix to ensure uniqueness
1749
- const updateParams = this.buildUpdateCursorParameters(relatedEntity, fkField, relatedEntity.CodeName);
1778
+ // Use the combined prefix to ensure uniqueness across multiple FKs to same entity
1779
+ const updateParams = this.buildUpdateCursorParameters(relatedEntity, fkField, variablePrefix);
1750
1780
  const spCallParams = updateParams.allParams;
1751
1781
  return `
1752
1782
  -- Cascade update on ${relatedEntity.BaseTable} using cursor to call ${spName}
1753
1783
  ${updateParams.declarations}
1754
- DECLARE ${cursorName} CURSOR FOR
1784
+ DECLARE ${cursorName} CURSOR FOR
1755
1785
  SELECT ${updateParams.selectFields}
1756
1786
  FROM [${relatedEntity.SchemaName}].[${relatedEntity.BaseTable}]
1757
1787
  WHERE ${whereClause}
1758
-
1788
+
1759
1789
  OPEN ${cursorName}
1760
1790
  FETCH NEXT FROM ${cursorName} INTO ${updateParams.fetchInto}
1761
-
1791
+
1762
1792
  WHILE @@FETCH_STATUS = 0
1763
1793
  BEGIN
1764
1794
  -- Set the FK field to NULL
1765
- SET @${relatedEntity.CodeName}_${fkField.CodeName} = NULL
1766
-
1795
+ SET @${variablePrefix}_${fkField.CodeName} = NULL
1796
+
1767
1797
  -- Call the update SP for the related entity
1768
1798
  EXEC [${relatedEntity.SchemaName}].[${spName}] ${spCallParams}
1769
-
1799
+
1770
1800
  FETCH NEXT FROM ${cursorName} INTO ${updateParams.fetchInto}
1771
1801
  END
1772
-
1802
+
1773
1803
  CLOSE ${cursorName}
1774
1804
  DEALLOCATE ${cursorName}`;
1775
1805
  }
1776
1806
  // For delete operation, use a simpler prefix for primary keys only
1777
- const pkComponents = this.buildPrimaryKeyComponents(relatedEntity, relatedEntity.CodeName);
1807
+ const pkComponents = this.buildPrimaryKeyComponents(relatedEntity, variablePrefix);
1778
1808
  return `
1779
1809
  -- Cascade delete from ${relatedEntity.BaseTable} using cursor to call ${spName}
1780
1810
  DECLARE ${pkComponents.varDeclarations}
@@ -1835,7 +1865,7 @@ GO${permissions}
1835
1865
  fetchInto = pkComponents.fetchInto;
1836
1866
  allParams = pkComponents.spParams;
1837
1867
  // Then, add all updateable fields with the same prefix
1838
- const sortedFields = (0, util_1.sortBySequenceAndCreatedAt)(entity.Fields);
1868
+ const sortedFields = sortBySequenceAndCreatedAt(entity.Fields);
1839
1869
  for (const ef of sortedFields) {
1840
1870
  if (!ef.IsPrimaryKey && !ef.IsVirtual && ef.AllowUpdateAPI && !ef.AutoIncrement && !ef.IsSpecialDateField) {
1841
1871
  if (declarations !== '')
@@ -1861,7 +1891,7 @@ GO${permissions}
1861
1891
  */
1862
1892
  analyzeCascadeDeleteDependencies(entity) {
1863
1893
  if (entity.CascadeDeletes) {
1864
- const md = new core_1.Metadata();
1894
+ const md = new Metadata();
1865
1895
  // Find all fields in other entities that are foreign keys to this entity
1866
1896
  for (const e of md.Entities) {
1867
1897
  for (const ef of e.Fields) {
@@ -1903,7 +1933,7 @@ GO${permissions}
1903
1933
  async buildCascadeDeleteDependencies(entities) {
1904
1934
  // Clear existing dependencies
1905
1935
  this.cascadeDeleteDependencies.clear();
1906
- (0, status_logging_1.logStatus)(`Building cascade delete dependencies...`);
1936
+ logStatus(`Building cascade delete dependencies...`);
1907
1937
  // Analyze cascade deletes for each entity with CascadeDeletes=true
1908
1938
  // This will populate the cascadeDeleteDependencies map WITHOUT generating SQL
1909
1939
  let entitiesWithCascadeDeletes = 0;
@@ -1911,23 +1941,23 @@ GO${permissions}
1911
1941
  if (entity.CascadeDeletes) {
1912
1942
  entitiesWithCascadeDeletes++;
1913
1943
  if (entity.spDeleteGenerated) {
1914
- (0, status_logging_1.logStatus)(` Analyzing cascade deletes for ${entity.Name}...`);
1944
+ logStatus(` Analyzing cascade deletes for ${entity.Name}...`);
1915
1945
  this.analyzeCascadeDeleteDependencies(entity);
1916
1946
  }
1917
1947
  else {
1918
- (0, status_logging_1.logStatus)(` Skipping ${entity.Name} - has CascadeDeletes but spDeleteGenerated=false`);
1948
+ logStatus(` Skipping ${entity.Name} - has CascadeDeletes but spDeleteGenerated=false`);
1919
1949
  }
1920
1950
  }
1921
1951
  }
1922
- (0, status_logging_1.logStatus)(`Total entities with CascadeDeletes=true: ${entitiesWithCascadeDeletes}`);
1952
+ logStatus(`Total entities with CascadeDeletes=true: ${entitiesWithCascadeDeletes}`);
1923
1953
  // Log the dependency map
1924
- (0, status_logging_1.logStatus)(`Cascade delete dependency map built:`);
1954
+ logStatus(`Cascade delete dependency map built:`);
1925
1955
  for (const [dependedOnEntityId, dependentEntityIds] of this.cascadeDeleteDependencies) {
1926
1956
  const dependedOnEntity = entities.find(e => e.ID === dependedOnEntityId);
1927
1957
  const dependentNames = Array.from(dependentEntityIds)
1928
1958
  .map(id => entities.find(e => e.ID === id)?.Name || id)
1929
1959
  .join(', ');
1930
- (0, status_logging_1.logStatus)(` ${dependedOnEntity?.Name || dependedOnEntityId} is depended on by: ${dependentNames}`);
1960
+ logStatus(` ${dependedOnEntity?.Name || dependedOnEntityId} is depended on by: ${dependentNames}`);
1931
1961
  }
1932
1962
  }
1933
1963
  /**
@@ -1937,8 +1967,8 @@ GO${permissions}
1937
1967
  async getModifiedEntitiesWithUpdateAPI(entities) {
1938
1968
  const modifiedEntitiesMap = new Map();
1939
1969
  // Get the list of modified entity names from the metadata management phase
1940
- const modifiedEntityNames = manage_metadata_1.ManageMetadataBase.modifiedEntityList;
1941
- (0, status_logging_1.logStatus)(`Modified entities from metadata phase: ${modifiedEntityNames.join(', ')}`);
1970
+ const modifiedEntityNames = ManageMetadataBase.modifiedEntityList;
1971
+ logStatus(`Modified entities from metadata phase: ${modifiedEntityNames.join(', ')}`);
1942
1972
  // Convert entity names to IDs and filter for those with update API
1943
1973
  for (const entityName of modifiedEntityNames) {
1944
1974
  const entity = entities.find(e => e.Name === entityName &&
@@ -1946,15 +1976,15 @@ GO${permissions}
1946
1976
  e.spUpdateGenerated);
1947
1977
  if (entity) {
1948
1978
  modifiedEntitiesMap.set(entity.Name, entity.ID);
1949
- (0, status_logging_1.logStatus)(` - ${entity.Name} (${entity.ID}) has update API and will be tracked`);
1979
+ logStatus(` - ${entity.Name} (${entity.ID}) has update API and will be tracked`);
1950
1980
  }
1951
1981
  else {
1952
1982
  const nonUpdateEntity = entities.find(e => e.Name === entityName);
1953
1983
  if (nonUpdateEntity) {
1954
- (0, status_logging_1.logStatus)(` - ${entityName} found but AllowUpdateAPI=${nonUpdateEntity.AllowUpdateAPI}, spUpdateGenerated=${nonUpdateEntity.spUpdateGenerated}`);
1984
+ logStatus(` - ${entityName} found but AllowUpdateAPI=${nonUpdateEntity.AllowUpdateAPI}, spUpdateGenerated=${nonUpdateEntity.spUpdateGenerated}`);
1955
1985
  }
1956
1986
  else {
1957
- (0, status_logging_1.logStatus)(` - ${entityName} not found in entities list`);
1987
+ logStatus(` - ${entityName} not found in entities list`);
1958
1988
  }
1959
1989
  }
1960
1990
  }
@@ -1988,38 +2018,38 @@ GO${permissions}
1988
2018
  // Get entities that were modified during metadata management
1989
2019
  const modifiedEntitiesMap = await this.getModifiedEntitiesWithUpdateAPI(entities);
1990
2020
  if (modifiedEntitiesMap.size > 0) {
1991
- (0, status_logging_1.logStatus)(`Found ${modifiedEntitiesMap.size} entities with schema changes affecting update SPs`);
2021
+ logStatus(`Found ${modifiedEntitiesMap.size} entities with schema changes affecting update SPs`);
1992
2022
  // Convert map values to set of IDs
1993
2023
  const changedEntityIds = new Set(modifiedEntitiesMap.values());
1994
2024
  // Find entities that need delete SP regeneration
1995
2025
  const entitiesNeedingRegeneration = await this.getEntitiesRequiringCascadeDeleteRegeneration(pool, changedEntityIds);
1996
2026
  if (entitiesNeedingRegeneration.size > 0) {
1997
- (0, status_logging_1.logStatus)(`Identified ${entitiesNeedingRegeneration.size} entities requiring delete SP regeneration due to cascade dependencies`);
2027
+ logStatus(`Identified ${entitiesNeedingRegeneration.size} entities requiring delete SP regeneration due to cascade dependencies`);
1998
2028
  // Store the entity IDs that need regeneration (only if spDeleteGenerated=true)
1999
2029
  for (const entityId of entitiesNeedingRegeneration) {
2000
2030
  const entity = entities.find(e => e.ID === entityId);
2001
2031
  if (entity && entity.spDeleteGenerated) {
2002
2032
  this.entitiesNeedingDeleteSPRegeneration.add(entityId);
2003
- (0, status_logging_1.logStatus)(` - Marked ${entity.Name} for delete SP regeneration (cascade dependency)`);
2033
+ logStatus(` - Marked ${entity.Name} for delete SP regeneration (cascade dependency)`);
2004
2034
  }
2005
2035
  else if (entity && !entity.spDeleteGenerated) {
2006
- (0, status_logging_1.logStatus)(` - Skipping ${entity.Name} - has cascade dependency but spDeleteGenerated=false (custom SP)`);
2036
+ logStatus(` - Skipping ${entity.Name} - has cascade dependency but spDeleteGenerated=false (custom SP)`);
2007
2037
  }
2008
2038
  }
2009
2039
  // Order entities by dependencies for proper regeneration
2010
2040
  this.orderedEntitiesForDeleteSPRegeneration = this.orderEntitiesByDependencies(entities, this.entitiesNeedingDeleteSPRegeneration);
2011
2041
  if (this.orderedEntitiesForDeleteSPRegeneration.length > 0) {
2012
- (0, status_logging_1.logStatus)(`Ordered entities for delete SP regeneration:`);
2042
+ logStatus(`Ordered entities for delete SP regeneration:`);
2013
2043
  this.orderedEntitiesForDeleteSPRegeneration.forEach((entityId, index) => {
2014
2044
  const entity = entities.find(e => e.ID === entityId);
2015
- (0, status_logging_1.logStatus)(` ${index + 1}. ${entity?.Name || entityId}`);
2045
+ logStatus(` ${index + 1}. ${entity?.Name || entityId}`);
2016
2046
  });
2017
2047
  }
2018
2048
  }
2019
2049
  }
2020
2050
  }
2021
2051
  catch (error) {
2022
- (0, status_logging_1.logError)(`Error in cascade delete dependency analysis: ${error}`);
2052
+ logError(`Error in cascade delete dependency analysis: ${error}`);
2023
2053
  // Continue with normal processing even if dependency analysis fails
2024
2054
  }
2025
2055
  }
@@ -2060,7 +2090,7 @@ GO${permissions}
2060
2090
  if (visiting.has(entityId)) {
2061
2091
  // Circular dependency detected - mark it but don't fail
2062
2092
  const entity = entities.find(e => e.ID === entityId);
2063
- (0, status_logging_1.logStatus)(`Warning: Circular cascade delete dependency detected involving ${entity?.Name || entityId}`);
2093
+ logStatus(`Warning: Circular cascade delete dependency detected involving ${entity?.Name || entityId}`);
2064
2094
  circularDeps.add(entityId);
2065
2095
  return false; // Signal circular dependency but continue processing
2066
2096
  }
@@ -2086,17 +2116,16 @@ GO${permissions}
2086
2116
  if (!success && circularDeps.has(entityId)) {
2087
2117
  // Entity is part of circular dependency - add it anyway in arbitrary order
2088
2118
  // The SQL will still be generated, just not in perfect dependency order
2089
- (0, status_logging_1.logStatus)(` - Adding ${entities.find(e => e.ID === entityId)?.Name || entityId} despite circular dependency`);
2119
+ logStatus(` - Adding ${entities.find(e => e.ID === entityId)?.Name || entityId} despite circular dependency`);
2090
2120
  visited.add(entityId);
2091
2121
  ordered.push(entityId);
2092
2122
  }
2093
2123
  }
2094
2124
  }
2095
2125
  if (circularDeps.size > 0) {
2096
- (0, status_logging_1.logStatus)(`Note: ${circularDeps.size} entities have circular cascade delete dependencies and will be regenerated in arbitrary order.`);
2126
+ logStatus(`Note: ${circularDeps.size} entities have circular cascade delete dependencies and will be regenerated in arbitrary order.`);
2097
2127
  }
2098
2128
  return ordered;
2099
2129
  }
2100
2130
  }
2101
- exports.SQLCodeGenBase = SQLCodeGenBase;
2102
2131
  //# sourceMappingURL=sql_codegen.js.map