@diagramers/cli 1.0.17 → 1.0.19

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.
package/README.md CHANGED
@@ -1,195 +1,490 @@
1
1
  # @diagramers/cli
2
2
 
3
- Command-line tools for managing Diagramers projects with comprehensive API development features.
3
+ A powerful command-line interface for rapid API development with TypeScript, Node.js, and MongoDB. Streamline your development workflow with automated code generation, project scaffolding, and database management.
4
4
 
5
- ## 🚀 Installation
5
+ ## 🚀 Features
6
+
7
+ - **Project Initialization**: Create new API and admin projects from templates
8
+ - **Code Generation**: Generate complete CRUD modules, database tables, and relationships
9
+ - **Project Management**: Update and extend existing projects with new features
10
+ - **Database Management**: Create tables, relationships, and manage database context
11
+ - **Template Processing**: Customize project templates for different environments
12
+
13
+ ## 📦 Installation
6
14
 
7
15
  ```bash
8
16
  npm install -g @diagramers/cli
9
17
  ```
10
18
 
11
- ## 📋 Commands
19
+ ## 🎯 Quick Start
20
+
21
+ ```bash
22
+ # Create a new API project
23
+ diagramers init api my-ecommerce-api
24
+
25
+ # Navigate to your project
26
+ cd my-ecommerce-api
27
+
28
+ # Generate a complete product module
29
+ diagramers api generate:module product
30
+
31
+ # Generate database tables
32
+ diagramers api generate:table category
33
+ diagramers api generate:table brand
34
+
35
+ # Create relationships between tables
36
+ diagramers api generate:relation product category
37
+ diagramers api generate:relation product brand
38
+ ```
39
+
40
+ ## 📋 Command Reference
12
41
 
13
- ### Initialize Projects
42
+ ### Project Initialization
14
43
 
15
44
  ```bash
16
- # Create new API project
17
- diagramers init api my-new-api
45
+ # Create new API project with TypeScript, MongoDB, and Express
46
+ diagramers init api <project-name>
18
47
 
19
- # Create new admin project
20
- diagramers init admin my-admin-dashboard
48
+ # Create new admin dashboard project
49
+ diagramers init admin <project-name>
21
50
  ```
22
51
 
23
- ### Update Projects
52
+ **What's included:**
53
+ - TypeScript configuration
54
+ - Express.js server setup
55
+ - MongoDB with Mongoose
56
+ - Firebase Functions integration
57
+ - Socket.io for real-time features
58
+ - Authentication system
59
+ - Email and SMS services
60
+ - Audit logging
61
+ - Environment configurations
62
+
63
+ ### Project Updates
24
64
 
25
65
  ```bash
26
- # Update existing project with latest features
66
+ # Update with latest features
27
67
  diagramers update
28
68
 
29
- # Update with backup
69
+ # Update with backup (recommended)
30
70
  diagramers update --backup
31
71
 
32
- # Force update (overwrite conflicts)
72
+ # Force update (overwrites conflicts)
33
73
  diagramers update --force
34
74
  ```
35
75
 
36
- ### Extend Projects
76
+ ### Project Extensions
37
77
 
38
78
  ```bash
39
79
  # List available features
40
80
  diagramers extend --list
41
81
 
42
- # Add authentication feature
82
+ # Add authentication system
43
83
  diagramers extend --feature auth
44
84
 
45
- # Add email system
85
+ # Add email service
46
86
  diagramers extend --feature email
47
87
 
48
88
  # Add WebSocket support
49
89
  diagramers extend --feature socket
50
90
 
51
- # Add cron jobs
91
+ # Add cron job system
52
92
  diagramers extend --feature cron
53
93
 
54
94
  # Add audit logging
55
95
  diagramers extend --feature audit
56
96
  ```
57
97
 
58
- ### API-Specific Commands
98
+ ## 🔧 API Development Commands
59
99
 
60
- The CLI provides powerful commands specifically for API development:
100
+ ### Generate Complete Module
61
101
 
62
- #### Generate Full Module
63
- Creates a complete CRUD module with entity, schema, service, controller, and routes:
102
+ Creates a full CRUD module with all layers:
64
103
 
65
104
  ```bash
66
- # Generate a complete product module
105
+ diagramers api generate:module <name>
106
+ ```
107
+
108
+ **Example:**
109
+ ```bash
67
110
  diagramers api generate:module product
111
+ ```
68
112
 
69
- # Generate a user profile module
70
- diagramers api generate:module user_profile
113
+ **Generated Files:**
114
+ ```
115
+ src/
116
+ ├── entities/product.ts # TypeScript interface
117
+ ├── schemas/product.ts # Mongoose schema
118
+ ├── services/product-service.ts # Business logic
119
+ ├── controllers/product-controller.ts # Request handlers
120
+ └── routes/product-routes.ts # API endpoints
71
121
  ```
72
122
 
73
- **What it creates:**
74
- - 📝 Entity interface (`src/entities/product.ts`)
75
- - 📋 Mongoose schema (`src/schemas/product.ts`)
76
- - 🔧 Service layer (`src/services/product-service.ts`)
77
- - 🎮 Controller (`src/controllers/product-controller.ts`)
78
- - 🛣️ Routes (`src/routes/product-routes.ts`)
79
- - 🔄 Updates `dbcontext.ts` and `routes/index.ts`
123
+ **API Endpoints Created:**
124
+ - `GET /api/products` - List all products
125
+ - `GET /api/products/:id` - Get product by ID
126
+ - `POST /api/products` - Create new product
127
+ - `PUT /api/products/:id` - Update product
128
+ - `DELETE /api/products/:id` - Delete product
129
+
130
+ ### Generate Database Table
80
131
 
81
- #### Generate Database Table
82
- Creates only the database layer (entity and schema) without API endpoints:
132
+ Creates only the database layer (entity and schema):
133
+
134
+ ```bash
135
+ diagramers api generate:table <name>
136
+ ```
83
137
 
138
+ **Example:**
84
139
  ```bash
85
- # Generate a category table
86
140
  diagramers api generate:table category
141
+ diagramers api generate:table brand
142
+ diagramers api generate:table inventory
143
+ ```
87
144
 
