@solidxai/core 0.1.11-beta.2 → 0.1.11-beta.5

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 (145) hide show
  1. package/dist/commands/migrate-removed-fields.command.d.ts +19 -0
  2. package/dist/commands/migrate-removed-fields.command.d.ts.map +1 -0
  3. package/dist/commands/migrate-removed-fields.command.js +77 -0
  4. package/dist/commands/migrate-removed-fields.command.js.map +1 -0
  5. package/dist/controllers/datasource-introspection.controller.d.ts +38 -1
  6. package/dist/controllers/datasource-introspection.controller.d.ts.map +1 -1
  7. package/dist/controllers/datasource-introspection.controller.js +38 -0
  8. package/dist/controllers/datasource-introspection.controller.js.map +1 -1
  9. package/dist/dtos/datasource-introspection-mapping.dto.d.ts +5 -0
  10. package/dist/dtos/datasource-introspection-mapping.dto.d.ts.map +1 -1
  11. package/dist/dtos/datasource-introspection-mapping.dto.js +24 -2
  12. package/dist/dtos/datasource-introspection-mapping.dto.js.map +1 -1
  13. package/dist/entities/{legacy-common-with-id.entity.d.ts → legacy-common-with-generated-id.entity.d.ts} +2 -2
  14. package/dist/entities/legacy-common-with-generated-id.entity.d.ts.map +1 -0
  15. package/dist/entities/{legacy-common-with-id.entity.js → legacy-common-with-generated-id.entity.js} +4 -4
  16. package/dist/entities/legacy-common-with-generated-id.entity.js.map +1 -0
  17. package/dist/entities/{legacy-common.entity.d.ts → legacy-common.entity-with-existing-id.entity.d.ts} +1 -1
  18. package/dist/entities/legacy-common.entity-with-existing-id.entity.d.ts.map +1 -0
  19. package/dist/entities/{legacy-common.entity.js → legacy-common.entity-with-existing-id.entity.js} +1 -1
  20. package/dist/entities/legacy-common.entity-with-existing-id.entity.js.map +1 -0
  21. package/dist/helpers/command.service.d.ts +2 -0
  22. package/dist/helpers/command.service.d.ts.map +1 -1
  23. package/dist/helpers/command.service.js +6 -2
  24. package/dist/helpers/command.service.js.map +1 -1
  25. package/dist/helpers/model-metadata-helper.service.js +3 -3
  26. package/dist/helpers/model-metadata-helper.service.js.map +1 -1
  27. package/dist/helpers/nodemon-heartbeat.d.ts +5 -0
  28. package/dist/helpers/nodemon-heartbeat.d.ts.map +1 -0
  29. package/dist/helpers/nodemon-heartbeat.js +67 -0
  30. package/dist/helpers/nodemon-heartbeat.js.map +1 -0
  31. package/dist/index.d.ts +3 -2
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +3 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/interfaces/datasource-introspection-provider.interface.d.ts +1 -0
  36. package/dist/interfaces/datasource-introspection-provider.interface.d.ts.map +1 -1
  37. package/dist/interfaces/datasource-introspection-provider.interface.js.map +1 -1
  38. package/dist/seeders/module-metadata-seeder.service.d.ts +7 -1
  39. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  40. package/dist/seeders/module-metadata-seeder.service.js +101 -1
  41. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  42. package/dist/seeders/seed-data/solid-core-metadata.json +121 -34
  43. package/dist/services/api-key.service.d.ts.map +1 -1
  44. package/dist/services/api-key.service.js.map +1 -1
  45. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts +4 -0
  46. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts.map +1 -1
  47. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js +18 -1
  48. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js.map +1 -1
  49. package/dist/services/crud.service.d.ts +1 -0
  50. package/dist/services/crud.service.d.ts.map +1 -1
  51. package/dist/services/crud.service.js +27 -0
  52. package/dist/services/crud.service.js.map +1 -1
  53. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.d.ts.map +1 -1
  54. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js +12 -0
  55. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js.map +1 -1
  56. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.d.ts.map +1 -1
  57. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js +2 -0
  58. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js.map +1 -1
  59. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.d.ts.map +1 -1
  60. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js +28 -15
  61. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js.map +1 -1
  62. package/dist/services/datasource-introspection.service.d.ts +53 -2
  63. package/dist/services/datasource-introspection.service.d.ts.map +1 -1
  64. package/dist/services/datasource-introspection.service.js +559 -174
  65. package/dist/services/datasource-introspection.service.js.map +1 -1
  66. package/dist/services/import-transaction.service.d.ts.map +1 -1
  67. package/dist/services/import-transaction.service.js +3 -0
  68. package/dist/services/import-transaction.service.js.map +1 -1
  69. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +10 -10
  70. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  71. package/dist/services/model-metadata.service.d.ts +1 -0
  72. package/dist/services/model-metadata.service.d.ts.map +1 -1
  73. package/dist/services/model-metadata.service.js +28 -0
  74. package/dist/services/model-metadata.service.js.map +1 -1
  75. package/dist/services/module-metadata.service.d.ts.map +1 -1
  76. package/dist/services/module-metadata.service.js +27 -0
  77. package/dist/services/module-metadata.service.js.map +1 -1
  78. package/dist/services/removed-field-migration.service.d.ts +30 -0
  79. package/dist/services/removed-field-migration.service.d.ts.map +1 -0
  80. package/dist/services/removed-field-migration.service.js +319 -0
  81. package/dist/services/removed-field-migration.service.js.map +1 -0
  82. package/dist/solid-core.module.d.ts.map +1 -1
  83. package/dist/solid-core.module.js +4 -0
  84. package/dist/solid-core.module.js.map +1 -1
  85. package/dist/subscribers/audit.subscriber.d.ts +4 -1
  86. package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
  87. package/dist/subscribers/audit.subscriber.js +54 -6
  88. package/dist/subscribers/audit.subscriber.js.map +1 -1
  89. package/dist-tests/api/authenticate.spec.js +119 -0
  90. package/dist-tests/api/authenticate.spec.js.map +1 -0
  91. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +97 -0
  92. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +1 -0
  93. package/dist-tests/api/ping.spec.js +21 -0
  94. package/dist-tests/api/ping.spec.js.map +1 -0
  95. package/dist-tests/helpers/auth.js +41 -0
  96. package/dist-tests/helpers/auth.js.map +1 -0
  97. package/dist-tests/helpers/env.js +11 -0
  98. package/dist-tests/helpers/env.js.map +1 -0
  99. package/docs/agent-hub-grooming.md +301 -0
  100. package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +438 -0
  101. package/docs/dashboards/dashboard-curl-smoke-tests.txt +146 -0
  102. package/docs/dashboards/delete-legacy-dashboard-metadata.sql +172 -0
  103. package/docs/datasource-introspection-ddl-analysis.md +326 -0
  104. package/docs/datasource-introspection-implementation-plan.md +306 -0
  105. package/docs/grouping-enhancements.md +89 -0
  106. package/docs/java-spring/README.md +3 -0
  107. package/docs/java-spring/solid-core-module-deep-dive-report.md +1317 -0
  108. package/docs/module-package-import-handoff.md +691 -0
  109. package/docs/seed-changes.md +65 -0
  110. package/docs/test-data-workflow.md +200 -0
  111. package/docs/type-declaration-import-issue.md +24 -0
  112. package/package.json +1 -1
  113. package/src/commands/migrate-removed-fields.command.ts +81 -0
  114. package/src/controllers/datasource-introspection.controller.ts +27 -1
  115. package/src/dtos/datasource-introspection-mapping.dto.ts +15 -0
  116. package/src/entities/{legacy-common-with-id.entity.ts → legacy-common-with-generated-id.entity.ts} +1 -1
  117. package/src/helpers/command.service.ts +10 -2
  118. package/src/helpers/model-metadata-helper.service.ts +1 -1
  119. package/src/helpers/nodemon-heartbeat.ts +59 -0
  120. package/src/index.ts +3 -2
  121. package/src/interfaces/datasource-introspection-provider.interface.ts +1 -0
  122. package/src/seeders/module-metadata-seeder.service.ts +118 -0
  123. package/src/seeders/seed-data/solid-core-metadata.json +121 -34
  124. package/src/services/api-key.service.ts +36 -36
  125. package/src/services/computed-fields/entity/concat-entity-computed-field-provider.service.ts +15 -1
  126. package/src/services/crud.service.ts +36 -11
  127. package/src/services/datasource-introspection/mssql-datasource-introspection-provider.service.ts +12 -0
  128. package/src/services/datasource-introspection/mysql-datasource-introspection-provider.service.ts +2 -0
  129. package/src/services/datasource-introspection/postgres-datasource-introspection-provider.service.ts +28 -15
  130. package/src/services/datasource-introspection.service.ts +653 -150
  131. package/src/services/import-transaction.service.ts +5 -0
  132. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +2 -2
  133. package/src/services/model-metadata.service.ts +45 -1
  134. package/src/services/module-metadata.service.ts +35 -0
  135. package/src/services/removed-field-migration.service.ts +334 -0
  136. package/src/solid-core.module.ts +4 -0
  137. package/src/subscribers/audit.subscriber.ts +70 -5
  138. package/.claude/settings.local.json +0 -15
  139. package/CLAUDE.md +0 -26
  140. package/dist/entities/legacy-common-with-id.entity.d.ts.map +0 -1
  141. package/dist/entities/legacy-common-with-id.entity.js.map +0 -1
  142. package/dist/entities/legacy-common.entity.d.ts.map +0 -1
  143. package/dist/entities/legacy-common.entity.js.map +0 -1
  144. package/src/services/1.js +0 -6
  145. /package/src/entities/{legacy-common.entity.ts → legacy-common.entity-with-existing-id.entity.ts} +0 -0
