@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
@@ -85,7 +85,7 @@ let EnvConfigService = class EnvConfigService {
85
85
  return this.getValue(key, throwOnMissing).toLowerCase() === 'true';
86
86
  }
87
87
  getPort() {
88
- return this.getNumber('PORT', false);
88
+ return this.getNumber('PORT', false) || 3000;
89
89
  }
90
90
  isProduction() {
91
91
  return this.getValue('MODE', false).toUpperCase() !== 'DEV';
@@ -2,10 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
- Object.defineProperty(exports, "setupModuleSwaggerDocs", {
6
- enumerable: true,
7
- get: function() {
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get setupModuleSwaggerDocs () {
8
13
  return setupModuleSwaggerDocs;
14
+ },
15
+ get setupSwaggerDocs () {
16
+ return setupSwaggerDocs;
9
17
  }
10
18
  });
11
19
  const _swagger = require("@nestjs/swagger");
@@ -94,6 +102,65 @@ const _swagger = require("@nestjs/swagger");
94
102
  paths: filteredPaths
95
103
  };
96
104
  }
105
+ /**
106
+ * Filter OpenAPI document to exclude examples from endpoint responses
107
+ */ function filterExamples(document, exclusions) {
108
+ if (!exclusions?.length || !document.paths) {
109
+ return document;
110
+ }
111
+ const filteredPaths = {};
112
+ for (const [path, pathItem] of Object.entries(document.paths)){
113
+ const filteredPathItem = {};
114
+ for (const [method, operation] of Object.entries(pathItem)){
115
+ // Skip non-operation properties
116
+ if (!operation || typeof operation !== 'object') {
117
+ filteredPathItem[method] = operation;
118
+ continue;
119
+ }
120
+ // Check if this endpoint matches any exclusion pattern
121
+ const matchingExclusions = exclusions.filter((exclusion)=>{
122
+ const pathMatches = pathMatchesPattern(path, exclusion.pathPattern);
123
+ const methodMatches = !exclusion.method || exclusion.method === method;
124
+ return pathMatches && methodMatches;
125
+ });
126
+ if (matchingExclusions.length > 0) {
127
+ // Collect all examples to exclude
128
+ const examplesToExclude = new Set();
129
+ matchingExclusions.forEach((exclusion)=>{
130
+ exclusion.examples.forEach((example)=>examplesToExclude.add(example));
131
+ });
132
+ // Deep clone the operation to avoid mutating original
133
+ const filteredOperation = JSON.parse(JSON.stringify(operation));
134
+ // Filter examples from responses
135
+ if (filteredOperation.responses) {
136
+ for (const [statusCode, response] of Object.entries(filteredOperation.responses)){
137
+ const responseObj = response;
138
+ if (responseObj?.content) {
139
+ const content = responseObj.content;
140
+ for (const [mediaType, mediaTypeObj] of Object.entries(content)){
141
+ const media = mediaTypeObj;
142
+ if (media?.examples && typeof media.examples === 'object') {
143
+ const examples = media.examples;
144
+ for (const exampleName of examplesToExclude){
145
+ delete examples[exampleName];
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ filteredPathItem[method] = filteredOperation;
153
+ } else {
154
+ filteredPathItem[method] = operation;
155
+ }
156
+ }
157
+ filteredPaths[path] = filteredPathItem;
158
+ }
159
+ return {
160
+ ...document,
161
+ paths: filteredPaths
162
+ };
163
+ }
97
164
  /**
98
165
  * Check if a path matches a pattern (supports wildcards)
99
166
  */ function pathMatchesPattern(path, pattern) {
@@ -187,6 +254,13 @@ function setupModuleSwaggerDocs(app, configs) {
187
254
  if (config.excludeQueryParameters?.length) {
188
255
  document = filterQueryParameters(document, config.excludeQueryParameters);
189
256
  }
257
+ // Filter out excluded examples
258
+ if (config.excludeExamples?.length) {
259
+ document = filterExamples(document, config.excludeExamples);
260
+ }
190
261
  _swagger.SwaggerModule.setup(config.path, app, document);
191
262
  });
192
263
  }
264
+ function setupSwaggerDocs(app, ...modules) {
265
+ setupModuleSwaggerDocs(app, modules);
266
+ }
package/cjs/docs/index.js CHANGED
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
5
  _export_star(require("./docs.config"), exports);
6
- _export_star(require("./docs.setup"), exports);
7
6
  function _export_star(from, to) {
8
7
  Object.keys(from).forEach(function(k) {
9
8
  if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
@@ -1,7 +1,9 @@
1
1
  /**
2
- * Base Entity Interface
3
- * All entities should implement this for consistency
4
- */ "use strict";
2
+ * Entity Contract Interfaces
3
+ *
4
+ * These interfaces define the standard fields that entities should have.
5
+ * Use them as contracts when implementing new entities.
6
+ */ /** Base entity with UUID, timestamps, and soft delete */ "use strict";
5
7
  Object.defineProperty(exports, "__esModule", {
6
8
  value: true
7
9
  });
@@ -1,6 +1,4 @@
1
- /**
2
- * Supported database types
3
- */ "use strict";
1
+ "use strict";
4
2
  Object.defineProperty(exports, "__esModule", {
5
3
  value: true
6
4
  });
@@ -22,10 +22,9 @@ const _datasourceconfigbuilder = require("../utils/datasource-config.builder");
22
22
  function createMigrationDataSource(options) {
23
23
  const { config, tenantId } = options;
24
24
  const { database, tenant, tenantConfig } = (0, _datasourceconfigbuilder.getDatabaseForTenant)(config, tenantId);
25
- // Log target info
26
25
  console.log(`\n📦 Database: ${database}${tenant ? ` [${tenant.name}]` : ' [default]'}`);
27
26
  console.log(`📁 Migrations: migrations/${tenantId || 'default'}/`);
28
- if (config.bootstrapAppConfig?.databaseMode == 'multi-tenant' && !tenantId && config.tenants && config.tenants.length > 0) {
27
+ if (config.bootstrapAppConfig?.databaseMode === 'multi-tenant' && !tenantId && config.tenants && config.tenants.length > 0) {
29
28
  console.log(`\n💡 Multi-tenant mode. Available tenants:`);
30
29
  config.tenants.forEach((t)=>console.log(` - TENANT_ID=${t.id} → ${t.database}`));
31
30
  }
@@ -40,7 +39,6 @@ function createMigrationDataSource(options) {
40
39
  ...config.defaultDatabaseConfig,
41
40
  database
42
41
  };
43
- // Resolve entities based on tenant config
44
42
  const entities = (0, _datasourceconfigbuilder.resolveEntities)(config, tenantConfig);
45
43
  const dsOptions = (0, _datasourceconfigbuilder.buildDataSourceOptions)(dbConfig, entities, (0, _datasourceconfigbuilder.getMigrationsGlobPattern)(config.migrationsPath, tenantId), config.migrationsTableName);
46
44
  return new _typeorm.DataSource({
@@ -1,15 +1,3 @@
1
- /**
2
- * Migration Module Exports
3
- *
4
- * Public API for TypeORM migration management in single and multi-tenant databases.
5
- *
6
- * Main exports:
7
- * - createMigrationDataSource: Create DataSource for migrations
8
- * - initializeDataSource: Create and initialize DataSource
9
- * - runMigrationCli: Programmatic CLI execution
10
- *
11
- * The CLI entry point is at ./cli.ts (used by npm scripts)
12
- */ // DataSource factory for migration config files
13
1
  "use strict";
14
2
  Object.defineProperty(exports, "__esModule", {
15
3
  value: true
@@ -53,23 +53,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
53
53
  }
54
54
  return newObj;
55
55
  }
56
- /**
57
- * Migration CLI Service
58
- *
59
- * Handles migration command execution for single and multi-tenant databases.
60
- *
61
- * Commands:
62
- * - generate: Create new migration
63
- * - run: Execute pending migrations
64
- * - revert: Rollback last migration
65
- * - status: Check migration status
66
- * - run:all/revert:all/status:all: Batch operations for all tenants
67
- *
68
- * Configuration:
69
- * - TENANT_ID: Target tenant (optional, defaults to default database)
70
- * - --config=<path>: Migration config file path
71
- * - --datasource=<path>: DataSource file path for TypeORM CLI
72
- */ function showHelp() {
56
+ function showHelp() {
73
57
  console.log(`
74
58
  Migration CLI
75
59
 
@@ -75,6 +75,36 @@ function _interop_require_wildcard(obj, nodeInterop) {
75
75
  }
76
76
  return newObj;
77
77
  }
78
+ function getLabel(tenantId) {
79
+ return tenantId ? `[${tenantId}]` : '[default]';
80
+ }
81
+ async function withDataSource(config, tenantId, operation) {
82
+ const { database } = (0, _utils.getDatabaseForTenant)(config, tenantId);
83
+ const label = getLabel(tenantId);
84
+ try {
85
+ const ds = await (0, _datasourcefactory.initializeDataSource)({
86
+ config,
87
+ tenantId
88
+ });
89
+ const partial = await operation(ds, label);
90
+ await ds.destroy();
91
+ return {
92
+ tenantId,
93
+ database,
94
+ success: true,
95
+ ...partial
96
+ };
97
+ } catch (error) {
98
+ const errorMessage = error instanceof Error ? error.message : String(error);
99
+ console.error(`${label} ✗ Failed: ${errorMessage}`);
100
+ return {
101
+ tenantId,
102
+ database,
103
+ success: false,
104
+ error: errorMessage
105
+ };
106
+ }
107
+ }
78
108
  function ensureMigrationsFolder(basePath, tenantId) {
79
109
  const folder = (0, _utils.getMigrationsFolderPath)(basePath, tenantId);
80
110
  if (!_fs.existsSync(folder)) {
@@ -116,15 +146,8 @@ function findDatasourcePath() {
116
146
  throw new Error('Could not find datasource file. Specify with --datasource=<path>');
117
147
  }
118
148
  async function runMigrations(config, tenantId) {
119
- const { database } = (0, _utils.getDatabaseForTenant)(config, tenantId);
120
- const label = tenantId ? `[${tenantId}]` : '[default]';
121
- try {
122
- const ds = await (0, _datasourcefactory.initializeDataSource)({
123
- config,
124
- tenantId
125
- });
149
+ return withDataSource(config, tenantId, async (ds, label)=>{
126
150
  const migrations = await ds.runMigrations();
127
- await ds.destroy();
128
151
  if (migrations.length === 0) {
129
152
  console.log(`${label} No pending migrations.`);
130
153
  } else {
@@ -132,78 +155,27 @@ async function runMigrations(config, tenantId) {
132
155
  migrations.forEach((m)=>console.log(` - ${m.name}`));
133
156
  }
134
157
  return {
135
- tenantId,
136
- database,
137
- success: true,
138
158
  migrationsRun: migrations.map((m)=>m.name)
139
159
  };
140
- } catch (error) {
141
- const errorMessage = error instanceof Error ? error.message : String(error);
142
- console.error(`${label} ✗ Failed: ${errorMessage}`);
143
- return {
144
- tenantId,
145
- database,
146
- success: false,
147
- error: errorMessage
148
- };
149
- }
160
+ });
150
161
  }
151
162
  async function revertMigration(config, tenantId) {
152
- const { database } = (0, _utils.getDatabaseForTenant)(config, tenantId);
153
- const label = tenantId ? `[${tenantId}]` : '[default]';
154
- try {
155
- const ds = await (0, _datasourcefactory.initializeDataSource)({
156
- config,
157
- tenantId
158
- });
163
+ return withDataSource(config, tenantId, async (ds, label)=>{
159
164
  await ds.undoLastMigration();
160
- await ds.destroy();
161
165
  console.log(`${label} ✓ Reverted!`);
162
- return {
163
- tenantId,
164
- database,
165
- success: true
166
- };
167
- } catch (error) {
168
- const errorMessage = error instanceof Error ? error.message : String(error);
169
- console.error(`${label} ✗ Failed: ${errorMessage}`);
170
- return {
171
- tenantId,
172
- database,
173
- success: false,
174
- error: errorMessage
175
- };
176
- }
166
+ return {};
167
+ });
177
168
  }
178
169
  async function migrationStatus(config, tenantId) {
179
- const { database } = (0, _utils.getDatabaseForTenant)(config, tenantId);
180
- const label = tenantId ? `[${tenantId}]` : '[default]';
181
- try {
182
- const ds = await (0, _datasourcefactory.initializeDataSource)({
183
- config,
184
- tenantId
185
- });
170
+ return withDataSource(config, tenantId, async (ds, label)=>{
186
171
  const hasPending = await ds.showMigrations();
187
- await ds.destroy();
188
172
  console.log(`${label} Pending: ${hasPending ? 'Yes' : 'No'}`);
189
173
  return {
190
- tenantId,
191
- database,
192
- success: true,
193
174
  migrationsRun: [
194
175
  hasPending ? 'Has pending' : 'Up to date'
195
176
  ]
196
177
  };
197
- } catch (error) {
198
- const errorMessage = error instanceof Error ? error.message : String(error);
199
- console.error(`${label} ✗ Failed: ${errorMessage}`);
200
- return {
201
- tenantId,
202
- database,
203
- success: false,
204
- error: errorMessage
205
- };
206
- }
178
+ });
207
179
  }
208
180
  async function runForAllTenants(config, command) {
209
181
  const tenants = (0, _utils.getActiveTenants)(config);
@@ -22,25 +22,15 @@ function _define_property(obj, key, value) {
22
22
  return obj;
23
23
  }
24
24
  let BaseSeeder = class BaseSeeder {
25
- /**
26
- * Clear all records from the entity table
27
- * Respects soft delete if entity has deletedAt column
28
- * @param hard If true, perform hard delete (ignore soft delete)
29
- */ async clear(hard = false) {
25
+ async clear(hard = false) {
30
26
  const hasSoftDelete = this.metadata.deleteDateColumn !== undefined;
31
27
  if (hard || !hasSoftDelete) {
32
- // Hard delete - remove all records
33
28
  await this.repository.clear();
34
29
  } else {
35
- // Soft delete - set deletedAt
36
30
  await this.repository.createQueryBuilder().softDelete().where('id IS NOT NULL').execute();
37
31
  }
38
32
  }
39
- /**
40
- * Get count of existing records
41
- * @param includeDeleted If true, count soft-deleted records
42
- * @returns Total record count
43
- */ async count(includeDeleted = false) {
33
+ async count(includeDeleted = false) {
44
34
  const hasSoftDelete = this.metadata.deleteDateColumn !== undefined;
45
35
  if (hasSoftDelete && !includeDeleted) {
46
36
  return this.repository.count({
@@ -51,22 +41,13 @@ let BaseSeeder = class BaseSeeder {
51
41
  }
52
42
  return this.repository.count();
53
43
  }
54
- /**
55
- * Check if entity has any records
56
- * @returns True if no records exist
57
- */ async isEmpty() {
58
- const total = await this.count();
59
- return total === 0;
44
+ async isEmpty() {
45
+ return await this.count() === 0;
60
46
  }
61
- /**
62
- * Get entity name for logging
63
- */ getEntityName() {
47
+ getEntityName() {
64
48
  return this.metadata.tableName;
65
49
  }
66
- /**
67
- * Wrap operation in transaction
68
- * @param operation Function to execute in transaction
69
- */ async withTransaction(operation) {
50
+ async withTransaction(operation) {
70
51
  const queryRunner = this.dataSource.createQueryRunner();
71
52
  await queryRunner.connect();
72
53
  await queryRunner.startTransaction();