88
- # Generate a settings table
89
- diagramers api generate:table app_settings
145
+ **Generated Files:**
146
+ ```
147
+ src/
148
+ ├── entities/category.ts # TypeScript interface
149
+ └── schemas/category.ts # Mongoose schema
90
150
  ```
91
151
 
92
- **What it creates:**
93
- - 📝 Entity interface (`src/entities/category.ts`)
94
- - 📋 Mongoose schema (`src/schemas/category.ts`)
95
- - 🔄 Updates `dbcontext.ts`
152
+ **Use Cases:**
153
+ - Lookup tables (categories, brands, statuses)
154
+ - Configuration tables (settings, preferences)
155
+ - Reference data (countries, currencies)
156
+ - Internal data models
96
157
 
97
- #### Process Template
98
- Customize your API project template with a new name:
158
+ ### Generate Table Relationships
159
+
160
+ Creates relationships between existing tables:
99
161
 
100
162
  ```bash
101
- # Process template for a new project
102
- diagramers api process:template my-custom-api
163
+ diagramers api generate:relation <table1> <table2> [type]
103
164
  ```
104
165
 
105
- **What it updates:**
106
- - 📦 Package.json name and description
107
- - 📄 README.md title and description
108
- - ⚙️ Configuration files (development, staging, production)
109
- - 🗄️ Database names in config files
110
- - 📄 Creates `.env.example` template
166
+ **Relationship Types:**
167
+ - `one-to-one` (default)
168
+ - `one-to-many`
169
+ - `many-to-many`
170
+
171
+ **Examples:**
172
+ ```bash
173
+ # One-to-many: Product belongs to Category
174
+ diagramers api generate:relation product category one-to-many
111
175
 
112
- ## 🎯 Use Cases
176
+ # Many-to-many: Product has many Tags
177
+ diagramers api generate:relation product tag many-to-many
113
178
 
114
- ### When to use `generate:module`:
115
- - Building full CRUD APIs
116
- - Creating complete feature modules
117
- - When you need both database and API endpoints
179
+ # One-to-one: User has one Profile
180
+ diagramers api generate:relation user profile one-to-one
181
+ ```
118
182
 
119
- ### When to use `generate:table`:
120
- - Adding database tables without API endpoints
121
- - Creating lookup/reference tables
122
- - Building data models for internal use
123
- - When you only need the database layer
183
+ **What it does:**
184
+ - Updates entity interfaces with relationship fields
185
+ - Updates schemas with references and indexes
186
+ - Adds population methods to services
187
+ - Updates database context
124
188
 
125
- ### When to use `process:template`:
126
- - Customizing a new API project
127
- - Setting up project-specific configurations
128
- - Preparing for deployment with custom names
189
+ ### Process Template
129
190
 
130
- ## 🔧 Development
191
+ Customize your project for different environments:
131
192
 
132
193
  ```bash
133
- # Clone the repository
134
- git clone https://github.com/diagramers/diagramers-cli.git
135
- cd diagramers-cli
194
+ diagramers api process:template <project-name>
195
+ ```
136
196
 
137
- # Install dependencies
138
- npm install
197
+ **Example:**
198
+ ```bash
199
+ diagramers api process:template ecommerce-production
200
+ ```
139
201
 
140
- # Build the CLI
141
- npm run build
202
+ **What it updates:**
203
+ - Package.json name and description
204
+ - README.md title and description
205
+ - Database names in configuration files
206
+ - Environment-specific settings
207
+ - Creates .env.example template
142
208
 
143
- # Run in development mode
144
- npm run dev
209
+ ## 🏗️ Project Structure
145
210
 
146
- # Link locally
147
- npm link
211
+ After using the CLI, your project will have this structure:
148
212
 
149
- # Test the CLI
150
- diagramers --help
213
+ ```
214
+ src/
215
+ ├── entities/ # TypeScript interfaces
216
+ │ ├── user.ts
217
+ │ ├── product.ts
218
+ │ └── category.ts
219
+ ├── schemas/ # Mongoose schemas
220
+ │ ├── user.ts
221
+ │ ├── product.ts
222
+ │ └── category.ts
223
+ ├── services/ # Business logic layer
224
+ │ ├── user-service.ts
225
+ │ ├── product-service.ts
226
+ │ └── category-service.ts
227
+ ├── controllers/ # Request handlers
228
+ │ ├── user-controller.ts
229
+ │ ├── product-controller.ts
230
+ │ └── category-controller.ts
231
+ ├── routes/ # API endpoints
232
+ │ ├── user-routes.ts
233
+ │ ├── product-routes.ts
234
+ │ ├── category-routes.ts
235
+ │ └── index.ts
236
+ ├── helpers/ # Utilities and helpers
237
+ │ ├── dbcontext.ts # Database context
238
+ │ ├── auth.ts
239
+ │ ├── mailer.ts
240
+ │ └── result.ts
241
+ └── config/ # Environment configurations
242
+ ├── development.ts
243
+ ├── staging.ts
244
+ └── production.ts
151
245
  ```
152
246
 
153
- ## 📚 Examples
247
+ ## 🎯 Real-World Examples
154
248
 
155
- ### Complete API Development Workflow
249
+ ### E-commerce API Development
156
250
 
157
251
  ```bash
158
- # 1. Initialize a new API project
159
- diagramers init api my-ecommerce-api
160
-
161
- # 2. Navigate to the project
162
- cd my-ecommerce-api
252
+ # 1. Initialize project
253
+ diagramers init api ecommerce-api
254
+ cd ecommerce-api
163
255
 
164
- # 3. Generate full modules for main entities
256
+ # 2. Generate main entities with full CRUD
165
257
  diagramers api generate:module product
166
258
  diagramers api generate:module user
167
259
  diagramers api generate:module order
168
260
 
169
- # 4. Generate tables for supporting data
261
+ # 3. Generate supporting tables
170
262
  diagramers api generate:table category
171
263
  diagramers api generate:table brand
264
+ diagramers api generate:table tag
172
265
  diagramers api generate:table shipping_method
266
+ diagramers api generate:table payment_method
173
267
 
174
- # 5. Process template for production
268
+ # 4. Create relationships
269
+ diagramers api generate:relation product category one-to-many
270
+ diagramers api generate:relation product brand one-to-many
271
+ diagramers api generate:relation product tag many-to-many
272
+ diagramers api generate:relation order user one-to-many
273
+ diagramers api generate:relation order shipping_method one-to-one
274
+
275
+ # 5. Customize for production
175
276
  diagramers api process:template ecommerce-production
176
277
  ```
