@memberjunction/codegen-lib 4.0.0 → 4.2.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 (42) hide show
  1. package/README.md +826 -639
  2. package/dist/Angular/angular-codegen.d.ts +9 -2
  3. package/dist/Angular/angular-codegen.d.ts.map +1 -1
  4. package/dist/Angular/angular-codegen.js +84 -62
  5. package/dist/Angular/angular-codegen.js.map +1 -1
  6. package/dist/Angular/entity-data-grid-related-entity-component.d.ts.map +1 -1
  7. package/dist/Angular/entity-data-grid-related-entity-component.js +2 -1
  8. package/dist/Angular/entity-data-grid-related-entity-component.js.map +1 -1
  9. package/dist/Config/config.d.ts.map +1 -1
  10. package/dist/Config/config.js +10 -0
  11. package/dist/Config/config.js.map +1 -1
  12. package/dist/Database/manage-metadata.d.ts +301 -5
  13. package/dist/Database/manage-metadata.d.ts.map +1 -1
  14. package/dist/Database/manage-metadata.js +1041 -156
  15. package/dist/Database/manage-metadata.js.map +1 -1
  16. package/dist/Database/sql.d.ts.map +1 -1
  17. package/dist/Database/sql.js +4 -0
  18. package/dist/Database/sql.js.map +1 -1
  19. package/dist/Database/sql_codegen.d.ts +13 -0
  20. package/dist/Database/sql_codegen.d.ts.map +1 -1
  21. package/dist/Database/sql_codegen.js +99 -28
  22. package/dist/Database/sql_codegen.js.map +1 -1
  23. package/dist/Misc/advanced_generation.d.ts +68 -6
  24. package/dist/Misc/advanced_generation.d.ts.map +1 -1
  25. package/dist/Misc/advanced_generation.js +94 -49
  26. package/dist/Misc/advanced_generation.js.map +1 -1
  27. package/dist/Misc/entity_subclasses_codegen.d.ts +5 -0
  28. package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
  29. package/dist/Misc/entity_subclasses_codegen.js +24 -6
  30. package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
  31. package/dist/Misc/graphql_server_codegen.d.ts.map +1 -1
  32. package/dist/Misc/graphql_server_codegen.js +3 -1
  33. package/dist/Misc/graphql_server_codegen.js.map +1 -1
  34. package/dist/__tests__/metadataConfig.test.d.ts +12 -0
  35. package/dist/__tests__/metadataConfig.test.d.ts.map +1 -0
  36. package/dist/__tests__/metadataConfig.test.js +604 -0
  37. package/dist/__tests__/metadataConfig.test.js.map +1 -0
  38. package/package.json +21 -17
  39. package/dist/Angular/user-view-grid-related-entity-component.d.ts +0 -43
  40. package/dist/Angular/user-view-grid-related-entity-component.d.ts.map +0 -1
  41. package/dist/Angular/user-view-grid-related-entity-component.js +0 -85
  42. package/dist/Angular/user-view-grid-related-entity-component.js.map +0 -1
package/README.md CHANGED
@@ -1,31 +1,6 @@
1
1
  # @memberjunction/codegen-lib
2
2
 
3
- 🚀 **The most sophisticated code generation engine you've ever seen** - automatically transforms your database schema into a complete, type-safe, full-stack application with AI-powered intelligence.
4
-
5
- ## What Makes This Badass?
6
-
7
- MemberJunction's CodeGen doesn't just generate boilerplate code. It's an **AI-powered, metadata-driven architecture** that creates **bulletproof, production-ready applications** from your database schema with **zero manual intervention**.
8
-
9
- ### 🧠 AI-Powered Intelligence
10
- - **CHECK Constraint Translation**: Our AI automatically translates complex SQL CHECK constraints into **perfect TypeScript union types** and **Zod validation schemas**
11
- - **Smart Type Inference**: Analyzes relationships and generates **contextually appropriate Angular form controls** (dropdowns, search boxes, checkboxes)
12
- - **Intelligent Naming**: AI-driven naming conventions ensure your generated code follows best practices
13
-
14
- ### ⚡ Synchronization Across Everything
15
- Watch your database changes **instantly propagate** through your entire stack:
16
- ```
17
- Database Schema Change → TypeScript Entities → Angular Forms → SQL Procedures → GraphQL Schema
18
- ```
19
- **One command. Complete synchronization. Zero breaking changes.**
20
-
21
- ### 🎯 What Gets Generated (Automatically)
22
- - **TypeScript Entity Classes** with full type safety and validation
23
- - **Angular Form Components** with proper field types and validation
24
- - **SQL Stored Procedures** for all CRUD operations
25
- - **Database Views** with optimized joins and indexing
26
- - **GraphQL Schemas** and resolvers
27
- - **Zod Validation Schemas** from SQL constraints
28
- - **Complete API Endpoints** with type-safe parameters
3
+ The code generation engine for the MemberJunction platform. This library transforms database schema metadata into a complete, type-safe, full-stack application: TypeScript entity classes with Zod validation, Angular form components, SQL stored procedures and views, GraphQL resolvers, and Action subclasses -- all from a single `mj codegen` invocation.
29
4
 
30
5
  ## Installation
31
6
 
@@ -33,824 +8,1036 @@ Database Schema Change → TypeScript Entities → Angular Forms → SQL Procedu
33
8
  npm install @memberjunction/codegen-lib
34
9
  ```
35
10
 
36
- ## The Magic in Action
37
-
38
- ### From This SQL Constraint:
39
- ```sql
40
- ALTER TABLE [AIPrompt]
41
- ADD [PromptRole] nvarchar(20) NOT NULL
42
- CONSTRAINT [CK_AIPrompt_PromptRole] CHECK ([PromptRole] IN (N'System', N'User', N'Assistant', N'SystemOrUser'))
11
+ ## Overview
12
+
13
+ CodeGenLib sits at the center of MemberJunction's development workflow. When you change a database schema (add a table, alter a column, define a CHECK constraint), CodeGenLib detects those changes, updates internal metadata, and regenerates synchronized code across every layer of the stack. The result is guaranteed type safety from database to UI with zero manual boilerplate.
14
+
15
+ The library is designed for extensibility: every major generator is a base class that can be subclassed and registered via `@RegisterClass` to override or extend default behavior.
16
+
17
+ ```mermaid
18
+ flowchart TD
19
+ subgraph Input["Input Sources"]
20
+ DB["SQL Server\nDatabase Schema"]
21
+ CFG["mj.config.cjs\nConfiguration"]
22
+ AI["AI Prompts\n(Advanced Generation)"]
23
+ end
24
+
25
+ subgraph Pipeline["CodeGen Pipeline"]
26
+ META["Metadata\nManagement"]
27
+ SQLGEN["SQL\nGeneration"]
28
+ ENTITY["Entity Class\nGeneration"]
29
+ ANGULAR["Angular\nGeneration"]
30
+ GQL["GraphQL\nGeneration"]
31
+ ACTION["Action\nGeneration"]
32
+ end
33
+
34
+ subgraph Output["Generated Outputs"]
35
+ VIEWS["Views &\nStored Procedures"]
36
+ TS["TypeScript Entity\nClasses + Zod"]
37
+ NG["Angular Form\nComponents"]
38
+ GQLR["GraphQL\nResolvers"]
39
+ ACTS["Action\nSubclasses"]
40
+ JSON["DB Schema\nJSON"]
41
+ end
42
+
43
+ DB --> META
44
+ CFG --> META
45
+ AI --> META
46
+
47
+ META --> SQLGEN
48
+ META --> ENTITY
49
+ META --> ANGULAR
50
+ META --> GQL
51
+ META --> ACTION
52
+
53
+ SQLGEN --> VIEWS
54
+ ENTITY --> TS
55
+ ANGULAR --> NG
56
+ GQL --> GQLR
57
+ ACTION --> ACTS
58
+ META --> JSON
59
+
60
+ style Input fill:#2d6a9f,stroke:#1a4971,color:#fff
61
+ style Pipeline fill:#7c5295,stroke:#563a6b,color:#fff
62
+ style Output fill:#2d8659,stroke:#1a5c3a,color:#fff
43
63
  ```
44
64
 