@@ -50,19 +50,101 @@ const crypto_1 = require("crypto");
50
50
  const fs = __importStar(require("fs/promises"));
51
51
  const path = __importStar(require("path"));
52
52
  const lodash_1 = require("lodash");
53
+ const ts_morph_1 = require("ts-morph");
53
54
  const legacy_table_type_enum_1 = require("../enums/legacy-table-type.enum");
55
+ const string_helper_1 = require("../helpers/string.helper");
54
56
  const module_metadata_helper_service_1 = require("../helpers/module-metadata-helper.service");
55
57
  const model_metadata_helper_service_1 = require("../helpers/model-metadata-helper.service");
56
58
  const model_metadata_repository_1 = require("../repository/model-metadata.repository");
57
59
  const module_metadata_repository_1 = require("../repository/module-metadata.repository");
58
60
  const datasource_management_service_1 = require("./datasource-management.service");
61
+ const command_service_1 = require("../helpers/command.service");
59
62
  const field_metadata_service_1 = require("./field-metadata.service");
60
63
  const model_metadata_service_1 = require("./model-metadata.service");
64
+ const module_metadata_service_1 = require("./module-metadata.service");
61
65
  const mssql_datasource_introspection_provider_service_1 = require("./datasource-introspection/mssql-datasource-introspection-provider.service");
62
66
  const mysql_datasource_introspection_provider_service_1 = require("./datasource-introspection/mysql-datasource-introspection-provider.service");
63
67
  const postgres_datasource_introspection_provider_service_1 = require("./datasource-introspection/postgres-datasource-introspection-provider.service");