177
278
 
178
- ## 🛠️ Project Structure
279
+ ### Blog API Development
280
+
281
+ ```bash
282
+ # 1. Initialize project
283
+ diagramers init api blog-api
284
+ cd blog-api
179
285
 
180
- After using the CLI, your API project will have:
286
+ # 2. Generate main entities
287
+ diagramers api generate:module post
288
+ diagramers api generate:module user
289
+ diagramers api generate:module comment
181
290
 
291
+ # 3. Generate supporting tables
292
+ diagramers api generate:table category
293
+ diagramers api generate:table tag
294
+
295
+ # 4. Create relationships
296
+ diagramers api generate:relation post user one-to-many
297
+ diagramers api generate:relation post category one-to-many
298
+ diagramers api generate:relation post tag many-to-many
299
+ diagramers api generate:relation comment post one-to-many
300
+ diagramers api generate:relation comment user one-to-many
182
301
  ```
183
- src/
184
- ├── entities/ # TypeScript interfaces
185
- ├── schemas/ # Mongoose schemas
186
- ├── services/ # Business logic
187
- ├── controllers/ # Request handlers
188
- ├── routes/ # API endpoints
189
- ├── helpers/ # Utilities
190
- └── config/ # Environment configs
302
+
303
+ ## 🔄 Database Relationships
304
+
305
+ ### One-to-Many Relationship
306
+
307
+ ```typescript
308
+ // Product belongs to Category
309
+ interface IProduct {
310
+ _id: ObjectId;
311
+ name: string;
312
+ categoryId: ObjectId; // Reference to category
313
+ category?: ICategory; // Populated field
314
+ }
315
+
316
+ interface ICategory {
317
+ _id: ObjectId;
318
+ name: string;
319
+ products?: IProduct[]; // Virtual field
320
+ }
321
+ ```
322
+
323
+ ### Many-to-Many Relationship
324
+
325
+ ```typescript
326
+ // Product has many Tags
327
+ interface IProduct {
328
+ _id: ObjectId;
329
+ name: string;
330
+ tagIds: ObjectId[]; // Array of references
331
+ tags?: ITag[]; // Populated field
332
+ }
333
+
334
+ interface ITag {
335
+ _id: ObjectId;
336
+ name: string;
337
+ productIds: ObjectId[]; // Array of references
338
+ products?: IProduct[]; // Virtual field
339
+ }
340
+ ```
341
+
342
+ ### One-to-One Relationship
343
+
344
+ ```typescript
345
+ // User has one Profile
346
+ interface IUser {
347
+ _id: ObjectId;
348
+ email: string;
349
+ profileId: ObjectId; // Reference to profile
350
+ profile?: IProfile; // Populated field
351
+ }
352
+
353
+ interface IProfile {
354
+ _id: ObjectId;
355
+ firstName: string;
356
+ lastName: string;
357
+ userId: ObjectId; // Reference back to user
358
+ user?: IUser; // Populated field
359
+ }
360
+ ```
361
+
362
+ ## 🛠️ Development Workflow
363
+
364
+ ### 1. Project Setup
365
+ ```bash
366
+ # Create new project
367
+ diagramers init api my-api
368
+ cd my-api
369
+
370
+ # Install dependencies
371
+ npm install
372
+
373
+ # Start development server
374
+ npm run serve
375
+ ```
376
+
377
+ ### 2. Database Design
378
+ ```bash
379
+ # Generate main entities
380
+ diagramers api generate:module user
381
+ diagramers api generate:module product
382
+
383
+ # Generate supporting tables
384
+ diagramers api generate:table category
385
+ diagramers api generate:table brand
386
+
387
+ # Create relationships
388
+ diagramers api generate:relation product category one-to-many
389
+ diagramers api generate:relation product brand one-to-many
390
+ ```
391
+
392
+ ### 3. API Development
393
+ ```bash
394
+ # Test your endpoints
395
+ curl http://localhost:4000/api/products
396
+ curl http://localhost:4000/api/categories
191
397
  ```
192
398
 
399
+ ### 4. Production Deployment
400
+ ```bash
401
+ # Process template for production
402
+ diagramers api process:template my-api-production
403
+
404
+ # Build and deploy
405
+ npm run build:prod
406
+ npm run deploy
407
+ ```
408
+
409
+ ## 🔧 Configuration
410
+
411
+ ### Environment Variables
412
+
413
+ The CLI creates a comprehensive `.env.example` file with:
414
+
415
+ ```env
416
+ # Server Configuration
417
+ PORT=4000
418
+ HOST=localhost
419
+
420
+ # Database Configuration
421
+ MONGODB_URI=mongodb://127.0.0.1:27017/my-api-development
422
+
423
+ # Firebase Configuration
424
+ FIREBASE_API_KEY=your-api-key
425
+ FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
426
+
427
+ # JWT Configuration
428
+ JWT_SECRET=your-jwt-secret-key
429
+
430
+ # Email Configuration
431
+ SMTP_HOST=smtp.gmail.com
432
+ SMTP_PORT=587
433
+ SMTP_USER=your-email@gmail.com
434
+ SMTP_PASS=your-app-password
435
+ ```
436
+
437
+ ### Database Configuration
438
+
439
+ The CLI automatically configures different database names for each environment:
440
+
441
+ - **Development**: `my-api-development`
442
+ - **Staging**: `my-api-staging`
443
+ - **Production**: `my-api-production`
444
+
445
+ ## 🚀 Best Practices
446
+
447
+ ### 1. Naming Conventions
448
+ - Use singular names for entities: `product`, `user`, `category`
449
+ - Use descriptive names: `user_profile`, `product_inventory`
450
+ - Follow camelCase for multi-word names
451
+
452
+ ### 2. Database Design
453
+ - Generate tables first, then relationships
454
+ - Use `generate:table` for lookup data
455
+ - Use `generate:module` for main business entities
456
+ - Always create relationships after both tables exist
457
+
458
+ ### 3. API Development
459
+ - Test endpoints after generation
460
+ - Customize generated code for your business logic
461
+ - Add validation and error handling
462
+ - Implement proper authentication and authorization
463
+
464
+ ### 4. Project Organization
465
+ - Keep related files together
466
+ - Use consistent naming patterns
467
+ - Document your API endpoints
468
+ - Version your API properly
469
+
470
+ ## 🤝 Contributing
471
+
472
+ 1. Fork the repository
473
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
474
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
475
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
476
+ 5. Open a Pull Request
477
+
193
478
  ## 📄 License
