@memberjunction/codegen-lib 5.4.1 → 5.6.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 (75) hide show
  1. package/README.md +65 -2
  2. package/dist/Angular/angular-codegen.d.ts.map +1 -1
  3. package/dist/Angular/angular-codegen.js +26 -12
  4. package/dist/Angular/angular-codegen.js.map +1 -1
  5. package/dist/Angular/related-entity-components.js +2 -2
  6. package/dist/Angular/related-entity-components.js.map +1 -1
  7. package/dist/Config/config.d.ts +10 -0
  8. package/dist/Config/config.d.ts.map +1 -1
  9. package/dist/Config/config.js +10 -0
  10. package/dist/Config/config.js.map +1 -1
  11. package/dist/Database/codeGenDatabaseProvider.d.ts +544 -0
  12. package/dist/Database/codeGenDatabaseProvider.d.ts.map +1 -0
  13. package/dist/Database/codeGenDatabaseProvider.js +29 -0
  14. package/dist/Database/codeGenDatabaseProvider.js.map +1 -0
  15. package/dist/Database/manage-metadata.d.ts +165 -60
  16. package/dist/Database/manage-metadata.d.ts.map +1 -1
  17. package/dist/Database/manage-metadata.js +592 -483
  18. package/dist/Database/manage-metadata.js.map +1 -1
  19. package/dist/Database/providers/postgresql/PostgreSQLCodeGenConnection.d.ts +53 -0
  20. package/dist/Database/providers/postgresql/PostgreSQLCodeGenConnection.d.ts.map +1 -0
  21. package/dist/Database/providers/postgresql/PostgreSQLCodeGenConnection.js +112 -0
  22. package/dist/Database/providers/postgresql/PostgreSQLCodeGenConnection.js.map +1 -0
  23. package/dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.d.ts +344 -0
  24. package/dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.d.ts.map +1 -0
  25. package/dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.js +1567 -0
  26. package/dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.js.map +1 -0
  27. package/dist/Database/providers/sqlserver/SQLServerCodeGenConnection.d.ts +42 -0
  28. package/dist/Database/providers/sqlserver/SQLServerCodeGenConnection.d.ts.map +1 -0
  29. package/dist/Database/providers/sqlserver/SQLServerCodeGenConnection.js +84 -0
  30. package/dist/Database/providers/sqlserver/SQLServerCodeGenConnection.js.map +1 -0
  31. package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.d.ts +372 -0
  32. package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.d.ts.map +1 -0
  33. package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.js +1483 -0
  34. package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.js.map +1 -0
  35. package/dist/Database/reorder-columns.d.ts +2 -2
  36. package/dist/Database/reorder-columns.d.ts.map +1 -1
  37. package/dist/Database/reorder-columns.js +9 -9
  38. package/dist/Database/reorder-columns.js.map +1 -1
  39. package/dist/Database/sql.d.ts +10 -5
  40. package/dist/Database/sql.d.ts.map +1 -1
  41. package/dist/Database/sql.js +44 -228
  42. package/dist/Database/sql.js.map +1 -1
  43. package/dist/Database/sql_codegen.d.ts +31 -29
  44. package/dist/Database/sql_codegen.d.ts.map +1 -1
  45. package/dist/Database/sql_codegen.js +209 -842
  46. package/dist/Database/sql_codegen.js.map +1 -1
  47. package/dist/Misc/action_subclasses_codegen.js +3 -2
  48. package/dist/Misc/action_subclasses_codegen.js.map +1 -1
  49. package/dist/Misc/entity_subclasses_codegen.d.ts +4 -4
  50. package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
  51. package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
  52. package/dist/Misc/graphql_server_codegen.d.ts +6 -1
  53. package/dist/Misc/graphql_server_codegen.d.ts.map +1 -1
  54. package/dist/Misc/graphql_server_codegen.js +33 -35
  55. package/dist/Misc/graphql_server_codegen.js.map +1 -1
  56. package/dist/Misc/sql_logging.d.ts +2 -2
  57. package/dist/Misc/sql_logging.d.ts.map +1 -1
  58. package/dist/Misc/sql_logging.js +1 -1
  59. package/dist/Misc/sql_logging.js.map +1 -1
  60. package/dist/Misc/system_integrity.d.ts +6 -6
  61. package/dist/Misc/system_integrity.d.ts.map +1 -1
  62. package/dist/Misc/system_integrity.js +33 -8
  63. package/dist/Misc/system_integrity.js.map +1 -1
  64. package/dist/Misc/temp_batch_file.d.ts.map +1 -1
  65. package/dist/Misc/temp_batch_file.js +4 -1
  66. package/dist/Misc/temp_batch_file.js.map +1 -1
  67. package/dist/index.d.ts +5 -0
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +5 -0
  70. package/dist/index.js.map +1 -1
  71. package/dist/runCodeGen.d.ts +30 -75
  72. package/dist/runCodeGen.d.ts.map +1 -1
  73. package/dist/runCodeGen.js +123 -215
  74. package/dist/runCodeGen.js.map +1 -1
  75. package/package.json +18 -15