68
+ const DATASOURCE_TYPE_FAMILY_ALIASES = {
69
+ boolean: ["bit", "bool", "boolean"],
70
+ bigint: ["bigint"],
71
+ int: ["int", "integer", "smallint", "tinyint"],
72
+ numeric: ["numeric"],
73
+ decimal: ["decimal", "money", "smallmoney"],
74
+ float: ["float", "double", "double precision", "real", "float4", "float8"],
75
+ date: ["date"],
76
+ time: ["time", "time without time zone", "time with time zone"],
77
+ datetime: ["datetime", "datetime2", "smalldatetime", "timestamp", "timestamp without time zone"],
78
+ datetimeWithTimezone: ["timestamptz", "timestamp with time zone"],
79
+ json: ["json", "simple-json", "simplejson"],
80
+ jsonb: ["jsonb"],
81
+ uuid: ["uniqueidentifier", "uuid"],
82
+ longText: ["text", "ntext", "longtext", "clob"],
83
+ mediumText: ["mediumtext"],
84
+ shortText: ["varchar", "char", "citext"],
85
+ unicodeShortText: ["nvarchar", "nchar"],
86
+ };
87
+ const ORM_TYPE_BY_DATASOURCE_FAMILY = {
88
+ mssql: {
89
+ boolean: "bit",
90
+ bigint: "bigint",
91
+ int: "int",
92
+ numeric: "numeric",
93
+ decimal: "decimal",
94
+ float: "decimal",
95
+ date: "date",
96
+ time: "time",
97
+ datetime: "datetime",
98
+ datetimeWithTimezone: "datetime",
99
+ json: "simple-json",
100
+ jsonb: "simple-json",
101
+ uuid: "uniqueidentifier",
102
+ longText: "text",
103
+ mediumText: "text",
104
+ shortText: "varchar",
105
+ unicodeShortText: "nvarchar",
106
+ },
107
+ mysql: {
108
+ boolean: "boolean",
109
+ bigint: "int",
110
+ int: "int",
111
+ numeric: "decimal",
112
+ decimal: "decimal",
113
+ float: "double",
114
+ date: "date",
115
+ time: "time",
116
+ datetime: "datetime",
117
+ datetimeWithTimezone: "datetime",
118
+ json: "json",
119
+ jsonb: "json",
120
+ uuid: "varchar",
121
+ longText: "longtext",
122
+ mediumText: "mediumtext",
123
+ shortText: "varchar",
124
+ unicodeShortText: "varchar",
125
+ },
126
+ postgres: {
127
+ boolean: "boolean",
128
+ bigint: "bigint",
129
+ int: "integer",
130
+ numeric: "decimal",
131
+ decimal: "decimal",
132
+ float: "decimal",
133
+ date: "date",
134
+ time: "time",
135
+ datetime: "timestamp",
136
+ datetimeWithTimezone: "timestamptz",
137
+ json: "json",
138
+ jsonb: "jsonb",
139
+ uuid: "uuid",
140
+ longText: "text",
141
+ mediumText: "text",
142
+ shortText: "varchar",
143
+ unicodeShortText: "varchar",
144
+ },
145
+ };
64
146
  let DatasourceIntrospectionService = class DatasourceIntrospectionService {
65
- constructor(moduleRef, datasourceManagementService, moduleMetadataRepository, modelMetadataRepository, moduleMetadataHelperService, modelMetadataHelperService, fieldMetadataService, modelMetadataService, mssqlProvider, mysqlProvider, postgresProvider) {
147
+ constructor(moduleRef, datasourceManagementService, moduleMetadataRepository, modelMetadataRepository, moduleMetadataHelperService, modelMetadataHelperService, fieldMetadataService, modelMetadataService, moduleMetadataService, commandService, mssqlProvider, mysqlProvider, postgresProvider) {
66
148
  this.moduleRef = moduleRef;
67
149
  this.datasourceManagementService = datasourceManagementService;
68
150
  this.moduleMetadataRepository = moduleMetadataRepository;
@@ -71,6 +153,8 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
71
153
  this.modelMetadataHelperService = modelMetadataHelperService;
72
154
  this.fieldMetadataService = fieldMetadataService;
73
155
  this.modelMetadataService = modelMetadataService;
156
+ this.moduleMetadataService = moduleMetadataService;
157
+ this.commandService = commandService;
74
158
  this.mssqlProvider = mssqlProvider;
75
159
  this.mysqlProvider = mysqlProvider;
76
160
  this.postgresProvider = postgresProvider;
@@ -189,24 +273,98 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
189
273
  const persistedModel = plan.operation === "update" && mappingDto.modelId
190
274
  ? await this.modelMetadataService.update(mappingDto.modelId, plan.modelPayload)
191
275
  : await this.modelMetadataService.create(plan.modelPayload);
276
+ const persistedModelId = mappingDto.modelId ?? persistedModel?.id ?? null;
277
+ return {
278
+ data: {
279
+ modelId: persistedModelId,
280
+ operation: plan.operation,
281
+ metadataJson: plan.metadataJson,
282
+ summary: plan.summary,
283
+ },
284
+ };
285
+ }
286
+ async createMigrationArtifacts(moduleId, mappingDto) {
287
+ const plan = await this.buildMappingPreview(moduleId, mappingDto);
288
+ const module = await this.loadModule(moduleId);
289
+ const datasource = this.requireDatasource(mappingDto.dataSource);
290
+ if (!mappingDto.modelId) {
291
+ throw new common_1.BadRequestException("Persist the model mapping first so the generated entity can be registered before creating migration artifacts.");
292
+ }
192
293
  if (plan.migration.willGenerate && plan.migration.content && plan.migration.filePath) {
193
294
  await fs.mkdir(path.dirname(plan.migration.filePath), { recursive: true });
194
295
  await fs.writeFile(plan.migration.filePath, plan.migration.content, "utf8");
195
296
  }
196
- const persistedModelId = mappingDto.modelId ?? persistedModel?.id ?? null;
297
+ const datasourceFile = await this.syncTypeormDatasourceFile(module.name, datasource, plan.modelPayload.singularName);
197
298
  return {
198
299
  data: {
199
- modelId: persistedModelId,
300
+ modelId: mappingDto.modelId,
200
301
  operation: plan.operation,
201
- metadataJson: plan.metadataJson,
202
302
  migration: {
203
303
  ...plan.migration,
204
304
  written: Boolean(plan.migration.willGenerate && plan.migration.filePath),
205
305
  },
306
+ datasourceFile,
206
307
  summary: plan.summary,
207
308
  },
208
309
  };
209
310
  }
311
+ async generateCode(moduleId) {
312
+ await this.loadModule(moduleId);
313
+ const output = await this.moduleMetadataService.generateCodeViaCtl(moduleId);
314
+ return {
315
+ data: {
316
+ moduleId,
317
+ output,
318
+ message: "Module code generation completed.",
319
+ },
320
+ };
321
+ }
322
+ async runMigration(moduleId, datasourceName) {
323
+ const module = await this.loadModule(moduleId);
324
+ const datasource = this.requireDatasource(datasourceName);
325
+ const cwd = path.join(process.cwd(), "..");
326
+ const outputs = [];
327
+ const migrationOutput = await this.commandService.executeCommandWithArgs({
328
+ command: "npx",
329
+ args: ["-y", "@solidxai/solidctl@latest", "migration", "-d", datasource.name, "run"],
330
+ cwd,
331
+ });
332
+ outputs.push([
333
+ `npx @solidxai/solidctl@latest migration -d ${datasource.name} run`,
334
+ migrationOutput,
335
+ ].filter(Boolean).join("\n"));
336
+ const buildOutput = await this.commandService.executeCommandWithArgs({
337
+ command: "npx",
338
+ args: ["-y", "@solidxai/solidctl@latest", "build"],
339
+ cwd,
340
+ });
341
+ outputs.push([
342
+ "npx @solidxai/solidctl@latest build",
343
+ buildOutput,
344
+ ].filter(Boolean).join("\n"));
345
+ const seedOutput = await this.commandService.executeCommandWithArgs({
346
+ command: "npx",
347
+ args: ["-y", "@solidxai/solidctl@latest", "seed", "--modules-to-seed", module.name],
348
+ cwd,
349
+ });
350
+ outputs.push([
351
+ `npx @solidxai/solidctl@latest seed --modules-to-seed ${module.name}`,
352
+ seedOutput,
353
+ ].filter(Boolean).join("\n"));
354
+ return {
355
+ data: {
356
+ moduleId,
357
+ datasource: datasource.name,
358
+ output: outputs.join("\n\n"),
359
+ command: [
360
+ `npx @solidxai/solidctl@latest migration -d ${datasource.name} run`,
361
+ "npx @solidxai/solidctl@latest build",
362
+ `npx @solidxai/solidctl@latest seed --modules-to-seed ${module.name}`,
363
+ ].join("\n"),
364
+ message: `Migration, build, and seed completed successfully for datasource "${datasource.name}".`,
365
+ },
366
+ };
367
+ }
210
368
  async buildMappingPreview(moduleId, mappingDto) {
211
369
  const module = await this.loadModule(moduleId);
212
370
  const datasource = this.requireDatasource(mappingDto.dataSource);
@@ -252,6 +410,13 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
252
410
  mappedModelId: mappingDto.modelId ?? null,
253
411
  metadataJson,
254
412
  migration,
413
+ datasourceFile: {
414
+ filePath: path.resolve(process.cwd(), "src", `typeorm-${datasource.name}-datasource.ts`),
415
+ created: false,
416
+ updated: false,
417
+ entityRegistered: false,
418
+ migrationGlobRegistered: false,
419
+ },
255
420
  summary: {
256
421
  includedColumnNames,
257
422
  removedColumnNames,
@@ -455,6 +620,12 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
455
620
  }
456
621
  return rest;
457
622
  }
623
+ sanitizeConfiguredFieldPayload(fieldConfig) {
624
+ if (!fieldConfig || typeof fieldConfig !== "object" || Array.isArray(fieldConfig)) {
625
+ return {};
626
+ }
627
+ return this.sanitizeExistingFieldPayload(fieldConfig);
628
+ }
458
629
  normalizeReviewedMetadataModel(reviewedModel, mappingDto) {
459
630
  if (!reviewedModel) {
460
631
  return null;
@@ -577,32 +748,37 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
577
748
  }
578
749
  buildFieldPayload(column, mappingDto, existingField) {
579
750
  const preserved = existingField ? this.sanitizeExistingFieldPayload(existingField) : {};
751
+ const configured = this.sanitizeConfiguredFieldPayload(column.fieldConfig);
752
+ const merged = {
753
+ ...preserved,
754
+ ...configured,
755
+ };
580
756
  const maxLength = column.characterMaximumLength && ["shortText", "longText"].includes(column.solidFieldType)
581
757
  ? column.characterMaximumLength
582
- : preserved.max;
758
+ : merged.max;
583
759
  return {
584
- ...preserved,
760
+ ...merged,
585
761
  ...(column.fieldId ? { id: column.fieldId } : {}),
586
762
  name: column.fieldName,
587
- displayName: preserved.displayName ?? (0, lodash_1.startCase)(column.fieldName),
588
- description: preserved.description ?? "",
763
+ displayName: column.displayName?.trim() || merged.displayName || (0, lodash_1.startCase)(column.fieldName),
764
+ description: merged.description ?? "",
589
765
  type: column.solidFieldType,
590
766
  ormType: column.ormType,
591
- length: preserved.length ?? column.characterMaximumLength ?? undefined,
592
- defaultValue: preserved.defaultValue ?? column.defaultValue ?? undefined,
593
- required: preserved.required ?? (!column.nullable || column.isPrimaryKey),
594
- unique: preserved.unique ?? false,
595
- encrypt: preserved.encrypt ?? false,
596
- index: preserved.index ?? false,
767
+ length: merged.length ?? column.characterMaximumLength ?? undefined,
768
+ defaultValue: merged.defaultValue ?? column.defaultValue ?? undefined,
769
+ required: merged.required ?? (!column.nullable || column.isPrimaryKey),
770
+ unique: merged.unique ?? false,
771
+ encrypt: merged.encrypt ?? false,
772
+ index: merged.index ?? false,
597
773
  max: maxLength ?? undefined,
598
- min: preserved.min ?? undefined,
599
- private: preserved.private ?? false,
600
- isSystem: preserved.isSystem ?? false,
774
+ min: merged.min ?? undefined,
775
+ private: merged.private ?? false,
776
+ isSystem: merged.isSystem ?? false,
601
777
  isMarkedForRemoval: false,
602
778
  columnName: column.columnName,
603
779
  isUserKey: mappingDto.userKeyField === column.fieldName,
604
- enableAuditTracking: preserved.enableAuditTracking ?? mappingDto.enableAuditTracking,
605
- isMultiSelect: preserved.isMultiSelect ?? false,
780
+ enableAuditTracking: merged.enableAuditTracking ?? mappingDto.enableAuditTracking,
781
+ isMultiSelect: merged.isMultiSelect ?? false,
606
782
  isPrimaryKey: column.isPrimaryKey,
607
783
  };
608
784
  }
@@ -696,23 +872,19 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
696
872
  const generatedIdUniqueName = this.buildMigrationObjectName("UQ", tableNameLiteral, "ss_id");
697
873
  const upColumnStatements = missingSystemColumns.map((definition) => {
698
874
  const columnLiteral = this.renderTableColumnLiteral(definition);
699
- return [
700
- ` if (!(await queryRunner.hasColumn(tableName, ${JSON.stringify(definition.columnName)}))) {`,
701
- ` await queryRunner.addColumn(tableName, new TableColumn(${columnLiteral}));`,
702
- " }",
703
- ].join("\n");
875
+ return ` if (!(await queryRunner.hasColumn(tableName, ${JSON.stringify(definition.columnName)})))\n`
876
+ + ` await queryRunner.addColumn(tableName, new TableColumn(${columnLiteral}));`;
704
877
  }).join("\n\n");
705
878
  const postUpStatements = [];
706
879
  if (missingSystemColumns.some((definition) => definition.columnName === "ss_deleted_at")) {
707
880
  postUpStatements.push([
708
881
  " {",
709
882
  " const table = await queryRunner.getTable(tableName);",
710
- ` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(deletedAtIndexName)})) {`,
711
- " await queryRunner.createIndex(tableName, new TableIndex({",
712
- ` name: ${JSON.stringify(deletedAtIndexName)},`,
713
- " columnNames: [\"ss_deleted_at\"],",
714
- " }));",
715
- " }",
883
+ ` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(deletedAtIndexName)}))`,
884
+ ` await queryRunner.createIndex(tableName, new TableIndex(${this.renderTableIndexLiteral({
885
+ name: deletedAtIndexName,
886
+ columnNames: ["ss_deleted_at"],
887
+ })}));`,
716
888
  " }",
717
889
  ].join("\n"));
718
890
  }
@@ -720,13 +892,12 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
720
892
  postUpStatements.push([
721
893
  " {",
722
894
  " const table = await queryRunner.getTable(tableName);",
723
- ` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(generatedIdUniqueName)})) {`,
724
- " await queryRunner.createIndex(tableName, new TableIndex({",
725
- ` name: ${JSON.stringify(generatedIdUniqueName)},`,
726
- " columnNames: [\"ss_id\"],",
727
- " isUnique: true,",
728
- " }));",
729
- " }",
895
+ ` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(generatedIdUniqueName)}))`,
896
+ ` await queryRunner.createIndex(tableName, new TableIndex(${this.renderTableIndexLiteral({
897
+ name: generatedIdUniqueName,
898
+ columnNames: ["ss_id"],
899
+ isUnique: true,
900
+ })}));`,
730
901
  " }",
731
902
  ].join("\n"));
732
903
  }
@@ -736,9 +907,8 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
736
907
  " {",
737
908
  " const table = await queryRunner.getTable(tableName);",
738
909
  ` const index = table?.indices.find((entry) => entry.name === ${JSON.stringify(generatedIdUniqueName)});`,
739
- " if (index) {",
910
+ " if (index)",
740
911
  " await queryRunner.dropIndex(tableName, index);",
741
- " }",
742
912
  " }",
743
913
  ].join("\n"));