45
- ### To This TypeScript (Automatically):
46
- ```typescript
47
- PromptRole: z.union([
48
- z.literal('System'),
49
- z.literal('User'),
50
- z.literal('Assistant'),
51
- z.literal('SystemOrUser')
52
- ]).describe('Determines how the prompt is used in conversation...')
65
+ ## Key Features
66
+
67
+ - **Full-Stack Synchronization**: A single schema change propagates to TypeScript entities, Angular forms, SQL procedures, and GraphQL resolvers automatically
68
+ - **AI-Powered Intelligence**: Uses AI prompts to translate CHECK constraints into Zod schemas, generate semantic form layouts, identify name fields, and create entity descriptions
69
+ - **Extensible Architecture**: Every generator (`SQLCodeGenBase`, `EntitySubClassGeneratorBase`, `AngularClientGeneratorBase`, etc.) can be subclassed and overridden via MJ's class factory
70
+ - **Zod Validation Schemas**: Generates Zod schemas from SQL CHECK constraints with proper union types and refinements
71
+ - **Recursive Hierarchy Support**: Automatically detects self-referential foreign keys and generates CTE-based `Root{FieldName}` columns in views
72
+ - **Cascade Delete Generation**: Produces cursor-based cascade delete procedures that call child entity stored procedures, respecting business logic at every level
73
+ - **Force Regeneration**: Surgically regenerate specific SQL objects for specific entities without requiring schema changes
74
+ - **Class Registration Manifests**: Prevents tree-shaking of `@RegisterClass`-decorated classes by generating static import manifests
75
+ - **SQL Migration Logging**: Outputs all generated SQL as Flyway-compatible migration files with schema placeholder support
76
+ - **Configurable via Zod-Validated Config**: All settings validated at startup through comprehensive Zod schemas with sensible defaults
77
+
78
+ ## Architecture
79
+
80
+ ### Pipeline Stages
81
+
82
+ The code generation process follows a well-defined pipeline orchestrated by the `RunCodeGenBase` class:
83
+
84
+ ```mermaid
85
+ flowchart LR
86
+ S0["BEFORE\nCommands"]
87
+ S1["Metadata\nManagement"]
88
+ S2["SQL Object\nGeneration"]
89
+ S3["GraphQL\nResolvers"]
90
+ S4["Entity\nSubclasses"]
91
+ S5["Angular\nComponents"]
92
+ S6["DB Schema\nJSON"]
93
+ S7["Action\nSubclasses"]
94
+ S8["Integrity\nChecks"]
95
+ S9["AFTER\nCommands"]
96
+
97
+ S0 --> S1 --> S2 --> S3 --> S4 --> S5 --> S6 --> S7 --> S8 --> S9
98
+
99
+ style S0 fill:#64748b,stroke:#475569,color:#fff
100
+ style S1 fill:#2d6a9f,stroke:#1a4971,color:#fff
101
+ style S2 fill:#2d6a9f,stroke:#1a4971,color:#fff
102
+ style S3 fill:#7c5295,stroke:#563a6b,color:#fff
103
+ style S4 fill:#7c5295,stroke:#563a6b,color:#fff
104
+ style S5 fill:#7c5295,stroke:#563a6b,color:#fff
105
+ style S6 fill:#7c5295,stroke:#563a6b,color:#fff
106
+ style S7 fill:#7c5295,stroke:#563a6b,color:#fff
107
+ style S8 fill:#b8762f,stroke:#8a5722,color:#fff
108
+ style S9 fill:#64748b,stroke:#475569,color:#fff
53
109
  ```
54
110
 
55
- ### To This Angular Form (Automatically):
56
- ```typescript
57
- <mj-form-field
58
- [record]="record"
59
- FieldName="PromptRole"
60
- Type="dropdownlist" // AI chose dropdown based on constraint
61
- [EditMode]="EditMode"
62
- ></mj-form-field>
63
- ```
111
+ | Stage | Class | Description |
112
+ |-------|-------|-------------|
113
+ | BEFORE Commands | `RunCommandsBase` | Execute pre-generation shell commands and SQL scripts |
114
+ | Metadata Management | `ManageMetadataBase` | Analyze schema changes, create/update entity metadata, run AI-powered field analysis |
115
+ | SQL Generation | `SQLCodeGenBase` | Generate base views (with recursive CTEs), stored procedures (create/update/delete), foreign key indexes, permissions |
116
+ | GraphQL Resolvers | `GraphQLServerGeneratorBase` | Generate TypeGraphQL resolver and type definitions for all API-enabled entities |
117
+ | Entity Subclasses | `EntitySubClassGeneratorBase` | Generate TypeScript entity classes with Zod validation schemas, typed getters/setters, and value list types |
118
+ | Angular Components | `AngularClientGeneratorBase` | Generate Angular form components with smart field types, category-based layouts, and related entity tabs |
119
+ | DB Schema JSON | `DBSchemaGeneratorBase` | Export database schema as JSON for documentation and AI consumption |
120
+ | Action Subclasses | `ActionSubClassGeneratorBase` | Generate Action implementation classes from metadata-defined business logic |
121
+ | Integrity Checks | `SystemIntegrityBase` | Validate entity field sequences and other system consistency rules |
122
+ | AFTER Commands | `RunCommandsBase` | Execute post-generation commands (typically package builds) |
64
123
 
65
- ### To This SQL Procedure (Automatically):
66
- ```sql
67
- CREATE PROCEDURE [spCreateAIPrompt]
68
- @PromptRole nvarchar(20),
69
- -- 20+ other parameters auto-generated
70
- AS BEGIN
71
- -- Complete CRUD logic with validation
72
- END
73
- ```
124
+ ### Core vs Non-Core Entity Separation
74
125
 
75
- **All from ONE schema change. All type-safe. All production-ready.**
126
+ CodeGen distinguishes between core MemberJunction entities (in the `__mj` schema) and application-specific entities. Each generator runs twice: once for core entities with output directed to `@memberjunction/core-entities`, and once for non-core entities with output directed to the application's generated packages. This separation ensures MJ framework code and application code stay independent.
76
127
 
77
- ## Quick Start - Watch The Magic
128
+ ### Class Factory Extensibility
78
129
 
79
- ```typescript
80
- import { initializeConfig, runCodeGen } from '@memberjunction/codegen-lib';
130
+ Every generator base class can be subclassed and registered with a higher priority to customize behavior:
81
131
 
82
- // Initialize configuration
83
- await initializeConfig();
132
+ ```mermaid
133
+ classDiagram
134
+ class RunCodeGenBase {
135
+ +setupDataSource() SQLServerDataProvider
136
+ +Run(skipDatabaseGeneration) void
137
+ }
84
138
 
85
- // Generate your entire application stack
86
- await runCodeGen();
139
+ class ManageMetadataBase {
140
+ +manageMetadata(pool, user) boolean
141
+ +loadGeneratedCode(pool, user) boolean
142
+ }
87
143
 
88
- // That's it. Seriously.
89
- ```
144
+ class SQLCodeGenBase {
145
+ +manageSQLScriptsAndExecution(pool, entities, dir, user) boolean
146
+ +runCustomSQLScripts(pool, when) boolean
147
+ }
90
148
 
91
- Your database schema just became:
92
- - ✅ **295+ TypeScript entity classes** with full validation
93
- - ✅ **Complete Angular UI** with smart form controls
94
- - ✅ **All SQL stored procedures** for every operation
95
- - **GraphQL API** with type-safe resolvers
96
- - **Perfect type safety** across your entire stack
149
+ class EntitySubClassGeneratorBase {
150
+ +generateAllEntitySubClasses(pool, entities, dir, skipDB) boolean
151
+ }
152
+
153
+ class AngularClientGeneratorBase {
154
+ +generateAngularCode(entities, dir, prefix, user) boolean
155
+ }
156
+
157
+ class GraphQLServerGeneratorBase {
158
+ +generateGraphQLServerCode(entities, dir, lib, exclude) boolean
159
+ }
160
+
161
+ class ActionSubClassGeneratorBase {
162
+ +generateActions(actions, dir) boolean
163
+ }
97
164
 
98
- ## Core Capabilities
165
+ RunCodeGenBase --> ManageMetadataBase : creates via ClassFactory
166
+ RunCodeGenBase --> SQLCodeGenBase : creates via ClassFactory
167
+ RunCodeGenBase --> EntitySubClassGeneratorBase : creates via ClassFactory
168
+ RunCodeGenBase --> AngularClientGeneratorBase : creates via ClassFactory
169
+ RunCodeGenBase --> GraphQLServerGeneratorBase : creates via ClassFactory
170
+ RunCodeGenBase --> ActionSubClassGeneratorBase : creates via ClassFactory
171
+
172
+ style RunCodeGenBase fill:#2d6a9f,stroke:#1a4971,color:#fff
173
+ style ManageMetadataBase fill:#7c5295,stroke:#563a6b,color:#fff
174
+ style SQLCodeGenBase fill:#7c5295,stroke:#563a6b,color:#fff
175
+ style EntitySubClassGeneratorBase fill:#7c5295,stroke:#563a6b,color:#fff
176
+ style AngularClientGeneratorBase fill:#7c5295,stroke:#563a6b,color:#fff
177
+ style GraphQLServerGeneratorBase fill:#7c5295,stroke:#563a6b,color:#fff
178
+ style ActionSubClassGeneratorBase fill:#7c5295,stroke:#563a6b,color:#fff
179
+ ```
99
180
 
100
- ### 🏗️ Entity Subclass Generation
101
- Generates **bullet-proof TypeScript classes** from your database schema:
181
+ To override any generator, subclass the base and register it:
102
182
 
103
183
  ```typescript