@@ -3,6 +3,8 @@
3
3
  * Coordinates all aspects of code generation including database schema analysis,
4
4
  * metadata management, SQL generation, TypeScript entities, Angular components,
5
5
  * GraphQL resolvers, and more.
6
+ *
7
+ * Supports both SQL Server and PostgreSQL database platforms via the dbType configuration.
6
8
  */
7
9
  import { GraphQLServerGeneratorBase } from './Misc/graphql_server_codegen.js';
8
10
  import { SQLCodeGenBase } from './Database/sql_codegen.js';
@@ -10,7 +12,7 @@ import { EntitySubClassGeneratorBase } from './Misc/entity_subclasses_codegen.js
10
12
  import { UserCache, setupSQLServerClient } from '@memberjunction/sqlserver-dataprovider';
11
13
  import { MSSQLConnection, sqlConfig } from './Config/db-connection.js';
12
14
  import { ManageMetadataBase } from './Database/manage-metadata.js';
13
- import { outputDir, commands, mj_core_schema, configInfo, getSettingValue } from './Config/config.js';
15
+ import { outputDir, commands, mj_core_schema, configInfo, getSettingValue, dbType } from './Config/config.js';
14
16
  import { logError, logStatus, logWarning, startSpinner, updateSpinner, succeedSpinner, failSpinner, warnSpinner } from './Misc/status_logging.js';
15
17
  import * as MJ from '@memberjunction/core';
16
18
  import { RunCommandsBase } from './Misc/runCommand.js';
@@ -18,119 +20,116 @@ import { DBSchemaGeneratorBase } from './Database/dbSchema.js';
18
20
  import { AngularClientGeneratorBase } from './Angular/angular-codegen.js';
19
21
  import { SQLServerProviderConfigData } from '@memberjunction/sqlserver-dataprovider';
20
22
  import { CreateNewUserBase } from './Misc/createNewUser.js';
21
- import { MJGlobal } from '@memberjunction/global';
23
+ import { MJGlobal, UUIDsEqual } from '@memberjunction/global';
22
24
  import { ActionSubClassGeneratorBase } from './Misc/action_subclasses_codegen.js';
23
25
  import { SQLLogging } from './Misc/sql_logging.js';
26
+ import { SQLServerCodeGenConnection } from './Database/providers/sqlserver/SQLServerCodeGenConnection.js';
27
+ import { PostgreSQLCodeGenConnection } from './Database/providers/postgresql/PostgreSQLCodeGenConnection.js';
28
+ import { PostgreSQLDataProvider, PostgreSQLProviderConfigData } from '@memberjunction/postgresql-dataprovider';
29
+ import pg from 'pg';
24
30
  import { SystemIntegrityBase } from './Misc/system_integrity.js';
25
31
  import { ActionEngineBase } from '@memberjunction/actions-base';
26
32
  import { AIEngine } from '@memberjunction/aiengine';
33
+ import { SetProvider, UserInfo } from '@memberjunction/core';
27
34
  // Import pre-built MJ class registrations manifest (covers all @memberjunction/* packages)
28
35
  import '@memberjunction/server-bootstrap-lite/mj-class-registrations';
29
36
  /** Extract core schema name from configuration */
30
37
  const { mjCoreSchema } = configInfo;
31
38
  /**
32
39
  * Main orchestrator class for the MemberJunction code generation process.
33
- *
34
- * This class coordinates a comprehensive code generation pipeline that transforms
35
- * database schemas into a complete, type-safe, full-stack application. The process includes:
36
- *
37
- * **Pipeline Steps:**
38
- * 1. **Database Setup** - Initialize connections and metadata
39
- * 2. **Metadata Management** - Analyze schema changes and update metadata
40
- * 3. **SQL Generation** - Create views, procedures, and indexes
41
- * 4. **TypeScript Entities** - Generate entity classes with validation
42
- * 5. **Angular Components** - Create forms and UI components
43
- * 6. **GraphQL Resolvers** - Generate API endpoints
44
- * 7. **Action Classes** - Create business logic containers
45
- * 8. **Documentation** - Generate schema JSON for AI/documentation
46
- * 9. **Post-processing** - Run commands and integrity checks
47
- *
48
- * **Customization:**
49
- * You can sub-class this class and override specific methods to customize
50
- * the code generation process for your specific needs.
51
- *
52
- * @example
53
- * ```typescript
54
- * const codeGen = new RunCodeGenBase();
55
- * await codeGen.Run(); // Full generation
56
- * await codeGen.Run(true); // Skip database operations
57
- * ```
58
40
  */