744
914
  }
@@ -747,20 +917,16 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
747
917
  " {",
748
918
  " const table = await queryRunner.getTable(tableName);",
749
919
  ` const index = table?.indices.find((entry) => entry.name === ${JSON.stringify(deletedAtIndexName)});`,
750
- " if (index) {",
920
+ " if (index)",
751
921
  " await queryRunner.dropIndex(tableName, index);",
752
- " }",
753
922
  " }",
754
923
  ].join("\n"));
755
924
  }
756
925
  downStatements.push(...missingSystemColumns
757
926
  .slice()
758
927
  .reverse()
759
- .map((definition) => [
760
- ` if (await queryRunner.hasColumn(tableName, ${JSON.stringify(definition.columnName)})) {`,
761
- ` await queryRunner.dropColumn(tableName, ${JSON.stringify(definition.columnName)});`,
762
- " }",
763
- ].join("\n")));
928
+ .map((definition) => ` if (await queryRunner.hasColumn(tableName, ${JSON.stringify(definition.columnName)}))\n`
929
+ + ` await queryRunner.dropColumn(tableName, ${JSON.stringify(definition.columnName)});`));
764
930
  return [
765
931
  "import { MigrationInterface, QueryRunner, TableColumn, TableIndex } from \"typeorm\";",
766
932
  "",
@@ -783,27 +949,36 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
783
949
  }