194
479
 
195
- MIT
480
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
481
+
482
+ ## 🆘 Support
483
+
484
+ - **Documentation**: [GitHub Wiki](https://github.com/diagramers/diagramers-cli/wiki)
485
+ - **Issues**: [GitHub Issues](https://github.com/diagramers/diagramers-cli/issues)
486
+ - **Discussions**: [GitHub Discussions](https://github.com/diagramers/diagramers-cli/discussions)
487
+
488
+ ---
489
+
490
+ **Built with ❤️ by the Diagramers Team**
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/commands/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,WAmD1C"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/commands/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,WAoE1C"}
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.apiCommand = apiCommand;
7
7
  const api_generator_1 = require("../services/api-generator");
8
8
  const table_generator_1 = require("../services/table-generator");
9
+ const relation_generator_1 = require("../services/relation-generator");
9
10
  const template_processor_1 = require("../services/template-processor");
10
11
  const chalk_1 = __importDefault(require("chalk"));
11
12
  function apiCommand(program) {
@@ -57,6 +58,23 @@ function apiCommand(program) {
57
58
  process.exit(1);
58
59
  }
59
60
  });
61
+ // Generate relation command
62
+ api
63
+ .command('generate:relation <table1> <table2> [type]')
64
+ .description('Generate a relationship between two existing tables')
65
+ .option('-t, --type <type>', 'Relationship type: one-to-one, one-to-many, many-to-many', 'one-to-one')
66
+ .action(async (table1, table2, options) => {
67
+ try {
68
+ const relationType = options.type || 'one-to-one';
69
+ console.log(chalk_1.default.blue(`🔗 Creating ${relationType} relationship between ${table1} and ${table2}...`));
70
+ await (0, relation_generator_1.generateRelation)(table1, table2, relationType);
71
+ console.log(chalk_1.default.green(`✅ Relationship created successfully!`));
72
+ }
73
+ catch (error) {
74
+ console.error(chalk_1.default.red(`❌ Error creating relationship: ${error.message}`));
75
+ process.exit(1);
76
+ }
77
+ });
60
78
  return api;
61
79
  }
62
80
  //# sourceMappingURL=api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/commands/api.ts"],"names":[],"mappings":";;;;;AAMA,gCAmDC;AAxDD,6DAA2D;AAC3D,iEAA4D;AAC5D,uEAAiE;AACjE,kDAA0B;AAE1B,SAAgB,UAAU,CAAC,OAAgB;IACzC,MAAM,GAAG,GAAG,OAAO;SAChB,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,+CAA+C,CAAC,CAAC;IAEhE,0BAA0B;IAC1B,GAAG;SACA,OAAO,CAAC,wBAAwB,CAAC;SACjC,WAAW,CAAC,4EAA4E,CAAC;SACzF,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;QAC7B,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC,CAAC;YACzD,MAAM,IAAA,8BAAc,EAAC,IAAI,CAAC,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,aAAa,IAAI,2BAA2B,CAAC,CAAC,CAAC;QACzE,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,8BAA8B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,2BAA2B;IAC3B,GAAG;SACA,OAAO,CAAC,yBAAyB,CAAC;SAClC,WAAW,CAAC,0CAA0C,CAAC;SACvD,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;QAC7B,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,uCAAuC,IAAI,EAAE,CAAC,CAAC,CAAC;YACvE,MAAM,IAAA,oCAAe,EAAC,IAAI,CAAC,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,wCAAwC,IAAI,IAAI,CAAC,CAAC,CAAC;QAC7E,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,gCAAgC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,yBAAyB;IACzB,GAAG;SACA,OAAO,CAAC,uBAAuB,CAAC;SAChC,WAAW,CAAC,2DAA2D,CAAC;SACxE,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;QAC7B,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC,CAAC;YACxD,MAAM,IAAA,+BAAa,EAAC,IAAI,CAAC,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,YAAY,IAAI,2BAA2B,CAAC,CAAC,CAAC;QACxE,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,6BAA6B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/commands/api.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -25,6 +25,7 @@ Examples:
25
25
  $ diagramers extend --feature auth
26
26
  $ diagramers api generate:module product
27
27
  $ diagramers api generate:table category
28
+ $ diagramers api generate:relation product category one-to-many
28
29
  $ diagramers api process:template my-api-project