59
41
  export class RunCodeGenBase {
60
42
  /**
61
- * Sets up the SQL Server data source and initializes the MemberJunction core metadata.
62
- * This method establishes the database connection pool and configures the data provider
63
- * that will be used throughout the code generation process.
64
- *
65
- * Override this method to customize the data source setup process for different
66
- * database providers or connection configurations.
67
- *
68
- * @returns Promise resolving to the configured SQLServerDataProvider instance
69
- * @throws Error if connection setup fails
43
+ * Sets up the data source based on the configured database type (SQL Server or PostgreSQL).
44
+ * Initializes the appropriate data provider, connection, and user cache.
70
45
  */
71
46
  async setupDataSource() {
72
- /****************************************************************************************
73
- // First, setup the data source and make sure the metadata and related stuff for MJCore is initialized
74
- ****************************************************************************************/
75
47
  startSpinner('Initializing database connection...');
76
- const pool = await MSSQLConnection(); // get the MSSQL connection pool
48
+ const platform = dbType();
49
+ if (platform === 'postgresql') {
50
+ return this.setupPostgreSQLDataSource();
51
+ }
52
+ return this.setupSQLServerDataSource();
53
+ }
54
+ /**
55
+ * Sets up SQL Server data source (original behavior).
56
+ */
57
+ async setupSQLServerDataSource() {
58
+ const pool = await MSSQLConnection();
77
59
  const config = new SQLServerProviderConfigData(pool, mj_core_schema());
78
- const sqlServerProvider = await setupSQLServerClient(config);
79
- // Get connection details from the sqlConfig
60
+ const provider = await setupSQLServerClient(config);
61
+ const conn = new SQLServerCodeGenConnection(pool);
80
62
  let connectionInfo = sqlConfig.server;
81
- if (sqlConfig.port) {
82
- connectionInfo += `:${sqlConfig.port}`;
83
- }
84
- if (sqlConfig.options?.instanceName) {
85
- connectionInfo += `\\${sqlConfig.options.instanceName}`;
63
+ if (sqlConfig.port)
64
+ connectionInfo += ':' + sqlConfig.port;
65
+ if (sqlConfig.options?.instanceName)
66
+ connectionInfo += '\\' + sqlConfig.options.instanceName;
67
+ connectionInfo += '/' + sqlConfig.database;
68
+ await UserCache.Instance.Refresh(pool);
69
+ const userMatch = UserCache.Users.find((u) => u?.Type?.trim().toLowerCase() === 'owner');
70
+ const currentUser = userMatch ? userMatch : UserCache.Users[0];
71
+ succeedSpinner('SQL Server connection initialized: ' + connectionInfo);
72
+ return { provider, connection: conn, currentUser, connectionInfo };
73
+ }
74
+ /**
75
+ * Sets up PostgreSQL data source.
76
+ */
77
+ async setupPostgreSQLDataSource() {
78
+ const pgHost = process.env.PG_HOST ?? configInfo.dbHost;
79
+ const pgPort = parseInt(process.env.PG_PORT ?? String(configInfo.dbPort), 10) || 5432;
80
+ const pgDatabase = process.env.PG_DATABASE ?? configInfo.dbDatabase;
81
+ const pgUser = process.env.PG_USERNAME ?? configInfo.codeGenLogin;
82
+ const pgPassword = process.env.PG_PASSWORD ?? configInfo.codeGenPassword;
83
+ const coreSchema = mj_core_schema();
84
+ const pool = new pg.Pool({
85
+ host: pgHost,
86
+ port: pgPort,
87
+ database: pgDatabase,
88
+ user: pgUser,
89
+ password: pgPassword,
90
+ max: 20,
91
+ });
92
+ // Test connection
93
+ const client = await pool.connect();
94
+ client.release();
95
+ // Configure the PostgreSQL data provider
96
+ const pgConfig = new PostgreSQLProviderConfigData({ Host: pgHost, Port: pgPort, Database: pgDatabase, User: pgUser, Password: pgPassword }, coreSchema, 1 // checkRefreshIntervalSeconds: must be > 0 to trigger initial metadata load
97
+ );
98
+ const provider = new PostgreSQLDataProvider();
99
+ await provider.Config(pgConfig);
100
+ SetProvider(provider);
101
+ const conn = new PostgreSQLCodeGenConnection(pool);
102
+ // Load users (PostgreSQL version - query views directly)
103
+ const usersResult = await conn.query('SELECT * FROM "' + coreSchema + '"."vwUsers"');
104
+ const rolesResult = await conn.query('SELECT * FROM "' + coreSchema + '"."vwUserRoles"');
105
+ const userInfos = usersResult.recordset.map((user) => {
106
+ user.UserRoles = rolesResult.recordset.filter((role) => UUIDsEqual(role.UserID, user.ID));
107
+ return new UserInfo(provider, user);
108
+ });
109
+ const userMatch = userInfos.find((u) => u?.Type?.trim().toLowerCase() === 'owner');
110
+ const currentUser = userMatch ?? userInfos[0];
111
+ if (!currentUser) {
112
+ throw new Error('No users found in PostgreSQL. Ensure vwUsers has at least one user.');
86
113
  }
87
- connectionInfo += `/${sqlConfig.database}`;
88
- succeedSpinner(`Database connection initialized: ${connectionInfo}`);
89
- return sqlServerProvider;
114
+ const connectionInfo = pgHost + ':' + pgPort + '/' + pgDatabase;
115
+ succeedSpinner('PostgreSQL connection initialized: ' + connectionInfo);
116
+ return { provider, connection: conn, currentUser, connectionInfo };
90
117
  }
91
118
  /**
92
119
  * Main entry point for the complete code generation process.
93
- *
94
- * Orchestrates the entire pipeline from database schema analysis to final code output.
95
- * The process is highly configurable through the configuration file and can be
96
- * partially skipped for faster iteration during development.
97
- *
98
- * **Process Flow:**
99
- * 1. Initialize data sources and user context
100
- * 2. Execute pre-generation commands and scripts
101
- * 3. Manage metadata and schema changes
102
- * 4. Generate SQL objects (views, procedures, indexes)
103
- * 5. Generate TypeScript entity classes
104
- * 6. Generate Angular UI components
105
- * 7. Generate GraphQL API resolvers
106
- * 8. Generate Action business logic classes
107
- * 9. Create documentation JSON
108
- * 10. Run integrity checks
109
- * 11. Execute post-generation commands
110
- *
111
- * @param skipDatabaseGeneration If true, skips all database-related operations
112
- * (metadata management, SQL generation). Useful for faster UI-only regeneration.
113
- * @throws Error if any critical step fails
114
- * @returns Promise that resolves when generation is complete
115
120
  */
116
121
  async Run(skipDatabaseGeneration = false) {
117
122
  try {
118
123
  const startTime = new Date();
119
- startSpinner(`Starting MemberJunction CodeGen @ ${startTime.toLocaleString()}`);
120
- const provider = await this.setupDataSource();
121
- updateSpinner('Loading user cache and metadata...');
122
- const pool = await MSSQLConnection();
123
- await UserCache.Instance.Refresh(pool);
124
- const userMatch = UserCache.Users.find((u) => u?.Type?.trim().toLowerCase() === 'owner');
125
- const currentUser = userMatch ? userMatch : UserCache.Users[0]; // if we don't find an Owner, use the first user in the cache
126
- // get the entity metadata
124
+ const platform = dbType();
125
+ startSpinner('Starting MemberJunction CodeGen (' + platform + ') @ ' + startTime.toLocaleString());
126
+ const { provider, connection: conn, currentUser } = await this.setupDataSource();
127
127
  const md = new MJ.Metadata();
128
128
  if (md.Entities.length === 0) {
129
129
  failSpinner('No entities found in metadata');
130
130
  process.exit(1);
131
131
  }
132
- succeedSpinner(`Loaded ${md.Entities.length} entities from metadata`);
133
- // Initialize AIEngine for advanced generation features (needed for prompt metadata access)
132
+ succeedSpinner('Loaded ' + md.Entities.length + ' entities from metadata');
134
133
  if (configInfo.advancedGeneration?.enableAdvancedGeneration) {
135
134
  startSpinner('Initializing AI Engine for advanced generation...');
136
135
  await AIEngine.Instance.Config(false, currentUser);
@@ -138,14 +137,10 @@ export class RunCodeGenBase {
138
137
  }
139
138
  const runCommandsObject = MJGlobal.Instance.ClassFactory.CreateInstance(RunCommandsBase);
140
139
  const sqlCodeGenObject = MJGlobal.Instance.ClassFactory.CreateInstance(SQLCodeGenBase);
141
- // check to see if the user wants to skip database generation via the config settings
142
140
  const skipDB = skipDatabaseGeneration || getSettingValue('skip_database_generation', false);
143
141
  if (!skipDB) {
144
142
  startSpinner('Handling SQL Script Execution, Metadata Maintenance, and SQL Object Generation...');
145
- SQLLogging.initSQLLogging(); // initialize the SQL Logging functionality
146
- /****************************************************************************************
147
- // STEP 0 --- Precursor Step execute any commands specified in the config file
148
- ****************************************************************************************/
143
+ SQLLogging.initSQLLogging();
149
144
  const beforeCommands = commands('BEFORE');
150
145
  if (beforeCommands && beforeCommands.length > 0) {
151
146
  updateSpinner('Executing BEFORE commands...');
@@ -153,15 +148,9 @@ export class RunCodeGenBase {
153
148
  if (results.some((r) => !r.success))
154
149
  logError('ERROR running one or more BEFORE commands');
155
150
  }
156
- /****************************************************************************************
157
- // STEP 0.1 --- Execute any before SQL Scripts specified in the config file
158
- ****************************************************************************************/
159
151
  updateSpinner('Executing before-all SQL Scripts...');
160
- if (!(await sqlCodeGenObject.runCustomSQLScripts(pool, 'before-all')))
152
+ if (!(await sqlCodeGenObject.runCustomSQLScripts(conn, 'before-all')))
161
153
  logError('ERROR running before-all SQL Scripts');
162
- /****************************************************************************************
163
- // STEP 0.2 --- Create a new user if there is newUserSetup info in the config file
164
- ****************************************************************************************/
165
154
  const newUserSetup = configInfo.newUserSetup;
166
155
  if (newUserSetup) {
167
156
  updateSpinner('Setting up new user...');
@@ -177,33 +166,31 @@ export class RunCodeGenBase {
177
166
  }
178
167
  }
179
168
  }
180
- /****************************************************************************************
181
- // STEP 1 - Manage Metadata - including generating new metadata as required
182
- ****************************************************************************************/
183
169
  const manageMD = MJGlobal.Instance.ClassFactory.CreateInstance(ManageMetadataBase);
184
170
  updateSpinner('Managing Metadata...');
185
- const metadataSuccess = await manageMD.manageMetadata(pool, currentUser);
171
+ const metadataSuccess = await manageMD.manageMetadata(conn, currentUser);
186
172
  if (!metadataSuccess) {
187
173
  failSpinner('ERROR managing metadata');
188
174
  }
189
175
  else {
190
- // now - we need to tell our metadata object to refresh itself
191
176
  await provider.Refresh();
192
177
  succeedSpinner('Metadata management completed');
193
178
  }
194
- /****************************************************************************************
195
- // STEP 2 - SQL Script Generation
196
- ****************************************************************************************/
197
179
  const sqlOutputDir = outputDir('SQL', true);
198
180
  if (sqlOutputDir) {
199
181
  startSpinner('Managing SQL Scripts and Execution...');
200
- const sqlSuccess = await sqlCodeGenObject.manageSQLScriptsAndExecution(pool, md.Entities, sqlOutputDir, currentUser);
182
+ const sqlSuccess = await sqlCodeGenObject.manageSQLScriptsAndExecution(conn, md.Entities, sqlOutputDir, currentUser);
201
183
  if (!sqlSuccess) {
202
184
  failSpinner('Error managing SQL scripts and execution');
203
185
  }
204
186
  else {
205
187
  succeedSpinner('SQL scripts and execution completed');
206
188
  }
189
+ // Refresh metadata again after SQL scripts execution because the stale
190
+ // entity relationship cleanup (step 3.5) runs inside manageSQLScriptsAndExecution,
191
+ // AFTER the entity field sync. The first refresh (above) loaded metadata before
192
+ // those DB changes, so the in-memory metadata still has stale relationships.
193
+ await provider.Refresh();
207
194
  }
208
195
  else {
209
196
  warnSpinner('SQL output directory NOT found in config file, skipping...');
@@ -211,26 +198,20 @@ export class RunCodeGenBase {
211
198
  }
212
199
  else {
213
200
  warnSpinner('Skipping database generation (skip_database_generation = true)');
214
- // we skipped the database generation but we need to load generated code for validators from the database to ensure that we have them
215
- // ready for later use.
216
201
  const manageMD = MJGlobal.Instance.ClassFactory.CreateInstance(ManageMetadataBase);
217
202
  startSpinner('Checking/Loading AI Generated Code from Metadata...');
218
- const metadataSuccess = await manageMD.loadGeneratedCode(pool, currentUser);
203
+ const metadataSuccess = await manageMD.loadGeneratedCode(conn, currentUser);
219
204
  if (!metadataSuccess) {
220
205
  failSpinner('ERROR checking/loading AI Generated Code from Metadata');
221
- return; // FATAL ERROR - we can't continue
206
+ return;
222
207
  }
223
208
  else {
224
209
  succeedSpinner('AI Generated Code loaded from Metadata');
225
210
  }
226
211
  }
227
- // Apply excludeSchemas filter to all entities before splitting into core/non-core
228
- // This ensures TypeScript, Angular, and GraphQL generators respect schema exclusions
229
- // (SQL generation already applies this filter internally in sql_codegen.ts)
230
212
  const apiEntities = md.Entities.filter((e) => e.IncludeInAPI);
231
213
  const excludedSchemaNames = configInfo.excludeSchemas.map(s => s.toLowerCase());
232
214
  const includedEntities = apiEntities.filter((e) => !excludedSchemaNames.includes(e.SchemaName.trim().toLowerCase()));
233
- // Log excluded schemas if any entities were filtered out
234
215
  const excludedCount = apiEntities.length - includedEntities.length;
235
216
  if (excludedCount > 0) {
236
217
  const excludedBySchema = apiEntities
@@ -241,24 +222,17 @@ export class RunCodeGenBase {
241
222
  return acc;
242
223
  }, {});
243
224
  const schemaDetails = Object.entries(excludedBySchema)
244
- .map(([schema, count]) => `${schema}: ${count}`)
225
+ .map(([schema, count]) => schema + ': ' + count)
245
226
  .join(', ');
246
- logStatus(`Excluded ${excludedCount} entities from code generation by schema: ${schemaDetails}`);
227
+ logStatus('Excluded ' + excludedCount + ' entities from code generation by schema: ' + schemaDetails);
247
228
  }
248
229
  const coreEntities = includedEntities.filter((e) => e.SchemaName.trim().toLowerCase() === mjCoreSchema.trim().toLowerCase());
249
230
  const nonCoreEntities = includedEntities.filter((e) => e.SchemaName.trim().toLowerCase() !== mjCoreSchema.trim().toLowerCase());
250
- // Check if we're in verbose mode to determine spinner behavior
251
231
  const isVerbose = configInfo?.verboseOutput ?? false;
252
- if (!isVerbose) {
253
- // In non-verbose mode, start a single spinner for all TypeScript generation
232
+ if (!isVerbose)
254
233
  startSpinner('Generating TypeScript code...');
255
- }
256
- /****************************************************************************************
257
- // STEP 3(a) - GraphQL Server Code Gen
258
- ****************************************************************************************/
259
234
  const graphQLCoreResolversOutputDir = outputDir('GraphQLCoreEntityResolvers', false);
260
235
  if (graphQLCoreResolversOutputDir) {
261
- // generate the GraphQL server code
262
236
  if (isVerbose)
263
237
  startSpinner('Generating CORE Entity GraphQL Resolver Code...');
264
238
  const graphQLGenerator = MJGlobal.Instance.ClassFactory.CreateInstance(GraphQLServerGeneratorBase);
@@ -266,13 +240,11 @@ export class RunCodeGenBase {
266
240
  failSpinner('Error generating GraphQL server code');
267
241
  return;
268
242
  }
269
- else if (isVerbose) {
243
+ else if (isVerbose)
270
244
  succeedSpinner('CORE Entity GraphQL Resolver Code generated');
271
- }
272
245
  }
273
246
  const graphqlOutputDir = outputDir('GraphQLServer', true);
274
247
  if (graphqlOutputDir) {
275
- // generate the GraphQL server code
276
248
  if (isVerbose)
277
249
  startSpinner('Generating GraphQL Resolver Code...');
278
250
  const graphQLGenerator = MJGlobal.Instance.ClassFactory.CreateInstance(GraphQLServerGeneratorBase);
@@ -281,56 +253,39 @@ export class RunCodeGenBase {
281
253
  failSpinner('Error generating GraphQL Resolver code');
282
254
  return;
283
255
  }
284
- else if (isVerbose) {
256
+ else if (isVerbose)
285
257
  succeedSpinner('GraphQL Resolver Code generated');
286
- }
287
258
  }
288
- else if (isVerbose) {
259
+ else if (isVerbose)
289
260
  warnSpinner('GraphQL server output directory NOT found in config file, skipping...');
290
- }
291
- /****************************************************************************************
292
- // STEP 4 - Core Entity Subclass Code Gen
293
- ****************************************************************************************/
294
261
  const coreEntitySubClassOutputDir = outputDir('CoreEntitySubClasses', false);
295
262
  if (coreEntitySubClassOutputDir && coreEntitySubClassOutputDir.length > 0) {
296
- // generate the entity subclass code
297
263
  if (isVerbose)
298
264
  startSpinner('Generating CORE Entity Subclass Code...');
299
265
  const entitySubClassGeneratorObject = MJGlobal.Instance.ClassFactory.CreateInstance(EntitySubClassGeneratorBase);
300
- if (!await entitySubClassGeneratorObject.generateAllEntitySubClasses(pool, coreEntities, coreEntitySubClassOutputDir, skipDB)) {
266
+ if (!await entitySubClassGeneratorObject.generateAllEntitySubClasses(conn, coreEntities, coreEntitySubClassOutputDir, skipDB)) {
301
267
  failSpinner('Error generating entity subclass code');
302
268
  return;
303
269
  }
304
- else if (isVerbose) {
270
+ else if (isVerbose)
305
271
  succeedSpinner('CORE Entity Subclass Code generated');
306
- }
307
272
  }
308
- /****************************************************************************************
309
- // STEP 4.1 - Entity Subclass Code Gen
310
- ****************************************************************************************/
311
273
  const entitySubClassOutputDir = outputDir('EntitySubClasses', true);
312
274
  if (entitySubClassOutputDir) {
313
- // generate the entity subclass code
314
275
  if (isVerbose)
315
276
  startSpinner('Generating Entity Subclass Code...');
316
277
  const entitySubClassGeneratorObject = MJGlobal.Instance.ClassFactory.CreateInstance(EntitySubClassGeneratorBase);
317
- if (!await entitySubClassGeneratorObject.generateAllEntitySubClasses(pool, nonCoreEntities, entitySubClassOutputDir, skipDB)) {
278
+ if (!await entitySubClassGeneratorObject.generateAllEntitySubClasses(conn, nonCoreEntities, entitySubClassOutputDir, skipDB)) {
318
279
  failSpinner('Error generating entity subclass code');
319
280
  return;
320
281
  }
321
- else if (isVerbose) {
282
+ else if (isVerbose)
322
283
  succeedSpinner('Entity Subclass Code generated');
323
- }
324
284
  }
325
- else if (isVerbose) {
285
+ else if (isVerbose)
326
286
  warnSpinner('Entity subclass output directory NOT found in config file, skipping...');
327
- }
328
- /****************************************************************************************
329
- // STEP 5 - Angular Code Gen
330
- ****************************************************************************************/
331
287
  const angularCoreEntitiesOutputDir = outputDir('AngularCoreEntities', false);
332
288
  if (angularCoreEntitiesOutputDir) {
333
- // generate the Angular client code
334
289
  if (isVerbose)
335
290
  startSpinner('Generating Angular CORE Entities Code...');
336
291
  const angularGenerator = MJGlobal.Instance.ClassFactory.CreateInstance(AngularClientGeneratorBase);
@@ -338,13 +293,11 @@ export class RunCodeGenBase {
338
293
  failSpinner('Error generating Angular CORE Entities code');
339
294
  return;
340
295
  }
341
- else if (isVerbose) {
296
+ else if (isVerbose)
342
297
  succeedSpinner('Angular CORE Entities Code generated');
343
- }
344
298
  }
345
299
  const angularOutputDir = outputDir('Angular', false);
346
300
  if (angularOutputDir) {
347
- // generate the Angular client code
348
301
  if (isVerbose)
349
302
  startSpinner('Generating Angular Code...');
350
303
  const angularGenerator = MJGlobal.Instance.ClassFactory.CreateInstance(AngularClientGeneratorBase);
@@ -352,37 +305,26 @@ export class RunCodeGenBase {
352
305
  failSpinner('Error generating Angular code');
353
306
  return;
354
307
  }
355
- else if (isVerbose) {
308
+ else if (isVerbose)
356
309
  succeedSpinner('Angular Code generated');
357
- }
358
310
  }
359
- else if (isVerbose) {
311
+ else if (isVerbose)
360
312
  warnSpinner('Angular output directory NOT found in config file, skipping...');
361
- }
362
- /****************************************************************************************
363
- // STEP 6 - Database Schema Output in JSON - for documentation and can be used by AI/etc.
364
- ****************************************************************************************/
365
313
  const dbSchemaOutputDir = outputDir('DBSchemaJSON', false);
366
314
  if (dbSchemaOutputDir) {
367
- // generate the GraphQL client code
368
315
  if (isVerbose)
369
316
  startSpinner('Generating Database Schema JSON Output...');
370
317
  const schemaGeneratorObject = MJGlobal.Instance.ClassFactory.CreateInstance(DBSchemaGeneratorBase);
371
318
  if (!schemaGeneratorObject.generateDBSchemaJSONOutput(md.Entities, dbSchemaOutputDir)) {
372
319
  failSpinner('Error generating Database Schema JSON Output, non-fatal, continuing...');
373
320
  }
374
- else if (isVerbose) {
321
+ else if (isVerbose)
375
322
  succeedSpinner('Database Schema JSON Output generated');
376
- }
377
323
  }
378
- else if (isVerbose) {
324
+ else if (isVerbose)
379
325
  warnSpinner('DB Schema output directory NOT found in config file, skipping...');
380
- }
381
- /****************************************************************************************
382
- // STEP 7 - Actions Code Gen
383
- ****************************************************************************************/
384
326
  const coreActionsOutputDir = outputDir('CoreActionSubclasses', false);
385
- await ActionEngineBase.Instance.Config(false, currentUser); // this is inefficient as we have the server
327
+ await ActionEngineBase.Instance.Config(false, currentUser);
386
328
  if (coreActionsOutputDir) {
387
329
  if (isVerbose)
388
330
  startSpinner('Generating CORE Actions Code...');
@@ -391,9 +333,8 @@ export class RunCodeGenBase {
391
333
  failSpinner('Error generating CORE Actions code');
392
334
  return;
393
335
  }
394
- else if (isVerbose) {
336
+ else if (isVerbose)
395
337
  succeedSpinner('CORE Actions Code generated');
396
- }
397
338
  }
398
339
  const actionsOutputDir = outputDir('ActionSubclasses', false);
399
340
  if (actionsOutputDir) {
@@ -404,80 +345,47 @@ export class RunCodeGenBase {
404
345
  failSpinner('Error generating Actions code');
405
346
  return;
406
347
  }
407
- else if (isVerbose) {
348
+ else if (isVerbose)
408
349
  succeedSpinner('Actions Code generated');
409
- }
410
350
  }
411
- else if (isVerbose) {
351
+ else if (isVerbose)
412
352
  warnSpinner('Actions output directory NOT found in config file, skipping...');
413
- }
414
- // WRAP UP SQL LOGGING HERE
415
- SQLLogging.finishSQLLogging(); // finish up the SQL Logging
416
- // Complete the TypeScript generation spinner in non-verbose mode
417
- if (!isVerbose) {
353
+ SQLLogging.finishSQLLogging();
354
+ if (!isVerbose)
418
355
  succeedSpinner('TypeScript code generation completed');
419
- }
420
- // now run integrity checks
421
356
  startSpinner('Running system integrity checks...');
422
- await SystemIntegrityBase.RunIntegrityChecks(pool, true);
357
+ await SystemIntegrityBase.RunIntegrityChecks(conn, true);
423
358
  succeedSpinner('System integrity checks completed');
424
- /****************************************************************************************
425
- // STEP 8 --- Finalization Step - execute any AFTER commands specified in the config file
426
- ****************************************************************************************/
427
359
  const afterCommands = commands('AFTER');
428
360
  if (afterCommands && afterCommands.length > 0) {
429
361
  startSpinner('Executing AFTER commands...');
430
362
  const results = await runCommandsObject.runCommands(afterCommands);
431
- if (results.some((r) => !r.success)) {
363
+ if (results.some((r) => !r.success))
432
364
  failSpinner('ERROR running one or more AFTER commands');
433
- }
434
- else {
365
+ else
435
366
  succeedSpinner('AFTER commands completed');
436
- }
437
367
  }
438
- /****************************************************************************************
439
- // STEP 9 --- Execute any AFTER SQL Scripts specified in the config file
440
- ****************************************************************************************/
441
368
  if (!skipDB) {
442
369
  startSpinner('Executing after-all SQL Scripts...');
443
- if (!(await sqlCodeGenObject.runCustomSQLScripts(pool, 'after-all'))) {
370
+ if (!(await sqlCodeGenObject.runCustomSQLScripts(conn, 'after-all')))
444
371
  failSpinner('ERROR running after-all SQL Scripts');
445
- }
446
- else {
372
+ else
447
373
  succeedSpinner('After-all SQL Scripts completed');
448
- }
449
374
  }
450
375
  const endTime = new Date();
451
376
  const totalSeconds = (endTime.getTime() - startTime.getTime()) / 1000;
452
- succeedSpinner(`MJ CodeGen Complete! ${md.Entities.length} entities processed in ${totalSeconds}s @ ${endTime.toLocaleString()}`);
453
- process.exit(0); // wrap it up, 0 means success
377
+ succeedSpinner('MJ CodeGen Complete! ' + md.Entities.length + ' entities processed in ' + totalSeconds + 's @ ' + endTime.toLocaleString());
378
+ process.exit(0);
454
379
  }
455
380
  catch (e) {
456
- failSpinner(`CodeGen failed: ${e}`);
381
+ failSpinner('CodeGen failed: ' + e);
457
382
  logError(e);
458
- process.exit(1); // error code
383
+ process.exit(1);
459
384
  }
460
385
  }
461
386
  }
462
387
  /**
463
388
  * Convenience function to run the MemberJunction code generation process.
464
- * Creates a new instance of RunCodeGenBase and executes the full generation pipeline.
465
- *
466
- * This is the recommended way to trigger code generation from external scripts
467
- * or applications.
468
- *
469
- * @param skipDatabaseGeneration Whether to skip database-related operations
470
- * @returns Promise that resolves when generation is complete
471
- * @throws Error if generation fails
472
- *
473
- * @example
474
- * ```typescript
475
- * // Full generation
476
- * await runMemberJunctionCodeGeneration();
477
- *
478
- * // Skip database operations for faster UI generation
479
- * await runMemberJunctionCodeGeneration(true);
480
- * ```
481
389
  */
482
390
  export async function runMemberJunctionCodeGeneration(skipDatabaseGeneration = false) {
483
391
  const runObject = MJGlobal.Instance.ClassFactory.CreateInstance(RunCodeGenBase);