784
950
  renderTableColumnLiteral(definition) {
785
951
  const properties = [
786
- `{\n name: ${JSON.stringify(definition.columnName)},`,
787
- ` type: ${JSON.stringify(definition.type)},`,
952
+ `name: ${JSON.stringify(definition.columnName)}`,
953
+ `type: ${JSON.stringify(definition.type)}`,
788
954
  ];
789
955
  if (definition.length) {
790
- properties.push(` length: ${JSON.stringify(definition.length)},`);
956
+ properties.push(`length: ${JSON.stringify(definition.length)}`);
791
957
  }
792
958
  if (typeof definition.isNullable === "boolean") {
793
- properties.push(` isNullable: ${definition.isNullable},`);
959
+ properties.push(`isNullable: ${definition.isNullable}`);
794
960
  }
795
961
  if (definition.defaultExpression) {
796
- properties.push(` default: ${JSON.stringify(definition.defaultExpression)},`);
962
+ properties.push(`default: ${JSON.stringify(definition.defaultExpression)}`);
797
963
  }
798
964
  if (definition.isGenerated) {
799
- properties.push(" isGenerated: true,");
800
- properties.push(" generationStrategy: \"increment\",");
965
+ properties.push("isGenerated: true");
966
+ properties.push("generationStrategy: \"increment\"");
801
967
  }
802
968
  if (definition.isUnique) {
803
- properties.push(" isUnique: true,");
969
+ properties.push("isUnique: true");
804
970
  }
805
- properties.push(" }");
806
- return properties.join("\n");
971
+ return `{ ${properties.join(", ")}, }`;
972
+ }
973
+ renderTableIndexLiteral(definition) {
974
+ const properties = [
975
+ `name: ${JSON.stringify(definition.name)}`,
976
+ `columnNames: ${JSON.stringify(definition.columnNames)}`,
977
+ ];
978
+ if (definition.isUnique) {
979
+ properties.push("isUnique: true");
980
+ }
981
+ return `{ ${properties.join(", ")}, }`;
807
982
  }