104
- // Auto-generated from your schema
105
- export class AIPromptEntity extends BaseEntity {
106
- // 30+ properties with perfect types
107
- PromptRole: 'System' | 'User' | 'Assistant' | 'SystemOrUser';
108
-
109
- // AI-powered validation from CHECK constraints
110
- validate(): ValidationResult {
111
- return this.validateWithZod(AIPromptSchema);
112
- }
184
+ import { RegisterClass } from '@memberjunction/global';
185
+ import { EntitySubClassGeneratorBase } from '@memberjunction/codegen-lib';
186
+
187
+ @RegisterClass(EntitySubClassGeneratorBase, undefined, 1) // priority 1 overrides default (0)
188
+ export class CustomEntityGenerator extends EntitySubClassGeneratorBase {
189
+ // Override methods to customize generation
113
190
  }
114
191
  ```
115
192
 
116
- ### 🎨 Angular Component Generation
117
- Creates **production-ready Angular forms** with intelligent field types:
193
+ ## Usage
194
+
195
+ ### Running the Full Pipeline
118
196
 
119
197
  ```typescript
120
- // Auto-detects relationships and creates search components
121
- <mj-form-field
122
- FieldName="CategoryID"
123
- Type="textbox" // Smart field type selection
124
- LinkType="Record" // Auto-detected relationship
125
- LinkComponentType="Search" // AI chose search over dropdown
126
- ></mj-form-field>
127
- ```
198
+ import { RunCodeGenBase, initializeConfig } from '@memberjunction/codegen-lib';
128
199
 
129
- ### 🗃️ SQL Script Generation
130
- Generates **optimized database objects** with best practices:
200
+ // Initialize configuration from working directory
201
+ const config = initializeConfig(process.cwd());
131
202
 
132
- ```sql
133
- -- Auto-generated indexes for performance
134
- CREATE INDEX IDX_AUTO_MJ_FKEY_AIPrompt_CategoryID
135
- ON [AIPrompt] ([CategoryID]);
203
+ // Run the complete code generation pipeline
204
+ const codeGen = new RunCodeGenBase();
205
+ await codeGen.Run();
136
206
 
137
- -- Complete CRUD procedures with validation
138
- CREATE PROCEDURE [spCreateAIPrompt]
139
- @PromptRole nvarchar(20) -- Validated against CHECK constraint
140
- -- Full implementation auto-generated
207
+ // Or skip database operations for faster UI-only regeneration
208
+ await codeGen.Run(true);
141
209
  ```
142
210
 
143
- #### 🌲 Automatic Recursive Hierarchy Support
211
+ The convenience function provides a simpler entry point:
144
212
 
145
- CodeGen **automatically detects self-referential foreign keys** and generates `Root{FieldName}` columns in base views using efficient recursive CTEs. This enables instant root node lookup for hierarchical data structures with **zero overhead when not selected**.
213
+ ```typescript
214
+ import { runMemberJunctionCodeGeneration } from '@memberjunction/codegen-lib';
146
215
 
147
- **How It Works:**
216
+ await runMemberJunctionCodeGeneration();
217
+ ```
148
218
 
149
- For any table with a self-referential foreign key (like `ParentTaskID` → `Task.ID`), CodeGen automatically:
219
+ ### Using Individual Generators
150
220
 
151
- 1. **Detects the recursive relationship** - Identifies foreign keys where `RelatedEntityID === entity.ID`
152
- 2. **Generates a recursive CTE** - Creates SQL that traverses the hierarchy to find the root
153
- 3. **Adds Root columns** - Exposes `Root{FieldName}` in the base view (e.g., `RootParentTaskID`)
154
- 4. **Zero-overhead when unused** - SQL optimizer eliminates the CTE when column not selected
221
+ Each generator can be used independently:
155
222
 
156
- **Example - Task Hierarchy:**
223
+ ```typescript
224
+ import { EntitySubClassGeneratorBase } from '@memberjunction/codegen-lib';
225
+ import { MJGlobal } from '@memberjunction/global';
157
226
 
158
- ```sql
159
- CREATE TABLE [Task] (
160
- [ID] uniqueidentifier PRIMARY KEY,
161
- [ParentTaskID] uniqueidentifier FOREIGN KEY REFERENCES [Task]([ID]),
162
- [Name] nvarchar(255)
227
+ const generator = MJGlobal.Instance.ClassFactory.CreateInstance<EntitySubClassGeneratorBase>(
228
+ EntitySubClassGeneratorBase
163
229
  );
230
+
231
+ await generator.generateAllEntitySubClasses(pool, entities, outputDir, false);
164
232
  ```
165
233
 
166
- **CodeGen Automatically Generates:**
234
+ ### Generating Class Registration Manifests
167
235
 
168
- ```sql
169
- CREATE VIEW [vwTasks]
170
- AS
171
- WITH
172
- CTE_RootParentTaskID AS (
173
- -- Anchor: rows with no parent (root nodes)
174
- SELECT
175
- [ID],
176
- [ID] AS [RootParentTaskID]
177
- FROM
178
- [__mj].[Task]
179
- WHERE
180
- [ParentTaskID] IS NULL
181
-
182
- UNION ALL
183
-
184
- -- Recursive: traverse up the hierarchy
185
- SELECT
186
- child.[ID],
187
- parent.[RootParentTaskID]
188
- FROM
189
- [__mj].[Task] child
190
- INNER JOIN
191
- CTE_RootParentTaskID parent ON child.[ParentTaskID] = parent.[ID]
192
- )
193
- SELECT
194
- t.*,
195
- CTE_RootParentTaskID.[RootParentTaskID] -- Auto-generated root column
196
- FROM
197
- [__mj].[Task] AS t
198
- LEFT OUTER JOIN
199
- CTE_RootParentTaskID
200
- ON
201
- t.[ID] = CTE_RootParentTaskID.[ID]
202
- ```
236
+ The manifest generator prevents tree-shaking of `@RegisterClass`-decorated classes:
203
237
 
204
- **Benefits:**
238
+ ```typescript
239
+ import { generateClassRegistrationsManifest } from '@memberjunction/codegen-lib';
205
240
 
206
- - **Automatic Detection** - No configuration needed, works for any recursive FK
207
- - ✅ **Multiple Recursive FKs** - Handles tables with multiple self-referential relationships
208
- - ✅ **SQL Optimizer Magic** - CTE only executes when `RootParentTaskID` is selected
209
- - **Always Correct** - No stale data (unlike computed columns or triggers)
210
- - ✅ **TypeScript Integration** - Root fields automatically appear in entity classes
211
- - ✅ **Naming Convention** - Consistent `Root{FieldName}` pattern across all entities
241
+ const result = await generateClassRegistrationsManifest({
242
+ outputPath: './src/generated/class-registrations-manifest.ts',
243
+ appDir: './packages/MJAPI',
244
+ excludePackages: ['@memberjunction'], // Use pre-built manifest for MJ packages
245
+ });
212
246
 
213
- **Use Cases:**
247
+ if (result.success) {
248
+ console.log(`${result.packages.length} packages, ${result.classes.length} classes`);
249
+ }
250
+ ```
214
251
 
215
- - **Organizational Charts** - `Employee.ManagerID` `RootManagerID` finds CEO
216
- - **Task Hierarchies** - `Task.ParentTaskID` → `RootParentTaskID` finds root project
217
- - **Category Trees** - `Category.ParentCategoryID` → `RootParentCategoryID` finds top level
218
- - **Comment Threads** - `Comment.ParentCommentID` → `RootParentCommentID` finds original post
219
- - **Bill of Materials** - `Part.ParentPartID` → `RootParentPartID` finds top-level assembly
252
+ See the [Class Manifest Guide](CLASS_MANIFEST_GUIDE.md) for comprehensive documentation on the manifest system.
220
253
 
221
- **Performance Note:**
254
+ ## Configuration
222
255
 
223
- The CTE approach is **ideal for read-heavy workloads** (typical in business applications). The SQL optimizer completely eliminates the CTE from the execution plan when the root column isn't selected, meaning zero overhead for queries that don't need hierarchy information.
256
+ CodeGenLib uses [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig) to locate configuration. The recommended approach is a `mj.config.cjs` file at the repository root:
224
257
 
225
- #### 🎯 Smart Delete Procedures with Cascade Handling
258
+ ```javascript
259
+ module.exports = {
260
+ // Database connection
261
+ dbHost: 'localhost',
262
+ dbPort: 1433,
263
+ dbDatabase: 'YourDatabase',
264
+ codeGenLogin: 'codegen_user',
265
+ codeGenPassword: 'your_password',
266
+ mjCoreSchema: '__mj',
267
+
268
+ // Output directories for each generator
269
+ output: [
270
+ { type: 'SQL', directory: '../../SQL Scripts/generated' },
271
+ { type: 'Angular', directory: '../MJExplorer/src/app/generated' },
272
+ { type: 'GraphQLServer', directory: '../MJAPI/src/generated' },
273
+ { type: 'CoreEntitySubclasses', directory: '../MJCoreEntities/src/generated' },
274
+ { type: 'EntitySubclasses', directory: '../GeneratedEntities/src/generated' },
275
+ ],
276
+
277
+ // AI-powered features
278
+ advancedGeneration: {
279
+ enableAdvancedGeneration: true,
280
+ features: [
281
+ { name: 'SmartFieldIdentification', enabled: true },
282
+ { name: 'FormLayoutGeneration', enabled: true },
283
+ { name: 'ParseCheckConstraints', enabled: true },
284
+ { name: 'TransitiveJoinIntelligence', enabled: true },
285
+ ],
286
+ },
226
287
 
227
- Our generated delete procedures are **production-grade** with intelligent handling of:
288
+ // SQL output for Flyway migrations
289
+ SQLOutput: {
290
+ enabled: true,
291
+ folderPath: './migrations/v3/',
292
+ convertCoreSchemaToFlywayMigrationFile: true,
293
+ },
228
294
 
229
- **1. Result Feedback - Know What Happened**
230
- ```sql
231
- -- Returns NULL for all PKs when no record found
232
- IF @@ROWCOUNT = 0
233
- SELECT NULL AS [CustomerID], NULL AS [OrderID]
234
- ELSE
235
- SELECT @CustomerID AS [CustomerID], @OrderID AS [OrderID]
295
+ // Force regeneration of specific objects
296
+ forceRegeneration: {
297
+ enabled: false,
298
+ entityWhereClause: "SchemaName = 'dbo'",
299
+ baseViews: true,
300
+ spUpdate: true,
301
+ },
302
+ };
236
303
  ```
237
304
 
238
- **2. Cascade Deletes via Stored Procedure Calls**
305
+ All configuration is validated at startup using Zod schemas, with clear error messages for invalid settings. Environment variables (`DB_HOST`, `DB_DATABASE`, `CODEGEN_DB_USERNAME`, `CODEGEN_DB_PASSWORD`) provide fallback values for database connection settings.
239
306
 
240
- Instead of basic DELETE statements, we generate **cursor-based cascade operations** that respect your business logic:
307
+ ### Key Configuration Sections
241
308
 
242
- ```sql
243
- -- BAD: Direct DELETE (bypasses business logic)
244
- DELETE FROM OrderItems WHERE OrderID = @OrderID
309
+ | Section | Purpose |
310
+ |---------|---------|
311
+ | `output` | Maps each generator type to its output directory |
312
+ | `advancedGeneration` | Controls which AI-powered features are enabled |
313
+ | `newEntityDefaults` | Default settings for newly discovered entities (permissions, tracking, API access) |
314
+ | `forceRegeneration` | Surgically regenerate specific SQL object types for filtered entities |
315
+ | `SQLOutput` | Controls Flyway migration file generation from SQL logging |
316
+ | `commands` | Shell commands to run before/after generation (typically package builds) |
317
+ | `excludeSchemas` / `excludeTables` | Filter schemas and tables from metadata discovery |
245
318
 
246
- -- GOOD: Cursor-based SP calls (respects custom logic)
247
- DECLARE @RelatedItemID INT
248
- DECLARE cascade_delete_OrderItem_cursor CURSOR FOR
249
- SELECT [ItemID] FROM [OrderItems] WHERE [OrderID] = @OrderID
319
+ ## What Gets Generated
250
320
 
251
- OPEN cascade_delete_OrderItem_cursor
252
- FETCH NEXT FROM cascade_delete_OrderItem_cursor INTO @RelatedItemID
321
+ ### TypeScript Entity Classes
253
322
 
254
- WHILE @@FETCH_STATUS = 0
255
- BEGIN
256
- -- Calls YOUR stored procedure, enabling N-level cascades
257
- EXEC [spDeleteOrderItem] @RelatedItemID
258
- FETCH NEXT FROM cascade_delete_OrderItem_cursor INTO @RelatedItemID
259
- END
323
+ From a SQL table with CHECK constraints, CodeGen produces a complete entity class:
260
324
 
261
- CLOSE cascade_delete_OrderItem_cursor
262
- DEALLOCATE cascade_delete_OrderItem_cursor
263
- ```
325
+ ```typescript
326
+ // Auto-generated from database schema
327
+ export class AIPromptEntity extends BaseEntity {
328
+ // Typed getter/setter for CHECK-constrained field
329
+ get PromptRole(): 'System' | 'User' | 'Assistant' | 'SystemOrUser' {
330
+ return this.Get('PromptRole');
331
+ }
332
+ set PromptRole(value: 'System' | 'User' | 'Assistant' | 'SystemOrUser') {
333
+ this.Set('PromptRole', value);
334
+ }
264
335
 
265
- **Benefits:**
266
- - **Respects custom delete logic** in related entities
267
- - ✅ **Enables multi-level cascades** (if OrderItem has its own cascades)
268
- - ✅ **Maintains referential integrity** through proper SP calls
269
- - ✅ **Clear result feedback** - NULL means no record deleted
336
+ // Zod validation from CHECK constraint
337
+ validate(): ValidationResult {
338
+ return this.validateWithZod(AIPromptSchema);
339
+ }
340
+ }
341
+
342
+ // Zod schema with union types from CHECK constraint
343
+ export const AIPromptSchema = z.object({
344
+ PromptRole: z.union([
345
+ z.literal('System'),
346
+ z.literal('User'),
347
+ z.literal('Assistant'),
348
+ z.literal('SystemOrUser'),
349
+ ]),
350
+ // ... all other fields
351
+ });
352
+ ```
270
353
 
271
- **3. Nullable Foreign Key Handling**
354
+ ### SQL Views with Recursive Hierarchy Support
272
355
 
273
- For nullable FKs, we update via stored procedures too:
356
+ For tables with self-referential foreign keys, CodeGen automatically generates recursive CTEs:
274
357
 
275
358
  ```sql