29
30
  `);
30
31
  program.parse();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AACpC,0CAA8C;AAC9C,8CAAkD;AAClD,8CAAkD;AAClD,wCAA4C;AAG5C,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,2CAA2C,CAAC;KACxD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,eAAe;AACf,IAAA,kBAAW,EAAC,OAAO,CAAC,CAAC;AACrB,IAAA,sBAAa,EAAC,OAAO,CAAC,CAAC;AACvB,IAAA,sBAAa,EAAC,OAAO,CAAC,CAAC;AACvB,IAAA,gBAAU,EAAC,OAAO,CAAC,CAAC;AAEpB,gBAAgB;AAChB,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE;;;;;;;;;CAS5B,CAAC,CAAC;AAEH,OAAO,CAAC,KAAK,EAAE,CAAC;AAEhB,uCAAuC;AACvC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAClC,OAAO,CAAC,UAAU,EAAE,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AACpC,0CAA8C;AAC9C,8CAAkD;AAClD,8CAAkD;AAClD,wCAA4C;AAG5C,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,2CAA2C,CAAC;KACxD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,eAAe;AACf,IAAA,kBAAW,EAAC,OAAO,CAAC,CAAC;AACrB,IAAA,sBAAa,EAAC,OAAO,CAAC,CAAC;AACvB,IAAA,sBAAa,EAAC,OAAO,CAAC,CAAC;AACvB,IAAA,gBAAU,EAAC,OAAO,CAAC,CAAC;AAEpB,gBAAgB;AAChB,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE;;;;;;;;;;CAU5B,CAAC,CAAC;AAEH,OAAO,CAAC,KAAK,EAAE,CAAC;AAEhB,uCAAuC;AACvC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAClC,OAAO,CAAC,UAAU,EAAE,CAAC;AACvB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export type RelationType = 'one-to-one' | 'one-to-many' | 'many-to-many';
2
+ export declare function generateRelation(table1: string, table2: string, relationType?: RelationType): Promise<void>;
3
+ //# sourceMappingURL=relation-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation-generator.d.ts","sourceRoot":"","sources":["../../src/services/relation-generator.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,aAAa,GAAG,cAAc,CAAC;AAEzE,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAE,YAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAyC/H"}
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateRelation = generateRelation;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ async function generateRelation(table1, table2, relationType = 'one-to-one') {
40
+ // Validate table names
41
+ if (!table1 || !table2 || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(table1) || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(table2)) {
42
+ throw new Error('Invalid table names. Use only letters, numbers, hyphens, and underscores. Must start with a letter.');
43
+ }
44
+ const table1Capitalized = table1.charAt(0).toUpperCase() + table1.slice(1);
45
+ const table2Capitalized = table2.charAt(0).toUpperCase() + table2.slice(1);
46
+ const currentDir = process.cwd();
47
+ // Check for required API project structure
48
+ const requiredDirs = ['src/entities', 'src/schemas'];
49
+ const missingDirs = requiredDirs.filter(dir => !fs.existsSync(path.join(currentDir, dir)));
50
+ if (missingDirs.length > 0) {
51
+ throw new Error(`This command should be run from a diagramers API project. Missing directories: ${missingDirs.join(', ')}`);
52
+ }
53
+ // Check if both tables exist
54
+ const table1EntityPath = path.join(currentDir, 'src/entities', `${table1}.ts`);
55
+ const table2EntityPath = path.join(currentDir, 'src/entities', `${table2}.ts`);
56
+ const table1SchemaPath = path.join(currentDir, 'src/schemas', `${table1}.ts`);
57
+ const table2SchemaPath = path.join(currentDir, 'src/schemas', `${table2}.ts`);
58
+ if (!fs.existsSync(table1EntityPath) || !fs.existsSync(table2EntityPath)) {
59
+ throw new Error(`Both tables must exist before creating relationships. Missing: ${!fs.existsSync(table1EntityPath) ? table1 : ''} ${!fs.existsSync(table2EntityPath) ? table2 : ''}`);
60
+ }
61
+ console.log(`🔗 Creating ${relationType} relationship between ${table1} and ${table2}...`);
62
+ // Update entities
63
+ console.log('📝 Updating entities...');
64
+ updateEntity(table1EntityPath, table1Capitalized, table2Capitalized, relationType, 'forward');
65
+ updateEntity(table2EntityPath, table2Capitalized, table1Capitalized, relationType, 'reverse');
66
+ // Update schemas
67
+ console.log('📋 Updating schemas...');
68
+ updateSchema(table1SchemaPath, table1Capitalized, table2Capitalized, relationType, 'forward');
69
+ updateSchema(table2SchemaPath, table2Capitalized, table1Capitalized, relationType, 'reverse');
70
+ console.log('✅ Relationship created successfully!');
71
+ console.log(`🔗 ${table1} ↔ ${table2} (${relationType})`);
72
+ }
73
+ function updateEntity(entityPath, tableCapitalized, relatedTableCapitalized, relationType, direction) {
74
+ let entityContent = fs.readFileSync(entityPath, 'utf8');
75
+ // Add import for the related interface
76
+ const importStatement = `import { I${relatedTableCapitalized} } from './${direction === 'forward' ? relatedTableCapitalized.toLowerCase() : relatedTableCapitalized.toLowerCase()}';`;
77
+ if (!entityContent.includes(importStatement)) {
78
+ // Find the last import statement and add after it
79
+ const importRegex = /import.*from.*['"];?\s*$/gm;
80
+ const matches = [...entityContent.matchAll(importRegex)];
81
+ if (matches.length > 0) {
82
+ const lastImport = matches[matches.length - 1];
83
+ const insertIndex = lastImport.index + lastImport[0].length;
84
+ entityContent = entityContent.slice(0, insertIndex) + '\n' + importStatement + entityContent.slice(insertIndex);
85
+ }
86
+ }
87
+ // Add relationship fields based on type
88
+ const interfaceRegex = /export interface I\w+ extends mongoose\.Document \{([^}]*)\}/s;
89
+ const match = entityContent.match(interfaceRegex);
90
+ if (match) {
91
+ const interfaceContent = match[1];
92
+ const newFields = generateEntityFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
93
+ // Add new fields before the closing brace
94
+ const updatedInterfaceContent = interfaceContent.trim() + '\n ' + newFields;
95
+ entityContent = entityContent.replace(interfaceRegex, `export interface I${tableCapitalized} extends mongoose.Document {$1${updatedInterfaceContent}\n}`);
96
+ }
97
+ fs.writeFileSync(entityPath, entityContent);
98
+ }
99
+ function updateSchema(schemaPath, tableCapitalized, relatedTableCapitalized, relationType, direction) {
100
+ let schemaContent = fs.readFileSync(schemaPath, 'utf8');
101
+ // Add import for the related schema
102
+ const importStatement = `import { ${relatedTableCapitalized}Entity } from './${direction === 'forward' ? relatedTableCapitalized.toLowerCase() : relatedTableCapitalized.toLowerCase()}';`;
103
+ if (!schemaContent.includes(importStatement)) {
104
+ // Find the last import statement and add after it
105
+ const importRegex = /import.*from.*['"];?\s*$/gm;
106
+ const matches = [...schemaContent.matchAll(importRegex)];
107
+ if (matches.length > 0) {
108
+ const lastImport = matches[matches.length - 1];
109
+ const insertIndex = lastImport.index + lastImport[0].length;
110
+ schemaContent = schemaContent.slice(0, insertIndex) + '\n' + importStatement + schemaContent.slice(insertIndex);
111
+ }
112
+ }
113
+ // Add schema fields based on relationship type
114
+ const schemaRegex = /export const \w+Schema = new mongoose\.Schema\(([^)]*),/s;
115
+ const match = schemaContent.match(schemaRegex);
116
+ if (match) {
117
+ const schemaFields = match[1];
118
+ const newFields = generateSchemaFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
119
+ // Add new fields before the closing brace
120
+ const updatedSchemaFields = schemaFields.trim() + '\n ' + newFields;
121
+ schemaContent = schemaContent.replace(schemaRegex, `export const ${tableCapitalized.toLowerCase()}Schema = new mongoose.Schema(${updatedSchemaFields},`);
122
+ }
123
+ // Add virtual fields and population methods
124
+ const modelRegex = /export const \w+Entity = mongoose\.model<.*>\(.*\);?\s*$/;
125
+ const virtualFields = generateVirtualFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
126
+ if (virtualFields) {
127
+ schemaContent = schemaContent.replace(modelRegex, `${virtualFields}\n\n$&`);
128
+ }
129
+ fs.writeFileSync(schemaPath, schemaContent);
130
+ }
131
+ function generateEntityFields(tableCapitalized, relatedTableCapitalized, relationType, direction) {
132
+ switch (relationType) {
133
+ case 'one-to-one':
134
+ if (direction === 'forward') {
135
+ return `_${relatedTableCapitalized.toLowerCase()}Id: ObjectId,\n ${relatedTableCapitalized.toLowerCase()}?: I${relatedTableCapitalized}`;
136
+ }
137
+ else {
138
+ return `_${tableCapitalized.toLowerCase()}Id: ObjectId,\n ${tableCapitalized.toLowerCase()}?: I${tableCapitalized}`;
139
+ }
140
+ case 'one-to-many':
141
+ if (direction === 'forward') {
142
+ return `${relatedTableCapitalized.toLowerCase()}Id: ObjectId,\n ${relatedTableCapitalized.toLowerCase()}?: I${relatedTableCapitalized}`;
143
+ }
144
+ else {
145
+ return `${tableCapitalized.toLowerCase()}s?: I${tableCapitalized}[]`;
146
+ }
147
+ case 'many-to-many':
148
+ if (direction === 'forward') {
149
+ return `${relatedTableCapitalized.toLowerCase()}Ids: ObjectId[],\n ${relatedTableCapitalized.toLowerCase()}s?: I${relatedTableCapitalized}[]`;
150
+ }
151
+ else {
152
+ return `${tableCapitalized.toLowerCase()}Ids: ObjectId[],\n ${tableCapitalized.toLowerCase()}s?: I${tableCapitalized}[]`;
153
+ }
154
+ default:
155
+ return '';
156
+ }
157
+ }
158
+ function generateSchemaFields(tableCapitalized, relatedTableCapitalized, relationType, direction) {
159
+ switch (relationType) {
160
+ case 'one-to-one':
161
+ if (direction === 'forward') {
162
+ return `${relatedTableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}',\n required: false\n }`;
163
+ }
164
+ else {
165
+ return `${tableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${tableCapitalized.toLowerCase()}',\n required: false\n }`;
166
+ }
167
+ case 'one-to-many':
168
+ if (direction === 'forward') {
169
+ return `${relatedTableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}',\n required: false\n }`;
170
+ }
171
+ else {
172
+ return ''; // No field needed for reverse one-to-many
173
+ }
174
+ case 'many-to-many':
175
+ if (direction === 'forward') {
176
+ return `${relatedTableCapitalized.toLowerCase()}Ids: [{\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}'\n }]`;
177
+ }
178
+ else {
179
+ return `${tableCapitalized.toLowerCase()}Ids: [{\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${tableCapitalized.toLowerCase()}'\n }]`;
180
+ }
181
+ default:
182
+ return '';
183
+ }
184
+ }
185
+ function generateVirtualFields(tableCapitalized, relatedTableCapitalized, relationType, direction) {
186
+ switch (relationType) {
187
+ case 'one-to-one':
188
+ if (direction === 'forward') {
189
+ return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '${relatedTableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
190
+ }
191
+ else {
192
+ return `// Virtual populate for ${tableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '${tableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
193
+ }
194
+ case 'one-to-many':
195
+ if (direction === 'forward') {
196
+ return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '${relatedTableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
197
+ }
198
+ else {
199
+ return `// Virtual populate for ${tableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}s', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '_id',\n foreignField: '${tableCapitalized.toLowerCase()}Id'\n});`;
200
+ }
201
+ case 'many-to-many':
202
+ if (direction === 'forward') {
203
+ return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}s', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '${relatedTableCapitalized.toLowerCase()}Ids',\n foreignField: '_id'\n});`;
204
+ }
205
+ else {
206
+ return `// Virtual populate for ${tableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}s', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '${tableCapitalized.toLowerCase()}Ids',\n foreignField: '_id'\n});`;
207
+ }
208
+ default:
209
+ return '';
210
+ }
211
+ }
212
+ //# sourceMappingURL=relation-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation-generator.js","sourceRoot":"","sources":["../../src/services/relation-generator.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diagramers/cli",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "Diagramers CLI - Command-line tools for managing Diagramers projects",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -1,6 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import { generateModule } from '../services/api-generator';
3
3
  import { generateTable } from '../services/table-generator';
