@flusys/nestjs-core 1.1.0-beta → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +505 -63
  2. package/cjs/config/env-config.service.js +1 -1
  3. package/cjs/docs/docs.config.js +77 -3
  4. package/cjs/docs/index.js +0 -1
  5. package/cjs/interfaces/base-entity.interface.js +5 -3
  6. package/cjs/interfaces/database.interface.js +1 -3
  7. package/cjs/migration/datasource.factory.js +1 -3
  8. package/cjs/migration/index.js +0 -12
  9. package/cjs/migration/migration.cli.js +1 -17
  10. package/cjs/migration/migration.runner.js +37 -65
  11. package/cjs/seeders/base-seeder.js +6 -25
  12. package/cjs/seeders/cli.js +65 -172
  13. package/cjs/seeders/data-generator.js +96 -142
  14. package/cjs/seeders/entity-reader.js +0 -17
  15. package/cjs/seeders/field-patterns.js +172 -0
  16. package/cjs/seeders/index.js +16 -8
  17. package/cjs/seeders/seed-config.js +9 -48
  18. package/cjs/seeders/seed-runner.js +8 -14
  19. package/cjs/utils/datasource-config.builder.js +2 -14
  20. package/docs/docs.config.d.ts +7 -0
  21. package/docs/index.d.ts +0 -1
  22. package/fesm/config/env-config.service.js +1 -1
  23. package/fesm/docs/docs.config.js +68 -0
  24. package/fesm/docs/index.js +0 -1
  25. package/fesm/interfaces/app-config.interfaces.js +1 -3
  26. package/fesm/interfaces/base-entity.interface.js +5 -5
  27. package/fesm/interfaces/database.interface.js +1 -5
  28. package/fesm/migration/cli.js +1 -20
  29. package/fesm/migration/datasource.factory.js +3 -20
  30. package/fesm/migration/index.js +0 -14
  31. package/fesm/migration/migration.cli.js +1 -17
  32. package/fesm/migration/migration.runner.js +43 -132
  33. package/fesm/seeders/base-seeder.js +7 -51
  34. package/fesm/seeders/cli.js +65 -182
  35. package/fesm/seeders/data-generator.js +96 -149
  36. package/fesm/seeders/entity-reader.js +0 -17
  37. package/fesm/seeders/field-patterns.js +143 -0
  38. package/fesm/seeders/index.js +3 -7
  39. package/fesm/seeders/seed-config.js +9 -59
  40. package/fesm/seeders/seed-runner.js +8 -14
  41. package/fesm/utils/datasource-config.builder.js +2 -13
  42. package/interfaces/base-entity.interface.d.ts +3 -0
  43. package/package.json +2 -2
  44. package/seeders/data-generator.d.ts +1 -1
  45. package/seeders/entity-reader.d.ts +0 -1
  46. package/seeders/field-patterns.d.ts +12 -0
  47. package/seeders/index.d.ts +3 -3
  48. package/seeders/seed-config.d.ts +1 -0
  49. package/seeders/seed-runner.d.ts +1 -0
  50. package/utils/datasource-config.builder.d.ts +0 -1
  51. package/cjs/docs/docs.setup.js +0 -14
  52. package/cjs/seeders/template-generator.js +0 -297
  53. package/docs/docs.setup.d.ts +0 -3
  54. package/fesm/docs/docs.setup.js +0 -4
  55. package/fesm/seeders/template-generator.js +0 -257
  56. package/seeders/template-generator.d.ts +0 -16
