@flusys/nestjs-core 1.1.0-beta → 1.1.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 (58) 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 +110 -142
  14. package/cjs/seeders/entity-reader.js +0 -17
  15. package/cjs/seeders/field-patterns.js +176 -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 +110 -149
  36. package/fesm/seeders/entity-reader.js +0 -17
  37. package/fesm/seeders/field-patterns.js +147 -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/app-config.interfaces.d.ts +1 -0
  43. package/interfaces/base-entity.interface.d.ts +3 -0
  44. package/interfaces/database.interface.d.ts +1 -0
  45. package/package.json +2 -2
  46. package/seeders/data-generator.d.ts +1 -1
  47. package/seeders/entity-reader.d.ts +0 -1
  48. package/seeders/field-patterns.d.ts +12 -0
  49. package/seeders/index.d.ts +3 -3
  50. package/seeders/seed-config.d.ts +1 -0
  51. package/seeders/seed-runner.d.ts +1 -0
  52. package/utils/datasource-config.builder.d.ts +0 -1
  53. package/cjs/docs/docs.setup.js +0 -14
  54. package/cjs/seeders/template-generator.js +0 -297
  55. package/docs/docs.setup.d.ts +0 -3
  56. package/fesm/docs/docs.setup.js +0 -4
  57. package/fesm/seeders/template-generator.js +0 -257
  58. package/seeders/template-generator.d.ts +0 -16
@@ -1,37 +1,26 @@
1
1
  import { faker } from '@faker-js/faker';