276
- -- Fetch all fields, update only the FK to NULL
277
- DECLARE cascade_update_Customer_cursor CURSOR FOR
278
- SELECT * FROM [Customers] WHERE [RegionID] = @RegionID
359
+ -- Auto-detected: Task.ParentTaskID references Task.ID
360
+ CREATE VIEW [vwTasks] AS
361
+ WITH CTE_RootParentTaskID AS (
362
+ SELECT [ID], [ID] AS [RootParentTaskID]
363
+ FROM [__mj].[Task]
364
+ WHERE [ParentTaskID] IS NULL
365
+
366
+ UNION ALL
367
+
368
+ SELECT child.[ID], parent.[RootParentTaskID]
369
+ FROM [__mj].[Task] child
370
+ INNER JOIN CTE_RootParentTaskID parent
371
+ ON child.[ParentTaskID] = parent.[ID]
372
+ )
373
+ SELECT t.*, cte.[RootParentTaskID]
374
+ FROM [__mj].[Task] AS t
375
+ LEFT OUTER JOIN CTE_RootParentTaskID cte ON t.[ID] = cte.[ID]
376
+ ```
377
+
378
+ The CTE is zero-overhead: the SQL optimizer eliminates it entirely when the root column is not selected.
379
+
380
+ ### Angular Form Components
279
381
 
280
- -- In cursor loop:
281
- SET @UpdateRegionID = NULL -- Only FK changes
282
- EXEC [spUpdateCustomer] @CustomerID, @Name, @Email, @UpdateRegionID
382
+ CodeGen creates production-ready Angular forms with AI-determined field categories and smart field types:
383
+
384
+ ```typescript
385
+ @Component({
386
+ selector: 'mj-ai-prompt-form',
387
+ template: `
388
+ <mj-form-field [record]="record"
389
+ FieldName="PromptRole"
390
+ Type="dropdownlist"
391
+ [EditMode]="EditMode">
392
+ </mj-form-field>
393
+ `
394
+ })
395
+ export class AIPromptFormComponent extends BaseFormComponent { }
283
396
  ```
284
397
 
285
- **4. Configuration Error Detection**
398
+ ### Cascade Delete Procedures
286
399
 
287
- CodeGen **warns you** about misconfigured cascade scenarios:
400
+ Delete procedures use cursor-based stored procedure calls to respect business logic at every level of the hierarchy:
288
401
 
289
402
  ```sql
