@flusys/nestjs-core 1.1.0-beta → 1.2.0-rc.1

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 (61) hide show
  1. package/README.md +136 -162
  2. package/cjs/config/env-config.service.js +14 -10
  3. package/cjs/docs/docs.config.js +99 -15
  4. package/cjs/docs/index.js +0 -1
  5. package/cjs/index.js +0 -1
  6. package/cjs/interfaces/base-entity.interface.js +5 -3
  7. package/cjs/interfaces/database.interface.js +1 -3
  8. package/cjs/migration/datasource.factory.js +1 -3
  9. package/cjs/migration/index.js +0 -12
  10. package/cjs/migration/migration.cli.js +1 -17
  11. package/cjs/migration/migration.runner.js +37 -65
  12. package/cjs/utils/datasource-config.builder.js +2 -14
  13. package/config/env-config.service.d.ts +3 -1
  14. package/docs/docs.config.d.ts +8 -0
  15. package/docs/index.d.ts +0 -1
  16. package/fesm/config/env-config.service.js +14 -10
  17. package/fesm/docs/docs.config.js +89 -15
  18. package/fesm/docs/index.js +0 -1
  19. package/fesm/index.js +0 -2
  20. package/fesm/interfaces/app-config.interfaces.js +1 -3
  21. package/fesm/interfaces/base-entity.interface.js +5 -5
  22. package/fesm/interfaces/database.interface.js +1 -5
  23. package/fesm/migration/cli.js +1 -20
  24. package/fesm/migration/datasource.factory.js +3 -20
  25. package/fesm/migration/index.js +0 -14
  26. package/fesm/migration/migration.cli.js +1 -17
  27. package/fesm/migration/migration.runner.js +43 -132
  28. package/fesm/utils/datasource-config.builder.js +2 -13
  29. package/index.d.ts +0 -1
  30. package/interfaces/app-config.interfaces.d.ts +1 -0
  31. package/interfaces/base-entity.interface.d.ts +2 -2
  32. package/interfaces/database.interface.d.ts +2 -1
  33. package/package.json +9 -9
  34. package/utils/datasource-config.builder.d.ts +0 -1
  35. package/cjs/docs/docs.setup.js +0 -14
  36. package/cjs/seeders/base-seeder.js +0 -95
  37. package/cjs/seeders/cli.js +0 -398
  38. package/cjs/seeders/data-generator.js +0 -247
  39. package/cjs/seeders/entity-reader.js +0 -168
  40. package/cjs/seeders/index.js +0 -77
  41. package/cjs/seeders/seed-config.js +0 -101
  42. package/cjs/seeders/seed-runner.js +0 -320
  43. package/cjs/seeders/template-generator.js +0 -297
  44. package/docs/docs.setup.d.ts +0 -3
  45. package/fesm/docs/docs.setup.js +0 -4
  46. package/fesm/seeders/base-seeder.js +0 -110
  47. package/fesm/seeders/cli.js +0 -366
  48. package/fesm/seeders/data-generator.js +0 -249
  49. package/fesm/seeders/entity-reader.js +0 -170
  50. package/fesm/seeders/index.js +0 -11
  51. package/fesm/seeders/seed-config.js +0 -85
  52. package/fesm/seeders/seed-runner.js +0 -323
  53. package/fesm/seeders/template-generator.js +0 -257
  54. package/seeders/base-seeder.d.ts +0 -14
  55. package/seeders/cli.d.ts +0 -3
  56. package/seeders/data-generator.d.ts +0 -15
  57. package/seeders/entity-reader.d.ts +0 -45
  58. package/seeders/index.d.ts +0 -7
  59. package/seeders/seed-config.d.ts +0 -11
  60. package/seeders/seed-runner.d.ts +0 -47
  61. package/seeders/template-generator.d.ts +0 -16