808
983
  buildMigrationObjectName(prefix, tableName, columnName) {
809
984
  const hash = (0, crypto_1.createHash)("md5").update(`${tableName}:${columnName}:${prefix}`).digest("hex").slice(0, 24);
@@ -832,12 +1007,14 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
832
1007
  async buildTableInventory(datasource) {
833
1008
  const provider = this.getProvider(datasource.type);
834
1009
  const runtimeDataSource = this.resolveDataSourceByName(datasource.name);
835
- const [tables, columns, models] = await Promise.all([
1010
+ const [rawTables, rawColumns, models] = await Promise.all([
836
1011
  provider.listTables(runtimeDataSource),
837
1012
  provider.listColumns(runtimeDataSource),
838
1013
  this.modelMetadataRepository.find({ relations: { module: true } }),
839
1014
  ]);
840
- const modelMap = this.buildModelMap(models);
1015
+ const tables = rawTables.filter((table) => !this.isSolidxInternalTableName(table.tableName));
1016
+ const columns = rawColumns.filter((column) => !this.isSolidxInternalTableName(column.tableName));
1017
+ const modelLookup = this.buildModelLookup(models);
841
1018
  const columnsByKey = this.groupColumnsByTable(columns);
842
1019
  const tableKeys = new Set();
843
1020
  for (const table of tables) {
@@ -857,7 +1034,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
857
1034
  }
858
1035
  const suggestedLegacyTableType = this.detectLegacyTableType(datasource.type, tableColumns);
859
1036
  const suggestedIdColumn = this.getSuggestedIdColumn(datasource.type, tableColumns, suggestedLegacyTableType);
860
- const mappedModel = modelMap.get(this.toModelMapKey(datasource.name, tableName)) ?? null;
1037
+ const mappedModel = this.resolveMappedModel(modelLookup, datasource.name, tableName);
861
1038
  const physicalColumns = tableColumns.filter((column) => !this.isHandledBySuperclass(column.columnName, suggestedLegacyTableType));
862
1039
  return {
863
1040
  schema,
@@ -892,16 +1069,37 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
892
1069
  columnsByKey,
893
1070
  };
894
1071
  }
895
- buildModelMap(models) {
896
- const map = new Map();
1072
+ buildModelLookup(models) {
1073
+ const byDatasourceAndTable = new Map();
1074
+ const byTableName = new Map();
897
1075
  for (const model of models) {
898
1076
  if (!model.tableName) {
899
1077
  continue;
900
1078
  }
901
- map.set(this.toModelMapKey(model.dataSource, model.tableName), model);
902
- map.set(this.toModelMapKey("*", model.tableName), model);
1079
+ byDatasourceAndTable.set(this.toModelMapKey(model.dataSource, model.tableName), model);
1080
+ const normalizedTableName = model.tableName.trim().toLowerCase();
1081
+ const bucket = byTableName.get(normalizedTableName);
1082
+ if (bucket) {
1083
+ bucket.push(model);
1084
+ continue;
1085
+ }
1086
+ byTableName.set(normalizedTableName, [model]);
903
1087
  }
904
- return map;
1088
+ return {
1089
+ byDatasourceAndTable,
1090
+ byTableName,
1091
+ };
1092
+ }
1093
+ resolveMappedModel(lookup, datasourceName, tableName) {
1094
+ const exactMatch = lookup.byDatasourceAndTable.get(this.toModelMapKey(datasourceName, tableName));
1095
+ if (exactMatch) {
1096
+ return exactMatch;
1097
+ }
1098
+ const fallbackMatches = lookup.byTableName.get(tableName.trim().toLowerCase()) ?? [];
1099
+ if (fallbackMatches.length === 1) {
1100
+ return fallbackMatches[0];
1101
+ }
1102
+ return fallbackMatches[0] ?? null;
905
1103
  }
906
1104
  groupColumnsByTable(columns) {
907
1105
  const grouped = new Map();
@@ -916,6 +1114,9 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
916
1114
  }
917
1115
  return grouped;
918
1116
  }
1117
+ isSolidxInternalTableName(tableName) {
1118
+ return typeof tableName === "string" && tableName.toLowerCase().startsWith("ss_");
1119
+ }
919
1120
  detectLegacyTableType(datasourceType, columns) {
920
1121
  const normalizedDatasourceType = datasourceType.toLowerCase();
921
1122
  const hasExistingGeneratedId = columns.some((column) => column.isAutoGenerated);
@@ -983,12 +1184,16 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
983
1184
  }
984
1185
  toDetailColumnRecord(column, legacyTableType, datasourceType) {
985
1186
  const superclassFieldName = this.getSuperclassFieldName(column.columnName, legacyTableType);
1187
+ const normalizedColumnName = column.columnName.toLowerCase();
986
1188
  const fieldName = legacyTableType === legacy_table_type_enum_1.LegacyTableType.EXISTING_ID && column.isAutoGenerated
987
1189
  ? "id"
988
- : this.toFieldName(column.columnName);
1190
+ : legacyTableType === legacy_table_type_enum_1.LegacyTableType.GENERATED_ID && normalizedColumnName === "id"
1191
+ ? "legacyId"
1192
+ : this.toFieldName(column.columnName);
989
1193
  return {
990
1194
  columnName: column.columnName,
991
1195
  fieldName,
1196
+ displayName: (0, lodash_1.startCase)(fieldName),
992
1197
  dataType: column.dataType,
993
1198
  solidFieldType: this.toSolidFieldType(column),
994
1199
  ormType: this.toOrmFieldType(column, datasourceType),
@@ -999,6 +1204,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
999
1204
  numericScale: column.numericScale,
1000
1205
  ordinalPosition: column.ordinalPosition,
1001
1206
  isAutoGenerated: column.isAutoGenerated,
1207
+ isPrimaryKey: column.isPrimaryKey,
1002
1208
  handledBySuperclass: Boolean(superclassFieldName),
1003
1209
  superclassFieldName,
1004
1210
  };
@@ -1033,127 +1239,70 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
1033
1239
  }
1034
1240
  return "LegacyCommonEntityWithExistingId";
1035
1241
  }
1036
- toSolidFieldType(column) {
1037
- const normalized = column.dataType.toLowerCase();
1038
- const charLength = column.characterMaximumLength ?? null;
1039
- if (["bit", "bool", "boolean"].includes(normalized)) {
1040
- return "boolean";
1041
- }
1042
- if (["bigint"].includes(normalized)) {
1043
- return "bigint";
1044
- }
1045
- if (["int", "integer", "smallint", "tinyint"].includes(normalized)) {
1046
- return "int";
1047
- }
1048
- if (["numeric", "decimal", "float", "double", "real", "money", "smallmoney"].includes(normalized)) {
1049
- return "decimal";
1050
- }
1051
- if (["date"].includes(normalized)) {
1052
- return "date";
1053
- }
1054
- if (["time"].includes(normalized)) {
1055
- return "time";
1056
- }
1057
- if (["datetime", "datetime2", "timestamp", "timestamptz", "smalldatetime"].includes(normalized) || normalized.includes("date")) {
1058
- return "datetime";
1242
+ getDatasourceTypeFamily(dataType) {
1243
+ const normalized = dataType.toLowerCase();
1244
+ for (const [family, aliases] of Object.entries(DATASOURCE_TYPE_FAMILY_ALIASES)) {
1245
+ if (aliases.includes(normalized)) {
1246
+ return family;
1247
+ }
1059
1248
  }
1060
- if (["json", "jsonb", "simple-json", "simplejson"].includes(normalized) || normalized.includes("json")) {
1249
+ if (normalized.includes("json")) {
1061
1250
  return "json";
1062
1251
  }
1063
- if (["uniqueidentifier", "uuid"].includes(normalized)) {
1064
- return "uuid";
1065
- }
1066
- if (["text", "ntext", "mediumtext", "longtext", "clob"].includes(normalized)) {
1067
- return "longText";
1068
- }
1069
- if (["varchar", "nvarchar", "char", "nchar", "citext"].includes(normalized)) {
1070
- if (charLength !== null && charLength > 255) {
1071
- return "longText";
1072
- }
1073
- return "shortText";
1252
+ if (normalized.includes("date")) {
1253
+ return "datetime";
1074
1254
  }
1075
- return "shortText";
1255
+ return null;
1076
1256
  }
1077
- toOrmFieldType(column, datasourceType) {
1078
- const normalized = column.dataType.toLowerCase();
1079
- const normalizedDatasourceType = datasourceType.toLowerCase();
1080
- if (normalizedDatasourceType === "mssql") {
1081
- if (["bit", "bool", "boolean"].includes(normalized))
1082
- return "bit";
1083
- if (["bigint"].includes(normalized))
1257
+ toSolidFieldType(column) {
1258
+ const family = this.getDatasourceTypeFamily(column.dataType);
1259
+ const charLength = column.characterMaximumLength ?? null;
1260
+ switch (family) {
1261
+ case "boolean":
1262
+ return "boolean";
1263
+ case "bigint":
1084
1264
  return "bigint";
1085
- if (["int", "integer", "smallint", "tinyint"].includes(normalized))
1265
+ case "int":
1086
1266
  return "int";
1087
- if (["numeric"].includes(normalized))
1088
- return "numeric";
1089
- if (["decimal", "float", "double", "real", "money", "smallmoney"].includes(normalized))
1267
+ case "numeric":
1268
+ case "decimal":
1269
+ case "float":
1090
1270
  return "decimal";
1091
- if (["date"].includes(normalized))
1271
+ case "date":
1092
1272
  return "date";
1093
- if (["time"].includes(normalized))
1273
+ case "time":
1094
1274
  return "time";
1095
- if (["datetime2"].includes(normalized))
1096
- return "datetime2";
1097
- if (["datetime", "smalldatetime", "timestamp"].includes(normalized) || normalized.includes("date"))
1275
+ case "datetime":
1276
+ case "datetimeWithTimezone":
1098
1277
  return "datetime";
1099
- if (["json", "jsonb", "simple-json", "simplejson"].includes(normalized) || normalized.includes("json"))
1100
- return "simple-json";
1101
- if (["uniqueidentifier", "uuid"].includes(normalized))
1102
- return "uniqueidentifier";
1103
- if (["text", "ntext", "mediumtext", "longtext", "clob"].includes(normalized))
1104
- return "text";
1105
- if (["nvarchar", "nchar"].includes(normalized))
1106
- return "nvarchar";
1107
- return "varchar";
1108
- }
1109
- if (normalizedDatasourceType === "mysql") {
1110
- if (["bigint"].includes(normalized))
1111
- return "int";
1112
- if (["int", "integer", "smallint", "tinyint"].includes(normalized))
1113
- return "int";
1114
- if (["decimal", "numeric"].includes(normalized))
1115
- return "decimal";
1116
- if (["float", "double", "real", "money", "smallmoney"].includes(normalized))
1117
- return "double";
1118
- if (["bit", "bool", "boolean"].includes(normalized))
1119
- return "boolean";
1120
- if (["date"].includes(normalized))
1121
- return "date";
1122
- if (["time"].includes(normalized))
1123
- return "time";
1124
- if (["datetime", "timestamp", "smalldatetime"].includes(normalized) || normalized.includes("date"))
1125
- return "datetime";
1126
- if (["json", "jsonb", "simple-json", "simplejson"].includes(normalized) || normalized.includes("json"))
1278
+ case "json":
1279
+ case "jsonb":
1127
1280
  return "json";
1128
- if (["mediumtext"].includes(normalized))
1129
- return "mediumtext";
1130
- if (["longtext", "text", "ntext", "clob"].includes(normalized))
1131
- return "longtext";
1132
- return "varchar";
1133
- }
1134
- if (["bigint"].includes(normalized))
1135
- return "bigint";
1136
- if (["int", "integer", "smallint", "tinyint"].includes(normalized))
1137
- return "integer";
1138
- if (["decimal", "numeric", "float", "double", "real", "money", "smallmoney"].includes(normalized))
1139
- return "decimal";
1140
- if (["bit", "bool", "boolean"].includes(normalized))
1141
- return "boolean";
1142
- if (["date"].includes(normalized))
1143
- return "date";
1144
- if (["time"].includes(normalized))
1145
- return "time";
1146
- if (["timestamptz"].includes(normalized))
1147
- return "timestamptz";
1148
- if (["datetime", "datetime2", "timestamp", "smalldatetime"].includes(normalized) || normalized.includes("date"))
1149
- return "timestamp";
1150
- if (["jsonb"].includes(normalized))
1151
- return "jsonb";
1152
- if (["json", "simple-json", "simplejson"].includes(normalized) || normalized.includes("json"))
1153
- return "json";
1154
- if (["text", "ntext", "mediumtext", "longtext", "clob"].includes(normalized))
1155
- return "text";
1156
- return "varchar";
1281
+ case "uuid":
1282
+ return "uuid";
1283
+ case "mediumText":
1284
+ case "longText":
1285
+ return "longText";
1286
+ case "shortText":
1287
+ case "unicodeShortText":
1288
+ if (charLength !== null && charLength > 255) {
1289
+ return "longText";
1290
+ }
1291
+ return "shortText";
1292
+ default:
1293
+ return "shortText";
1294
+ }
1295
+ }
1296
+ toOrmFieldType(column, datasourceType) {
1297
+ const family = this.getDatasourceTypeFamily(column.dataType);
1298
+ const normalizedDatasourceType = datasourceType.toLowerCase();
1299
+ const ormType = family
1300
+ ? ORM_TYPE_BY_DATASOURCE_FAMILY[normalizedDatasourceType]?.[family]
1301
+ : undefined;
1302
+ if (ormType) {
1303
+ return ormType;
1304
+ }
1305
+ return normalizedDatasourceType === "postgres" ? "varchar" : "varchar";
1157
1306
  }
1158
1307
  toFieldName(columnName) {
1159
1308
  return (0, lodash_1.camelCase)(columnName.replace(/^ss_/, ""));
@@ -1190,6 +1339,240 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
1190
1339
  toModelMapKey(datasourceName, tableName) {
1191
1340
  return `${datasourceName.toLowerCase()}::${tableName.toLowerCase()}`;
1192
1341
  }
1342
+ async syncTypeormDatasourceFile(moduleName, datasource, singularName) {
1343
+ const datasourceFilePath = path.resolve(process.cwd(), "src", `typeorm-${datasource.name}-datasource.ts`);
1344
+ const entityClassName = (0, string_helper_1.classify)(singularName);
1345
+ const entityImportPath = `./${moduleName}/entities/${(0, lodash_1.kebabCase)(singularName)}.entity`;
1346
+ const migrationGlob = `join(__dirname, './${moduleName}/migrations/${datasource.name}/*.{ts,js}')`;
1347
+ const existingContent = await fs.readFile(datasourceFilePath, "utf8").catch(() => null);
1348
+ if (!existingContent) {
1349
+ const rendered = this.renderTypeormDatasourceTemplate({
1350
+ datasource,
1351
+ entityClassName,
1352
+ entityImportPath,
1353
+ migrationGlob,
1354
+ });
1355
+ await fs.writeFile(datasourceFilePath, rendered, "utf8");
1356
+ return {
1357
+ filePath: datasourceFilePath,
1358
+ created: true,
1359
+ updated: false,
1360
+ entityRegistered: true,
1361
+ migrationGlobRegistered: true,
1362
+ };
1363
+ }
1364
+ const project = new ts_morph_1.Project({
1365
+ skipAddingFilesFromTsConfig: true,
1366
+ manipulationSettings: {
1367
+ quoteKind: ts_morph_1.QuoteKind.Single,
1368
+ indentationText: ts_morph_1.IndentationText.FourSpaces,
1369
+ insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
1370
+ },
1371
+ });
1372
+ const sourceFile = project.createSourceFile(datasourceFilePath, existingContent, { overwrite: true });
1373
+ const entityImportDeclaration = sourceFile.getImportDeclarations().find((declaration) => declaration.getModuleSpecifierValue() === entityImportPath);
1374
+ if (entityImportDeclaration) {
1375
+ const alreadyImported = entityImportDeclaration.getNamedImports().some((namedImport) => namedImport.getName() === entityClassName);
1376
+ if (!alreadyImported) {
1377
+ entityImportDeclaration.addNamedImport(entityClassName);
1378
+ }
1379
+ }
1380
+ else {
1381
+ sourceFile.addImportDeclaration({
1382
+ moduleSpecifier: entityImportPath,
1383
+ namedImports: [entityClassName],
1384
+ });
1385
+ }
1386
+ const entitiesArray = sourceFile
1387
+ .getVariableDeclarationOrThrow("entities")
1388
+ .getInitializerIfKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
1389
+ const entityAlreadyRegistered = this.arrayLiteralContainsValue(entitiesArray, entityClassName);
1390
+ if (!entityAlreadyRegistered) {
1391
+ entitiesArray.addElement(entityClassName);
1392
+ }
1393
+ const dataSourceOptions = this.getTypeormDatasourceOptions(sourceFile);
1394
+ const migrationsArray = this.getObjectPropertyArrayLiteral(dataSourceOptions, "migrations");
1395
+ const migrationAlreadyRegistered = this.arrayLiteralContainsValue(migrationsArray, migrationGlob);
1396
+ if (!migrationAlreadyRegistered) {
1397
+ migrationsArray.addElement(migrationGlob);
1398
+ }
1399
+ sourceFile.organizeImports();
1400
+ sourceFile.formatText();
1401
+ const nextContent = sourceFile.getFullText();
1402
+ const updated = nextContent !== existingContent;
1403
+ if (updated) {
1404
+ await fs.writeFile(datasourceFilePath, nextContent, "utf8");
1405
+ }
1406
+ return {
1407
+ filePath: datasourceFilePath,
1408
+ created: false,
1409
+ updated,
1410
+ entityRegistered: true,
1411
+ migrationGlobRegistered: true,
1412
+ };
1413
+ }
1414
+ renderTypeormDatasourceTemplate(input) {
1415
+ const { datasource, entityClassName, entityImportPath, migrationGlob } = input;
1416
+ const envPrefix = datasource.envPrefix?.toUpperCase?.() || "DEFAULT";
1417
+ const portFallback = datasource.type === "postgres" ? 5432 : datasource.type === "mysql" ? 3306 : 1433;
1418
+ const project = new ts_morph_1.Project({
1419
+ skipAddingFilesFromTsConfig: true,
1420
+ manipulationSettings: {
1421
+ quoteKind: ts_morph_1.QuoteKind.Single,
1422
+ indentationText: ts_morph_1.IndentationText.FourSpaces,
1423
+ insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
1424
+ },
1425
+ });
1426
+ const sourceFile = project.createSourceFile("typeorm-template.ts", "", { overwrite: true });
1427
+ sourceFile.addImportDeclaration({ moduleSpecifier: "reflect-metadata" });
1428
+ sourceFile.addImportDeclaration({ moduleSpecifier: "path", namedImports: ["join"] });
1429
+ sourceFile.addImportDeclaration({
1430
+ moduleSpecifier: "dotenv",
1431
+ namedImports: [{ name: "config", alias: "dotenvConfig" }],
1432
+ });
1433
+ sourceFile.addStatements("dotenvConfig({ path: join(__dirname, '../.env') });");
1434
+ sourceFile.addStatements("");
1435
+ sourceFile.addImportDeclaration({ moduleSpecifier: "typeorm", namedImports: ["DataSource"] });
1436
+ sourceFile.addImportDeclaration({
1437
+ moduleSpecifier: "@solidxai/core",
1438
+ namespaceImport: "SolidCoreModuleExports",
1439
+ });
1440
+ sourceFile.addImportDeclaration({
1441
+ moduleSpecifier: "@solidxai/core",
1442
+ namedImports: ["getDynamicModuleNames", "parseBooleanEnv"],
1443
+ });
1444
+ sourceFile.addImportDeclaration({
1445
+ moduleSpecifier: "typeorm",
1446
+ namedImports: ["getMetadataArgsStorage"],
1447
+ });
1448
+ sourceFile.addImportDeclaration({
1449
+ moduleSpecifier: "typeorm-naming-strategies",
1450
+ namedImports: ["SnakeNamingStrategy"],
1451
+ });
1452
+ sourceFile.addImportDeclaration({
1453
+ moduleSpecifier: entityImportPath,
1454
+ namedImports: [entityClassName],
1455
+ });
1456
+ sourceFile.addFunction({
1457
+ name: "getEntitiesFromExports",
1458
+ parameters: [{ name: "exports", type: "Record<string, any>" }],
1459
+ statements: (writer) => {
1460
+ writer.writeLine("const metadataStorage = getMetadataArgsStorage();");
1461
+ writer.writeLine("return Object.values(exports).filter((item) =>");
1462
+ writer.indent(() => {
1463
+ writer.writeLine("metadataStorage.tables.some((table) => table.target === item),");
1464
+ });
1465
+ writer.write(");");
1466
+ },
1467
+ });
1468
+ sourceFile.addVariableStatement({
1469
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
1470
+ declarations: [
1471
+ {
1472
+ name: "coreEntities",
1473
+ initializer: "getEntitiesFromExports(SolidCoreModuleExports)",
1474
+ },
1475
+ ],
1476
+ });
1477
+ sourceFile.addVariableStatement({
1478
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
1479
+ declarations: [
1480
+ {
1481
+ name: "dynamicModules",
1482
+ initializer: "getDynamicModuleNames()",
1483
+ },
1484
+ ],
1485
+ });
1486
+ sourceFile.addVariableStatement({
1487
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
1488
+ declarations: [
1489
+ {
1490
+ name: "entities",
1491
+ initializer: `[\n ...coreEntities,\n ${entityClassName},\n]`,
1492
+ },
1493
+ ],
1494
+ });
1495
+ sourceFile.addVariableStatement({
1496
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
1497
+ declarations: [
1498
+ {
1499
+ name: "logging",
1500
+ initializer: `parseBooleanEnv('${envPrefix}_DATABASE_LOGGING')`,
1501
+ },
1502
+ ],
1503
+ });
1504
+ sourceFile.addStatements("// IMPORTANT: synchronize must be false when using migrations");
1505
+ sourceFile.addVariableStatement({
1506
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
1507
+ isExported: true,
1508
+ declarations: [
1509
+ {
1510
+ name: "DefaultDataSource",
1511
+ initializer: (writer) => {
1512
+ writer.write("new DataSource(");
1513
+ writer.inlineBlock(() => {
1514
+ writer.writeLine(`type: '${datasource.type}',`);
1515
+ writer.blankLine();
1516
+ writer.writeLine(`host: process.env.${envPrefix}_DATABASE_HOST,`);
1517
+ writer.writeLine(`port: +(process.env.${envPrefix}_DATABASE_PORT || ${portFallback}),`);
1518
+ writer.writeLine(`username: process.env.${envPrefix}_DATABASE_USER,`);
1519
+ writer.writeLine(`password: process.env.${envPrefix}_DATABASE_PASSWORD,`);
1520
+ writer.writeLine(`database: process.env.${envPrefix}_DATABASE_NAME,`);
1521
+ writer.blankLine();
1522
+ writer.writeLine("entities,");
1523
+ writer.writeLine("migrations: [");
1524
+ writer.indent(() => {
1525
+ writer.writeLine(`${migrationGlob},`);
1526
+ });
1527
+ writer.writeLine("],");
1528
+ writer.writeLine("synchronize: false,");
1529
+ writer.writeLine("logging,");
1530
+ writer.writeLine("namingStrategy: new SnakeNamingStrategy(),");
1531
+ if (datasource.type === "mssql") {
1532
+ writer.writeLine("options: {");
1533
+ writer.indent(() => {
1534
+ writer.writeLine("encrypt: false,");
1535
+ writer.writeLine("trustServerCertificate: true,");
1536
+ });
1537
+ writer.writeLine("},");
1538
+ }
1539
+ });
1540
+ writer.write(")");
1541
+ },
1542
+ },
1543
+ ],
1544
+ });
1545
+ sourceFile.organizeImports();
1546
+ sourceFile.formatText();
1547
+ return sourceFile.getFullText();
1548
+ }
1549
+ arrayLiteralContainsValue(arrayLiteral, rawValue) {
1550
+ const normalizedTarget = rawValue.replace(/\s+/g, "");
1551
+ return arrayLiteral.getElements().some((element) => element.getText().replace(/\s+/g, "") === normalizedTarget);
1552
+ }
1553
+ getTypeormDatasourceOptions(sourceFile) {
1554
+ const defaultDataSourceDeclaration = sourceFile.getVariableDeclarationOrThrow("DefaultDataSource");
1555
+ const initializer = defaultDataSourceDeclaration.getInitializer();
1556
+ if (!initializer || !ts_morph_1.Node.isNewExpression(initializer)) {
1557
+ throw new common_1.BadRequestException("Unable to locate DefaultDataSource initializer in datasource file.");
1558
+ }
1559
+ const options = initializer.getArguments()[0];
1560
+ if (!options || !ts_morph_1.Node.isObjectLiteralExpression(options)) {
1561
+ throw new common_1.BadRequestException("Unable to locate DefaultDataSource options object in datasource file.");
1562
+ }
1563
+ return options;
1564
+ }
1565
+ getObjectPropertyArrayLiteral(objectLiteral, propertyName) {
1566
+ const property = objectLiteral.getProperty(propertyName);
1567
+ if (!property || !ts_morph_1.Node.isPropertyAssignment(property)) {
1568
+ throw new common_1.BadRequestException(`Unable to locate "${propertyName}" property in datasource file.`);
1569
+ }
1570
+ const initializer = property.getInitializer();
1571
+ if (!initializer || !ts_morph_1.Node.isArrayLiteralExpression(initializer)) {
1572
+ throw new common_1.BadRequestException(`Unable to locate "${propertyName}" array in datasource file.`);
1573
+ }
1574
+ return initializer;
1575
+ }
1193
1576
  buildSynchronizeBlockedMessage(datasource) {
1194
1577
  return `Datasource "${datasource.displayName}" has synchronize enabled. Set ${datasource.envPrefix}_DATABASE_SYNCHRONIZE=false before starting legacy table introspection.`;
1195
1578
  }
@@ -1271,6 +1654,8 @@ exports.DatasourceIntrospectionService = DatasourceIntrospectionService = __deco
1271
1654
  model_metadata_helper_service_1.ModelMetadataHelperService,
1272
1655
  field_metadata_service_1.FieldMetadataService,
1273
1656
  model_metadata_service_1.ModelMetadataService,
1657
+ module_metadata_service_1.ModuleMetadataService,
1658
+ command_service_1.CommandService,
1274
1659
  mssql_datasource_introspection_provider_service_1.MssqlDatasourceIntrospectionProviderService,
1275
1660
  mysql_datasource_introspection_provider_service_1.MysqlDatasourceIntrospectionProviderService,
1276
1661
  postgres_datasource_introspection_provider_service_1.PostgresDatasourceIntrospectionProviderService])