2
+ import { detectFieldPattern, detectTypeCategory, getStringLengthCategory, getTokenLength } from './field-patterns';
2
3
  /**
3
4
  * Data Generator Service
4
5
  *
5
6
  * Generates realistic sample data based on column metadata.
6
7
  * Uses @faker-js/faker for data generation with intelligent field detection.
7
- *
8
- * Usage:
9
- * ```typescript
10
- * const generator = new DataGenerator();
11
- * const value = generator.generateValue(columnInfo);
12
- * const entity = generator.generateEntity(columns);
13
- * ```
14
8
  */ export class DataGenerator {
15
9
  /**
16
10
  * Generate value for a single column based on its metadata
17
11
  */ generateValue(column) {
18
- // Skip generated fields
19
- if (column.isGenerated) {
20
- return undefined;
21
- }
12
+ if (column.isGenerated) return undefined;
22
13
  // Handle nullable fields (50% chance of null)
23
- if (column.isNullable && faker.datatype.boolean()) {
24
- return null;
25
- }
14
+ if (column.isNullable && faker.datatype.boolean()) return null;
26
15
  // Check for enum
27
16
  if (column.enum && column.enum.length > 0) {
28
17
  return faker.helpers.arrayElement(column.enum);
29
18
  }
30
- // Generate based on column name pattern
31
- const nameLower = column.propertyName.toLowerCase();
32
- const value = this.generateByName(nameLower, column);
33
- if (value !== undefined) {
34
- return value;
19
+ // Generate based on field pattern
20
+ const pattern = detectFieldPattern(column);
21
+ if (pattern) {
22
+ const value = this.generateByPattern(pattern, column);
23
+ if (value !== undefined) return value;
35
24
  }
36
25
  // Generate based on column type
37
26
  return this.generateByType(column);
@@ -49,143 +38,122 @@ import { faker } from '@faker-js/faker';
49
38
  return entity;
50
39
  }
51
40
  /**
52
- * Generate value based on column name patterns
53
- */ generateByName(nameLower, column) {
54
- // Identity fields (skip, handled by database)
55
- if ([
56
- 'id',
57
- 'createdat',
58
- 'updatedat',
59
- 'deletedat'
60
- ].includes(nameLower)) {
61
- return undefined;
62
- }
63
- // Audit fields (skip, handled by interceptors)
64
- if ([
65
- 'createdbyid',
66
- 'updatedbyid',
67
- 'deletedbyid'
68
- ].includes(nameLower)) {
69
- return null;
70
- }
71
- // Name fields
72
- if (nameLower.includes('firstname')) return faker.person.firstName();
73
- if (nameLower.includes('lastname')) return faker.person.lastName();
74
- if (nameLower.includes('fullname') || nameLower === 'name') {
75
- return faker.person.fullName();
76
- }
77
- // Contact fields
78
- if (nameLower.includes('email')) return faker.internet.email().toLowerCase();
79
- if (nameLower.includes('phone')) return faker.phone.number();
80
- if (nameLower.includes('mobile')) return faker.phone.number();
81
- // Location fields
82
- if (nameLower.includes('address')) return faker.location.streetAddress();
83
- if (nameLower.includes('street')) return faker.location.street();
84
- if (nameLower.includes('city')) return faker.location.city();
85
- if (nameLower.includes('state')) return faker.location.state();
86
- if (nameLower.includes('country')) return faker.location.country();
87
- if (nameLower.includes('zipcode') || nameLower.includes('postalcode')) {
88
- return faker.location.zipCode();
89
- }
90
- // URL/Web fields
91
- if (nameLower.includes('url')) return faker.internet.url();
92
- if (nameLower.includes('website')) return faker.internet.url();
93
- if (nameLower.includes('domain')) return faker.internet.domainName();
94
- if (nameLower.includes('slug')) {
95
- return faker.helpers.slugify(faker.lorem.words(3)).toLowerCase();
96
- }
97
- // Text fields
98
- if (nameLower.includes('description')) return faker.lorem.paragraph();
99
- if (nameLower.includes('summary')) return faker.lorem.sentence();
100
- if (nameLower.includes('content')) return faker.lorem.paragraphs(3);
101
- if (nameLower.includes('title')) return faker.lorem.sentence();
102
- // User fields
103
- if (nameLower.includes('username')) return faker.internet.username();
104
- if (nameLower.includes('password')) return '$2b$12$dummy.hashed.password.value'; // Dummy bcrypt hash
105
- // Status/Active fields
106
- if (nameLower.includes('isactive')) return faker.datatype.boolean();
107
- if (nameLower.includes('isenabled')) return faker.datatype.boolean();
108
- if (nameLower.includes('ispublic')) return faker.datatype.boolean();
109
- if (nameLower.includes('isverified')) return faker.datatype.boolean();
110
- // Date fields
111
- if (nameLower.includes('birthdate') || nameLower.includes('dateofbirth')) {
112
- return faker.date.birthdate({
113
- min: 18,
114
- max: 80,
115
- mode: 'age'
116
- });
117
- }
118
- if (nameLower.includes('expiresat') || nameLower.includes('expirydate')) {
119
- return faker.date.future();
120
- }
121
- if (nameLower.includes('verifiedat')) {
122
- return faker.datatype.boolean() ? faker.date.recent() : null;
123
- }
124
- // Serial/Order fields
125
- if (nameLower.includes('serial') || nameLower.includes('order')) {
126
- return faker.number.int({
127
- min: 1,
128
- max: 100
129
- });
130
- }
131
- // Company/Organization
132
- if (nameLower.includes('company') && !nameLower.includes('id')) {
133
- return faker.company.name();
41
+ * Generate value based on detected field pattern
42
+ */ generateByPattern(pattern, column) {
43
+ switch(pattern){
44
+ case 'skip':
45
+ return undefined;
46
+ case 'null':
47
+ return null;
48
+ case 'boolean':
49
+ return faker.datatype.boolean();
50
+ case 'token':
51
+ return faker.string.alphanumeric(getTokenLength(column));
52
+ case 'firstName':
53
+ return faker.person.firstName();
54
+ case 'lastName':
55
+ return faker.person.lastName();
56
+ case 'fullName':
57
+ return faker.person.fullName();
58
+ case 'email':
59
+ return faker.internet.email().toLowerCase();
60
+ case 'phone':
61
+ return faker.phone.number();
62
+ case 'address':
63
+ return faker.location.streetAddress();
64
+ case 'street':
65
+ return faker.location.street();
66
+ case 'city':
67
+ return faker.location.city();
68
+ case 'state':
69
+ return faker.location.state();
70
+ case 'country':
71
+ return faker.location.country();
72
+ case 'zipCode':
73
+ return faker.location.zipCode();
74
+ case 'url':
75
+ return faker.internet.url();
76
+ case 'domain':
77
+ return faker.internet.domainName();
78
+ case 'slug':
79
+ return faker.helpers.slugify(faker.lorem.words(3)).toLowerCase();
80
+ case 'description':
81
+ return faker.lorem.paragraph();
82
+ case 'summary':
83
+ return faker.lorem.sentence();
84
+ case 'content':
85
+ return faker.lorem.paragraphs(3);
86
+ case 'title':
87
+ return faker.lorem.sentence();
88
+ case 'username':
89
+ return faker.internet.username();
90
+ case 'password':
91
+ return '$2b$12$dummy.hashed.password.value';
92
+ case 'birthdate':
93
+ return faker.date.birthdate({
94
+ min: 18,
95
+ max: 80,
96
+ mode: 'age'
97
+ });
98
+ case 'futureDate':
99
+ return faker.date.future();
100
+ case 'recentDateOrNull':
101
+ return faker.datatype.boolean() ? faker.date.recent() : null;
102
+ case 'serial':
103
+ return faker.number.int({
104
+ min: 1,
105
+ max: 100
106
+ });
107
+ case 'company':
108
+ return faker.company.name();
109
+ case 'emailProvider':
110
+ return faker.helpers.arrayElement([
111
+ 'smtp',
112
+ 'sendgrid',
113
+ 'mailgun',
114
+ 'ses',
115
+ 'postmark'
116
+ ]);
117
+ case 'formAccessType':
118
+ return faker.helpers.arrayElement([
119
+ 'public',
120
+ 'authenticated',
121
+ 'permission'
122
+ ]);
123
+ default:
124
+ return undefined;
134
125
  }
135
- return undefined;
136
126
  }
137
127
  /**
138
128
  * Generate value based on column type
139
129
  */ generateByType(column) {
140
- const type = column.type.toLowerCase();
141
- switch(type){
142
- // String types
143
- case 'varchar':
144
- case 'character varying':
145
- case 'text':
130
+ const category = detectTypeCategory(column.type);
131
+ switch(category){
146
132
  case 'string':
147
133
  return this.generateString(column);
148
- // Number types
149
- case 'int':
150
134
  case 'integer':
151
- case 'smallint':
152
- case 'bigint':
153
135
  return faker.number.int({
154
136
  min: 1,
155
137
  max: 1000
156
138
  });
157
139
  case 'decimal':
158
- case 'numeric':
159
- case 'float':
160
- case 'double':
161
- case 'real':
162
140
  return faker.number.float({
163
141
  min: 0,
164
142
  max: 10000,
165
143
  fractionDigits: column.scale || 2
166
144
  });
167
- // Boolean
168
145
  case 'boolean':
169
- case 'bool':
170
146
  return faker.datatype.boolean();
171
- // Date/Time
172
147
  case 'date':
173
- return faker.date.recent({
174
- days: 30
175
- });
176
148
  case 'timestamp':
177
- case 'datetime':
178
149
  return faker.date.recent({
179
150
  days: 30
180
151
  });
181
152
  case 'time':
182
153
  return faker.date.recent().toTimeString().split(' ')[0];
183
- // UUID
184
154
  case 'uuid':
185
155
  return faker.string.uuid();
186
- // JSON
187
156
  case 'json':
188
- case 'jsonb':
189
157
  return {
190
158
  key1: faker.lorem.word(),
191
159
  key2: faker.number.int({
@@ -193,13 +161,11 @@ import { faker } from '@faker-js/faker';
193
161
  max: 100
194
162
  })
195
163
  };
196
- // Array
197
164
  case 'array':
198
165
  return [
199
166
  faker.lorem.word(),
200
167
  faker.lorem.word()
201
168
  ];
202
- // Default
203
169
  default:
204
170
  return faker.lorem.word();
205
171
  }
@@ -208,42 +174,37 @@ import { faker } from '@faker-js/faker';
208
174
  * Generate string value respecting length constraints
209
175
  */ generateString(column) {
210
176
  const maxLength = typeof column.length === 'number' ? column.length : 255;
211
- if (maxLength <= 50) {
212
- return faker.lorem.word().substring(0, maxLength);
213
- } else if (maxLength <= 255) {
214
- return faker.lorem.sentence().substring(0, maxLength);
215
- } else {
216
- return faker.lorem.paragraph().substring(0, maxLength);
177
+ const category = getStringLengthCategory(column.length);
178
+ switch(category){
179
+ case 'word':
180
+ return faker.lorem.word().substring(0, maxLength);
181
+ case 'sentence':
182
+ return faker.lorem.sentence().substring(0, maxLength);
183
+ case 'paragraph':
184
+ return faker.lorem.paragraph().substring(0, maxLength);
217
185
  }
218
186
  }
219
187
  /**
220
188
  * Generate related entity ID (for foreign keys)
221
- * @param relatedEntities Array of entities to pick from
222
189
  */ generateRelationId(relatedEntities) {
223
- if (relatedEntities.length === 0) {
224
- return null;
225
- }
190
+ if (relatedEntities.length === 0) return null;
226
191
  return faker.helpers.arrayElement(relatedEntities).id;
227
192
  }
228
193
  /**
229
194
  * Generate multiple related entity IDs (for many-to-many)
230
- * @param relatedEntities Array of entities to pick from
231
- * @param min Minimum number of relations
232
- * @param max Maximum number of relations
233
195
  */ generateRelationIds(relatedEntities, min = 1, max = 3) {
234
- if (relatedEntities.length === 0) {
235
- return [];
236
- }
196
+ if (relatedEntities.length === 0) return [];
237
197
  const count = faker.number.int({
238
198
  min,
239
199
  max: Math.min(max, relatedEntities.length)
240
200
  });
241
- const selected = faker.helpers.arrayElements(relatedEntities, count);
242
- return selected.map((e)=>e.id);
201
+ return faker.helpers.arrayElements(relatedEntities, count).map((e)=>e.id);
243
202
  }
244
203
  constructor(locale = 'en'){
245
- // Note: faker v8+ uses faker.locale instead of setLocale
246
- // For v8+: faker.locale = locale;
247
- // Locale is set globally, not needed for basic usage
204
+ faker.setDefaultRefDate(new Date());
205
+ if (locale !== 'en') {
206
+ // @ts-expect-error - faker locale property access
207
+ faker.locale = locale;
208
+ }
248
209
  }
249
210
  }
@@ -55,23 +55,6 @@ function _define_property(obj, key, value) {
55
55
  return this.topologicalSort(graph);
56
56
  }
57
57
  /**
58
- * Check if entity has required columns populated
59
- * Used to determine if entity is ready for seeding
60
- */ hasRequiredDependencies(entityName, dataSource) {
61
- const info = this.getEntityInfo(entityName);
62
- for (const relation of info.relations){
63
- if (!relation.isNullable && relation.type === 'many-to-one') {
64
- const targetRepo = dataSource.getRepository(relation.targetEntity);
65
- if (targetRepo) {
66
- // Check if target entity has data
67
- // Note: This is a simplified check, actual implementation may need async
68
- return true;
69
- }
70
- }
71
- }
72
- return true;
73
- }
74
- /**
75
58
  * Find entity metadata by name or table name
76
59
  */ findEntityMetadata(entityName) {
77
60
  return this.getAllEntities().find((e)=>e.name === entityName || e.tableName === entityName);
@@ -0,0 +1,147 @@
1
+ /** System fields that should be skipped during generation */ export const SYSTEM_FIELDS = [
2
+ 'id',
3
+ 'createdAt',
4
+ 'updatedAt',
5
+ 'deletedAt',
6
+ 'createdById',
7
+ 'updatedById',
8
+ 'deletedById'
9
+ ];
10
+ /** Audit fields that return null */ export const AUDIT_FIELDS = [
11
+ 'createdbyid',
12
+ 'updatedbyid',
13
+ 'deletedbyid'
14
+ ];
15
+ /** Identity fields that are skipped */ export const IDENTITY_FIELDS = [
16
+ 'id',
17
+ 'createdat',
18
+ 'updatedat',
19
+ 'deletedat'
20
+ ];
21
+ /** Boolean indicator keywords */ export const BOOLEAN_KEYWORDS = [
22
+ 'verified',
23
+ 'active',
24
+ 'enabled',
25
+ 'public',
26
+ 'readonly',
27
+ 'valid'
28
+ ];
29
+ /**
30
+ * Detect field pattern from column name
31
+ */ export function detectFieldPattern(column) {
32
+ const nameLower = column.propertyName.toLowerCase();
33
+ const typeLower = column.type.toLowerCase();
34
+ // Identity fields (skip)
35
+ if (IDENTITY_FIELDS.includes(nameLower)) return 'skip';
36
+ // Audit fields (null)
37
+ if (AUDIT_FIELDS.includes(nameLower)) return 'null';
38
+ // Boolean fields with specific keywords
39
+ if ((typeLower === 'boolean' || typeLower === 'bool') && BOOLEAN_KEYWORDS.some((k)=>nameLower.includes(k))) {
40
+ return 'boolean';
41
+ }
42
+ // Token fields
43
+ if (nameLower.includes('token')) return 'token';
44
+ // Name fields
45
+ if (nameLower.includes('firstname')) return 'firstName';
46
+ if (nameLower.includes('lastname')) return 'lastName';
47
+ if (nameLower.includes('fullname') || nameLower === 'name') return 'fullName';
48
+ // Contact fields
49
+ if (nameLower.includes('email') && typeLower !== 'boolean') return 'email';
50
+ if (nameLower.includes('phone') || nameLower.includes('mobile')) return 'phone';
51
+ // Location fields
52
+ if (nameLower.includes('address')) return 'address';
53
+ if (nameLower.includes('street')) return 'street';
54
+ if (nameLower.includes('city')) return 'city';
55
+ if (nameLower.includes('state')) return 'state';
56
+ if (nameLower.includes('country')) return 'country';
57
+ if (nameLower.includes('zipcode') || nameLower.includes('postalcode')) return 'zipCode';
58
+ // URL/Web fields
59
+ if (nameLower.includes('url') || nameLower.includes('website')) return 'url';
60
+ if (nameLower.includes('domain')) return 'domain';
61
+ if (nameLower.includes('slug')) return 'slug';
62
+ // Text fields
63
+ if (nameLower.includes('description')) return 'description';
64
+ if (nameLower.includes('summary')) return 'summary';
65
+ if (nameLower.includes('content')) return 'content';
66
+ if (nameLower.includes('title')) return 'title';
67
+ // User fields
68
+ if (nameLower.includes('username')) return 'username';
69
+ if (nameLower.includes('password')) return 'password';
70
+ // Boolean status fields (fallback)
71
+ if (nameLower.includes('isactive') || nameLower.includes('isenabled') || nameLower.includes('ispublic') || nameLower.includes('isverified')) {
72
+ return 'boolean';
73
+ }
74
+ // Date fields
75
+ if (nameLower.includes('birthdate') || nameLower.includes('dateofbirth')) return 'birthdate';
76
+ if (nameLower.includes('expiresat') || nameLower.includes('expirydate')) return 'futureDate';
77
+ if (nameLower.includes('verifiedat')) return 'recentDateOrNull';
78
+ // Serial/Order fields
79
+ if (nameLower.includes('serial') || nameLower.includes('order')) return 'serial';
80
+ // Company
81
+ if (nameLower.includes('company') && !nameLower.includes('id')) return 'company';
82
+ // Email provider type
83
+ if (nameLower === 'provider' || nameLower === 'emailprovider') return 'emailProvider';
84
+ // Form access type
85
+ if (nameLower === 'accesstype' || nameLower === 'access_type') return 'formAccessType';
86
+ return undefined;
87
+ }
88
+ /**
89
+ * Detect column type category
90
+ */ export function detectTypeCategory(type) {
91
+ const typeLower = type.toLowerCase();
92
+ if ([
93
+ 'varchar',
94
+ 'character varying',
95
+ 'text',
96
+ 'string'
97
+ ].includes(typeLower)) return 'string';
98
+ if ([
99
+ 'int',
100
+ 'integer',
101
+ 'smallint',
102
+ 'bigint'
103
+ ].includes(typeLower)) return 'integer';
104
+ if ([
105
+ 'decimal',
106
+ 'numeric',
107
+ 'float',
108
+ 'double',
109
+ 'real'
110
+ ].includes(typeLower)) return 'decimal';
111
+ if ([
112
+ 'boolean',
113
+ 'bool'
114
+ ].includes(typeLower)) return 'boolean';
115
+ if (typeLower === 'date') return 'date';
116
+ if ([
117
+ 'timestamp',
118
+ 'datetime'
119
+ ].includes(typeLower)) return 'timestamp';
120
+ if (typeLower === 'time') return 'time';
121
+ if (typeLower === 'uuid') return 'uuid';
122
+ if ([
123
+ 'json',
124
+ 'jsonb'
125
+ ].includes(typeLower)) return 'json';
126
+ if (typeLower === 'array') return 'array';
127
+ return 'unknown';
128
+ }
129
+ /**
130
+ * Get string length category for faker generation
131
+ */ export function getStringLengthCategory(length) {
132
+ const maxLength = typeof length === 'number' ? length : 255;
133
+ if (maxLength <= 50) return 'word';
134
+ if (maxLength <= 255) return 'sentence';
135
+ return 'paragraph';
136
+ }
137
+ /**
138
+ * Check if field is a system field
139
+ */ export function isSystemField(fieldName) {
140
+ return SYSTEM_FIELDS.includes(fieldName);
141
+ }
142
+ /**
143
+ * Get token max length respecting column constraints
144
+ */ export function getTokenLength(column) {
145
+ const maxLength = typeof column.length === 'number' ? column.length : 64;
146
+ return Math.min(maxLength, 32);
147
+ }
@@ -1,11 +1,7 @@
1
- /**
2
- * Seed System Exports
3
- *
4
- * Public API for seed data generation system.
5
- */ export { BaseSeeder } from './base-seeder';
1
+ export { BaseSeeder } from './base-seeder';
6
2
  export { EntityReader, IEntityInfo, IColumnInfo, IRelationInfo } from './entity-reader';
7
3
  export { DataGenerator } from './data-generator';
8
- export { TemplateGenerator } from './template-generator';
9
4
  export { SeedRunner, ISeedResult, ISeedOptions, ISeederLogger, defaultLogger } from './seed-runner';
10
- export { seedConfig, ISeedConfig, getEntityCount, shouldSkipEntity, getSeedingOrder } from './seed-config';
5
+ export { seedConfig, ISeedConfig, getEntityCount, shouldSkipEntity, getSeedingOrder, configureSeedConfig } from './seed-config';
6
+ export { SYSTEM_FIELDS, isSystemField, detectFieldPattern, detectTypeCategory } from './field-patterns';
11
7
  export { runSeedCli } from './cli';
@@ -1,81 +1,31 @@
1
- /**
2
- * Seed Configuration
3
- *
4
- * Configuration for seed data generation.
5
- * Customize record counts, seeding order, and behavior.
6
- */ /**
7
- * Default seed configuration
8
- */ export const seedConfig = {
9
- // Default record counts per entity
10
- counts: {
11
- // Auth entities
12
- Company: 10,
13
- CompanyBranch: 25,
14
- User: 50,
15
- // IAM entities
16
- Role: 8,
17
- Permission: 45,
18
- Action: 30,
19
- Menu: 20,
20
- UserIAMPermission: 50,
21
- UserCompanyPermission: 100,
22
- // Storage entities
23
- StorageConfig: 5,
24
- Folder: 20,
25
- FileManager: 100
26
- },
27
- // Entity seeding order (respects FK constraints)
28
- // Parent entities must come before child entities
29
- order: [
30
- // Auth - foundational
31
- 'Company',
32
- 'CompanyBranch',
33
- 'User',
34
- // IAM - permissions system
35
- 'Action',
36
- 'Role',
37
- 'Permission',
38
- 'Menu',
39
- 'UserIAMPermission',
40
- 'UserCompanyPermission',
41
- // Storage - file system
42
- 'StorageConfig',
43
- 'Folder',
44
- 'FileManager'
45
- ],
46
- // Skip these entities (system tables, migrations)
1
+ export const seedConfig = {
2
+ counts: {},
3
+ order: [],
47
4
  skipEntities: [
48
5
  'migrations',
49
6
  'typeorm_metadata',
50
7
  'Migration',
51
8
  'Typeorm_Metadata'
52
9
  ],
53
- // Faker locale (en, ar, es, fr, de, etc.)
54
10
  locale: 'en',
55
- // Respect soft delete when clearing data
56
11
  respectSoftDelete: true
57
12
  };
58
- /**
59
- * Get count for entity (with default fallback)
60
- */ export function getEntityCount(entityName, config = seedConfig) {
13
+ export function configureSeedConfig(config) {
14
+ Object.assign(seedConfig, config);
15
+ }
16
+ export function getEntityCount(entityName, config = seedConfig) {
61
17
  return config.counts[entityName] || 10;
62
18
  }
63
- /**
64
- * Check if entity should be skipped
65
- */ export function shouldSkipEntity(entityName, config = seedConfig) {
19
+ export function shouldSkipEntity(entityName, config = seedConfig) {
66
20
  return config.skipEntities.some((skip)=>skip.toLowerCase() === entityName.toLowerCase());
67
21
  }
68
- /**
69
- * Get seeding order for entities
70
- */ export function getSeedingOrder(availableEntities, config = seedConfig) {
22
+ export function getSeedingOrder(availableEntities, config = seedConfig) {
71
23
  const ordered = [];
72
- // Add entities in configured order
73
24
  for (const entityName of config.order){
74
25
  if (availableEntities.includes(entityName) && !shouldSkipEntity(entityName, config)) {
75
26
  ordered.push(entityName);
76
27
  }
77
28
  }
78
- // Add remaining entities not in order configuration
79
29
  for (const entityName of availableEntities){
80
30
  if (!ordered.includes(entityName) && !shouldSkipEntity(entityName, config)) {
81
31
  ordered.push(entityName);