@@ -1,297 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "TemplateGenerator", {
6
- enumerable: true,
7
- get: function() {
8
- return TemplateGenerator;
9
- }
10
- });
11
- const _fs = /*#__PURE__*/ _interop_require_wildcard(require("fs"));
12
- const _path = /*#__PURE__*/ _interop_require_wildcard(require("path"));
13
- function _getRequireWildcardCache(nodeInterop) {
14
- if (typeof WeakMap !== "function") return null;
15
- var cacheBabelInterop = new WeakMap();
16
- var cacheNodeInterop = new WeakMap();
17
- return (_getRequireWildcardCache = function(nodeInterop) {
18
- return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
19
- })(nodeInterop);
20
- }
21
- function _interop_require_wildcard(obj, nodeInterop) {
22
- if (!nodeInterop && obj && obj.__esModule) {
23
- return obj;
24
- }
25
- if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
26
- return {
27
- default: obj
28
- };
29
- }
30
- var cache = _getRequireWildcardCache(nodeInterop);
31
- if (cache && cache.has(obj)) {
32
- return cache.get(obj);
33
- }
34
- var newObj = {
35
- __proto__: null
36
- };
37
- var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
38
- for(var key in obj){
39
- if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
40
- var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
41
- if (desc && (desc.get || desc.set)) {
42
- Object.defineProperty(newObj, key, desc);
43
- } else {
44
- newObj[key] = obj[key];
45
- }
46
- }
47
- }
48
- newObj.default = obj;
49
- if (cache) {
50
- cache.set(obj, newObj);
51
- }
52
- return newObj;
53
- }
54
- let TemplateGenerator = class TemplateGenerator {
55
- /**
56
- * Generate seeder file for an entity
57
- * @param entityInfo Entity metadata
58
- * @param outputDir Output directory for seeder files
59
- */ generateSeederFile(entityInfo, outputDir) {
60
- const fileName = this.getSeederFileName(entityInfo.name);
61
- const filePath = _path.join(outputDir, fileName);
62
- const content = this.generateSeederContent(entityInfo);
63
- // Ensure directory exists
64
- if (!_fs.existsSync(outputDir)) {
65
- _fs.mkdirSync(outputDir, {
66
- recursive: true
67
- });
68
- }
69
- // Write file
70
- _fs.writeFileSync(filePath, content, 'utf-8');
71
- return filePath;
72
- }
73
- /**
74
- * Generate index file that exports all seeders
75
- * @param entityInfos Array of entity metadata
76
- * @param outputDir Output directory
77
- */ generateIndexFile(entityInfos, outputDir) {
78
- const indexPath = _path.join(outputDir, 'index.ts');
79
- const exports1 = entityInfos.map((info)=>{
80
- const className = this.getSeederClassName(info.name);
81
- const fileName = this.getSeederFileName(info.name).replace('.ts', '');
82
- return `export { ${className} } from './${fileName}';`;
83
- }).join('\n');
84
- _fs.writeFileSync(indexPath, exports1, 'utf-8');
85
- return indexPath;
86
- }
87
- /**
88
- * Generate seeder file content
89
- */ generateSeederContent(entityInfo) {
90
- const className = this.getSeederClassName(entityInfo.name);
91
- const imports = this.generateImports(entityInfo);
92
- const classContent = this.generateClass(entityInfo, className);
93
- return `${imports}\n\n${classContent}\n`;
94
- }
95
- /**
96
- * Generate import statements
97
- */ generateImports(entityInfo) {
98
- const imports = [
99
- `import { DataSource } from 'typeorm';`,
100
- `import { faker } from '@faker-js/faker';`,
101
- `import { BaseSeeder } from '@flusys/nestjs-core/seeders/base-seeder';`
102
- ];
103
- // Add entity import (adjust path as needed)
104
- const entityImportPath = this.getEntityImportPath(entityInfo.name);
105
- imports.push(`import { ${entityInfo.name} } from '${entityImportPath}';`);
106
- // Add relation imports
107
- const relationEntities = new Set(entityInfo.relations.map((r)=>r.targetEntity));
108
- for (const relEntity of relationEntities){
109
- const relPath = this.getEntityImportPath(relEntity);
110
- imports.push(`import { ${relEntity} } from '${relPath}';`);
111
- }
112
- return imports.join('\n');
113
- }
114
- /**
115
- * Generate class content
116
- */ generateClass(entityInfo, className) {
117
- const generateMethod = this.generateGenerateMethod(entityInfo);
118
- const relationMethod = this.generateRelationMethod(entityInfo);
119
- return `
120
- /**
121
- * ${className}
122
- *
123
- * Seeder for ${entityInfo.name} entity.
124
- * Generates sample data for development/testing.
125
- */
126
- export class ${className} extends BaseSeeder<${entityInfo.name}> {
127
- constructor(dataSource: DataSource) {
128
- super(dataSource, ${entityInfo.name});
129
- }
130
-
131
- ${generateMethod}${relationMethod ? '\n\n' + relationMethod : ''}
132
- }`.trim();
133
- }
134
- /**
135
- * Generate the generate() method
136
- */ generateGenerateMethod(entityInfo) {
137
- const fields = this.generateFieldAssignments(entityInfo.columns);
138
- return ` /**
139
- * Generate sample ${entityInfo.name} data
140
- * @param count Number of records to generate
141
- * @returns Array of created entities
142
- */
143
- async generate(count: number = 10): Promise<${entityInfo.name}[]> {
144
- const entities: ${entityInfo.name}[] = [];
145
-
146
- for (let i = 0; i < count; i++) {
147
- const entity = this.repository.create({
148
- ${fields}
149
- });
150
-
151
- entities.push(entity);
152
- }
153
-
154
- return this.repository.save(entities);
155
- }`;
156
- }
157
- /**
158
- * Generate field assignments for entity creation
159
- */ generateFieldAssignments(columns) {
160
- const assignments = [];
161
- for (const column of columns){
162
- // Skip system fields
163
- if (this.isSystemField(column.propertyName)) {
164
- continue;
165
- }
166
- // Skip foreign keys (handled in relations method)
167
- if (column.propertyName.endsWith('Id')) {
168
- continue;
169
- }
170
- const fakerCall = this.getFakerCall(column);
171
- const comment = column.isNullable ? ' // Optional' : ' // Required';
172
- assignments.push(` ${column.propertyName}: ${fakerCall},${comment}`);
173
- }
174
- return assignments.join('\n');
175
- }
176
- /**
177
- * Generate relation handling method if entity has relations
178
- */ generateRelationMethod(entityInfo) {
179
- const manyToOneRelations = entityInfo.relations.filter((r)=>r.type === 'many-to-one');
180
- if (manyToOneRelations.length === 0) {
181
- return null;
182
- }
183
- const relationParams = manyToOneRelations.map((r)=>`${r.propertyName}s: ${r.targetEntity}[]`).join(', ');
184
- const relationAssignments = manyToOneRelations.map((r)=>{
185
- return ` const random${r.targetEntity} = faker.helpers.arrayElement(${r.propertyName}s);
186
- entity.${r.propertyName}Id = random${r.targetEntity}.id;`;
187
- }).join('\n');
188
- return ` /**
189
- * Generate with related entities
190
- * @param ${relationParams}
191
- * @param count Number of records to generate
192
- */
193
- async generateWithRelations(${relationParams}, count: number = 10): Promise<${entityInfo.name}[]> {
194
- const entities = await this.generate(count);
195
-
196
- for (const entity of entities) {
197
- ${relationAssignments}
198
- await this.repository.save(entity);
199
- }
200
-
201
- return entities;
202
- }`;
203
- }
204
- /**
205
- * Get faker call for column based on its properties
206
- */ getFakerCall(column) {
207
- const nameLower = column.propertyName.toLowerCase();
208
- // Name fields
209
- if (nameLower.includes('firstname')) return 'faker.person.firstName()';
210
- if (nameLower.includes('lastname')) return 'faker.person.lastName()';
211
- if (nameLower === 'name' || nameLower.includes('fullname')) {
212
- return 'faker.person.fullName()';
213
- }
214
- // Contact
215
- if (nameLower.includes('email')) return 'faker.internet.email().toLowerCase()';
216
- if (nameLower.includes('phone')) return 'faker.phone.number()';
217
- // Text
218
- if (nameLower.includes('description')) return 'faker.lorem.paragraph()';
219
- if (nameLower.includes('content')) return 'faker.lorem.paragraphs(2)';
220
- if (nameLower.includes('title')) return 'faker.lorem.sentence()';
221
- // URL/Web
222
- if (nameLower.includes('slug')) {
223
- return "faker.helpers.slugify(faker.lorem.words(3)).toLowerCase()";
224
- }
225
- if (nameLower.includes('url')) return 'faker.internet.url()';
226
- // Status
227
- if (nameLower.includes('isactive') || nameLower.includes('isenabled')) {
228
- return 'true';
229
- }
230
- // Type-based
231
- const type = column.type.toLowerCase();
232
- if (type.includes('bool')) return 'faker.datatype.boolean()';
233
- if (type.includes('int') || type.includes('serial')) {
234
- return 'faker.number.int({ min: 1, max: 1000 })';
235
- }
236
- if (type.includes('decimal') || type.includes('float') || type.includes('numeric')) {
237
- return 'faker.number.float({ min: 0, max: 1000, precision: 0.01 })';
238
- }
239
- if (type.includes('date') || type.includes('timestamp')) {
240
- return 'faker.date.recent({ days: 30 })';
241
- }
242
- if (type.includes('uuid')) return 'faker.string.uuid()';
243
- if (type.includes('json')) return '{ key: faker.lorem.word() }';
244
- // Default string
245
- const maxLength = typeof column.length === 'number' ? column.length : 255;
246
- if (maxLength <= 50) {
247
- return 'faker.lorem.word()';
248
- } else if (maxLength <= 255) {
249
- return 'faker.lorem.sentence()';
250
- } else {
251
- return 'faker.lorem.paragraph()';
252
- }
253
- }
254
- /**
255
- * Check if field is a system field (should be skipped)
256
- */ isSystemField(fieldName) {
257
- const systemFields = [
258
- 'id',
259
- 'createdAt',
260
- 'updatedAt',
261
- 'deletedAt',
262
- 'createdById',
263
- 'updatedById',
264
- 'deletedById'
265
- ];
266
- return systemFields.includes(fieldName);
267
- }
268
- /**
269
- * Get seeder class name from entity name
270
- */ getSeederClassName(entityName) {
271
- return `${entityName}Seeder`;
272
- }
273
- /**
274
- * Get seeder file name from entity name
275
- */ getSeederFileName(entityName) {
276
- const kebab = entityName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
277
- return `${kebab}.seeder.ts`;
278
- }
279
- /**
280
- * Get entity import path (adjust based on project structure)
281
- */ getEntityImportPath(entityName) {
282
- // Try to determine module from entity name
283
- // This is a simplified approach - adjust based on actual project structure
284
- const nameLower = entityName.toLowerCase();
285
- if (nameLower.includes('user') || nameLower.includes('company') || nameLower.includes('branch')) {
286
- return `@flusys/nestjs-auth/entities`;
287
- }
288
- if (nameLower.includes('role') || nameLower.includes('permission') || nameLower.includes('action') || nameLower.includes('menu')) {
289
- return `@flusys/nestjs-iam/entities`;
290
- }
291
- if (nameLower.includes('file') || nameLower.includes('folder') || nameLower.includes('storage')) {
292
- return `@flusys/nestjs-storage/entities`;
293
- }
294
- // Default fallback
295
- return `@flusys/nestjs-shared/entities`;
296
- }
297
- };
@@ -1,3 +0,0 @@
1
- import { INestApplication } from '@nestjs/common';
2
- import { IModuleSwaggerOptions } from './docs.config';
3
- export declare function setupSwaggerDocs(app: INestApplication, ...modules: IModuleSwaggerOptions[]): void;
@@ -1,4 +0,0 @@
1
- import { setupModuleSwaggerDocs } from './docs.config';
2
- export function setupSwaggerDocs(app, ...modules) {
3
- setupModuleSwaggerDocs(app, modules);
4
- }
@@ -1,110 +0,0 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
- /**
15
- * Base Seeder Class
16
- *
17
- * Abstract base class for all entity seeders.
18
- * Provides common methods for data generation, clearing, and counting.
19
- *
20
- * Usage:
21
- * ```typescript
22
- * export class UserSeeder extends BaseSeeder<User> {
23
- * constructor(dataSource: DataSource) {
24
- * super(dataSource, User);
25
- * }
26
- *
27
- * async generate(count: number): Promise<User[]> {
28
- * const users: User[] = [];
29
- * for (let i = 0; i < count; i++) {
30
- * users.push(this.repository.create({
31
- * name: faker.person.fullName(),
32
- * email: faker.internet.email(),
33
- * }));
34
- * }
35
- * return this.repository.save(users);
36
- * }
37
- * }
38
- * ```
39
- */ export class BaseSeeder {
40
- /**
41
- * Clear all records from the entity table
42
- * Respects soft delete if entity has deletedAt column
43
- * @param hard If true, perform hard delete (ignore soft delete)
44
- */ async clear(hard = false) {
45
- const hasSoftDelete = this.metadata.deleteDateColumn !== undefined;
46
- if (hard || !hasSoftDelete) {
47
- // Hard delete - remove all records
48
- await this.repository.clear();
49
- } else {
50
- // Soft delete - set deletedAt
51
- await this.repository.createQueryBuilder().softDelete().where('id IS NOT NULL').execute();
52
- }
53
- }
54
- /**
55
- * Get count of existing records
56
- * @param includeDeleted If true, count soft-deleted records
57
- * @returns Total record count
58
- */ async count(includeDeleted = false) {
59
- const hasSoftDelete = this.metadata.deleteDateColumn !== undefined;
60
- if (hasSoftDelete && !includeDeleted) {
61
- return this.repository.count({
62
- where: {
63
- deletedAt: null
64
- }
65
- });
66
- }
67
- return this.repository.count();
68
- }
69
- /**
70
- * Check if entity has any records
71
- * @returns True if no records exist
72
- */ async isEmpty() {
73
- const total = await this.count();
74
- return total === 0;
75
- }
76
- /**
77
- * Get entity name for logging
78
- */ getEntityName() {
79
- return this.metadata.tableName;
80
- }
81
- /**
82
- * Wrap operation in transaction
83
- * @param operation Function to execute in transaction
84
- */ async withTransaction(operation) {
85
- const queryRunner = this.dataSource.createQueryRunner();
86
- await queryRunner.connect();
87
- await queryRunner.startTransaction();
88
- try {
89
- const transactionalRepo = queryRunner.manager.getRepository(this.entity);
90
- const result = await operation(transactionalRepo);
91
- await queryRunner.commitTransaction();
92
- return result;
93
- } catch (error) {
94
- await queryRunner.rollbackTransaction();
95
- throw error;
96
- } finally{
97
- await queryRunner.release();
98
- }
99
- }
100
- constructor(dataSource, entity){
101
- _define_property(this, "dataSource", void 0);
102
- _define_property(this, "entity", void 0);
103
- _define_property(this, "repository", void 0);
104
- _define_property(this, "metadata", void 0);
105
- this.dataSource = dataSource;
106
- this.entity = entity;
107
- this.repository = dataSource.getRepository(entity);
108
- this.metadata = this.repository.metadata;
109
- }
110
- }