4
+ import { generateRelation } from '../services/relation-generator';
4
5
  import { processTemplate } from '../services/template-processor';
5
6
  import chalk from 'chalk';
6
7
 
@@ -54,5 +55,22 @@ export function apiCommand(program: Command) {
54
55
  }
55
56
  });
56
57
 
58
+ // Generate relation command
59
+ api
60
+ .command('generate:relation <table1> <table2> [type]')
61
+ .description('Generate a relationship between two existing tables')
62
+ .option('-t, --type <type>', 'Relationship type: one-to-one, one-to-many, many-to-many', 'one-to-one')
63
+ .action(async (table1: string, table2: string, options: any) => {
64
+ try {
65
+ const relationType = options.type || 'one-to-one';
66
+ console.log(chalk.blue(`🔗 Creating ${relationType} relationship between ${table1} and ${table2}...`));
67
+ await generateRelation(table1, table2, relationType);
68
+ console.log(chalk.green(`✅ Relationship created successfully!`));
69
+ } catch (error: any) {
70
+ console.error(chalk.red(`❌ Error creating relationship: ${error.message}`));
71
+ process.exit(1);
72
+ }
73
+ });
74
+
57
75
  return api;
58
76
  }
package/src/index.ts CHANGED
@@ -29,6 +29,7 @@ Examples:
29
29
  $ diagramers extend --feature auth