@@ -1,257 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- /**
4
- * Template Generator Service
5
- *
6
- * Generates seeder template files for entities.
7
- * Creates TypeScript class files with BaseSeeder extension.
8
- *
9
- * Usage:
10
- * ```typescript
11
- * const generator = new TemplateGenerator();
12
- * generator.generateSeederFile(entityInfo, outputPath);
13
- * ```
14
- */ export class TemplateGenerator {
15
- /**
16
- * Generate seeder file for an entity
17
- * @param entityInfo Entity metadata
18
- * @param outputDir Output directory for seeder files
19
- */ generateSeederFile(entityInfo, outputDir) {
20
- const fileName = this.getSeederFileName(entityInfo.name);
21
- const filePath = path.join(outputDir, fileName);
22
- const content = this.generateSeederContent(entityInfo);
23
- // Ensure directory exists
24
- if (!fs.existsSync(outputDir)) {
25
- fs.mkdirSync(outputDir, {
26
- recursive: true
27
- });
28
- }
29
- // Write file
30
- fs.writeFileSync(filePath, content, 'utf-8');
31
- return filePath;
32
- }
33
- /**
34
- * Generate index file that exports all seeders
35
- * @param entityInfos Array of entity metadata
36
- * @param outputDir Output directory
37
- */ generateIndexFile(entityInfos, outputDir) {
38
- const indexPath = path.join(outputDir, 'index.ts');
39
- const exports = entityInfos.map((info)=>{
40
- const className = this.getSeederClassName(info.name);
41
- const fileName = this.getSeederFileName(info.name).replace('.ts', '');
42
- return `export { ${className} } from './${fileName}';`;
43
- }).join('\n');
44
- fs.writeFileSync(indexPath, exports, 'utf-8');
45
- return indexPath;
46
- }
47
- /**
48
- * Generate seeder file content
49
- */ generateSeederContent(entityInfo) {
50
- const className = this.getSeederClassName(entityInfo.name);
51
- const imports = this.generateImports(entityInfo);
52
- const classContent = this.generateClass(entityInfo, className);
53
- return `${imports}\n\n${classContent}\n`;
54
- }
55
- /**
56
- * Generate import statements
57
- */ generateImports(entityInfo) {
58
- const imports = [
59
- `import { DataSource } from 'typeorm';`,
60
- `import { faker } from '@faker-js/faker';`,
61
- `import { BaseSeeder } from '@flusys/nestjs-core/seeders/base-seeder';`
62
- ];
63
- // Add entity import (adjust path as needed)
64
- const entityImportPath = this.getEntityImportPath(entityInfo.name);
65
- imports.push(`import { ${entityInfo.name} } from '${entityImportPath}';`);
66
- // Add relation imports
67
- const relationEntities = new Set(entityInfo.relations.map((r)=>r.targetEntity));
68
- for (const relEntity of relationEntities){
69
- const relPath = this.getEntityImportPath(relEntity);
70
- imports.push(`import { ${relEntity} } from '${relPath}';`);
71
- }
72
- return imports.join('\n');
73
- }
74
- /**
75
- * Generate class content
76
- */ generateClass(entityInfo, className) {
77
- const generateMethod = this.generateGenerateMethod(entityInfo);
78
- const relationMethod = this.generateRelationMethod(entityInfo);
79
- return `
80
- /**
81
- * ${className}
82
- *
83
- * Seeder for ${entityInfo.name} entity.
84
- * Generates sample data for development/testing.
85
- */
86
- export class ${className} extends BaseSeeder<${entityInfo.name}> {
87
- constructor(dataSource: DataSource) {
88
- super(dataSource, ${entityInfo.name});
89
- }
90
-
91
- ${generateMethod}${relationMethod ? '\n\n' + relationMethod : ''}
92
- }`.trim();
93
- }
94
- /**
95
- * Generate the generate() method
96
- */ generateGenerateMethod(entityInfo) {
97
- const fields = this.generateFieldAssignments(entityInfo.columns);
98
- return ` /**
99
- * Generate sample ${entityInfo.name} data
100
- * @param count Number of records to generate
101
- * @returns Array of created entities
102
- */
103
- async generate(count: number = 10): Promise<${entityInfo.name}[]> {
104
- const entities: ${entityInfo.name}[] = [];
105
-
106
- for (let i = 0; i < count; i++) {
107
- const entity = this.repository.create({
108
- ${fields}
109
- });
110
-
111
- entities.push(entity);
112
- }
113
-
114
- return this.repository.save(entities);
115
- }`;
116
- }
117
- /**
118
- * Generate field assignments for entity creation
119
- */ generateFieldAssignments(columns) {
120
- const assignments = [];
121
- for (const column of columns){
122
- // Skip system fields
123
- if (this.isSystemField(column.propertyName)) {
124
- continue;
125
- }
126
- // Skip foreign keys (handled in relations method)
127
- if (column.propertyName.endsWith('Id')) {
128
- continue;
129
- }
130
- const fakerCall = this.getFakerCall(column);
131
- const comment = column.isNullable ? ' // Optional' : ' // Required';
132
- assignments.push(` ${column.propertyName}: ${fakerCall},${comment}`);
133
- }
134
- return assignments.join('\n');
135
- }
136
- /**
137
- * Generate relation handling method if entity has relations
138
- */ generateRelationMethod(entityInfo) {
139
- const manyToOneRelations = entityInfo.relations.filter((r)=>r.type === 'many-to-one');
140
- if (manyToOneRelations.length === 0) {
141
- return null;
142
- }
143
- const relationParams = manyToOneRelations.map((r)=>`${r.propertyName}s: ${r.targetEntity}[]`).join(', ');
144
- const relationAssignments = manyToOneRelations.map((r)=>{
145
- return ` const random${r.targetEntity} = faker.helpers.arrayElement(${r.propertyName}s);
146
- entity.${r.propertyName}Id = random${r.targetEntity}.id;`;
147
- }).join('\n');
148
- return ` /**
149
- * Generate with related entities
150
- * @param ${relationParams}
151
- * @param count Number of records to generate
152
- */
153
- async generateWithRelations(${relationParams}, count: number = 10): Promise<${entityInfo.name}[]> {
154
- const entities = await this.generate(count);
155
-
156
- for (const entity of entities) {
157
- ${relationAssignments}
158
- await this.repository.save(entity);
159
- }
160
-
161
- return entities;
162
- }`;
163
- }
164
- /**
165
- * Get faker call for column based on its properties
166
- */ getFakerCall(column) {
167
- const nameLower = column.propertyName.toLowerCase();
168
- // Name fields
169
- if (nameLower.includes('firstname')) return 'faker.person.firstName()';
170
- if (nameLower.includes('lastname')) return 'faker.person.lastName()';
171
- if (nameLower === 'name' || nameLower.includes('fullname')) {
172
- return 'faker.person.fullName()';
173
- }
174
- // Contact
175
- if (nameLower.includes('email')) return 'faker.internet.email().toLowerCase()';
176
- if (nameLower.includes('phone')) return 'faker.phone.number()';
177
- // Text
178
- if (nameLower.includes('description')) return 'faker.lorem.paragraph()';
179
- if (nameLower.includes('content')) return 'faker.lorem.paragraphs(2)';
180
- if (nameLower.includes('title')) return 'faker.lorem.sentence()';
181
- // URL/Web
182
- if (nameLower.includes('slug')) {
183
- return "faker.helpers.slugify(faker.lorem.words(3)).toLowerCase()";
184
- }
185
- if (nameLower.includes('url')) return 'faker.internet.url()';
186
- // Status
187
- if (nameLower.includes('isactive') || nameLower.includes('isenabled')) {
188
- return 'true';
189
- }
190
- // Type-based
191
- const type = column.type.toLowerCase();
192
- if (type.includes('bool')) return 'faker.datatype.boolean()';
193
- if (type.includes('int') || type.includes('serial')) {
194
- return 'faker.number.int({ min: 1, max: 1000 })';
195
- }
196
- if (type.includes('decimal') || type.includes('float') || type.includes('numeric')) {
197
- return 'faker.number.float({ min: 0, max: 1000, precision: 0.01 })';
198
- }
199
- if (type.includes('date') || type.includes('timestamp')) {
200
- return 'faker.date.recent({ days: 30 })';
201
- }
202
- if (type.includes('uuid')) return 'faker.string.uuid()';
203
- if (type.includes('json')) return '{ key: faker.lorem.word() }';
204
- // Default string
205
- const maxLength = typeof column.length === 'number' ? column.length : 255;
206
- if (maxLength <= 50) {
207
- return 'faker.lorem.word()';
208
- } else if (maxLength <= 255) {
209
- return 'faker.lorem.sentence()';
210
- } else {
211
- return 'faker.lorem.paragraph()';
212
- }
213
- }
214
- /**
215
- * Check if field is a system field (should be skipped)
216
- */ isSystemField(fieldName) {
217
- const systemFields = [
218
- 'id',
219
- 'createdAt',
220
- 'updatedAt',
221
- 'deletedAt',
222
- 'createdById',
223
- 'updatedById',
224
- 'deletedById'
225
- ];
226
- return systemFields.includes(fieldName);
227
- }
228
- /**
229
- * Get seeder class name from entity name
230
- */ getSeederClassName(entityName) {
231
- return `${entityName}Seeder`;
232
- }
233
- /**
234
- * Get seeder file name from entity name
235
- */ getSeederFileName(entityName) {
236
- const kebab = entityName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
237
- return `${kebab}.seeder.ts`;
238
- }
239
- /**
240
- * Get entity import path (adjust based on project structure)
241
- */ getEntityImportPath(entityName) {
242
- // Try to determine module from entity name
243
- // This is a simplified approach - adjust based on actual project structure
244
- const nameLower = entityName.toLowerCase();
245
- if (nameLower.includes('user') || nameLower.includes('company') || nameLower.includes('branch')) {
246
- return `@flusys/nestjs-auth/entities`;
247
- }
248
- if (nameLower.includes('role') || nameLower.includes('permission') || nameLower.includes('action') || nameLower.includes('menu')) {
249
- return `@flusys/nestjs-iam/entities`;
250
- }
251
- if (nameLower.includes('file') || nameLower.includes('folder') || nameLower.includes('storage')) {
252
- return `@flusys/nestjs-storage/entities`;
253
- }
254
- // Default fallback
255
- return `@flusys/nestjs-shared/entities`;
256
- }
257
- }
@@ -1,16 +0,0 @@
1
- import { IEntityInfo } from './entity-reader';
2
- export declare class TemplateGenerator {
3
- generateSeederFile(entityInfo: IEntityInfo, outputDir: string): string;
4
- generateIndexFile(entityInfos: IEntityInfo[], outputDir: string): string;
5
- private generateSeederContent;
6
- private generateImports;
7
- private generateClass;
8
- private generateGenerateMethod;
9
- private generateFieldAssignments;
10
- private generateRelationMethod;
11
- private getFakerCall;
12
- private isSystemField;
13
- private getSeederClassName;
14
- private getSeederFileName;
15
- private getEntityImportPath;
16
- }