290
- -- WARNING: Orders has non-nullable FK to Customer but doesn't allow delete API
291
- -- This will cause a referential integrity violation
403
+ CREATE PROCEDURE [spDeleteOrder] @ID UNIQUEIDENTIFIER AS
404
+ BEGIN
405
+ -- Cascade through child stored procedures
406
+ DECLARE @ItemID UNIQUEIDENTIFIER
407
+ DECLARE cascade_cursor CURSOR FOR
408
+ SELECT [ID] FROM [OrderItems] WHERE [OrderID] = @ID
409
+
410
+ OPEN cascade_cursor
411
+ FETCH NEXT FROM cascade_cursor INTO @ItemID
412
+ WHILE @@FETCH_STATUS = 0
413
+ BEGIN
414
+ EXEC [spDeleteOrderItem] @ItemID -- Respects OrderItem's own cascade logic
415
+ FETCH NEXT FROM cascade_cursor INTO @ItemID
416
+ END
417
+ CLOSE cascade_cursor
418
+ DEALLOCATE cascade_cursor
419
+
420
+ DELETE FROM [Orders] WHERE [ID] = @ID
421
+ END
292
422
  ```
293
423
 
294
- The warnings appear both in generated SQL **and** console output during generation.
424
+ ## AI-Powered Advanced Generation
425
+
426
+ When `advancedGeneration.enableAdvancedGeneration` is enabled, CodeGen uses AI prompts (stored in the database as AI Prompt entities) to enhance the generation process:
427
+
428
+ ```mermaid
429
+ flowchart TD
430
+ subgraph Features["AI-Powered Features"]
431
+ SF["Smart Field\nIdentification"]
432
+ FL["Form Layout\nGeneration"]
433
+ CC["CHECK Constraint\nParsing"]
434
+ ED["Entity\nDescriptions"]
435
+ TJ["Transitive Join\nIntelligence"]
436
+ EN["Entity Name\nGeneration"]
437
+ end
438
+
439
+ subgraph Results["What AI Determines"]
440
+ SF --> R1["Name fields, Default-in-View\nfields, Searchable fields"]
441
+ FL --> R2["Field categories, Icons\nDisplay names, Extended types"]
442
+ CC --> R3["Zod schemas, Validation\nfunctions, Descriptions"]
443
+ ED --> R4["Entity descriptions\nfor new entities"]
444
+ TJ --> R5["Junction table detection\nMany-to-many relationships"]
445
+ EN --> R6["Human-friendly entity\nnames from table names"]
446
+ end
447
+
448
+ style Features fill:#7c5295,stroke:#563a6b,color:#fff
449
+ style Results fill:#2d8659,stroke:#1a5c3a,color:#fff
450
+ ```
295
451
 
296
- #### 🔄 Smart Update Procedures with Result Validation
452
+ | Feature | Purpose | When It Runs |
453
+ |---------|---------|-------------|
454
+ | `SmartFieldIdentification` | Determines which field is the "name" field, which fields show in default views, and which are searchable | Entity/field creation, or when `AutoUpdate` flags allow |
455
+ | `FormLayoutGeneration` | Groups fields into semantic categories with icons and display names | Every run (forms are always regenerated) |
456
+ | `ParseCheckConstraints` | Translates SQL CHECK constraints into Zod validation schemas and TypeScript union types | When CHECK constraints are detected |
457
+ | `EntityDescriptions` | Generates human-readable descriptions for entities | Entity creation only |
458
+ | `TransitiveJoinIntelligence` | Detects junction tables and many-to-many relationships | Entity/relationship creation |
459
+ | `EntityNames` | Converts technical table names to user-friendly entity names | Entity creation only |
460
+ | `VirtualEntityFieldDecoration` | Analyzes SQL view definitions to identify PKs, FKs, descriptions, and extended types for virtual entities | Virtual entity creation (idempotent unless `forceRegenerate` option is set) |
297
461
 
298
- Our update procedures also provide **clear feedback** when operating on non-existent records:
462
+ ### Form Layout Stability Guarantees
299
463
 
300
- ```sql
301
- -- Check if update affected any rows
302
- IF @@ROWCOUNT = 0
303
- -- Return empty result set (maintains column structure)
304
- SELECT TOP 0 * FROM [vwCustomer] WHERE 1=0
305
- ELSE
306
- -- Return the updated record with calculated fields
307
- SELECT * FROM [vwCustomer] WHERE [CustomerID] = @CustomerID
308
- ```
464
+ The form layout system enforces stability to prevent unnecessary churn:
309
465
 
310
- **Why This Matters:**
311
- - **Empty result set** = Record not found (update failed)
312
- - **Record returned** = Update successful
313
- - **Maintains schema** = Calling code doesn't break
314
- - **Includes calculated fields** = Get the latest computed values
466
+ - Existing category names and icons are never changed by AI
467
+ - AI can assign fields to existing categories or create categories for genuinely new field groups
468
+ - Existing fields cannot be moved to newly created categories (prevents renaming)
469
+ - Per-field `AutoUpdateCategory` and `AutoUpdateDisplayName` flags provide granular control
315
470
 
316
- ### 🌐 GraphQL Schema Generation
317
- Creates **type-safe GraphQL APIs** from your entities:
471
+ ## Force Regeneration
318
472
 
319
- ```graphql
320
- type AIPrompt {
321
- id: ID!
322
- promptRole: PromptRoleEnum! # Auto-generated from CHECK constraint
323
- category: AIPromptCategory # Auto-resolved relationships
473
+ Regenerate specific SQL objects without schema changes using surgical filtering:
474
+
475
+ ```javascript
476
+ // In mj.config.cjs
477
+ forceRegeneration: {
478
+ enabled: true,
479
+ // Filter to specific entities
480
+ entityWhereClause: "SchemaName = 'CRM' AND __mj_UpdatedAt >= '2025-06-24'",
481
+ // Control which object types regenerate
482
+ baseViews: true,
483
+ spCreate: false,
484
+ spUpdate: true,
485
+ spDelete: false,
486
+ indexes: true,
324
487
  }
488
+ ```
325
489
 
326
- enum PromptRoleEnum {
327
- SYSTEM
328
- USER
329
- ASSISTANT
330
- SYSTEMORUSER
490
+ Only the intersection of matched entities and enabled object types gets regenerated.
491
+
492
+ ## SQL Migration Logging
493
+
494
+ All SQL generated during metadata management and object generation is logged to a Flyway-compatible migration file. The `SQLOutput` configuration controls this behavior:
495
+
496
+ ```javascript
497
+ SQLOutput: {
498
+ enabled: true,
499
+ folderPath: './migrations/v3/',
500
+ appendToFile: true,
501
+ convertCoreSchemaToFlywayMigrationFile: true,
502
+ schemaPlaceholders: [
503
+ { schema: '__mj', placeholder: '${flyway:defaultSchema}' },
504
+ ],
331
505
  }
332
506
  ```
333
507
 
334
- ### 🔬 Database Schema Introspection
335
- **Reverse-engineers your entire database** into metadata:
508
+ The `SQLLogging` class accumulates all SQL statements during a run and writes them as a single migration file with schema names replaced by Flyway placeholders.
509
+
510
+ ## Source Structure
336
511
 
337
- ```typescript
338
- const schemaInfo = await analyzeSchema(connection);
339
- // Discovers tables, relationships, constraints, indexes
340
- // Feeds AI engine for intelligent code generation
512
+ ```
513
+ src/
514
+ index.ts # Public API exports
515
+ runCodeGen.ts # RunCodeGenBase - main pipeline orchestrator
516
+
517
+ Config/
518
+ config.ts # Zod-validated configuration schemas and loaders
519
+ db-connection.ts # SQL Server connection pool management
520
+
521
+ Database/
522
+ manage-metadata.ts # ManageMetadataBase - schema analysis and metadata sync
523
+ sql_codegen.ts # SQLCodeGenBase - views, procedures, indexes, permissions
524
+ sql.ts # SQLUtilityBase - SQL file management and execution
525
+ dbSchema.ts # DBSchemaGeneratorBase - JSON schema export
526
+ reorder-columns.ts # Table column reordering utilities
527
+
528
+ Angular/
529
+ angular-codegen.ts # AngularClientGeneratorBase - form and module generation
530
+ related-entity-components.ts # Base classes for related entity display components
531
+ entity-data-grid-related-entity-component.ts # Data grid component generator
532
+ join-grid-related-entity-component.ts # Join grid component generator
533
+ timeline-related-entity-component.ts # Timeline component generator
534
+
535
+ Misc/
536
+ entity_subclasses_codegen.ts # EntitySubClassGeneratorBase - TypeScript entity generation
537
+ action_subclasses_codegen.ts # ActionSubClassGeneratorBase - Action class generation
538
+ graphql_server_codegen.ts # GraphQLServerGeneratorBase - resolver generation
539
+ advanced_generation.ts # AdvancedGeneration - AI-powered enhancement features
540
+ status_logging.ts # Spinner and log utilities (ora-based)
541
+ sql_logging.ts # SQLLogging - migration file accumulator
542
+ system_integrity.ts # SystemIntegrityBase - post-generation validation
543
+ createNewUser.ts # CreateNewUserBase - initial user setup
544
+ runCommand.ts # RunCommandsBase - shell command execution
545
+ util.ts # File system and sorting utilities
546
+
547
+ Manifest/
548
+ GenerateClassRegistrationsManifest.ts # Tree-shaking prevention manifest generator
341
549
  ```
342
550
 
343
- ## Advanced Features That Blow Minds
551
+ ## API Reference
344
552
 
345
- ### 🎨 AI-Powered Form Layout Generation
553
+ ### RunCodeGenBase
346
554
 
347
- CodeGen uses AI to automatically organize entity fields into **semantic categories** with icons, creating intuitive form layouts without manual configuration.
555
+ The main orchestrator class. Creates instances of all generator classes via `MJGlobal.ClassFactory` and runs the pipeline.
348
556
 
349
- #### What It Does:
557
+ | Method | Description |
558
+ |--------|-------------|
559
+ | `Run(skipDatabaseGeneration?)` | Execute the full code generation pipeline. Pass `true` to skip database operations. |
560
+ | `setupDataSource()` | Initialize the SQL Server connection pool and data provider. |
350
561
 
351
- 1. **Field Categorization** - Groups fields into domain-specific categories (e.g., "Billing Address", "Pricing and Charges", "System Metadata")
352
- 2. **Category Icons** - Assigns Font Awesome icons to each category for visual navigation
353
- 3. **Category Descriptions** - Generates tooltip descriptions for UX enhancement
354
- 4. **Entity Importance Analysis** - Determines if entities should appear in navigation for new users
355
- 5. **Smart Display Names** - Converts technical field names to user-friendly labels (e.g., `BillToAddress1` → "Billing Address Line 1")
562
+ ### ManageMetadataBase
356
563
 
357
- #### Entity Importance Detection:
564
+ Analyzes database schema changes and updates MJ metadata tables.
358
565
 
359
- The AI uses **FK ratio analysis** to classify entities:
566
+ | Method | Description |
567
+ |--------|-------------|
568
+ | `manageMetadata(pool, user)` | Full metadata management: detect schema changes, create entities/fields, run AI features. |
569
+ | `loadGeneratedCode(pool, user)` | Load previously generated AI code from database (used when skipping DB generation). |
360
570
 
361
- | Entity Type | FK Ratio | Example | DefaultForNewUser |
362
- |-------------|----------|---------|-------------------|
363
- | **Primary** | 10-30% | Contact, Order, Deal | ✅ Yes |
364
- | **Supporting** | 20-40% | OrderItem, Address | Sometimes |
365
- | **Reference/Type** | 0-20% | OrderStatus, ContactType | ❌ No |
366
- | **Junction** | 40-80% | UserRole, ContactAccount | ❌ No |
571
+ ### SQLCodeGenBase
367
572
 
368
- #### Stability Guarantees:
573
+ Generates database objects: views, stored procedures, indexes, and permissions.
369
574
 
370
- The system enforces **category stability** to prevent unnecessary churn on existing entities:
575
+ | Method | Description |
576
+ |--------|-------------|
577
+ | `manageSQLScriptsAndExecution(pool, entities, dir, user)` | Generate and execute all SQL objects for the given entities. |
578
+ | `runCustomSQLScripts(pool, when)` | Execute custom SQL scripts configured for the specified timing. |
371
579
 
372
- **What's Preserved (Never Changed by AI):**
373
- - ✅ Existing category names - AI cannot rename "Personal Info" to "Personal Details"
374
- - ✅ Existing category icons - Icons set by admins or previous runs are preserved
375
- - ✅ Existing category descriptions - Descriptions are only added, never modified
580
+ ### EntitySubClassGeneratorBase
376
581
 
377
- **What AI Can Do:**
378
- - ✅ Assign NEW fields to existing categories
379
- - ✅ Assign NEW fields to NEW categories (when no existing category fits)
380
- - ✅ Move existing fields between EXISTING categories (with discretion)
381
- - ❌ Move existing fields to NEW categories (blocked - prevents renaming)
582
+ Generates TypeScript entity classes with Zod validation.
382
583
 
383
- **Enforcement Example:**
384
- ```
385
- Field 'Email' is in category 'Contact Info'
386
- LLM suggests moving to 'Communication Details' (new category)
387
- REJECTED: Cannot move existing field to new category
388
- → Field stays in 'Contact Info'
389
- ```
584
+ | Method | Description |
585
+ |--------|-------------|
586
+ | `generateAllEntitySubClasses(pool, entities, dir, skipDB)` | Generate all entity subclass files including Zod schemas. |
587
+ | `generateEntitySubClass(pool, entity, includeHeader, skipDB)` | Generate a single entity subclass. |
588
+ | `GenerateSchemaAndType(entity)` | Generate Zod schema and TypeScript type for an entity. |
390
589
 
391
- **Control Flags on EntityField:**
392
- - `AutoUpdateCategory` - If FALSE, field's category is locked
393
- - `AutoUpdateDisplayName` - If FALSE, display name is locked
394
- - `AutoUpdateIsNameField` - If FALSE, name field designation is locked
590
+ ### AngularClientGeneratorBase
395
591
 
396
- #### DefaultForNewUser - Only for New Entities:
592
+ Generates Angular form components, section components, and Angular modules.
397
593
 
398
- The `DefaultForNewUser` flag is **only set when an entity is first created**, not on subsequent updates. This ensures:
399
- - Admins retain full control over navigation visibility
400
- - CodeGen won't override manual admin decisions
401
- - Existing entity configurations remain stable
594
+ | Method | Description |
595
+ |--------|-------------|
596
+ | `generateAngularCode(entities, dir, prefix, user)` | Generate all Angular components and modules. |
402
597
 
403
- #### Storage Format:
598
+ ### GraphQLServerGeneratorBase
404
599
 
405
- Category information is stored in `EntitySetting` with two formats for compatibility:
600
+ Generates TypeGraphQL resolver and type definitions.
406
601
 
407
- **New Format** (`FieldCategoryInfo`):
408
- ```json
409
- {
410
- "Billing Address": {
411
- "icon": "fa fa-file-invoice",
412
- "description": "Address for invoice delivery and billing correspondence"
413
- },
414
- "System Metadata": {
415
- "icon": "fa fa-cog",
416
- "description": "System-managed audit and tracking fields"
417
- }
418
- }
419
- ```
602
+ | Method | Description |
603
+ |--------|-------------|
604
+ | `generateGraphQLServerCode(entities, dir, lib, exclude)` | Generate GraphQL resolvers for all entities. |
420
605
 
421
- **Legacy Format** (`FieldCategoryIcons`) - maintained for backwards compatibility:
422
- ```json
423
- {
424
- "Billing Address": "fa fa-file-invoice",
425
- "System Metadata": "fa fa-cog"
426
- }
427
- ```
606
+ ### generateClassRegistrationsManifest
428
607
 
429
- ### 🤖 AI-Powered CHECK Constraint Translation
430
- Our AI doesn't just copy constraints - it **understands intent**:
608
+ Generates an import manifest that prevents tree-shaking of `@RegisterClass` decorated classes. See the [Class Manifest Guide](CLASS_MANIFEST_GUIDE.md) for full documentation.
431
609
 
432
- ```sql
433
- -- Complex constraint
434
- CHECK ([Status] IN ('Draft', 'Published', 'Archived')
435
- AND [PublishedAt] IS NOT NULL WHEN [Status] = 'Published')
436
- ```
610
+ | Option | Description |
611
+ |--------|-------------|
612
+ | `outputPath` | Path for the generated manifest file |
613
+ | `appDir` | Directory containing the app's `package.json` (default: `process.cwd()`) |
614
+ | `filterBaseClasses` | Only include classes extending specific base classes |
615
+ | `excludePackages` | Skip packages matching name prefixes (e.g., `['@memberjunction']`) |
437
616
 
438
- Becomes **perfect TypeScript**:
617
+ ### Configuration Functions
439
618
 
440
- ```typescript
441
- Status: z.union([z.literal('Draft'), z.literal('Published'), z.literal('Archived')])
442
- .refine((status, ctx) => {
443
- if (status === 'Published' && !this.PublishedAt) {
444
- ctx.addIssue({ code: 'custom', message: 'Published items must have PublishedAt' });
445
- }
446
- })
447
- ```
619
+ | Function | Description |
620
+ |----------|-------------|
621
+ | `initializeConfig(cwd)` | Load and validate configuration from the given directory |
622
+ | `outputDir(type, fallback)` | Get the configured output directory for a generator type |
623
+ | `getSettingValue(name, default)` | Get a named setting value from configuration |
624
+ | `mj_core_schema()` | Get the MJ core schema name (typically `__mj`) |
448
625
 
449
- ### 🔄 Real-Time Synchronization
450
- Change your database schema → **Everything updates automatically**:
626
+ ## Dependencies
451
627
 
452
- 1. **Flyway migration** executes
453
- 2. **CodeGen detects changes**
454
- 3. **Regenerates affected code**
455
- 4. **Type safety maintained** across entire stack
456
- 5. **Zero manual intervention**
628
+ This package depends on:
457
629
 
458
- ### 🚀 Performance Optimization
459
- - **Intelligent caching** prevents unnecessary regeneration
460
- - **Incremental updates** for changed entities only
461
- - **Optimized SQL** with proper indexing strategies
462
- - **Lazy loading** for large schema datasets
630
+ - [@memberjunction/core](../MJCore) - Entity framework, metadata system, and type utilities
631
+ - [@memberjunction/core-entities](../MJCoreEntities) - Generated entity classes for MJ system entities
632
+ - [@memberjunction/global](../MJGlobal) - `@RegisterClass` decorator and `MJGlobal.ClassFactory`
633
+ - [@memberjunction/sqlserver-dataprovider](../SQLServerDataProvider) - SQL Server data provider and connection management
634
+ - [@memberjunction/ai](../AI) - AI provider abstraction layer
635
+ - [@memberjunction/ai-prompts](../AI/Prompts) - AI prompt execution for advanced generation features
636
+ - [@memberjunction/ai-core-plus](../AI/CorePlus) - AI prompt parameter types
637
+ - [@memberjunction/aiengine](../AIEngine) - AI engine for model and prompt configuration
638
+ - [@memberjunction/actions](../Actions/Engine) - Action engine for action code generation
639
+ - [@memberjunction/actions-base](../Actions/Base) - Action base classes and types
640
+ - [@memberjunction/config](../Config) - Configuration merging utilities
641
+ - [@memberjunction/server-bootstrap-lite](../server-bootstrap-lite) - Pre-built class registration manifest
463
642
 
464
- ### 🔒 Enterprise-Grade Security
465
- - **Parameterized queries** in all generated SQL
466
- - **Input validation** at every layer
467
- - **SQL injection protection** built-in
468
- - **Type-safe APIs** prevent runtime errors
643
+ ## Related Packages
469
644
 
470
- ## Configuration
645
+ - [@memberjunction/cli](../MJCLI) - CLI that invokes CodeGenLib (`mj codegen` commands)
646
+ - [@memberjunction/core-entities](../MJCoreEntities) - Contains the generated `entity_subclasses.ts` output
647
+ - [@memberjunction/server-bootstrap](../server-bootstrap) - Ships pre-built server-side class manifest
648
+ - [@memberjunction/ng-bootstrap](../Angular/ng-bootstrap) - Ships pre-built Angular class manifest
471
649
 
472
- Create a `.memberjunctionrc` file:
650
+ ## Documentation
473
651
 
474
- ```json
475
- {
476
- "memberjunction": {
477
- "database": {
478
- "server": "localhost",
479
- "database": "YourDatabase",
480
- "trustedConnection": true
481
- },
482
- "directories": {
483
- "output": "./generated",
484
- "entities": "./generated/entities",
485
- "actions": "./generated/actions",
486
- "angular": "./generated/angular",
487
- "sql": "./generated/sql"
488
- },
489
- "ai": {
490
- "enabled": true,
491
- "provider": "openai" // Powers constraint translation
492
- }
493
- }
494
- }
495
- ```
652
+ - [Class Manifest Guide](CLASS_MANIFEST_GUIDE.md) - Comprehensive guide to the manifest system for preventing tree-shaking of `@RegisterClass` classes
653
+ - [EXAMPLE_MANIFEST_MJAPI.md](EXAMPLE_MANIFEST_MJAPI.md) - Example server-side manifest (54 packages, 715 classes)
654
+ - [EXAMPLE_MANIFEST_MJEXPLORER.md](EXAMPLE_MANIFEST_MJEXPLORER.md) - Example client-side manifest (17 packages, 721 classes)
496
655
 
497
- ## Real-World Example
656
+ ## IS-A Type Relationships in CodeGen
498
657
 
499
- Starting with a simple table:
658
+ MemberJunction supports **IS-A (inheritance) relationships** between entities, where one entity extends another by adding additional fields while inheriting the parent's schema. CodeGen automatically handles IS-A relationships with specialized generation logic.
500
659
 
501
- ```sql
502
- CREATE TABLE [Customer] (
503
- [ID] uniqueidentifier PRIMARY KEY DEFAULT newsequentialid(),
504
- [Name] nvarchar(255) NOT NULL,
505
- [Status] nvarchar(20) CHECK ([Status] IN ('Active', 'Inactive', 'Suspended')),
506
- [CreatedAt] datetimeoffset DEFAULT getutcdate()
507
- );
508
- ```
660
+ For comprehensive conceptual documentation, see the **[IS-A Relationships Guide](../../MJCore/docs/isa-relationships.md)** in MJCore.
509
661
 
510
- **One CodeGen run produces:**
662
+ ### How CodeGen Handles IS-A Entities
511
663
 
512
- ### TypeScript Entity (175 lines)
513
- ```typescript
514
- export class CustomerEntity extends BaseEntity {
515
- Status: 'Active' | 'Inactive' | 'Suspended';
516
- // + complete validation, save methods, relationships
517
- }
518
- ```
664
+ When an entity has a `ParentEntity` relationship (IS-A child):
519
665
 
520
- ### Angular Component (89 lines)
521
- ```typescript
522
- @Component({
523
- template: `Complete form with validation and smart controls`
524
- })
525
- export class CustomerDetailsComponent {
526
- // Ready for production use
527
- }
528
- ```
666
+ #### 1. SQL View Generation - Parent JOINs
667
+ **Child views automatically JOIN to parent views** to provide a complete record with all inherited fields:
529
668
 
530
- ### SQL Procedures (200+ lines)
531
669
  ```sql
532
- -- spCreateCustomer, spUpdateCustomer, spDeleteCustomer
533
- -- Complete with validation and error handling
670
+ -- CodeGen automatically generates:
671
+ CREATE VIEW [vwEmployee]
672
+ AS
673
+ SELECT
674
+ e.*, -- All Employee fields
675
+ p.FirstName, -- Inherited from Person
676
+ p.LastName, -- Inherited from Person
677
+ p.DateOfBirth -- Inherited from Person
678
+ FROM
679
+ [__mj].[Employee] AS e
680
+ INNER JOIN
681
+ [__mj].[vwPerson] AS p ON e.[ID] = p.[ID]
534
682
  ```
535
683
 
536
- ### GraphQL Schema (45 lines)
537
- ```graphql
538
- type Customer {
539
- # Complete type-safe schema
540
- }
541
- ```
684
+ This ensures querying the child view returns a complete record including all parent fields.
542
685
 
543
- **Total: 500+ lines of production code from 6 lines of SQL.**
686
+ #### 2. Stored Procedure Generation - Child Fields Only
687
+ **Create and Update procedures only include the child's own fields**, not parent fields:
544
688
 
545
- ### Class Registrations Manifest Generation
689
+ ```sql
690
+ -- spCreateEmployee only has Employee-specific parameters
691
+ CREATE PROCEDURE [spCreateEmployee]
692
+ @ID uniqueidentifier,
693
+ @EmployeeNumber nvarchar(50),
694
+ @HireDate date,
695
+ @Salary decimal(18,2)
696
+ -- No FirstName, LastName (those are Person fields)
697
+ AS BEGIN
698
+ -- Only inserts into Employee table
699
+ INSERT INTO [__mj].[Employee] (ID, EmployeeNumber, HireDate, Salary)
700
+ VALUES (@ID, @EmployeeNumber, @HireDate, @Salary)
701
+ END
702
+ ```
546
703
 
547
- Generates an import manifest that prevents tree-shaking of `@RegisterClass` decorated classes. The tool walks an app's transitive dependency tree and produces a tailored manifest for each application.
704
+ **Why this design?** When creating an Employee, you first create the Person record (which gets an ID), then use that same ID to create the Employee record. The stored procedures reflect this two-step creation pattern.
548
705
 
549
- **Why this matters:** Decorators like `@RegisterClass` only execute if their file is imported. Bundlers can tree-shake entire files that appear unused, silently breaking MemberJunction's class factory system. The manifest ensures all registered classes are imported.
706
+ #### 3. GraphQL Schema Generation - Complete Field Set
707
+ **GraphQL input types include ALL fields (parent + child)** for seamless API usage:
550
708
 
551
- **How it works:**
552
- 1. Reads the app's `package.json` and walks the full transitive dependency tree
553
- 2. Scans each dependency's `src/**/*.ts` for `@RegisterClass` decorators using the TypeScript Compiler API
554
- 3. Generates a manifest file with `import` statements for every package that contains registered classes
709
+ ```graphql
710
+ input CreateEmployeeInput {
711
+ # Parent fields (from Person)
712
+ firstName: String!
713
+ lastName: String!
714
+ dateOfBirth: Date
715
+
716
+ # Child fields (from Employee)
717
+ employeeNumber: String!
718
+ hireDate: Date!
719
+ salary: Decimal!
720
+ }
721
+ ```
555
722
 
556
- **Per-app results (verified):**
723
+ This provides a convenient single-operation API while the resolver handles the underlying two-step creation.
557
724
 
558
- | App | Deps Walked | Packages with @RegisterClass | Total Classes |
559
- |-----|-------------|------------------------------|---------------|
560
- | MJAPI | 985 | 54 | 715 |
561
- | MJExplorer | 1179 | 17 | 721 |
725
+ #### 4. TypeScript Entity Classes - JSDoc Annotations
726
+ **Generated entity classes include JSDoc annotations** on getter/setter methods to indicate IS-A relationships:
562
727
 
563
- **Programmatic usage:**
564
728
  ```typescript
565
- import { generateClassRegistrationsManifest } from '@memberjunction/codegen-lib';
729
+ export class EmployeeEntity extends BaseEntity {
730
+ /**
731
+ * Inherited from Person entity
732
+ */
733
+ get FirstName(): string {
734
+ return this.Get('FirstName');
735
+ }
566
736
 
567
- const result = await generateClassRegistrationsManifest({
568
- outputPath: './src/generated/class-registrations-manifest.ts',
569
- appDir: './packages/MJAPI', // defaults to process.cwd()
570
- filterBaseClasses: ['BaseEngine'], // optional filter
571
- });
737
+ set FirstName(value: string) {
738
+ this.Set('FirstName', value);
739
+ }
572
740
 
573
- if (result.success) {
574
- console.log(`${result.packages.length} packages, ${result.classes.length} classes`);
741
+ // Own fields have no annotation
742
+ get EmployeeNumber(): string {
743
+ return this.Get('EmployeeNumber');
744
+ }
575
745
  }
576
746
  ```
577
747
 
578
- **CLI usage (via MJCLI):**
579
- ```bash
580
- mj codegen manifest --output ./src/generated/class-registrations-manifest.ts
581
- ```
582
-
583
- See the [MJCLI README](../MJCLI/README.md) for full CLI documentation.
584
-
585
- **Pre-built manifests for npm distribution:**
748
+ #### 5. EntityField Metadata Synchronization
749
+ **`manageEntityFields()` respects IS-A hierarchy** when syncing field metadata:
586
750
 
587
- When MJ packages are installed via npm, consumers only receive `dist/` (no `src/`), so the manifest generator can't scan them. To solve this, MJ ships **pre-built manifests** inside the bootstrap packages:
751
+ - Fields from parent entities are NOT duplicated in child entity metadata
752
+ - Only the child's own fields appear in `EntityField` for the child entity
753
+ - `RelatedEntityID` and field relationships are preserved across the hierarchy
754
+ - Prevents metadata pollution from inherited fields
588
755
 
589
- - `@memberjunction/server-bootstrap` 623 server-side classes from 54 packages
590
- - `@memberjunction/ng-bootstrap` — 383 Angular classes from 14 packages
756
+ ### Configuration in Database Metadata
591
757
 
592
- External consumers import the pre-built manifest for MJ classes, then generate a supplemental manifest for their own classes using `--exclude-packages @memberjunction`.
758
+ IS-A relationships are defined in the `Entity` table:
593
759
 
594
- For complete setup instructions, CLI reference, and troubleshooting, see the **[Class Manifest Guide](CLASS_MANIFEST_GUIDE.md)**.
760
+ ```sql
761
+ -- Person is the base entity
762
+ INSERT INTO Entity (ID, ParentEntity, Name)
763
+ VALUES (NEWID(), NULL, 'Person')
595
764
 
596
- **Example output:**
597
- - [MJAPI manifest (server-side)](EXAMPLE_MANIFEST_MJAPI.md) — 54 packages, 715 classes (AI providers, actions, encryption, scheduling, storage, etc.)
598
- - [MJExplorer manifest (client-side)](EXAMPLE_MANIFEST_MJEXPLORER.md) — 17 packages, 721 classes (Angular components, dashboards, forms, etc.)
765
+ -- Employee IS-A Person
766
+ INSERT INTO Entity (ID, ParentEntity, Name)
767
+ VALUES (NEWID(), 'Person', 'Employee')
768
+ ```
599
769
 
600
- ## API Reference
770
+ CodeGen detects the `ParentEntity` relationship and applies the specialized generation logic automatically.
601
771
 
602
- ### Core Functions
772
+ ### Use Cases for IS-A Relationships
603
773
 
604
- ```typescript
605
- // Generate everything at once
606
- await runCodeGen();
774
+ Common scenarios where IS-A relationships improve your schema:
607
775
 
608
- // Generate specific components
609
- await generateEntitySubClasses(options);
610
- await generateAngularEntityCode(options);
611
- await generateSQLScripts(options);
612
- await generateGraphQLServerCode(options);
613
- ```
776
+ - **Person → Employee, Customer, Vendor** - Shared contact information with role-specific fields
777
+ - **Document → Invoice, PurchaseOrder, Contract** - Common document metadata with type-specific data
778
+ - **Product → PhysicalProduct, DigitalProduct** - Shared catalog info with delivery-specific fields
779
+ - **Communication → Email, SMS, PhoneCall** - Common tracking with channel-specific metadata
614
780
 
615
- ### Entity Subclass Generation
781
+ ### Best Practices
616
782
 
617
- ```typescript
618
- import { generateEntitySubClasses } from '@memberjunction/codegen-lib';
619
-
620
- const result = await generateEntitySubClasses({
621
- outputDirectory: './generated/entities',
622
- generateLoader: true,
623
- generateCustomEntityClasses: true,
624
- aiEnhanced: true, // Enable AI features
625
- incrementalMode: true, // Only update changed entities
626
- validateGenerated: true // Compile-check generated code
627
- });
628
- ```
783
+ 1. **Keep hierarchies shallow** - One or two levels is ideal (Person → Employee, not Person → Worker → Employee)
784
+ 2. **Parent entities should be meaningful** - Don't create artificial base classes just for inheritance
785
+ 3. **Child fields should be truly specific** - If a field applies to all children, put it in the parent
786
+ 4. **ID management is manual** - When creating child records, explicitly use the parent's ID
629
787
 
630
- ### Action Subclass Generation
788
+ ## Virtual Entity Support
631
789
 
632
- ```typescript
633
- import { generateActionSubClasses } from '@memberjunction/codegen-lib';
790
+ MemberJunction supports **virtual entities** - entities backed by database views instead of tables. Virtual entities enable read-only access to complex queries, external data sources, or denormalized views while maintaining the full MemberJunction metadata and API experience.
634
791
 
635
- const result = await generateActionSubClasses({
636
- outputDirectory: './generated/actions',
637
- generateLoader: true
638
- });
639
- ```
792
+ For comprehensive conceptual documentation, see the **[Virtual Entities Guide](../../MJCore/docs/virtual-entities.md)** in MJCore.
640
793
 
641
- ### GraphQL Server Generation
794
+ ### Configuration-Driven Virtual Entity Creation
642
795
 
643
- ```typescript
644
- import { generateGraphQLServerCode } from '@memberjunction/codegen-lib';
796
+ Virtual entities are defined in `database-metadata-config.json` under the `VirtualEntities` array:
645
797
 
646
- await generateGraphQLServerCode({
647
- outputDirectory: './generated/graphql',
648
- entities: entityMetadata
649
- });
798
+ ```json
799
+ {
800
+ "VirtualEntities": [
801
+ {
802
+ "ViewName": "vwSalesSummary",
803
+ "EntityName": "Sales Summary",
804
+ "SchemaName": "__mj",
805
+ "Description": "Aggregated sales data by region and period",
806
+ "PrimaryKey": ["SummaryID"],
807
+ "ForeignKeys": [
808
+ {
809
+ "FieldName": "RegionID",
810
+ "SchemaName": "__mj",
811
+ "RelatedTable": "Region",
812
+ "RelatedField": "ID",
813
+ "Description": "FK to Region table"
814
+ }
815
+ ]
816
+ }
817
+ ]
818
+ }
650
819
  ```
651
820
 
652
- ### SQL Code Generation
821
+ #### Key Configuration Properties
653
822
 
654
- ```typescript
655
- import { generateSQLScripts } from '@memberjunction/codegen-lib';
823
+ - **`ViewName`**: The SQL view name (must already exist in the database)
824
+ - **`EntityName`**: The MemberJunction entity name (appears in metadata, UI, APIs)
825
+ - **`SchemaName`**: Database schema (typically `__mj` for core entities)
826
+ - **`Description`**: Entity description for metadata and documentation
827
+ - **`PrimaryKey`**: Array of column names forming the primary key (supports composite keys)
828
+ - **`ForeignKeys`**: Optional array of foreign key relationships to other entities (if omitted, LLM decoration discovers them)
656
829
 
657
- await generateSQLScripts({
658
- outputDirectory: './generated/sql',
659
- includeStoredProcedures: true,
660
- includeViews: true
661
- });
662
- ```
830
+ ### CodeGen Pipeline for Virtual Entities
663
831
 
664
- ### Angular Component Generation
832
+ CodeGen processes virtual entities through several specialized steps:
665
833
 
666
- ```typescript
667
- import { generateAllAngularEntityCode } from '@memberjunction/codegen-lib';
834
+ #### 1. `processVirtualEntityConfig()` - Entity Creation
835
+ Reads the `VirtualEntities` configuration and calls `spCreateVirtualEntity` for each entry:
668
836
 
669
- await generateAllAngularEntityCode({
670
- outputDirectory: './generated/angular',
671
- entities: entityMetadata
672
- });
837
+ ```typescript
838
+ // CodeGen calls this stored procedure for each virtual entity
839
+ EXEC spCreateVirtualEntity
840
+ @Name = 'Sales Summary',
841
+ @SchemaName = '__mj',
842
+ @BaseView = 'vwSalesSummary',
843
+ @Description = 'Aggregated sales data...',
844
+ @PrimaryKeyColumnName = 'SummaryID'
673
845
  ```
674
846
 
675
- ## Performance Stats
676
-
677
- On a typical MemberJunction database with **150+ tables**:
847
+ This creates the `Entity` metadata record with `VirtualEntity = 1`.
678
848
 
679
- - **Entity Generation**: 2.3 seconds
680
- - **Angular Components**: 4.7 seconds
681
- - **SQL Procedures**: 1.8 seconds
682
- - **Total Stack Generation**: **<10 seconds**
849
+ #### 2. `manageVirtualEntities()` - Field Synchronization
850
+ Scans `sys.columns` on the virtual entity's view and creates `EntityField` metadata for each column:
683
851
 
684
- For **295 entity classes** and **thousands of generated files**.
852
+ - Automatically detects data types, nullability, and max lengths
853
+ - Creates `EntityField` records for all view columns
854
+ - Updates existing fields if column definitions change
855
+ - Marks fields as `IsVirtual = 1` in metadata
685
856
 
686
- ## Integration with MemberJunction Ecosystem
687
-
688
- Works seamlessly with:
857
+ ```typescript
858
+ // CodeGen inspects the view schema
859
+ SELECT
860
+ c.name,
861
+ t.name AS TypeName,
862
+ c.max_length,
863
+ c.is_nullable
864
+ FROM
865
+ sys.columns c
866
+ INNER JOIN
867
+ sys.types t ON c.user_type_id = t.user_type_id
868
+ WHERE
869
+ object_id = OBJECT_ID('__mj.vwSalesSummary')
870
+ ```
689
871
 
690
- - `@memberjunction/core` - Entity framework
691
- - `@memberjunction/ai` - AI-powered features
692
- - `@memberjunction/angular-explorer` - UI framework
693
- - `@memberjunction/graphql-dataprovider` - API layer
694
- - `@memberjunction/sqlserver-dataprovider` - Data access
872
+ #### 3. `applySoftPKFKConfig()` - Explicit Relationship Overrides
873
+ Applies the `primaryKeyColumnName` and `foreignKeyDefinitions` from the config:
695
874
 
696
- ## Advanced Features
875
+ ```typescript
876
+ // Sets the primary key field
877
+ UPDATE EntityField
878
+ SET IsPrimaryKey = 1
879
+ WHERE EntityID = @VirtualEntityID
880
+ AND Name = 'SummaryID'
881
+
882
+ // Creates foreign key relationships
883
+ INSERT INTO EntityRelationship (...)
884
+ SELECT ... FROM foreignKeyDefinitions
885
+ ```
697
886
 
698
- ### Custom Templates
887
+ **Why explicit FK definitions?** Views don't have database-level foreign keys, so CodeGen can't detect relationships automatically. The config provides this metadata.
699
888
 
700
- You can provide custom templates for code generation:
889
+ #### 4. LLM-Assisted Field Decoration (Advanced Feature)
890
+ The **`decorateVirtualEntitiesWithLLM()`** pipeline step uses AI to enhance virtual entity field metadata:
701
891
 
702
892
  ```typescript
703
- import { setCustomTemplate } from '@memberjunction/codegen-lib';
704
-
705
- setCustomTemplate('entity', myCustomEntityTemplate);
893
+ import { AIPromptRunner } from '@memberjunction/ai-prompts';
894
+
895
+ // CodeGen calls a database-driven prompt to decorate fields
896
+ const promptParams = new AIPromptParams();
897
+ promptParams.prompt = 'Decorate Virtual Entity Fields';
898
+ promptParams.data = {
899
+ entityName: 'Sales Summary',
900
+ viewDefinition: viewSQL,
901
+ existingFields: fieldsFromMetadata
902
+ };
903
+
904
+ const runner = new AIPromptRunner();
905
+ const result = await runner.ExecutePrompt(promptParams);
706
906
  ```
707
907
 
708
- ### Schema Analysis
908
+ The LLM analyzes the view definition and provides:
909
+ - **Display names** - User-friendly field labels (e.g., `TotalRevenue` → "Total Revenue")
910
+ - **Descriptions** - Field-level documentation explaining what each column represents
911
+ - **Category assignments** - Semantic grouping for form layouts
912
+ - **DefaultInView flags** - Recommended visibility settings for grid displays
709
913
 
710
- ```typescript
711
- import { analyzeSchema } from '@memberjunction/codegen-lib';
914
+ This is controlled by the `VirtualEntityFieldDecoration` feature in the Advanced Generation Features configuration.
712
915
 
713
- const schemaInfo = await analyzeSchema(databaseConnection);
714
- // Work with schema information
715
- ```
916
+ ### Virtual Entity Metadata Structure
716
917
 
717
- ### Progress Tracking
918
+ After CodeGen processing, virtual entities have complete metadata:
718
919
 
719
- ```typescript
720
- import { onProgress } from '@memberjunction/codegen-lib';
920
+ ```sql
921
+ -- Entity record
922
+ SELECT * FROM Entity WHERE Name = 'Sales Summary'
923
+ -- VirtualEntity = 1, BaseView = 'vwSalesSummary'
721
924
 
722
- onProgress((status) => {
723
- console.log(`Progress: ${status.message} (${status.percentage}%)`);
724
- });
925
+ -- EntityField records (auto-detected from view)
926
+ SELECT * FROM EntityField WHERE EntityID = @SalesEntityID
927
+ -- Name, Type, Description, IsVirtual = 1
928
+
929
+ -- EntityRelationship records (from config)
930
+ SELECT * FROM EntityRelationship WHERE EntityID = @SalesEntityID
931
+ -- Foreign keys defined in foreignKeyDefinitions
725
932
  ```
726
933
 
727
- ### Force Regeneration with Smart Filtering
934
+ ### Generated Code for Virtual Entities
728
935
 
729
- Need to regenerate specific SQL objects without schema changes? Our force regeneration feature gives you **surgical precision** over what gets regenerated:
936
+ Virtual entities generate the same TypeScript, GraphQL, and Angular code as table-based entities:
730
937
 
731
- ```javascript
732
- // In mj.config.cjs
733
- forceRegeneration: {
734
- enabled: true,
735
- // Filter to specific entities using SQL WHERE clause
736
- entityWhereClause: "SchemaName = 'CRM' AND __mj_UpdatedAt >= '2025-06-24'",
737
-
738
- // Granular control over what gets regenerated
739
- baseViews: true, // Regenerate base views
740
- spCreate: false, // Skip create procedures
741
- spUpdate: true, // Regenerate update procedures
742
- spDelete: false, // Skip delete procedures
743
- indexes: true, // Regenerate foreign key indexes
744
- fullTextSearch: false // Skip full-text search components
745
- }
746
- ```
938
+ **TypeScript Entity Class:**
939
+ ```typescript
940
+ export class SalesSummaryEntity extends BaseEntity {
941
+ get SummaryID(): string {
942
+ return this.Get('SummaryID');
943
+ }
747
944
 
748
- #### Common Scenarios:
945
+ get RegionID(): string {
946
+ return this.Get('RegionID');
947
+ }
749
948
 
750
- **Regenerate views for recently modified entities:**
751
- ```javascript
752
- forceRegeneration: {
753
- enabled: true,
754
- entityWhereClause: "__mj_UpdatedAt >= '2025-06-24 22:00:00'",
755
- baseViews: true
756
- }
757
- ```
949
+ get TotalRevenue(): number {
950
+ return this.Get('TotalRevenue');
951
+ }
758
952
 
759
- **Regenerate all stored procedures for a specific schema:**
760
- ```javascript
761
- forceRegeneration: {
762
- enabled: true,
763
- entityWhereClause: "SchemaName = 'Sales'",
764
- allStoredProcedures: true
953
+ // Save/Delete methods throw errors (read-only entity)
765
954
  }
766
955
  ```
767
956
 
768
- **Regenerate specific SQL object for a single entity:**
769
- ```javascript
770
- forceRegeneration: {
771
- enabled: true,
772
- entityWhereClause: "Name = 'Customer'",
773
- spUpdate: true // Just regenerate the update procedure
957
+ **GraphQL Schema:**
958
+ ```graphql
959
+ type SalesSummary {
960
+ summaryID: ID!
961
+ regionID: ID!
962
+ region: Region # Auto-resolved from FK definition
963
+ totalRevenue: Float!
774
964
  }
775
- ```
776
965
 
777
- **Regenerate everything (no filtering):**
778
- ```javascript
779
- forceRegeneration: {
780
- enabled: true,
781
- // No entityWhereClause = regenerate for ALL entities
782
- allStoredProcedures: true,
783
- baseViews: true,
784
- indexes: true
966
+ type Query {
967
+ SalesSummaries(filter: String): [SalesSummary!]!
785
968
  }
786
969
  ```
787
970
 
788
- #### How It Works:
789
- 1. **Entity Filtering**: The `entityWhereClause` runs against the Entity metadata table to select which entities qualify
790
- 2. **Type Filtering**: Individual flags control which SQL object types get regenerated
791
- 3. **Smart Combination**: Only regenerates the intersection (selected entities AND selected types)
792
- 4. **Error Handling**: Invalid WHERE clauses stop execution with clear error messages
971
+ **Angular Form:**
972
+ ```html
973
+ <mj-form-field
974
+ [record]="record"
975
+ FieldName="TotalRevenue"
976
+ Type="textbox"
977
+ [ReadOnly]="true" <!-- Virtual entities are read-only -->
978
+ ></mj-form-field>
979
+ ```
793
980
 
794
- ## Error Handling
981
+ ### Virtual Entity Limitations
795
982
 
796
- The library provides comprehensive error handling:
983
+ Virtual entities are **read-only** by design:
797
984
 
798
- ```typescript
799
- try {
800
- await runCodeGen();
801
- } catch (error) {
802
- if (error.code === 'CONFIG_NOT_FOUND') {
803
- // Handle missing configuration
804
- } else if (error.code === 'DB_CONNECTION_FAILED') {
805
- // Handle database connection errors
806
- }
807
- }
808
- ```
985
+ - No `spCreate`, `spUpdate`, or `spDelete` procedures generated
986
+ - `AllowCreateAPI`, `AllowUpdateAPI`, `AllowDeleteAPI` set to `0` in metadata
987
+ - Entity class `Save()` and `Delete()` methods throw errors
988
+ - GraphQL mutations not generated for virtual entities
989
+ - Angular forms display in read-only mode
809
990
 
810
- ## Why This Changes Everything
991
+ ### Advanced Generation Features Configuration
811
992
 
812
- **Before MemberJunction CodeGen:**
813
- - Weeks of manual entity creation
814
- - Inconsistent validation logic
815
- - Type mismatches between layers
816
- - Manual Angular form creation
817
- - Brittle SQL procedures
818
- - Schema changes break everything
993
+ Virtual entity LLM decoration is controlled in the `advancedGeneration.features` array in `mj.config.cjs`:
819
994
 
820
- **After MemberJunction CodeGen:**
821
- - **10 seconds** to regenerate entire stack
822
- - **Perfect type safety** across all layers
823
- - **AI-powered** intelligent code generation
824
- - **Zero manual intervention**
825
- - **Production-ready** from day one
995
+ ```javascript
996
+ advancedGeneration: {
997
+ enableAdvancedGeneration: true,
998
+ features: [
999
+ {
1000
+ name: 'VirtualEntityFieldDecoration',
1001
+ enabled: true,
1002
+ // Optional: force re-decoration even if entities already have soft PK/FK annotations
1003
+ options: [{ name: 'forceRegenerate', value: true }],
1004
+ },
1005
+ ],
1006
+ },
1007
+ ```
826
1008
 
827
- ## Best Practices
1009
+ By default, `VirtualEntityFieldDecoration` is **enabled** and uses an idempotency check — entities that already have `IsSoftPrimaryKey` or `IsSoftForeignKey` annotations are skipped. Set the `forceRegenerate` option to `true` to override this check and re-run LLM decoration for all virtual entities (useful after prompt improvements or when you want to refresh metadata).
828
1010
 
829
- 1. **Configuration Management** - Use environment-specific configuration files
830
- 2. **Output Organization** - Keep generated code in separate directories
831
- 3. **Version Control** - Consider excluding generated files from version control
832
- 4. **Regular Updates** - Regenerate code when metadata changes
833
- 5. **Custom Extensions** - Extend generated classes rather than modifying them
1011
+ When active, CodeGen calls `decorateVirtualEntitiesWithLLM()` after field synchronization.
834
1012
 
835
- ## Contributing
1013
+ ### Use Cases for Virtual Entities
836
1014
 
837
- When contributing to this package:
1015
+ Virtual entities excel at:
838
1016
 
839
- 1. **Test with real schemas** - We generate production apps
840
- 2. **Maintain AI accuracy** - Constraint translation must be perfect
841
- 3. **Performance matters** - Large schemas must generate quickly
842
- 4. **Type safety is sacred** - Never compromise type correctness
1017
+ - **Reporting and Analytics** - Pre-aggregated views for dashboards (sales summaries, usage metrics)
1018
+ - **External Data Sources** - Linked server views, API-backed views, federated queries
1019
+ - **Denormalized Views** - Flattened data for grid displays without JOIN overhead
1020
+ - **Legacy System Integration** - Expose legacy tables through normalized MJ entity layer
1021
+ - **Calculated Fields** - Complex computed columns not suitable for table storage
1022
+ - **Security Views** - Row-level security via filtered views with full MJ API access
843
1023
 
844
- ## License
1024
+ ### Best Practices
845
1025
 
846
- This package is part of the MemberJunction ecosystem and follows the same licensing terms.
1026
+ 1. **View must exist first** - Create the SQL view before running CodeGen with virtual entity config
1027
+ 2. **Primary key is required** - Views must have a unique identifier column
1028
+ 3. **Use meaningful names** - `entityName` appears throughout UI and APIs
1029
+ 4. **Document foreign keys** - Explicit FK definitions enable relationship navigation
1030
+ 5. **Enable LLM decoration** - Let AI generate field descriptions for better UX
1031
+ 6. **Keep views simple** - Complex views with subqueries may have performance issues
1032
+ 7. **Test read operations** - Verify grid displays and API queries perform acceptably
847
1033
 
848
- ---
1034
+ ## Contributing
849
1035
 
850
- **Ready to experience the future of application development?**
1036
+ See the [MemberJunction Contributing Guide](../../CONTRIBUTING.md) for development setup and guidelines.
851
1037
 
852
- ```bash
853
- npm install @memberjunction/codegen-lib
854
- ```
1038
+ When contributing to CodeGenLib:
855
1039
 
856
- Your database schema deserves better than manual code generation. Give it the AI-powered, production-ready, full-stack treatment it deserves.
1040
+ 1. All generator base classes use the class factory pattern -- always subclass and register rather than modifying base classes directly
1041
+ 2. Generated SQL must be compatible with SQL Server and produce valid Flyway migration output
1042
+ 3. Generated TypeScript must compile without errors and follow MJ naming conventions (PascalCase public members)
1043
+ 4. AI-powered features must enforce stability guarantees (existing categories and icons are never changed)