30
30
  $ diagramers api generate:module product
31
31
  $ diagramers api generate:table category
32
+ $ diagramers api generate:relation product category one-to-many
32
33
  $ diagramers api process:template my-api-project
33
34
  `);
34
35
 
@@ -0,0 +1,203 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+
4
+ export type RelationType = 'one-to-one' | 'one-to-many' | 'many-to-many';
5
+
6
+ export async function generateRelation(table1: string, table2: string, relationType: RelationType = 'one-to-one'): Promise<void> {
7
+ // Validate table names
8
+ if (!table1 || !table2 || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(table1) || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(table2)) {
9
+ throw new Error('Invalid table names. Use only letters, numbers, hyphens, and underscores. Must start with a letter.');
10
+ }
11
+
12
+ const table1Capitalized = table1.charAt(0).toUpperCase() + table1.slice(1);
13
+ const table2Capitalized = table2.charAt(0).toUpperCase() + table2.slice(1);
14
+ const currentDir = process.cwd();
15
+
16
+ // Check for required API project structure
17
+ const requiredDirs = ['src/entities', 'src/schemas'];
18
+ const missingDirs = requiredDirs.filter(dir => !fs.existsSync(path.join(currentDir, dir)));
19
+ if (missingDirs.length > 0) {
20
+ throw new Error(`This command should be run from a diagramers API project. Missing directories: ${missingDirs.join(', ')}`);
21
+ }
22
+
23
+ // Check if both tables exist
24
+ const table1EntityPath = path.join(currentDir, 'src/entities', `${table1}.ts`);
25
+ const table2EntityPath = path.join(currentDir, 'src/entities', `${table2}.ts`);
26
+ const table1SchemaPath = path.join(currentDir, 'src/schemas', `${table1}.ts`);
27
+ const table2SchemaPath = path.join(currentDir, 'src/schemas', `${table2}.ts`);
28
+
29
+ if (!fs.existsSync(table1EntityPath) || !fs.existsSync(table2EntityPath)) {
30
+ throw new Error(`Both tables must exist before creating relationships. Missing: ${!fs.existsSync(table1EntityPath) ? table1 : ''} ${!fs.existsSync(table2EntityPath) ? table2 : ''}`);
31
+ }
32
+
33
+ console.log(`🔗 Creating ${relationType} relationship between ${table1} and ${table2}...`);
34
+
35
+ // Update entities
36
+ console.log('📝 Updating entities...');
37
+ updateEntity(table1EntityPath, table1Capitalized, table2Capitalized, relationType, 'forward');
38
+ updateEntity(table2EntityPath, table2Capitalized, table1Capitalized, relationType, 'reverse');
39
+
40
+ // Update schemas
41
+ console.log('📋 Updating schemas...');
42
+ updateSchema(table1SchemaPath, table1Capitalized, table2Capitalized, relationType, 'forward');
43
+ updateSchema(table2SchemaPath, table2Capitalized, table1Capitalized, relationType, 'reverse');
44
+
45
+ console.log('✅ Relationship created successfully!');
46
+ console.log(`🔗 ${table1} ↔ ${table2} (${relationType})`);
47
+ }
48
+
49
+ function updateEntity(entityPath: string, tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): void {
50
+ let entityContent = fs.readFileSync(entityPath, 'utf8');
51
+
52
+ // Add import for the related interface
53
+ const importStatement = `import { I${relatedTableCapitalized} } from './${direction === 'forward' ? relatedTableCapitalized.toLowerCase() : relatedTableCapitalized.toLowerCase()}';`;
54
+ if (!entityContent.includes(importStatement)) {
55
+ // Find the last import statement and add after it
56
+ const importRegex = /import.*from.*['"];?\s*$/gm;
57
+ const matches = [...entityContent.matchAll(importRegex)];
58
+ if (matches.length > 0) {
59
+ const lastImport = matches[matches.length - 1];
60
+ const insertIndex = lastImport.index! + lastImport[0].length;
61
+ entityContent = entityContent.slice(0, insertIndex) + '\n' + importStatement + entityContent.slice(insertIndex);
62
+ }
63
+ }
64
+
65
+ // Add relationship fields based on type
66
+ const interfaceRegex = /export interface I\w+ extends mongoose\.Document \{([^}]*)\}/s;
67
+ const match = entityContent.match(interfaceRegex);
68
+
69
+ if (match) {
70
+ const interfaceContent = match[1];
71
+ const newFields = generateEntityFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
72
+
73
+ // Add new fields before the closing brace
74
+ const updatedInterfaceContent = interfaceContent.trim() + '\n ' + newFields;
75
+ entityContent = entityContent.replace(interfaceRegex, `export interface I${tableCapitalized} extends mongoose.Document {$1${updatedInterfaceContent}\n}`);
76
+ }
77
+
78
+ fs.writeFileSync(entityPath, entityContent);
79
+ }
80
+
81
+ function updateSchema(schemaPath: string, tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): void {
82
+ let schemaContent = fs.readFileSync(schemaPath, 'utf8');
83
+
84
+ // Add import for the related schema
85
+ const importStatement = `import { ${relatedTableCapitalized}Entity } from './${direction === 'forward' ? relatedTableCapitalized.toLowerCase() : relatedTableCapitalized.toLowerCase()}';`;
86
+ if (!schemaContent.includes(importStatement)) {
87
+ // Find the last import statement and add after it
88
+ const importRegex = /import.*from.*['"];?\s*$/gm;
89
+ const matches = [...schemaContent.matchAll(importRegex)];
90
+ if (matches.length > 0) {
91
+ const lastImport = matches[matches.length - 1];
92
+ const insertIndex = lastImport.index! + lastImport[0].length;
93
+ schemaContent = schemaContent.slice(0, insertIndex) + '\n' + importStatement + schemaContent.slice(insertIndex);
94
+ }
95
+ }
96
+
97
+ // Add schema fields based on relationship type
98
+ const schemaRegex = /export const \w+Schema = new mongoose\.Schema\(([^)]*),/s;
99
+ const match = schemaContent.match(schemaRegex);
100
+
101
+ if (match) {
102
+ const schemaFields = match[1];
103
+ const newFields = generateSchemaFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
104
+
105
+ // Add new fields before the closing brace
106
+ const updatedSchemaFields = schemaFields.trim() + '\n ' + newFields;
107
+ schemaContent = schemaContent.replace(schemaRegex, `export const ${tableCapitalized.toLowerCase()}Schema = new mongoose.Schema(${updatedSchemaFields},`);
108
+ }
109
+
110
+ // Add virtual fields and population methods
111
+ const modelRegex = /export const \w+Entity = mongoose\.model<.*>\(.*\);?\s*$/;
112
+ const virtualFields = generateVirtualFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
113
+
114
+ if (virtualFields) {
115
+ schemaContent = schemaContent.replace(modelRegex, `${virtualFields}\n\n$&`);
116
+ }
117
+
118
+ fs.writeFileSync(schemaPath, schemaContent);
119
+ }
120
+
121
+ function generateEntityFields(tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): string {
122
+ switch (relationType) {
123
+ case 'one-to-one':
124
+ if (direction === 'forward') {
125
+ return `_${relatedTableCapitalized.toLowerCase()}Id: ObjectId,\n ${relatedTableCapitalized.toLowerCase()}?: I${relatedTableCapitalized}`;
126
+ } else {
127
+ return `_${tableCapitalized.toLowerCase()}Id: ObjectId,\n ${tableCapitalized.toLowerCase()}?: I${tableCapitalized}`;
128
+ }
129
+
130
+ case 'one-to-many':
131
+ if (direction === 'forward') {
132
+ return `${relatedTableCapitalized.toLowerCase()}Id: ObjectId,\n ${relatedTableCapitalized.toLowerCase()}?: I${relatedTableCapitalized}`;
133
+ } else {
134
+ return `${tableCapitalized.toLowerCase()}s?: I${tableCapitalized}[]`;
135
+ }
136
+
137
+ case 'many-to-many':
138
+ if (direction === 'forward') {
139
+ return `${relatedTableCapitalized.toLowerCase()}Ids: ObjectId[],\n ${relatedTableCapitalized.toLowerCase()}s?: I${relatedTableCapitalized}[]`;
140
+ } else {
141
+ return `${tableCapitalized.toLowerCase()}Ids: ObjectId[],\n ${tableCapitalized.toLowerCase()}s?: I${tableCapitalized}[]`;
142
+ }
143
+
144
+ default:
145
+ return '';
146
+ }
147
+ }
148
+
149
+ function generateSchemaFields(tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): string {
150
+ switch (relationType) {
151
+ case 'one-to-one':
152
+ if (direction === 'forward') {
153
+ return `${relatedTableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}',\n required: false\n }`;
154
+ } else {
155
+ return `${tableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${tableCapitalized.toLowerCase()}',\n required: false\n }`;
156
+ }
157
+
158
+ case 'one-to-many':
159
+ if (direction === 'forward') {
160
+ return `${relatedTableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}',\n required: false\n }`;
161
+ } else {
162
+ return ''; // No field needed for reverse one-to-many
163
+ }
164
+
165
+ case 'many-to-many':
166
+ if (direction === 'forward') {
167
+ return `${relatedTableCapitalized.toLowerCase()}Ids: [{\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}'\n }]`;
168
+ } else {
169
+ return `${tableCapitalized.toLowerCase()}Ids: [{\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${tableCapitalized.toLowerCase()}'\n }]`;
170
+ }
171
+
172
+ default:
173
+ return '';
174
+ }
175
+ }
176
+
177
+ function generateVirtualFields(tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): string {
178
+ switch (relationType) {
179
+ case 'one-to-one':
180
+ if (direction === 'forward') {
181
+ return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '${relatedTableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
182
+ } else {
183
+ return `// Virtual populate for ${tableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '${tableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
184
+ }
185
+
186
+ case 'one-to-many':
187
+ if (direction === 'forward') {
188
+ return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '${relatedTableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
189
+ } else {
190
+ return `// Virtual populate for ${tableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}s', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '_id',\n foreignField: '${tableCapitalized.toLowerCase()}Id'\n});`;
191
+ }
192
+
193
+ case 'many-to-many':
194
+ if (direction === 'forward') {
195
+ return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}s', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '${relatedTableCapitalized.toLowerCase()}Ids',\n foreignField: '_id'\n});`;
196
+ } else {
197
+ return `// Virtual populate for ${tableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}s', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '${tableCapitalized.toLowerCase()}Ids',\n foreignField: '_id'\n});`;
198
+ }
199
+
200
+ default:
201
+ return '';
202
+ }
203
+ }